[RFC] libelf: segment fault on x86-64 while file's bss offset have a large number
by Hongxu Jia
*Environment
cat /etc/issue
Ubuntu 13.04 \n \l
uname -a
Linux pek-hjia-d1 3.8.0-31-generic #46-Ubuntu SMP Tue Sep 10 20:03:44 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
*Problem
1) Here is the test source code
$ cat >> test.c << EOF
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include "libelf.h"
int main(int argc, char *argv[])
{
int fd;
Elf *e;
if (elf_version(EV_CURRENT) == EV_NONE)
{
printf ("library out of date\n");
exit (1);
}
if ((fd = open("test/xB.linkhuge", O_RDWR)) < 0) {
printf("%s %d failed\n", __FUNCTION__, __LINE__);
exit (1);
}
if ((e = elf_begin(fd, ELF_C_RDWR_MMAP, (Elf *) 0)) == 0) {
printf("failed %s", elf_errmsg (-1));
exit (1);
}
elf_flagelf (e, ELF_C_SET, ELF_F_LAYOUT);
elf_update(e, ELF_C_WRITE);
elf_end(e);
close(fd);
}
EOF
2) Download http://kojipkgs.fedoraproject.org/packages/elfutils/0.157/2.fc21/x86_64/e...
to get libelf.a for debug.
Download http://kojipkgs.fedoraproject.org/packages/elfutils/0.157/2.fc21/x86_64/e...
to get libelf.h for debug.
3) Compile test.c with libelf.a
$ gcc test.c -o test_case -static -L. -lelf
4) Prepare file whose bss offset have a large number '00200000'
Download the attachment from
https://bugzilla.redhat.com/show_bug.cgi?id=1020842
$ ls test/xB.linkhuge -al
-rwxr-xr-x 1 jiahongxu jiahongxu 1221403 Oct 18 18:55 test/xB.linkhuge
$ readelf -a xB.linkhuge
......
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
skip..
[25] .data PROGBITS 00000000005128a0 001128a0
0000000000010168 0000000000000000 WA 0 0 32
[26] .bss NOBITS 0000000001000000 00200000
0000000000010050 0000000000000000 WA 0 0 32
[27] .comment PROGBITS 0000000000000000 00122a08
0000000000000011 0000000000000001 MS 0 0 1
......
5) Run test_case with strace, there was mmap/munmap error.
$ strace ./test_case
execve("./test_case", ["./test_case"], [/* 59 vars */]) = 0
uname({sys="Linux", node="pek-hjia-d1", ...}) = 0
brk(0) = 0x16a6000
brk(0x16a71c0) = 0x16a71c0
arch_prctl(ARCH_SET_FS, 0x16a6880) = 0
brk(0x16c81c0) = 0x16c81c0
brk(0x16c9000) = 0x16c9000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("test/xB.linkhuge", O_RDWR) = 3
fcntl(3, F_GETFL) = 0x8002 (flags O_RDWR|O_LARGEFILE)
fstat(3, {st_mode=S_IFREG|0755, st_size=1221403, ...}) = 0
mmap(NULL, 1221403, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7ff720fe2000
fstat(3, {st_mode=S_IFREG|0755, st_size=1221403, ...}) = 0
ftruncate(3, 2097152) = 0
msync(0x7ff720fe2000, 1216568, MS_SYNC) = 0
munmap(0x7ff720fe2000, 2097152) = 0
close(3) = 0
exit_group(0) = ?
6) $ ls test/xB.linkhuge -al
-rwxr-xr-x 1 jiahongxu jiahongxu 2097152 Oct 18 19:04 test/xB.linkhuge
*Analysis
1) While ELF_C_RDWR_MMAP was used, elf_begin invoked mmap() to map file
into memory with the size of '1221403'.
...strace log...
mmap(NULL, 1221403, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7ff720fe2000
................
2) While 'xB.linkhuge' bss Offset has a large number '00200000', elf_update
caculated file size by __elf64_updatenull_wrlock and the size was
enlarged from '1221403' to '2097152'
3) In this situation, elf_update invoked ftruncate to enlarge the file,
and memory size (elf->maximum_size) also was incorrectly updated.
...strace log...
ftruncate(3, 2097152)
................
4) There was segment fault in elf_end which invoked munmap with the
length is the enlarged file size '2097152', not the length of
mmap '1216568'.
...strace log...
munmap(0x7ff720fe2000, 2097152) = 0
................
*Solution
1) I tried to modify elf_update.c, don't update memory size
(elf->maximum_size) in this situation. It fixed this issue
and everything looks ok, but I am not sure the modification
is necessary.
......
11 diff --git a/libelf/elf_update.c b/libelf/elf_update.c
12 --- a/libelf/elf_update.c
13 +++ b/libelf/elf_update.c
14 @@ -120,7 +120,9 @@ write_file (Elf *elf, off_t size, int change_bo, size_t shnum)
15 size = -1;
16 }
17
18 - if (size != -1 && elf->parent == NULL)
19 + /* If the file is enlarged by truncate, we should not update maximum_size to
20 + avoid segment fault while invoking munmap in elf_end */
21 + if (size != -1 && elf->parent == NULL && (size_t) size <= elf->maximum_size)
22 elf->maximum_size = size;
......
2) I also tried to add check before munmap in elf_end by msync with
the length of elf->maximum_size, if msync return error, munmap
should not be invoked, this could avoid segment fault.
--- a/libelf/elf_end.c
+++ b/libelf/elf_end.c
@@ -217,7 +217,10 @@ elf_end (elf)
if ((elf->flags & ELF_F_MALLOCED) != 0)
free (elf->map_address);
else if ((elf->flags & ELF_F_MMAPPED) != 0)
- munmap (elf->map_address, elf->maximum_size);
+ {
+ if (msync (elf->map_address, elf->maximum_size, MS_SYNC) == 0)
+ munmap (elf->map_address, elf->maximum_size);
+ }
}
3) Any suggestion is welcomed.
--
1.8.1.2
9 years, 8 months
libelf RDWR and elf_newscn do not work
by Jiri Slaby
Hi,
if I add a section using the code below, I obtain a section like this:
[29] NULL 0000000000000000
fff1000400000001 000000 00 0 0 281487861612544
The attached patch fixed it for me. Any ideas?
Code:
if (elf_version(EV_CURRENT) == EV_NONE)
errx(1, "elf_version: %s" , elf_errmsg(-1));
fd = open(argv[1], O_RDWR, 0);
if (fd < 0)
err(1, "open");
elf = elf_begin(fd, ELF_C_RDWR, NULL);
if (!elf)
errx(EXIT_FAILURE, "elf_begin: %s", elf_errmsg(-1));
data = elf_newdata(scn);
if (!data)
errx(1, "!elf_newdata: %s", elf_errmsg(-1));
GElf_Shdr shdr_data, *shdr;
shdr = gelf_getshdr(scn, &shdr_data);
if (!shdr)
errx(1, "gelf_getshdr: %s", elf_errmsg(-1));
shdr->sh_type = SHT_NOBITS;
if (!gelf_update_shdr(scn, shdr))
errx(EXIT_FAILURE, "gelf_update_shdr: %s", elf_errmsg(-1));
if (elf_update(elf, ELF_C_NULL) < 0)
errx(EXIT_FAILURE, "elf_update1: %s", elf_errmsg(-1));
if (elf_update(elf, ELF_C_WRITE) < 0)
errx(EXIT_FAILURE, "elf_update2: %s", elf_errmsg(-1));
if (elf_end(elf))
errx(EXIT_FAILURE, "elf_end: %s", elf_errmsg(-1));
close(fd);
thanks,
--
js
suse labs
9 years, 10 months
[PATCH] Check for prefixed ar, readelf, and nm
by Michael Forney
Sometimes with cross-compile toolchains, the tools are prefixed with the
target arch. Using AC_CHECK_TOOL looks for tools named like this.
---
I'm not entirely sure why the additional libdwfl_objects variable is necessary,
but was required on my system in order for automake to generate Makefile.in
correctly with regards to variable expansion. However, it seems innocent
enough.
config/eu.am | 2 +-
configure.ac | 3 +++
libasm/Makefile.am | 2 +-
libdw/Makefile.am | 5 +++--
libelf/Makefile.am | 2 +-
tests/Makefile.am | 6 ++++--
tests/run-arsymtest.sh | 2 +-
7 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/config/eu.am b/config/eu.am
index 86e5a4e..38718c7 100644
--- a/config/eu.am
+++ b/config/eu.am
@@ -61,4 +61,4 @@ endif
CLEANFILES = *.gcno *.gcda
-textrel_check = if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi
+textrel_check = if $(READELF) -d $@ | fgrep -q TEXTREL; then exit 1; fi
diff --git a/configure.ac b/configure.ac
index b4c249c..c69dd3e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -79,6 +79,9 @@ AC_PROG_CC
AC_PROG_RANLIB
AC_PROG_YACC
AM_PROG_LEX
+AM_PROG_AR
+AC_CHECK_TOOL([READELF], [readelf])
+AC_CHECK_TOOL([NM], [nm])
AC_CACHE_CHECK([for gcc with C99 support], ac_cv_c99, [dnl
old_CFLAGS="$CFLAGS"
diff --git a/libasm/Makefile.am b/libasm/Makefile.am
index e16d4be..4d81536 100644
--- a/libasm/Makefile.am
+++ b/libasm/Makefile.am
@@ -69,7 +69,7 @@ libasm.so: libasm_pic.a libasm.map
-Wl,--version-script,$(srcdir)/libasm.map,--no-undefined \
-Wl,--soname,$@.$(VERSION) \
../libebl/libebl.a ../libelf/libelf.so $(libasm_so_LDLIBS)
- if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi
+ if $(READELF) -d $@ | fgrep -q TEXTREL; then exit 1; fi
ln -fs $@ $@.$(VERSION)
install: install-am libasm.so
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 5fef2e1..a22166a 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -113,7 +113,7 @@ libdw.so: $(srcdir)/libdw.map libdw_pic.a \
-Wl,--version-script,$<,--no-undefined \
-Wl,--whole-archive $(filter-out $<,$^) -Wl,--no-whole-archive\
-ldl $(zip_LIBS)
- if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi
+ if $(READELF) -d $@ | fgrep -q TEXTREL; then exit 1; fi
ln -fs $@ $@.$(VERSION)
install: install-am libdw.so
@@ -129,7 +129,8 @@ uninstall: uninstall-am
rmdir --ignore-fail-on-non-empty $(DESTDIR)$(includedir)/elfutils
endif
-libdw_a_LIBADD = $(addprefix ../libdwfl/,$(shell $(AR) t ../libdwfl/libdwfl.a))
+libdwfl_objects = $(shell $(AR) t ../libdwfl/libdwfl.a)
+libdw_a_LIBADD = $(addprefix ../libdwfl/,$(libdwfl_objects))
noinst_HEADERS = libdwP.h memory-access.h dwarf_abbrev_hash.h \
dwarf_sig8_hash.h cfi.h encoded-value.h
diff --git a/libelf/Makefile.am b/libelf/Makefile.am
index 5903ea8..4646fba 100644
--- a/libelf/Makefile.am
+++ b/libelf/Makefile.am
@@ -106,7 +106,7 @@ libelf.so: libelf_pic.a libelf.map
$(LINK) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \
-Wl,--version-script,$(srcdir)/libelf.map,--no-undefined \
-Wl,--soname,$@.$(VERSION),-z,defs,-z,relro $(libelf_so_LDLIBS)
- if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi
+ if $(READELF) -d $@ | fgrep -q TEXTREL; then exit 1; fi
ln -fs $@ $@.$(VERSION)
install: install-am libelf.so
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d07cb0b..bc97523 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -231,7 +231,8 @@ installed_TESTS_ENVIRONMENT = libdir=$(DESTDIR)$(libdir); \
export abs_srcdir; export abs_builddir; \
export abs_top_builddir; \
export libdir; export bindir; \
- export LC_ALL; export LANG; export VALGRIND_CMD;
+ export LC_ALL; export LANG; export VALGRIND_CMD; \
+ NM=$(NM); export NM;
installed_LOG_COMPILER = $(abs_srcdir)/test-wrapper.sh \
installed $(tests_rpath) \
'$(program_transform_name)'
@@ -244,7 +245,8 @@ TESTS_ENVIRONMENT = LC_ALL=C; LANG=C; VALGRIND_CMD=$(valgrind_cmd); \
abs_top_builddir=$(abs_top_builddir); \
export abs_srcdir; export abs_builddir; \
export abs_top_builddir; \
- export LC_ALL; export LANG; export VALGRIND_CMD;
+ export LC_ALL; export LANG; export VALGRIND_CMD; \
+ NM=$(NM); export NM;
LOG_COMPILER = $(abs_srcdir)/test-wrapper.sh \
$(abs_top_builddir)/libdw:$(abs_top_builddir)/backends:$(abs_top_builddir)/libelf:$(abs_top_builddir)/libasm
diff --git a/tests/run-arsymtest.sh b/tests/run-arsymtest.sh
index dc016e1..b0fdfcd 100755
--- a/tests/run-arsymtest.sh
+++ b/tests/run-arsymtest.sh
@@ -28,7 +28,7 @@ tempfiles $okfile $tmpfile $testfile
result=77
if test -f $lib; then
# Generate list using `nm' we check against.
- nm -s $lib |
+ ${NM} -s $lib |
sed -e '1,/^Arch/d' -e '/^$/,$d' |
sort > $okfile
--
1.8.4.1
9 years, 10 months
[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
zcmV<B3mWu7T4*^jL0KkKSuURr_W%mkfB*mg|Nq|q|NH;<|L_0*-~aD#<^S~1Rn!0E
z_Fs2@Z-3wlFSlLZ%V-VnFFe#O=MC#yF}Cp4$~5k?cMUY5GH4N|nrW(d(@E-jC#rbG
zPf%(4lhpK?rlw43HlxaTrk|=Fq3U{=nWTD|9;Sz+^&X~;3@4}thm`d_LqG%5P|!U<
z(@#;Mk?IXhg*^<Kni!g=fJx}5>Y6>Ep{9>0^#EvS00E!?&;vj;&;S|$27mwqKmY&$
zX!QWoLMBKin1Xsnr>W>@G$zzzQ1vt#0B8e4AOHXlP&68782|==00E!|jQ|XQ27^G;
zN)V&cY@<M885%V8G-;qRF#)ESni^@NAoT`AKs0HQra&41$Oepzk5FhdXfXiOK+p!7
zGzc0R00Ton8W{|LG-NUXqd|}uAQ=Nq00EJqpc()JKn8#s003wJ4FQ5gku(xEqct{z
zK}^)h(TMd1k5fPZ14Gm_0C=W=(Deq7L;wH=fMfsw15Z!@000_o_l!;=QzD*27#Tw^
ze%Zo@0rmvSMb}?4sKFtXh(icNf3k)c+iYV7gRWvw)4P<3I;_Gh$zfqD-g2Bso{)GV
z7RnWph?GjpVc-&(;<+nb1B_xMTr464-eafV!Ly&Km*TS3qfJSPHF`$=wj+I^dh}mJ
zXG&)+PZ{g56vGsZAVlEo2^9C71QnQT2|Stc5J-m7Q<BP!A)Qjhb|HykMB#QFRpzPV
zMWZYQFO(=GkVynG$qaHKW5}xJW!}nnQ!l8MhoHhQ_pBII>nA)8Q7Dwo8wPCydCwp$
zptN1r(%UH{D#8PGz~3<$7cOB-m|~$e<sB5bB?GmPFzv@>)n8lDZSno`eQr0W5J;}2
zoA!GC6%WzOM-u3SyCDRQADqG<M598mNV6v0fn^n@Iae3>UZ#HP5(Gg^PJs%5Z7!d|
zGzjW;IK5-gh=DY6Z!M!hxq4gxJ4)$*`G7s<G&3ckonZ#KwjA6+w2>td2h@QeQ4%ql
zNoGHxiBz$0<d%()@O3W#hNFer=p^c%jjm$adhAP>8i%rXZxiI<%h*4zejDdZRtqIs
zSLULg&9*{hpERRj6aY4Xw$LGIni9upAXXT!7$miiAYlwfJMmgj#U_Yi6f_`J0hGrE
zk`jn$fE+Rg#=~OJ1|ZN$q_yoNLk7@VVOoesN3tWy;G&ZOF63aVG=i%^RiHrF0ifKV
zA&Dbo&<0ymv;$};Y8aaqjiMQ=h*D`H8Vq=Zk)<#p0+R`%;u~XRfhd$M?jclQ5XqAW
z&B6i0a9>JHV{^<YZvx=9Wuj5S0)!-ShC{WSThV`)RMv=8TGpvEO%YL92bU|E$;<hg
zwUro{@8;^@C*;wf@}(~>BKCBs%3HR{aN%lKDXf5P5yKIHHY6v)#O-=xL4<<|BsD`?
zrd6>sXS{RXW~}p9atcZAiQSu)FTHeEn)9j+I-rkjl3=j>YpYb)jBCk+jS7dV2dNu_
z?k|pw#+@M$!%!k=0QSTI3t524nzyXxI3p6xX9(VboN>~478%jp2+#^eSpk9sM?!$~
zgtWhhYj5TE?cuq9w$J4IJj_}&lsWsH?9SHS9AbzGl0uM!b_fDNM}-kJfNPOaH2P8n
zGgu5Mv};&<%quY3hSNsc!)$3x*Sp90oWAcYnX1uE-jM{ECK!!u9yKFLlV<ji8isVc
zI1Nh?fI@O8ssdC(iF<0=TT91xcX#iSKKVi6=hPx$ns=Rc_ov@3s7p}81?(*#S>#9n
zo#%!qFQFu{cl&F5diCg<5V5a}zsO2(oZ&b{2E6U~6#yW-ycSehL~!R&P>?SQK!Ff*
z$VlpKcU}dhc#Ub{TF|bQAg4Q{RMSx|5J*nr!Gi4zH4vkmp32VAv)DV;_;Fy1mcue6
z0mZ;a_Z)h3I+g^DLIvp<4JxAiCF=|^HgKeX;1zxNqT{3LTN$L<t<cDvHK*50PjhTc
zRw+?#@@fQ{Ub_%x+6mu;!F2;xp#aXJqcI9*1T_Gb5mG4u9jZ}Rb;7m`;CpykMU;p{
zAqXynAWQl(Ck)HUbrpfU0Ts1nUZUN(>oEY~g=|bq!qDz!AJN$*+q6=u!6d5$mR$#g
zLzl!&2fAh{{_m#Q0-_L=ylSUIv&Gd>dMMqPCmb-;LIAL2-9~6t-)>y6@5Ra7va>4>
zT%*Z1aBwcG3h_u|qp~L?b{ZZ5kpuu9%7T(8w(lxpK4gJAs4`NpVRBB0QGhXvx~Np0
zO*sTqjwC?EMcCk_HDdOwO&@ccC27rph(+TUL~R171_+2?k7<I?CBRXquC$sI_PxNN
zzw$Rg#HJBEouF78DAb}GH<474u~C*PsbN4k^AI71y6H5HQK=}8DFrC0y}Nb2so6QG
zc^xx?QIxLhfv^yVjv>bcSkBhQuyzigF9ZvmJ(va#pbU7kcmdMFCb*(Npn(aUbpnj~
zqy{p*iBH0oV+JHi2Eb~!L1<u$5zvO^3afzVBG?{mCfI#r<#0nHylZ|OdGH3Z$Wy_|
zT9``w0wWb1cte5FFxXkAY4T=~B`HcwLIzN_DQ07YkSzS$;Tsx@%;xp|{nQ18+jRoP
zC{B?uog9K4;;ic7g0iV)W-38)A2`x?0tD$5s6bH_(oI6G9qa)mS_!q9aW+w4HriIn
z{ObH$pGzx88fR&^`cEH_)$JH|lz#8L@@z%OIu+>k?A-mpsT-ZC5=DS^?eLp#AuBAK
zdpGcHIajc8zI;`L)ooMUNKYrSsV0<Qu=A#-l|)Ca4f?bfSKdva`sh4CW&bfgd?4Pb
zLyy-Zmd`Yf48S<4XUmi@-!dQHc>cdb1@<EPak5{p(}ot~b{r1IBrFh6pIH(i0#@fs
zEGz<cLqU)<0&#fEi4ynCAFj&e2@4neILHEHS`Uh8_PSV78PU01+zvm*J*HgzSj`pj
z#-@C?pUqkhfY%Cw#?_WUAgsTRV_233<v@ejChFT}QAf085vOarG$mhUvs)UxiXK|m
z5*<49>CaK3YjYnpJuI_1Ib7o^ViQ{9O$#_d3}i$NwA*7uLW4h;N=*{e>5FxpSA#{(
zqA!D3R|@yhD0ELya4HZ_Q=u&)a-giN3-gn<nq)IIkxvB>PSWDSk^z+hE49|zNC%ZJ
zO_Wh~2ot(lNzf|Bh89{U3#+N@R#eE6YUL8Z(2x~lIu(XUxu*prQz|YXjV=-DO>TBW
zBNDw}Oyg;ZL5)Db$(jnd^xvSE6+$ht7NZLT4Tsb*kfWkA5`<9dbmRhxPMHDb2$9Dc
zo+v<#2vvCSja*`3wqs9I9Bh-Otbsj22(TD3uM02{K@~iLMWS4&tQMLg8Lq%oD-^OM
z?EvIXreMZ`0$+)Rks-hyI1yYc$wje1D}vTV0wE|^4Hd*<=+s2YqE=_k9Ez~59Vyp9
zkOPb&Q)1jvi^~ZlnS@Xb8US#42vh=4n1ZbpC=>`&w9vT113<2Tb5RQ8cv`eiNLzJO
zwm>%T@a$@_UNo)>wF-dAA(pfEXgNga9fk&S?&isO76QREo3p8dzN<7KKrGt`aJq!P
zW*6DaWKFD?Z(2)cFkP%AF`lg3rtUGAYKxaapvuUw1dkaUO>xz`rWqz$-2f1wU30R6
zcSxJq^pf5+R-<DxU)Q4`1yb_kVX}FAFK2g8S`_`8>saHWOX$E+ee+E)@~}5q><{WG
z+56dToMAct3*EvMLyH<~w$iJJ#6VG-@-}cPTyQxPAwzBPcKCU)ah!f%mG%>eG|6eE
z86WNp9eS|9Wg{zod%2AY4<E&WTB}OXtPO@lX-a89oKZ?|CN-Kp#+PV+IL(|8zUZ>3
z=<y94o?q^f;1H+K#NG7r>D>i*hQokseh(s*WD7SgUl;lY1}%&J!o6?GfG7=nsu(Qx
zL|&`fyIKTtWY~H*1diBP6#N|C-xp`D!`SL{S<7>t4DH>NaZOG&m2^NGMwW@rH6}L9
nz^X|ntyMf?sGDwf^6#JUNq>2zr8`Qo&-`7<6yZWZx_mp_!b@pV
literal 0
HcmV?d00001
--
1.7.1
9 years, 10 months
[patch v6 4/5] x86* unwinder: src/
by Jan Kratochvil
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.
Signed-off-by: Jan Kratochvil <jan.kratochvil(a)redhat.com>
--- 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
--- /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;
+}
9 years, 10 months
[patch] Fix executable_for_core for non-dwfl_standard_argp
by Jan Kratochvil
Hi Mark,
currently dwfl->executable_for_core is private. It is set from
libdwfl/argp-std.c but it is used also from libdwfl/link_map.c.
Applications not using dwfl_standard_argp () cannot use executable_for_core.
This has effect for running jankratochvil/unwindx86 on RHEL-5. RHEL-5 does
not have build-ids, therefore its core files do not contain the first page of
each ELF file. I such case one needs dwfl->executable_for_core to find the
missing program headers (to find DYNAMIC segment). src/stack.c was working
there (thanks to dwfl_standard_argp ()) but tests/backtrace.c was not, as it
calls dwfl_core_file_report () on its own.
Unfortunately jankratochvil/unwindx86 does not work yet on RHEL-5 but that is
unrelated (there is some race with SIGSTOPs, that will be a different patch).
No testcase here. It should be reproducible with jankratochvil/unwindx86 on
RHEL-5 hosts. I could provide a core file generated without the first ELF
page dumping with matching executable but I have not.
Thanks,
Jan
./
2013-10-29 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* NEWS (Version 0.158): New.
libdw/
2013-10-29 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* libdw.map (ELFUTILS_0.158): New.
libdwfl/
2013-10-29 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* argp-std.c (parse_opt): Use executable parameter of
dwfl_core_file_report.
* core-file.c (dwfl_core_file_report): Add parameter executable. Set
it to DWFL. Add NEW_VERSION for it.
(_compat_without_executable_dwfl_core_file_report): New. Twice.
* libdwfl.h (dwfl_core_file_report): Add parameter executable, update
the function comment.
diff --git a/NEWS b/NEWS
index 7236f66..9d73f09 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+Version 0.158
+
+libdwfl: dwfl_core_file_report has new parameter executable.
+
Version 0.157
libdw: Add new functions dwarf_getlocations, dwarf_getlocation_attr
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 09eae6a..5fb6660 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -267,3 +267,9 @@ ELFUTILS_0.157 {
dwarf_getlocation_die;
dwarf_getlocation_attr;
} ELFUTILS_0.156;
+
+ELFUTILS_0.158 {
+ global:
+ # Replaced ELFUTILS_0.146 version, which has a wrapper without executable.
+ dwfl_core_file_report;
+} ELFUTILS_0.157;
diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c
index c884390..322cdf4 100644
--- a/libdwfl/argp-std.c
+++ b/libdwfl/argp-std.c
@@ -295,9 +295,6 @@ parse_opt (int key, char *arg, struct argp_state *state)
if (opt->core)
{
- if (opt->e)
- dwfl->executable_for_core = strdup (opt->e);
-
int fd = open64 (opt->core, O_RDONLY);
if (fd < 0)
{
@@ -317,7 +314,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
return error == DWFL_E_ERRNO ? errno : EIO;
}
- int result = INTUSE(dwfl_core_file_report) (dwfl, core);
+ int result = INTUSE(dwfl_core_file_report) (dwfl, core, opt->e);
if (result < 0)
{
elf_end (core);
diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
index 7207591..37613b8 100644
--- a/libdwfl/core-file.c
+++ b/libdwfl/core-file.c
@@ -398,7 +398,7 @@ clear_r_debug_info (struct r_debug_info *r_debug_info)
}
int
-dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
+dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
{
size_t phnum;
if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
@@ -407,6 +407,19 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
return -1;
}
+ free (dwfl->executable_for_core);
+ if (executable == NULL)
+ dwfl->executable_for_core = NULL;
+ else
+ {
+ dwfl->executable_for_core = strdup (executable);
+ if (dwfl->executable_for_core == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ }
+
/* First report each PT_LOAD segment. */
GElf_Phdr notes_phdr;
int ndx = dwfl_report_core_segments (dwfl, elf, phnum, ¬es_phdr);
@@ -524,3 +537,16 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
return sniffed || listed >= 0 ? listed + sniffed : listed;
}
INTDEF (dwfl_core_file_report)
+NEW_VERSION (dwfl_core_file_report, ELFUTILS_0.158)
+
+#ifdef SHARED
+int _compat_without_executable_dwfl_core_file_report (Dwfl *dwfl, Elf *elf);
+COMPAT_VERSION_NEWPROTO (dwfl_core_file_report, ELFUTILS_0.146,
+ without_executable)
+
+int
+_compat_without_executable_dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
+{
+ return dwfl_core_file_report (dwfl, elf, NULL);
+}
+#endif
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index 2b70e28..2ba8234 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -349,11 +349,13 @@ extern int dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
This can follow a dwfl_report_offline call to bootstrap the
DT_DEBUG method of following the dynamic linker link_map chain, in
case the core file does not contain enough of the executable's text
- 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.
+ segment to locate its PT_DYNAMIC in the dump. In such case you need to
+ supply non-NULL EXECUTABLE, otherwise dynamic libraries will not be loaded
+ into the DWFL map. 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.
Returns the number of modules reported, or -1 for errors. */
-extern int dwfl_core_file_report (Dwfl *dwfl, Elf *elf);
+extern int dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable);
/* Call dwfl_report_module for each file mapped into the address space of PID.
Returns zero on success, -1 if dwfl_report_module failed,
9 years, 10 months
FYI unwinder unwinder limits
by Jan Kratochvil
Hi Mark,
just implemented the discussed simple execution limits.
The diff here is made with -b|--ignore-space-change.
Jan
diff --git a/libdwfl/frame_unwind.c b/libdwfl/frame_unwind.c
index a875e98..94213e5 100644
--- a/libdwfl/frame_unwind.c
+++ b/libdwfl/frame_unwind.c
@@ -36,6 +36,13 @@
#include "../libdw/dwarf.h"
#include <sys/ptrace.h>
+/* Maximum number of DWARF expression stack slots before returning an error. */
+#define DWARF_EXPR_STACK_MAX 0x100
+
+/* Maximum number of DWARF expression executed operations before returning an
+ error. */
+#define DWARF_EXPR_STEPS_MAX 0x1000
+
#ifndef MAX
# define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
@@ -90,7 +97,6 @@ bra_compar (const void *key_voidp, const void *elem_voidp)
/* If FRAME is NULL is are computing CFI frame base. In such case another
DW_OP_call_frame_cfa is no longer permitted. */
-/* FIXME: Handle bytecode deadlocks and overflows. */
static bool
expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
@@ -108,6 +114,11 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
bool
push (Dwarf_Addr val)
{
+ if (stack_used >= DWARF_EXPR_STACK_MAX)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
if (stack_used == stack_allocated)
{
stack_allocated = MAX (stack_allocated * 2, 32);
@@ -137,7 +148,14 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
Dwarf_Addr val1, val2;
bool is_location = false;
+ size_t steps_count = 0;
for (const Dwarf_Op *op = ops; op < ops + nops; op++)
+ {
+ if (++steps_count > DWARF_EXPR_STEPS_MAX)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
switch (op->atom)
{
/* DW_OP_* order matches libgcc/unwind-dw2.c execute_stack_op: */
@@ -447,6 +465,7 @@ expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
__libdwfl_seterrno (DWFL_E_INVALID_DWARF);
return false;
}
+ }
if (! pop (result))
{
free (stack);
9 years, 11 months
[patch v6 3/5] x86* unwinder: libdwfl/
by Jan Kratochvil
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 (Dwfl_Thread, Dwfl_Frame): New typedefs.
(dwfl_core_file_report, dwfl_linux_proc_report): Extend comments.
(Dwfl_Thread_Callbacks): New definition.
(struct ebl, 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.
Signed-off-by: Jan Kratochvil <jan.kratochvil(a)redhat.com>
--- 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). */
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -259,6 +259,16 @@ 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;
ELFUTILS_0.157 {
--- 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
--- 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)
--- 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);
--- /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)
--- /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;
+}
--- /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)
--- /dev/null
+++ b/libdwfl/dwfl_frame_pid.c
@@ -0,0 +1,231 @@
+/* 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_thread_state_registers_cb (const int firstreg,
+ unsigned nregs,
+ const Dwarf_Word *regs,
+ void *arg)
+{
+ Dwfl_Thread *thread = (Dwfl_Thread *) arg;
+ return INTUSE(dwfl_thread_state_registers) (thread, firstreg, nregs, regs);
+}
+
+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;
+ Dwfl_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ return ebl_set_initial_registers_tid (ebl, tid,
+ pid_thread_state_registers_cb, thread);
+}
+
+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;
+}
--- /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)
--- /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);
+}
--- 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
@@ -41,6 +41,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 +360,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 +577,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. */
+struct ebl;
+bool dwfl_attach_state (Dwfl *dwfl, struct 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);
+
+/* 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
--- 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
--- 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
@@ -300,6 +300,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)
--- 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
9 years, 11 months
FYI unwinder src/stack.c simplification
by Jan Kratochvil
Hi Mark,
I just found that with
__libdwfl_attach_state_for_core hooked into dwfl_core_file_report
and
__libdwfl_attach_state_for_pid hooked into dwfl_linux_proc_report
we no longer need most of the code as dwfl_standard_argp handles it all.
Currently it was calling dwfl_linux_proc_report / dwfl_core_file_report twice.
That is not the case for tests/backtrace.c which intentionally does not use
dwfl_standard_argp so it has to do everything by hand (=to call
dwfl_core_file_report and dwfl_linux_proc_report, nothing more).
Jan
9 years, 11 months
[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
9 years, 11 months