summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--config/eu.am1
-rw-r--r--configure.ac2
-rw-r--r--lib/ChangeLog4
-rw-r--r--lib/eu-config.h4
-rw-r--r--libdw/ChangeLog288
-rw-r--r--libdw/Makefile.am22
-rw-r--r--libdw/c++/data-values.hh126
-rw-r--r--libdw/c++/dwarf2544
-rw-r--r--libdw/c++/dwarf-knowledge.cc160
-rw-r--r--libdw/c++/dwarf_comparator587
-rw-r--r--libdw/c++/dwarf_data1600
-rw-r--r--libdw/c++/dwarf_edit398
-rw-r--r--libdw/c++/dwarf_output2794
-rw-r--r--libdw/c++/dwarf_ref_maker179
-rw-r--r--libdw/c++/dwarf_tracker737
-rw-r--r--libdw/c++/edit-values.cc89
-rw-r--r--libdw/c++/exception.cc76
-rw-r--r--libdw/c++/known.cc251
-rw-r--r--libdw/c++/line_info.cc347
-rw-r--r--libdw/c++/output-shape.cc159
-rw-r--r--libdw/c++/output-values.cc85
-rw-r--r--libdw/c++/subr.hh1476
-rw-r--r--libdw/c++/values.cc729
-rw-r--r--libdw/libdwP.h24
-rw-r--r--src/ChangeLog287
-rw-r--r--src/Makefile.am29
-rw-r--r--src/dwarfcmp.cc867
-rw-r--r--src/dwarflint-coverage.c334
-rw-r--r--src/dwarflint-coverage.h66
-rw-r--r--src/dwarflint-expected-at.cc760
-rw-r--r--src/dwarflint-expected.hh108
-rw-r--r--src/dwarflint-hl.cc300
-rw-r--r--src/dwarflint-readctx.c317
-rw-r--r--src/dwarflint-readctx.h47
-rw-r--r--src/dwarflint.c5238
-rw-r--r--src/dwarflint.h287
-rw-r--r--src/dwarfstrings.c628
-rw-r--r--src/dwarfstrings.h41
-rw-r--r--src/expr_opcodes.h154
-rw-r--r--src/readelf.c594
-rw-r--r--tests/ChangeLog106
-rw-r--r--tests/Makefile.am17
-rw-r--r--tests/dwarf-print.cc68
-rw-r--r--tests/dwarf_edit.cc66
-rw-r--r--tests/print-die.cc364
-rw-r--r--tests/print-die.hh40
-rwxr-xr-xtests/run-dwarf-attributes.sh37
-rwxr-xr-xtests/run-dwarf_edit.sh36
-rwxr-xr-xtests/run-dwarfcmp-self.sh69
-rwxr-xr-xtests/run-dwarfcmp-test-self.sh4
-rwxr-xr-xtests/run-dwarflint-self.sh53
52 files changed, 22999 insertions, 608 deletions
diff --git a/ChangeLog b/ChangeLog
index 018a1025..4b7fc717 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,14 @@
* configure.ac (LOCALEDIR, DATADIRNAME): Removed.
+2009-08-20 Roland McGrath <[email protected]>
+
+ * configure.ac (--enable-gprof): Set CXXFLAGS too.
+
+2009-01-10 Roland McGrath <[email protected]>
+
+ * configure.ac: Add AC_PROG_CXX.
+
2009-09-21 Ulrich Drepper <[email protected]>
* configure.ac: Update for more modern autoconf.
diff --git a/config/eu.am b/config/eu.am
index f259c5bb..6bf63176 100644
--- a/config/eu.am
+++ b/config/eu.am
@@ -32,6 +32,7 @@ AM_CFLAGS = -std=gnu99 -Wall -Wshadow \
$(if $($(*F)_no_Wunused),,-Wunused -Wextra) \
$(if $($(*F)_no_Wformat),-Wno-format,-Wformat=2) \
$($(*F)_CFLAGS)
+AM_CXXFLAGS = $(AM_CFLAGS:gnu99=gnu++0x)
if MUDFLAP
AM_CFLAGS += -fmudflap
diff --git a/configure.ac b/configure.ac
index 4addfec0..53cd8c05 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,6 +62,7 @@ AC_PROG_CC
AC_PROG_RANLIB
AC_PROG_YACC
AM_PROG_LEX
+AC_PROG_CXX
AC_CACHE_CHECK([for gcc with C99 support], ac_cv_c99, [dnl
old_CFLAGS="$CFLAGS"
@@ -157,6 +158,7 @@ AC_ARG_ENABLE([gprof],
AS_HELP_STRING([--enable-gprof],[build binaries with gprof support]), [use_gprof=yes], [use_gprof=no])
if test "$use_gprof" = yes; then
CFLAGS="$CFLAGS -pg"
+ CXXFLAGS="$CXXFLAGS -pg"
LDFLAGS="$LDFLAGS -pg"
fi
AM_CONDITIONAL(GPROF, test "$use_gprof" = yes)
diff --git a/lib/ChangeLog b/lib/ChangeLog
index 089747aa..f4aa0dfc 100644
--- a/lib/ChangeLog
+++ b/lib/ChangeLog
@@ -1,3 +1,7 @@
+2009-01-25 Roland McGrath <[email protected]>
+
+ * eu-config.h (__STDC_LIMIT_MACROS): Define it.
+
2010-02-15 Roland McGrath <[email protected]>
* Makefile.am: Use config/eu.am for common stuff.
diff --git a/lib/eu-config.h b/lib/eu-config.h
index f814604f..91a152bb 100644
--- a/lib/eu-config.h
+++ b/lib/eu-config.h
@@ -182,6 +182,10 @@ asm (".section predict_data, \"aw\"; .previous\n"
/* This macro is used by the tests conditionalize for standalone building. */
#define ELFUTILS_HEADER(name) <lib##name.h>
+/* C++ needs this for <stdint.h> to define UINT64_MAX et al. */
+#ifndef __STDC_LIMIT_MACROS
+# define __STDC_LIMIT_MACROS
+#endif
#ifdef SHARED
# define OLD_VERSION(name, version) \
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index a6e84572..c16e91d5 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -57,6 +57,294 @@
2009-10-15 Roland McGrath <[email protected]>
+ * c++/dwarf_output: Major copier revamp.
+
+ * c++/dwarf_comparator (dwarf_comparator::reference_match):
+ Don't bail out before calling tracker's notice_match on mismatches.
+
+2009-10-02 Roland McGrath <[email protected]>
+
+ * c++/subr.hh (subr::stackish): New template type.
+
+ * c++/dwarf_comparator: Fix overload magic to distinguish reference
+ comparisons from child comparisons.
+
+2009-10-01 Roland McGrath <[email protected]>
+
+ * c++/dwarf_tracker (dwarf_path_finder::walk_down_to): Fix sibling
+ check for short-circuit optimization to handle end iterator case.
+
+ * c++/line_info.cc (dwarf::source_file::name): Check for failure
+ return from dwarf_formstring.
+ (dwarf::source_file::to_string): Likewise.
+
+2009-09-30 Roland McGrath <[email protected]>
+
+ * c++/dwarf_tracker (dwarf_path_finder::walk::jump): New method.
+ (dwarf_ref_tracker::walk::jump): New method.
+
+ * c++/dwarf (dwarf::debug_info_entry): Make constructor from file and
+ offset public.
+ (dwarf::debug_info_entry::compile_unit): New method.
+
+ * c++/dwarf_comparator (dwarf_comparator::reference_match): Use the
+ subtracker/subcomparator for attributes too.
+
+ * c++/subr.hh (subr::sharing_stack::clear): New method.
+
+ * c++/values.cc (dwarf::attr_value::what_space):
+ Grok exprloc, flag_present, sec_offset.
+
+ * c++/values.cc (dwarf::attr_value::what_space): Fix data[48] case
+ to favor constants when expected.
+
+2009-09-25 Roland McGrath <[email protected]>
+
+ * c++/dwarf (dwarf::location_attr::operator==): Compare a singleton as
+ a list so it matches a one-element full-range list.
+
+ * c++/values.cc (dwarf::range_list::const_iterator::const_iterator):
+ For begin case, actually get the offset from the attribute!
+ (dwarf::location_attr::begin): Likewise.
+ * c++/dwarf (dwarf::range_list, dwarf::location_attr): Fix iterator
+ initializers accordingly.
+
+2009-09-21 Roland McGrath <[email protected]>
+
+ * c++/dwarf-knowledge.cc (expected_value_space): DW_AT_const_value can
+ take an address too.
+
+2009-09-08 Roland McGrath <[email protected]>
+
+ * c++/dwarf_output: Match duplicates going into the collector even
+ when they contain circular references.
+
+ * c++/dwarf_comparator (dwarf_comparator::equal_enough):
+ New class method, broken out of ...
+ * c++/dwarf_tracker (dwarf_ref_tracker::equal_enough::operator ()):
+ ... here. Call it.
+ (dwarf_path_finder, dwarf_ref_tracker): Don't assume iterators do ->.
+
+ * c++/subr.hh (subr::wrapped_input_container): New class.
+
+2009-09-01 Roland McGrath <[email protected]>
+
+ * c++/dwarf_tracker (dwarf_path_finder): Use subr::sharing_stack.
+
+ * c++/subr.hh (subr::sharing_stack): New template class.
+ * c++/dwarf (dwarf::debug_info_entry::children_type::const_iterator):
+ Use it in place fo std::stack.
+
+2009-08-31 Roland McGrath <[email protected]>
+
+ * c++/subr.hh (subr::dynamic_equality_set): New template class.
+ * c++/dwarf_output (dwarf_output_collector): Use it for _m_attr_sets.
+
+2009-08-27 Roland McGrath <[email protected]>
+
+ * c++/dwarf_output (dwarf_output::dwarf_output): Add a variant with a
+ user-supplied copier, reimplement the collector-only version.
+ (dwarf_output::copier): New constructors and destructor public.
+
+ * c++/dwarf_output (dwarf_output::copier::seen::promote_pending):
+ Notice circularity inside resolve_refs.
+
+2009-08-25 Roland McGrath <[email protected]>
+
+ * c++/dwarf_tracker: Soup up circular reference handling to handle
+ mismatched duplication vs sharing in semantically matching trees.
+ * c++/dwarf_comparator: Update user.
+
+2009-08-21 Roland McGrath <[email protected]>
+
+ * c++/subr.hh (hash<std::string>): Use elf_gnu_hash algorithm.
+
+ * c++/dwarf (dwarf::source_file::identity): New method.
+ * c++/dwarf_data (dwarf_data::source_file::identity): Likewise.
+ * c++/dwarf_output (dwarf_output::copier): Cache input string
+ and source file pointers to short-circuit repeated full hashconsing.
+
+2009-08-20 Roland McGrath <[email protected]>
+
+ * c++/values.cc (value_string): Fix constant-block case.
+
+ * c++/dwarf_comparator: Use tracker::subtracker for subcomparator.
+ Let mismatch hooks return bool that is true to keep comparing.
+ * c++/dwarf_tracker: Define subtracker.
+
+2009-08-19 Roland McGrath <[email protected]>
+
+ * c++/dwarf_tracker: Revamp derived tracker logic.
+ * c++/dwarf_comparator: Update user.
+
+ * c++/subr.hh (wrapped_input_iterator::base): New method.
+ (nothing): Kill operator (), really just nothing here.
+ (create_container): Replace post-set hook with setter hook.
+ (is): New callable template.
+ (nostream): New template, dummy for << debug hacks.
+
+ * c++/dwarf_output: Revamp construction for ref handling.
+ * c++/dwarf_data: Miscellaneous updates for dwarf_output changes.
+ * c++/dwarf_edit: Likewise.
+
+ * c++/dwarf (skipping_wrapper::const_iterator): Add hasher.
+ (debug_info_entry::raw_children_type::const_iterator): Likewise.
+ (debug_info_entry::attributes_type): Make ordered an inline
+ rather than a field.
+ (range_list, arange_list): Likewise.
+
+ * c++/data-values.hh (what_space): Use dynamic_cast rather than
+ typeid, so we accept derived classes.
+
+2009-08-16 Roland McGrath <[email protected]>
+
+ * c++/dwarf_tracker (dwarf_ref_tracker): Add missing destructor.
+
+2009-07-08 Roland McGrath <[email protected]>
+
+ * c++/dwarf-knowledge.cc (expected_value_space):
+ DW_AT_data_member_location can be constant or location.
+ * c++/values.cc (what_space): Treat only data[48] as *ptr when those
+ are expected. No other data forms can be *ptr.
+
+ * c++/output-shape.cc: New file.
+ * Makefile.am (libdwpp_a_SOURCES): Add it.
+ * c++/dwarf_output: Call add_shape method there.
+
+2009-07-06 Roland McGrath <[email protected]>
+
+ * c++/dwarf_tracker (tracker): Break out private class into ...
+ (dwarf_path_finder): ... here.
+
+2009-07-03 Roland McGrath <[email protected]>
+
+ * c++/dwarf: Give line info stubby to_string methods.
+ * c++/dwarf_data: Likewise.
+ * c++/edit-values.cc: Likewise.
+
+ * c++/dwarf: Fix location_attr iterators.
+ * c++/values.cc: Likewise.
+
+ * Makefile.am (AM_CXXFLAGS): New variable (from ../src/Makefile.am).
+
+ * c++/dwarf: Fix range_list canonicalizing comparisons.
+
+ * c++/dwarf_edit: Fix copy construction using a ref-maker.
+ * c++/dwarf_data: Updates.
+ * c++/subr.hh: Updates.
+ * c++/dwarf_ref_maker: New file.
+ * Makefile.am (pkginclude_HEADERS): Add it.
+
+2009-07-02 Roland McGrath <[email protected]>
+
+ * c++/dwarf: Get rid of VS::unit_reference.
+ * c++/dwarf-knowledge.cc: Likewise.
+ * c++/dwarf_comparator: Likewise.
+ * c++/dwarf_data: Likewise.
+ * libdw/c++/values.cc: Likewise.
+ * c++/data-values.hh: Likewise.
+
+ * c++/dwarf_output: Rewrite.
+ * c++/subr.hh: New helpers for dwarf_output/dwarf_data.
+
+ * c++/dwarf_edit: Much guts moved to ...
+ * c++/dwarf_data: ... here, new file.
+ * c++/known.cc: Update specializations.
+
+ * libdw/c++/edit-values.cc (what_space): Moved to ...
+ * c++/data-values.hh: ... here, new file.
+
+ * Makefile.am (noinst_HEADERS): Add it.
+
+ * c++/output-values.cc: New file.
+ * Makefile.am (libdwpp_a_SOURCES): Add it.
+ Makefile.am (pkginclude_HEADERS): Add dwarf_data, dwarf_output.
+
+ * c++/dwarf: Use to_string function overload, not to_string method.
+ * c++/values.cc: Define to_string specializations.
+
+2009-07-01 Roland McGrath <[email protected]>
+
+ * c++/dwarf_tracker: Major revamp for efficiency and to handle
+ circular reference chains.
+ * c++/dwarf_comparator: Tracker interface changes.
+
+2009-06-19 Roland McGrath <[email protected]>
+
+ * c++/dwarf_comparator: New file.
+ * c++/dwarf_tracker: New file.
+ * Makefile.am (pkginclude_HEADERS): Add them.
+
+ * c++/dwarf_edit: Miscellaneous fixes.
+ Support dwarf_enum, to_string for attributes.
+ * c++/known.cc: Support dwarf_edit::dwarf_enum.
+ * c++/edit-values.cc: Support to_string for attributes.
+ * c++/values.cc: Rejiggered accordingly.
+
+ * c++/dwarf-knowledge.cc (expected_value_space): Expect unit_reference
+ only for DW_TAG_imported_unit.
+
+ * c++/dwarf: Miscellaneous fixes.
+
+2009-04-02 Roland McGrath <[email protected]>
+
+ * Makefile.am (noinst_HEADERS): Add known-dwarf.h and
+ c++/dwarf-knowledge.cc here.
+
+2009-03-29 Roland McGrath <[email protected]>
+
+ * c++/exception.cc: New file.
+ * Makefile.am (libdwpp_a_SOURCES): Add it.
+
+2009-03-25 Roland McGrath <[email protected]>
+
+ * c++/dwarf, c++/values.cc: Proper dwarf_constant support.
+ * c++/dwarf_edit, c++/edit-values.cc: Likewise.
+ * c++/known.cc: New file.
+ * Makefile.am (libdwpp_a_SOURCES): Add it.
+
+2009-03-24 Petr Machata <[email protected]>
+
+ * c++/dwarf (dwarf::debug_info_entry::raw_attributes):
+ Fix iteration over attributes.
+
+2009-03-24 Roland McGrath <[email protected]>
+
+ * Makefile.am (libdwpp_a_SOURCES): New variable.
+ (lib_LIBRARIES): Add libdwpp.a to it.
+
+2009-02-26 Roland McGrath <[email protected]>
+
+ * c++/dwarf (dwarf::attr_value): Add _m_tag private member.
+ (dwarf::attr_value, dwarf::attribute): Update initializers.
+ (dwarf::raw_attributes::const_iterator): Update caller.
+ * c++/values.cc (attr_value::what_space): Use it.
+
+2009-02-01 Roland McGrath <[email protected]>
+
+ * c++/edit-values.cc: New file.
+
+2009-01-28 Roland McGrath <[email protected]>
+
+ * c++/dwarf: Add line table support.
+ * c++/values.cc: New file.
+ * c++/line_info.cc: New file.
+ * c++/subr.hh: New file.
+ * Makefile.am (pkginclude_HEADERS): Add it.
+
+2009-01-26 Roland McGrath <[email protected]>
+
+ * c++/dwarf (dwarf_output): Renamed to dwarf_edit and moved to ...
+ * c++/dwarf_edit: ... here. New file.
+ * Makefile.am (pkginclude_HEADERS): Add it.
+
+2009-01-10 Roland McGrath <[email protected]>
+
+ * c++/dwarf: New file.
+ * Makefile.am (pkginclude_HEADERS): Add it.
+
+2009-10-15 Roland McGrath <[email protected]>
+
* libdw_form.c (__libdw_form_val_len): Grok DW_FORM_sec_offset,
DW_FORM_exprloc, DW_FORM_flag_present, and DW_FORM_ref_sig8.
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 4bd0f2a8..08c3f092 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -39,7 +39,15 @@ noinst_PROGRAMS = $(noinst_LIBRARIES:_pic.a=.so)
endif
include_HEADERS = dwarf.h
-pkginclude_HEADERS = libdw.h
+pkginclude_HEADERS = libdw.h \
+ c++/subr.hh c++/dwarf_data \
+ c++/dwarf c++/dwarf_edit c++/dwarf_output \
+ c++/dwarf_tracker c++/dwarf_ref_maker \
+ c++/dwarf_comparator
+
+noinst_HEADERS = libdwP.h memory-access.h dwarf_abbrev_hash.h \
+ cfi.h encoded-value.h \
+ known-dwarf.h c++/dwarf-knowledge.cc c++/data-values.hh
libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
dwarf_getpubnames.c dwarf_getabbrev.c dwarf_tag.c \
@@ -83,6 +91,15 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
dwarf_getcfi.c dwarf_getcfi_elf.c dwarf_cfi_end.c \
dwarf_aggregate_size.c
+# XXX need to figure out C++ dso crapola
+lib_LIBRARIES += libdwpp.a
+libdwpp_a_SOURCES = c++/values.cc \
+ c++/exception.cc \
+ c++/known.cc \
+ c++/line_info.cc \
+ c++/edit-values.cc \
+ c++/output-values.cc c++/output-shape.cc
+
if MAINTAINER_MODE
BUILT_SOURCES = $(srcdir)/known-dwarf.h
MAINTAINERCLEANFILES = $(srcdir)/known-dwarf.h
@@ -124,9 +141,6 @@ endif
libdw_a_LIBADD = $(addprefix ../libdwfl/,$(shell $(AR) t ../libdwfl/libdwfl.a))
-noinst_HEADERS = libdwP.h memory-access.h dwarf_abbrev_hash.h \
- cfi.h encoded-value.h
-
EXTRA_DIST = libdw.map
MOSTLYCLEANFILES = $(am_libdw_pic_a_OBJECTS) libdw.so.$(VERSION)
diff --git a/libdw/c++/data-values.hh b/libdw/c++/data-values.hh
new file mode 100644
index 00000000..5f109042
--- /dev/null
+++ b/libdw/c++/data-values.hh
@@ -0,0 +1,126 @@
+/* elfutils::dwarf_data common internal templates.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include "dwarf_data"
+
+template<typename v, typename subtype>
+static inline bool is_a (const typename v::value_dispatch *value)
+{
+ return dynamic_cast<const subtype *> (value) != NULL;
+}
+
+namespace elfutils
+{
+ template<class impl, typename v>
+ dwarf::value_space
+ dwarf_data::attr_value<impl, v>::what_space () const
+ {
+ if (is_a<v, typename v::value_flag> (_m_value))
+ return dwarf::VS_flag;
+ if (is_a<v, typename v::value_dwarf_constant> (_m_value))
+ return dwarf::VS_dwarf_constant;
+ if (is_a<v, typename v::value_reference> (_m_value))
+ return dwarf::VS_reference;
+ if (is_a<v, typename v::value_lineptr> (_m_value))
+ return dwarf::VS_lineptr;
+ if (is_a<v, typename v::value_macptr> (_m_value))
+ return dwarf::VS_macptr;
+ if (is_a<v, typename v::value_rangelistptr> (_m_value))
+ return dwarf::VS_rangelistptr;
+ if (is_a<v, typename v::value_identifier> (_m_value))
+ return dwarf::VS_identifier;
+ if (is_a<v, typename v::value_string> (_m_value))
+ return dwarf::VS_string;
+ if (is_a<v, typename v::value_source_file> (_m_value))
+ return dwarf::VS_source_file;
+ if (is_a<v, typename v::value_source_line> (_m_value))
+ return dwarf::VS_source_line;
+ if (is_a<v, typename v::value_source_column> (_m_value))
+ return dwarf::VS_source_column;
+ if (is_a<v, typename v::value_address> (_m_value))
+ return dwarf::VS_address;
+ if (is_a<v, typename v::value_constant> (_m_value)
+ || is_a<v, typename v::value_constant_block> (_m_value))
+ return dwarf::VS_constant;
+ if (is_a<v, typename v::value_location> (_m_value))
+ return dwarf::VS_location;
+
+ throw std::logic_error ("attr_value has no known value_space!");
+ }
+
+ template<typename attr_pair>
+ static inline std::string
+ attribute_string (const attr_pair &attr)
+ {
+ std::string result = dwarf::attributes::name (attr.first);
+ result += "=";
+ result += attr.second.to_string ();
+ return result;
+ }
+
+ template<typename die_type>
+ std::string
+ die_string (const die_type &die)
+ {
+ std::string result ("<");
+ result += dwarf::tags::name (die.tag ());
+
+ typename die_type::attributes_type::const_iterator name_attr
+ = die.attributes ().find (::DW_AT_name);
+ if (name_attr != die.attributes ().end ())
+ {
+ result += " ";
+ result += to_string (*name_attr);
+ }
+
+ result += die.has_children () ? ">" : "/>";
+ return result;
+ }
+
+};
diff --git a/libdw/c++/dwarf b/libdw/c++/dwarf
new file mode 100644
index 00000000..c698c1e7
--- /dev/null
+++ b/libdw/c++/dwarf
@@ -0,0 +1,2544 @@
+/* -*- C++ -*- interfaces for libdw.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifndef _ELFUTILS_DWARF
+#define _ELFUTILS_DWARF 1
+
+#include "libdw.h"
+#include "dwarf.h"
+#include "subr.hh"
+#include <stdexcept>
+
+#include <list>
+#include <map>
+#include <set>
+#include <vector>
+#include <algorithm>
+#include <functional>
+#include <tr1/unordered_map>
+
+/* Abstractly, one DWARF object file consists of a few containers.
+ (We omit .debug_frame for now. It does not interact with the others.)
+
+ 1. list of compilation units (.debug_info)
+ 2. map of PC ranges to CU (.debug_aranges)
+ 3. map of global names to CU+DIE (.debug_pubnames)
+ 4. map of type names to CU+DIE (.debug_pubtypes)
+
+ These maps refer to the CUs in .debug_info and optimize lookups
+ compared to simple iteration.
+
+ A compile_unit is a debug_info_entry.
+ A debug_info_entry consists of a tag (int/enum), and two containers:
+ children and attributes. The attributes are an unordered map of name
+ (int/enum) to attribute value (complex variant record). Children are
+ in an ordered list, each also a debug_info_entry.
+
+ dwarf.compile_units () works like list<compile_unit>
+ -> compile_unit : debug_info_entry
+ .attributes () like unordered_map<int, attr_value>
+ .children () works like list<debug_info_entry>
+ -> debug_info_entry
+ .attributes ()
+ .children ()
+
+ A compile_unit is not deeply special, it's just a debug_info_entry.
+ It has its own class just for some convenience methods that only
+ make sense for a compile_unit DIE.
+
+ This is the "logical" view of the file, grafting and eliding parts of the
+ raw information that are purely the structural elements of DWARF and not
+ part of the abstract semantics. In the file reader (elfutils::dwarf),
+ these containers form a layer above the raw containers that expose the
+ file data directly (as the libdw C interfaces do).
+
+ dwarf.raw_compile_units () works like list<compile_unit>
+ -> compile_unit : debug_info_entry
+ .raw_attributes () like unordered_map<int, attr_value>
+ .raw_children () works like list<debug_info_entry>
+ -> debug_info_entry
+ .raw_attributes ()
+ .raw_children ()
+
+ compile_units () elides DW_TAG_partial_unit members,
+ raw_compile_units () includes them.
+
+ attributes () elides DW_AT_sibling, raw_attributes () includes it.
+
+ raw_children () reports DW_TAG_imported_unit as any other child.
+ children () flattens imported units into the containing list.
+
+ The == and != comparisons for dwarf and debug_info_entry objects compare
+ their logical containers, not the raw containers. The comparisons are
+ defined via templates, so you can compare elfutils::dwarf with any other
+ class that implements the same structure of containers with input iterators.
+
+ The elfutils::dwarf class and its inner classes form a thin, read-only
+ layer of virtual containers that ideally could inline away entirely to
+ calls into the C libdw API and small amounts of stack storage. The tree
+ of objects described above never exists in memory in its entirety. The
+ objects are constructed on the fly in each call to a container method.
+
+ See the dwarf_edit and dwarf_output headers for other classes that are
+ template-compatible with the "logical view" interface above, but do not
+ support any of the "raw" container variants. These == and != comparisons
+ are template-driven too, so all different classes can be compared.
+
+ The output classes have template-driven copy constructors, so they can be
+ copied from files or substructures of the elfutils::dwarf input classes.
+
+ ------ XXX to be done: more file-level containers
+
+ input side only:
+
+ units_by_addr : map<pair<begin,end>, CU> and map<address, CU>
+ use dwarf_getarange_addr
+
+ pub{names,types} : map<string, debug_info_entry> (across all CUs)
+
+ output too:
+
+ pubnames_map : map<string, debug_info_entry>
+ pub{names,types}_units : map<compile_unit, pubnames_map>
+ too much lang knowledge to autogenerate for now,
+ output will do it explicitly
+
+ */
+
+// DWARF reader interfaces: front end to <libdw.h> routines
+namespace elfutils
+{
+ template<typename type>
+ inline std::string to_string (const type &item)
+ {
+ return item.to_string ();
+ }
+
+ template<typename key1, typename value1, class pair2>
+ inline bool operator== (const std::pair<key1, value1> &a, const pair2 &b)
+ {
+ return a.first == b.first && a.second == b.second;
+ }
+
+ // Used like std::vector<elt>, but is really just a simple fixed array.
+ template<typename elt>
+ class const_vector
+ {
+ private:
+ size_t _m_size;
+ const elt *_m_array;
+
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef elt value_type;
+ typedef const elt *const_iterator;
+
+ const_vector ()
+ : _m_size (0), _m_array (NULL) {}
+ const_vector (const const_vector &v)
+ : _m_size (v._m_size), _m_array (v._m_array) {}
+ const_vector (const elt *start, const elt *stop)
+ : _m_size (stop - start), _m_array (start) {}
+ const_vector (const ::Dwarf_Block &b)
+ : _m_size (b.length), _m_array (reinterpret_cast<const elt *> (b.data)) {}
+
+ inline const_vector &operator= (const const_vector &v)
+ {
+ _m_size = v._m_size;
+ _m_array = v._m_array;
+ return *this;
+ }
+
+ inline size_t size () const
+ {
+ return _m_size;
+ }
+ inline bool empty () const
+ {
+ return _m_size == 0;
+ }
+
+ const_iterator begin () const
+ {
+ return _m_array;
+ }
+ const_iterator end () const
+ {
+ return &_m_array[_m_size];
+ }
+
+ template<typename other>
+ inline operator other () const
+ {
+ return other (begin (), end ());
+ }
+
+ template<typename vec>
+ inline bool operator== (const vec &other) const
+ {
+ return (other.size () == size ()
+ && std::equal (begin (), end (), other.begin ()));
+ }
+ template<typename vec>
+ inline bool operator!= (const vec &other) const
+ {
+ return !(*this == other);
+ }
+
+ };
+
+ // One DWARF object file.
+ class dwarf
+ {
+ private:
+ static const char *known_tag (int);
+ static const char *known_attribute (int);
+
+ public:
+ typedef subr::known<__typeof ("DW_TAG_"), known_tag> tags;
+ typedef subr::known<__typeof ("DW_AT_"), known_attribute> attributes;
+
+ template<typename attribute>
+ static inline std::string attribute_name (const attribute &attr)
+ {
+ int code = attr.first;
+ return attributes::name (code);
+ }
+
+ template<int key>
+ class known_enum
+ {
+ public:
+ static size_t prefix_length ();
+ static const char *identifier (int);
+ inline static const char *name (int value)
+ {
+ const char *id = identifier (value);
+ return id != NULL ? id + prefix_length () : NULL;
+ }
+
+ // XXX perhaps have iterator/lookup methods like a read-only map?
+ };
+
+ typedef known_enum< ::DW_AT_producer> forms;
+ typedef known_enum< ::DW_AT_location> ops;
+
+ private:
+ static void throw_libdw (::Dwarf *dw); // XXX raises (...)
+ static void throw_libdw (::Dwarf_CU *); // XXX raises (...)
+
+ inline void xif (bool fail) const
+ {
+ if (unlikely (fail))
+ throw_libdw (_m_dw);
+ }
+ static inline void xif (::Dwarf_CU *cu, bool fail)
+ {
+ if (unlikely (fail))
+ throw_libdw (cu);
+ }
+
+ static inline void xif (const ::Dwarf_Attribute *attr, bool fail)
+ {
+ xif (attr->cu, fail);
+ }
+ static inline void xif (const ::Dwarf_Die *die, bool fail)
+ {
+ xif (die->cu, fail);
+ }
+
+ template<typename raw, typename raw_element, typename element,
+ bool skip (const raw_element &)>
+ class skipping_wrapper
+ {
+ protected:
+ typedef typename raw::const_iterator raw_iterator;
+
+ raw _m_raw;
+
+ protected:
+ inline skipping_wrapper (const raw &r) : _m_raw (r) {}
+
+ public:
+ inline skipping_wrapper (const skipping_wrapper &w) : _m_raw (w._m_raw) {}
+
+ /*
+ iterator: wraps raw iterator, skips DW_AT_sibling
+ size/empty: search for DW_AT_sibling, adjust raw size
+ */
+
+ class const_iterator
+ : public std::iterator<std::input_iterator_tag, element>
+ {
+ friend class skipping_wrapper<raw, raw_element, element, skip>;
+ private:
+ raw_iterator _m_raw;
+ const raw_iterator _m_end;
+
+ inline void jiggle ()
+ {
+ while (_m_raw != _m_end && unlikely (skip (*_m_raw)))
+ ++_m_raw;
+ }
+
+ public:
+ inline const_iterator ()
+ : _m_raw (), _m_end (raw::end ())
+ {
+ }
+
+ const_iterator (const const_iterator &i)
+ : _m_raw (i._m_raw), _m_end (i._m_end) {}
+
+ // Start at the raw position and skip as necessary.
+ const_iterator (const raw_iterator &begin, const raw_iterator &end)
+ : _m_raw (begin), _m_end (end)
+ {
+ jiggle ();
+ }
+
+ inline const_iterator &operator= (const const_iterator &other)
+ {
+ _m_raw = other._m_raw;
+ return *this;
+ }
+
+ inline bool operator== (const const_iterator &other) const
+ {
+ return _m_raw == other._m_raw;
+ }
+ inline bool operator!= (const const_iterator &other) const
+ {
+ return !(*this == other);
+ }
+
+ struct hasher : public std::unary_function<const_iterator, size_t>
+ {
+ size_t operator () (const const_iterator &i) const
+ {
+ return subr::hash_this (i._m_raw);
+ }
+ };
+
+ inline const_iterator &operator++ () // prefix
+ {
+ ++_m_raw;
+ jiggle ();
+ return *this;
+ }
+ inline const_iterator operator++ (int) // postfix
+ {
+ const_iterator prev = *this;
+ ++*this;
+ return prev;
+ }
+
+ inline element operator* () const
+ {
+ return static_cast<element> (*_m_raw);
+ }
+ };
+
+ inline const_iterator begin () const
+ {
+ return const_iterator (_m_raw.begin (), _m_raw.end ());
+ }
+ inline const_iterator end () const
+ {
+ const raw_iterator raw_end = _m_raw.end ();
+ return const_iterator (raw_end, raw_end);
+ }
+ };
+
+ public:
+ /*
+ getstring
+ */
+
+ class attribute;
+ class attr_value;
+ class location_attr;
+ class range_list;
+ class ranges;
+ class line_info_table;
+ class directory_table;
+ class file_table;
+ class line_table;
+ class line_entry;
+ class dwarf_enum;
+
+ class debug_info_entry
+ {
+ private:
+ ::Dwarf_Die _m_die;
+ inline ::Dwarf_Die *thisdie () const
+ {
+ return const_cast< ::Dwarf_Die *> (&_m_die);
+ }
+
+ friend class dwarf;
+ friend class attr_value;
+ protected:
+
+ inline void xif (bool fail) const
+ {
+ dwarf::xif (_m_die.cu, fail);
+ }
+
+ inline debug_info_entry ()
+ {
+ memset (&_m_die, 0, sizeof _m_die);
+ }
+
+ public:
+ debug_info_entry (const debug_info_entry &die) : _m_die (die._m_die) {}
+
+ inline debug_info_entry (const dwarf &dw, ::Dwarf_Off off)
+ {
+ dw.xif (::dwarf_offdie (dw._m_dw, off, &_m_die) == NULL);
+ }
+
+ /* Return the compile_unit entry containing this entry.
+ Note this might be a DW_TAG_partial_unit. */
+ inline debug_info_entry compile_unit () const
+ {
+ debug_info_entry result;
+ xif (::dwarf_diecu (thisdie (), &result._m_die, NULL, NULL) == NULL);
+ return result;
+ }
+
+ // Containers, see class definitions below.
+ class raw_children_type;
+ inline raw_children_type raw_children () const;
+ class raw_attributes_type;
+ raw_attributes_type raw_attributes () const;
+ class children_type;
+ inline children_type children () const;
+ class attributes_type;
+ attributes_type attributes () const;
+
+ class const_pointer;
+
+ inline std::string to_string () const;
+
+ inline int tag () const
+ {
+ int t = ::dwarf_tag (thisdie ());
+ xif (t <= 0);
+ return t;
+ }
+
+ bool has_children () const
+ {
+ int has = ::dwarf_haschildren (thisdie ());
+ xif (has < 0);
+ return has != 0;
+ }
+
+ /*
+ const char *tag_name () const
+ const_string tag_name () const // "name" or "0x123"
+ */
+
+ template<typename die>
+ bool operator== (const die &other) const
+ {
+ return (tag () == other.tag ()
+ && attributes () == other.attributes ()
+ && children () == other.children ());
+ }
+ template<typename die>
+ bool operator!= (const die &other) const
+ {
+ return !(*this == other);
+ }
+
+ ::Dwarf_Off offset () const
+ {
+ return ::dwarf_dieoffset (thisdie ());
+ }
+
+ inline const dwarf::ranges ranges () const
+ {
+ return dwarf::ranges (*this);
+ }
+
+ /* This is an identity pointer that only matches the very same
+ DIE in the very same file (same opened Dwarf instance). */
+ typedef uintptr_t identity_type;
+ inline identity_type identity () const
+ {
+ return (uintptr_t) _m_die.addr;
+ }
+
+ ::Dwarf_Off cost () const;
+ };
+
+ // Container for raw list of child DIEs, intended to be a compatible with
+ // a read-only, unidirectional subset of std::list<debug_info_entry>.
+ class debug_info_entry::raw_children_type
+ {
+ friend class debug_info_entry;
+ private:
+ const debug_info_entry &_m_die;
+
+ protected:
+ inline raw_children_type (const debug_info_entry &die) : _m_die (die) {}
+
+ public:
+ typedef debug_info_entry value_type;
+
+ inline raw_children_type (const raw_children_type &c)
+ : _m_die (c._m_die)
+ {}
+
+ bool empty () const
+ {
+ return begin () == end ();
+ }
+
+ class const_iterator
+ : public std::iterator<std::input_iterator_tag, debug_info_entry>
+ {
+ friend class debug_info_entry;
+ friend class attr_value;
+ private:
+ debug_info_entry _m_die;
+
+ inline const_iterator ()
+ {}
+
+ inline const_iterator (const debug_info_entry &parent)
+ {
+ int result = ::dwarf_child (parent.thisdie (), &_m_die._m_die);
+ parent.xif (result < 0);
+ }
+
+ // Construct from a reference attribute.
+ inline const_iterator (Dwarf_Attribute *attr)
+ {
+ dwarf::xif (attr, ::dwarf_formref_die (attr, &_m_die._m_die) == NULL);
+ }
+
+ public:
+ inline const_iterator (const const_iterator &i) : _m_die (i._m_die) {}
+
+ inline const debug_info_entry &operator* () const
+ {
+ if (unlikely (_m_die._m_die.addr == NULL))
+ throw std::runtime_error ("dereferencing end iterator");
+ return _m_die;
+ }
+ inline const debug_info_entry *operator-> () const
+ {
+ return &(operator* ());
+ }
+
+ inline const_iterator &operator= (const const_iterator &other)
+ {
+ _m_die = other._m_die;
+ return *this;
+ }
+
+ // Assign directly from a DIE, as if "taking its address".
+ inline const_iterator &operator= (const debug_info_entry &die)
+ {
+ _m_die = die;
+ return *this;
+ }
+
+ inline bool operator== (const const_iterator &other) const
+ {
+ return _m_die._m_die.addr == other._m_die._m_die.addr;
+ }
+ inline bool operator!= (const const_iterator &other) const
+ {
+ return !(*this == other);
+ }
+
+ struct hasher : public std::unary_function<const_iterator, size_t>
+ {
+ size_t operator () (const const_iterator &i) const
+ {
+ return subr::hash_this ((uintptr_t) i._m_die._m_die.addr);
+ }
+ };
+
+ inline const_iterator &operator++ () // prefix
+ {
+ int result = ::dwarf_siblingof (&_m_die._m_die, &_m_die._m_die);
+ _m_die.xif (result < 0);
+ if (result > 0) // Hit the end.
+ *this = const_iterator ();
+ return *this;
+ }
+ inline const_iterator operator++ (int) // postfix
+ {
+ const_iterator prev = *this;
+ ++*this;
+ return prev;
+ }
+ };
+ const_iterator begin () const
+ {
+ return const_iterator (_m_die);
+ }
+ static inline const_iterator end ()
+ {
+ return const_iterator ();
+ }
+
+ template<typename other_children>
+ bool operator== (const other_children &other) const
+ {
+ return subr::container_equal (*this, other);
+ }
+ template<typename other_children>
+ bool operator!= (const other_children &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ // Container for list of raw attributes as (name, value) pairs,
+ // intended to be compatible with a read-only, unidirectional
+ // subset of std::list<std::pair<int, attr_value>>.
+ class debug_info_entry::raw_attributes_type
+ {
+ friend class debug_info_entry;
+ private:
+ const debug_info_entry &_m_die;
+
+ raw_attributes_type (const debug_info_entry &die)
+ : _m_die (die)
+ {}
+
+ public:
+ typedef attribute value_type;
+
+ inline raw_attributes_type (const raw_attributes_type &a)
+ : _m_die (a._m_die)
+ {}
+
+ size_t size () const;
+ inline bool empty () const
+ {
+ return size () == 0;
+ }
+
+ class const_iterator
+ : public std::iterator<std::input_iterator_tag, attribute>
+ {
+ friend class raw_attributes_type;
+ private:
+ debug_info_entry _m_die;
+ ptrdiff_t _m_offset; // Offset for next iteration in dwarf_getattrs.
+ ::Dwarf_Attribute _m_attr;
+
+ /* We get called up to twice per iteration. The first time, we
+ store *ATTR in the instance variable and return DWARF_CB_OK so
+ that we might be called again. The second time, we return
+ DWARF_CB_ABORT so that the iteration will stop at the next
+ attribute's offset. */
+ static int getattrs_callback (Dwarf_Attribute *attr, void *arg)
+ {
+ const_iterator *i = static_cast<const_iterator *> (arg);
+ if (i->_m_attr.valp == NULL)
+ {
+ i->_m_attr = *attr;
+ return DWARF_CB_OK;
+ }
+ return DWARF_CB_ABORT;
+ }
+
+ inline const_iterator (const debug_info_entry &die, ptrdiff_t offset)
+ : _m_die (die), _m_offset (offset), _m_attr ()
+ {}
+
+ inline const_iterator (ptrdiff_t offset)
+ : _m_die (), _m_offset (offset), _m_attr ()
+ {}
+
+ public:
+ // Default constructor: invalid for anything but operator=.
+ inline const_iterator ()
+ : _m_die (), _m_offset (-1), _m_attr ()
+ {}
+
+ inline const_iterator (const const_iterator &i)
+ : _m_die (i._m_die), _m_offset (i._m_offset), _m_attr (i._m_attr)
+ {}
+
+ inline const_iterator &operator= (const const_iterator &other)
+ {
+ _m_die = other._m_die;
+ _m_offset = other._m_offset;
+ _m_attr = other._m_attr;
+ return *this;
+ }
+
+ inline bool operator== (const const_iterator &other) const
+ {
+ return (_m_die._m_die.addr == other._m_die._m_die.addr
+ && _m_attr.valp == other._m_attr.valp);
+ }
+ inline bool operator!= (const const_iterator &other) const
+ {
+ return !(*this == other);
+ }
+
+ inline const_iterator &operator++ () // prefix
+ {
+ _m_attr.valp = NULL;
+ int result = ::dwarf_getattrs (&_m_die._m_die, &getattrs_callback,
+ (void *) this, _m_offset);
+ _m_die.xif (result < 0);
+ _m_offset = result;
+ if (result == 1)
+ // End iterators have no live pointers.
+ _m_die._m_die.addr = NULL;
+ return *this;
+ }
+ inline const_iterator operator++ (int) // postfix
+ {
+ const_iterator prev = *this;
+ ++*this;
+ return prev;
+ }
+
+ inline attribute operator* () const
+ {
+ if (unlikely (_m_attr.valp == NULL))
+ throw std::runtime_error ("dereferencing end iterator");
+ return attribute (_m_die, _m_attr);
+ }
+ };
+ inline const_iterator begin () const
+ {
+ const_iterator i = const_iterator (_m_die, 0);
+ return ++i;
+ }
+ static inline const_iterator end ()
+ {
+ return const_iterator (1);
+ }
+
+ // XXX can do faster internal (?)
+ inline const_iterator find (int name) const
+ {
+ const_iterator i = begin ();
+ while (i != end () && (*i).first != name)
+ ++i;
+ return i;
+ }
+ };
+
+ // Container for list of child DIEs, intended to be a compatible with
+ // a read-only, unidirectional subset of std::list<debug_info_entry>.
+ // Same as raw_children, but flattens DW_TAG_imported_unit children.
+ class debug_info_entry::children_type
+ : public debug_info_entry::raw_children_type
+ {
+ friend class debug_info_entry;
+ private:
+
+ inline children_type (const debug_info_entry &die)
+ : raw_children_type::raw_children_type (die) {}
+
+ public:
+ typedef debug_info_entry value_type;
+
+ inline children_type (const children_type &c)
+ : raw_children_type (c)
+ {}
+
+ class const_iterator
+ : public std::iterator<std::input_iterator_tag, debug_info_entry>
+ {
+ friend class children_type;
+ private:
+
+ typedef raw_children_type::const_iterator raw_iterator;
+ subr::stackish<raw_iterator> _m_stack;
+
+ /* Push and pop until _m_stack.top () == raw_children_type::end ()
+ or it's looking at a DIE other than DW_TAG_imported_unit. */
+ inline void jiggle ()
+ {
+ while (true)
+ {
+ const raw_iterator &i = _m_stack.const_top ();
+
+ if (i == raw_children_type::end ())
+ {
+ /* We're at the end of this raw DIE.
+ Pop out to the iterator on the importing unit. */
+ _m_stack.pop ();
+
+ if (_m_stack.empty ())
+ // That was the outermost unit, this is the end.
+ break;
+
+ continue;
+ }
+
+ if (i->tag () == ::DW_TAG_imported_unit)
+ // We have an imported unit. Look at its referent.
+ _m_stack.push (i->attributes ().at (::DW_AT_import)
+ .reference ()->raw_children ().begin ());
+ else
+ // This is some other DIE. Iterate on it.
+ break;
+ }
+ }
+
+ public:
+ inline const_iterator ()
+ : _m_stack ()
+ {}
+
+ inline const_iterator (const raw_iterator &i)
+ {
+ _m_stack.push (i);
+ jiggle ();
+ }
+
+ inline const_iterator (const const_iterator &i)
+ : _m_stack (i._m_stack)
+ {}
+
+ // Construct directly from a DIE, as if "taking its address".
+ inline const_iterator (const debug_info_entry &die)
+ {
+ raw_iterator it;
+ it = die;
+ _m_stack.push (it);
+ jiggle ();
+ }
+
+ inline const_iterator &operator= (const const_iterator &other)
+ {
+ _m_stack = other._m_stack;
+ return *this;
+ }
+
+ inline bool operator== (const const_iterator &other) const
+ {
+ return _m_stack == other._m_stack;
+ }
+ inline bool operator!= (const const_iterator &other) const
+ {
+ return !(*this == other);
+ }
+
+ inline const debug_info_entry &operator* () const
+ {
+ return *_m_stack.top ();
+ }
+ inline const debug_info_entry *operator-> () const
+ {
+ return &(operator* ());
+ }
+
+ inline const_iterator &operator++ () // prefix
+ {
+ ++_m_stack.top ();
+ jiggle ();
+ return *this;
+ }
+ inline const_iterator operator++ (int) // postfix
+ {
+ const_iterator prev = *this;
+ ++*this;
+ return prev;
+ }
+ };
+
+ // Actually always const.
+ typedef const_iterator iterator;
+
+ const_iterator begin () const
+ {
+ return const_iterator (raw_children_type::begin ());
+ }
+ const_iterator end () const
+ {
+ return const_iterator (raw_children_type::end ());
+ }
+
+ template<typename other_children>
+ bool operator== (const other_children &other) const
+ {
+ return subr::container_equal (*this, other);
+ }
+ template<typename other_children>
+ bool operator!= (const other_children &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ class debug_info_entry::const_pointer
+ : public debug_info_entry::children_type::const_iterator
+ {};
+
+ private:
+ static inline bool skip_sibling (const attribute &attr)
+ {
+ return attr.first == ::DW_AT_sibling;
+ }
+
+ // Circumvent C++ namespace lookup.
+ typedef class debug_info_entry::raw_attributes_type die_raw_attrs;
+ typedef skipping_wrapper<die_raw_attrs, attribute, attribute, skip_sibling>
+ attributes_base;
+
+ public:
+ // Container for attributes, indexed by name, intended to be compatible
+ // with a read-only subset of std::unordered_map<int, attr_value>.
+ // This excludes DW_AT_sibling.
+ class debug_info_entry::attributes_type : public attributes_base
+ {
+ friend class dwarf;
+ private:
+ inline attributes_type (const raw_attributes_type &raw)
+ : attributes_base (raw) {}
+
+ public:
+ typedef int key_type;
+ typedef attr_value mapped_type;
+ typedef attribute value_type;
+
+ static inline bool ordered ()
+ {
+ return false;
+ }
+
+ inline attributes_type (const attributes_type &a)
+ : attributes_base (a)
+ {}
+
+ typedef attributes_base::const_iterator const_iterator;
+
+ /*
+ iterator: wraps raw_attributes iterator, skips DW_AT_sibling
+ size/empty: search for DW_AT_sibling, adjust raw_attributes size
+ */
+
+ inline const_iterator find (int name) const
+ {
+ if (unlikely (name == ::DW_AT_sibling))
+ return end ();
+ return const_iterator (_m_raw.find (name), _m_raw.end ());
+ }
+
+ inline const attr_value at (int name)
+ {
+ const_iterator i = find (name);
+ if (unlikely (i == end ()))
+ throw std::out_of_range ("XXX");
+ return (*i).second;
+ }
+ inline const attr_value operator[] (int name)
+ {
+ return at (name);
+ }
+
+ // We are rvalue-coercible into a std::map, which is sorted by name.
+ inline operator std::map<int, attr_value> () const
+ {
+ return std::map<int, attr_value> (begin (), end ());
+ }
+ /*
+ template<typename attrs>
+ inline operator attrs () const
+ {
+ return attrs (begin (), end ());
+ }
+ */
+
+ template<typename attrs>
+ bool operator== (const attrs &other) const
+ {
+ /* Our container is unordered (i.e., in file order). A set of
+ attributes is conceptually equal if all the pairs match,
+ regardless of the order. But the container_equal algorithm will
+ compare corresponding elements in order. So we need an ordered
+ map of our attributes for the comparison. */
+ const std::map<int, attr_value> mine = *this;
+ const std::map<int, typename attrs::mapped_type> his = other;
+ return mine.size () == his.size () && subr::container_equal (mine, his);
+ }
+
+ template<typename attrs>
+ bool operator!= (const attrs &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ class compile_unit : public debug_info_entry
+ {
+ public:
+ inline compile_unit (const debug_info_entry &die)
+ : debug_info_entry (die) {}
+
+ // Fetch the CU's DW_AT_stmt_list.
+ const line_info_table line_info () const;
+
+ // Convenience methods for line_info_table sub-containers.
+ inline const directory_table include_directories () const
+ {
+ return line_info ().include_directories ();
+ }
+ inline const file_table files () const
+ {
+ return line_info ().files ();
+ }
+ inline const line_table lines () const
+ {
+ return line_info ().lines ();
+ }
+
+ /*
+ containers/iterators:
+
+ XXX macros
+
+ abbrevs (punt)
+
+ */
+ };
+
+ // These are the kinds of values that attributes can have.
+ enum value_space
+ {
+ // These spaces refer purely to DWARF concepts.
+ VS_flag, // Boolean.
+ VS_dwarf_constant, // Known DW_X_* space of integer constants.
+ VS_discr_list, // Block as used for DW_AT_discr_list.
+ VS_reference, // Pointer to another DIE.
+ VS_lineptr, // Pointer into .debug_line section.
+ VS_macptr, // Pointer into .debug_macinfo section.
+ VS_rangelistptr, // Pointer into .debug_ranges section.
+
+ // These spaces refer to textual details of the program source.
+ VS_identifier, // String, identifier in source language.
+ VS_string, // String, miscellaneous use.
+ VS_source_file, // Source file, string or index into file table.
+ VS_source_line, // Line number in source file.
+ VS_source_column, // Column number in source file.
+
+ // These spaces refer to target-format values in the debuggee.
+ VS_address, // Address constant.
+ VS_constant, // Other constant, integer or in target formats.
+ VS_location, // Location expression or location list.
+ };
+
+ /* A source file can be just a file name. When represented in the
+ .debug_line file table, it can also have a modtime and a file size.
+ If the modtime or size stored is zero, it doesn't count. */
+ class source_file
+ {
+ friend class attr_value;
+ friend class file_table;
+ friend class line_entry;
+ private:
+ ::Dwarf_Attribute _m_attr;
+ inline ::Dwarf_Attribute *thisattr () const
+ {
+ return const_cast< ::Dwarf_Attribute *> (&_m_attr);
+ }
+
+ source_file (const Dwarf_Attribute &attr) : _m_attr (attr) {}
+
+ public:
+ std::string to_string () const;
+
+ const char *name () const;
+ ::Dwarf_Word mtime () const;
+ ::Dwarf_Word size () const;
+
+ template<typename other_file>
+ bool operator== (const other_file &other) const
+ {
+ if (mtime () != 0)
+ {
+ ::Dwarf_Word other_mtime = other.mtime ();
+ if (other_mtime != 0 && other_mtime != mtime ())
+ return false;
+ }
+ if (size () != 0)
+ {
+ ::Dwarf_Word other_size = other.size ();
+ if (other_size != 0 && other_size != size ())
+ return false;
+ }
+ return subr::name_equal<typeof (other.name ())> () (name (),
+ other.name ());
+ }
+ template<typename other_file>
+ inline bool operator!= (const other_file &other) const
+ {
+ return !(*this == other);
+ }
+
+ /* Return a value unique to us while we're in memory.
+ This is a stable pointer into the Dwarf_Files data. */
+ inline uintptr_t identity () const
+ {
+ return (uintptr_t) name ();
+ }
+ };
+
+ // This describes the value of an attribute.
+ class attr_value
+ {
+ friend class attribute;
+ friend class location_attr;
+ friend class range_list;
+ friend class dwarf_enum;
+ private:
+ const int _m_tag;
+ ::Dwarf_Attribute _m_attr;
+ inline ::Dwarf_Attribute *thisattr () const
+ {
+ return const_cast< ::Dwarf_Attribute *> (&_m_attr);
+ }
+ inline int whatattr () const
+ {
+ return ::dwarf_whatattr (thisattr ());
+ }
+
+ attr_value (int tag, const ::Dwarf_Attribute &attr)
+ : _m_tag (tag), _m_attr (attr) {}
+
+ inline bool same (const attr_value &other) const
+ {
+ return _m_attr.valp == other._m_attr.valp;
+ }
+
+ public:
+ // not copyable, don't worry about ref lifetime(?)
+ // attr_value (const attr_value &v) : _m_attr (v.attr) {}
+
+ value_space what_space () const;
+ inline std::string to_string () const;
+
+ // Return an iterator on which * will yield the referent debug_info_entry.
+ inline debug_info_entry::children_type::const_iterator
+ reference () const
+ {
+ return (debug_info_entry::raw_children_type::const_iterator
+ (thisattr ()));
+ }
+
+ // XXX reloc, dwfl
+ ::Dwarf_Addr address () const;
+
+ bool flag () const;
+
+ const location_attr location () const;
+
+ const char *string () const;
+ inline const char *identifier () const
+ {
+ return string ();
+ }
+
+ const dwarf::source_file source_file () const;
+ inline unsigned int source_line () const
+ {
+ return constant ();
+ }
+ inline unsigned int source_column () const
+ {
+ return constant ();
+ }
+
+ // XXX reloc
+ ::Dwarf_Word constant () const;
+ ::Dwarf_Sword signed_constant () const;
+ const_vector<uint8_t> constant_block () const;
+ bool constant_is_integer () const;
+
+ inline const dwarf_enum dwarf_constant () const
+ {
+ return dwarf_enum (*this);
+ }
+
+ inline const range_list ranges () const
+ {
+ return range_list (*this);
+ }
+
+ const line_info_table line_info () const;
+
+ // XXX macptr
+
+ template<typename value>
+ inline bool operator== (const value &other) const
+ {
+ const value_space what = what_space ();
+ if (likely (other.what_space () == what))
+ switch (what)
+ {
+ case VS_reference:
+ // Stateless reference equality is just identity.
+ return (reference ()->identity ()
+ == other.reference ()->identity ());
+
+ case VS_flag:
+ return flag () == other.flag ();
+
+ case VS_rangelistptr:
+ return ranges () == other.ranges ();
+
+ case VS_lineptr:
+ return line_info () == other.line_info ();
+
+ case VS_macptr: // XXX punt for now, treat as constant
+ /*FALLTHRU*/
+ case VS_dwarf_constant:
+ return constant () == other.constant ();
+
+ case VS_constant:
+ if (constant_is_integer ())
+ return (other.constant_is_integer ()
+ && constant () == other.constant ());
+ return (!other.constant_is_integer ()
+ && constant_block () == other.constant_block ());
+
+ case VS_source_line:
+ return source_line () == other.source_line ();
+ case VS_source_column:
+ return source_column () == other.source_column ();
+
+ case VS_identifier:
+ return subr::name_equal<typeof (other.identifier ())> ()
+ (identifier (), other.identifier ());
+
+ case VS_string:
+ return subr::name_equal<typeof (other.string ())> ()
+ (string (), other.string ());
+
+ case VS_address:
+ return address () == other.address ();
+
+ case VS_source_file:
+ return source_file () == other.source_file ();
+
+ case VS_location:
+ return location () == other.location ();
+
+ case VS_discr_list:
+ throw std::runtime_error ("XXX unimplemented");
+ }
+ return false;
+ }
+ template<typename value>
+ inline bool operator!= (const value &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ /* The DW_AT_ranges attribute yields a range list.
+ XXX reloc
+ This is equivalent to unordered_set<pair<Dwarf_Addr, Dwarf_Addr> >. */
+ class range_list
+ {
+ friend class attr_value;
+ private:
+ const attr_value _m_attr;
+
+ range_list (const attr_value &attr) : _m_attr (attr) {}
+
+ public:
+ typedef std::pair< ::Dwarf_Addr, ::Dwarf_Addr> key_type; // XXX reloc
+ typedef key_type value_type;
+
+ static inline bool ordered ()
+ {
+ return false;
+ }
+
+ inline bool canonical () const
+ {
+ return false;
+ }
+
+ inline range_list (const range_list &other)
+ : _m_attr (other._m_attr)
+ {}
+
+ std::string to_string () const;
+
+ class const_iterator
+ : public std::iterator<std::input_iterator_tag, value_type>
+ {
+ friend class range_list;
+ protected:
+ ::Dwarf_Addr _m_base; // XXX reloc
+ ::Dwarf_Addr _m_begin; // XXX reloc
+ ::Dwarf_Addr _m_end; // XXX reloc
+ ::Dwarf_CU *_m_cu;
+ ptrdiff_t _m_offset;
+
+ const_iterator (Dwarf_Attribute *, ptrdiff_t offset);
+
+ public:
+ // Default constructor: only valid for operator=.
+ inline const_iterator ()
+ : _m_base (-1), _m_begin (0), _m_end (0), _m_cu (NULL), _m_offset (-1)
+ {}
+
+ inline const_iterator (const const_iterator &i)
+ : _m_base (i._m_base), _m_begin (i._m_begin), _m_end (i._m_end),
+ _m_cu (i._m_cu), _m_offset (i._m_offset)
+ {}
+
+ inline value_type operator* () const
+ {
+ if (unlikely (_m_offset == 1))
+ throw std::runtime_error ("dereferencing end iterator");
+ return std::make_pair (_m_base + _m_begin, _m_base + _m_end);
+ }
+
+ inline const_iterator &operator= (const const_iterator &other)
+ {
+ _m_base = other._m_base;
+ _m_begin = other._m_begin;
+ _m_end = other._m_end;
+ _m_cu = other._m_cu;
+ _m_offset = other._m_offset;
+ return *this;
+ }
+
+ inline bool operator== (const const_iterator &other) const
+ {
+ return _m_offset == other._m_offset && _m_cu == other._m_cu;
+ }
+ inline bool operator!= (const const_iterator &other) const
+ {
+ return !(*this == other);
+ }
+
+ const_iterator &operator++ (); // prefix
+ inline const_iterator operator++ (int) // postfix
+ {
+ const_iterator prev = *this;
+ ++*this;
+ return prev;
+ }
+ };
+
+ const_iterator begin () const
+ {
+ const_iterator it (_m_attr.thisattr (), 0);
+ return ++it;
+ }
+ const_iterator end () const
+ {
+ return const_iterator (_m_attr.thisattr (), 1);
+ }
+
+ const_iterator find (const key_type &match) const
+ {
+ return std::find (begin (), end (), match);
+ }
+
+ private:
+ struct entry_contains
+ : public std::binary_function<key_type, ::Dwarf_Addr, bool>
+ {
+ inline bool operator() (const key_type &range, const ::Dwarf_Addr addr)
+ const
+ {
+ return addr >= range.first && addr < range.second;
+ }
+ };
+
+ public:
+ const_iterator find (const ::Dwarf_Addr addr) const
+ {
+ return std::find_if (begin (), end (),
+ std::bind2nd (entry_contains (), addr));
+ }
+
+ inline operator std::set<key_type> () const
+ {
+ return std::set<key_type> (begin (), end ());
+ }
+
+ template<typename ranges>
+ inline bool operator== (const ranges &other) const
+ {
+ /* Our container is unordered (i.e., in file order). A range list
+ is conceptually equal if all the pairs match, regardless of the
+ order. But the std::equal algorithm will compare corresponding
+ elements in order. So we need an ordered set for comparison. */
+ std::set<key_type> mine = *this;
+ coalesce (mine);
+ std::set<key_type> his = other;
+ coalesce (his);
+ return mine == his;
+ }
+ template<typename ranges>
+ inline bool operator!= (const ranges &other) const
+ {
+ return !(*this == other);
+ }
+
+ // Not very wise to call.
+ size_t size () const
+ {
+ return subr::length (begin (), end ());
+ }
+ };
+
+ /* A location attribute yields a location expression.
+ Either it's a single expression, or a map of PC to location. */
+ class location_attr
+ {
+ friend class attr_value;
+ private:
+ attr_value _m_attr;
+
+ location_attr (const attr_value &attr) : _m_attr (attr) {}
+
+ inline bool same (const location_attr &it) const
+ {
+ return _m_attr.same (it._m_attr);
+ }
+
+ template<typename pair>
+ struct nonempty : public std::unary_function<pair, bool>
+ {
+ inline bool operator () (const pair &x)
+ {
+ return !x.second.empty ();
+ }
+ };
+
+ template<typename pair>
+ struct any : public std::unary_function<pair, bool>
+ {
+ inline bool operator () (const pair &)
+ {
+ return true;
+ }
+ };
+
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ // XXX need proper type for exprs
+ typedef const_vector<uint8_t> mapped_type;
+ typedef std::pair< ::Dwarf_Addr, ::Dwarf_Addr> key_type; // XXX reloc
+ typedef std::pair<const key_type, mapped_type> value_type;
+
+ std::string to_string () const;
+
+ bool is_list () const;
+
+ inline mapped_type location () const
+ {
+ if (is_list ())
+ throw std::runtime_error ("location is list, not single location");
+ return _m_attr.constant_block ();
+ }
+
+ class const_iterator
+ : public range_list::const_iterator
+ {
+ friend class location_attr;
+ private:
+ ::Dwarf_Block _m_block;
+
+ void advance ();
+
+ // For end iterator.
+ inline explicit const_iterator (Dwarf_Attribute *attr)
+ : dwarf::range_list::const_iterator (attr, 1), _m_block ()
+ {}
+
+ public:
+ typedef location_attr::value_type value_type;
+
+ inline const_iterator ()
+ : _m_block ()
+ {}
+
+ inline const_iterator (const const_iterator &i)
+ : range_list::const_iterator (i), _m_block (i._m_block)
+ {}
+
+ inline const_iterator &operator= (const const_iterator &i)
+ {
+ range_list::const_iterator::operator= (i);
+ _m_block = i._m_block;
+ return *this;
+ }
+
+ inline bool operator== (const const_iterator &it) const
+ {
+ return _m_block.data == it._m_block.data;
+ }
+ inline bool operator!= (const const_iterator &it) const
+ {
+ return !(*this == it);
+ };
+
+ const_iterator &operator++ (); // prefix
+ inline const_iterator operator++ (int) // postfix
+ {
+ const_iterator prev = *this;
+ ++*this;
+ return prev;
+ }
+
+ inline value_type operator* () const
+ {
+ if (unlikely (_m_block.data == NULL))
+ throw std::runtime_error ("dereferencing end iterator");
+
+ return value_type (key_type (_m_base + _m_begin, _m_base + _m_end),
+ const_vector<uint8_t> (_m_block));
+ }
+ };
+
+ const_iterator begin () const;
+ inline const_iterator end () const
+ {
+ return const_iterator (_m_attr.thisattr ());
+ }
+
+ inline bool empty () const
+ {
+ if (is_list ())
+ return std::find_if (begin (), end (),
+ nonempty<value_type> ()) == end ();
+ return location ().empty ();
+ }
+ inline size_type size () const
+ {
+ if (is_list ())
+ return subr::length (begin (), end ());
+ return location ().empty () ? 0 : 1;
+ }
+
+ template<typename other_attr>
+ bool operator== (const other_attr &other) const
+ {
+ if (empty ())
+ return (other.empty ()
+ || std::find_if (other.begin (), other.end (),
+ nonempty<typename other_attr::value_type> ()
+ ) == other.end ());
+
+ if (!is_list () && !other.is_list ())
+ return !other.empty () && location () == other.location ();
+
+ return subr::container_equal (*this, other);
+ }
+ template<typename other_file>
+ inline bool operator!= (const other_file &other) const
+ {
+ return !(*this == other);
+ }
+
+ /*
+ XXX missing: find, at; by key_type or by PC
+ XXX worse than that: multiple overlapping matches!
+ */
+ };
+
+ // This describes a CU's directory table, a simple array of strings.
+ class directory_table
+ {
+ private:
+ ::Dwarf_Files *_m_files;
+
+ template<typename table>
+ inline bool table_equal (const table &other) const
+ {
+ /* We ignore the first element, the compilation directory.
+ This is not encoded in the .debug_line table, but in
+ the DW_AT_comp_dir attribute of the referring CU.
+ The directory table itself matches regardless. */
+ const_iterator i = begin ();
+ typename table::const_iterator j = other.begin ();
+ return subr::container_equal
+ (++i, end (), ++j, other.end (),
+ subr::deref<directory_table, table,
+ subr::name_equal<typename table::value_type> > ());
+ }
+
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef const char *value_type;
+
+ inline directory_table (::Dwarf_Files *const files)
+ : _m_files (files) {}
+ inline directory_table (const directory_table &t)
+ : _m_files (t._m_files) {}
+
+ std::string to_string () const;
+
+ typedef const char *const *const_iterator;
+
+ inline bool empty () const
+ {
+ return size () == 0;
+ }
+
+ size_t size () const;
+ const_iterator begin () const;
+ const_iterator end () const;
+
+ template<typename table>
+ inline bool operator== (const table &other) const
+ {
+ return table_equal (other);
+ }
+ template<typename table>
+ inline bool operator!= (const table &other) const
+ {
+ return !(*this == other);
+ }
+ // Short-circuit for comparing to self.
+ inline bool operator== (const directory_table &other) const
+ {
+ return _m_files == other._m_files || table_equal (other);
+ }
+ };
+
+ /* This describes a CU's file table. It works like a read-only
+ std::vector<source_file>, and also supports lookup by name. */
+ class file_table
+ {
+ private:
+ ::Dwarf_Files *_m_files;
+
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef source_file value_type;
+
+ inline file_table (::Dwarf_Files *const files)
+ : _m_files (files) {}
+ inline file_table (const file_table &t)
+ : _m_files (t._m_files) {}
+
+ inline file_table &operator= (const file_table &t)
+ {
+ _m_files = t._m_files;
+ return *this;
+ }
+
+ typedef subr::indexed_iterator<file_table> const_iterator;
+
+ inline bool empty () const
+ {
+ return size () == 0;
+ }
+
+ size_t size () const;
+
+ inline const_iterator begin () const
+ {
+ return const_iterator (*this, 0);
+ }
+ inline const_iterator end () const
+ {
+ return const_iterator (*this, size ());
+ }
+
+ const source_file at (size_t idx) const;
+ const source_file operator[] (size_t idx) const
+ {
+ return at (idx);
+ }
+
+ // Look up by matching file name.
+ const_iterator find (const source_file &) const;
+ const_iterator find (const char *filename) const
+ {
+ const_iterator i = begin ();
+ while (i != end () && strcmp ((*i).name (), filename) != 0)
+ ++i;
+ return i;
+ }
+ template<typename string>
+ const_iterator find (const string &filename) const
+ {
+ const_iterator i = begin ();
+ while (i != end () && filename != (*i).name ())
+ ++i;
+ return i;
+ }
+ };
+
+ // This describes one entry in the line information table.
+ class line_entry
+ {
+ private:
+ ::Dwarf_Line *_m_line;
+
+ public:
+ line_entry (::Dwarf_Line *entry) : _m_line (entry) {}
+ line_entry (const line_entry &entry) : _m_line (entry._m_line) {}
+
+ // XXX reloc, dwfl
+ ::Dwarf_Addr address () const;
+
+ bool statement () const;
+ bool basic_block () const;
+ bool end_sequence () const;
+ bool prologue_end () const;
+ bool epilogue_begin () const;
+
+ const source_file file () const;
+ unsigned int line () const;
+ unsigned int column () const;
+
+ template<typename entry>
+ bool operator< (const entry &other) const
+ {
+ return address () < other.address ();
+ }
+ template<typename entry>
+ bool operator> (const entry &other) const
+ {
+ return address () > other.address ();
+ }
+ template<typename entry>
+ bool operator<= (const entry &other) const
+ {
+ return address () <= other.address ();
+ }
+ template<typename entry>
+ bool operator>= (const entry &other) const
+ {
+ return address () >= other.address ();
+ }
+
+ template<typename entry>
+ inline bool operator== (const entry &other) const
+ {
+ return (address () == other.address ()
+ && line () == other.line ()
+ && column () == other.column ()
+ && statement () == other.statement ()
+ && basic_block () == other.basic_block ()
+ && end_sequence () == other.end_sequence ()
+ && prologue_end () == other.prologue_end ()
+ && epilogue_begin () == other.epilogue_begin ()
+ && file () == other.file ());
+ }
+ template<typename entry>
+ inline bool operator!= (const entry &other) const
+ {
+ return !(*this == other);
+ }
+ // Short-circuit for our own type.
+ bool operator== (const line_entry &other) const;
+ };
+
+ /* This describes a CU's line information table.
+ It works like a read-only std::vector<line_entry>,
+ and also supports lookup by address.
+ XXX later, by file/line
+ */
+ class line_table
+ {
+ private:
+ ::Dwarf_Lines *_m_lines;
+
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef line_entry value_type;
+
+ inline line_table (::Dwarf_Lines *const lines)
+ : _m_lines (lines) {}
+ inline line_table (const line_table &t)
+ : _m_lines (t._m_lines) {}
+
+ inline line_table &operator= (const line_table &t)
+ {
+ _m_lines = t._m_lines;
+ return *this;
+ }
+
+ std::string to_string () const;
+
+ typedef subr::indexed_iterator<line_table> const_iterator;
+
+ inline bool empty () const
+ {
+ return size () == 0;
+ }
+
+ size_t size () const;
+
+ inline const_iterator begin () const
+ {
+ return const_iterator (*this, 0);
+ }
+ inline const_iterator end () const
+ {
+ return const_iterator (*this, size ());
+ }
+
+ const line_entry at (size_t idx) const;
+ const line_entry operator[] (size_t idx) const
+ {
+ return at (idx);
+ }
+
+ template<typename table>
+ inline bool operator== (const table &other) const
+ {
+ return size () == other.size () && subr::container_equal (*this, other);
+ }
+ template<typename table>
+ inline bool operator!= (const table &other) const
+ {
+ return !(*this == other);
+ }
+ // Short-circuit for comparing to self.
+ inline bool operator== (const line_table &other) const
+ {
+ return (_m_lines == other._m_lines
+ || subr::container_equal (*this, other));
+ }
+
+ // Look up by matching address.
+ const_iterator find (::Dwarf_Addr) const;
+ };
+
+ // The DW_AT_stmt_list attribute yields a line info table.
+ class line_info_table
+ {
+ private:
+ ::Dwarf_Files *_m_files;
+
+ public:
+ inline line_info_table (::Dwarf_Files *const t)
+ : _m_files (t) {}
+ inline line_info_table (const line_info_table &t)
+ : _m_files (t._m_files) {}
+
+ inline line_info_table &operator= (const line_info_table &t)
+ {
+ _m_files = t._m_files;
+ return *this;
+ }
+
+ std::string to_string () const;
+
+ inline const directory_table include_directories () const
+ {
+ return directory_table (_m_files);
+ }
+ inline const file_table files () const
+ {
+ return file_table (_m_files);
+ }
+ const line_table lines () const;
+
+ template<typename table>
+ inline bool operator== (const table &other) const
+ {
+ return (include_directories () == other.include_directories ()
+ && lines () == other.lines ());
+ }
+ template<typename table>
+ inline bool operator!= (const table &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ class dwarf_enum
+ {
+ friend class attr_value;
+ private:
+ const attr_value _m_attr;
+
+ dwarf_enum (const attr_value &attr) : _m_attr (attr) {}
+
+ public:
+ inline operator unsigned int () const
+ {
+ return _m_attr.constant ();
+ }
+
+ std::string to_string () const;
+
+ const char *identifier () const;
+ const char *name () const;
+
+ // Return the DW_AT_* indicating which enum this value belongs to.
+ unsigned int which () const
+ {
+ return _m_attr.whatattr ();
+ }
+
+ template<typename constant>
+ inline bool operator== (const constant &other) const
+ {
+ return (static_cast<unsigned int> (*this)
+ == static_cast<unsigned int> (other));
+ }
+ template<typename constant>
+ inline bool operator!= (const constant &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ // This describes one attribute, equivalent to pair<const int, attr_value>.
+ class attribute
+ {
+ friend class debug_info_entry::raw_attributes_type::const_iterator;
+ friend class attr_value;
+ private:
+ inline ::Dwarf_Attribute *thisattr () const
+ {
+ return second.thisattr ();
+ }
+
+ class lhs
+ {
+ friend class attribute;
+ private:
+ const attribute &_m_attr;
+
+ lhs (attribute &attr) : _m_attr (attr) {}
+
+ public:
+ operator int () const
+ {
+ return ::dwarf_whatattr (_m_attr.thisattr ());
+ }
+ };
+
+ attribute (const debug_info_entry &die, const ::Dwarf_Attribute &attr)
+ : first (*this), second (die.tag (), attr) {}
+
+ public:
+ lhs first;
+ attr_value second;
+
+ inline attribute (const attribute &a)
+ : first (*this), second (a.second) {}
+
+ // This lets pair<...> x = (attribute) y work.
+ template<typename value>
+ operator std::pair<const int, value> () const
+ {
+ return std::make_pair (static_cast<int> (first), value (second));
+ }
+
+ template<typename pair>
+ inline bool operator== (const pair &other) const
+ {
+ return first == other.first && second == other.second;
+ }
+ template<typename pair>
+ inline bool operator!= (const pair &other) const
+ {
+ return !(*this == other);
+ }
+
+ inline std::string to_string () const;
+ };
+
+ /* This works like range_list, but is based on a debug_info_entry using
+ dwarf_ranges. If the entry has DW_AT_low_pc and DW_AT_high_pc, this
+ will present a singleton list; if it has a DW_AT_ranges, it will be
+ the same as the range_list presentation. If neither, an empty list. */
+ class ranges
+ {
+ friend class debug_info_entry;
+ private:
+ debug_info_entry _m_die;
+
+ ranges (const debug_info_entry &die) : _m_die (die) {}
+
+ public:
+ typedef std::pair< ::Dwarf_Addr, ::Dwarf_Addr> key_type; // XXX reloc
+ typedef key_type value_type;
+
+ ranges (const ranges &other) : _m_die (other._m_die) {}
+
+ std::string to_string () const;
+
+ class const_iterator
+ : public std::iterator<std::input_iterator_tag, value_type>
+ {
+ friend class ranges;
+ private:
+ debug_info_entry _m_die;
+ ::Dwarf_Addr _m_base; // XXX reloc
+ ::Dwarf_Addr _m_begin; // XXX reloc
+ ::Dwarf_Addr _m_end; // XXX reloc
+ ptrdiff_t _m_offset;
+
+ inline const_iterator (const debug_info_entry &die)
+ : _m_die (die), _m_offset (0) {}
+
+ public:
+ inline const_iterator (const const_iterator &i)
+ : _m_die (i._m_die), _m_base (i._m_base),
+ _m_begin (i._m_begin), _m_end (i._m_end),
+ _m_offset (i._m_offset) {}
+
+ inline value_type operator* () const
+ {
+ if (unlikely (_m_offset == 0))
+ throw std::runtime_error ("dereferencing end iterator");
+ return std::make_pair (_m_begin, _m_end);
+ }
+
+ inline const_iterator &operator= (const const_iterator &other)
+ {
+ _m_die = other._m_die;
+ _m_base = other._m_base;
+ _m_begin = other._m_begin;
+ _m_end = other._m_end;
+ _m_offset = other._m_offset;
+ return *this;
+ }
+
+ inline bool operator== (const const_iterator &other) const
+ {
+ return (_m_die._m_die.addr == other._m_die._m_die.addr
+ && _m_offset == other._m_offset);
+ }
+ inline bool operator!= (const const_iterator &other) const
+ {
+ return !(*this == other);
+ }
+
+ const_iterator &operator++ () // prefix
+ {
+ do
+ _m_offset = dwarf_ranges (_m_die.thisdie (), _m_offset,
+ &_m_base, &_m_begin, &_m_end);
+ // Skip over empty ranges.
+ while (_m_offset != 0 && _m_begin == _m_end);
+ return *this;
+ }
+ inline const_iterator operator++ (int) // postfix
+ {
+ const_iterator prev = *this;
+ ++*this;
+ return prev;
+ }
+ };
+
+ const_iterator begin () const
+ {
+ const_iterator it (_m_die);
+ return ++it;
+ }
+ const_iterator end () const
+ {
+ return const_iterator (_m_die);
+ }
+
+ inline bool empty () const
+ {
+ return begin () == end ();
+ }
+
+ const_iterator find (const key_type &match) const
+ {
+ return std::find (begin (), end (), match);
+ }
+
+ private:
+ struct entry_contains
+ : public std::binary_function<key_type, ::Dwarf_Addr, bool>
+ {
+ inline bool operator() (const key_type &range, const ::Dwarf_Addr addr)
+ const
+ {
+ return addr >= range.first && addr < range.second;
+ }
+ };
+
+ public:
+ const_iterator find (const ::Dwarf_Addr addr) const
+ {
+ return std::find_if (begin (), end (),
+ std::bind2nd (entry_contains (), addr));
+ }
+
+ inline operator std::set<key_type> () const
+ {
+ return std::set<key_type> (begin (), end ());
+ }
+
+ template<typename ranges>
+ inline bool operator== (const ranges &other) const
+ {
+ /* Our container is unordered (i.e., in file order). A range list
+ is conceptually equal if all the pairs match, regardless of the
+ order. But the std::equal algorithm will compare corresponding
+ elements in order. So we need an ordered set for comparison. */
+ std::set<key_type> mine = *this;
+ coalesce (mine);
+ std::set<key_type> his = other;
+ coalesce (his);
+ return mine == his;
+ }
+ template<typename ranges>
+ inline bool operator!= (const ranges &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ // Container for raw CUs in file order, intended to be compatible
+ // with a read-only subset of std::list<compile_unit>.
+ class raw_compile_units
+ {
+ friend class dwarf;
+ private:
+ const dwarf &_m_file;
+
+ raw_compile_units (const dwarf &file) : _m_file (file) {}
+
+ public:
+ typedef compile_unit value_type;
+
+ inline raw_compile_units (const raw_compile_units &u)
+ : _m_file (u._m_file) {}
+
+ class const_iterator
+ : public std::iterator<std::input_iterator_tag, compile_unit>
+ {
+ friend class raw_compile_units;
+ private:
+ debug_info_entry _m_die;
+ const dwarf *_m_file; // XXX
+ ::Dwarf_Off _m_next; // XXX
+
+ inline const_iterator (const dwarf &file)
+ : _m_file (&file), _m_next (0) {}
+
+ public:
+ inline const_iterator ()
+ : _m_die (), _m_file (NULL), _m_next (-1)
+ {}
+
+ inline const_iterator (const const_iterator &i)
+ : _m_die (i._m_die), _m_file (i._m_file), _m_next (i._m_next) {}
+
+ inline const debug_info_entry &operator* () const
+ {
+ if (unlikely (_m_next == (::Dwarf_Off) -1))
+ throw std::runtime_error ("dereferencing end iterator");
+ return _m_die;
+ }
+ inline const debug_info_entry *operator-> () const
+ {
+ return &(operator* ());
+ }
+
+ inline const_iterator &operator= (const const_iterator &other)
+ {
+ _m_die = other._m_die;
+ _m_next = other._m_next;
+ _m_file = other._m_file; // XXX
+ return *this;
+ }
+
+ inline bool operator== (const const_iterator &other) const
+ {
+ return _m_next == other._m_next && _m_file == other._m_file;
+ }
+ inline bool operator!= (const const_iterator &other) const
+ {
+ return !(*this == other);
+ }
+
+ inline const_iterator &operator++ () // prefix
+ {
+ // XXX should be rewritten to use libdw_findcu internals
+ // slow way for first crack to avoid DSO issues
+ _m_next = _m_file->nextcu (_m_next, _m_die.thisdie ());
+ if (_m_next == (::Dwarf_Off) -1)
+ // End iterators have no file pointer.
+ _m_file = NULL;
+ return *this;
+ }
+ inline const_iterator operator++ (int) // postfix
+ {
+ const_iterator prev = *this;
+ ++*this;
+ return prev;
+ }
+ };
+
+ const_iterator begin () const
+ {
+ const_iterator it (_m_file);
+ return ++it;
+ }
+ static inline const_iterator end ()
+ {
+ return const_iterator ();
+ }
+ };
+ inline raw_compile_units raw_compile_units () const
+ {
+ return raw_compile_units::raw_compile_units (*this);
+ }
+
+ private:
+ static inline bool skip_partial_unit (const compile_unit &unit)
+ {
+ switch (unit.tag ())
+ {
+ case ::DW_TAG_partial_unit:
+ return true;
+ case ::DW_TAG_compile_unit:
+ return false;
+ default:
+ throw std::exception(); // XXX invalid dwarf
+ }
+ }
+
+ typedef skipping_wrapper<class raw_compile_units,
+ compile_unit, compile_unit,
+ skip_partial_unit> compile_units_base;
+
+ public:
+
+ // Container for logical CUs in file order, intended to be compatible
+ // with a read-only subset of std::list<compile_unit>.
+ class compile_units : public compile_units_base
+ {
+ friend class dwarf;
+ private:
+ compile_units (class raw_compile_units raw) : compile_units_base (raw) {}
+
+ public:
+ typedef compile_unit value_type;
+
+ compile_units (const compile_units &u) : compile_units_base (u) {}
+
+ template<typename units>
+ bool operator== (const units &other) const
+ {
+ return subr::container_equal (*this, other);
+ }
+ template<typename units>
+ bool operator!= (const units &other) const
+ {
+ return !(*this == other);
+ }
+ };
+ inline class compile_units compile_units () const
+ {
+ return compile_units::compile_units (raw_compile_units ());
+ }
+
+ private:
+ ::Dwarf *_m_dw;
+
+ public:
+ // XXX temp hack
+ inline ::Dwarf_Off nextcu (::Dwarf_Off offset, ::Dwarf_Die *die) const
+ {
+ ::Dwarf_Off next;
+ ::size_t header_size;
+ int result = ::dwarf_nextcu (_m_dw, offset, &next, &header_size,
+ NULL, NULL, NULL);
+ xif (result < 0);
+ if (result == 0)
+ xif (::dwarf_offdie (_m_dw, offset + header_size, die) == NULL);
+ else
+ memset (die, 0, sizeof *die);
+ return next;
+ }
+
+ inline dwarf (::Dwarf *dw) : _m_dw (dw) {};
+
+ inline dwarf (const dwarf &dw) : _m_dw (dw._m_dw) {};
+
+ template<typename file>
+ inline bool operator== (const file &other) const
+ {
+ return compile_units () == other.compile_units ();
+ }
+ template<typename file>
+ inline bool operator!= (const file &other) const
+ {
+ return !(*this == other);
+ }
+
+ // XXX reloc
+ class arange_list
+ : public std::set<std::pair< ::Dwarf_Addr, ::Dwarf_Addr> >
+ {
+ private:
+ typedef std::set<std::pair< ::Dwarf_Addr, ::Dwarf_Addr> > _base;
+
+ public:
+ typedef _base::key_type key_type;
+ typedef _base::value_type value_type;
+ typedef _base::iterator iterator;
+ typedef _base::const_iterator const_iterator;
+
+ static inline bool ordered ()
+ {
+ return true;
+ }
+
+ struct hasher : public subr::container_hasher<arange_list> {};
+
+ inline arange_list () {}
+ inline arange_list (const arange_list &other)
+ : _base (static_cast<const _base &> (other))
+ {}
+
+ template<typename iterator>
+ arange_list (iterator first, iterator last)
+ : _base (first, last)
+ {}
+
+ template<typename input>
+ inline arange_list (const input &other)
+ : _base (other.begin (), other.end ())
+ {}
+
+ std::string to_string () const;
+
+ inline std::string to_string ()
+ {
+ coalesce (*this);
+ return ((const arange_list *) this)->to_string ();
+ }
+
+ inline bool canonical () const
+ {
+ // Can't be sure.
+ return false;
+ }
+
+ inline bool canonical ()
+ {
+ // Make it so.
+ coalesce (*this);
+ return true;
+ }
+
+ inline bool operator== (arange_list &other)
+ {
+ // Since we are not const, coalesce both in place.
+ coalesce (other);
+ if (size () < other.size ())
+ // Coalescing can only make us smaller.
+ return false;
+ coalesce (*this);
+ return size () == other.size () && subr::container_equal (*this, other);
+ }
+
+ template<typename list>
+ inline bool operator== (const list &other)
+ {
+ // Since we are not const, coalesce in place.
+ coalesce (*this);
+
+ if (list::ordered () && other.canonical ()
+ && size () != other.size ())
+ return false;
+
+ // If he happens to be sorted and canonical, we'll match.
+ if (subr::container_equal (*this, other))
+ return true;
+
+ // If he was sorted and canonical and we didn't match, it's conclusive.
+ if (list::ordered () && other.canonical ())
+ return false;
+
+ // Make a sorted and canonicalized copy to compare to.
+ _base his (other);
+ if (size () > his.size ()
+ || (list::ordered () && size () == his.size ()))
+ // Coalescing can only make him smaller.
+ return false;
+ coalesce (his);
+ return subr::container_equal (*this, his);
+ }
+
+ template<typename list>
+ inline bool operator== (const list &other) const
+ {
+ if (list::ordered () && other.canonical ()
+ && size () < other.size ())
+ // Coalescing can only make us smaller.
+ return false;
+
+ // If we both happen to be sorted and canonical, we'll match.
+ if (subr::container_equal (*this, other))
+ return true;
+
+ // Make a non-const copy that will coalesce in its operator==.
+ if (list::ordered () && other.canonical ())
+ return size () != other.size () && arange_list (*this) == other;
+
+ return arange_list (other) == *this;
+ }
+ };
+
+ private:
+ struct arange_less
+ : public std::binary_function<compile_unit, compile_unit, bool>
+ {
+ inline bool operator() (const compile_unit &a, const compile_unit &b)
+ const
+ {
+ return a.offset () < b.offset ();
+ }
+ };
+
+ public:
+ typedef std::map<compile_unit, arange_list, arange_less> aranges_map;
+
+ aranges_map aranges () const;
+
+ private:
+ static bool adjacency (const arange_list::key_type &a,
+ const arange_list::key_type &b)
+ {
+ return a.second == b.first;
+ }
+
+ // Coalesce adjacent ranges.
+ static void coalesce (std::set<arange_list::key_type> &set)
+ {
+ for (std::set<arange_list::key_type>::iterator i = set.begin ();
+ (i = std::adjacent_find (i, set.end (), adjacency)) != set.end ();
+ ++i)
+ {
+ std::set<arange_list::key_type>::iterator j = i;
+ std::set<arange_list::key_type>::iterator k = ++j;
+ while (++k != set.end () && adjacency (*j, *k))
+ ++j;
+ const arange_list::key_type joined (i->first, j->second);
+ set.erase (i, k);
+ i = set.insert (joined).first;
+ }
+ }
+ };
+
+ inline class dwarf::debug_info_entry::raw_children_type
+ dwarf::debug_info_entry::raw_children () const
+ {
+ return raw_children_type::raw_children_type (*this);
+ }
+
+ inline class dwarf::debug_info_entry::children_type
+ dwarf::debug_info_entry::children () const
+ {
+ return children_type::children_type (*this);
+ }
+
+ inline class dwarf::debug_info_entry::raw_attributes_type
+ dwarf::debug_info_entry::raw_attributes () const
+ {
+ return raw_attributes_type::raw_attributes_type (*this);
+ }
+
+ inline class dwarf::debug_info_entry::attributes_type
+ dwarf::debug_info_entry::attributes () const
+ {
+ return attributes_type::attributes_type (raw_attributes ());
+ }
+
+ // Explicit specializations.
+ template<>
+ std::string
+ to_string<dwarf::debug_info_entry> (const dwarf::debug_info_entry &);
+ inline std::string dwarf::debug_info_entry::to_string () const
+ {
+ return elfutils::to_string (*this); // Use that.
+ }
+ template<>
+ std::string to_string<dwarf::attribute> (const dwarf::attribute &);
+ inline std::string dwarf::attribute::to_string () const
+ {
+ return elfutils::to_string (*this); // Use that.
+ }
+ template<>
+ std::string to_string<dwarf::attr_value> (const dwarf::attr_value &);
+ inline std::string dwarf::attr_value::to_string () const
+ {
+ return elfutils::to_string (*this); // Use that.
+ }
+ template<>
+ std::string to_string<dwarf::dwarf_enum> (const dwarf::dwarf_enum &);
+ inline std::string dwarf::dwarf_enum::to_string () const
+ {
+ return elfutils::to_string (*this); // Use that.
+ }
+
+};
+
+#endif // <elfutils/dwarf>
diff --git a/libdw/c++/dwarf-knowledge.cc b/libdw/c++/dwarf-knowledge.cc
new file mode 100644
index 00000000..3d8edeb5
--- /dev/null
+++ b/libdw/c++/dwarf-knowledge.cc
@@ -0,0 +1,160 @@
+#include <config.h>
+#include "dwarf"
+
+using namespace std;
+using namespace elfutils;
+
+#define VS(what) (1U << dwarf::VS_##what)
+
+/* Return a bitmask of value spaces expected for this attribute of this tag.
+ Primarily culled from the DWARF 3 spec: 7.5.4, Figure 20. */
+
+static unsigned int
+expected_value_space (int attr, int tag)
+{
+
+ switch (attr)
+ {
+ case DW_AT_sibling:
+ case DW_AT_common_reference:
+ case DW_AT_containing_type:
+ case DW_AT_default_value:
+ case DW_AT_abstract_origin:
+ case DW_AT_base_types:
+ case DW_AT_friend:
+ case DW_AT_priority:
+ case DW_AT_specification:
+ case DW_AT_type:
+ case DW_AT_use_location:
+ case DW_AT_data_location:
+ case DW_AT_extension:
+ case DW_AT_small:
+ case DW_AT_object_pointer:
+ case DW_AT_namelist_item:
+ return VS(reference);
+
+ case DW_AT_location:
+ case DW_AT_string_length:
+ case DW_AT_return_addr:
+ case DW_AT_frame_base:
+ case DW_AT_segment:
+ case DW_AT_static_link:
+ case DW_AT_vtable_elem_location:
+ return VS(location);
+
+ case DW_AT_data_member_location:
+ return VS(location) | VS(constant);
+
+ case DW_AT_name:
+ switch (tag)
+ {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ return VS(source_file);
+ default:
+ return VS(identifier);
+ }
+
+ case DW_AT_ordering:
+ case DW_AT_language:
+ case DW_AT_visibility:
+ case DW_AT_inline:
+ case DW_AT_accessibility:
+ case DW_AT_address_class:
+ case DW_AT_calling_convention:
+ case DW_AT_encoding:
+ case DW_AT_identifier_case:
+ case DW_AT_virtuality:
+ case DW_AT_endianity:
+ return VS(dwarf_constant);
+
+ case DW_AT_byte_size:
+ case DW_AT_byte_stride:
+ case DW_AT_bit_size:
+ case DW_AT_bit_offset:
+ case DW_AT_bit_stride:
+ case DW_AT_lower_bound:
+ case DW_AT_upper_bound:
+ case DW_AT_count:
+ case DW_AT_allocated:
+ case DW_AT_associated:
+ return VS(reference) | VS(constant) | VS(location); // XXX non-loc expr
+
+ case DW_AT_stmt_list:
+ return VS(lineptr);
+ case DW_AT_macro_info:
+ return VS(macptr);
+ case DW_AT_ranges:
+ return VS(rangelistptr);
+
+ case DW_AT_low_pc:
+ case DW_AT_high_pc:
+ case DW_AT_entry_pc:
+ return VS(address);
+
+ case DW_AT_discr:
+ return VS(reference);
+ case DW_AT_discr_value:
+ return VS(constant);
+ case DW_AT_discr_list:
+ return VS(discr_list);
+
+ case DW_AT_import:
+ return VS(reference);
+
+ case DW_AT_comp_dir:
+ return VS(source_file);
+
+ case DW_AT_const_value:
+ return VS(constant) | VS(string) | VS(address);
+
+ case DW_AT_is_optional:
+ case DW_AT_prototyped:
+ case DW_AT_artificial:
+ case DW_AT_declaration:
+ case DW_AT_external:
+ case DW_AT_variable_parameter:
+ case DW_AT_use_UTF8:
+ case DW_AT_mutable:
+ case DW_AT_threads_scaled:
+ case DW_AT_explicit:
+ case DW_AT_elemental:
+ case DW_AT_pure:
+ case DW_AT_recursive:
+ return VS(flag);
+
+ case DW_AT_producer:
+ return VS(string);
+
+ case DW_AT_start_scope:
+ return VS(constant);
+
+ case DW_AT_binary_scale:
+ case DW_AT_decimal_scale:
+ case DW_AT_decimal_sign:
+ case DW_AT_digit_count:
+ return VS(constant);
+
+ case DW_AT_decl_file:
+ case DW_AT_call_file:
+ return VS(source_file);
+ case DW_AT_decl_line:
+ case DW_AT_call_line:
+ return VS(source_line);
+ case DW_AT_decl_column:
+ case DW_AT_call_column:
+ return VS(source_column);
+
+ case DW_AT_trampoline:
+ return VS(address) | VS(flag) | VS(reference) | VS(string);
+
+ case DW_AT_description:
+ case DW_AT_picture_string:
+ return VS(string);
+
+ case DW_AT_MIPS_linkage_name:
+ return VS(identifier);
+ }
+
+ return 0;
+}
diff --git a/libdw/c++/dwarf_comparator b/libdw/c++/dwarf_comparator
new file mode 100644
index 00000000..1fe062c8
--- /dev/null
+++ b/libdw/c++/dwarf_comparator
@@ -0,0 +1,587 @@
+/* elfutils::dwarf_comparator -- -*- C++ -*- templates for comparing DWARF data
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifndef _ELFUTILS_DWARF_COMPARATOR
+#define _ELFUTILS_DWARF_COMPARATOR 1
+
+#include "dwarf"
+
+namespace elfutils
+{
+ // Prototypical stub for reference tracker object.
+ // This keeps no state, and no two contexts ever match.
+ template<class dwarf1, class dwarf2>
+ struct dwarf_tracker_base
+ {
+ typedef typename dwarf1::compile_units::const_iterator cu1;
+ typedef typename dwarf2::compile_units::const_iterator cu2;
+ typedef typename dwarf1::debug_info_entry dwarf1_die;
+ typedef typename dwarf2::debug_info_entry dwarf2_die;
+ typedef typename dwarf1_die::children_type::const_iterator die1;
+ typedef typename dwarf2_die::children_type::const_iterator die2;
+ typedef typename dwarf1_die::attributes_type::const_iterator attr1;
+ typedef typename dwarf2_die::attributes_type::const_iterator attr2;
+
+ // This object is created to start a walk and destroyed to finish one.
+ struct walk
+ {
+ inline walk (dwarf_tracker_base *, const cu1 &, const cu2 &)
+ {
+ }
+ inline ~walk ()
+ {
+ }
+ };
+
+ // This object is created in pre-order and destroyed in post-order.
+ struct step
+ {
+ inline step (dwarf_tracker_base *, const die1 &, const die2 &)
+ {
+ }
+ inline ~step ()
+ {
+ }
+ };
+
+ inline void visit (const typename dwarf1::debug_info_entry &,
+ const typename dwarf2::debug_info_entry &)
+ {
+ }
+
+ inline bool mismatch (cu1 &, const cu1 &, // at, end
+ cu2 &, const cu2 &)
+ {
+ return false;
+ }
+
+ inline bool mismatch (die1 &, const die1 &, // at, end
+ die2 &, const die2 &)
+ {
+ return false;
+ }
+
+ inline bool mismatch (attr1 &, const attr1 &, // at, end
+ attr2 &, const attr2 &)
+ {
+ return false;
+ }
+
+ struct left_context_type {};
+ struct right_context_type {};
+
+ // Return the lhs context of an arbitrary DIE.
+ inline const left_context_type left_context (const die1 &)
+ {
+ return left_context_type ();
+ }
+
+ // Return the rhs context of an arbitrary DIE.
+ inline const right_context_type right_context (const die2 &)
+ {
+ return right_context_type ();
+ }
+
+ inline bool context_quick_mismatch (const left_context_type &,
+ const right_context_type &)
+
+ {
+ return true;
+ }
+
+ inline bool context_match (const left_context_type &,
+ const right_context_type &)
+ {
+ return false;
+ }
+
+ struct reference_match {};
+
+ // This call is used purely in hopes of a cache hit.
+ inline bool prematch (reference_match &, const die1 &, const die2 &)
+ {
+ return false;
+ }
+
+ // This call is used only as part of a real reference lookup.
+ inline bool reference_matched (reference_match &,
+ const die1 &, const die2 &)
+ {
+ return false;
+ }
+
+ // Check for a negative cache hit after prematch or reference_match.
+ inline bool cannot_match (reference_match &, const die1 &, const die2 &)
+ {
+ return false;
+ }
+
+ // This can cache a result.
+ inline bool notice_match (reference_match &, const die1 &, const die2 &,
+ bool result)
+ {
+ return result;
+ }
+
+ template<typename item1, typename item2>
+ inline bool identical (const item1 &, const item2 &)
+ {
+ return false;
+ }
+
+ inline dwarf_tracker_base ()
+ {}
+
+ typedef dwarf_tracker_base subtracker;
+ inline dwarf_tracker_base (const dwarf_tracker_base &, reference_match &,
+ const left_context_type &,
+ const right_context_type &)
+ {}
+ };
+
+ template<class dwarf1, class dwarf2,
+ bool ignore_refs = false,
+ class tracker = dwarf_tracker_base<dwarf1, dwarf2>
+ >
+ class dwarf_comparator
+ : public std::binary_function<dwarf1, dwarf2, bool>
+ {
+ private:
+ tracker &_m_tracker;
+
+ typedef dwarf_comparator<dwarf1, dwarf2, false,
+ typename tracker::subtracker> subcomparator;
+
+ template<typename item1, typename item2>
+ struct matcher : public std::binary_function<item1, item2, bool>
+ {
+ dwarf_comparator &_m_cmp;
+ matcher (dwarf_comparator &cmp)
+ : _m_cmp (cmp)
+ {}
+
+ inline bool operator () (const item1 &a, const item2 &b)
+ {
+ return _m_cmp.match_deref (a, b);
+ }
+ };
+#define MATCHER(item) \
+ matcher<typename dwarf1::item::const_iterator, \
+ typename dwarf2::item::const_iterator> (*this)
+
+ inline bool match (const dwarf1 &a, const dwarf2 &b)
+ {
+ return match (a.compile_units (), b.compile_units ());
+ }
+
+ typedef typename dwarf1::compile_units compile_units1;
+ typedef typename dwarf2::compile_units compile_units2;
+ typedef typename dwarf1::compile_units::const_iterator cu1_it;
+ typedef typename dwarf2::compile_units::const_iterator cu2_it;
+ inline bool match (const compile_units1 &a, const compile_units2 &b)
+ {
+ cu1_it it1 = a.begin ();
+ cu2_it it2 = b.begin ();
+ const cu1_it end1 = a.end ();
+ const cu2_it end2 = b.end ();
+ do
+ {
+ if (subr::container_equal
+ (it1, end1, it2, end2, MATCHER (compile_units)))
+ return true;
+ }
+ while (_m_tracker.mismatch (it1, end1, it2, end2));
+ return false;
+ }
+
+ typedef typename dwarf1::debug_info_entry die1;
+ typedef typename dwarf2::debug_info_entry die2;
+ inline bool match_deref (const cu1_it &a, const cu2_it &b)
+ {
+ typename tracker::walk in (&_m_tracker, a, b);
+ return equals (*a, *b);
+ }
+
+ inline bool match (const die1 &a, const die2 &b)
+ {
+ _m_tracker.visit (a, b);
+ if (a.tag () != b.tag ())
+ return nomatch (a, b, "DIE tag");
+ if (!equals (a.attributes (), b.attributes ()))
+ return nomatch (a, b, "DIE attrs");
+ if (!equals (a.children (), b.children ()))
+ return nomatch (a, b, "DIE children");
+ return true;
+ }
+
+ template<typename in, typename out>
+ static inline void populate (out &o, const in &map)
+ {
+ for (typename in::const_iterator i = map.begin ();
+ i != map.end ();
+ ++i)
+ o.insert (std::make_pair ((*i).first, i));
+ }
+
+ typedef typename dwarf1::debug_info_entry::attributes_type attributes1;
+ typedef typename dwarf2::debug_info_entry::attributes_type attributes2;
+ typedef typename attributes1::const_iterator ait1;
+ typedef typename attributes2::const_iterator ait2;
+ typedef std::map<int, ait1> ait1_map;
+ typedef std::map<int, ait2> ait2_map;
+
+ struct match_lhs
+ : public std::binary_function<ait1, ait2, bool>
+ {
+ inline bool operator () (const ait1 &it1, const ait2 &it2)
+ {
+ return (*it1).first == (*it2).first;
+ }
+ };
+
+ struct match_rhs
+ : public std::binary_function<ait1, ait2, bool>
+ {
+ dwarf_comparator &_m_cmp;
+ match_rhs (dwarf_comparator &cmp)
+ : _m_cmp (cmp)
+ {}
+
+ inline bool operator () (const ait1 &it1, const ait2 &it2)
+ {
+ return _m_cmp.equals ((*it1).second, (*it2).second);
+ }
+ };
+
+ struct match_sorted
+ : public std::binary_function<typename ait1_map::value_type,
+ typename ait2_map::value_type,
+ bool>
+ {
+ dwarf_comparator &_m_cmp;
+ match_sorted (dwarf_comparator<dwarf1, dwarf2, ignore_refs, tracker> &cmp)
+ : _m_cmp (cmp)
+ {}
+
+ inline bool operator () (const typename ait1_map::value_type &x,
+ const typename ait2_map::value_type &y)
+ {
+ return (x.first == y.first
+ && _m_cmp.equals ((*x.second).second, (*y.second).second));
+ }
+ };
+
+ inline bool match (const attributes1 &a, const attributes2 &b)
+ {
+ ait1 it1 = a.begin ();
+ ait2 it2 = b.begin ();
+ const ait1 end1 = a.end ();
+ const ait2 end2 = b.end ();
+ if (subr::container_equal (it1, end1, it2, end2, match_lhs ()))
+ {
+ // The set of attributes matches, in order. Compare the values.
+ it1 = a.begin ();
+ it2 = b.begin ();
+ do
+ {
+ if (subr::container_equal (it1, end1, it2, end2,
+ match_rhs (*this)))
+ return true;
+ }
+ while (_m_tracker.mismatch (it1, end1, it2, end2));
+ return false;
+ }
+
+ if (it1 != end1 && it2 != end2
+ && !(attributes1::ordered () && attributes2::ordered ()))
+ {
+ /* We have the same number of attributes, but the names don't
+ match. Populate two sorted maps and compare those. */
+
+ ait1_map sorted1;
+ populate (sorted1, a);
+
+ ait2_map sorted2;
+ populate (sorted2, b);
+
+ std::pair<typename ait1_map::iterator,
+ typename ait2_map::iterator> result
+ = std::mismatch (sorted1.begin (), sorted1.end (),
+ sorted2.begin (), match_sorted (*this));
+ if (result.first == sorted1.end ()
+ && result.second == sorted2.end ())
+ return true;
+
+ it1 = result.first->second;
+ it2 = result.second->second;
+ }
+
+ return _m_tracker.mismatch (it1, end1, it2, end2);
+ }
+
+ typedef typename dwarf1::debug_info_entry::children_type children1;
+ typedef typename dwarf2::debug_info_entry::children_type children2;
+ typedef typename children1::const_iterator cit1;
+ typedef typename children2::const_iterator cit2;
+
+ inline bool match_child (const cit1 &a, const cit2 &b)
+ {
+ typename tracker::step into (&_m_tracker, a, b);
+ return equals (*a, *b);
+ }
+
+ inline bool match_deref (const cit1 &a, const cit2 &b)
+ {
+ // Maybe the tracker has already cached a correspondence of DIEs.
+ typename tracker::reference_match matched;
+ if (_m_tracker.prematch (matched, a, b))
+ return true;
+
+ if (_m_tracker.cannot_match (matched, a, b))
+ return nomatch (*a, *b, "children cached");
+
+ bool result = match_child (a, b);
+
+ // Let the tracker cache a result for its reference_matched.
+ return _m_tracker.notice_match (matched, a, b, result);
+ }
+
+ inline bool match (const children1 &a, const children2 &b)
+ {
+ cit1 it1 = a.begin ();
+ cit2 it2 = b.begin ();
+ const cit1 end1 = a.end ();
+ const cit2 end2 = b.end ();
+ do
+ if (subr::container_equal (it1, end1, it2, end2,
+ MATCHER (debug_info_entry::children_type)))
+ return true;
+ while (_m_tracker.mismatch (it1, end1, it2, end2));
+ return false;
+ }
+
+ typedef typename dwarf1::attribute attribute1;
+ typedef typename dwarf2::attribute attribute2;
+ inline bool match (const attribute1 &a, const attribute2 &b)
+ {
+ return a.first == b.first && equals (a.second, b.second);
+ }
+
+ typedef typename dwarf1::attr_value attr_value1;
+ typedef typename dwarf2::attr_value attr_value2;
+ inline bool match (const attr_value1 &a, const attr_value2 &b)
+ {
+ const dwarf::value_space what = a.what_space ();
+ if (what == b.what_space ())
+ switch (what)
+ {
+ case dwarf::VS_reference:
+ return reference_match (a.reference (), b.reference ());
+
+ case dwarf::VS_flag:
+ return a.flag () == b.flag ();
+
+ case dwarf::VS_rangelistptr:
+ return a.ranges () == b.ranges ();
+
+ case dwarf::VS_lineptr:
+ return a.line_info () == b.line_info ();
+
+ case dwarf::VS_macptr: // XXX punt for now, treat as constant
+ return a.constant () == b.constant ();
+
+ case dwarf::VS_dwarf_constant:
+ return a.dwarf_constant () == b.dwarf_constant ();
+
+ case dwarf::VS_constant:
+ if (a.constant_is_integer ())
+ return (b.constant_is_integer ()
+ && a.constant () == b.constant ());
+ return (!b.constant_is_integer ()
+ && subr::container_equal (a.constant_block (),
+ b.constant_block ()));
+
+ case dwarf::VS_source_line:
+ return a.source_line () == b.source_line ();
+ case dwarf::VS_source_column:
+ return a.source_column () == b.source_column ();
+
+ case dwarf::VS_identifier:
+ return subr::name_equal<typeof (b.identifier ())> ()
+ (a.identifier (), b.identifier ());
+
+ case dwarf::VS_string:
+ return subr::name_equal<typeof (b.string ())> ()
+ (a.string (), b.string ());
+
+ case dwarf::VS_address:
+ return a.address () == b.address ();
+
+ case dwarf::VS_source_file:
+ return a.source_file () == b.source_file ();
+
+ case dwarf::VS_location:
+ return a.location () == b.location ();
+
+ case dwarf::VS_discr_list:
+ throw std::runtime_error ("XXX unimplemented");
+ }
+ return false;
+ }
+
+ // This is a convenient place to hack for debugging output and such.
+ inline bool nomatch (const die1 &, const die2 &, const char *)
+ {
+ return false;
+ }
+
+ /* We call references equal if they are literally the same DIE,
+ or if they are identical subtrees sitting in matching contexts.
+ The tracker's context_match method decides what that means. */
+ inline bool reference_match (const cit1 &ref1, const cit2 &ref2)
+ {
+ if (ignore_refs)
+ return true;
+
+ const die1 &a = *ref1;
+ const die2 &b = *ref2;
+
+ if (a.identity () == b.identity ()) // Object identity.
+ return true;
+
+ // Simplest mismatches with the cheapest checks first.
+ if (a.tag () != b.tag ())
+ return nomatch (a, b, "tag");
+
+ const bool has_children = a.has_children ();
+ if (has_children != b.has_children ())
+ return nomatch (a, b, "has_children");
+
+ // Maybe the tracker has already cached a correspondence of references.
+ typename tracker::reference_match matched;
+ if (_m_tracker.reference_matched (matched, ref1, ref2))
+ return true;
+
+ if (_m_tracker.cannot_match (matched, ref1, ref2))
+ return nomatch (a, b, "cached");
+
+ // Now we really have to get the tracker involved.
+ const typename tracker::left_context_type &lhs
+ = _m_tracker.left_context (ref1);
+ const typename tracker::right_context_type &rhs
+ = _m_tracker.right_context (ref2);
+
+ bool result = true;
+
+ /* First do the cheap mismatch check on the contexts, then check the
+ contents and contexts in ascending order of costliness of a check. */
+ if (_m_tracker.context_quick_mismatch (lhs, rhs))
+ result = nomatch (a, b, "quick context");
+
+ /* To compare the children, we have to clone the tracker and use a
+ new one, in case of any reference attributes in their subtrees.
+ The new tracker jump-starts its walk to the referenced DIE from
+ the root of the CU.
+
+ We use the subtracker and subcomparator for the attributes as well,
+ in case the main tracker has side-effects like printing. */
+
+ typename tracker::subtracker t (_m_tracker, matched, lhs, rhs);
+ subcomparator cmp (t);
+
+ if (result && !cmp.equals (a.attributes (), b.attributes ()))
+ result = nomatch (a, b, "attribute");
+
+ if (result && !_m_tracker.context_match (lhs, rhs))
+ result = nomatch (a, b, "context");
+
+ if (result && has_children && !cmp.equals (a.children (), b.children ()))
+ result = nomatch (a, b, "children");
+
+ // Let the tracker cache a result for its reference_matched.
+ return _m_tracker.notice_match (matched, ref1, ref2, result);
+ }
+
+ // This is what the public equals method uses for references.
+ inline bool match (const cit1 &a, const cit2 &b)
+ {
+ return reference_match (a, b);
+ }
+
+ public:
+ inline explicit dwarf_comparator (tracker &t)
+ : _m_tracker (t)
+ {}
+
+ inline bool operator () (const dwarf1 &a, const dwarf2 &b)
+ {
+ return match (a, b);
+ }
+
+ template<typename item1, typename item2>
+ inline bool equals (const item1 &a, const item2 &b)
+ {
+ return _m_tracker.identical (a, b) || match (a, b);
+ }
+
+ /* Predicate for DIEs "equal enough" to match as context for a subtree.
+ The definition we use is that the DIE has the same tag and all its
+ attributes are equal, excepting that references in attribute values
+ are not compared. */
+ static inline bool equal_enough (const die1 &a, const die2 &b)
+ {
+ dwarf_tracker_base<dwarf1, dwarf2> context_tracker;
+ return (a.tag () == b.tag ()
+ && (dwarf_comparator<dwarf1, dwarf2, true> (context_tracker)
+ .equals (a.attributes (), b.attributes ())));
+ }
+ };
+};
+
+#endif // <elfutils/dwarf_comparator>
diff --git a/libdw/c++/dwarf_data b/libdw/c++/dwarf_data
new file mode 100644
index 00000000..134ff8e3
--- /dev/null
+++ b/libdw/c++/dwarf_data
@@ -0,0 +1,1600 @@
+/* elfutils::dwarf_data -- internal DWARF data representations in -*- C++ -*-
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifndef _ELFUTILS_DWARF_DATA
+#define _ELFUTILS_DWARF_DATA 1
+
+#include "dwarf"
+#include <cassert>
+#include <bitset>
+
+/* This contains common classes/templates used by dwarf_output and dwarf_edit.
+
+ These are implementations of the "boring" components of the dwarf
+ object interface.
+*/
+
+namespace elfutils
+{
+ // This is a class only for scoping purposes.
+ // It contains no members, only inner classes.
+ class dwarf_data
+ {
+ public:
+
+ // Main container anchoring all the output.
+ template<class impl>
+ class compile_units : public std::list<typename impl::compile_unit>
+ {
+ friend class subr::create_container;
+
+ protected:
+ typedef std::list<typename impl::compile_unit> _base;
+
+ // Constructor copying CUs from input container.
+ template<typename input, typename arg_type>
+ inline compile_units (const input &other, arg_type &arg)
+ : _base (subr::argify<input, compile_units, arg_type &>
+ (other.begin (), arg),
+ subr::argify<input, compile_units, arg_type &>
+ (other.end (), arg))
+ {}
+
+ public:
+ // Default constructor: an empty container, no CUs.
+ inline compile_units () {}
+
+ template<typename other_children>
+ bool operator== (const other_children &other) const
+ {
+ return subr::container_equal (*this, other);
+ }
+ template<typename other_children>
+ bool operator!= (const other_children &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ template<class impl>
+ class compile_unit : public impl::debug_info_entry
+ {
+ friend class subr::create_container;
+ friend class impl::compile_units;
+
+ protected:
+ template<typename input>
+ static inline const input &require_cu (const input &cu)
+ {
+ if (cu.tag () != ::DW_TAG_compile_unit)
+ throw std::runtime_error
+ ("top-level debug_info_entry must be DW_TAG_compile_unit");
+ return cu;
+ }
+
+ template<typename die_type, typename arg_type>
+ inline void set (const die_type &die, arg_type &arg)
+ {
+ impl::debug_info_entry::set (require_cu (die), arg);
+ }
+
+ public:
+ explicit inline compile_unit ()
+ : impl::debug_info_entry ()
+ {
+ this->_m_tag = ::DW_TAG_compile_unit;
+ }
+
+ inline compile_unit (const compile_unit &other)
+ : impl::debug_info_entry (require_cu (other))
+ {}
+
+ template<typename input, typename arg_type>
+ inline compile_unit (const input &cu, arg_type &arg)
+ : impl::debug_info_entry (typename impl::debug_info_entry::pointer (),
+ require_cu (cu), arg)
+ {}
+
+ /* Assignment details are up to the base class.
+ We just ensure it's really a compile_unit. */
+ inline compile_unit &
+ operator= (const typename impl::debug_info_entry &other)
+ {
+ impl::debug_info_entry::operator= (require_cu (other));
+ return *this;
+ }
+
+ // Fetch the CU's DW_AT_stmt_list.
+ inline const typename impl::line_info_table &line_info () const
+ {
+ return this->attributes ().at (::DW_AT_stmt_list).line_info ();
+ }
+
+ // Convenience methods for line_info_table sub-containers.
+ inline const typename impl::directory_table &include_directories () const
+ {
+ return line_info ().include_directories ();
+ }
+
+ inline const typename impl::line_table &lines () const
+ {
+ return line_info ().lines ();
+ }
+
+#if 0 // XXX const issues
+ inline typename impl::line_info_table &line_info ()
+ {
+ return this->attributes ()[::DW_AT_stmt_list].line_info ();
+ }
+
+ inline typename impl::directory_table &include_directories ()
+ {
+ return line_info ().include_directories ();
+ }
+
+ inline typename impl::line_table &lines ()
+ {
+ return line_info ().lines ();
+ }
+#endif
+ };
+
+ class source_file
+ {
+ private:
+ std::string _m_name;
+ ::Dwarf_Word _m_mtime;
+ ::Dwarf_Word _m_size;
+
+ public:
+
+ struct hasher
+ : public std::unary_function<source_file, size_t>
+ {
+ size_t operator () (const source_file &v) const
+ {
+ size_t hash = 0;
+ subr::hash_combine (hash, v._m_name);
+ subr::hash_combine (hash, v._m_mtime);
+ subr::hash_combine (hash, v._m_size);
+ return hash;
+ }
+ };
+
+ source_file () : _m_name (), _m_mtime (0), _m_size (0) {}
+ source_file (const std::string &n, ::Dwarf_Word m = 0, ::Dwarf_Word s = 0)
+ : _m_name (n), _m_mtime (m), _m_size (s) {}
+ source_file (const char *n, ::Dwarf_Word m = 0, ::Dwarf_Word s = 0)
+ : _m_name (n), _m_mtime (m), _m_size (s) {}
+
+ template<typename file>
+ source_file (const file &other)
+ : _m_name (other.name ()),
+ _m_mtime (other.mtime ()), _m_size (other.size ()) {}
+
+ template<typename file>
+ inline source_file &operator= (const file &other)
+ {
+ _m_name = other.name ();
+ _m_mtime = other.mtime ();
+ _m_size = other.size ();
+ return *this;
+ }
+ inline source_file &operator= (const std::string &n)
+ {
+ _m_name = n;
+ _m_mtime = 0;
+ _m_size = 0;
+ return *this;
+ }
+ inline source_file &operator= (const char *n)
+ {
+ _m_name = n;
+ _m_mtime = 0;
+ _m_size = 0;
+ return *this;
+ }
+
+ std::string to_string () const;
+
+ inline std::string &name ()
+ {
+ return _m_name;
+ }
+ inline const std::string &name () const
+ {
+ return _m_name;
+ }
+ inline ::Dwarf_Word &mtime ()
+ {
+ return _m_mtime;
+ }
+ inline ::Dwarf_Word mtime () const
+ {
+ return _m_mtime;
+ }
+ inline ::Dwarf_Word &size ()
+ {
+ return _m_size;
+ }
+ inline ::Dwarf_Word size () const
+ {
+ return _m_size;
+ }
+
+ template<typename other_file>
+ bool operator== (const other_file &other) const
+ {
+ if (mtime () != 0)
+ {
+ ::Dwarf_Word other_mtime = other.mtime ();
+ if (other_mtime != 0 && other_mtime != mtime ())
+ return false;
+ }
+ if (size () != 0)
+ {
+ ::Dwarf_Word other_size = other.size ();
+ if (other_size != 0 && other_size != size ())
+ return false;
+ }
+ return name () == other.name ();
+ }
+ template<typename other_file>
+ inline bool operator!= (const other_file &other) const
+ {
+ return !(*this == other);
+ }
+
+ // Return a value unique to us while we're in memory.
+ inline uintptr_t identity () const
+ {
+ return (uintptr_t) this;
+ }
+ };
+
+ // This describes a CU's directory table, a simple array of strings.
+ class directory_table : public std::vector<std::string>
+ {
+ private:
+ typedef std::vector<std::string> _base;
+
+ public:
+ struct hasher : public subr::container_hasher<directory_table> {};
+
+ directory_table () {}
+
+ template<typename table>
+ directory_table (const table &other)
+ : _base (other.begin (), other.end ()) {}
+
+ std::string to_string () const;
+
+ template<typename table>
+ inline bool operator== (const table &other) const
+ {
+ return size () == other.size () && subr::container_equal (*this, other);
+ }
+ template<typename table>
+ inline bool operator!= (const table &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ private:
+
+ /* This is the common base class for all line_entry<T> instantiations.
+ For some reason beyond my ken, std::bitset<flag_count>::reference
+ as a return type is rejected by the compiler when used in a template
+ class, but not a non-template class. Go figure. */
+ class line_entry_common
+ {
+ protected:
+ unsigned int _m_line;
+ unsigned int _m_column;
+
+ enum flag_bit
+ {
+ flag_statement,
+ flag_basic_block,
+ flag_end_sequence,
+ flag_prologue_end,
+ flag_epilogue_begin,
+ flag_count
+ };
+ std::bitset<flag_count> _m_flags;
+
+ public:
+ line_entry_common ()
+ : _m_line (0), _m_column (0) {}
+
+ inline unsigned int &line ()
+ {
+ return _m_line;
+ }
+ inline unsigned int line () const
+ {
+ return _m_line;
+ }
+ inline unsigned int &column ()
+ {
+ return _m_column;
+ }
+ inline unsigned int column () const
+ {
+ return _m_column;
+ }
+
+#define _DWARF_EDIT_LE_FLAG(what) \
+ bool what () const \
+ { \
+ return _m_flags[flag_##what]; \
+ } \
+ std::bitset<flag_count>::reference what () \
+ { \
+ return _m_flags[flag_##what]; \
+ }
+ _DWARF_EDIT_LE_FLAG (statement)
+ _DWARF_EDIT_LE_FLAG (basic_block)
+ _DWARF_EDIT_LE_FLAG (end_sequence)
+ _DWARF_EDIT_LE_FLAG (prologue_end)
+ _DWARF_EDIT_LE_FLAG (epilogue_begin)
+#undef _DWARF_EDIT_LE_FLAG
+ };
+
+ public:
+ /* This holds a line table entry.
+ It's parameterized by the source_file representation. */
+ template<typename source_file>
+ class line_entry : public line_entry_common
+ {
+ private:
+ ::Dwarf_Addr _m_addr; // XXX dwfl, reloc
+ source_file _m_file;
+
+ public:
+
+ struct hasher
+ : public std::unary_function<line_entry, size_t>
+ {
+ size_t operator () (const line_entry &v) const
+ {
+ size_t hash = 0;
+ subr::hash_combine (hash, v._m_addr);
+ subr::hash_combine (hash, v._m_file);
+ subr::hash_combine (hash, v._m_line);
+ subr::hash_combine (hash, v._m_column);
+ return hash;
+ }
+ };
+
+ line_entry (::Dwarf_Addr addr)
+ : line_entry_common (), _m_addr (addr), _m_file ()
+ {}
+
+ template<typename entry>
+ line_entry (const entry &other)
+ : line_entry_common (), _m_addr (0), _m_file ()
+ {
+ *this = other;
+ }
+
+ template<typename entry>
+ line_entry &operator= (const entry &other)
+ {
+ _m_addr = other.address ();
+ _m_file = other.file ();
+ _m_line = other.line ();
+ _m_column = other.column ();
+ statement () = other.statement ();
+ basic_block () = other.basic_block ();
+ end_sequence () = other.end_sequence ();
+ prologue_end () = other.prologue_end ();
+ epilogue_begin () = other.epilogue_begin ();
+ return *this;
+ }
+
+ inline ::Dwarf_Addr &address ()
+ {
+ return _m_addr;
+ }
+ inline ::Dwarf_Addr address () const
+ {
+ return _m_addr;
+ }
+ inline source_file &file ()
+ {
+ return _m_file;
+ }
+ inline const source_file &file () const
+ {
+ return _m_file;
+ }
+
+ template<typename entry>
+ bool operator< (const entry &other) const
+ {
+ return address () < other.address ();
+ }
+ template<typename entry>
+ bool operator> (const entry &other) const
+ {
+ return address () > other.address ();
+ }
+ template<typename entry>
+ bool operator<= (const entry &other) const
+ {
+ return address () <= other.address ();
+ }
+ template<typename entry>
+ bool operator>= (const entry &other) const
+ {
+ return address () >= other.address ();
+ }
+
+ template<typename entry>
+ inline bool operator== (const entry &other) const
+ {
+ return (address () == other.address ()
+ && line () == other.line ()
+ && column () == other.column ()
+ && statement () == other.statement ()
+ && basic_block () == other.basic_block ()
+ && end_sequence () == other.end_sequence ()
+ && prologue_end () == other.prologue_end ()
+ && epilogue_begin () == other.epilogue_begin ()
+ && file () == other.file ());
+ }
+ template<typename entry>
+ inline bool operator!= (const entry &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ /* This holds a line table.
+ It's parameterized by the line_entry representation. */
+ template<typename line_entry>
+ class line_table : public std::vector<line_entry>
+ {
+ private:
+ typedef std::vector<line_entry> _base;
+
+ public:
+ typedef typename _base::size_type size_type;
+ typedef typename _base::difference_type difference_type;
+ typedef typename _base::value_type value_type;
+ typedef typename _base::iterator iterator;
+ typedef typename _base::const_iterator const_iterator;
+
+ struct hasher : public subr::container_hasher<line_table> {};
+
+ line_table () {}
+
+ template<typename table>
+ line_table (const table &other) : _base (other.begin (), other.end ()) {}
+
+ std::string to_string () const;
+
+ template<typename table>
+ inline bool operator== (const table &other) const
+ {
+ return (_base::size () == other.size ()
+ && subr::container_equal (*this, other));
+ }
+ template<typename table>
+ inline bool operator!= (const table &other) const
+ {
+ return !(*this == other);
+ }
+
+ // Look up by matching address.
+ iterator find (::Dwarf_Addr);
+ const_iterator find (::Dwarf_Addr) const;
+ };
+
+ /* This holds the entirety of line information. It's parameterized
+ by the directory_table and line_table representations. */
+ template<typename directory_table, typename line_table>
+ class line_info_table
+ : private std::pair<directory_table, line_table>
+ {
+ private:
+ typedef std::pair<directory_table, line_table> _base;
+
+ public:
+ friend class subr::base_hasher<line_info_table, _base>;
+ typedef subr::base_hasher<line_info_table, _base> hasher;
+
+ inline line_info_table () : _base () {}
+
+ template<typename table>
+ inline line_info_table (const table &other)
+ : _base (other.include_directories (), other.lines ())
+ {}
+
+ template<typename table>
+ inline line_info_table &operator= (const table &other)
+ {
+ this->first = directory_table (other.include_directories ());
+ this->second = line_table (other.lines ());
+ return *this;
+ }
+
+ std::string to_string () const;
+
+ inline directory_table &include_directories ()
+ {
+ return this->first;
+ }
+ inline const directory_table &include_directories () const
+ {
+ return this->first;
+ }
+ inline line_table &lines ()
+ {
+ return this->second;
+ }
+ inline const line_table &lines () const
+ {
+ return this->second;
+ }
+
+ template<typename table>
+ inline bool operator== (const table &other) const
+ {
+ return (include_directories () == other.include_directories ()
+ && lines () == other.lines ());
+ }
+ template<typename table>
+ inline bool operator!= (const table &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ class dwarf_enum
+ : private std::pair< ::Dwarf_Word, unsigned int>
+ {
+ private:
+ typedef std::pair< ::Dwarf_Word, unsigned int> _base;
+
+ protected:
+ inline dwarf_enum ()
+ : _base (0, 0)
+ {}
+
+ public:
+ friend class subr::base_hasher<dwarf_enum, _base>;
+ typedef subr::base_hasher<dwarf_enum, _base> hasher;
+
+ inline dwarf_enum (unsigned int attr, unsigned int value)
+ : _base (value, attr)
+ {}
+
+ template<typename constant>
+ inline dwarf_enum (const constant &other)
+ : _base (static_cast<unsigned int> (other), other.which ())
+ {}
+
+ // Return the DW_AT_* indicating which enum this value belongs to.
+ inline unsigned int which () const
+ {
+ return this->second;
+ }
+
+ inline operator unsigned int () const
+ {
+ return this->first;
+ }
+
+ inline dwarf_enum &operator= (::Dwarf_Word value)
+ {
+ this->first = value;
+ return *this;
+ }
+
+ inline dwarf_enum &operator= (const dwarf_enum& other)
+ {
+ if (this->second == 0)
+ {
+ throw std::logic_error ("dwarf_enum default constructed");
+ this->second = other.second;
+ }
+ else if (this->second != other.second)
+ throw std::runtime_error
+ ("cannot assign dwarf_constant () from "
+ + dwarf::attributes::name (other.second) + "to "
+ + dwarf::attributes::name (this->second));
+
+ this->first = other.first;
+ return *this;
+ }
+
+ template<typename constant>
+ inline dwarf_enum &operator= (const constant& other)
+ {
+ return *this = dwarf_enum (other.which (), other);
+ }
+
+ std::string to_string () const;
+
+ const char *identifier () const;
+ const char *name () const;
+
+ template<typename constant>
+ inline bool operator== (const constant &other) const
+ {
+ return (static_cast<unsigned int> (*this)
+ == static_cast<unsigned int> (other));
+ }
+ template<typename constant>
+ inline bool operator!= (const constant &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ // Same as set<pair<Dwarf_Addr, Dwarf_Addr>>.
+ typedef dwarf::arange_list range_list;
+
+ class location_attr
+ : public std::map<dwarf::location_attr::key_type, std::vector<uint8_t> >
+ {
+ private:
+ typedef std::map<dwarf::location_attr::key_type,
+ std::vector<uint8_t> > _base;
+
+ template<typename pair>
+ struct nonempty : public std::unary_function<pair, bool>
+ {
+ inline bool operator () (const pair &x)
+ {
+ return !x.second.empty ();
+ }
+ };
+
+ public:
+ typedef _base::size_type size_type;
+ typedef _base::difference_type difference_type;
+ typedef _base::key_type key_type;
+ typedef _base::mapped_type mapped_type;
+ typedef _base::value_type value_type;
+ typedef _base::iterator iterator;
+ typedef _base::const_iterator const_iterator;
+
+ struct hasher : public subr::container_hasher<location_attr> {};
+
+ inline location_attr () : _base () {}
+ inline location_attr (const location_attr &other)
+ : _base (static_cast<const _base &> (other)) {}
+ template<typename loc>
+ inline location_attr (const loc &other) : _base ()
+ {
+ *this = other;
+ }
+
+ template<typename loc>
+ inline location_attr &operator= (const loc &other)
+ {
+ clear ();
+ if (other.empty ())
+ ;
+ else if (other.is_list ())
+ for (typename loc::const_iterator i = other.begin ();
+ i != other.end ();
+ ++i)
+ {
+ const typename loc::mapped_type &x = (*i).second;
+ (*this)[(*i).first] = mapped_type (x.begin (), x.end ());
+ }
+ else
+ {
+ mapped_type v = other.location ();
+ (*this)[key_type (0, -1)] = v;
+ }
+ return *this;
+ }
+
+ inline bool is_list () const
+ {
+ if (empty ())
+ return false;
+ if (size () > 1)
+ return true;
+
+ const key_type &elt = begin ()->first;
+ return !(elt.first == 0 && elt.second == (Dwarf_Addr) -1);
+ }
+
+ inline mapped_type &location ()
+ {
+ if (empty ())
+ return (*this)[key_type (0, -1)];
+
+ value_type &v = *begin ();
+ if (v.first.first != 0 || v.first.second != (Dwarf_Addr) -1
+ || size () > 1)
+ throw std::runtime_error ("location is list, not single location");
+
+ return v.second;
+ }
+ inline const mapped_type &location () const
+ {
+ if (size () == 1)
+ {
+ const value_type &v = *begin ();
+ if (v.first.first == 0 && v.first.second == (Dwarf_Addr) -1)
+ return v.second;
+ }
+ throw std::runtime_error ("location is list, not single location");
+ }
+
+ template<typename other_attr>
+ bool operator== (const other_attr &other) const
+ {
+ if (empty ())
+ return (other.empty ()
+ || std::find_if (other.begin (), other.end (),
+ nonempty<typename other_attr::value_type> ()
+ ) == other.end ());
+ if (!is_list ())
+ return (!other.is_list () && !other.empty ()
+ && subr::container_equal (location (), other.location ()));
+
+ return other.is_list () && subr::container_equal (*this, other);
+ }
+ template<typename other_attr>
+ inline bool operator!= (const other_attr &other) const
+ {
+ return !(*this == other);
+ }
+
+ std::string to_string () const;
+ };
+
+ template<typename impl, bool alloc_values = true>
+ struct value
+ {
+ struct value_dispatch
+ {
+ virtual ~value_dispatch () {}
+ };
+
+ typedef value_dispatch value_cell_type;
+
+ static const bool delete_value = alloc_values;
+
+ template<typename flavor>
+ static inline flavor &
+ variant (flavor *&, const value_dispatch *&)
+ {
+ assert (!alloc_values);
+ throw std::logic_error ("can't happen!");
+ }
+
+ template<typename flavor>
+ static inline flavor &
+ variant (flavor *&result, value_dispatch *&value)
+ {
+ assert (alloc_values);
+ if (value == NULL)
+ {
+ result = new flavor;
+ value = result;
+ return *result;
+ }
+ result = dynamic_cast<flavor *> (value);
+ if (result == NULL)
+ throw std::runtime_error ("wrong value type");
+ return *result;
+ }
+
+ template<typename arg_type>
+ struct maker
+ {
+ inline explicit maker (const arg_type &) {}
+
+ template<typename flavor, typename input>
+ static inline void
+ make (value_dispatch *&v, flavor *&result,
+ int /*whatattr*/, const input &x, arg_type &arg)
+ {
+ assert (alloc_values);
+ v = result = new flavor (x, arg);
+ }
+ };
+
+ template<typename arg_type>
+ static inline maker<arg_type> make (arg_type &arg)
+ {
+ return maker<arg_type> (arg);
+ }
+
+ struct value_string : public value_dispatch, public std::string
+ {
+ typedef subr::hash<std::string> hasher;
+
+ inline value_string () {}
+
+ template<typename string, typename arg_type>
+ inline value_string (const string &s, arg_type &)
+ : std::string (s)
+ {}
+
+ template<typename string>
+ inline value_string (const string &s)
+ : std::string (s)
+ {}
+
+ std::string to_string () const
+ {
+ std::string result ("\"");
+ result += *this;
+ result += "\"";
+ return result;
+ }
+ };
+
+ struct value_identifier : public value_string
+ {
+ inline value_identifier () {}
+
+ template<typename id, typename arg_type>
+ inline value_identifier (const id &s, arg_type &arg)
+ : value_string (s, arg)
+ {}
+
+ template<typename id>
+ inline value_identifier (const id &s)
+ : value_string (s)
+ {}
+ };
+
+ struct value_reference : public value_dispatch
+ {
+ typedef typename impl::debug_info_entry::pointer value_type;
+ value_type ref;
+
+ // Default constructor: reference to nowhere, invalid.
+ inline value_reference ()
+ : ref ()
+ {}
+
+ /* This is only kosher for a pointer into the same dwarf_edit
+ object. This is what plain assignment does. This just uses
+ this pointer, rather than translating it from another file
+ into this one (which requires a tracker). */
+ inline value_reference (const value_type &i, subr::nothing &)
+ : ref (i)
+ {}
+
+ template<typename iter, typename tracker>
+ inline value_reference (const iter &i, tracker &t)
+ : ref () // Invalid until t.finish ().
+ {
+ t.refer (&ref, i);
+ }
+ };
+
+ struct value_flag : public value_dispatch
+ {
+ bool flag;
+
+ inline value_flag (bool t = true)
+ : flag (t)
+ {}
+
+ template<typename arg_type>
+ inline value_flag (bool t, arg_type &)
+ : flag (t)
+ {}
+ };
+
+ struct value_address : public value_dispatch
+ {
+ // XXX dwfl, reloc
+ ::Dwarf_Addr addr;
+
+ inline value_address (::Dwarf_Addr x = 0)
+ : addr (x)
+ {}
+
+ template<typename arg_type>
+ inline value_address (::Dwarf_Addr x, arg_type &)
+ : addr (x)
+ {}
+
+ struct hasher : public std::unary_function<value_address, size_t>
+ {
+ inline size_t operator () (const value_address &c) const
+ {
+ return c.addr;
+ }
+ };
+
+ inline operator ::Dwarf_Addr () const
+ {
+ return addr;
+ }
+
+ inline bool operator== (::Dwarf_Addr x) const
+ {
+ return addr == x;
+ }
+ };
+
+ struct value_rangelistptr : public value_dispatch, public range_list
+ {
+ inline value_rangelistptr () {}
+
+ template<typename list, typename arg_type>
+ inline value_rangelistptr (const list &other, arg_type &)
+ : range_list (other)
+ {}
+
+ template<typename list>
+ inline value_rangelistptr (const list &other)
+ : range_list (other)
+ {}
+ };
+
+ struct value_lineptr : public value_dispatch, public impl::line_info_table
+ {
+ inline value_lineptr () {}
+
+ template<typename table, typename arg_type>
+ inline value_lineptr (const table &other, arg_type &)
+ : impl::line_info_table (other)
+ {}
+ };
+
+ struct value_constant : public value_dispatch
+ {
+ union
+ {
+ ::Dwarf_Word word;
+ ::Dwarf_Sword sword;
+ };
+
+ inline value_constant (::Dwarf_Word value = 0)
+ : word (value)
+ {}
+
+ template<typename arg_type>
+ inline value_constant (::Dwarf_Word x, arg_type &)
+ : word (x)
+ {}
+
+ struct hasher : public std::unary_function<value_constant, size_t>
+ {
+ inline size_t operator () (const value_constant &c) const
+ {
+ return c.word;
+ }
+ };
+
+ inline operator ::Dwarf_Word () const
+ {
+ return word;
+ }
+
+ inline bool operator== (::Dwarf_Word x) const
+ {
+ return word == x;
+ }
+ };
+
+ struct value_constant_block : public value_dispatch,
+ public std::vector<uint8_t>
+ {
+ typedef subr::hash<std::vector<uint8_t> > hasher;
+
+ inline value_constant_block () {}
+
+ template<typename block, typename arg_type>
+ inline value_constant_block (const block &b, arg_type &)
+ : std::vector<uint8_t> (b.begin (), b.end ())
+ {}
+
+ template<typename block>
+ inline value_constant_block (const block &b)
+ : std::vector<uint8_t> (b.begin (), b.end ())
+ {}
+ };
+
+ struct value_dwarf_constant : public value_dispatch, public dwarf_enum
+ {
+ inline value_dwarf_constant () {}
+
+ template<typename constant>
+ inline value_dwarf_constant (const constant &other)
+ : dwarf_enum (other)
+ {}
+
+ template<typename constant, typename arg_type>
+ inline value_dwarf_constant (const constant &other, arg_type &)
+ : dwarf_enum (other)
+ {}
+ };
+
+ struct value_source_file : public value_dispatch, public source_file
+ {
+ inline value_source_file () {}
+
+ template<typename file, typename arg_type>
+ inline value_source_file (const file &other, arg_type &)
+ : source_file (other)
+ {}
+
+ template<typename file>
+ inline value_source_file (const file &other)
+ : source_file (other)
+ {}
+ };
+
+ struct value_source_line : public value_dispatch
+ {
+ unsigned int n;
+
+ inline value_source_line (unsigned int x = 0)
+ : n (x)
+ {}
+
+ template<typename arg_type>
+ inline value_source_line (unsigned int m, arg_type &)
+ : n (m)
+ {}
+
+ struct hasher : public std::unary_function<value_source_line, size_t>
+ {
+ inline size_t operator () (const value_source_line &c) const
+ {
+ return c.n;
+ }
+ };
+
+ inline operator unsigned int () const
+ {
+ return n;
+ }
+
+ inline bool operator== (unsigned int x) const
+ {
+ return n == x;
+ }
+ };
+
+ class value_source_column : public value_source_line
+ {
+ };
+
+ struct value_macptr : public value_dispatch {};
+
+ struct value_location : public value_dispatch, public location_attr
+ {
+ inline value_location () {}
+
+ template<typename loc, typename arg_type>
+ inline value_location (const loc &other, arg_type &)
+ : location_attr (other)
+ {}
+
+ template<typename loc>
+ inline value_location (const loc &other)
+ : location_attr (other)
+ {}
+ };
+ };
+
+ // Forward decl.
+ template<class impl, typename v = value<impl> > class attributes_type;
+
+ template<class impl, typename vw = value<impl> >
+ class attr_value
+ {
+ friend class attributes_type<impl, vw>;
+ friend class value<impl>;
+
+ protected:
+ typename vw::value_cell_type *_m_value;
+ typedef typename impl::debug_info_entry::pointer die_ptr;
+
+ template<typename value, typename arg_type = subr::nothing>
+ struct init
+ {
+ inline init (attr_value *av, int whatattr,
+ const value &other, arg_type &arg)
+ {
+ do_init (av, whatattr, other, arg);
+ }
+
+ inline init (attr_value *av, int whatattr, const value &other)
+ {
+ arg_type dummy;
+ do_init (av, whatattr, other, dummy);
+ }
+
+ static inline void do_init (attr_value *av, int whatattr,
+ const value &other, arg_type &arg)
+ {
+ switch (other.what_space ())
+ {
+#define _DWARF_DATA_AV_MAKE(flavor, fetch) \
+ case dwarf::VS_##flavor: \
+ { \
+ typename vw::value_##flavor *p; \
+ vw::make (arg) \
+ .make (av->_m_value, p, whatattr, other.fetch (), arg); \
+ } \
+ break
+
+ _DWARF_DATA_AV_MAKE (identifier, identifier);
+ _DWARF_DATA_AV_MAKE (string, string);
+ _DWARF_DATA_AV_MAKE (flag, flag);
+ _DWARF_DATA_AV_MAKE (rangelistptr, ranges);
+ _DWARF_DATA_AV_MAKE (lineptr, line_info);
+ _DWARF_DATA_AV_MAKE (address, address);
+ _DWARF_DATA_AV_MAKE (source_line, source_line);
+ _DWARF_DATA_AV_MAKE (source_file, source_file);
+ _DWARF_DATA_AV_MAKE (dwarf_constant, dwarf_constant);
+ _DWARF_DATA_AV_MAKE (reference, reference);
+ _DWARF_DATA_AV_MAKE (location, location);
+ //_DWARF_DATA_AV_MAKE (macptr, macros); XXX
+
+ case dwarf::VS_constant:
+ if (other.constant_is_integer ())
+ {
+ typename vw::value_constant *p;
+ vw::make (arg).make (av->_m_value, p, whatattr,
+ other.constant (), arg);
+ }
+ else
+ {
+ typename vw::value_constant_block *p;
+ vw::make (arg).make (av->_m_value, p, whatattr,
+ other.constant_block (), arg);
+ }
+ break;
+
+ default:
+ case dwarf::VS_discr_list:
+ throw std::runtime_error ("XXX unimplemented");
+
+#undef _DWARF_DATA_AV_MAKE
+ }
+ }
+ };
+
+ // This is kosher only when freshly default-constructed.
+ template<typename value, typename arg_type>
+ inline void set (int whatattr, const value &other, arg_type &arg)
+ {
+ assert (_m_value == NULL);
+ init<value, arg_type> me (this, whatattr, other, arg);
+ }
+
+ template<typename flavor>
+ inline flavor &const_variant () const
+ {
+ flavor *p = dynamic_cast<flavor *> (_m_value);
+ if (p == NULL)
+ throw std::runtime_error (_m_value != NULL ? "wrong value type"
+ : "uninitialized attr_value (const?)");
+ return *p;
+ }
+
+ template<typename flavor>
+ inline const flavor &variant () const
+ {
+ return const_variant<const flavor> ();
+ }
+
+ template<typename flavor>
+ inline flavor &variant ()
+ {
+ flavor *p;
+ return vw::variant (p, _m_value);
+ }
+
+ template<typename value>
+ inline attr_value &copy (const value &other)
+ {
+ if (_m_value != NULL)
+ {
+ delete _m_value;
+ _m_value = NULL;
+ }
+ init<value> me (this, 0, other);
+ return *this;
+ }
+
+ public:
+ attr_value (const attr_value &other)
+ : _m_value (NULL)
+ {
+ if (other._m_value != NULL)
+ init<attr_value> me (this, 0, other);
+ }
+
+ inline attr_value ()
+ : _m_value (NULL)
+ {}
+
+ ~attr_value ()
+ {
+ if (vw::delete_value && _m_value != NULL)
+ delete _m_value;
+ }
+
+ template<typename value>
+ inline attr_value &operator= (const value &other)
+ {
+ return copy (other);
+ }
+
+ /* This is the same as the generic template one,
+ but we have to define it explicitly to override
+ the default assignment operator. */
+ inline attr_value &operator= (const attr_value &other)
+ {
+ return copy (other);
+ }
+
+ dwarf::value_space what_space () const;
+ inline std::string to_string () const;
+
+ inline const bool &flag () const
+ {
+ return variant<typename vw::value_flag> ().flag;
+ }
+
+ inline bool &flag ()
+ {
+ return variant<typename vw::value_flag> ().flag;
+ }
+
+ // XXX dwfl, reloc
+ inline const ::Dwarf_Addr &address () const
+ {
+ return variant<typename vw::value_address> ().addr;
+ }
+
+ // XXX dwfl, reloc
+ inline ::Dwarf_Addr &address ()
+ {
+ return variant<typename vw::value_address> ().addr;
+ }
+
+ inline const die_ptr &reference () const
+ {
+ return variant<typename vw::value_reference> ().ref;
+ }
+
+ inline die_ptr &reference ()
+ {
+ return variant<typename vw::value_reference> ().ref;
+ }
+
+ inline const location_attr &location () const
+ {
+ return static_cast<const location_attr &>
+ (variant<typename vw::value_location> ());
+ }
+
+ inline location_attr &location ()
+ {
+ return static_cast<location_attr &>
+ (variant<typename vw::value_location> ());
+ }
+
+ inline const std::string &string () const
+ {
+ if (dynamic_cast<const typename vw::value_source_file *> (_m_value))
+ return source_file ().name ();
+ return static_cast<const std::string &>
+ (variant<typename vw::value_string> ());
+ }
+
+ inline std::string &string ()
+ {
+ if (dynamic_cast<const typename vw::value_source_file *> (_m_value))
+ return source_file ().name ();
+ return static_cast<std::string &>
+ (variant<typename vw::value_string> ());
+ }
+
+ inline const std::string &identifier () const
+ {
+ return static_cast<const std::string &>
+ (variant<typename vw::value_identifier> ());
+ }
+
+ inline std::string &identifier ()
+ {
+ return static_cast<std::string &>
+ (variant<typename vw::value_identifier> ());
+ }
+
+ inline const typename impl::source_file &source_file () const
+ {
+ return static_cast<const typename impl::source_file &>
+ (variant<typename vw::value_source_file> ());
+ }
+
+ inline typename impl::source_file &source_file ()
+ {
+ return static_cast<typename impl::source_file &>
+ (variant<typename vw::value_source_file> ());
+ }
+
+ inline const unsigned int &source_line () const
+ {
+ return variant<typename vw::value_source_line> ().n;
+ }
+
+ inline unsigned int &source_line ()
+ {
+ return variant<typename vw::value_source_line> ().n;
+ }
+
+ inline const unsigned int &source_column () const
+ {
+ return variant<typename vw::value_source_column> ().n;
+ }
+
+ inline unsigned int &source_column ()
+ {
+ return variant<typename vw::value_source_column> ().n;
+ }
+
+ inline const ::Dwarf_Word &constant () const
+ {
+ return variant<typename vw::value_constant> ().word;
+ }
+
+ inline ::Dwarf_Word &constant ()
+ {
+ return variant<typename vw::value_constant> ().word;
+ }
+
+ inline const ::Dwarf_Sword &signed_constant () const
+ {
+ return variant<typename vw::value_constant> ().sword;
+ }
+
+ inline ::Dwarf_Sword &signed_constant ()
+ {
+ return variant<typename vw::value_constant> ().sword;
+ }
+
+ inline const std::vector<uint8_t> &constant_block () const
+ {
+ return static_cast<const std::vector<uint8_t> &>
+ (variant<typename vw::value_constant_block> ());
+ }
+
+ inline std::vector<uint8_t> &constant_block ()
+ {
+ return static_cast<std::vector<uint8_t> &>
+ (variant<typename vw::value_constant_block> ());
+ }
+
+ inline const typename impl::dwarf_enum &dwarf_constant () const
+ {
+ return variant<typename vw::value_dwarf_constant> ();
+ }
+
+ inline typename impl::dwarf_enum &dwarf_constant ()
+ {
+ return variant<typename vw::value_dwarf_constant> ();
+ }
+
+ inline bool constant_is_integer () const
+ {
+ return (dynamic_cast<const typename vw::value_constant *> (_m_value)
+ != NULL);
+ }
+
+ inline const typename impl::range_list &ranges () const
+ {
+ return static_cast<const range_list &>
+ (variant<const typename vw::value_rangelistptr> ());
+ }
+
+ inline typename impl::range_list &ranges ()
+ {
+ return static_cast<range_list &>
+ (variant<typename vw::value_rangelistptr> ());
+ }
+
+ inline const typename impl::line_info_table &line_info () const
+ {
+ return static_cast<const typename impl::line_info_table &>
+ (variant<const typename vw::value_lineptr> ());
+ }
+
+ inline typename impl::line_info_table &line_info ()
+ {
+ return static_cast<typename impl::line_info_table &>
+ (variant<typename vw::value_lineptr> ());
+ }
+
+ // macptr
+
+ template<typename value>
+ inline bool operator== (const value &other) const
+ {
+ const dwarf::value_space what = what_space ();
+ if (likely (other.what_space () == what))
+ switch (what)
+ {
+ case dwarf::VS_identifier:
+ return identifier () == other.identifier ();
+ case dwarf::VS_string:
+ return string () == other.string ();
+ case dwarf::VS_reference:
+ return reference () == other.reference ();
+ case dwarf::VS_flag:
+ return flag () == other.flag ();
+ case dwarf::VS_rangelistptr:
+ return ranges () == other.ranges ();
+ case dwarf::VS_lineptr:
+ return line_info () == other.line_info ();
+ case dwarf::VS_constant:
+ if (constant_is_integer ())
+ return (other.constant_is_integer ()
+ && constant () == other.constant ());
+ return (!other.constant_is_integer ()
+ && constant_block () == other.constant_block ());
+ case dwarf::VS_source_file:
+ return source_file () == other.source_file ();
+ case dwarf::VS_source_line:
+ return source_line () == other.source_line ();
+ case dwarf::VS_source_column:
+ return source_column () == other.source_column ();
+ case dwarf::VS_address:
+ return address () == other.address ();
+ case dwarf::VS_location:
+ return location () == other.location ();
+ case dwarf::VS_dwarf_constant:
+ return dwarf_constant () == other.dwarf_constant ();
+#if 0
+ case dwarf::VS_macptr:
+ return macptr () == other.macptr ();
+#endif
+ default:
+ case dwarf::VS_discr_list:
+ throw std::runtime_error ("XXX unimplemented");
+ }
+ return false;
+ }
+ template<typename value>
+ inline bool operator!= (const value &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ template<class impl, typename v>
+ class attributes_type
+ : public std::map<int, typename impl::attr_value>
+ {
+ friend class impl::me;
+ protected:
+ typedef std::map<int, typename impl::attr_value> base_type;
+
+ inline attributes_type () {}
+
+ template<typename iter>
+ inline attributes_type (const iter &from, const iter &to)
+ : base_type (from, to)
+ {}
+
+ template<typename input, typename arg_type>
+ inline void set (const input &other, arg_type &c)
+ {
+ for (typename input::const_iterator attr = other.begin ();
+ attr != other.end ();
+ ++attr)
+ (*this)[(*attr).first].set ((*attr).first, (*attr).second, c);
+ }
+
+ public: // XXX should be protected
+
+ /* We don't use the base_type (begin, end) iterator constructor here
+ for good reason. The ref-maker needs to collect back-pointers
+ into our mapped_value (attr_value) objects. It would not fly to
+ have that done in a temporary attr_value object that gets copied
+ into the map cell by assignment. We must make sure that when a
+ value_reference is constructed, it is really the one sitting in
+ our map that the ref-maker will want to update later. */
+ template<typename input, typename arg_type>
+ inline attributes_type (const input &other, arg_type &c)
+ : base_type ()
+ {
+ set (other, c);
+ }
+
+ public:
+ typedef typename base_type::key_type key_type;
+ typedef typename base_type::value_type value_type;
+ typedef typename base_type::mapped_type mapped_type;
+
+ static inline bool ordered ()
+ {
+ return true;
+ }
+
+ template<typename attrs>
+ inline operator attrs () const
+ {
+ return attrs (base_type::begin (), base_type::end ());
+ }
+ };
+
+ };
+
+ // Explicit specializations.
+ template<>
+ std::string to_string<dwarf_data::dwarf_enum> (const dwarf_data::dwarf_enum&);
+ inline std::string dwarf_data::dwarf_enum::to_string () const
+ {
+ return elfutils::to_string (*this); // Use that.
+ }
+
+ template<class impl, typename v>
+ inline std::string dwarf_data::attr_value<impl, v>::to_string () const
+ {
+ return elfutils::to_string (*this); // Use that.
+ }
+
+};
+
+#endif
diff --git a/libdw/c++/dwarf_edit b/libdw/c++/dwarf_edit
new file mode 100644
index 00000000..9b093697
--- /dev/null
+++ b/libdw/c++/dwarf_edit
@@ -0,0 +1,398 @@
+/* elfutils::dwarf_edit -- mutable DWARF representation in -*- C++ -*-
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifndef _ELFUTILS_DWARF_EDIT
+#define _ELFUTILS_DWARF_EDIT 1
+
+#include "dwarf"
+#include "dwarf_data"
+#include "dwarf_ref_maker"
+
+#include <type_traits>
+
+
+/* Read the comments for elfutils::dwarf first.
+
+ The elfutils::dwarf_edit class is template-compatible with the logical
+ containers described in elfutils::dwarf, and copy-constructible from the
+ input class.
+
+ The elfutils::dwarf_edit containers are mutable, unlike the input
+ classes. You can modify the DWARF directly in all the normal ways the
+ corresponding std containers have, or build it up from scratch. When
+ you have it how you want it, you can pass it into elfutils::dwarf_output.
+
+ The dwarf_edit classes will use unreasonable amounts of memory for large
+ DWARF data sets, like from reading in whole large program and DSO files.
+ To transform input files efficiently, you should construct dwarf_output
+ directly from input dwarf with transformations applied on the fly, and
+ not use dwarf_edit at all.
+
+ dwarf_edit is the only mutable representation and so it's easy to use in
+ a straightforward imperative style. Use it for transformations on small
+ data files, or for creating small data sets from scratch. */
+
+// DWARF manipulation interfaces (pure object construction)
+namespace elfutils
+{
+ template<typename elt>
+ inline bool operator== (const std::vector<elt> &a, const const_vector<elt> &b)
+ {
+ return a.size () == b.size () && subr::container_equal (a, b);
+ }
+
+ class dwarf_edit
+ {
+ private:
+ friend class dwarf_data;
+ typedef dwarf_edit me;
+
+ public:
+ typedef dwarf_data::source_file source_file;
+ typedef dwarf_data::directory_table directory_table;
+ typedef dwarf_data::line_entry<source_file> line_entry;
+ typedef dwarf_data::line_table<line_entry> line_table;
+ typedef dwarf_data::line_info_table<directory_table,
+ line_table> line_info_table;
+ typedef dwarf_data::dwarf_enum dwarf_enum;
+ typedef dwarf_data::range_list range_list;
+ typedef dwarf_data::location_attr location_attr;
+ typedef dwarf_data::attr_value<dwarf_edit> attr_value;
+
+ class debug_info_entry
+ {
+ friend class subr::create_container;
+
+ public:
+ typedef dwarf_data::attributes_type<dwarf_edit> attributes_type;
+
+ class children_type : public std::list<debug_info_entry>
+ {
+ friend class debug_info_entry;
+ private:
+ inline children_type () {}
+
+ template<typename input, typename tracker>
+ static inline void
+ equivalence (const iterator &out,
+ const typename input::const_iterator &in,
+ bool, // last-sibling
+ tracker &t)
+ {
+ out->set (*in, t);
+ t.equivalence (out, in);
+ }
+
+ template<typename input, typename tracker>
+ inline children_type (const input &other, tracker &t)
+ {
+ subr::create_container (this, other, t, equivalence<input, tracker>);
+ }
+
+ public:
+ typedef debug_info_entry value_type;
+
+ inline iterator add_entry (int tag, const iterator &pos)
+ {
+ return insert (pos, debug_info_entry (tag));
+ }
+
+ inline iterator add_entry (int tag)
+ {
+ return add_entry (tag, end ());
+ }
+ };
+
+ typedef children_type::iterator pointer;
+ typedef children_type::const_iterator const_pointer;
+
+ protected:
+ int _m_tag;
+ attributes_type _m_attributes;
+ children_type _m_children;
+
+ // This is can only be used by the children_type constructor,
+ // which immediately calls set.
+ inline debug_info_entry ()
+ : _m_tag (-1), _m_attributes (), _m_children ()
+ {}
+
+ template<typename die_type, typename arg_type>
+ inline void set (const die_type &die, arg_type &arg)
+ {
+ try
+ {
+ _m_tag = die.tag ();
+ _m_attributes.swap (attributes_type (die.attributes (), arg));
+ _m_children.swap (children_type (die.children (), arg));
+ }
+ catch (...)
+ {
+ // Never leave a partially-formed DIE.
+ _m_tag = -1;
+ _m_attributes.clear ();
+ _m_children.clear ();
+ throw;
+ };
+ }
+
+ public:
+ inline debug_info_entry (int t)
+ : _m_tag (t), _m_attributes (), _m_children ()
+ {
+ if (unlikely (t <= 0))
+ throw std::invalid_argument ("invalid tag");
+ }
+
+ /* The template constructor lets us copy in from any class that has
+ compatibly iterable containers for attributes and children. */
+ template<typename die_type, typename tracker>
+ debug_info_entry (const die_type &die, tracker &t)
+ : _m_tag (die.tag ()),
+ _m_attributes (die.attributes (), t),
+ _m_children (die.children (), t)
+ {}
+
+ inline std::string to_string () const;
+
+ inline int tag () const
+ {
+ return _m_tag;
+ }
+
+ inline bool has_children () const
+ {
+ return !_m_children.empty ();
+ }
+
+ inline children_type &children ()
+ {
+ return _m_children;
+ }
+ inline const children_type &children () const
+ {
+ return _m_children;
+ }
+
+ inline attributes_type &attributes ()
+ {
+ return _m_attributes;
+ }
+ inline const attributes_type &attributes () const
+ {
+ return _m_attributes;
+ }
+
+ template<typename die>
+ bool operator== (const die &other) const
+ {
+ return (other.tag () == tag ()
+ && other.attributes () == attributes ()
+ && other.children () == children ());
+ }
+ template<typename die>
+ bool operator!= (const die &other) const
+ {
+ return !(*this == other);
+ }
+
+ inline dwarf::debug_info_entry::identity_type identity () const
+ {
+ return (uintptr_t) this;
+ }
+
+ inline ::Dwarf_Off offset () const
+ {
+ return identity ();
+ }
+
+ inline ::Dwarf_Off cost () const
+ {
+ return 0;
+ }
+
+ // Convenience entry point.
+ inline pointer add_entry (int child_tag)
+ {
+ return children ().add_entry (child_tag);
+ }
+ };
+
+ typedef debug_info_entry::attributes_type::value_type attribute;
+
+ typedef dwarf_data::compile_unit<dwarf_edit> compile_unit;
+
+ // Main container anchoring all the output.
+ class compile_units
+ : public dwarf_data::compile_units<dwarf_edit>
+ {
+ friend class dwarf_edit;
+
+ private:
+ inline compile_units (const compile_units &)
+ : dwarf_data::compile_units<dwarf_edit> ()
+ {
+ throw std::logic_error
+ ("must copy-construct top-level dwarf_edit object instead");
+ }
+
+ // Constructor copying CUs from input container.
+ template<typename input, typename tracker>
+ inline compile_units (const input &other, tracker &t)
+ {
+ subr::create_container (this, other, t);
+ }
+
+ public:
+ // Default constructor: an empty container, no CUs.
+ inline compile_units () {}
+
+ inline compile_unit &add_unit ()
+ {
+ push_back (compile_unit ());
+ return back ();
+ }
+ };
+
+ private:
+ compile_units _m_units;
+
+ typedef dwarf_ref_maker<dwarf_edit, dwarf_edit> edit_ref_maker;
+
+ public:
+ class compile_units &compile_units ()
+ {
+ return _m_units;
+ }
+ const class compile_units &compile_units () const
+ {
+ return _m_units;
+ }
+
+ // Convenience entry point.
+ inline compile_unit &add_unit ()
+ {
+ return compile_units ().add_unit ();
+ }
+
+ // Default constructor: an empty container, no CUs.
+ inline dwarf_edit () {}
+
+ // Constructor copying CUs from an input file (dwarf or dwarf_edit).
+ template<typename input, typename tracker>
+ inline dwarf_edit (const input &dw, tracker &t,
+ subr::guard<tracker> guard = subr::guard<tracker> ())
+ : _m_units (dw.compile_units (), guard (t))
+ {
+ guard.clear ();
+ }
+
+ // Copying constructor with default ref-maker.
+ template<typename input>
+ inline dwarf_edit (const input &dw,
+ dwarf_ref_maker<dwarf_edit, input> t
+ = dwarf_ref_maker<dwarf_edit, input> (),
+ subr::guard<dwarf_ref_maker<dwarf_edit, input> > guard
+ = subr::guard<dwarf_ref_maker<dwarf_edit, input> > ())
+ : _m_units (dw.compile_units (), guard (t))
+ {
+ guard.clear ();
+ }
+
+ // We have to write this explicitly or it will do default-copying!
+ inline dwarf_edit (const dwarf_edit &dw,
+ edit_ref_maker t = edit_ref_maker (),
+ subr::guard<edit_ref_maker > guard
+ = subr::guard<edit_ref_maker > ())
+ : _m_units (dw.compile_units (), guard (t))
+ {
+ guard.clear ();
+ }
+
+ template<typename file>
+ inline bool operator== (const file &other) const
+ {
+ return compile_units () == other.compile_units ();
+ }
+ template<typename file>
+ inline bool operator!= (const file &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ // Explicit specializations.
+ template<>
+ std::string to_string<dwarf_edit::debug_info_entry>
+ (const dwarf_edit::debug_info_entry &);
+ inline std::string dwarf_edit::debug_info_entry::to_string () const
+ {
+ return elfutils::to_string (*this); // Use that.
+ }
+ template<>
+ std::string to_string<dwarf_edit::attribute> (const dwarf_edit::attribute &);
+ template<>
+ std::string to_string<dwarf_edit::attr_value> (const dwarf_edit::attr_value&);
+
+ template<>
+ std::string dwarf_edit::line_table::to_string () const;
+ template<>
+ std::string dwarf_edit::line_info_table::to_string () const;
+
+ // Explicit instantiations.
+ extern template class dwarf_data::line_entry<dwarf_edit::source_file>;
+ extern template class dwarf_data::line_table<dwarf_edit::line_entry>;
+ extern template class dwarf_data::line_info_table<dwarf_edit::directory_table,
+ dwarf_edit::line_table>;
+ extern template class dwarf_data::attr_value<dwarf_edit>;
+ extern template class dwarf_data::value<dwarf_edit>;
+
+};
+
+#endif // <elfutils/dwarf_edit>
diff --git a/libdw/c++/dwarf_output b/libdw/c++/dwarf_output
new file mode 100644
index 00000000..3110ea30
--- /dev/null
+++ b/libdw/c++/dwarf_output
@@ -0,0 +1,2794 @@
+/* elfutils::dwarf_output -- DWARF file generation in -*- C++ -*-
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifndef _ELFUTILS_DWARF_OUTPUT
+#define _ELFUTILS_DWARF_OUTPUT 1
+
+#include "dwarf"
+#include "dwarf_edit"
+#include "dwarf_comparator"
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <vector>
+#include <stack>
+#include <queue>
+#include <bitset>
+#include <set>
+#include <tr1/unordered_set>
+
+/* Read the comments for elfutils::dwarf first.
+
+ The elfutils::dwarf_output class is template-compatible with the logical
+ containers described in elfutils::dwarf and elfutils::dwarf_edit.
+
+ The dwarf_output representation of the DWARF data is immutable once
+ created. The only way to create the object is by copy-construction
+ from another compatible object: dwarf, dwarf_edit, or dwarf_output.
+ Construction collects all the information necessary to generate the
+ formatted DWARF sections. */
+
+namespace elfutils
+{
+ class dwarf_output_collector;
+
+ class dwarf_output
+ {
+ private:
+ friend class dwarf_output_collector;
+ friend class dwarf_data;
+ typedef dwarf_output me;
+
+ public:
+ typedef dwarf_data::source_file source_file;
+ typedef dwarf_data::directory_table directory_table;
+ typedef dwarf_data::line_entry<source_file> line_entry;
+ typedef dwarf_data::line_table<line_entry> line_table;
+ typedef dwarf_data::line_info_table<directory_table,
+ line_table> line_info_table;
+ typedef dwarf_data::dwarf_enum dwarf_enum;
+ typedef dwarf_data::range_list range_list;
+ typedef dwarf_data::location_attr location_attr;
+
+ class compile_units;
+ class debug_info_entry;
+ class attr_value;
+
+ protected:
+ static inline void never_copy ()
+ {
+ throw std::logic_error
+ ("must copy-construct top-level dwarf_output object instead");
+ }
+
+ template<typename input> class copier; // Below.
+
+#if 0
+ /* An iterator adapter for use in iterator-based constructors.
+ collectify (iterator) yields an iterator on input where *i
+ constructs output::value_type (input::value_type v, collector). */
+ template<typename input, typename output>
+ static inline typename subr::argifier<input, output,
+ dwarf_output_collector &>::result_type
+ collectify (const typename input::const_iterator &in,
+ dwarf_output_collector &c)
+ {
+ return subr::argifier<input, output, dwarf_output_collector &> (c) (in);
+ }
+#endif
+
+ /* Every kind of value is made by calling into the copier, which
+ returns a const pointer into a value_set living in the collector. */
+ struct value
+ : public dwarf_data::value<dwarf_output, false>
+ {
+ typedef const value_dispatch value_cell_type;
+
+ typedef dwarf_data::value<dwarf_output> data;
+
+ template<typename copier_type> struct maker;
+
+ template<typename arg_type>
+ static inline maker<arg_type> make (arg_type &arg)
+ {
+ return maker<arg_type> (arg);
+ }
+
+ struct value_reference; // Defined below.
+ };
+
+ struct die_info;
+ typedef std::pair<const debug_info_entry, die_info> die_info_pair;
+
+ public:
+
+ class debug_info_entry
+ {
+ friend class dwarf_output;
+ friend class dwarf_output_collector;
+
+ public:
+ class attributes_type
+ : public dwarf_data::attributes_type<dwarf_output, value>
+ {
+ friend class dwarf_output;
+
+ private:
+ typedef dwarf_data::attributes_type<dwarf_output, value> _base;
+
+ size_t _m_hash;
+
+ inline attributes_type ()
+ : _base (), _m_hash (0)
+ {}
+
+ struct same_attr : public std::equal_to<value_type>
+ {
+ bool operator () (const value_type &a,
+ const value_type &b) const
+ {
+ return a.first == b.first && a.second.is (b.second);
+ }
+ };
+
+ inline void do_hash ()
+ {
+ // Precompute our hash value based on our contents.
+ for (iterator i = begin (); i != end (); ++i)
+ subr::hash_combine (_m_hash, *i);
+ }
+
+ inline const _base &base () const
+ {
+ return *this;
+ }
+
+ public:
+ template<typename iter>
+ inline attributes_type (const iter &from, const iter &to)
+ : _base (from, to), _m_hash (0)
+ {
+ do_hash ();
+ }
+
+ friend class subr::hashed_hasher<attributes_type>;
+ typedef subr::hashed_hasher<attributes_type> hasher;
+
+ template<typename input, typename arg_type>
+ inline attributes_type (const input &other, arg_type &c)
+ : _base (other, c), _m_hash (0)
+ {
+ do_hash ();
+ }
+
+ inline bool is (const attributes_type &these) const
+ {
+ return (_m_hash == these._m_hash
+ && size () == these.size ()
+ && std::equal (begin (), end (), these.begin (),
+ same_attr ()));
+ }
+ };
+
+ class children_type
+ : public std::vector<die_info_pair *>
+ {
+ friend class dwarf_output;
+ friend class dwarf_output_collector;
+
+ protected:
+ typedef std::vector<die_info_pair *> _base;
+
+ size_t _m_hash;
+
+ inline void set_hash ()
+ {
+ _m_hash = 0;
+ for (_base::iterator i = _base::begin (); i != _base::end (); ++i)
+ subr::hash_combine (_m_hash, (uintptr_t) *i);
+ }
+
+ inline children_type () {}
+
+ inline const _base &info () const
+ {
+ return *this;
+ }
+
+ struct deref
+ : public std::unary_function<die_info_pair *,
+ const debug_info_entry &>
+ {
+ inline deref (...) {}
+ inline const debug_info_entry &operator () (die_info_pair *) const;
+ };
+
+ template<typename circular>
+ inline void reify_children (die_info_pair *, bool, unsigned int &)
+ const;
+
+ public:
+ template<typename iter>
+ inline children_type (const iter &first, const iter &last)
+ : _base (first, last)
+ {
+ set_hash ();
+ }
+
+ friend class subr::hashed_hasher<children_type>;
+ typedef subr::hashed_hasher<children_type> hasher;
+
+ typedef debug_info_entry value_type;
+ typedef debug_info_entry &reference;
+ typedef debug_info_entry &const_reference;
+ typedef debug_info_entry *pointer;
+ typedef debug_info_entry *const_pointer;
+
+ inline bool is (const children_type &these) const
+ {
+ return (_m_hash == these._m_hash
+ && size () == these.size ()
+ && std::equal (_base::begin (), _base::end (),
+ these._base::begin ()));
+ }
+
+ typedef subr::wrapped_input_iterator<
+ _base, deref, const debug_info_entry> const_iterator;
+ typedef const_iterator iterator;
+
+ inline const_iterator begin () const
+ {
+ return const_iterator (_base::begin (), subr::nothing ());
+ }
+
+ inline const_iterator end () const
+ {
+ return const_iterator (_base::end (), subr::nothing ());
+ }
+ };
+
+ typedef children_type::iterator pointer;
+ typedef children_type::const_iterator const_pointer;
+
+ protected:
+ const children_type *_m_children;
+ const attributes_type *_m_attributes;
+ size_t _m_hash;
+ int _m_tag;
+
+ // This is can only be used by the children_type constructor,
+ // which immediately calls set.
+ inline debug_info_entry ()
+ : _m_children (NULL),
+ _m_attributes (NULL),
+ _m_hash (0),
+ _m_tag (-1)
+ {}
+
+ inline debug_info_entry (int what,
+ const children_type *childs,
+ const attributes_type *attrs)
+ : _m_children (childs),
+ _m_attributes (attrs),
+ _m_tag (what)
+ {
+ set_hash ();
+ }
+
+ inline void set_hash ()
+ {
+ _m_hash = _m_tag;
+ subr::hash_combine (_m_hash, *_m_attributes);
+ subr::hash_combine (_m_hash, *_m_children);
+ }
+
+ public:
+ friend class subr::hashed_hasher<debug_info_entry>;
+ typedef subr::hashed_hasher<debug_info_entry> hasher;
+
+ inline bool is (const debug_info_entry &that) const
+ {
+ return (_m_hash == that._m_hash
+ && _m_tag == that._m_tag
+ && _m_attributes == that._m_attributes
+ && _m_children == that._m_children);
+ }
+
+ inline std::string to_string () const;
+
+ inline int tag () const
+ {
+ return _m_tag;
+ }
+
+ inline bool has_children () const
+ {
+ return !_m_children->empty ();
+ }
+
+ inline const children_type &children () const
+ {
+ return *_m_children;
+ }
+
+ inline const attributes_type &attributes () const
+ {
+ return *_m_attributes;
+ }
+
+ template<typename die>
+ bool operator== (const die &other) const
+ {
+ return (other.tag () == tag ()
+ && other.attributes () == attributes ()
+ && other.children () == children ());
+ }
+ template<typename die>
+ bool operator!= (const die &other) const
+ {
+ return !(*this == other);
+ }
+
+ inline dwarf::debug_info_entry::identity_type identity () const
+ {
+ return (uintptr_t) this;
+ }
+
+ inline ::Dwarf_Off offset () const
+ {
+ return identity ();
+ }
+
+ inline ::Dwarf_Off cost () const
+ {
+ return 0;
+ }
+ };
+
+ struct value::value_reference
+ : public dwarf_data::value<dwarf_output, false>::value_reference
+ {
+ typedef dwarf_data::value<dwarf_output, false>::value_reference _base;
+
+ // Default constructor: reference to nowhere, invalid.
+ inline value_reference ()
+ : _base ()
+ {}
+
+ inline value_reference (const value_type &i, subr::nothing &dummy)
+ : _base (i, dummy)
+ {}
+
+ /* The hash of a value_reference is its referent's identity,
+ because we can have multiple value_reference objects that
+ wind up pointing to the same entry. This method is virtual
+ for the circular_reference case, below. */
+ virtual size_t hash () const
+ {
+ return ref->identity ();
+ }
+ };
+
+ class attr_value
+ : public dwarf_data::attr_value<dwarf_output, value>
+ {
+ friend class dwarf_output;
+
+ private:
+ typedef dwarf_data::attr_value<dwarf_output, value> _base;
+
+ public:
+ inline std::string to_string () const;
+
+ /* These constructors can only be used by the containers
+ used in the collector. The attributes_type map in an
+ actual debug_info_entry object is always const. */
+ inline attr_value ()
+ : _base ()
+ {}
+
+ inline attr_value (const attr_value &other)
+ : _base ()
+ {
+ _m_value = other._m_value;
+ }
+
+ /* Two identical values in fact share the same cell in the collector.
+ So we can use simple pointer comparison here. */
+ inline bool is (const attr_value &that) const
+ {
+ return _m_value == that._m_value;
+ }
+
+ // The is () test works only on a dwarf_output sharing the same collector.
+ inline bool operator== (const attr_value &other) const
+ {
+ return is (other) || _base::operator== (other);
+ }
+ inline bool operator!= (const attr_value &other) const
+ {
+ return !(*this == other);
+ }
+
+ /* We can use the _m_value pointer itself as a perfect hash,
+ because all identical values share the same cell in the
+ collector. The exception to this is for references. See
+ value_reference::hash. */
+ struct hasher : public std::unary_function<attr_value, size_t>
+ {
+ inline size_t operator () (const attr_value &v) const
+ {
+ const value::value_reference *ref
+ = dynamic_cast<const value::value_reference *> (v._m_value);
+ return ref == NULL ? (uintptr_t) v._m_value : ref->hash ();
+ }
+ };
+ };
+
+ typedef debug_info_entry::attributes_type::value_type attribute;
+
+ typedef dwarf_data::compile_unit<dwarf_output> compile_unit;
+
+ /* Main container anchoring all the output.
+
+ This is the only container that actually lives in the dwarf_output
+ object. All others live in the dwarf_output_collector's sets, and
+ we return const references to those copies.
+
+ This list is actually mutable as a std::list. But note that you
+ should never remove a compile_unit, though you can reorder the
+ list. Nothing is ever removed from the collector, so your final
+ output file can wind up with unreferenced data being encoded. If
+ you do remove any elements, then you should start a fresh collector
+ and construct a new dwarf_output object by copying using that
+ collector (or, equivalently, call o.compile_units ().recollect (C)
+ on the new collector C). */
+ class compile_units
+ : public dwarf_data::compile_units<dwarf_output>
+ {
+ friend class dwarf_output;
+
+ private:
+ inline compile_units (const compile_units &)
+ : dwarf_data::compile_units<dwarf_output> ()
+ {
+ never_copy ();
+ }
+
+ template<typename input, typename copier_type>
+ static inline void
+ cu_maker (const iterator &out,
+ const typename input::const_iterator &in,
+ bool, // last-sibling
+ copier_type &c)
+ {
+ c.make_unit (in, out);
+ }
+
+ // Constructor copying CUs from input container.
+ template<typename input, typename copier>
+ inline compile_units (const input &other, copier &c)
+ {
+ subr::create_container (this, other, c, cu_maker<input, copier>);
+ }
+
+ public:
+ // Default constructor: an empty container, no CUs.
+ inline compile_units () {}
+ };
+
+ private:
+ compile_units _m_units;
+
+ public:
+ class compile_units &compile_units ()
+ {
+ return _m_units;
+ }
+ const class compile_units &compile_units () const
+ {
+ return _m_units;
+ }
+
+ private:
+ // Bind default copy-constructor and prevent it.
+ inline dwarf_output (const dwarf_output &)
+ {
+ throw std::logic_error ("copying dwarf_output requires a collector");
+ }
+
+ public:
+ // Constructor for an empty file, can add to its compile_units ().
+ inline dwarf_output () {}
+
+ /* Constructor copying CUs from an input file (can be any of dwarf,
+ dwarf_edit, or dwarf_output). Supply your own copier to reuse a
+ copier across multiple input files. This is worthwhile only if
+ they share a string table and such in memory. */
+ template<typename input>
+ inline dwarf_output (const input &dw, copier<input> &maker)
+ : _m_units (dw.compile_units (), maker)
+ {}
+
+ // Normal construction instantiates a copier derived from the collector.
+ template<typename input>
+ inline dwarf_output (const input &dw, dwarf_output_collector &c)
+ {
+ copier<input> maker (c);
+ _m_units.swap (compile_units::compile_units (dw.compile_units (), maker));
+ }
+
+ template<typename file>
+ inline bool operator== (const file &other) const
+ {
+ return compile_units () == other.compile_units ();
+ }
+ template<typename file>
+ inline bool operator!= (const file &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ // Explicit specializations.
+ template<>
+ std::string to_string<dwarf_output::debug_info_entry>
+ (const dwarf_output::debug_info_entry &);
+ inline std::string dwarf_output::debug_info_entry::to_string () const
+ {
+ return elfutils::to_string (*this); // Use that.
+ }
+ template<>
+ std::string
+ to_string<dwarf_output::attribute> (const dwarf_output::attribute &);
+ template<>
+ std::string
+ to_string<dwarf_output::attr_value> (const dwarf_output::attr_value &);
+
+ inline std::string dwarf_output::attr_value::to_string () const
+ {
+ return elfutils::to_string (*this); // Use that.
+ }
+
+ template<typename copier_type>
+ struct dwarf_output::value::maker
+ {
+ inline explicit maker (copier_type &) {}
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_string *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_string (x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_identifier *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_identifier (x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_reference *&,
+ int, const input &x, copier_type &c)
+ {
+ c.add_reference (x, &v);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_flag *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_flag (x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_address *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_address (x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_rangelistptr *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_ranges (x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_lineptr *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_line_info (x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_constant *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_constant (x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_constant_block *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_constant_block (x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_dwarf_constant *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_dwarf_constant (x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_source_file *&,
+ int attr, const input &x, copier_type &c)
+ {
+ v = c ().add_source_file (attr, x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_source_line *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_source_line (x);
+ }
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_source_column *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_source_column (x);
+ }
+
+ // XXX macptr
+
+ template<typename input>
+ inline void make (const value_dispatch *&v, value_location *&,
+ int, const input &x, copier_type &c)
+ {
+ v = c ().add_location (x);
+ }
+ };
+
+ template<>
+ struct dwarf_output::value::maker<subr::nothing>
+ {
+ inline explicit maker (subr::nothing &) {}
+
+ template<typename... args>
+ inline void make (args&&...)
+ {
+ throw std::logic_error ("dwarf_output cannot be default-constructed");
+ }
+ };
+
+ struct dwarf_output::die_info
+ {
+ die_info_pair *_m_parent;
+ std::queue<value::value_reference *> _m_refs;
+ std::set< ::Dwarf_Off> _m_originals; // XXX fix for cross-file sharing input
+ size_t _m_original_cost;
+ std::bitset<2> _m_with_sibling;
+ unsigned int _m_uses;
+
+ inline die_info ()
+ : _m_parent (NULL), _m_refs (),
+ _m_originals (), _m_original_cost (0),
+ _m_with_sibling (), _m_uses (0)
+ {}
+
+ inline ~die_info ()
+ {
+ while (!_m_refs.empty ())
+ {
+ delete _m_refs.front ();
+ _m_refs.pop ();
+ }
+ }
+
+ inline void original (unsigned int &incoming_count,
+ ::Dwarf_Off off, ::Dwarf_Off cost)
+ {
+ assert ((::Dwarf_Off) (size_t) cost == cost);
+ if (_m_originals.insert (off).second)
+ {
+ ++incoming_count;
+ _m_original_cost += cost;
+ }
+ }
+
+ inline std::set< ::Dwarf_Off>::size_type count_originals () const
+ {
+ return _m_originals.size ();
+ }
+
+ inline ptrdiff_t original_cost () const
+ {
+ return _m_original_cost;
+ }
+
+ inline ::Dwarf_Off original_offset () const
+ {
+ return *_m_originals.begin ();
+ }
+
+ template<typename streamish>
+ inline streamish &dump_originals (streamish &out) const
+ {
+ out << std::hex;
+ for (typename std::set< ::Dwarf_Off>::const_iterator i
+ = _m_originals.begin ();
+ i !=_m_originals.end ();
+ ++i)
+ out << " " << *i;
+ return out << std::dec;
+ }
+
+ inline ptrdiff_t output_cost () const
+ {
+ // XXX temporary proxy
+ return (double (_m_original_cost) / double (count_originals ())) + 0.5;
+ }
+
+ inline std::ostream &list_originals (std::ostream &o) const
+ {
+ for (std::set< ::Dwarf_Off>::const_iterator i = _m_originals.begin ();
+ i != _m_originals.end ();
+ ++i)
+ o << " " << std::hex << *i;
+ return o;
+ }
+
+ inline unsigned int uses () const
+ {
+ return _m_uses;
+ }
+
+ inline void assert_unused () const
+ {
+ assert (uses () == 0);
+ assert (_m_with_sibling.none ());
+ assert (_m_refs.empty ());
+ }
+
+ inline void self (value::value_reference *ref)
+ {
+ _m_refs.push (ref);
+ }
+
+ inline void
+ self (const debug_info_entry::pointer &ref)
+ {
+ subr::nothing dummy;
+ self (new value::value_reference (ref, dummy));
+ }
+
+ inline void
+ self (const debug_info_entry::children_type::_base::const_iterator &ref)
+ {
+ self (debug_info_entry::pointer (ref, subr::nothing ()));
+ }
+
+ inline bool selfless () const
+ {
+ return _m_refs.empty ();
+ }
+
+ inline value::value_reference *self () const
+ {
+ assert (!selfless ());
+ return _m_refs.front ();
+ }
+
+ template<typename circular>
+ inline void
+ reify_refs (const debug_info_entry::pointer &ref)
+ {
+ for (size_t n = _m_refs.size (); n > 0; --n)
+ {
+ value::value_reference *self_ref = _m_refs.front ();
+ self_ref->ref = ref;
+ _m_refs.pop ();
+ _m_refs.push (self_ref);
+
+ circular *circle = dynamic_cast<circular *> (self_ref);
+ if (circle != NULL && !circle->final ())
+ circle->placed ();
+ }
+ }
+
+ template<typename circular>
+ inline void
+ placed (die_info_pair *parent, const debug_info_entry::pointer &ref,
+ bool have_sibling, unsigned int &total)
+ {
+ // Record first parent.
+ if (_m_parent == NULL)
+ {
+ assert (uses () == 0 || parent == NULL);
+ _m_parent = parent;
+ }
+ else
+ assert (uses () > 0);
+
+ if (selfless ())
+ {
+ assert (uses () == 0);
+ self (ref);
+ }
+ else
+ reify_refs<circular> (ref);
+
+ ++total;
+ ++_m_uses;
+ _m_with_sibling[have_sibling] = true;
+ }
+ };
+
+ template<>
+ inline subr::nostream &
+ dwarf_output::die_info::dump_originals (subr::nostream &out) const
+ {
+ return out;
+ }
+
+ /* This is the wrapper in the guts of a children_type iterator.
+ It turns the real pointer we store into a debug_info_entry
+ reference for the generic tree-walk API. */
+ inline const dwarf_output::debug_info_entry &
+ dwarf_output::debug_info_entry::children_type::deref::operator ()
+ (dwarf_output::die_info_pair *p) const
+ {
+ return p->first;
+ }
+
+ /* This is called when a children_type is installed freshly in the collector.
+ Fill in its back pointers. */
+ template<typename circular>
+ inline void
+ dwarf_output::debug_info_entry::children_type::
+ reify_children (die_info_pair *parent, bool placed, unsigned int &total) const
+ {
+ _base::const_iterator i = _base::begin ();
+ bool have_sibling = i != _base::end ();
+ while (have_sibling)
+ {
+ const const_iterator here (i, subr::nothing ());
+ have_sibling = ++i != _base::end ();
+ die_info &child = (*here.base ())->second;
+ if (placed)
+ child.placed<circular> (parent, here, have_sibling, total);
+ else
+ child.reify_refs<circular> (here);
+ }
+ }
+
+ class dwarf_output_collector
+ {
+ friend class dwarf_output;
+
+ private:
+ unsigned int _m_total;
+ unsigned int _m_incoming;
+
+ typedef dwarf_output::die_info die_info;
+ typedef dwarf_output::die_info_pair die_info_pair;
+ typedef dwarf_output::debug_info_entry die_type;
+ typedef die_type::attributes_type attrs_type;
+ typedef die_type::children_type children_type;
+ typedef children_type::const_iterator die_ptr;
+
+ // Simple value sets for leaf types.
+ subr::value_set<dwarf_output::value::value_string> _m_strings;
+ subr::value_set<dwarf_output::value::value_identifier> _m_identifiers;
+ subr::value_set<dwarf_output::value::value_address> _m_address;
+ subr::value_set<dwarf_output::value::value_rangelistptr> _m_ranges;
+ subr::value_set<dwarf_output::value::value_lineptr> _m_line_info;
+ subr::value_set<dwarf_output::value::value_constant> _m_constants;
+ subr::value_set<dwarf_output::value::value_constant_block> _m_const_block;
+ subr::value_set<dwarf_output::value::value_dwarf_constant> _m_dwarf_const;
+ subr::value_set<dwarf_output::value::value_source_file> _m_source_file;
+ subr::value_set<dwarf_output::value::value_source_line> _m_source_line;
+ subr::value_set<dwarf_output::value::value_source_column> _m_source_column;
+ subr::value_set<dwarf_output::value::value_location> _m_locations;
+
+ // The set of Boolean flags is a doubleton.
+ static const dwarf_output::value::value_flag flag_true;
+ static const dwarf_output::value::value_flag flag_false;
+ static inline const dwarf_output::value::value_flag *flag (bool flag)
+ {
+ return flag ? &flag_true : &flag_false;
+ }
+
+ // Set of attribute maps.
+ typedef std::pair<attrs_type, int> attrs_pair;
+ subr::dynamic_equality_set<attrs_pair> _m_attr_sets;
+
+ template<typename match_type>
+ inline const attrs_pair *
+ add_attributes (int tag, const attrs_type &candidate, match_type &matcher)
+ {
+ return _m_attr_sets.add (std::make_pair (candidate, tag), matcher);
+ }
+
+ // Set of children lists.
+ subr::identity_set<children_type> _m_broods;
+
+ inline const children_type *
+ add_children (const children_type &candidate, bool &fresh)
+ {
+ std::pair<subr::identity_set<children_type>::iterator, bool> p
+ = _m_broods.insert (candidate);
+ const children_type &result = *p.first;
+ fresh = p.second;
+ return &result;
+ }
+
+ // Set of unique DIEs.
+ typedef subr::identity_map<die_type, die_info> die_map;
+ die_map _m_unique;
+
+ inline die_info_pair *add_entry (int tag,
+ const children_type *children,
+ const attrs_type *attrs)
+ {
+ std::pair <die_map::iterator, bool>
+ ins = _m_unique.insert (std::make_pair (die_type (tag, children, attrs),
+ die_info ()));
+ die_info_pair &x = *ins.first;
+ if (ins.second)
+ x.second.assert_unused ();
+
+ return &x;
+ }
+
+ struct shape_type
+ {
+ typedef std::vector<std::pair<int, int> > attrs_type;
+ attrs_type _m_attrs;
+ bool _m_has_children;
+ size_t _m_hash;
+
+ friend class subr::hashed_hasher<shape_type>;
+ typedef subr::hashed_hasher<shape_type> hasher;
+
+ inline void hashnadd (int name, int form);
+ inline shape_type (const die_type &die, bool last_sibling);
+
+ inline bool operator== (const shape_type &other) const
+ {
+ return (_m_hash == other._m_hash
+ && _m_has_children == other._m_has_children
+ && _m_attrs == other._m_attrs);
+ }
+ inline bool operator!= (const shape_type &other) const
+ {
+ return !(*this == other);
+ }
+ };
+
+ typedef subr::nothing shape_info;
+
+ typedef std::tr1::unordered_map<shape_type, shape_info,
+ shape_type::hasher> shape_map;
+ shape_map _m_shapes;
+
+ void add_shape (die_type &die, bool last_sibling);
+
+ struct stats_cmp
+ : public std::binary_function<const die_map::value_type *,
+ const die_map::value_type *,
+ void>
+ {
+ inline bool operator () (const die_map::value_type *a,
+ const die_map::value_type *b) const
+ {
+ // Sort by number of input entries collected into this one entry.
+ if (a->second.count_originals () != b->second.count_originals ())
+ return a->second.count_originals () > b->second.count_originals ();
+
+ // Secondarily sort by aggregate input cost.
+ if (a->second.original_cost () != b->second.original_cost ())
+ return a->second.original_cost () > b->second.original_cost ();
+
+ // Finally, sort by input file order.
+ return a->second.original_offset () > b->second.original_offset ();
+ }
+ };
+
+ typedef std::multiset<const die_map::value_type *, stats_cmp
+ > die_stats_order;
+
+ struct die_stats_sorter
+ : public std::unary_function<die_map::value_type, void>
+ {
+ die_stats_order &_m_order;
+ inline explicit die_stats_sorter (die_stats_order &o) : _m_order (o) {}
+
+ inline void operator () (const die_map::value_type &elt)
+ {
+ _m_order.insert (&elt);
+ }
+ };
+
+ static void die_stats (std::ostream &out, const die_map::value_type *elt)
+ {
+ const die_info *info = &elt->second;
+ out << std::dec << info->uses ()
+ << "\thash=" << std::hex << subr::hash_this (elt->first)
+ << "\t(" << info->_m_with_sibling.to_string () << ") "
+ << std::dec << info->original_cost ()
+ << " (" << (info->original_cost () - info->output_cost ())
+ << ")\t" << to_string (elt->first) << "\t";
+ info->list_originals (out)
+ << "\n";
+ }
+
+ struct attr_collision
+ {
+ std::ostream &_m_out;
+ inline explicit attr_collision (std::ostream &out) : _m_out (out) {}
+
+ inline void
+ operator () (size_t hash,
+ const subr::dynamic_equality_set<attrs_pair>::bucket_type &b)
+ const
+ {
+ _m_out << "attrs bucket " << std::hex << hash
+ << std::dec << ": " << b.size () << " collisions\n";
+ subr::for_each (b, *this);
+ }
+
+ inline void operator () (const attrs_pair &attrs) const
+ {
+ _m_out << "\t" << dwarf::tags::name (attrs.second)
+ << " " << attrs.first.size ();
+ subr::for_each (attrs.first, *this);
+ _m_out << "\n";
+ }
+
+ inline void operator () (const attrs_type::value_type &attr) const
+ {
+ _m_out << " " << dwarf::attributes::name (attr.first);
+ }
+ };
+
+ const bool _m_ignore_context;
+
+ inline dwarf_output_collector (const dwarf_output_collector &)
+ : _m_ignore_context (false)
+ {
+ throw std::logic_error ("cannot copy-construct");
+ }
+
+ public:
+ inline dwarf_output_collector (bool ignore = true) // XXX
+ : _m_total (0), _m_incoming (0), _m_ignore_context (ignore)
+ {}
+
+ inline bool ignore_context () const
+ {
+ return _m_ignore_context;
+ }
+
+ void stats (std::ostream &out = std::cout) const
+ {
+ out << "collected " << std::dec << _m_unique.size ()
+ << " unique of " << _m_total << " total from "
+ << _m_incoming << " DIEs\n";
+
+ subr::container_hash_stats (out, "strings", _m_strings);
+ subr::container_hash_stats (out, "identifiers", _m_identifiers);
+ subr::container_hash_stats (out, "address", _m_address);
+ subr::container_hash_stats (out, "ranges", _m_ranges);
+ subr::container_hash_stats (out, "line_info", _m_line_info);
+ subr::container_hash_stats (out, "constants", _m_constants);
+ subr::container_hash_stats (out, "const_block", _m_const_block);
+ subr::container_hash_stats (out, "dwarf_const", _m_dwarf_const);
+ subr::container_hash_stats (out, "source_file", _m_source_file);
+ subr::container_hash_stats (out, "source_line", _m_source_line);
+ subr::container_hash_stats (out, "source_column", _m_source_column);
+ subr::container_hash_stats (out, "locations", _m_locations);
+
+ subr::container_hash_stats (out, "broods", _m_broods);
+ _m_attr_sets.hash_stats (out, "attr_sets", attr_collision (out));
+
+ die_stats_order ordered;
+ subr::for_each (_m_unique, die_stats_sorter (ordered));
+ subr::for_each (ordered, std::bind1st (std::ptr_fun (die_stats), out));
+ }
+ };
+
+ template<typename dw>
+ class dwarf_output::copier
+ {
+ friend class dwarf_output;
+ private:
+ typedef typename dw::debug_info_entry input_die;
+ typedef typename input_die::children_type::const_iterator input_die_ptr;
+
+ dwarf_output_collector *_m_collector;
+
+ /* A copier::entry represents one dw::debug_info_entry from the input,
+ indexed by identity. With real input files (dw=dwarf), that means
+ one input entry for each unique DIE offset in each file. (We also
+ record its offset in the input file, just for use in debugging and
+ statistics output.) An entry is in one of four states:
+
+ undefined: we have seen references in attributes, but have not yet
+ seen the entry itself in the copying walk of the input DIE tree;
+
+ pending: the entry is in the midst of being copied, or it has
+ references to non-final entries;
+
+ final: the entry is finished and stored in the collector, but
+ is not yet pointed to by any real children_type vector;
+
+ placed: the entry is final and at least one logical parent's
+ children_type vector is also finished and stored in the collector,
+ permitting final bookkeeping and reference iterator updates.
+
+ Whenever there are no undefined entries outstanding, it should by
+ definition be possible to turn every pending entry into a final
+ entry. To avoid complex bookkeeping, we simply keep track of the
+ total count of undefined entries. When we first encounter an entry
+ or a reference to it before we've copied, we increment that count.
+ Upon completing the copying of an entry, we decrement it. If that
+ makes the count reach zero, we immediately "finalize" the entry,
+ which is recursive on all its references and children not yet final.
+
+ */
+
+ struct entry; // Below.
+ struct entry_copier; // Below.
+ struct entry_finalizer; // Below.
+
+ /* An attr_value cell points to one of these when it's a reference to
+ an entry not already in the collector at finalization time, i.e. a
+ circular reference. To compare circular references during attribute
+ finalization, we follow the pending () pointer; see dwarf_pending,
+ below. Thereafter, the base type's ref element is initialized and
+ we can use that directly. */
+ class circular_reference
+ : public value::value_reference
+ {
+ private:
+ const std::vector<entry *> *_m_entry;
+
+ inline circular_reference (const circular_reference &)
+ : value::value_reference ()
+ {
+ throw std::logic_error ("cannot copy-construct");
+ }
+
+ public:
+ inline circular_reference (const entry *die, copier *)
+ : value::value_reference (),
+ _m_entry (new std::vector<entry *> (1, const_cast<entry *> (die)))
+ {
+ die->dump () << " new circular_reference " << this << "\n";
+ }
+
+ inline bool final () const
+ {
+ return _m_entry == NULL;
+ }
+
+ inline typename std::vector<entry *>::const_iterator pending () const
+ {
+ return _m_entry->begin ();
+ }
+
+ inline entry *pending_entry () const
+ {
+ return *pending ();
+ }
+
+ // We've been stored in the collector, so we are no longer pending.
+ inline void placed ()
+ {
+ pending_entry ()->dump () << " placed circular_reference "
+ << this << "\n";
+ delete _m_entry;
+ _m_entry = NULL;
+ }
+
+ inline ~circular_reference ()
+ {
+ if (unlikely (_m_entry != NULL))
+ {
+ pending_entry ()->dump () << " destroy circular_reference "
+ << this << "\n";
+ delete _m_entry;
+ }
+ }
+
+ /* We have a special case for a reference attribute that is part
+ of a circular chain. That value always hashes as zero, so that
+ each entry in a circular chain of references has the same hash
+ value as any entry that it otherwise matches and that has any
+ (eventually) circular reference as the corresponding
+ attribute's value. */
+ virtual size_t hash () const
+ {
+ return 0;
+ }
+ };
+
+ struct pending_entry
+ {
+ /* Pointers to each entry that appears in _m_attributes.
+ When a referent is already final, the entry * does not
+ appear in the attr_value and does not count here. */
+ std::stack<const value::value_dispatch **> _m_pending_refs;
+
+ // Set if we are attempting to finalize this entry right now.
+ entry_finalizer *_m_finalizing;
+
+ // Reference to ourself, pre-built in a circularity.
+ circular_reference *_m_self;
+
+ /* Cache of the final entry we already know we will become.
+ We'll set this when the walk of a reference comparison hits
+ this entry while finalizing another entry and records that it
+ is identical to an existing final entry. When the main walk
+ doing finalization hits us, we can short-circuit matching our
+ redundant entry in the collector sets. */
+ die_info_pair *_m_matched;
+
+ std::set<uintptr_t> _m_mismatched;
+
+ typedef dwarf_data::attributes_type<dwarf_output, value> attr_map;
+ attr_map _m_attributes;
+ std::vector<entry *> _m_children;
+ const int _m_tag;
+
+ // Set if _m_children contains any entries not already final.
+ bool _m_unfinished_children;
+
+ inline pending_entry (int tag)
+ : _m_finalizing (NULL), _m_self (NULL), _m_matched (NULL),
+ _m_tag (tag), _m_unfinished_children (false)
+ {}
+
+ inline ~pending_entry ()
+ {
+ if (unlikely (_m_self != NULL))
+ {
+ if (_m_self->final ())
+ entry::debug () << "XXX ~pending_entry _m_self is final\n";
+ else
+ _m_self->pending_entry ()->dump () << " ~pending_entry _m_self\n";
+ delete _m_self;
+ }
+ }
+
+ inline typename std::vector<entry *>::const_iterator
+ child (typename std::vector<entry *>::size_type i) const
+ {
+ return _m_children.begin () + i;
+ }
+
+ /* Finalize each pending entry that we refer to.
+ This is called only when we have a non-empty _m_pending_refs. */
+ inline void finalize_refs (copier *c)
+ {
+ do
+ {
+ const value::value_dispatch **const attr = _m_pending_refs.top ();
+ const entry *ref = dynamic_cast<const entry *> (*attr);
+ if (ref != NULL)
+ *attr = const_cast<entry *> (ref)->finalize_ref (c);
+ _m_pending_refs.pop ();
+ }
+ while (!_m_pending_refs.empty ());
+ }
+
+ // Finalize each unfinished child.
+ inline void finalize_children (copier *c)
+ {
+ _m_unfinished_children = false;
+ subr::for_each
+ (_m_children,
+ std::bind2nd (std::mem_fun (&entry::get_finalized),
+ std::make_pair (c, &_m_unfinished_children)));
+ }
+
+ /* This is called from finalize_ref (below) when we are
+ resolving a circular reference attribute. We cache the
+ uninitialized reference in _m_self, and return it. */
+ inline value::value_reference *
+ make_circular_reference (const entry *self, copier *c)
+ {
+ if (_m_self == NULL)
+ _m_self = new circular_reference (self, c);
+ return _m_self;
+ }
+
+ typedef std::pair <debug_info_entry::attributes_type, int> attrs_pair;
+ struct attrs_matcher
+ {
+ copier *_m_copier;
+ inline explicit attrs_matcher (copier *c) : _m_copier (c) {}
+
+ inline bool operator () (const attrs_pair &a, const attrs_pair &b)
+ {
+ return (a.second == b.second
+ && _m_copier->attrs_match (a.first, b.first));
+ }
+ };
+
+ /* We can only get here when all our children have been finalized.
+ So all we have to do is fetch that pointer. */
+ static inline die_info_pair *get_final_child (entry *child)
+ {
+ assert (child->_m_final != NULL);
+ return child->_m_final;
+ }
+
+ typedef typename subr::wrapped_input_iterator<
+ std::vector<entry *>,
+ std::pointer_to_unary_function<entry *, die_info_pair *>
+ > final_children_getter;
+ typedef typename final_children_getter::
+ template copy<debug_info_entry::children_type> get_final_children;
+
+ inline die_info_pair *final (copier *c,
+ ::Dwarf_Off offset, ::Dwarf_Off cost)
+ {
+ dwarf_output_collector *const co = c->_m_collector;
+
+ assert (_m_pending_refs.empty ());
+
+ bool fresh = false;
+ if (_m_matched == NULL)
+ {
+ attrs_matcher equalator (c);
+ const debug_info_entry::attributes_type *attrs
+ = &co->add_attributes (_m_tag, _m_attributes, equalator)->first;
+
+ const debug_info_entry::children_type *children
+ = co->add_children (get_final_children ()
+ (_m_children, std::ptr_fun (get_final_child)),
+ fresh);
+
+ _m_matched = co->add_entry (_m_tag, children, attrs);
+ }
+
+ // Final bookkeeping in the collector for this copied entry.
+ _m_matched->second.original (co->_m_incoming, offset, cost);
+
+ /* Now our entry is finalized in the collector (either newly
+ created there, or discovered to be a duplicate already
+ there). If this was part of a circularity, it created the
+ _m_self object and stored pointers to it in the collector
+ attributes maps. Now move that object into the final
+ entry's _m_refs list. From there it will get initialized. */
+ if (_m_self != NULL)
+ {
+ assert (!_m_self->final ());
+ _m_self->pending_entry ()->dump ()
+ << " register circular_reference " << _m_self << " "
+ << _m_matched->first.to_string () << " from";
+ _m_matched->second.dump_originals (entry::debug ()) << "\n";
+ _m_matched->second.self (_m_self);
+ _m_self = NULL;
+ }
+
+ /* Now we have a children_type in the collector. Fix up all
+ the backpointers to point into that _m_broods copy. Also
+ make sure each child gets its _m_parent pointer. Even if
+ this candidate children_type didn't actually get inserted
+ into the set (was not unique), we may need to reify new refs
+ to these children. */
+ _m_matched->first._m_children->reify_children<circular_reference>
+ (_m_matched, fresh, co->_m_total);
+
+ return _m_matched;
+ }
+
+ inline void notice_mismatch (const die_info_pair *not_me)
+ {
+ _m_mismatched.insert ((uintptr_t) not_me);
+ }
+
+ inline bool cached_mismatch (const die_info_pair *not_me)
+ {
+ return _m_mismatched.find ((uintptr_t) not_me) != _m_mismatched.end ();
+ }
+
+ static inline void dump_pending_ref (const value::value_dispatch **attr)
+ {
+ const entry *ref = dynamic_cast<const entry *> (*attr);
+ if (ref != NULL)
+ ref->debug () << " " << std::hex << ref->_m_offset << std::dec;
+ else
+ {
+ const circular_reference *circular
+ = dynamic_cast<const circular_reference *> (*attr);
+ if (circular != NULL && !circular->final ())
+ {
+ ref = circular->pending_entry ();
+ ref->debug () << " *" << std::hex << ref->_m_offset << std::dec;
+ }
+ }
+ }
+
+ inline void dump_tree (const entry *self) const
+ {
+ self->dump (true) << " " << dwarf::tags::name (_m_tag) << " "
+ << _m_children.size () << " children, "
+ << _m_pending_refs.size () << " refs";
+ //subr::for_each (_m_pending_refs, dump_pending_ref); XXX
+ self->debug () << "\n";
+ subr::for_each (_m_children, std::mem_fun (&entry::dump_entry));
+ self->dump (false, true) << " ends\n";
+ }
+ };
+
+ // This keeps state in the pending_entry's _m_finalizing pointer while live.
+ struct entry_finalizer
+ {
+ entry *const _m_entry;
+
+ inline entry_finalizer (entry *die)
+ : _m_entry (die)
+ {
+ _m_entry->debug () << std::flush;
+ assert (_m_entry->_m_pending->_m_finalizing == NULL);
+ _m_entry->_m_pending->_m_finalizing = this;
+ _m_entry->dump (true) << " finalizing\n";
+ }
+
+ inline ~entry_finalizer ()
+ {
+ if (unlikely (_m_entry->_m_pending != NULL))
+ {
+ assert (_m_entry->_m_pending->_m_finalizing == this);
+ _m_entry->_m_pending->_m_finalizing = NULL;
+ _m_entry->dump (false, true) << " failed to finalize!\n";
+ }
+ else
+ {
+ _m_entry->dump (false, true) << " finalized\n";
+ assert (_m_entry->_m_final != NULL);
+ _m_entry->dump_entry ();
+ }
+ }
+ };
+
+ /* This is what we record about each input DIE we have considered.
+ An attr_value that is a dangling reference to a DIE not yet
+ built in the output has one of these in place of a value_reference.
+ These all live in the _m_entries map, one per input-side DIE. */
+ struct entry
+ : public value::value_dispatch
+ {
+ ::Dwarf_Off _m_offset; // For debugging and statistics only.
+ ::Dwarf_Off _m_cost; // For statistics only.
+
+ // Completed DIE in the collector, or NULL.
+ die_info_pair *_m_final;
+
+ // Pending entry made with new, or NULL.
+ pending_entry *_m_pending;
+
+ // Set if we are building this in the copying walk right now.
+ entry_copier *_m_building;
+
+ // Set if we are in attrs_match on this entry right now.
+ die_info_pair *_m_comparing;
+
+ /* When we're final but not placed, we allocate a singleton vector
+ here and set a value_reference to an iterator in that vector.
+ That will be replaced with the iterator into a proper children
+ vector when we're placed. */
+ debug_info_entry::children_type::_base *_m_final_ref;
+
+ // First parent, for tracker purposes.
+ entry *_m_parent;
+ typename std::vector<entry *>::size_type _m_self_idx;
+
+ inline entry ()
+ : _m_offset (0), _m_final (NULL), _m_pending (NULL),
+ _m_building (NULL), _m_comparing (NULL),
+ _m_final_ref (NULL), _m_parent (NULL), _m_self_idx (0)
+ {}
+
+ inline void setup (copier *c, const input_die &in)
+ {
+ if (_m_offset == 0)
+ {
+ _m_offset = in.offset ();
+ ++c->_m_undefined_entries;
+ dump () << " seen => " << c->_m_undefined_entries << "\n";
+ }
+ }
+
+ inline ~entry ()
+ {
+ assert (_m_building == NULL);
+ if (_m_final_ref != NULL)
+ delete _m_final_ref;
+
+ // This should only hit in an exception case abandoning the copier.
+ if (unlikely (_m_pending != NULL))
+ delete _m_pending;
+ }
+
+ /* If we need a reference before we're placed, fake one up with
+ a singleton vector pointing to us, stored in _m_final_ref. */
+ inline value::value_reference *self ()
+ {
+ if (_m_final->second.selfless ())
+ {
+ assert (_m_final_ref == NULL);
+ _m_final_ref
+ = new debug_info_entry::children_type::_base (1, _m_final);
+ _m_final->second.self (_m_final_ref->begin ());
+ }
+ return _m_final->second.self ();
+ };
+
+ /* Called by entry_copier::add_reference, below.
+ We're adding a reference attribute pointing to this input entry. */
+ inline void refer (entry *referrer, const value::value_dispatch **backptr)
+ {
+ referrer->dump () << " refers to "
+ << std::hex << _m_offset << std::dec
+ << " (" << (_m_final ? "final"
+ : _m_pending ? "pending" : "undefined")
+ << (_m_building ? ", building" : "") << ")\n";
+
+ if (_m_final != NULL)
+ // It's finished, resolve the final reference.
+ *backptr = self ();
+ else
+ {
+ *backptr = this;
+ referrer->_m_pending->_m_pending_refs.push (backptr);
+ }
+ }
+
+ /* We are no longer an undefined entry, so decrement the count.
+ Then finalize as much as we can now. We attempt finalization
+ even when the count is nonzero, so that a leaf entry with no
+ forward references finishes immediately, and so then can its
+ parents and on up if they don't own any pending references. */
+ inline void defined_self (copier *c)
+ {
+ assert (_m_final == NULL);
+ assert (_m_pending != NULL);
+ assert (c->_m_undefined_entries > 0);
+ --c->_m_undefined_entries;
+ dump () << " defined_self => " << c->_m_undefined_entries << "\n";
+ finalize (c);
+ if (_m_final == NULL)
+ assert (c->_m_undefined_entries > 0);
+ }
+
+ /* A reference-following matching operation noticed along
+ the way that we have a doppleganger in the collector. */
+ inline void record_prematch (die_info_pair *doppleganger,
+ bool lhs)
+ {
+ doppleganger->second.dump_originals
+ (dump ()
+ << " record_prematch to " << doppleganger->first.to_string ()
+ << " from")
+ << (lhs ? " on lhs\n" : " on rhs\n");
+ /* XXX disabled! tentative circularity matches taint this record!
+ must record taint to avoid caching, or punt caching.
+ */
+ //_m_pending->_m_matched = doppleganger;
+ }
+
+ /* This is called by finalize_children. In case of imported_unit
+ use in the input, we could already have finalized this earlier
+ in the copying walk of the logical CU, so there is nothing to
+ do. Or, inside a circularity in finalize_refs, we might be
+ finalizing this child already in an outer recursion. In that
+ case, we can't finish it here. */
+ inline void get_finalized (const std::pair<copier *, bool *> p)
+ {
+ if (_m_final == NULL && _m_pending->_m_finalizing == NULL)
+ finalize (p.first);
+ if (_m_final == NULL)
+ *p.second = true;
+ }
+
+ // Attempt to turn the pending entry into a final entry.
+ void finalize (copier *c)
+ {
+ entry_finalizer finalizing (this);
+
+ if (c->_m_undefined_entries == 0)
+ {
+ // Nothing is undefined, so we can resolve pending references.
+ if (!_m_pending->_m_pending_refs.empty ())
+ {
+ dump (true) << " finalize_refs\n";
+ _m_pending->finalize_refs (c);
+ dump (false, true) << " finalize_refs done\n";
+ }
+
+ // Now we can finish off all our children.
+ if (_m_pending->_m_unfinished_children)
+ {
+ dump (true) << " finalize_children\n";
+ _m_pending->finalize_children (c);
+ dump (false, true) << " finalize_children done\n";
+ }
+ }
+
+ /* If there were no pending references or children to finish, or
+ if we just finished them all off, we can finally finalize! */
+ if (_m_pending->_m_pending_refs.empty ()
+ && !_m_pending->_m_unfinished_children)
+ {
+ // Create it in the collector.
+ _m_final = _m_pending->final (c, _m_offset, _m_cost);
+
+ // No more pending_entry required!
+ delete _m_pending;
+ _m_pending = NULL;
+ }
+ }
+
+ // Called by a referrer trying to finalize us.
+ inline const value_dispatch *finalize_ref (copier *c)
+ {
+ assert (c->_m_undefined_entries == 0);
+ if (_m_final == NULL)
+ {
+ if (_m_pending->_m_finalizing == NULL)
+ finalize (c);
+ if (_m_final == NULL)
+ {
+ dump () << " finalize_ref caught circularity\n" << std::flush;
+
+ /* We are recursing inside a finalize call.
+ This means we have a circular reference. */
+ return _m_pending->make_circular_reference (this, c);
+ }
+ }
+ return self ();
+ }
+
+#if 0 // Toggle this to enable massive debugging spew during construction.
+ static inline std::ostream &debug ()
+ {
+ return std::cout;
+ }
+
+ static inline std::ostream &
+ debug_prefix (bool in = false, bool out = false, bool print = true)
+ {
+ static std::string prefix;
+ if (out)
+ prefix.erase (--prefix.end ());
+ if (print)
+ debug () << prefix;
+ if (in)
+ prefix.push_back (' ');
+ return debug ();
+ }
+
+ std::ostream &dump (bool in = false, bool out = false) const
+ {
+ debug_prefix (in, out) << "XXX " << std::hex << _m_offset << std::dec;
+ if (_m_pending != NULL && _m_pending->_m_finalizing != NULL)
+ debug () << " (finalizing "
+ << (void *) _m_pending->_m_finalizing << ")";
+ return debug ();
+ }
+
+ void dump_entry () const
+ {
+ if (_m_final != NULL)
+ {
+ dump () << " final " << _m_final->first.to_string ()
+ << " hash=" << std::hex << subr::hash_this (_m_final->first)
+ << " from";
+ _m_final->second.dump_originals (debug ()) << "\n";
+ }
+ else if (_m_pending != NULL)
+ _m_pending->dump_tree (this);
+ else
+ dump () << " undefined\n";
+ }
+#else
+ static inline subr::nostream &debug ()
+ {
+ static subr::nostream n;
+ return n;
+ }
+
+ static inline subr::nostream &
+ debug_prefix (bool = false, bool = false, bool = true)
+ {
+ return debug ();
+ }
+
+ inline subr::nostream &dump (bool = false, bool = false) const
+ {
+ return debug ();
+ }
+
+ inline void dump_entry () const
+ {}
+#endif
+
+ // Find ourselves in a parent pending_entry's children vector.
+ inline typename std::vector<entry *>::const_iterator pending_self () const
+ {
+ debug () << std::flush;
+ assert (_m_pending != NULL);
+ return _m_parent->_m_pending->child (_m_self_idx);
+ }
+ };
+
+ // This object lives while we are copying one particular input DIE.
+ struct entry_copier
+ {
+ copier *_m_copier;
+ entry *_m_in;
+ pending_entry *_m_out;
+
+ /* On creation we set _m_building in DIE's record.
+ It should never be set already. */
+ inline entry_copier (copier *c, entry *die, const input_die &in)
+ : _m_copier (c),
+ _m_in (die),
+ _m_out (new pending_entry (in.tag ()))
+ {
+ if (unlikely (_m_in->_m_building != NULL))
+ throw std::runtime_error ("detected cycle in logical DWARF tree");
+ _m_in->_m_building = this;
+ _m_in->_m_cost = in.cost ();
+ _m_in->dump (true) << " copying (cost=" << _m_in->_m_cost << ")\n";
+ }
+
+ // On destruction, we clear _m_building.
+ inline ~entry_copier ()
+ {
+ _m_in->dump (false, true) << " copied\n";
+ assert (_m_in->_m_building == this);
+ _m_in->_m_building = NULL;
+ if (unlikely (_m_out != NULL)) // Exception unwind case only.
+ delete _m_out;
+ }
+
+ /* Populate _m_out from the corresponding input DIE. This invokes
+ all the main work of copying. The interesting parts happen in
+ add_reference and add_child, below. */
+ inline void populate (const input_die &in)
+ {
+ assert (_m_in->_m_pending == NULL);
+ _m_in->_m_pending = _m_out;
+
+ try
+ {
+ // This calls add_reference for each pending reference.
+ _m_out->_m_attributes.set (in.attributes (), *this);
+
+ for (input_die_ptr i = in.children ().begin ();
+ i != in.children ().end ();
+ ++i)
+ add_child (*i);
+ }
+ catch (...)
+ {
+ _m_in->_m_pending = NULL;
+ throw;
+ }
+
+ _m_out = NULL;
+ _m_in->defined_self (_m_copier);
+ }
+
+ // We're adding a reference attribute inside populate, above.
+ inline void add_reference (const input_die_ptr &to,
+ const value::value_dispatch **backptr)
+ {
+ _m_copier->enter (*to)->refer (_m_in, backptr);
+ }
+
+ // We're adding a child entry inside populate, above.
+ inline void add_child (const input_die &in)
+ {
+ entry *child = _m_copier->enter (in);
+ _m_out->_m_children.push_back (child);
+
+ /* If the input used DW_TAG_imported_unit, then the logical walk
+ can hit the same DIE twice. If so, we short-circuit right here. */
+ if (child->_m_final == NULL && child->_m_pending == NULL)
+ {
+ child->_m_parent = _m_in;
+ child->_m_self_idx = _m_out->_m_children.size () - 1;
+ entry_copier (_m_copier, child, in).populate (in);
+ }
+
+ if (child->_m_final == NULL)
+ // Record that it didn't finalize immediately, we'll do it later.
+ _m_out->_m_unfinished_children = true;
+ }
+
+ // Use "c ()" as a shorthand to get the copier out of the entry_copier.
+ inline copier &operator () () const
+ {
+ return *_m_copier;
+ }
+
+ /* Complain if we still have dangling references.
+ If not, it should be impossible to have pending entries left. */
+ inline die_info_pair *final_unit () const
+ {
+ assert (_m_out == NULL);
+ if (unlikely (_m_in->_m_final == NULL))
+ {
+ _m_in->dump_entry ();
+ _m_in->debug () << std::flush;
+ assert (_m_copier->_m_undefined_entries > 0);
+ throw std::runtime_error
+ ("compile_unit contains dangling reference attributes");
+ }
+ assert (_m_copier->_m_undefined_entries == 0);
+ return _m_in->_m_final;
+ }
+ };
+
+ struct unit_copier : public entry_copier
+ {
+ inline unit_copier (copier *c, const typename dw::compile_unit &in)
+ : entry_copier (c, c->enter (in), in)
+ {
+ populate (in);
+ }
+ };
+
+ struct dump_unplaced
+ : public std::unary_function<circular_reference *, void>
+ {
+ inline void operator () (circular_reference *ref)
+ {
+ std::cout << "XXX unplaced ref to "
+ << std::hex << (ref->pending_entry ())->_m_offset
+ << std::dec << "\n";
+ }
+ };
+
+ // Create a whole CU in the output.
+ inline void
+ make_unit (const typename dw::compile_units::const_iterator &in,
+ const compile_units::iterator &out)
+ {
+ die_info_pair *cu = unit_copier (this, *in).final_unit ();
+
+ // This really just increments _m_total for us, but also _m_uses.
+ cu->second.placed<circular_reference> (NULL,
+ cu->first.children ().end (),
+ false, _m_collector->_m_total);
+
+ *out = cu->first;
+ }
+
+ typedef std::tr1::unordered_map< ::Dwarf_Off, entry> entry_map;
+ entry_map _m_entries;
+ unsigned int _m_undefined_entries; // Count of _m_entries not copied yet.
+
+ inline entry *enter (const input_die &in)
+ {
+ entry *die = &_m_entries[in.identity ()];
+ die->setup (this, in);
+ return die;
+ }
+
+ /* This is an entire shadow of the dwarf:: interface, sufficient to
+ instantiate dwarf_comparator below. All its objects are
+ ephemeral and simply wrap a pending_entry and its constituents.
+ We always start with finalized attributes, but those can include
+ circular_reference objects pointing to pending entries that can't
+ be finalized until the finalization that this comparison is part
+ of has been done. Hence these objects have to bifurcate between
+ wrapping pending_entry and wrapping die_info_pair. */
+ class pending_dwarf
+ {
+ public:
+ class debug_info_entry;
+ class attr_value;
+ typedef std::pair<const int, attr_value> attribute;
+
+ typedef debug_info_entry compile_unit;
+
+ private:
+
+ // Both debug_info_entry and iterators just hold this pair of pointers.
+ struct entry_pointers
+ {
+ entry *pending;
+ die_info_pair *final;
+
+ inline entry_pointers (entry *a, die_info_pair *b)
+ : pending (a), final (b)
+ {}
+ };
+
+ struct pending_cu
+ : public std::unary_function<dwarf_output::compile_unit, compile_unit>
+ {
+ inline const compile_unit
+ operator () (const dwarf_output::compile_unit &) const
+ {
+ throw std::logic_error ("XXX implement me");
+ }
+ };
+
+ public:
+ // Not really used so far, just for completeness.
+ typedef subr::wrapped_input_container<class dwarf_output::compile_units,
+ pending_cu> compile_units;
+
+ class debug_info_entry
+ {
+ private:
+ entry_pointers _m_ptr;
+
+ static inline const debug_info_entry
+ child (const entry_pointers &ptr, size_t i)
+ {
+ if (ptr.final == NULL)
+ return debug_info_entry (ptr.pending->_m_pending->_m_children[i]);
+ return debug_info_entry (ptr.final->first.children ().info ()[i]);
+ }
+
+ // Turns an attribute into an attribute!
+ struct pending_attr
+ : public std::unary_function<dwarf_output::attribute, attribute>
+ {
+ inline pending_attr (const subr::nothing &) {}
+
+ inline const attribute
+ operator () (const dwarf_output::attribute &attr) const
+ {
+ return attribute (attr.first, attr.second);
+ }
+ };
+
+ public:
+ inline debug_info_entry ()
+ : _m_ptr (NULL, NULL)
+ {}
+
+ inline debug_info_entry (const debug_info_entry &entry)
+ : _m_ptr (entry._m_ptr)
+ {}
+
+ inline explicit debug_info_entry (die_info_pair *die)
+ : _m_ptr (NULL, die)
+ {}
+
+ inline explicit debug_info_entry (entry *die)
+ : _m_ptr (die, die->_m_final)
+ {}
+
+ inline bool final () const
+ {
+ return _m_ptr.final != NULL;
+ }
+
+ inline die_info_pair *get_final () const
+ {
+ assert (_m_ptr.final != NULL);
+ return _m_ptr.final;
+ }
+
+ inline entry *get_pending () const
+ {
+ assert (_m_ptr.pending != NULL);
+ return _m_ptr.pending;
+ }
+
+ inline bool is (const debug_info_entry &other) const
+ {
+ return (_m_ptr.final == other._m_ptr.final
+ && (final () || _m_ptr.pending == other._m_ptr.pending));
+ }
+
+ // Used by the tracker.
+ inline std::pair<die_info_pair *, entry *> context () const
+ {
+ return std::make_pair (_m_ptr.final, _m_ptr.pending);
+ }
+
+ inline int tag () const
+ {
+ return (_m_ptr.final != NULL
+ ? _m_ptr.final->first.tag ()
+ : _m_ptr.pending->_m_pending->_m_tag);
+ }
+
+ typedef subr::wrapped_input_container<typename pending_entry::attr_map,
+ pending_attr> attributes_type;
+
+ inline const attributes_type attributes () const
+ {
+ return attributes_type (_m_ptr.final == NULL
+ ? _m_ptr.pending->_m_pending->_m_attributes
+ : _m_ptr.final->first._m_attributes->base (),
+ subr::nothing ());
+ }
+
+ class children_type
+ {
+ friend class debug_info_entry;
+ private:
+ entry_pointers _m_ptr;
+
+ inline explicit children_type (const entry_pointers &ptr)
+ : _m_ptr (ptr)
+ {}
+
+ public:
+ class const_iterator
+ : public std::iterator<std::input_iterator_tag, debug_info_entry>
+ {
+ friend class children_type;
+ friend class attr_value;
+
+ private:
+ dwarf_output::debug_info_entry::children_type::
+ _base::const_iterator _m_final_iter;
+ typename std::vector<entry *>::const_iterator _m_pending_iter;
+ bool _m_final;
+
+ inline const_iterator
+ (const dwarf_output::debug_info_entry::const_pointer &i)
+ : _m_final_iter (i.base ()), _m_final (true)
+ {}
+
+ inline const_iterator
+ (const typename std::vector<entry *>::const_iterator &i)
+ : _m_pending_iter (i), _m_final (false)
+ {}
+
+ /* We have what appears to be a final reference attribute.
+ If it's actually a circular_reference, it might really
+ not be final after all. */
+ inline void init_from_ref (const value::value_reference *ref)
+ {
+ const circular_reference *circle
+ = dynamic_cast<const circular_reference *> (ref);
+ _m_final = circle == NULL || circle->final ();
+ if (_m_final)
+ _m_final_iter = ref->ref;
+ else
+ _m_pending_iter = circle->pending ();
+ }
+
+ // This is called only by attr_value::reference, below.
+ inline const_iterator (const value::value_reference *ref)
+ {
+ init_from_ref (ref);
+ assert ((**this).identity () == (**this).identity ());
+ }
+
+ /* This is called only by attr_value::reference, below.
+ We have what appears to be a reference to a pending entry.
+ In fact, this entry might already have been finalized even
+ though this reference to it has not been. */
+ inline const_iterator (entry *ref)
+ : _m_final (ref->_m_final != NULL)
+ {
+ if (_m_final)
+ init_from_ref (ref->self ());
+ else
+ _m_pending_iter = ref->pending_self ();
+ assert ((**this).identity () == (**this).identity ());
+ }
+
+ public:
+ inline const_iterator ()
+ {}
+
+ inline const_iterator (const const_iterator &other)
+ {
+ *this = other;
+ }
+
+ inline const debug_info_entry operator* () const
+ {
+ return (_m_final
+ ? debug_info_entry (*_m_final_iter)
+ : debug_info_entry (*_m_pending_iter));
+ }
+
+ inline bool operator== (const const_iterator &other) const
+ {
+ return (_m_final == other._m_final
+ && (_m_final
+ ? _m_final_iter == other._m_final_iter
+ : _m_pending_iter == other._m_pending_iter));
+ }
+ inline bool operator!= (const const_iterator &other) const
+ {
+ return !(*this == other);
+ }
+
+ inline const_iterator &operator= (const const_iterator &other)
+ {
+ _m_final = other._m_final;
+ if (_m_final)
+ _m_final_iter = other._m_final_iter;
+ else
+ _m_pending_iter = other._m_pending_iter;
+ return *this;
+ }
+
+ inline const_iterator &operator++ () // prefix
+ {
+ if (_m_final)
+ ++_m_final_iter;
+ else
+ ++_m_pending_iter;
+ return *this;
+ }
+ inline const_iterator operator++ (int) // postfix
+ {
+ const const_iterator old = *this;
+ ++*this;
+ return old;
+ }
+ };
+
+ inline bool empty () const
+ {
+ return size () == 0;
+ }
+
+ inline size_t size () const
+ {
+ return (_m_ptr.final != NULL
+ ? _m_ptr.final->first.children ().size ()
+ : _m_ptr.pending->_m_pending->_m_children.size ());
+ }
+
+ inline const_iterator begin () const
+ {
+ return (_m_ptr.final != NULL
+ ? const_iterator (_m_ptr.final->first.children ()
+ .begin ())
+ : const_iterator (_m_ptr.pending->_m_pending->_m_children
+ .begin ()));
+ }
+
+ inline const_iterator end () const
+ {
+ return (_m_ptr.final != NULL
+ ? const_iterator (_m_ptr.final->first.children ()
+ .end ())
+ : const_iterator (_m_ptr.pending->_m_pending->_m_children
+ .end ()));
+ }
+ };
+
+ typedef typename children_type::const_iterator const_pointer;
+ typedef const_pointer pointer;
+
+ inline const children_type children () const
+ {
+ return children_type (_m_ptr);
+ }
+
+ inline bool has_children () const
+ {
+ return !children ().empty ();
+ }
+
+ inline uintptr_t identity () const
+ {
+ return (uintptr_t) _m_ptr.final ?: (uintptr_t) _m_ptr.pending;
+ }
+
+ inline ::Dwarf_Off original_offset () const
+ {
+ if (_m_ptr.final == NULL)
+ return _m_ptr.pending->_m_offset;
+ return _m_ptr.final->second.original_offset ();
+ }
+ };
+
+ // This wrapper class exists only to enhance reference variant.
+ struct attr_value
+ : public dwarf_output::attr_value
+ {
+ inline attr_value (const dwarf_output::attr_value &other)
+ : dwarf_output::attr_value (other)
+ {}
+
+ // An entry * in a pending_entry's attr_map counts as a reference.
+ inline dwarf::value_space what_space () const
+ {
+ return (dynamic_cast<const entry *> (this->_m_value) != NULL
+ ? dwarf::VS_reference
+ : dwarf_output::attr_value::what_space ());
+ }
+
+ inline typename debug_info_entry::const_pointer reference () const
+ {
+ const entry *ref = dynamic_cast<const entry *> (_m_value);
+ if (ref == NULL)
+ // Either really a final reference, or a circular reference.
+ return typename debug_info_entry::const_pointer
+ (dynamic_cast<const value::value_reference *> (_m_value));
+
+ /* This is an attribute comparison inside the attrs_match
+ comparator. The attribute sets passed to attrs_match
+ directly don't hit this--they've already been finalized.
+ But following those references we got to another
+ pending_entry and its attributes that are not yet
+ finalized. If attrs_match winds up returning true, these
+ will never be finalized because they are duplicates. */
+ return typename debug_info_entry::const_pointer
+ (const_cast<entry *> (ref));
+ }
+ };
+
+ // Convenience wrapper.
+ static inline const typename debug_info_entry::attributes_type
+ attributes (const dwarf_output::debug_info_entry::attributes_type &attrs)
+ {
+ return typename debug_info_entry::attributes_type (attrs.base (),
+ subr::nothing ());
+ }
+ };
+
+ /* This is a specialized tracker used solely in attrs_match, below.
+ We are comparing final entries already in the collector against
+ the almost-final pending_entry ready to be stored. Both sides
+ are pending_dwarf rather than dwarf_output begin the left-hand
+ side, because a reference attribute of a "final" entry can be a
+ circular_reference that still points back to a pending entry. */
+ class tracker
+ : public dwarf_tracker_base<pending_dwarf, pending_dwarf>
+ {
+ private:
+ typedef dwarf_tracker_base<pending_dwarf, pending_dwarf> _base;
+
+ const bool _m_ignore_context;
+
+ inline bool ignore_context () const
+ {
+ return _m_ignore_context;
+ }
+
+ public:
+ typedef typename _base::cu1 cu1;
+ typedef typename _base::cu2 cu2;
+ typedef typename _base::die1 die1;
+ typedef typename _base::die2 die2;
+
+ inline explicit tracker (copier *c)
+ : _m_ignore_context (c->_m_collector->ignore_context ())
+ {}
+
+ typedef die_info_pair *left_context_type;
+ typedef std::pair<die_info_pair *, entry *> right_context_type;
+
+ // Return the lhs context of an arbitrary DIE.
+ inline left_context_type left_context (const die1 &ref)
+ {
+ return (*ref).get_final ();
+ }
+
+ // Return the rhs context of an arbitrary DIE.
+ inline const right_context_type right_context (const die2 &ref)
+ {
+ return (*ref).context ();
+ }
+
+ /* Comparing two final DIEs for context. They match only if their
+ immediate parents are the same final entry in the collector, or
+ if they are both top-level children of a CU. */
+ inline bool final_context_match (die_info_pair *a, die_info_pair *b)
+ {
+ a = a->second._m_parent;
+ b = b->second._m_parent;
+ if (a == b)
+ return true;
+ if (a == NULL || b == NULL)
+ return false;
+ return a->second._m_parent == NULL && b->second._m_parent == NULL;
+ }
+
+ inline bool context_quick_mismatch (const left_context_type &lhs,
+ const right_context_type &rhs)
+
+ {
+ if (ignore_context ())
+ return false;
+
+ if (rhs.first != NULL)
+ // Comparing final to final.
+ return !final_context_match (lhs, rhs.first);
+
+ // Comparing final to pending. XXX track depth??
+ return ((rhs.second->_m_parent == NULL)
+ != (lhs->second._m_parent == NULL));
+ }
+
+ inline bool context_match (const left_context_type &lhs,
+ const right_context_type &rhs)
+ {
+ if (ignore_context ())
+ return true;
+
+ if (rhs.first != NULL)
+ // Comparing final to final.
+ return final_context_match (lhs, rhs.first);
+
+ // Comparing final to pending.
+ die_info_pair *a = lhs->second._m_parent;
+ entry *b = rhs.second->_m_parent;
+ while (a != NULL)
+ {
+ if (b == NULL)
+ return false;
+
+ if (a->second._m_parent == NULL)
+ /* A is the top-level CU entry.
+ We don't compare the CU attributes.
+ It's a match if B is also up to its top level. */
+ return b->_m_parent == NULL;
+
+ if (!(dwarf_comparator<dwarf_output, pending_dwarf>::equal_enough
+ (a->first, typename pending_dwarf::debug_info_entry (b))))
+ return false;
+
+ a = a->second._m_parent;
+ b = b->_m_parent;
+ }
+
+ // We can only get here if these were actually CU references.
+ return b == NULL;
+ }
+
+#if 0
+ static inline std::ostream &
+ dump (const typename pending_dwarf::debug_info_entry &a,
+ const typename pending_dwarf::debug_info_entry &b,
+ bool in = false, bool out = false)
+ {
+ return entry::debug_prefix (in, out)
+ << "XXX " << (a.final () ? "final " : "pending ")
+ << std::hex << a.original_offset ()
+ << " vs " << (b.final () ? "final " : "pending ")
+ << b.original_offset () << std::dec;
+ }
+
+ static inline std::ostream &
+ dump (const die1 &ref1, const die2 &ref2,
+ bool in = false, bool out = false)
+ {
+ return dump (*ref1, *ref2, in, out);
+ }
+
+ struct step : public _base::step
+ {
+ inline step (tracker *t, const die1 &a, const die2 &b)
+ : _base::step (t, a, b)
+ {
+ dump (*a, *b, true) << " cmp\n";
+ }
+ inline ~step ()
+ {
+ entry::debug_prefix (false, true, false);
+ }
+ };
+#else
+ static inline subr::nostream &
+ dump (const typename pending_dwarf::debug_info_entry &,
+ const typename pending_dwarf::debug_info_entry &,
+ bool = false, bool = false)
+ {
+ return entry::debug ();
+ }
+
+ static inline subr::nostream &
+ dump (const die1 &, const die2 &,
+ bool = false, bool = false)
+ {
+ return entry::debug ();
+ }
+#endif
+
+ class reference_match
+ {
+ entry *_m_pending;
+
+ public:
+ inline reference_match ()
+ : _m_pending (NULL)
+ {
+ entry::debug_prefix (true, false, false);
+ }
+
+ inline bool prematch (tracker *, const die1 &ref1, const die2 &ref2)
+ {
+ const typename pending_dwarf::debug_info_entry a = *ref1;
+ const typename pending_dwarf::debug_info_entry b = *ref2;
+
+ dump (a, b) << " reference_match\n";
+
+ if (!a.final ())
+ // XXX pending circular lhs can never match ???
+ return !b.final () && a.get_pending () == b.get_pending ();
+
+ die_info_pair *const lhs = a.get_final ();
+
+ if (b.final ())
+ return lhs == b.get_final ();
+
+ entry *const rhs = b.get_pending ();
+
+ if (rhs->_m_pending->_m_matched != NULL)
+ return lhs == rhs->_m_pending->_m_matched;
+
+ if (rhs->_m_comparing != NULL)
+ {
+ /* We have a circularity on the right-hand side. We can tell
+ because _m_comparing remains set from an outer recursion
+ still in progress.
+
+ The circular chain of references rooted at A matches B if B
+ is also the root of its own circularity and everything along
+ those parallel chains matches. If the chains hadn't matched
+ so far, we would not have kept following them to get here.
+
+ So, this matches if what we were comparing to was the same A.
+ If it didn't match, we have left _m_pending clear, which makes
+ negative_cache trigger (below). */
+
+ if (rhs->_m_comparing != lhs)
+ return false;
+
+ dump (a, b) << " tentative circular match\n";
+ return true;
+ }
+
+ if (rhs->_m_pending->cached_mismatch (lhs))
+ return false;
+
+ /* Record that we have a walk in progress crossing B. When this
+ reference_match object goes out of scope in our caller, its
+ destructor will reset _m_comparing to clear this record. */
+ rhs->_m_comparing = lhs;
+ _m_pending = rhs;
+ return false;
+ }
+
+ inline bool negative_cache () const
+ {
+ return _m_pending == NULL;
+ }
+
+ inline ~reference_match ()
+ {
+ if (_m_pending != NULL)
+ {
+ assert (_m_pending->_m_comparing != NULL);
+ _m_pending->_m_comparing = NULL;
+ }
+ entry::debug_prefix (false, true, false);
+ }
+ };
+
+ // This call is used purely in hopes of a cache hit.
+ inline bool prematch (reference_match &matched,
+ const die1 &a, const die2 &b)
+ {
+ bool same = matched.prematch (this, a, b);
+ dump (a, b) << " prematch => " << same << "\n";
+ return same;
+ }
+
+ // This call is used only as part of a real reference lookup.
+ inline bool reference_matched (reference_match &matched,
+ const die1 &a, const die2 &b)
+ {
+ bool same = matched.prematch (this, a, b);
+ dump (a, b) << " reference_matched => " << same << "\n";
+ return same;
+ }
+
+ // Check for a negative cache hit after prematch or reference_match.
+ inline bool cannot_match (reference_match &matched,
+ const die1 &, const die2 &)
+ {
+ return matched.negative_cache ();
+ }
+
+ // This can cache a result.
+ inline bool notice_match (reference_match &matched,
+ const die1 &ref1, const die2 &ref2,
+ bool result)
+ {
+ if (result && matched.negative_cache ())
+ {
+ /* This positive result is from a tentative match of congruent
+ circular references. That doesn't mean they really match,
+ only that they might if the rest of their trees do. Don't
+ cache it as a match now. */
+ dump (ref1, ref2) << " should ignore tentative match\n";
+ return result;
+ }
+
+ const typename pending_dwarf::debug_info_entry a = *ref1;
+ const typename pending_dwarf::debug_info_entry b = *ref2;
+ dump (a, b) << " notice_match (" << result << ")\n";
+ if (result)
+ {
+ /* We've found two matching entries. If we just matched a
+ final entry to a pending entry, cache that knowledge so
+ we don't bother with the whole hash lookup and comparison
+ when we come to finalizing that pending entry itself. */
+
+ if (a.final ())
+ {
+ if (!b.final ())
+ b.get_pending ()->record_prematch (a.get_final (), false);
+ }
+ else if (b.final ())
+ a.get_pending ()->record_prematch (b.get_final (), true);
+ }
+ else
+ b.get_pending ()->_m_pending->notice_mismatch (a.get_final ());
+ return result;
+ }
+
+ template<typename item1, typename item2>
+ inline bool identical (const item1 &, const item2 &)
+ {
+ return false;
+ }
+
+ inline bool identical (const typename pending_dwarf::debug_info_entry &a,
+ const typename pending_dwarf::debug_info_entry &b)
+ {
+ return a.is (b);
+ }
+
+ inline bool identical (const typename pending_dwarf::attr_value &a,
+ const typename pending_dwarf::attr_value &b)
+ {
+ return a.is (b);
+ }
+
+ typedef tracker subtracker;
+ inline tracker (const tracker &proto, reference_match &,
+ const left_context_type &,
+ const right_context_type &)
+ : _m_ignore_context (proto._m_ignore_context)
+ {}
+ };
+
+ typedef dwarf_comparator<pending_dwarf, pending_dwarf,
+ false, tracker> comparator;
+
+ // This is what the entire pending_dwarf class exists for.
+ inline bool attrs_match (const debug_info_entry::attributes_type &a,
+ const debug_info_entry::attributes_type &b)
+ {
+ tracker t (this);
+ return comparator (t).equals (pending_dwarf::attributes (a),
+ pending_dwarf::attributes (b));
+ }
+
+ /* We're likely to come across the same strings/identifiers and source
+ files many times in a copying run. When they are the very same
+ pointers into the input dwarf object data, we can optimize the
+ ordinary string hash lookup in the value_set by caching the mapping
+ of input pointers to output values. */
+ template<typename value_type>
+ struct string_cache
+ {
+ std::map<uintptr_t, const value_type *> _m_cache;
+
+ template<typename input>
+ inline const value_type *add (subr::value_set<value_type> &set,
+ uintptr_t key, const input &x)
+ {
+ const value_type *&cache = _m_cache[key];
+ if (cache == NULL)
+ cache = set.add (x);
+ return cache;
+ }
+
+ inline const value_type *add (subr::value_set<value_type> &set,
+ const char *x)
+ {
+ return add (set, (uintptr_t) x, x);
+ }
+
+ inline const value_type *add (subr::value_set<value_type> &set,
+ const std::string &x)
+ {
+ return add (set, (uintptr_t) &x, x);
+ }
+
+ template<typename input>
+ inline const value_type *add (subr::value_set<value_type> &set,
+ const input &x)
+ {
+ return set.add (x);
+ }
+ };
+
+ string_cache<value::value_string> _m_strings;
+ string_cache<value::value_identifier> _m_identifiers;
+
+ std::tr1::unordered_map<
+ uintptr_t, const value::value_source_file *> _m_source_file_cache;
+
+ template<typename input>
+ inline const value::value_source_file *add_source_file (int /*whatattr*/,
+ const input &x)
+ {
+ const value::value_source_file *&cache
+ = _m_source_file_cache[x.identity ()];
+ if (cache == NULL)
+ cache = _m_collector->_m_source_file.add (x);
+ return cache;
+ }
+
+ template<typename input>
+ inline const value::value_string *add_string (const input &x)
+ {
+ return _m_strings.add (_m_collector->_m_strings, x);
+ }
+
+ template<typename input>
+ inline const value::value_string *add_identifier (const input &x)
+ {
+ return _m_identifiers.add (_m_collector->_m_identifiers, x);
+ }
+
+ template<typename input>
+ inline const value::value_flag *add_flag (const input &x)
+ {
+ return dwarf_output_collector::flag (x);
+ }
+
+ template<typename input>
+ inline const value::value_address *add_address (const input &x)
+ {
+ return _m_collector->_m_address.add (x);
+ }
+
+ template<typename input>
+ inline const value::value_rangelistptr *add_ranges (const input &x)
+ {
+ return _m_collector->_m_ranges.add (x);
+ }
+
+ template<typename input>
+ inline const value::value_lineptr *add_line_info (const input &x)
+ {
+ return _m_collector->_m_line_info.add (x, *_m_collector);
+ }
+
+ template<typename input>
+ inline const value::value_constant *add_constant (const input &x)
+ {
+ return _m_collector->_m_constants.add (x);
+ }
+
+ template<typename input>
+ inline const value::value_constant_block *
+ add_constant_block (const input &x)
+ {
+ return _m_collector->_m_const_block.add (x);
+ }
+
+ template<typename input>
+ inline const value::value_dwarf_constant *
+ add_dwarf_constant (const input &x)
+ {
+ return _m_collector->_m_dwarf_const.add (x);
+ }
+
+ template<typename input>
+ inline const value::value_source_line *add_source_line (const input &x)
+ {
+ return _m_collector->_m_source_line.add (x);
+ }
+
+ template<typename input>
+ inline const value::value_source_column *add_source_column (const input &x)
+ {
+ return _m_collector->_m_source_column.add (x);
+ }
+
+ template<typename input>
+ inline const value::value_location *add_location (const input &x)
+ {
+ return _m_collector->_m_locations.add (x);
+ }
+
+ public:
+ inline explicit copier (dwarf_output_collector &c)
+ : _m_collector (&c), _m_entries (), _m_undefined_entries (0)
+ {}
+
+ inline operator dwarf_output_collector & () const
+ {
+ return *_m_collector;
+ }
+ };
+
+ // Explicit instantiations.
+ extern template class dwarf_data::value<dwarf_output, false>;
+ extern template class dwarf_data::attr_value<dwarf_output,
+ dwarf_output::value>;
+ extern template class dwarf_data::attributes_type<dwarf_output,
+ dwarf_output::value>;
+ extern template class dwarf_data::compile_unit<dwarf_output>;
+ extern template class dwarf_data::compile_units<dwarf_output>;
+
+ extern template class dwarf_output::copier<dwarf>;
+ extern template class dwarf_output::copier<dwarf_edit>;
+};
+
+#endif // <elfutils/dwarf_output>
diff --git a/libdw/c++/dwarf_ref_maker b/libdw/c++/dwarf_ref_maker
new file mode 100644
index 00000000..cfb6c4a1
--- /dev/null
+++ b/libdw/c++/dwarf_ref_maker
@@ -0,0 +1,179 @@
+/* elfutils::dwarf_ref_maker -- -*- C++ -*- template type specification
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifndef _ELFUTILS_DWARF_REF_MAKER
+#define _ELFUTILS_DWARF_REF_MAKER 1
+
+#include "dwarf"
+#include <tr1/unordered_map>
+#include <vector>
+
+namespace elfutils
+{
+ // Prototypical stub for reference maker object.
+ // This keeps no state and can't really be used.
+ template<class output, class input>
+ struct dwarf_ref_maker_base
+ {
+ typedef typename input::debug_info_entry input_entry;
+ typedef typename input_entry::children_type::const_iterator input_ref;
+ typedef typename output::debug_info_entry::pointer output_ref;
+
+ // These are called around a whole-file construction.
+ inline void start () {}
+
+ // If called, all pointers passed in since start () before are bogus.
+ inline void abort () {}
+
+ // Construction is complete: now snap in all recorded references.
+ inline void finish (output &file) {}
+
+ // The referenced output DIE has been constructed to match the input DIE.
+ inline void equivalence (const output_ref &to, const input_ref &from)
+ {
+ }
+
+ // *REF is an uninitialized attr_value.reference ().
+ // It's meant to refer to the output DIE equivalent to the given input DIE.
+ inline void refer (output_ref *ref, const input_ref &target)
+ {
+ throw std::logic_error ("dwarf_ref_maker_base cannot make references");
+ }
+ };
+
+ // Simple maker used for a single copy-construction.
+ template<class output, class input>
+ class dwarf_ref_maker : public dwarf_ref_maker_base<output, input>
+ {
+ public:
+ typedef typename input::debug_info_entry input_entry;
+ typedef typename output::debug_info_entry output_entry;
+ typedef typename input_entry::children_type::const_iterator input_ref;
+ typedef typename output_entry::children_type::iterator output_ref;
+
+ private:
+
+ struct seen
+ {
+ bool _m_known;
+ output_ref _m_out;
+ std::vector<output_ref *> _m_refs;
+
+ inline seen ()
+ : _m_known (false), _m_out (), _m_refs ()
+ {}
+
+ inline void resolve ()
+ {
+ for (; !_m_refs.empty (); _m_refs.pop_back ())
+ *_m_refs.back () = _m_out;
+ }
+
+ inline void resolve (const output_ref &to)
+ {
+ _m_out = to;
+ _m_known = true;
+ resolve ();
+ }
+
+ inline void refer (output_ref *out)
+ {
+ _m_refs.push_back (out);
+ if (_m_known)
+ resolve ();
+ }
+ };
+
+ std::tr1::unordered_map<dwarf::debug_info_entry::identity_type,
+ seen> _m_map;
+
+ public:
+ inline dwarf_ref_maker ()
+ : _m_map ()
+ {}
+
+ inline dwarf_ref_maker (const dwarf_ref_maker &other)
+ : _m_map ()
+ {
+ if (unlikely (!other._m_map.empty ()))
+ throw std::logic_error
+ ("dwarf_ref_maker copy constructs only from default-constructed");
+ }
+
+ inline ~dwarf_ref_maker ()
+ {}
+
+ inline void abort ()
+ {
+ _m_map.clear ();
+ }
+
+ inline void equivalence (const output_ref &out, const input_ref &in)
+ {
+ _m_map[in->identity ()].resolve (out);
+ }
+
+ inline void refer (output_ref *out, const input_ref &in)
+ {
+ _m_map[in->identity ()].refer (out);
+ }
+
+ inline void finish (output &file)
+ {
+ while (_m_map.begin () != _m_map.end ())
+ if (_m_map.begin ()->second._m_known)
+ _m_map.erase (_m_map.begin ());
+ else
+ throw std::logic_error ("construction finished with unresolved refs");
+ }
+ };
+
+};
+
+#endif // <elfutils/dwarf_ref_maker>
diff --git a/libdw/c++/dwarf_tracker b/libdw/c++/dwarf_tracker
new file mode 100644
index 00000000..cbd253e8
--- /dev/null
+++ b/libdw/c++/dwarf_tracker
@@ -0,0 +1,737 @@
+/* elfutils::dwarf_ref_tracker -- DWARF reference tracking in -*- C++ -*-
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifndef _ELFUTILS_DWARF_TRACKER
+#define _ELFUTILS_DWARF_TRACKER 1
+
+#include "subr.hh"
+#include "dwarf"
+#include "dwarf_comparator"
+#include <tr1/unordered_map>
+#include <tr1/unordered_set>
+
+namespace elfutils
+{
+ // Basic tracker of tree-walk paths to DIEs.
+ template<typename dw>
+ class dwarf_path_finder
+ {
+ public:
+ typedef typename dw::compile_units::const_iterator cu;
+ typedef typename dw::debug_info_entry::children_type::const_iterator die;
+
+ /* We maintain the current path down the logical DIE tree from the CU
+ as a stack of iterators pointing to the DIE at each level.
+
+ Note that the path to a DIE includes the iterator to that DIE
+ itself as the last element. This is necessary to permit sharing
+ our _m_seen cache across CUs. That sharing is useful when CUs
+ might share children (i.e. they use DW_TAG_imported_unit).
+ But when they do, then the "construct a derived tracker that
+ jump-starts a walk" case for following a reference might be for
+ a reference to another CU than the one the base tracker is
+ walking (_m_root). When path_to finds the "context" path to the
+ referent, the iterator that jump-starts a new walk must be an
+ iterator pointing to the referent, but must be an iterator
+ somewhere in the _m_root CU's tree, not another CU's.
+
+ NOTE!!! XXX
+ This scenario means we can have a die_path in our _m_seen that
+ is not from our current _m_root CU. This is only safe as long
+ as we are sure that we have already completely walked the other
+ CU that die_path came from so all its entries are in _m_seen.
+ This ensures that a derived tracker that jump-starts its walk at
+ a path in another CU will never actually have to do any walking.
+ If it ever walked, it could go awry failing to recognize the end
+ of its CU's children list--it's not _m_root->children ().end ().
+ If we want to generalize dwarf_path_finder so it can be used as
+ a generic cache when we might not have walked whole CUs, then we
+ need to change things. We'd have to store _m_root along with
+ _m_path in _m_seen so that a derived tracker made from path_to
+ "context" can use the right _m_root.
+ */
+ typedef subr::stackish<die> die_path;
+
+ private:
+ // We use an empty list as a marker; every path includes at least one DIE.
+ static inline const die_path bad_die_path ()
+ {
+ return die_path ();
+ }
+ static inline bool bad_die_path (const die_path &path)
+ {
+ return path.empty ();
+ }
+
+ /* We record every DIE we have seen here, mapping its .identity () to
+ the die_path of parent DIEs taken to reach it, including itself. */
+ typedef std::tr1::unordered_map<dwarf::debug_info_entry::identity_type,
+ const die_path> die_map;
+ die_map *_m_seen;
+ bool _m_delete_seen;
+
+ cu _m_root;
+
+ die_path _m_path;
+
+ explicit dwarf_path_finder (const dwarf_path_finder &)
+ {
+ throw std::logic_error ("not copy-constructible");
+ }
+
+ inline bool at_top () const
+ {
+ return _m_path.empty ();
+ }
+
+ inline const die &current_die () const
+ {
+ assert (!at_top ());
+ return _m_path.top ();
+ }
+
+ inline die current_end () const
+ {
+ assert (!at_top ());
+ const typename die_path::const_reverse_iterator i = ++_m_path.rbegin ();
+ return (i == _m_path.rend ()
+ ? (*_m_root).children ().end ()
+ : (**i).children ().end ());
+ }
+
+ public:
+ // Default constructor: an original tracker.
+ inline dwarf_path_finder ()
+ : _m_seen (new die_map), _m_delete_seen (true)
+ {}
+
+ // Construct a derived tracker: does its own whole walk, but sharing caches.
+ inline dwarf_path_finder (const dwarf_path_finder &proto, bool)
+ : _m_seen (proto._m_seen), _m_delete_seen (false)
+ {}
+
+ /* Construct a derived tracker that jump-starts a walk.
+ CONTEXT is from a path_to call made on PROTO. */
+ inline dwarf_path_finder (const dwarf_path_finder &proto,
+ const die_path &context)
+ : _m_seen (proto._m_seen), _m_delete_seen (false),
+ _m_root (proto._m_root), _m_path (context)
+ {}
+
+ inline ~dwarf_path_finder ()
+ {
+ if (_m_delete_seen)
+ {
+ delete _m_seen;
+ // We should never be left with a partial walk on the books.
+ assert (_m_path.empty ());
+ }
+ }
+
+ // Main hooks for a normal walk.
+
+ /* A walk object does set-up work when constructed and tear-down
+ work when destroyed, so tear-down is done even for exceptions. */
+ struct walk
+ {
+ dwarf_path_finder *_m_tracker;
+ bool _m_jumped;
+
+ inline walk (dwarf_path_finder *w, const cu &root)
+ : _m_tracker (w), _m_jumped (false)
+ {
+ assert (_m_tracker->_m_path.empty ());
+ _m_tracker->_m_root = root;
+ }
+
+ inline ~walk ()
+ {
+ if (_m_jumped)
+ _m_tracker->_m_path.clear ();
+ else
+ assert (_m_tracker->_m_path.empty ());
+ }
+
+ inline void jump (const typename dw::debug_info_entry &there)
+ {
+ _m_jumped = true;
+ _m_tracker->prime_path_to (there);
+ }
+ };
+
+ /* A step object does pre-order work when constructed and post-order
+ work when destroyed, so post-order is done even for exceptions.
+ While this object lives, HERE is on the _m_path stack. */
+ struct step
+ {
+ dwarf_path_finder *_m_walker;
+ inline step (dwarf_path_finder *w, const die &here,
+ bool record = true)
+ : _m_walker (w)
+ {
+ // Append this DIE to the path we'll record for it and its children.
+ _m_walker->_m_path.push (here);
+
+ // Record the path down from the CU to see this DIE.
+ assert (!bad_die_path (_m_walker->_m_path));
+ if (record)
+ _m_walker->_m_seen->insert (std::make_pair ((*here).identity (),
+ _m_walker->_m_path));
+ }
+ inline ~step ()
+ {
+ _m_walker->_m_path.pop ();
+ }
+ };
+
+ bool prime_path_to (const typename dw::debug_info_entry &here,
+ dwarf::debug_info_entry::identity_type id)
+ {
+ if (here.identity () == id)
+ return true;
+
+ for (typename dw::debug_info_entry::children_type::const_iterator i
+ = here.children ().begin ();
+ i != here.children ().end ();
+ ++i)
+ {
+ step into (this, i);
+ if (prime_path_to (*i, id))
+ return true;
+ }
+
+ return false;
+ }
+
+ inline void unreachable (const typename dw::debug_info_entry &) const
+ {
+ throw std::runtime_error ("DIE not reachable from CU!");
+ }
+
+ inline void prime_path_to (const typename dw::debug_info_entry &there)
+ {
+ assert (at_top ());
+ bool found = prime_path_to (*_m_root, there.identity ());
+ assert (at_top ());
+ if (likely (found))
+ _m_path = (*_m_seen)[there.identity ()];
+ else
+ unreachable (there);
+ }
+
+ // Random access to a DIE, find the path of the walk that gets there.
+ inline const die_path &path_to (const die &a)
+ {
+ return path_to (*a);
+ }
+
+ inline const die_path &path_to (const typename dw::debug_info_entry &a)
+ {
+ const dwarf::debug_info_entry::identity_type id = a.identity ();
+ std::pair<typename die_map::iterator, bool> found
+ = _m_seen->insert (std::make_pair (id, bad_die_path ()));
+ if (found.second
+ /* It's not in our _m_seen map. Our main walk recording
+ into _m_seen is exhaustive, so this can only be a forward
+ reference. That is, we didn't already hit this DIE in
+ our top-level walk and so it is not in _m_seen yet.
+
+ We must do a separate walk to find it. Since we know
+ this is a forward reference, we don't have to start a
+ fresh walk from the root, just momentarily wind forward
+ from where we are. */
+ && !walk_down_to (id, found.first)
+ && !walk_over_to (id, found.first)
+ && !walk_up_to (id, found.first))
+ unreachable (a);
+ assert (&found.first->second != NULL);
+ assert (!bad_die_path (found.first->second));
+ return found.first->second;
+ }
+
+ private:
+ inline bool walk_to (const typename die::value_type &here,
+ dwarf::debug_info_entry::identity_type there,
+ typename die_map::iterator &cache)
+ {
+ return walk_to (here.children ().begin (),
+ here.children ().end (),
+ there, cache);
+ }
+
+ bool walk_to (die it, const die &end,
+ dwarf::debug_info_entry::identity_type there,
+ typename die_map::iterator &cache)
+ {
+ for (; it != end; ++it)
+ {
+ /* Note that we compare identities here, rather than passing down
+ a THERE iterator and comparing iterators. In dwarf_output, we
+ can have multiple iterators into distinct children_type vectors
+ that all point to the same entry. A reference could be one of
+ these iterators, and all mean the same entry. */
+ if ((*it).identity () == there)
+ {
+ /* We can't keep the old CACHE iterator and avoid this
+ find (hash lookup), because there could have been
+ other insertions in the map since it was taken.
+ Those can invalidate old iterators. */
+ cache = _m_seen->find (there);
+ _m_seen->erase (cache);
+
+ // Include the iterator we've found in the path to itself.
+ step into (this, it, false);
+
+ cache = _m_seen->insert (cache, std::make_pair (there, _m_path));
+ return true;
+ }
+ else
+ {
+ /* Do "step into" even for !has_children ()
+ because it records this child in _m_seen,
+ which we will rely on later. */
+ step into (this, it);
+ const typename die::value_type &child = *it;
+ if (child.has_children () && walk_to (child, there, cache))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* First descend into the current DIE's children.
+ _m_path already has the current DIE, so it is ready to go. */
+ // XXX is a reference to an owned DIE really possible??
+ inline bool walk_down_to (dwarf::debug_info_entry::identity_type there,
+ typename die_map::iterator &cache)
+ {
+ if ((*current_die ()).has_children ())
+ {
+ /* It's common to have a reference to the next sibling DIE.
+ So bypass the descent to HERE's children if THERE is
+ HERE's immediate next sibling. */
+
+ die next = current_die ();
+ ++next;
+
+ if (next == current_end () || there != (*next).identity ())
+ return walk_to (*current_die (), there, cache);
+ }
+ return false;
+ }
+
+ /* A step_up object saves _m_path when constructed
+ and restores it when destroyed. */
+ struct step_up
+ {
+ dwarf_path_finder *_m_walker;
+ die_path _m_save;
+ inline step_up (dwarf_path_finder *w)
+ : _m_walker (w), _m_save (w->_m_path)
+ {}
+ inline ~step_up ()
+ {
+ _m_walker->_m_path.swap (_m_save);
+ }
+ };
+
+ /* A step_back object pops the current DIE off _m_path when
+ constructed, and pushes it back when destroyed. */
+ struct step_back
+ {
+ dwarf_path_finder *_m_walker;
+ const die _m_here;
+ inline step_back (dwarf_path_finder *w, die &copy)
+ : _m_walker (w), _m_here (w->current_die ())
+ {
+ w->_m_path.pop ();
+ copy = _m_here;
+ }
+ inline ~step_back ()
+ {
+ _m_walker->_m_path.push (_m_here);
+ }
+ };
+
+ /* Now wind the walk forward starting from the current DIE's
+ immediate sibling. */
+ inline bool walk_over_to (dwarf::debug_info_entry::identity_type there,
+ typename die_map::iterator &cache)
+ {
+ const die end = current_end (); // Taken before step_back.
+ die next;
+ step_back from (this, next);
+ ++next;
+
+ return walk_to (next, end, there, cache);
+ }
+
+ /* Now wind the walk forward starting from the current DIE's
+ parent's immediate sibling. */
+ inline bool walk_up_to (dwarf::debug_info_entry::identity_type there,
+ typename die_map::iterator &cache)
+ {
+ if (!at_top ())
+ {
+ step_up from (this);
+ while (_m_path.pop (), !at_top ())
+ if (walk_over_to (there, cache))
+ return true;
+ }
+ return false;
+ }
+ };
+
+ // Standard tracker.
+ template<class dwarf1, class dwarf2>
+ class dwarf_ref_tracker : public dwarf_tracker_base<dwarf1, dwarf2>
+ {
+ private:
+ typedef dwarf_tracker_base<dwarf1, dwarf2> _base;
+
+ explicit dwarf_ref_tracker (const dwarf_ref_tracker &)
+ : _base ()
+ {
+ throw std::logic_error ("not copy-constructible");
+ }
+
+ public:
+ typedef typename _base::cu1 cu1;
+ typedef typename _base::cu2 cu2;
+ typedef typename _base::die1 die1;
+ typedef typename _base::die2 die2;
+ class reference_match;
+
+ protected:
+ typedef dwarf_path_finder<dwarf1> tracker1;
+ typedef dwarf_path_finder<dwarf2> tracker2;
+
+ tracker1 _m_left;
+ tracker2 _m_right;
+
+ struct ref_hasher : public std::unary_function<die2, size_t>
+ {
+ inline size_t operator () (const die2 &i) const
+ {
+ return (*i).identity ();
+ }
+ };
+
+ struct same_ref : public std::equal_to<die2>
+ {
+ inline bool operator () (const die2 &a, const die2 &b) const
+ {
+ return (*a).identity () == (*b).identity ();
+ }
+ };
+
+ typedef std::tr1::unordered_map<dwarf::debug_info_entry::identity_type,
+ reference_match *> active_map;
+ active_map _m_active;
+
+ typedef std::pair<const die2 *,
+ std::tr1::unordered_set<die2, ref_hasher, same_ref>
+ > equiv_list;
+ typedef std::tr1::unordered_map<dwarf::debug_info_entry::identity_type,
+ equiv_list> equiv_map;
+ equiv_map *_m_equiv;
+ bool _m_delete_equiv;
+
+ inline equiv_list *equiv_to (const die1 &a)
+ {
+ return &(*_m_equiv)[a->identity ()];
+ }
+
+ struct equal_enough : public std::binary_function<die1, die2, bool>
+ {
+ inline bool operator () (const die1 &a, const die2 &b)
+ {
+ return dwarf_comparator<dwarf1, dwarf2>::equal_enough (*a, *b);
+ }
+ };
+
+ public:
+ inline dwarf_ref_tracker ()
+ : _m_equiv (new equiv_map), _m_delete_equiv (true)
+ {}
+
+ inline dwarf_ref_tracker (const tracker1 &proto)
+ : _m_left (proto, true),
+ _m_equiv (new equiv_map), _m_delete_equiv (true)
+ {}
+
+ inline ~dwarf_ref_tracker ()
+ {
+ if (_m_delete_equiv)
+ delete _m_equiv;
+ }
+
+ inline void reset ()
+ {
+ _m_equiv->clear ();
+ assert (!_m_right->_m_delete_seen);
+ _m_right._m_seen->clear ();
+ }
+
+ struct walk
+ {
+ typename tracker1::walk _m_left;
+ typename tracker2::walk _m_right;
+
+ inline walk (dwarf_ref_tracker *w, const cu1 &a, const cu2 &b)
+ : _m_left (&w->_m_left, a), _m_right (&w->_m_right, b)
+ {}
+
+ // Wind forward to cache everything up through A and B.
+ inline void jump (const typename dwarf1::debug_info_entry &a,
+ const typename dwarf2::debug_info_entry &b)
+ {
+ _m_left.jump (a);
+ _m_right.jump (b);
+ }
+ };
+
+ struct step
+ {
+ typename tracker1::step _m_left;
+ typename tracker2::step _m_right;
+
+ inline step (dwarf_ref_tracker *w, const die1 &a, const die2 &b)
+ : _m_left (&w->_m_left, a), _m_right (&w->_m_right, b)
+ {}
+ };
+
+ typedef typename tracker1::die_path left_context_type;
+ inline const left_context_type &left_context (const die1 &die)
+ {
+ return _m_left.path_to (die);
+ }
+
+ typedef typename tracker2::die_path right_context_type;
+ inline const right_context_type &right_context (const die2 &die)
+ {
+ return _m_right.path_to (die);
+ }
+
+ // Very cheap check for an obvious mismatch of contexts.
+ inline bool context_quick_mismatch (const left_context_type &a,
+ const right_context_type &b)
+
+ {
+ return a.size () != b.size ();
+ }
+
+ // Full match when context_quick_mismatch has returned false.
+ inline bool context_match (const left_context_type &a,
+ const right_context_type &b)
+ {
+ equal_enough equalator;
+ // Ignore the top of the stack, which is the target DIE itself.
+ return a.equal (b, equalator, 1);
+ }
+
+ class reference_match
+ {
+ friend class dwarf_ref_tracker;
+ protected:
+ equiv_list *_m_lhs;
+ typename active_map::value_type *_m_rhs;
+ active_map *_m_active;
+
+ public:
+
+ inline reference_match ()
+ : _m_lhs (NULL), _m_rhs (NULL), _m_active (NULL)
+ {}
+
+ inline ~reference_match ()
+ {
+ if (_m_lhs != NULL)
+ _m_lhs->first = NULL;
+ if (_m_rhs != NULL)
+ _m_active->erase (_m_rhs->first);
+ }
+ };
+
+ /* A prematch during the main tree walk does the same cache lookup
+ as real reference matching. But it doesn't record itself as a
+ "walk in progress" for the circularity-catching logic. Doing so
+ can break that logic for comparison purposes. Since we key our
+ cache on identity, a lookup can hit a shared DIE as well as one
+ that is truly involved in our current walk. If we hit a shared
+ DIE on the main walk, and within that recursion (i.e. somewhere
+ in its children or along its own references) we encounter a
+ circularity, we'd take the main-walk's equiv_list record as the
+ root of the circularity on one side, while on the other side the
+ DIEs may not have been shared and so the same circularity is
+ actually rooted at the second instance of an identical DIE. */
+ inline bool prematch (reference_match &matched,
+ const die1 &a, const die2 &b)
+ {
+ return reference_matched (matched, a, b, false);
+ }
+
+ inline bool
+ reference_matched (reference_match &matched, const die1 &a, const die2 &b,
+ bool record = true)
+ {
+ equiv_list *elt = equiv_to (a);
+ if (elt->first == NULL)
+ {
+ matched._m_lhs = elt;
+
+ if (record)
+ /* Record that we have a walk in progress crossing A.
+ When MATCHED goes out of scope in our caller, its
+ destructor will reset ELT->first to clear this record. */
+ elt->first = &b;
+
+ // Short-circuit if we have already matched B to A.
+ return elt->second.find (b) != elt->second.end ();
+ }
+
+ /* We have a circularity on the left-hand side. We can tell because
+ ELT->first remains set from an outer recursion still in progress.
+
+ The circular chain of references rooted at A matches B if B is
+ also the root of its own circularity and everything along those
+ parallel chains matches. If the chains hadn't matched so far,
+ we would not have kept following them to get here.
+
+ We recorded the B that arrived at the first comparison with A.
+ We actually record the pointer on the caller's stack rather
+ than a copy of B, just because the iterator might be larger. */
+
+ if ((**elt->first).identity () == (*b).identity ())
+ return true;
+
+ /* Our right-hand side is not in lock-step on a matching circularity.
+ But it's still possible this is a matching reference nonetheless.
+ A difference in the sharing vs duplication of DIEs between the
+ left-hand and right-hand sides could mean that one side's chain of
+ references reaches the same cycle sooner than the other's.
+
+ Consider:
+
+ A1 -> A2 -> ... -> A1' -> A2 ...
+ B1 -> B2 -> ... -> B1 -> B2 ...
+
+ Here A1' is an identical copy of A1, duplicated in the A object.
+ Otherwise A1 matches B1, A2 matches B2, etc. The B walk started
+ at B1 and hits it again at the step comparing B1 to A1'. But the
+ A walk has not hit A1 again yet (and perhaps it never will), so
+ our test above does not match.
+
+ This is the simplest example. There might be more steps of the
+ reference chain that have duplicates on one side but have been
+ consolidated to a single entry on the other. There can also be
+ multiple reference attributes at each node that differ on this
+ issue, making all manner of tangled graphs that all really match
+ the same simpler graph (and thus each other).
+
+ Now we start recording the state of the right-hand side reference
+ chain walk, and keep going. When the right-hand side then becomes
+ circular, we check that it has coincided with the left-hand side.
+
+ This is guaranteed to terminate, at least. It should never have
+ any false positives, since that continuing walk would eventually
+ find the differences. We hope it doesn't have any false negatives
+ either, but to be sure of that would require more graph theory
+ than your humble writer can bring to bear. */
+
+ const std::pair<typename active_map::iterator, bool> p
+ = _m_active.insert (std::make_pair ((*b).identity (), &matched));
+ if (p.second)
+ {
+ assert (p.first->second == &matched);
+ matched._m_lhs = elt;
+ matched._m_active = &_m_active;
+ matched._m_rhs = &*p.first;
+ return false;
+ }
+
+ assert (p.first->second != &matched);
+ return p.first->second->_m_lhs == elt;
+ }
+
+ inline bool cannot_match (reference_match &matched,
+ const die1 &, const die2 &)
+ {
+ return matched._m_lhs == NULL && matched._m_rhs == NULL;
+ }
+
+ inline bool notice_match (reference_match &/*matched*/,
+ const die1 &, const die2 &/*b*/, bool matches)
+ {
+ /* XXX not reliable!
+ This match could be predicated on a tentative match of a
+ circular ref inside. We can't cache that!
+ if (matches && matched._m_lhs != NULL)
+ matched._m_lhs->second.insert (b);
+ */
+ return matches;
+ }
+
+ typedef dwarf_ref_tracker subtracker;
+
+ // Share the _m_seen maps with the prototype tracker,
+ // but start a fresh walk from the given starting point.
+ inline dwarf_ref_tracker (const dwarf_ref_tracker &proto, reference_match &,
+ const left_context_type &lhs,
+ const right_context_type &rhs)
+ : _m_left (proto._m_left, lhs),
+ _m_right (proto._m_right, rhs),
+ _m_equiv (proto._m_equiv), _m_delete_equiv (false)
+ {
+ // We are starting a recursive consideration of LHS vs RHS.
+ }
+ };
+};
+
+#endif // <elfutils/dwarf_tracker>
diff --git a/libdw/c++/edit-values.cc b/libdw/c++/edit-values.cc
new file mode 100644
index 00000000..976d315e
--- /dev/null
+++ b/libdw/c++/edit-values.cc
@@ -0,0 +1,89 @@
+/* elfutils::dwarf_edit attribute value interfaces.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include <config.h>
+#include "dwarf_edit"
+#include "data-values.hh"
+
+using namespace elfutils;
+
+// Explicit instantiations.
+template class dwarf_data::line_entry<dwarf_edit::source_file>;
+template class dwarf_data::line_table<dwarf_edit::line_entry>;
+template class dwarf_data::line_info_table<dwarf_edit::directory_table,
+ dwarf_edit::line_table>;
+template class dwarf_data::attr_value<dwarf_edit>;
+template class dwarf_data::value<dwarf_edit>;
+
+template<>
+std::string
+to_string<dwarf_edit::attribute> (const dwarf_edit::attribute &attr)
+{
+ return attribute_string (attr);
+}
+
+namespace elfutils
+{
+ template<>
+ std::string to_string (const dwarf_edit::debug_info_entry &die)
+ {
+ return die_string (die);
+ }
+};
+
+std::string
+dwarf_data::source_file::to_string () const
+{
+ if (likely (_m_mtime == 0) && likely (_m_size == 0))
+ return "\"" + _m_name + "\"";
+
+ std::ostringstream os;
+ os << "{\"" << _m_name << "," << _m_mtime << "," << _m_size << "}";
+ return os.str ();
+}
diff --git a/libdw/c++/exception.cc b/libdw/c++/exception.cc
new file mode 100644
index 00000000..8d3bb86a
--- /dev/null
+++ b/libdw/c++/exception.cc
@@ -0,0 +1,76 @@
+/* -*- C++ -*- exceptions for libdw.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include <config.h>
+#include <cassert>
+#include "dwarf"
+
+extern "C"
+{
+#include "libdwP.h"
+}
+
+using namespace elfutils;
+using namespace std;
+
+
+/* Throw
+ */
+void
+dwarf::throw_libdw (::Dwarf *)
+{
+ throw std::runtime_error (::dwarf_errmsg (-1));
+}
+
+// This is just for things that can't find the Dwarf pointer directly.
+void
+dwarf::throw_libdw (::Dwarf_CU *cu)
+{
+ throw_libdw (cu->dbg);
+}
diff --git a/libdw/c++/known.cc b/libdw/c++/known.cc
new file mode 100644
index 00000000..a7788416
--- /dev/null
+++ b/libdw/c++/known.cc
@@ -0,0 +1,251 @@
+/* Known named integer values in DWARF.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include <config.h>
+#include "dwarf"
+#include "dwarf_edit"
+#include "known-dwarf.h"
+
+using namespace elfutils;
+using namespace std;
+
+
+const char *
+dwarf::known_tag (int tag)
+{
+ switch (tag)
+ {
+#define ONE_KNOWN_DW_TAG(name, id) case id: return #id;
+#define ONE_KNOWN_DW_TAG_DESC(name, id, desc) ONE_KNOWN_DW_TAG (name, id)
+ ALL_KNOWN_DW_TAG
+ }
+ return NULL;
+}
+
+const char *
+dwarf::known_attribute (int name)
+{
+ switch (name)
+ {
+#define ONE_KNOWN_DW_AT(name, id) case id: return #id;
+#define ONE_KNOWN_DW_AT_DESC(name, id, desc) ONE_KNOWN_DW_AT (name, id)
+ ALL_KNOWN_DW_AT
+ }
+ return NULL;
+}
+
+namespace elfutils
+{
+ template<int key>
+ size_t
+ dwarf::known_enum<key>::prefix_length ()
+ {
+ return 0;
+ }
+
+ template<int key>
+ const char *
+ dwarf::known_enum<key>::identifier (int value)
+ {
+ return NULL;
+ }
+
+#define ALL_KNOWN_ENUM \
+ KNOWN_ENUM (accessibility, ACCESS) \
+ KNOWN_ENUM (encoding, ATE) \
+ KNOWN_ENUM (calling_convention, CC) \
+ KNOWN_ENUM (decimal_sign, DS) \
+ KNOWN_ENUM (endianity, END) \
+ KNOWN_ENUM (identifier_case, ID) \
+ KNOWN_ENUM (inline, INL) \
+ KNOWN_ENUM (language, LANG) \
+ KNOWN_ENUM (ordering, ORD) \
+ KNOWN_ENUM (virtuality, VIRTUALITY) \
+ KNOWN_ENUM (visibility, VIS)
+
+#define ONE_KNOWN_DW_ACCESS(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_ATE(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_CC(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_DS(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_END(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_ID(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_INL(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_LANG(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_LANG_DESC(name, id, desc) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_ORD(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_INL(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_VIRTUALITY(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_VIS(name, id) KNOWN_ENUM_CASE (id)
+
+ // Stupid C++ doesn't do [x] = y initializers.
+#define KNOWN_ENUM(attr, enum) \
+ template<> \
+ size_t \
+ dwarf::known_enum<DW_AT_##attr>::prefix_length () \
+ { \
+ return sizeof ("DW_" #enum "_") - 1; \
+ } \
+ template<> \
+ const char * \
+ dwarf::known_enum<DW_AT_##attr>::identifier (int value) \
+ { \
+ switch (value) \
+ { \
+ ALL_KNOWN_DW_##enum \
+ } \
+ return NULL; \
+ }
+#define KNOWN_ENUM_CASE(id) case id: return #id;
+
+ ALL_KNOWN_ENUM
+
+ // Not really enum cases, but pretend they are.
+#define ONE_KNOWN_DW_FORM(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_OP(name, id) KNOWN_ENUM_CASE (id)
+#define ONE_KNOWN_DW_OP_DESC(name, id, desc) KNOWN_ENUM_CASE (id)
+ KNOWN_ENUM (producer, FORM)
+ KNOWN_ENUM (location, OP)
+
+#undef KNOWN_ENUM
+#undef KNOWN_ENUM_CASE
+};
+
+static const char *
+known_identifier (unsigned int which, unsigned int value)
+{
+ switch (which)
+ {
+# define KNOWN_ENUM(attr, enum) \
+ case DW_AT_##attr: \
+ return dwarf::known_enum<DW_AT_##attr>::identifier (value);
+
+ ALL_KNOWN_ENUM
+
+# undef KNOWN_ENUM
+ }
+
+ return NULL;
+}
+
+static const char *
+known_name (unsigned int which, unsigned int value)
+{
+ switch (which)
+ {
+# define KNOWN_ENUM(attr, enum) \
+ case DW_AT_##attr: \
+ return dwarf::known_enum<DW_AT_##attr>::name (value);
+
+ ALL_KNOWN_ENUM
+
+# undef KNOWN_ENUM
+ }
+
+ return NULL;
+}
+
+template<typename constant>
+static inline const char *
+enum_identifier (const constant &value)
+{
+ return known_identifier (value.which (), value);
+}
+
+template<typename constant>
+static inline const char *
+enum_name (const constant &value)
+{
+ return known_name (value.which (), value);
+}
+
+const char *
+dwarf::dwarf_enum::identifier () const
+{
+ return enum_identifier (*this);
+}
+
+const char *
+dwarf::dwarf_enum::name () const
+{
+ return enum_name (*this);
+}
+
+const char *
+dwarf_data::dwarf_enum::identifier () const
+{
+ return enum_identifier (*this);
+}
+
+const char *
+dwarf_data::dwarf_enum::name () const
+{
+ return enum_name (*this);
+}
+
+template<class value_type>
+static inline std::string
+enum_string (const value_type &value)
+{
+ const char *known = value.name ();
+ return known == NULL ? subr::hex_string (value) : std::string (known);
+}
+
+template<>
+string
+to_string<dwarf::dwarf_enum> (const dwarf::dwarf_enum &value)
+{
+ return enum_string (value);
+}
+
+template<>
+string
+to_string<dwarf_data::dwarf_enum> (const dwarf_data::dwarf_enum &value)
+{
+ return enum_string (value);
+}
diff --git a/libdw/c++/line_info.cc b/libdw/c++/line_info.cc
new file mode 100644
index 00000000..b913da62
--- /dev/null
+++ b/libdw/c++/line_info.cc
@@ -0,0 +1,347 @@
+#include <config.h>
+#include <cassert>
+#include "dwarf"
+
+extern "C"
+{
+#include "libdwP.h"
+}
+
+using namespace elfutils;
+using namespace std;
+
+
+// dwarf::line_info_table
+
+const dwarf::line_info_table
+dwarf::attr_value::line_info () const
+{
+ assert (dwarf_whatattr (thisattr ()) == DW_AT_stmt_list);
+
+ CUDIE (cudie, _m_attr.cu);
+ Dwarf_Lines *lines;
+ size_t n;
+ xif (thisattr (), dwarf_getsrclines (&cudie, &lines, &n) < 0);
+
+ return line_info_table (_m_attr.cu->files);
+}
+
+const dwarf::line_table
+dwarf::line_info_table::lines () const
+{
+ return line_table (_m_files->cu->lines);
+}
+
+// dwarf::source_file
+
+const class dwarf::source_file
+dwarf::attr_value::source_file () const
+{
+ switch (what_space ())
+ {
+ case VS_string:
+ case VS_source_file:
+ break;
+ default:
+ throw std::runtime_error ("XXX not a file name");
+ }
+ return source_file::source_file (_m_attr);
+}
+
+static bool
+stringform (Dwarf_Attribute *attr)
+{
+ if (attr->valp != NULL)
+ switch (dwarf_whatform (attr))
+ {
+ case DW_FORM_string:
+ case DW_FORM_strp:
+ return true;
+ }
+ return false;
+}
+
+/* Mock up a dummy attribute with a special kludge that get_files groks.
+ We use these for source_file objects consed directly from an index
+ rather than from a real attribute. */
+static inline const Dwarf_Attribute
+dummy_source_file (Dwarf_CU *cu, unsigned int idx)
+{
+ const Dwarf_Attribute dummy = { idx, DW_FORM_indirect, NULL, cu };
+ return dummy;
+}
+
+static bool
+get_files (const Dwarf_Attribute *attr, Dwarf_Files **files, Dwarf_Word *idx)
+{
+ if (attr->valp == NULL)
+ {
+ // Dummy hack created by dummy_source_file, above.
+ assert (attr->form == DW_FORM_indirect);
+ *files = attr->cu->files;
+ *idx = attr->code;
+ return false;
+ }
+
+ CUDIE (cudie, attr->cu);
+ return (dwarf_formudata (const_cast<Dwarf_Attribute *> (attr), idx) < 0
+ || dwarf_getsrcfiles (&cudie, files, NULL) < 0);
+}
+
+Dwarf_Word
+dwarf::source_file::mtime () const
+{
+ if (stringform (thisattr ()))
+ return 0;
+
+ Dwarf_Files *files;
+ Dwarf_Word idx;
+ xif (thisattr (), get_files (thisattr (), &files, &idx));
+
+ Dwarf_Word result;
+ xif (thisattr (), dwarf_filesrc (files, idx, &result, NULL) == NULL);
+ return result;
+}
+
+Dwarf_Word
+dwarf::source_file::size () const
+{
+ if (stringform (thisattr ()))
+ return 0;
+
+ Dwarf_Files *files;
+ Dwarf_Word idx;
+ xif (thisattr (), get_files (thisattr (), &files, &idx));
+
+ Dwarf_Word result;
+ xif (thisattr (), dwarf_filesrc (files, idx, NULL, &result) == NULL);
+ return result;
+}
+
+const char *
+dwarf::source_file::name () const
+{
+ const char *result;
+ if (stringform (thisattr ()))
+ result = dwarf_formstring (thisattr ());
+ else
+ {
+ Dwarf_Files *files;
+ Dwarf_Word idx;
+ xif (thisattr (), get_files (thisattr (), &files, &idx));
+ result = dwarf_filesrc (files, idx, NULL, NULL);
+ }
+ xif (thisattr (), result == NULL);
+ return result;
+}
+
+static inline string
+plain_string (const char *filename)
+{
+ string result ("\"");
+ result += filename;
+ result += "\"";
+ return result;
+}
+
+string
+dwarf::source_file::to_string () const
+{
+ if (stringform (thisattr ()))
+ {
+ const char *result = dwarf_formstring (thisattr ());
+ xif (thisattr (), result == NULL);
+ return plain_string (result);
+ }
+
+ Dwarf_Files *files;
+ Dwarf_Word idx;
+ xif (thisattr (), get_files (thisattr (), &files, &idx));
+
+ Dwarf_Word file_mtime;
+ Dwarf_Word file_size;
+ const char *result = dwarf_filesrc (files, idx, &file_mtime, &file_size);
+ xif (thisattr (), result == NULL);
+
+ if (likely (file_mtime == 0) && likely (file_size == 0))
+ return plain_string (result);
+
+ std::ostringstream os;
+ os << "{\"" << result << "," << file_mtime << "," << file_size << "}";
+ return os.str ();
+}
+
+// dwarf::directory_table
+
+size_t
+dwarf::directory_table::size () const
+{
+ return _m_files->ndirs;
+}
+
+static inline dwarf::directory_table::const_iterator
+directory_table_array (Dwarf_Files *files)
+{
+ // See dwarf_getsrcdirs.
+ return reinterpret_cast<const char *const *> (&files->info[files->nfiles]);
+}
+
+dwarf::directory_table::const_iterator
+dwarf::directory_table::begin () const
+{
+ return directory_table_array (_m_files);
+}
+
+dwarf::directory_table::const_iterator
+dwarf::directory_table::end () const
+{
+ return directory_table_array (_m_files) + _m_files->ndirs;
+}
+
+// dwarf::file_table
+
+size_t
+dwarf::file_table::size () const
+{
+ return _m_files->nfiles;
+}
+
+const dwarf::source_file
+dwarf::file_table::at (size_t idx) const
+{
+ if (unlikely (idx >= _m_files->nfiles))
+ throw std::out_of_range ("XXX fileidx");
+
+ return dwarf::source_file (dummy_source_file (_m_files->cu, idx));
+}
+
+dwarf::file_table::const_iterator
+dwarf::file_table::find (const source_file &src) const
+{
+ if (src._m_attr.cu->files == _m_files)
+ {
+ // Same table, just cons an iterator using its index.
+ Dwarf_Files *files;
+ Dwarf_Word idx;
+ xif (files->cu, get_files (&src._m_attr, &files, &idx));
+ return const_iterator (*this, idx);
+ }
+
+ // Not from this table, just match on file name.
+ return find (src.name ());
+}
+
+// dwarf::line_table
+
+size_t
+dwarf::line_table::size () const
+{
+ return _m_lines->nlines;
+}
+
+const dwarf::line_entry
+dwarf::line_table::at (size_t idx) const
+{
+ if (unlikely (idx >= _m_lines->nlines))
+ throw std::out_of_range ("XXX line table index");
+
+ return line_entry (reinterpret_cast<Dwarf_Line *> (&_m_lines->info[idx]));
+}
+
+dwarf::line_table::const_iterator
+dwarf::line_table::find (Dwarf_Addr address) const
+{
+ size_t idx = _m_lines->nlines; // end ()
+ if (likely (idx > 0))
+ {
+ CUDIE (cudie, _m_lines->info[0].files->cu);
+ Dwarf_Line *line = dwarf_getsrc_die (&cudie, address);
+ if (line != NULL)
+ idx = line - &_m_lines->info[0];
+ }
+ return const_iterator (*this, idx);
+}
+
+// dwarf::line_entry
+
+const dwarf::source_file
+dwarf::line_entry::file () const
+{
+ return dwarf::source_file (dummy_source_file (_m_line->files->cu,
+ _m_line->file));
+}
+
+#define LINEFIELD(type, method, field) \
+ type \
+ dwarf::line_entry::method () const \
+ { \
+ return _m_line->field; \
+ }
+
+LINEFIELD (Dwarf_Addr, address, addr) // XXX dwfl?
+LINEFIELD (unsigned int, line, line)
+LINEFIELD (unsigned int, column, column)
+LINEFIELD (bool, statement, is_stmt)
+LINEFIELD (bool, basic_block, basic_block)
+LINEFIELD (bool, end_sequence, end_sequence)
+LINEFIELD (bool, prologue_end, prologue_end)
+LINEFIELD (bool, epilogue_begin, epilogue_begin)
+
+#undef LINEFIELD
+
+bool
+dwarf::line_entry::operator== (const dwarf::line_entry &other) const
+{
+ Dwarf_Line *const a = _m_line;
+ Dwarf_Line *const b = other._m_line;
+
+ if (a == b)
+ return true;
+
+ if (a->addr != b->addr
+ || a->line != b->line
+ || a->column != b->column
+ || a->is_stmt != b->is_stmt
+ || a->basic_block != b->basic_block
+ || a->end_sequence != b->end_sequence
+ || a->prologue_end != b->prologue_end
+ || a->epilogue_begin != b->epilogue_begin)
+ return false;
+
+ // Everything else matches, now have to try the file.
+ if (a->files == b->files)
+ // Same table, just compare indices.
+ return a->file == b->file;
+
+ Dwarf_Word atime;
+ Dwarf_Word asize;
+ const char *aname = dwarf_linesrc (a, &atime, &asize);
+ xif (a->files->cu, aname == NULL);
+ Dwarf_Word btime;
+ Dwarf_Word bsize;
+ const char *bname = dwarf_linesrc (b, &btime, &bsize);
+ xif (b->files->cu, bname == NULL);
+
+ /* The mtime and size only count when encoded as nonzero.
+ If either side is zero, we don't consider the field. */
+
+ if (atime != btime && atime != 0 && btime != 0)
+ return false;
+
+ if (asize != bsize && asize != 0 && bsize != 0)
+ return false;
+
+ return !strcmp (aname, bname);
+}
+
+// dwarf::compile_unit convenience functions.
+
+const dwarf::line_info_table
+dwarf::compile_unit::line_info () const
+{
+ Dwarf_Lines *l;
+ size_t n;
+ xif (dwarf_getsrclines (thisdie (), &l, &n) < 0);
+
+ return line_info_table (thisdie ()->cu->files);
+}
diff --git a/libdw/c++/output-shape.cc b/libdw/c++/output-shape.cc
new file mode 100644
index 00000000..8d25b389
--- /dev/null
+++ b/libdw/c++/output-shape.cc
@@ -0,0 +1,159 @@
+/* elfutils::dwarf_output abbrev generation.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include <config.h>
+#include "dwarf_output"
+
+using namespace elfutils;
+
+static inline int
+attr_form (int tag, const dwarf_output::attribute &attr)
+{
+ switch (attr.second.what_space ())
+ {
+ case dwarf::VS_address:
+ return DW_FORM_addr;
+
+ case dwarf::VS_flag:
+ return DW_FORM_flag;
+
+ case dwarf::VS_reference:
+ return DW_FORM_ref_addr;
+
+ case dwarf::VS_string:
+ case dwarf::VS_identifier:
+ return DW_FORM_string;
+
+ case dwarf::VS_constant:
+ if (! attr.second.constant_is_integer ())
+ return DW_FORM_block;
+ /* Fall through. */
+
+ case dwarf::VS_dwarf_constant:
+ case dwarf::VS_source_line:
+ case dwarf::VS_source_column:
+ return DW_FORM_udata;
+
+ case dwarf::VS_location:
+ if (!attr.second.location ().is_list ())
+ return DW_FORM_block;
+ /* Fall through. */
+
+ case dwarf::VS_lineptr:
+ case dwarf::VS_macptr:
+ case dwarf::VS_rangelistptr:
+ /* For class *ptr (including loclistptr), the one of data[48] that
+ matches offset_size is the only form encoding to use. Other data*
+ forms can mean the attribute is class constant instead. */
+ return DW_FORM_data4;
+
+ case dwarf::VS_source_file:
+ switch (attr.first)
+ {
+ case DW_AT_decl_file:
+ case DW_AT_call_file:
+ return DW_FORM_udata;
+
+ case DW_AT_comp_dir:
+ return DW_FORM_string;
+
+ case DW_AT_name:
+ switch (tag)
+ {
+ case DW_TAG_compile_unit:
+ case DW_TAG_partial_unit:
+ return DW_FORM_string;
+ }
+ break;
+ }
+ throw std::runtime_error ("source_file value unexpected in "
+ + to_string (attr));
+
+ case dwarf::VS_discr_list:
+ return DW_FORM_block;
+ }
+
+ throw std::logic_error ("strange value_space");
+}
+
+inline void
+dwarf_output_collector::shape_type::hashnadd (int name, int form)
+{
+ subr::hash_combine (_m_hash, name);
+ subr::hash_combine (_m_hash, form);
+ _m_attrs.push_back (std::make_pair (name, form));
+}
+
+inline
+dwarf_output_collector::shape_type::shape_type (const die_type &die,
+ bool last_sibling)
+ : _m_has_children (die.has_children ()), _m_hash (8675309 << _m_has_children)
+{
+ if (!last_sibling)
+ hashnadd (DW_AT_sibling, DW_FORM_ref_udata);
+
+ for (die_type::attributes_type::const_iterator it = die.attributes ().begin ();
+ it != die.attributes ().end ();
+ ++it)
+ hashnadd (it->first, attr_form (die.tag (), *it));
+}
+#if 0
+void
+dwarf_output_collector::add_shape (die_type &die, bool last_sibling)
+{
+ assert (die._m_shape == NULL);
+
+ shape_map::value_type &x
+ = *_m_shapes.insert (std::make_pair (shape_type (die, last_sibling),
+ shape_info ())).first;
+ // x.second.nusers++, etc.
+
+ die._m_shape = &x;
+}
+#endif
diff --git a/libdw/c++/output-values.cc b/libdw/c++/output-values.cc
new file mode 100644
index 00000000..044710c9
--- /dev/null
+++ b/libdw/c++/output-values.cc
@@ -0,0 +1,85 @@
+/* elfutils::dwarf_output attribute value interfaces.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include <config.h>
+#include "dwarf_output"
+#include "data-values.hh"
+
+#include <typeinfo>
+
+using namespace elfutils;
+
+// Explicit instantiations.
+template class dwarf_data::value<dwarf_output, false>;
+template class dwarf_data::attr_value<dwarf_output, dwarf_output::value>;
+template class dwarf_data::attributes_type<dwarf_output, dwarf_output::value>;
+template class dwarf_data::compile_unit<dwarf_output>;
+template class dwarf_data::compile_units<dwarf_output>;
+
+template class dwarf_output::copier<dwarf>;
+template class dwarf_output::copier<dwarf_edit>;
+
+template<>
+std::string
+to_string<dwarf_output::attribute> (const dwarf_output::attribute &attr)
+{
+ return attribute_string (attr);
+}
+
+namespace elfutils
+{
+ template<>
+ std::string to_string (const dwarf_output::debug_info_entry &die)
+ {
+ return die_string (die);
+ }
+};
+
+const dwarf_output::value::value_flag dwarf_output_collector::flag_true (1);
+const dwarf_output::value::value_flag dwarf_output_collector::flag_false (0);
diff --git a/libdw/c++/subr.hh b/libdw/c++/subr.hh
new file mode 100644
index 00000000..bd7464d3
--- /dev/null
+++ b/libdw/c++/subr.hh
@@ -0,0 +1,1476 @@
+/* Private helper classes for elfutils -*- C++ -*- interfaces.
+
+ */
+
+#ifndef _ELFUTILS_SUBR_HH
+#define _ELFUTILS_SUBR_HH 1
+
+#include <iterator>
+#include <functional>
+#include <cstring>
+#include <cassert>
+#include <iostream>
+#include <sstream>
+#include <tr1/unordered_map>
+#include <tr1/unordered_set>
+#include <vector>
+#include <deque>
+#include <stack>
+#include <set>
+#include <algorithm>
+#include <utility>
+#include <stdexcept>
+
+namespace elfutils
+{
+ namespace subr
+ {
+ template<typename container_type, typename op_type>
+ inline void for_each (container_type &container, op_type op)
+ {
+ std::for_each (container.begin (), container.end (), op);
+ }
+
+ template<typename T>
+ struct hash : public T::hasher {};
+
+ template<typename T>
+ static inline size_t hash_this (const T &v)
+ {
+ return hash<T> () (v);
+ }
+
+ template <typename T>
+ inline void hash_combine (size_t &seed, const T &v)
+ {
+ seed ^= hash_this (v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ }
+
+ template <typename T1, typename T2>
+ inline void hash_combine (size_t &seed, const std::pair<T1,T2> &v)
+ {
+ hash_combine (seed, v.first);
+ hash_combine (seed, v.second);
+ }
+
+ inline void
+ string_hash_combine (size_t &hash, const std::string::value_type &c)
+ {
+ hash = hash * 33 + c;
+ }
+
+ template<typename T, typename B>
+ struct base_hasher : public std::unary_function<T, size_t>
+ {
+ size_t operator () (const T &v) const
+ {
+ return hash_this<B> (v);
+ }
+ };
+
+ template<typename T1, typename T2>
+ struct cast_hasher : public std::unary_function<T1, size_t>
+ {
+ inline size_t operator () (const T1 &x) const
+ {
+ return hash_this (static_cast<T2> (x));
+ }
+ };
+
+ template<typename T, T magic>
+ struct integer_hash : public std::unary_function<T, size_t>
+ {
+ inline size_t operator () (const T &x) const
+ {
+ return x * magic;
+ }
+ };
+ template<>
+ struct hash<unsigned int>
+ : public integer_hash<unsigned int, 0x9e370001U>
+ {};
+ template<>
+ struct hash<uint64_t>
+ : public integer_hash<uint64_t, 0x9e37fffffffc0001ULL>
+ {};
+
+ template<>
+ struct hash<int> : public cast_hasher<int, unsigned int> {};
+ template<>
+ struct hash<uint8_t> : public cast_hasher<uint8_t, unsigned int> {};
+ template<>
+ struct hash<bool> : public cast_hasher<bool, unsigned int> {};
+
+ template<typename T1, typename T2>
+ struct hash<std::pair<T1, T2> >
+ : public std::unary_function<std::pair<T1, T2>, size_t>
+ {
+ inline size_t operator () (const std::pair<T1, T2> &x) const
+ {
+ size_t h = 0;
+ hash_combine (h, x);
+ return h;
+ }
+ };
+
+ template<typename T,
+ void (*combiner) (size_t &, const typename T::value_type &)
+ = hash_combine<typename T::value_type>,
+ size_t initial_hash = 0>
+ class container_hasher
+ : public std::unary_function<T, size_t>
+ {
+ private:
+ struct hasher
+ {
+ size_t &_m_hash;
+ inline explicit hasher (size_t &hash) : _m_hash (hash) {}
+ inline void operator () (const typename T::value_type &x)
+ {
+ return (*combiner) (_m_hash, x);
+ }
+ };
+ public:
+ inline size_t operator () (const T &x) const
+ {
+ size_t hash = initial_hash;
+ for_each (x, hasher (hash));
+ return hash;
+ }
+ };
+
+ template<typename T>
+ struct hash<std::vector<T> >
+ : public container_hasher<std::vector<T> >
+ {
+ };
+
+ template<>
+ struct hash<std::string>
+ : public container_hasher<std::string, string_hash_combine, 5381>
+ {
+ };
+
+ template<class T>
+ struct hashed_hasher
+ : public std::unary_function<T, size_t>
+ {
+ size_t operator () (const T &v) const
+ {
+ return v._m_hash;
+ }
+ };
+
+ template<typename string>
+ struct name_equal : public std::binary_function<const char *, string, bool>
+ {
+ template<typename mystring>
+ inline bool operator () (const mystring &me, const string &you) const
+ {
+ return you == me;
+ }
+ };
+
+ // Explicit specialization.
+ template<>
+ struct name_equal<const char *>
+ : public std::binary_function<const char *, const char *, bool>
+ {
+ bool operator () (const char *me, const char *you) const
+ {
+ return !strcmp (me, you);
+ }
+ template<typename mystring>
+ inline bool operator () (const mystring &me, const char *you) const
+ {
+ return me == you;
+ }
+ };
+
+ static inline std::string hex_string (int code)
+ {
+ std::ostringstream os;
+ os << std::hex << std::showbase << code;
+ return os.str ();
+ }
+
+ template<typename prefix_type, const char *lookup_known (int)>
+ struct known
+ {
+ // The names in the table are the identifiers, with prefix.
+ static inline std::string identifier (int code)
+ {
+ const char *known = lookup_known (code);
+ return known == NULL ? hex_string (code) : std::string (known);
+ }
+
+ // For the pretty name, skip over the prefix.
+ static inline std::string name (int code)
+ {
+ const char *known = lookup_known (code);
+ return (known == NULL ? hex_string (code)
+ : std::string (&known[sizeof (prefix_type) - 1]));
+ }
+ };
+
+ // This is like std::equal_to but for comparing two different types.
+ template<typename t1, typename t2>
+ struct equal_to : public std::binary_function<t1, t2, bool>
+ {
+ inline bool operator () (const t1 &a, const t2 &b) const
+ {
+ return a == b;
+ }
+ };
+
+ /* On a single type, our equal_to is like std::equal_to, but
+ we short-circuit for the case of matching pointers. */
+ template<typename T>
+ struct equal_to<T, T> : public std::binary_function<T, T, bool>
+ {
+ inline bool operator () (const T &a, const T &b) const
+ {
+ return &a == &b || a == b;
+ }
+ };
+
+ template<typename t1, typename t2, typename pred_type>
+ class deref
+ : public std::binary_function<typename t1::const_iterator,
+ typename t2::const_iterator,
+ bool>
+ {
+ private:
+ pred_type _m_pred;
+
+ public:
+ inline deref ()
+ : _m_pred ()
+ {}
+
+ inline deref (const pred_type &pred)
+ : _m_pred (pred)
+ {}
+
+ inline bool operator () (const typename t1::const_iterator &a,
+ const typename t2::const_iterator &b) const
+ {
+ return _m_pred (*a, *b);
+ }
+ };
+
+ template<typename t1, typename t2>
+ struct deref_equal_to
+ : public deref<t1, t2,
+ equal_to<typename t1::value_type, typename t2::value_type>
+ >
+ {};
+
+ template<typename iter1, typename iter2, typename pred_type>
+ inline bool container_equal (iter1 &first1, const iter1 &last1,
+ iter2 &first2, const iter2 &last2,
+ pred_type pred)
+ {
+ while (first1 != last1)
+ {
+ if (first2 == last2 || !pred (first1, first2))
+ return false;
+ ++first1;
+ ++first2;
+ }
+ return first2 == last2;
+ }
+
+ template<typename t1, typename t2, typename pred_type>
+ inline bool container_equal (const t1 &a, const t2 &b, pred_type pred)
+ {
+ typename t1::const_iterator first1 = a.begin ();
+ typename t1::const_iterator last1 = a.end ();
+ typename t2::const_iterator first2 = b.begin ();
+ typename t2::const_iterator last2 = b.end ();
+ return container_equal (first1, last1, first2, last2, pred);
+ }
+
+ template<typename t1, typename t2>
+ inline bool container_equal (const t1 &a, const t2 &b)
+ {
+ return container_equal (a, b, deref_equal_to<t1, t2> ());
+ }
+
+ template<typename iter>
+ inline typename iter::difference_type length (iter i, const iter &end)
+ {
+ typename iter::difference_type n = 0;
+ while (i != end)
+ ++i, ++n;
+ return n;
+ }
+
+ template<typename array, typename element = typename array::value_type>
+ class indexed_iterator
+ : public std::iterator<std::random_access_iterator_tag,
+ typename array::value_type,
+ typename array::difference_type>
+ {
+ private:
+ typedef typename array::size_type index_type;
+
+ array _m_contents;
+ index_type _m_idx;
+
+ public:
+ indexed_iterator (array contents, index_type idx)
+ : _m_contents (contents), _m_idx (idx) {}
+ indexed_iterator (const indexed_iterator &i)
+ : _m_contents (i._m_contents), _m_idx (i._m_idx) {}
+
+ inline element operator* () const
+ {
+ return _m_contents[_m_idx];
+ }
+ template<typename elt>
+ inline elt operator* () const
+ {
+ return _m_contents[_m_idx];
+ }
+ template<typename elt>
+ inline elt *operator-> () const
+ {
+ return &_m_contents[_m_idx];
+ }
+ template<typename elt>
+ inline elt operator[] (const index_type &n) const
+ {
+ return _m_contents[_m_idx + n];
+ }
+
+ inline indexed_iterator operator+ (const indexed_iterator &i) const
+ {
+ return indexed_iterator (_m_contents, _m_idx + i._m_idx);
+ }
+ inline indexed_iterator operator+ (const typename array::difference_type
+ &i) const
+ {
+ return indexed_iterator (_m_contents, _m_idx + i);
+ }
+ inline typename array::difference_type
+ operator- (const indexed_iterator &i) const
+ {
+ return _m_idx - i._m_idx;
+ }
+
+ inline bool operator== (const indexed_iterator &i) const
+ {
+ return _m_idx == i._m_idx;
+ }
+ inline bool operator!= (const indexed_iterator &i) const
+ {
+ return _m_idx != i._m_idx;
+ }
+ inline bool operator< (const indexed_iterator &i) const
+ {
+ return _m_idx < i._m_idx;
+ }
+ inline bool operator> (const indexed_iterator &i) const
+ {
+ return _m_idx > i._m_idx;
+ }
+ inline bool operator<= (const indexed_iterator &i) const
+ {
+ return _m_idx <= i._m_idx;
+ }
+ inline bool operator>= (const indexed_iterator &i) const
+ {
+ return _m_idx >= i._m_idx;
+ }
+
+ inline indexed_iterator &operator= (const indexed_iterator &i)
+ {
+ _m_idx = i._m_idx;
+ return *this;
+ }
+ inline indexed_iterator &operator+= (const index_type &n)
+ {
+ _m_idx += n;
+ return *this;
+ }
+ inline indexed_iterator &operator-= (const index_type &n)
+ {
+ _m_idx -= n;
+ return *this;
+ }
+
+ inline indexed_iterator &operator++ () // prefix
+ {
+ ++_m_idx;
+ return *this;
+ }
+ inline indexed_iterator operator++ (int) // postfix
+ {
+ return indexed_iterator (_m_contents, _m_idx++);
+ }
+ inline indexed_iterator &operator-- () // prefix
+ {
+ --_m_idx;
+ return *this;
+ }
+ inline indexed_iterator operator-- (int) // postfix
+ {
+ return indexed_iterator (_m_contents, _m_idx--);
+ }
+ };
+
+ // Pair of some value and its precomputed hash.
+ template<typename T>
+ class hashed_value
+ : public std::pair<size_t, const T>
+ {
+ private:
+ typedef std::pair<size_t, const T> _base;
+
+ public:
+ typedef T value_type;
+
+ struct hasher
+ : public std::unary_function<hashed_value, size_t>
+ {
+ inline size_t operator () (const hashed_value &v) const
+ {
+ return v.first;
+ }
+ };
+
+ hashed_value (const value_type &v)
+ : _base (hash_this (v), v) {}
+ hashed_value (const hashed_value &v)
+ : _base (v.first, v.second) {}
+
+ bool operator== (const hashed_value &other) const
+ {
+ return other.first == this->first && other.second == this->second;
+ }
+ };
+
+ // Set of hashed_value's.
+ template<typename value_type>
+ class value_set
+ : public std::tr1::unordered_set<hashed_value<value_type>,
+ struct hashed_value<value_type>::hasher>
+ {
+ public:
+ typedef hashed_value<value_type> hashed_value_type;
+
+ private:
+ typedef std::tr1::unordered_set<hashed_value_type,
+ struct hashed_value_type::hasher> _base;
+
+ public:
+ const value_type *add (const value_type &v)
+ {
+ std::pair<class _base::iterator, bool> p
+ = _base::insert (hashed_value_type (v));
+ if (p.second)
+ {
+ // XXX hook for collection: abbrev building, etc.
+ }
+ return &p.first->second;
+ };
+
+ template<typename input>
+ const value_type *add (const input &v)
+ {
+ return add (value_type (v));
+ }
+
+ template<typename input, typename arg_type>
+ const value_type *add (const input &v, arg_type &arg)
+ {
+ return add (value_type (v, arg));
+ }
+ };
+
+ // A container of hashed_value's that itself acts like a hashed_value.
+ // The parameter class should be a std::container<hashed_value<something>>.
+ template<typename container>
+ class hashed_container : public container
+ {
+ private:
+ typedef container _base;
+ typedef typename container::value_type elt_type;
+
+ public:
+ typedef typename elt_type::value_type value_type;
+
+ protected:
+ size_t _m_hash;
+
+ inline void set_hash ()
+ {
+ _m_hash = container_hasher<container> () (*this);
+ }
+
+ public:
+ friend class hashed_hasher<hashed_container>;
+ typedef hashed_hasher<hashed_container> hasher;
+
+ template<typename iterator>
+ hashed_container (iterator first, iterator last)
+ : _base (first, last)
+ {
+ set_hash ();
+ }
+
+ template<typename other_container>
+ hashed_container (const other_container &other)
+ : _base (other.begin (), other.end ())
+ {
+ set_hash ();
+ }
+
+ bool operator== (const hashed_container &other) const
+ {
+ return (other._m_hash == _m_hash &&
+ other.size () == _base::size ()
+ && std::equal (_base::begin (), _base::end (), other.begin (),
+ equal_to<elt_type, elt_type> ()));
+ }
+ };
+
+ // A vector of hashed_value's that itself acts like a hashed_value.
+ template<typename value_type>
+ struct hashed_vector
+ : public hashed_container<std::vector<hashed_value<value_type> > >
+ {};
+
+ // An unordered_map of hashed_value's that itself acts like a hashed_value.
+ template<typename key_type, typename value_type>
+ class hashed_unordered_map
+ : public hashed_container<std::tr1::unordered_map<
+ key_type,
+ hashed_value<value_type>,
+ class hashed_value<value_type>::hasher>
+ >
+ {};
+#if 0
+ template<typename key_type, typename value_type>
+ class hashed_unordered_map
+ : public std::tr1::unordered_map<key_type,
+ hashed_value<value_type>,
+ class hashed_value<value_type>::hasher>
+ {
+ private:
+ typedef std::tr1::unordered_map<key_type,
+ hashed_value<value_type>,
+ class hashed_value<value_type>::hasher>
+ _base;
+
+ size_t _m_hash;
+
+ inline void set_hash ()
+ {
+ struct hashit
+ {
+ size_t &_m_hash;
+ hashit (size_t &h) : _m_hash (h) {}
+
+ inline void operator () (const typename _base::value_type &p)
+ {
+ hash_combine (_m_hash, hash_this (p.first));
+ hash_combine (_m_hash, p.second.first);
+ }
+ };
+ for_each (static_cast<_base &> (*this), hashit (_m_hash));
+ }
+
+ public:
+ friend class hashed_hasher<hashed_unordered_map>;
+ typedef hashed_hasher<hashed_unordered_map> hasher;
+
+ template<typename iterator>
+ hashed_unordered_map (iterator first, iterator last)
+ : _base (first, last), _m_hash (0)
+ {
+ set_hash ();
+ }
+
+ template<typename container>
+ hashed_unordered_map (const container &other)
+ : _base (other.begin (), other.end ()), _m_hash (0)
+ {
+ set_hash ();
+ }
+ };
+#endif
+
+ template<typename T>
+ class auto_ref
+ {
+ private:
+ T *_m_ptr;
+
+ public:
+ auto_ref (const T &other)
+ : _m_ptr (&other)
+ {}
+
+ inline operator T& () const
+ {
+ return *_m_ptr;
+ }
+
+ auto_ref (const auto_ref<T> &other)
+ : _m_ptr (other._m_ptr)
+ {}
+
+ template<typename other>
+ inline bool operator== (const auto_ref<other> &x) const
+ {
+ return *_m_ptr == static_cast<other &> (x);
+ }
+ template<typename other>
+ inline bool operator== (const other &x) const
+ {
+ return *_m_ptr == x;
+ }
+ template<typename other>
+ inline bool operator!= (const other &x) const
+ {
+ return !(*this == x);
+ }
+ };
+
+ /* A wrapped_input_iterator is like an input::const_iterator,
+ but *i returns wrapper (*i) instead; wrapper returns element
+ (or const element & or something). */
+ template<typename input, class wrapper,
+ typename element = typename wrapper::result_type>
+ class wrapped_input_iterator : public input::const_iterator
+ {
+ private:
+ typedef typename input::const_iterator _base;
+
+ wrapper _m_wrapper;
+
+ public:
+ typedef element value_type;
+
+ inline wrapped_input_iterator ()
+ : _base ()
+ {}
+
+ template<typename arg_type>
+ inline wrapped_input_iterator (const _base &i, const arg_type &arg)
+ : _base (static_cast<_base> (i)), _m_wrapper (arg)
+ {}
+
+ inline wrapped_input_iterator (const wrapped_input_iterator &i)
+ : _base (static_cast<_base> (i)), _m_wrapper (i._m_wrapper)
+ {}
+
+ inline typename wrapper::result_type operator* () const
+ {
+ return _m_wrapper (_base::operator* ());
+ }
+
+ inline element *operator-> () const
+ {
+ return &(_m_wrapper (_base::operator* ()));
+ }
+
+ inline wrapped_input_iterator &operator++ () // prefix
+ {
+ _base::operator++ ();
+ return *this;
+ }
+ inline wrapped_input_iterator operator++ (int) // postfix
+ {
+ wrapped_input_iterator pre = *this;
+ ++*this;
+ return pre;
+ }
+ inline wrapped_input_iterator &operator-- () // prefix
+ {
+ _base::operator-- ();
+ return *this;
+ }
+ inline wrapped_input_iterator operator-- (int) // postfix
+ {
+ wrapped_input_iterator pre = *this;
+ --*this;
+ return pre;
+ }
+
+ inline const _base &base () const
+ {
+ return *this;
+ }
+
+ inline _base &base ()
+ {
+ return *this;
+ }
+
+ template<typename container = input>
+ struct copy
+ {
+ template<typename arg_type>
+ inline container operator () (const input &in,
+ const arg_type &arg = arg_type ())
+ {
+ return container (wrapped_input_iterator (in.begin (), arg),
+ wrapped_input_iterator (in.end (), arg));
+ }
+ };
+ };
+
+ /* A wrapped_input_container provides begin and end methods that
+ wrap the real container's iterators with wrapped_input_iterator. */
+ template<typename input, class wrapper,
+ typename element = typename wrapper::result_type>
+ class wrapped_input_container
+ {
+ private:
+ const input &_m_container;
+ wrapper _m_wrapper;
+
+ public:
+ typedef wrapped_input_iterator<input, wrapper, element> const_iterator;
+ typedef const_iterator iterator;
+
+ template<typename arg_type>
+ inline wrapped_input_container (const input &container,
+ const arg_type &arg)
+ : _m_container (container), _m_wrapper (arg)
+ {}
+
+ inline const_iterator begin () const
+ {
+ return const_iterator (_m_container.begin (), _m_wrapper);
+ }
+
+ inline const_iterator end () const
+ {
+ return const_iterator (_m_container.end (), _m_wrapper);
+ }
+
+ static inline bool ordered ()
+ {
+ return input::ordered ();
+ }
+ };
+
+ /* An iterator adapter for use in iterator-based constructors.
+ collectify (iterator) yields an iterator on input where *i
+ constructs output::value_type (input::value_type v, collector). */
+ template<typename input, typename output, typename arg_type>
+ struct argifier
+ : public std::unary_function<typename input::const_iterator,
+ typename output::iterator> // not really
+ {
+ typedef typename input::const_iterator inny;
+ typedef typename output::iterator outty;
+ typedef typename input::value_type inlet;
+ typedef typename output::value_type outlet;
+
+ /* Wrapper worker passed to wrapped_input_iterator.
+ This object holds the collector pointer. */
+ struct maker
+ : public std::unary_function<inlet, outlet>
+ {
+ const arg_type _m_arg;
+
+ inline maker (const arg_type &c)
+ : _m_arg (c)
+ {}
+
+ inline maker (const maker &m)
+ : _m_arg (m._m_arg)
+ {}
+
+ inline outlet operator () (const inlet &x) const
+ {
+ return outlet (x, _m_arg);
+ }
+ } _m_maker;
+
+ explicit inline argifier (const arg_type &c)
+ : _m_maker (c)
+ {}
+
+ typedef wrapped_input_iterator<input, maker> result_type;
+
+ inline result_type operator () (const inny &i)
+ {
+ return result_type (i, _m_maker);
+ }
+ };
+
+ template<typename input, typename output, typename arg_type>
+ static inline typename argifier<input, output, arg_type>::result_type
+ argify (const typename input::const_iterator &in, const arg_type &arg)
+ {
+ return argifier<input, output, arg_type> (arg) (in);
+ }
+
+ template<typename input, typename output, typename arg_type>
+ struct argifier2nd
+ : public std::unary_function<typename input::const_iterator,
+ typename output::iterator>
+ {
+ typedef typename input::const_iterator inny;
+ typedef typename output::iterator outty;
+ typedef typename input::value_type inlet;
+ typedef typename output::value_type outlet;
+
+ /* Wrapper worker passed to wrapped_input_iterator.
+ This object holds the collector pointer. */
+ struct pair_maker
+ : public argifier<input, output, arg_type>::maker
+ {
+ typedef typename argifier<input, output, arg_type>::maker maker;
+
+ inline pair_maker (const arg_type &c) : maker (c) {}
+ inline pair_maker (const pair_maker &m) : maker (m) {}
+
+ inline outlet operator () (const inlet &x) const
+ {
+ return std::make_pair (x.first,
+ typename outlet::second_type (x.second,
+ this->_m_arg));
+ }
+ } _m_maker;
+
+ explicit inline argifier2nd (const arg_type &c)
+ : _m_maker (c)
+ {}
+
+ typedef wrapped_input_iterator<input, pair_maker> const_iterator;
+
+ inline const_iterator operator () (const inny &i)
+ {
+ return const_iterator (i, _m_maker);
+ }
+ };
+
+ template<typename input, typename output, typename arg_type>
+ static inline typename argifier2nd<input, output, arg_type>::const_iterator
+ argify2nd (const typename input::const_iterator &in, const arg_type &arg)
+ {
+ return argifier2nd<input, output, arg_type> (arg) (in);
+ }
+
+ /* A guard object is intended to be ephemeral, existing solely to be
+ destroyed in exception paths where it was not cleared explicitly.
+ In that case, it calls tracker::soiled ().
+
+ For convenience, it can be constructed from a tracker reference or
+ pointer, or default-constructed and then filled. It's fillable by
+ calling the guard object as a function, passing it the tracker
+ reference or pointer, which it passes through on return:
+
+ guard<tracker> g;
+ use (g (t));
+ g.clear ();
+
+ This first calls T.start (). When G goes out of scope,
+ it calls T.abort () iff G.clear () was never called. */
+
+ template<typename tracker>
+ class guard
+ {
+ private:
+ tracker *_m_tracker;
+
+ inline void start ()
+ {
+ _m_tracker->start ();
+ }
+
+ public:
+ inline guard (tracker *t)
+ : _m_tracker (t)
+ {
+ start ();
+ }
+
+ inline guard (tracker &t)
+ : _m_tracker (&t)
+ {
+ start ();
+ }
+
+ inline guard ()
+ : _m_tracker (NULL)
+ {}
+
+ inline tracker *operator () (tracker *t)
+ {
+ _m_tracker = t;
+ start ();
+ return t;
+ }
+
+ inline tracker &operator () (tracker &t)
+ {
+ _m_tracker = &t;
+ start ();
+ return t;
+ }
+
+ inline operator tracker * () const
+ {
+ return _m_tracker;
+ }
+
+ inline operator tracker & () const
+ {
+ return *_m_tracker;
+ }
+
+ inline void clear ()
+ {
+ _m_tracker = NULL;
+ }
+
+ inline ~guard ()
+ {
+ if (unlikely (_m_tracker != NULL))
+ _m_tracker->abort ();
+ }
+ };
+
+ struct nothing
+ {
+ };
+
+ // Class instead of function so it can be a friend.
+ struct create_container
+ {
+ struct setter
+ {
+ template<typename in_iter, typename out_iter, typename arg_type>
+ inline void operator () (const out_iter &out, const in_iter &in,
+ bool, arg_type arg) const
+ {
+ out->set (*in, arg);
+ }
+ };
+
+ template<typename container, typename input, typename arg_type,
+ typename hook_type = const setter>
+ inline create_container (container *me, const input &other,
+ arg_type &arg, hook_type &hook = hook_type ())
+ {
+ typename input::const_iterator in = other.begin ();
+ bool last = in == other.end ();
+ while (!last)
+ {
+ /* Don't copy-construct the entry from *in here because that
+ copies it again into the list and destroys the first copy. */
+ me->push_back (typename container::value_type ());
+ typename container::iterator out = --me->end ();
+ const typename input::const_iterator here = in++;
+ last = in == other.end ();
+ hook (out, here, last, arg);
+ }
+ }
+ };
+
+ template<typename T>
+ struct is : public std::equal_to<T>
+ {
+ bool operator () (const T &a, const T &b) const
+ {
+ return a.is (b);
+ }
+ };
+
+ template<typename T>
+ struct is<T *> : public std::equal_to<T *>
+ {
+ bool operator () (const T *a, const T *b) const
+ {
+ return a == b || a->is (*b);
+ }
+ };
+
+#if 0 // unused
+ template<typename T1, typename T2>
+ struct is<std::pair<T1, T2> > : public std::equal_to<std::pair<T1, T2> >
+ {
+ bool operator () (const std::pair<T1, T2> &a,
+ const std::pair<T1, T2> &b) const
+ {
+ return (is<T1> () (a.first, b.first)
+ && is<T2> () (a.second, b.second));
+ }
+ };
+#endif
+
+ template<typename T>
+ struct identity_set
+ : public std::tr1::unordered_set<T, typename T::hasher, is<T> >
+ {
+ };
+
+ template<typename key_type, typename mapped_type>
+ struct identity_map
+ : public std::tr1::unordered_map<key_type, mapped_type,
+ typename key_type::hasher,
+ is<key_type> >
+ {};
+
+ /* This is like an unordered_set, but the equality predicate cannot
+ be fixed statically. Instead, each insertion call must pass in
+ the specific predicate to match against existing elements for
+ that insertion. */
+ template<typename T, typename hasher_type = hash<T> >
+ class dynamic_equality_set
+ {
+ public:
+ typedef T value_type;
+ typedef size_t hash_type;
+ typedef std::deque<T> bucket_type;
+
+ private:
+ typedef std::tr1::unordered_map<hash_type, bucket_type> map_type;
+
+ map_type _m_map;
+ hasher_type _m_hasher;
+
+ public:
+ template<typename match_type>
+ inline const value_type *
+ add (const value_type &candidate, match_type &match)
+ {
+ bucket_type &bucket = _m_map[_m_hasher (candidate)];
+
+ for (typename bucket_type::iterator i = bucket.begin ();
+ i != bucket.end ();
+ ++i)
+ {
+ const value_type &elt = *i;
+ if (match (elt, candidate))
+ // We have a winner!
+ return &elt;
+ }
+
+ // No matches: new element.
+ bucket.push_back (candidate);
+ return &(bucket.back ());
+ }
+
+ // Unclear why you didn't just use a normal identity_set then!
+ inline const value_type *add (const value_type &candidate)
+ {
+ is<value_type> equalator;
+ return add (candidate, equalator);
+ }
+
+ template<typename reporter>
+ inline void hash_stats (std::ostream &out, const char *name,
+ const reporter &report_collisions) const
+ {
+ size_t collisions = 0;
+ size_t empty_buckets = 0;
+ size_t total = 0;
+ size_t largest = 0;
+ for (typename map_type::const_iterator i = _m_map.begin ();
+ i != _m_map.end ();
+ ++i)
+ {
+ if (i->second.empty ())
+ ++empty_buckets;
+ else
+ {
+ size_t n = i->second.size () - 1;
+ collisions += n;
+ if (n > 0)
+ report_collisions (i->first, i->second);
+ }
+ if (i->second.size () > largest)
+ largest = i->second.size ();
+ total += i->second.size ();
+ }
+ out << name << ": " << total << ", "
+ << collisions << " collisions, "
+ << largest << " in largest bucket";
+ if (empty_buckets > 0)
+ out << ", " << empty_buckets << " empty buckets\n";
+ else
+ out << "\n";
+ }
+ };
+
+ template<typename set_type>
+ inline void container_hash_stats (std::ostream &out, const char *name,
+ const set_type &set)
+ {
+ std::set<size_t> hashes;
+ for (typename set_type::const_iterator i = set.begin ();
+ i != set.end ();
+ ++i)
+ hashes.insert (hash_this (*i));
+ out << name << ": " << set.size () << ", "
+ << hashes.size () << " hashes = "
+ << (set.size () - hashes.size ()) << " collisions\n";
+ }
+
+ /* sharing_stack<T> is like std::stack<T>, but copies share list
+ tails to reduce the memory footprint. Any non-const call to the
+ top method copies the top element so it's no longer shared.
+ So be sure to use const calls for any non-modifying access.
+ The top_const method is a short-hand for a const access to
+ a non-const container. */
+ template<typename T>
+ class sharing_stack
+ {
+ public:
+ typedef T value_type;
+ typedef size_t size_type;
+ typedef value_type &reference;
+ typedef const value_type &const_reference;
+
+ protected:
+ class element
+ {
+ private:
+ value_type _m_value;
+ element *_m_next;
+ unsigned int _m_count;
+
+ inline element &operator= (const element &)
+ {
+ throw std::logic_error ("cannot assign");
+ }
+
+ inline element ()
+ {
+ throw std::logic_error ("cannot default-construct");
+ }
+
+ public:
+ inline unsigned int count () const
+ {
+ return _m_count;
+ }
+
+ inline operator value_type & ()
+ {
+ return _m_value;
+ }
+
+ inline operator const value_type & () const
+ {
+ return _m_value;
+ }
+
+ inline element (const value_type &value, element *tail)
+ : _m_value (value), _m_next (tail), _m_count (1)
+ {
+ if (_m_next != NULL)
+ _m_next->acquire ();
+ }
+
+ inline element (const element &other)
+ : _m_value (other._m_value), _m_next (other._m_next), _m_count (1)
+ {
+ if (_m_next != NULL)
+ _m_next->acquire ();
+ }
+
+ inline ~element ()
+ {
+ if (_m_next != NULL)
+ _m_next->release ();
+ }
+
+ inline void acquire ()
+ {
+ assert (_m_count > 0);
+ ++_m_count;
+ }
+
+ inline void release ()
+ {
+ assert (_m_count > 0);
+ if (--_m_count == 0)
+ delete this;
+ }
+
+ inline element *pop ()
+ {
+ assert (_m_count > 0);
+ element *tail = _m_next;
+ if (--_m_count == 0)
+ {
+ _m_next = NULL;
+ delete this;
+ }
+ return tail;
+ }
+
+ inline const element *next () const
+ {
+ return _m_next;
+ }
+
+ inline bool shared () const
+ {
+ assert (_m_count > 0);
+ return _m_count > 1;
+ }
+ };
+
+ element *_m_head;
+ size_type _m_size;
+
+ inline void init (element *head, size_type n)
+ {
+ if (head == NULL)
+ assert (n == 0);
+ else
+ head->acquire ();
+ _m_head = head;
+ _m_size = n;
+ }
+
+ public:
+ inline void clear ()
+ {
+ if (_m_head == NULL)
+ assert (_m_size == 0);
+ else
+ {
+ _m_head->release ();
+ _m_head = NULL;
+ _m_size = 0;
+ }
+ }
+
+ inline bool empty () const
+ {
+ return _m_head == NULL;
+ }
+
+ inline size_type size () const
+ {
+ return _m_size;
+ }
+
+ inline void push (const value_type &value)
+ {
+ _m_head = new element (value, _m_head);
+ ++_m_size;
+ }
+
+ inline void pop ()
+ {
+ _m_head = _m_head->pop ();
+ --_m_size;
+ }
+
+ inline const value_type &top () const
+ {
+ assert (_m_head != NULL);
+ return *_m_head;
+ }
+
+ inline const value_type &const_top () const
+ {
+ return top ();
+ }
+
+ inline value_type &top ()
+ {
+ element *prev = _m_head;
+ if (prev->shared ())
+ {
+ _m_head = new element (*prev);
+ prev->release ();
+ }
+ assert (!_m_head->shared ());
+ return *_m_head;
+ }
+
+ inline sharing_stack ()
+ : _m_head (NULL), _m_size (0)
+ {}
+
+ inline sharing_stack (const sharing_stack &other)
+ {
+ init (other._m_head, other._m_size);
+ }
+
+ inline ~sharing_stack ()
+ {
+ clear ();
+ }
+
+ inline sharing_stack &operator= (const sharing_stack &other)
+ {
+ if (&other != this)
+ {
+ clear ();
+ init (other._m_head, other._m_size);
+ }
+ return *this;
+ }
+
+ inline void swap (sharing_stack &other)
+ {
+ std::swap (_m_head, other._m_head);
+ std::swap (_m_size, other._m_size);
+ }
+
+ class const_reverse_iterator
+ : public std::iterator<std::input_iterator_tag, value_type>
+ {
+ private:
+ const element *_m_elt;
+
+ friend class sharing_stack;
+ inline const_reverse_iterator (const element *elt)
+ : _m_elt (elt)
+ {}
+
+ public:
+ inline const value_type &operator* () const
+ {
+ return *_m_elt;
+ }
+
+ inline bool operator== (const const_reverse_iterator &other) const
+ {
+ return _m_elt == other._m_elt;
+ }
+ inline bool operator!= (const const_reverse_iterator &other) const
+ {
+ return !(*this == other);
+ }
+
+ inline const_reverse_iterator &operator++ () // prefix
+ {
+ _m_elt = _m_elt->next ();
+ return *this;
+ }
+ inline const_reverse_iterator operator++ (int) // postfix
+ {
+ const const_reverse_iterator old = *this;
+ ++*this;
+ return old;
+ }
+ };
+
+ inline const_reverse_iterator rbegin () const
+ {
+ return const_reverse_iterator (_m_head);
+ }
+
+ inline const_reverse_iterator rend () const
+ {
+ return const_reverse_iterator (NULL);
+ }
+
+ template<typename other_value_type, typename pred_type>
+ inline bool equal (const sharing_stack<other_value_type> &other,
+ pred_type &pred, size_type skip = 0) const
+ {
+ if (other.size () != size ())
+ return false;
+
+ const_reverse_iterator a = rbegin ();
+ typename sharing_stack<other_value_type>::const_reverse_iterator b
+ = other.rbegin ();
+
+ std::advance (a, skip);
+ std::advance (b, skip);
+
+ return std::equal (a, rend (), b, pred);
+ }
+
+ template<typename other_value_type>
+ inline bool operator== (const sharing_stack<other_value_type> &other)
+ const
+ {
+ equal_to<value_type, other_value_type> equalator;
+ return equal (other, equalator);
+ }
+
+ template<typename other_value_type>
+ inline bool operator!= (const sharing_stack<other_value_type> &other)
+ const
+ {
+ return !(*this == other);
+ }
+ };
+
+ // Compatible with sharing_stack, but actually a std::stack.
+ template<typename T, typename container_type = std::deque<T> >
+ struct stackish
+ : public std::stack<T, container_type>
+ {
+ inline const T &const_top () const
+ {
+ return this->top ();
+ }
+
+ inline void clear ()
+ {
+ this->c.clear ();
+ }
+
+ typedef typename container_type::const_reverse_iterator
+ const_reverse_iterator;
+
+ inline const_reverse_iterator rbegin () const
+ {
+ return this->c.rbegin ();
+ }
+
+ inline const_reverse_iterator rend () const
+ {
+ return this->c.rend ();
+ }
+
+ template<typename other_value_type, typename other_container_type,
+ typename pred_type>
+ inline bool
+ equal (const stackish<other_value_type, other_container_type> &other,
+ pred_type &pred, typename container_type::size_type skip = 0) const
+ {
+ if (other.size () != this->size ())
+ return false;
+
+ typename container_type::const_reverse_iterator a = this->rbegin ();
+ typename other_container_type::const_reverse_iterator b
+ = other.rbegin ();
+
+ std::advance (a, skip);
+ std::advance (b, skip);
+
+ return std::equal (a, this->rend (), b, pred);
+ }
+ };
+
+ /* This is a dummy you can template/syntactically use in
+ place of std::cout et al for disabled debugging spew. */
+ struct nostream
+ {
+ inline const nostream &
+ operator<< (std::ostream &(*) (std::ostream &)) const
+ {
+ return *this;
+ }
+
+ template<typename arg>
+ inline const nostream &
+ operator<< (const arg &) const
+ {
+ return *this;
+ }
+ };
+ };
+};
+
+#endif // <elfutils/subr.hh>
diff --git a/libdw/c++/values.cc b/libdw/c++/values.cc
new file mode 100644
index 00000000..e40fcaa2
--- /dev/null
+++ b/libdw/c++/values.cc
@@ -0,0 +1,729 @@
+/* -*- C++ -*- interfaces for libdw.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ In addition, as a special exception, Red Hat, Inc. gives You the
+ additional right to link the code of Red Hat elfutils with code licensed
+ under any Open Source Initiative certified open source license
+ (http://www.opensource.org/licenses/index.php) which requires the
+ distribution of source code with any binary distribution and to
+ distribute linked combinations of the two. Non-GPL Code permitted under
+ this exception must only link to the code of Red Hat elfutils through
+ those well defined interfaces identified in the file named EXCEPTION
+ found in the source code files (the "Approved Interfaces"). The files
+ of Non-GPL Code may instantiate templates or use macros or inline
+ functions from the Approved Interfaces without causing the resulting
+ work to be covered by the GNU General Public License. Only Red Hat,
+ Inc. may make changes or additions to the list of Approved Interfaces.
+ Red Hat's grant of this exception is conditioned upon your not adding
+ any new exceptions. If you wish to add a new Approved Interface or
+ exception, please contact Red Hat. You must obey the GNU General Public
+ License in all respects for all of the Red Hat elfutils code and other
+ code used in conjunction with Red Hat elfutils except the Non-GPL Code
+ covered by this exception. If you modify this file, you may extend this
+ exception to your version of the file, but you are not obligated to do
+ so. If you do not wish to provide this exception without modification,
+ you must delete this exception statement from your version and license
+ this file solely under the GPL without exception.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include <config.h>
+#include <cassert>
+#include "dwarf"
+#include "dwarf_edit"
+#include "dwarf_output"
+#include "data-values.hh"
+
+extern "C"
+{
+#include "libdwP.h"
+}
+
+using namespace elfutils;
+using namespace std;
+
+#include "dwarf-knowledge.cc"
+
+// dwarf::attr_value disambiguation and dispatch.
+
+/* For ambiguous the forms, we need to look up the expected
+ value spaces for this attribute to disambiguate.
+*/
+dwarf::value_space
+dwarf::attr_value::what_space () const
+{
+ unsigned int expected = expected_value_space (dwarf_whatattr (thisattr ()),
+ _m_tag);
+ unsigned int possible = 0;
+ switch (dwarf_whatform (thisattr ()))
+ {
+ case DW_FORM_flag:
+ case DW_FORM_flag_present:
+ return VS_flag;
+
+ case DW_FORM_addr:
+ return VS_address;
+
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ /* Location expression or target constant. */
+ possible = VS(location) | VS(constant);
+ if ((expected & possible) != possible)
+ /* When both are expected, a block is a location expression. */
+ break;
+ /* Fall through. */
+
+ case DW_FORM_exprloc:
+ return VS_location;
+
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_udata:
+ case DW_FORM_sdata:
+ /* Target constant, known DWARF constant. */
+ possible = (VS(dwarf_constant) | VS(constant)
+ | VS(source_file) | VS(source_line) | VS(source_column));
+ break;
+
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ // If a constant is not expected, these can be *ptr instead.
+ possible = (VS(dwarf_constant) | VS(constant)
+ | VS(source_file) | VS(source_line) | VS(source_column));
+ if (expected & possible)
+ break;
+
+ case DW_FORM_sec_offset:
+ possible = VS(location) | VS(lineptr) | VS(macptr) | VS(rangelistptr);
+ break;
+
+ case DW_FORM_string:
+ case DW_FORM_strp:
+ /* Identifier, file name, or string. */
+ possible = VS(identifier) | VS(source_file) | VS(string);
+ break;
+
+ case DW_FORM_ref_addr:
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata:
+ case DW_FORM_ref_sig8:
+ return VS_reference;
+
+ default:
+ throw std::runtime_error ("XXX bad form");
+ }
+
+ if (unlikely ((expected & possible) == 0))
+ {
+ // Otherwise we don't know enough to treat it robustly.
+ throw std::runtime_error ("XXX ambiguous form in unexpected attribute");
+ }
+
+ const int first = ffs (expected & possible) - 1;
+ if (likely ((expected & possible) == (1U << first)))
+ return static_cast<value_space> (first);
+
+ throw std::runtime_error ("XXX ambiguous form");
+}
+
+static string
+hex_string (Dwarf_Word value, const char *before = "", const char *after = "")
+{
+ std::ostringstream os;
+ os << std::hex << std::showbase << before << value << after;
+ return os.str ();
+}
+
+static string
+dec_string (Dwarf_Word value, const char *before = "", const char *after = "")
+{
+ std::ostringstream os;
+ os << before << value << after;
+ return os.str ();
+}
+
+static string
+addr_string (Dwarf_Addr value)
+{
+ // XXX some hook for symbol resolver??
+ return hex_string (value);
+}
+
+static inline string
+plain_string (const char *filename)
+{
+ return string ("\"") + filename + "\"";
+}
+
+static inline string
+plain_string (const string &filename)
+{
+ return "\"" + filename + "\"";
+}
+
+template<class value_type>
+static inline string
+value_string (const value_type &value)
+{
+ switch (value.what_space ())
+ {
+ case dwarf::VS_flag:
+ return value.flag () ? "1" : "0";
+
+ case dwarf::VS_rangelistptr:
+ return value.ranges ().to_string ();
+
+ case dwarf::VS_lineptr:
+ return value.line_info ().to_string ();
+
+ case dwarf::VS_macptr: // XXX punt for now, treat as constant
+ case dwarf::VS_constant:
+ if (value.constant_is_integer ())
+ return hex_string (value.constant ());
+ return dec_string (value.constant_block ().size (),
+ "{block of ", " bytes}");
+
+ case dwarf::VS_dwarf_constant:
+ return value.dwarf_constant ().to_string ();
+
+ case dwarf::VS_source_line:
+ return dec_string (value.source_line ());
+
+ case dwarf::VS_source_column:
+ return dec_string (value.source_column ());
+
+ case dwarf::VS_identifier:
+ return plain_string (value.identifier ());
+
+ case dwarf::VS_string:
+ return plain_string (value.string ());
+
+ case dwarf::VS_address:
+ return addr_string (value.address ());
+
+ case dwarf::VS_reference:
+ return hex_string (value.reference ()->offset (), "[", "]");
+
+ case dwarf::VS_source_file:
+ return value.source_file ().to_string ();
+
+ case dwarf::VS_location:
+ return value.location ().to_string ();
+
+ case dwarf::VS_discr_list:
+ break; // XXX DW_AT_discr_list unimplemented
+ }
+
+ throw std::runtime_error ("XXX unsupported value space");
+}
+
+template<>
+string
+to_string<dwarf::attribute> (const dwarf::attribute &attr)
+{
+ return attribute_string (attr);
+}
+
+template<>
+string
+to_string<dwarf::attr_value> (const dwarf::attr_value &value)
+{
+ return value_string (value);
+}
+
+template<>
+string
+to_string<dwarf_edit::attr_value> (const dwarf_edit::attr_value &value)
+{
+ return value_string (value);
+}
+
+template<>
+string
+to_string<dwarf_output::attr_value> (const dwarf_output::attr_value &value)
+{
+ return value_string (value);
+}
+
+// A few cases are trivial.
+#define SIMPLE(type, name, form) \
+ type \
+ dwarf::attr_value::name () const \
+ { \
+ type result; \
+ xif (thisattr (), dwarf_form##form (thisattr (), &result) < 0); \
+ return result; \
+ }
+
+SIMPLE (bool, flag, flag)
+
+// XXX check value_space is really constantish?? vs *ptr
+SIMPLE (Dwarf_Word, constant, udata)
+SIMPLE (Dwarf_Sword, signed_constant, sdata)
+
+SIMPLE (Dwarf_Addr, address, addr)
+
+const char *
+dwarf::attr_value::string () const
+{
+ const char *result = dwarf_formstring (thisattr ());
+ xif (thisattr(), result == NULL);
+ return result;
+}
+
+bool
+dwarf::attr_value::constant_is_integer () const
+{
+ switch (dwarf_whatform (thisattr ()))
+ {
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ return false;
+
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+ case DW_FORM_udata:
+ case DW_FORM_sdata:
+ return true;
+
+ default:
+ throw std::runtime_error ("XXX wrong form");
+ }
+}
+
+
+const_vector<uint8_t>
+dwarf::attr_value::constant_block () const
+{
+ Dwarf_Block block;
+
+ switch (dwarf_whatform (thisattr ()))
+ {
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ xif (thisattr(), dwarf_formblock (thisattr (), &block) < 0);
+ break;
+
+ case DW_FORM_data1:
+ block.length = 1;
+ block.data = thisattr ()->valp;
+ break;
+
+ case DW_FORM_data2:
+ block.length = 2;
+ block.data = thisattr ()->valp;
+ break;
+
+ case DW_FORM_data4:
+ block.length = 4;
+ block.data = thisattr ()->valp;
+ break;
+
+ case DW_FORM_data8:
+ block.length = 8;
+ block.data = thisattr ()->valp;
+ break;
+
+ case DW_FORM_udata:
+ case DW_FORM_sdata:
+ // XXX ?
+ if ((*(const uint8_t *) thisattr ()->valp & 0x80) == 0)
+ {
+ block.length = 1;
+ block.data = thisattr ()->valp;
+ break;
+ }
+
+ default:
+ throw std::runtime_error ("XXX wrong form");
+ }
+
+ return const_vector<uint8_t> (block);
+}
+
+namespace elfutils
+{
+ template<>
+ std::string to_string (const dwarf::debug_info_entry &die)
+ {
+ return die_string (die);
+ }
+};
+
+// dwarf::range_list
+
+dwarf::range_list::const_iterator::const_iterator (Dwarf_Attribute *attr,
+ ptrdiff_t offset)
+ : _m_base (-1), _m_begin (0), _m_end (0), _m_cu (attr->cu), _m_offset (offset)
+{
+ if (_m_offset == 0)
+ {
+ Dwarf_Word ofs;
+ xif (attr, dwarf_formudata (attr, &ofs) < 0); // XXX __libdw_formptr
+ _m_offset = ofs;
+ }
+}
+
+static bool
+range_list_advance (int secndx,
+ Dwarf_CU *cu,
+ Dwarf_Addr &base,
+ Dwarf_Addr &begin,
+ Dwarf_Addr &end,
+ ptrdiff_t &offset,
+ unsigned char **valp)
+{
+ const Elf_Data *d = cu->dbg->sectiondata[secndx];
+ if (unlikely (d == NULL))
+ throw std::runtime_error ("XXX no ranges");
+
+ if (unlikely (offset < 0) || unlikely ((size_t) offset >= d->d_size))
+ throw std::runtime_error ("XXX bad offset in ranges iterator");
+
+ unsigned char *readp = reinterpret_cast<unsigned char *> (d->d_buf) + offset;
+ unsigned char *const readendp
+ = reinterpret_cast<unsigned char *> (d->d_buf) + d->d_size;
+
+ while (true)
+ {
+ if (readendp - readp < cu->address_size * 2)
+ throw std::runtime_error ("XXX bad ranges");
+
+ if (cu->address_size == 8)
+ {
+ begin = read_8ubyte_unaligned_inc (cu->dbg, readp);
+ end = read_8ubyte_unaligned_inc (cu->dbg, readp);
+ if (begin == (uint64_t) -1l) /* Base address entry. */
+ {
+ base = end;
+ continue;
+ }
+ }
+ else
+ {
+ begin = read_4ubyte_unaligned_inc (cu->dbg, readp);
+ end = read_4ubyte_unaligned_inc (cu->dbg, readp);
+ if (begin == (uint32_t) -1) /* Base address entry. */
+ {
+ base = end;
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ if (begin == 0 && end == 0) /* End of list entry. */
+ offset = 1;
+ else
+ {
+ if (valp)
+ *valp = readp;
+ offset = readp - reinterpret_cast<unsigned char *> (d->d_buf);
+
+ if (base == (Dwarf_Addr) -1)
+ {
+ CUDIE (cudie, cu);
+
+ /* Find the base address of the compilation unit. It will
+ normally be specified by DW_AT_low_pc. In DWARF-3 draft 4,
+ the base address could be overridden by DW_AT_entry_pc. It's
+ been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc
+ for compilation units with discontinuous ranges. */
+ Dwarf_Attribute attr_mem;
+ if (unlikely (dwarf_lowpc (&cudie, &base) != 0)
+ && dwarf_formaddr (dwarf_attr (&cudie,
+ DW_AT_entry_pc,
+ &attr_mem),
+ &base) != 0)
+ {
+ return true; // XXX
+ }
+ }
+ }
+
+ return false;
+}
+
+dwarf::range_list::const_iterator &
+dwarf::range_list::const_iterator::operator++ ()
+{
+ xif (_m_cu, range_list_advance (IDX_debug_ranges, _m_cu, _m_base,
+ _m_begin, _m_end, _m_offset, NULL));
+ return *this;
+}
+
+
+template<typename container>
+string
+__libdw_ranges_to_string (const container &c)
+{
+ std::ostringstream os;
+
+ os << "<" << std::hex << std::showbase;
+
+ bool first = true;
+ for (typename container::const_iterator i = c.begin (); i != c.end (); ++i)
+ {
+ const typename container::value_type range = *i;
+ if (!first)
+ os << ",";
+ os << range.first << "-" << range.second;
+ first = false;
+ }
+
+ os << ">";
+
+ return os.str ();
+}
+
+string
+dwarf::range_list::to_string () const
+{
+ return __libdw_ranges_to_string (*this);
+}
+
+string
+dwarf::ranges::to_string () const
+{
+ return __libdw_ranges_to_string (*this);
+}
+
+string
+dwarf::arange_list::to_string () const
+{
+ return __libdw_ranges_to_string (*this);
+}
+
+dwarf::aranges_map
+dwarf::aranges () const
+{
+ Dwarf_Aranges *these;
+ xif (dwarf_getaranges (_m_dw, &these, NULL) < 0);
+
+ if (these == NULL)
+ return aranges_map ();
+
+ aranges_map result;
+ for (const Dwarf_Aranges_s::Dwarf_Arange_s *r = &these->info[0];
+ r < &these->info[these->naranges];
+ ++r)
+ result[compile_unit (debug_info_entry (_m_dw, r->offset))].insert
+ (arange_list::value_type (r->addr, r->addr + r->length));
+
+ return result;
+}
+
+// dwarf::location_attr
+
+const dwarf::location_attr
+dwarf::attr_value::location () const
+{
+ if (what_space () != VS_location)
+ throw std::runtime_error ("XXX not a location");
+
+ return location_attr (*this);
+}
+
+bool
+dwarf::location_attr::is_list () const
+{
+ switch (dwarf_whatform (_m_attr.thisattr ()))
+ {
+ case DW_FORM_block:
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ return false;
+ }
+
+ return true;
+}
+
+inline void
+dwarf::location_attr::const_iterator::advance ()
+{
+ xif (_m_cu, range_list_advance (IDX_debug_loc, _m_cu,
+ _m_base, _m_begin, _m_end, _m_offset,
+ &_m_block.data));
+ if (_m_offset > 1)
+ _m_offset += 2 + (_m_block.length
+ = read_2ubyte_unaligned_inc (_m_cu->dbg, _m_block.data));
+ else
+ // End iterator.
+ _m_block = Dwarf_Block ();
+}
+
+dwarf::location_attr::const_iterator
+dwarf::location_attr::begin () const
+{
+ const_iterator i (_m_attr.thisattr ());
+ if (is_list ())
+ {
+ // XXX __libdw_formptr
+ Dwarf_Word offset;
+ xif (_m_attr.thisattr (),
+ dwarf_formudata (_m_attr.thisattr (), &offset) < 0);
+ i._m_offset = offset;
+ i.advance ();
+ }
+ else
+ {
+ xif (_m_attr.thisattr (),
+ dwarf_formblock (_m_attr.thisattr (), &i._m_block) < 0);
+ i._m_base = 0;
+ i._m_end = -1;
+ i._m_offset = 0;
+ }
+
+ return i;
+}
+
+dwarf::location_attr::const_iterator &
+dwarf::location_attr::const_iterator::operator++ ()
+{
+ if (unlikely (_m_offset == 1))
+ throw std::runtime_error ("incrementing end iterator");
+
+ if (_m_offset == 0)
+ {
+ // Singleton, now at end.
+ _m_offset = 1;
+ _m_block.data = NULL;
+ _m_block.length = 0;
+ }
+ else
+ // Advance to next list entry.
+ advance ();
+
+ return *this;
+}
+
+template<typename locattr>
+static string
+locattr_string (const locattr *loc)
+{
+ return (loc->is_list () ? dec_string (loc->size (), "{loclist ", " entries}")
+ : "{locexpr}");
+}
+
+string
+dwarf::location_attr::to_string () const
+{
+ return locattr_string (this);
+}
+
+string
+dwarf_data::location_attr::to_string () const
+{
+ return locattr_string (this);
+}
+
+// dwarf::line_info_table
+
+template<typename line_info_table>
+static inline std::string
+line_info_string (const line_info_table *table)
+{
+ return ("[" + table->include_directories ().to_string ()
+ + ", " + table->lines ().to_string () + "]");
+}
+
+std::string
+dwarf::line_info_table::to_string () const
+{
+ return line_info_string (this);
+}
+
+namespace elfutils
+{
+ template<>
+ std::string
+ dwarf_edit::line_info_table::to_string () const
+ {
+ return line_info_string (this);
+ }
+
+};
+
+// dwarf::directory_table
+
+static std::string
+dirtable_string (size_t ndirs)
+{
+ return dec_string (ndirs, "{", " dirs}");
+}
+
+std::string
+dwarf::directory_table::to_string () const
+{
+ return dirtable_string (_m_files->ndirs);
+}
+
+std::string
+dwarf_data::directory_table::to_string () const
+{
+ return dirtable_string (size ());
+}
+
+// dwarf::line_table
+
+std::string
+dwarf::line_table::to_string () const
+{
+ return dec_string (_m_lines->nlines, "{", " line entries}");
+}
+
+namespace elfutils
+{
+ template<>
+ std::string
+ dwarf_edit::line_table::to_string () const
+ {
+ return dec_string (size (), "{", " line entries}");
+ }
+};
+
+::Dwarf_Off
+dwarf::debug_info_entry::cost () const
+{
+ Dwarf_Die next;
+ int result = dwarf_siblingof (thisdie (), &next);
+ xif (result < 0);
+ if (result == 0)
+ return (const char *) next.addr - (const char *) _m_die.addr;
+ if (next.addr != NULL)
+ return (const char *) next.addr - (const char *) _m_die.addr + 1;
+ return _m_die.cu->end - dwarf_dieoffset (thisdie ());
+}
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 44beac67..7a3a360a 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -313,14 +313,21 @@ struct Dwarf_CU
#define DIE_OFFSET_FROM_CU_OFFSET(cu_offset, offset_size) \
((cu_offset) + 3 * (offset_size) - 4 + 3)
-#define CUDIE(fromcu) \
- ((Dwarf_Die) \
- { \
- .cu = (fromcu), \
- .addr = ((char *) (fromcu)->dbg->sectiondata[IDX_debug_info]->d_buf \
- + (fromcu)->start + 3 * (fromcu)->offset_size - 4 + 3), \
+#define CUDIE_ADDR(fromcu) \
+ ((char *) (fromcu)->dbg->sectiondata[IDX_debug_info]->d_buf \
+ + DIE_OFFSET_FROM_CU_OFFSET ((fromcu)->start, (fromcu)->offset_size))
+
+#ifdef __cplusplus
+# define CUDIE(name, fromcu) \
+ Dwarf_Die name = { CUDIE_ADDR (fromcu), (fromcu), NULL, 0l }
+#else
+# define CUDIE(fromcu) \
+ ((Dwarf_Die) \
+ { \
+ .cu = (fromcu), \
+ .addr = CUDIE_ADDR (fromcu), \
})
-
+#endif
/* Macro information. */
struct Dwarf_Macro_s
@@ -444,6 +451,7 @@ extern int __libdw_intern_expression (Dwarf *dbg,
extern int __dwarf_errno_internal (void);
+#ifndef __cplusplus
/* Reader hooks. */
/* Relocation hooks return -1 on error (in that case the error code
@@ -591,7 +599,7 @@ unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
int err_nodata, unsigned char **endpp,
Dwarf_Off *offsetp)
internal_function;
-
+#endif /* Not C++ */
/* Aliases to avoid PLTs. */
diff --git a/src/ChangeLog b/src/ChangeLog
index 53415428..ae673bd2 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -14,6 +14,265 @@
* readelf.c (process_file): Don't leak an fd in failure case.
+2009-10-02 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc (talker): Show missing/extra children, not just count.
+ (noisy_compare): In explicit-offsets case, print for a positive match.
+
+ * dwarfcmp.cc (talker::print_one_reference_mismatch): Compare entries,
+ not references.
+
+2009-10-01 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc: Support more than for arguments to compare multiple
+ right-hand side entries against the same left-hand side.
+
+ * dwarfcmp.cc (talker): Describe reference mismatch details at the
+ end, iterating when that mentions new mismatches.
+
+2009-09-30 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc: Support four-argument usage to compare specific entries.
+
+2009-09-15 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc (talker, noisy_cmp): Describe context mismatch details
+ at the end.
+
+2009-08-27 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc (do_writer_test): New function, broken out of ...
+ (main): ... here.
+ [TEST]: Conditionalize -T and all its code on this.
+
+2009-08-25 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc (talker): Track real vs fake-positive match result
+ and cache only real results in the real tracker.
+
+2009-08-20 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc (verbose): New variable.
+ (options, parse_opt): Add --verbose/-l to set it.
+ (talker): Keep going after mismatches when verbose.
+
+2009-08-19 Roland McGrath <[email protected]>
+
+ Diagnose reference mismatches with more detail.
+ * dwarfcmp.cc (talker::reference_mismatch): New method.
+ (talker::mismatch): Call it.
+
+ * dwarfcmp.cc (talker): Update constructor for tracker changes.
+
+2009-08-16 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc (main): Call dwarf_end before return, for happy valgrind.
+
+2009-07-06 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc (test_writer): Make it an int.
+ (parse_opt): Make -T increment it.
+ (main): Test only dwarf_output for -T, only dwarf_edit for -TT,
+ both only for -TTT.
+
+2009-07-03 Roland McGrath <[email protected]>
+
+ * Makefile.am (AM_CXXFLAGS): Drop -Wno-unused-parameter.
+
+ * dwarfcmp.cc (open_file): Exit 77 for no DWARF under -T.
+
+2009-07-02 Roland McGrath <[email protected]>
+
+ * dwarflint-hl.cc (operator<<): dwarf::VS_unit_reference is gone.
+
+ * dwarfcmp.cc (main): Update -T constructors using tracker.
+ Instantiate and test dwarf_output for -T too.
+
+2009-07-01 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc (talker): Update constructor parameters.
+
+2009-06-19 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc: Revamp using dwarf_comparator.
+
+ * dwarflint-expected-at.cc: Include <config.h> first.
+ * dwarflint-expected.hh (expected_map::expectation_map):
+ Use dwarf::tags.
+ (to_string): Function removed.
+ * dwarflint-hl.cc (recursively_validate): Don't use it.
+
+ * dwarflint.c (abbrev_table_load): No-op control flow fiddle
+ silences gcc-4.4 -O3 warning.
+
+2009-04-02 Roland McGrath <[email protected]>
+
+ * Makefile.am (noinst_HEADERS): Add dwarfstrings.h here.
+ (dwarflint_SOURCES): Add dwarflint-expected.hh and dwarflint.h here.
+ (noinst_HEADERS): Add expr_opcodes.h too.
+
+2009-03-24 Roland McGrath <[email protected]>
+
+ * Makefile.am (libdwplusplus_SOURCES): Variable removed.
+ (dwarfcmp_SOURCES, dwarflint_SOURCES): Don't use it.
+ (libdwpp): New variable.
+ (dwarfcmp_LDADD, dwarflint_LDADD): Use it.
+
+2009-03-05 Petr Machata <[email protected]>
+
+ * dwarflint.c: Add --nohl command line switch.
+
+2009-01-28 Petr Machata <[email protected]>
+
+ * dwarflint.c: Coding style cleanups.
+ (found_hole): Tolerate zero-padding to given alignment.
+
+2009-01-27 Petr Machata <[email protected]>
+
+ * dwarflint.c: Implement validation of .debug_ranges and
+ references from .debug_info to .debug_ranges.
+
+2009-01-27 Petr Machata <[email protected]>
+
+ * dwarflint.c: Check that the base address selection entry
+ actually changes base address.
+
+2009-01-27 Petr Machata <[email protected]>
+
+ * dwarflint.c: Match the way dwarflint reports offsets with the
+ way elfutils does it.
+
+2009-01-26 Petr Machata <[email protected]>
+
+ * dwarflint.c (check_aranges_structural): Check that each CU is
+ referenced from one aranges section only.
+ (check_pub_structural): Likewise for pubnames and pubtypes.
+ (read_ctx_read_uleb128): Allow ten-byte ULEB128.
+
+2009-01-23 Petr Machata <[email protected]>
+
+ * dwarflint.c (check_debug_info_structural): Check that all CUs
+ have the same address size.
+
+2009-01-17 Roland McGrath <[email protected]>
+
+ * expr_opcodes.h: Fix DW_OP_deref entry.
+ Add DW_OP_GNU_push_tls_address, DW_OP_GNU_uninit.
+
+2009-01-17 Petr Machata <[email protected]>
+
+ * dwarflint.c: Better location handling: references now carry
+ along where did they originate (i.e. which section, offset, etc.),
+ all messages are location-aware.
+ (struct where): New structure that captures location inside the
+ Dwarf file. Can be chained to achieve chains of "caused by this
+ reference" messages ala GCC.
+
+2009-01-15 Petr Machata <[email protected]>
+
+ * dwarflint.c: Implement validation of .debug_loc and references
+ from .debug_info to .debug_loc.
+
+2009-01-14 Petr Machata <[email protected]>
+
+ * dwarfstrings.h (dwarf_locexpr_opcode_string): New.
+
+2009-01-14 Petr Machata <[email protected]>
+
+ * dwarflint.c: Validation .debug_pubtypes.
+
+2009-01-13 Petr Machata <[email protected]>
+
+ * dwarflint.c: Adjust check_pubnames_structural to be able to
+ validate also .debug_pubtypes.
+
+2009-01-13 Petr Machata <[email protected]>
+
+ * dwarflint.c: Rewrite message macros to functions. This makes it
+ possible to rewrite several other macros to functions.
+
+2009-01-12 Petr Machata <[email protected]>
+
+ * dwarflint.c: Implement validation of .debug_pubnames.
+
+2009-01-12 Petr Machata <[email protected]>
+
+ * dwarflint.c: Check padding between sections in .debug_aranges.
+
+2009-01-12 Petr Machata <[email protected]>
+
+ * dwarflint.c: For DIE references, remember both referrer and
+ referree. Add a new data structure ref_record to support that.
+ Add new message category, mc_die_rel_ref. Rename other DIE
+ relationship categories to mc_die_rel_* pattern.
+
+2009-01-12 Petr Machata <[email protected]>
+
+ * dwarflint.c: Changes in data structures for recording DIEs and CUs.
+
+2009-01-12 Petr Machata <[email protected]>
+
+ * dwarflint.c: Implement validation of .debug_aranges.
+
+2009-01-11 Petr Machata <[email protected]>
+
+ * dwarflint.c (process_file): Handle absence of .debug_info,
+ .debug_abbrev and .debug_str gracefully.
+
+2009-01-11 Petr Machata <[email protected]>
+
+ * dwarflint.c: A couple small fixes across the code.
+ (check_debug_info_structural): Return bool.
+ (check_cu_structural): Likewise.
+ (check_addr_record_addr): Likewise.
+ (check_die_references): Likewise.
+
+2009-01-10 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc (test_writer): New variable.
+ (options, parse_opt): Grok -T/--test-writer to set it.
+ (main): When set, exercise dwarf_output constructors and comparators.
+
+ * dwarflint.c (options, parse_opt): Replace --no-debug with
+ -i/--ignore-missing, to match dwarfcmp.
+
+2009-01-10 Petr Machata <[email protected]>
+
+ * dwarflint.c: Implement --no-debug: silently accept file, if its
+ debug info is not available.
+
+2009-01-10 Roland McGrath <[email protected]>
+
+ * dwarfcmp.cc: New file.
+ * Makefile.am (bin_PROGRAMS): Add dwarfcmp.
+ (dwarfcmp_SOURCES): New variable.
+ (dwarfcmp_no_Wformat, dwarfcmp_LDADD): New variables.
+
+2009-01-10 Petr Machata <[email protected]>
+
+ * dwarflint.c: Implement fine-grained message selection. Each
+ message has a category, which is bitwise OR of category options.
+ There are acceptance and rejection criteria for warnings and
+ errors, which can be tuned using command-line options (currently
+ --strict and --gnu).
+ Use dwarfstrings.h in two messages.
+
+2009-01-10 Petr Machata <[email protected]>
+
+ * readelf.c: Extract functions that format dwarf enums into a file
+ of its own, so it can be shared with dwarflint.
+ * dwarfstrings.h: That file.
+
+2009-01-09 Petr Machata <[email protected]>
+
+ * dwarflint.c: Sort the abbrev tables and look up abbreviations
+ with bisect search.
+
+2009-01-09 Petr Machata <[email protected]>
+
+ * dwarflint.c: Checking for zero padding and unreferenced bytes.
+ CU size and padding at the end of CU are now checked.
+
2010-02-15 Roland McGrath <[email protected]>
* Makefile.am: Use config/eu.am for common stuff.
@@ -342,6 +601,19 @@
* ld.c (ld_new_searchdir): Fix adding to search path list.
+2009-01-07 Petr Machata <[email protected]>
+
+ * dwarflint.c: Check that the sibling doesn't point to the
+ terminating DIE (the one with the code of 0).
+
+2009-01-07 Petr Machata <[email protected]>
+
+ * dwarflint.c: More checking DW_AT_sibling correctness.
+
+2009-01-06 Petr Machata <[email protected]>
+
+ * dwarflint.c: Checking DW_AT_sibling correctness.
+
2009-01-06 Ulrich Drepper <[email protected]>
* readelf.c: Implement call frame debug section dumping.
@@ -368,6 +640,21 @@
* strip.c: Likewise.
* unstrip.c: Likewise.
+2009-01-01 Petr Machata <[email protected]>
+
+ * dwarflint.c: Tweaks across the code:
+ - Memory allocation now done with xrealloc, xcalloc functions. As
+ a consequence, backoff for out of memory conditions was turned off.
+ - Coding style was improved on many places.
+ - Abbrev tag and name validation changed, everything up to the
+ hi_user is now accepted.
+ - Ambiguous references to NULL DIEs and Abbrevs were changed to
+ explicitly mention DIE with zero code and similar.
+
+2008-12-15 Petr Machata <[email protected]>
+
+ * dwarflint.c: New file.
+
2008-12-11 Roland McGrath <[email protected]>
* nm.c (sym_name): New function.
diff --git a/src/Makefile.am b/src/Makefile.am
index afd3bd3a..58c62362 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -45,8 +45,10 @@ 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 \
+ dwarflint dwarfcmp
+noinst_PROGRAMS = dwarfcmp-test
ld_dsos = libld_elf_i386_pic.a
if NATIVE_LD
@@ -54,7 +56,7 @@ noinst_LIBRARIES = libld_elf.a libar.a
native_ld_cflags = -DBASE_ELF_NAME=elf_$(base_cpu)
else
noinst_LIBRARIES = libld_elf.a libar.a $(ld_dsos)
-noinst_PROGRAMS = $(ld_dsos:_pic.a=.so)
+noinst_PROGRAMS += $(ld_dsos:_pic.a=.so)
endif
if NEVER
# We never build this library but we need to get the dependency files
@@ -69,8 +71,23 @@ ld_SOURCES = ld.c ldgeneric.c ldlex.l ldscript.y symbolhash.c sectionhash.c \
libar_a_SOURCES = arlib.c arlib2.c
+dwarfcmp_SOURCES = dwarfcmp.cc
+
+# dwarfcmp-test is just dwarfcmp built with -DTEST.
+dwarfcmp_test_CPPFLAGS = -DTEST
+dwarfcmp_test_SOURCES = $(dwarfcmp_SOURCES)
+dwarfcmp_test_LDADD = $(dwarfcmp_LDADD)
+
+dwarflint_SOURCES = dwarflint.c dwarflint.h dwarflint-hl.cc \
+ dwarflint-expected-at.cc dwarflint-expected.hh \
+ dwarfstrings.c dwarflint-coverage.c dwarflint-coverage.h \
+ dwarflint-readctx.c dwarflint-readctx.h
+
+readelf_SOURCES = readelf.c dwarfstrings.c
+
noinst_HEADERS = ld.h symbolhash.h sectionhash.h versionhash.h \
- ldscript.h xelf.h unaligned.h
+ ldscript.h xelf.h unaligned.h dwarfstrings.h \
+ expr_opcodes.h
EXTRA_DIST = elf32-i386.script libld_elf_i386.map $(ld_modules) arlib.h \
debugpred.h
@@ -92,12 +109,16 @@ endif
libebl = ../libebl/libebl.a
libeu = ../lib/libeu.a
+# XXX later the C++ stuff will be in libdw.so directly
+libdwpp = ../libdw/libdwpp.a $(libdw)
+
nm_no_Wformat = yes
size_no_Wformat = yes
strings_no_Wformat = yes
addr2line_no_Wformat = yes
# XXX While the file is not finished, don't warn about this
ldgeneric_no_Wunused = yes
+dwarfcmp_no_Wformat = yes
readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl
nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl
@@ -118,6 +139,8 @@ 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
+dwarfcmp_LDADD = $(libdwpp) $(libmudflap) -ldl
+dwarflint_LDADD = $(libebl) $(libelf) $(libdwpp) $(libeu) $(libmudflap) -ldl
ldlex.o: ldscript.c
ldlex_no_Werror = yes
diff --git a/src/dwarfcmp.cc b/src/dwarfcmp.cc
new file mode 100644
index 00000000..c0551c74
--- /dev/null
+++ b/src/dwarfcmp.cc
@@ -0,0 +1,867 @@
+/* Compare semantic content of two DWARF files.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cinttypes>
+#include "../libdw/libdwP.h" // XXX
+
+#include "c++/dwarf"
+#include "c++/dwarf_comparator"
+#include "c++/dwarf_tracker"
+
+using namespace elfutils;
+using namespace std;
+
+
+/* Name and version of program. */
+static void print_version (FILE *stream, struct argp_state *state);
+void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
+
+/* Bug report address. */
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+
+/* Values for the parameters which have no short form. */
+#define OPT_XXX 0x100
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Control options:"), 0 },
+ { "quiet", 'q', NULL, 0, N_("Output nothing; yield exit status only"), 0 },
+ { "verbose", 'l', NULL, 0, N_("Output all differences"), 0 },
+ { "ignore-missing", 'i', NULL, 0,
+ N_("Don't complain if both files have no DWARF at all"), 0 },
+
+#ifdef TEST
+ { "test-writer", 'T', NULL, 0, N_("Test DWARF output classes"), 0 },
+#endif
+
+ { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("\
+Compare two DWARF files for semantic equality.\n\
+In the second form, compare specific entries chosen by hexadecimal file offset,\
+\n\the entry at OFFSET1 in FILE1 against each of OFFSET2... in FILE2.");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("FILE1 FILE2\n\
+FILE1 FILE2 OFFSET1 OFFSET2...");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+/* Nonzero if only exit status is wanted. */
+static bool quiet;
+
+/* Nonzero if missing DWARF is equal DWARF. */
+static bool missing_ok;
+
+/* True if we should print all differences. */
+static bool verbose;
+
+#ifdef TEST
+/* Nonzero to test writer classes. */
+static int test_writer;
+#else
+# define test_writer 0
+#endif
+
+static Dwarf *
+open_file (const char *fname, int *fdp)
+{
+ int fd = open (fname, O_RDONLY);
+ if (unlikely (fd == -1))
+ error (2, errno, gettext ("cannot open '%s'"), fname);
+ Dwarf *dw = dwarf_begin (fd, DWARF_C_READ);
+ *fdp = fd;
+ if (dw == NULL)
+ {
+ int code = dwarf_errno ();
+ if (code != DWARF_E_NO_DWARF
+ || (!missing_ok && (!test_writer || (exit (77), false))))
+ error (2, 0,
+ gettext ("cannot create DWARF descriptor for '%s': %s"),
+ fname, dwarf_errmsg (code));
+ }
+ return dw;
+}
+
+
+// XXX make translation-friendly
+
+template<class dwarf1, class dwarf2, bool print_all>
+struct talker : public dwarf_ref_tracker<dwarf1, dwarf2>
+{
+ typedef dwarf_tracker_base<dwarf1, dwarf2> _base;
+ typedef dwarf_ref_tracker<dwarf1, dwarf2> subtracker;
+ typedef typename _base::cu1 cu1;
+ typedef typename _base::cu2 cu2;
+ typedef typename _base::die1 die1;
+ typedef typename _base::die2 die2;
+ typedef typename _base::dwarf1_die::attributes_type::const_iterator attr1;
+ typedef typename _base::dwarf2_die::attributes_type::const_iterator attr2;
+
+ template<typename die>
+ struct die_hasher
+ : public std::unary_function<die, dwarf::debug_info_entry::identity_type>
+ {
+ inline dwarf::debug_info_entry::identity_type
+ operator () (const die &ref) const
+ {
+ return (*ref).identity ();
+ }
+ };
+
+ template<typename die>
+ struct die_equal_to
+ : public std::equal_to<die>
+ {
+ inline dwarf::debug_info_entry::identity_type
+ operator () (const die &a, const die &b) const
+ {
+ return (*a).identity () == (*b).identity ();
+ }
+ };
+
+ typedef std::tr1::unordered_set<die2, die_hasher<die2>, die_equal_to<die2>
+ > die2_set;
+ typedef std::tr1::unordered_map<die1, die2_set,
+ die_hasher<die1>, die_equal_to<die1>
+ > context_map;
+ context_map bad_context_;
+ context_map reference_mismatches_;
+ typedef std::pair<dwarf::debug_info_entry::identity_type,
+ dwarf::debug_info_entry::identity_type> identity_pair;
+ std::set<identity_pair> mismatches_printed_;
+
+ string prefix_;
+
+ const typename dwarf1::debug_info_entry *a_;
+ const typename dwarf2::debug_info_entry *b_;
+ bool result_;
+ bool visiting_result_;
+ bool mismatches_pending_;
+
+ inline talker ()
+ : a_ (NULL), b_ (NULL), result_ (true), mismatches_pending_ (false)
+ {}
+
+ inline talker (const talker &proto, typename subtracker::reference_match &m,
+ const typename subtracker::left_context_type &l,
+ const typename subtracker::right_context_type &r)
+ : subtracker (static_cast<const subtracker &> (proto), m, l, r),
+ a_ (NULL), b_ (NULL)
+ {
+ }
+
+ inline ostream &location () const
+ {
+ return cout << prefix_
+ << hex << a_->offset () << " vs " << b_->offset ()
+ << ": ";
+ }
+
+ inline void visit (const typename dwarf1::debug_info_entry &a,
+ const typename dwarf2::debug_info_entry &b)
+ {
+ a_ = &a;
+ b_ = &b;
+ visiting_result_ = a.tag () == b.tag ();
+ if (!visiting_result_)
+ location () << dwarf::tags::name (a.tag ())
+ << " vs "
+ << dwarf::tags::name (b.tag ())
+ << endl;
+ }
+
+ inline bool keep_going ()
+ {
+ result_ = visiting_result_ = false;
+ return print_all;
+ }
+
+ inline bool notice_match (typename subtracker::reference_match &matched,
+ const die1 &a, const die2 &b, bool matches)
+ {
+ /* In the main walk, a mismatch would have gone to keep_going.
+ But in reference_match, the comparator uses a subtracker.
+ It won't have set visiting_result_, but it will return a
+ real non-matching result we can catch here. */
+ if (!matches)
+ visiting_result_ = false;
+
+ // Record the real result in the cache, not a fake match for -l.
+ subtracker::notice_match (matched, a, b, visiting_result_);
+
+ return matches || keep_going ();
+ }
+
+ inline bool mismatch (cu1 &it1, const cu1 &end1,
+ cu2 &it2, const cu2 &end2)
+ {
+ if (it1 == end1) // a lacks some of b's CUs.
+ {
+ cout << "files differ: "
+ << dec << subr::length (it2, end2)
+ << " extra compilation units "
+ << endl;
+ it2 = end2;
+ }
+ else if (it2 == end2) // b lacks some of a's CUs.
+ {
+ cout << "files differ: "
+ << dec << subr::length (it1, end1)
+ << " compilation units missing "
+ << endl;
+ it1 = end1;
+ }
+ else
+ // Otherwise the differing CU will have announced itself.
+ ++it1, ++it2;
+ return keep_going ();
+ }
+
+ template<typename die>
+ inline void list_children (const char *prefix, die &it, const die &end) const
+ {
+ for (location () << prefix << hex; it != end; ++it)
+ cout << " " << (*it).offset () << "=" << (*it).to_string ();
+ cout << endl;
+ }
+
+ inline bool mismatch (die1 &it1, const die1 &end1,
+ die2 &it2, const die2 &end2)
+ {
+ if (it1 == end1) // a_ lacks some of b_'s children.
+ list_children ("extra children:", it2, end2);
+ else if (it2 == end2) // b_ lacks some of a_'s children.
+ list_children ("missing children:", it1, end1);
+ else
+ // Otherwise the differing child will have announced itself.
+ ++it1, ++it2;
+ return keep_going ();
+ }
+
+ inline bool mismatch (attr1 &it1, const attr1 &end1,
+ attr2 &it2, const attr2 &end2)
+ {
+ if (it1 == end1) // a_ lacks some of b_'s attrs.
+ {
+ for (location () << " extra attributes:"; it2 != end2; ++it2)
+ cout << " " << to_string (*it2);
+ it2 = end2;
+ }
+ else if (it2 == end2) // b_ lacks some of a_'s attrs.
+ {
+ for (location () << " missing attributes:"; it1 != end1; ++it1)
+ cout << " " << to_string (*it1);
+ it1 = end1;
+ }
+ else
+ {
+ location () << to_string (*it1) << " vs " << to_string (*it2);
+ if ((*it1).second.what_space () == dwarf::VS_reference
+ && (*it2).second.what_space () == dwarf::VS_reference)
+ reference_mismatch ((*it1).second.reference (),
+ (*it2).second.reference ());
+ ++it1;
+ ++it2;
+ }
+ cout << endl;
+ return keep_going ();
+ }
+
+ typedef dwarf_comparator<dwarf1, dwarf2, false, subtracker> subcomparator;
+ inline void reference_mismatch (const die1 &ref1, const die2 &ref2)
+ {
+ subcomparator cmp (*(subtracker *) this);
+ if (cmp.equals (ref1, ref2) && !print_all)
+ cout << " (XXX refs now equal again!)"
+ << (cmp.equals (*ref1, *ref2) ? "" : " (and not identical!!)");
+ else if (cmp.equals (*ref1, *ref2))
+ {
+ cout << " (identical but contexts mismatch)";
+ bad_context_[ref1].insert (ref2);
+ }
+ else
+ {
+ _base notracker;
+ dwarf_comparator<dwarf1, dwarf2, true> cmp_norefs (notracker);
+ if (cmp_norefs.equals (*ref1, *ref2))
+ cout << " (" << ref1->to_string () << " with reference mismatches)";
+ else
+ cout << " (" << ref1->to_string ()
+ << " != " << ref2->to_string ()
+ << ")";
+ if (reference_mismatches_[ref1].insert (ref2).second)
+ mismatches_pending_ = true;
+ }
+ }
+
+ struct prefixer
+ {
+ string &string_;
+ inline explicit prefixer (string &p) : string_ (p) {}
+ inline ~prefixer ()
+ {
+ string_.clear ();
+ }
+ };
+
+ inline void
+ for_context_map (const context_map &context,
+ void (talker::*method) (const die1 &, const die2 &))
+ {
+ for (typename context_map::const_iterator i = context.begin ();
+ i != context.end ();
+ ++i)
+ for (typename die2_set::const_iterator j = i->second.begin ();
+ j != i->second.end ();
+ ++j)
+ (this->*method) (i->first, *j);
+ }
+
+ // REF1 and REF2 had unequal contents. Describe the details.
+ inline void print_one_reference_mismatch (const die1 &ref1, const die2 &ref2)
+ {
+ if (mismatches_printed_.insert (identity_pair ((*ref1).identity (),
+ (*ref2).identity ())).second)
+ {
+ prefixer here (prefix_);
+ {
+ ostringstream pfx;
+ pfx << "reference "
+ << hex << (*ref1).offset () << " vs " << (*ref2).offset ()
+ << ": ";
+ prefix_ = pfx.str ();
+ }
+ dwarf_comparator<dwarf1, dwarf2, false, talker> (*this)
+ .equals (*ref1, *ref2);
+ }
+ }
+
+ inline void print_reference_mismatches ()
+ {
+ // Print the pending mismatches and loop if that adds more fresh ones.
+ while (mismatches_pending_)
+ {
+ mismatches_pending_ = false;
+
+ // We need a copy so our iterators remain stable if we add new entries.
+ const context_map all = reference_mismatches_;
+ for_context_map (all, &talker::print_one_reference_mismatch);
+ }
+ }
+
+ // When using subr::sharing_stack, it is important this argument is const.
+ template<typename stack_type>
+ static inline const typename stack_type::value_type &
+ path_top (const stack_type &path)
+ {
+ return path.top ();
+ }
+
+ // REF1 and REF2 had equal contents but different contexts. Describe them.
+ inline void print_one_bad_context (const die1 &ref1, const die2 &ref2)
+ {
+ dwarf_comparator<dwarf1, dwarf2, true, talker> cmp (*this);
+
+ prefixer here (prefix_);
+ {
+ ostringstream pfx;
+ pfx << hex << (*ref1).offset () << " vs " << (*ref2).offset ()
+ << " context: ";
+ prefix_ = pfx.str ();
+ }
+
+ typename subtracker::left_context_type left = left_context (ref1);
+ typename subtracker::right_context_type right = right_context (ref2);
+
+ left.pop ();
+ right.pop ();
+ while (!left.empty ())
+ {
+ if (right.empty ())
+ {
+ cout << prefix_
+ << (*path_top (left)).offset () << " vs top-level" << endl;
+ return;
+ }
+
+ // This prints the differences if it finds some.
+ visit (*path_top (left), *path_top (right));
+ if (!visiting_result_)
+ {
+ cout << endl;
+ return;
+ }
+ if (!cmp.equals (a_->attributes (), b_->attributes ()))
+ return;
+
+ left.pop ();
+ right.pop ();
+ }
+ if (!right.empty ())
+ cout << prefix_
+ << "top-level vs " << (*path_top (right)).offset () << endl;
+ }
+
+ inline void print_bad_context ()
+ {
+ for_context_map (bad_context_, &talker::print_one_bad_context);
+ }
+};
+
+template<class dw>
+static inline typename dw::compile_units::const_iterator
+find_cu (const dw &file, const typename dw::debug_info_entry &entry)
+{
+ dwarf::debug_info_entry::identity_type id = entry.compile_unit ().identity ();
+
+ for (typename dw::compile_units::const_iterator cu
+ = file.compile_units ().begin ();
+ cu != file.compile_units ().end ();
+ ++cu)
+ {
+ if ((*cu).identity () == id)
+ return cu;
+ }
+
+ throw std::logic_error ("cannot find compile_unit for debug_info_entry!");
+}
+
+template<class dwarf1, class dwarf2, class tracker>
+struct cmp
+ : public dwarf_comparator<dwarf1, dwarf2, false, tracker>
+{
+ tracker _m_tracker;
+ inline cmp ()
+ : dwarf_comparator<dwarf1, dwarf2, false, tracker> (_m_tracker)
+ {}
+};
+
+// For a silent comparison we just use the standard ref tracker.
+template<class dwarf1, class dwarf2>
+struct quiet_cmp
+ : public cmp<dwarf1, dwarf2, dwarf_ref_tracker<dwarf1, dwarf2> >
+{
+ typedef dwarf_ref_tracker<dwarf1, dwarf2> my_tracker;
+
+ inline bool operator () (const dwarf1 &a, const dwarf2 &b)
+ {
+ return equals (a, b);
+ }
+
+ inline bool operator () (const dwarf1 &file1, const dwarf2 &file2,
+ const typename dwarf1::debug_info_entry &a,
+ const typename dwarf2::debug_info_entry &b)
+ {
+ typename my_tracker::walk in (&this->_m_tracker,
+ find_cu (file1, a), find_cu (file2, b));
+ in.jump (a, b);
+ return equals (a, b);
+ }
+};
+
+template<class dwarf1, class dwarf2>
+static inline bool
+quiet_compare (const dwarf1 &a, const dwarf2 &b)
+{
+ return quiet_cmp<dwarf1, dwarf2> () (a, b);
+}
+
+static inline const dwarf::debug_info_entry
+parse_offset (const char *name, const dwarf &file, const char *arg)
+{
+ char *end = NULL;
+ Dwarf_Off offset = strtoull (arg, &end, 16);
+ if (end == NULL || *end != '\0')
+ {
+ fputs (gettext ("Invalid offset parameter (use hex).\n"),
+ stderr);
+ argp_help (&argp, stderr, ARGP_HELP_SEE,
+ program_invocation_short_name);
+ exit (1);
+ }
+ try
+ {
+ return dwarf::debug_info_entry (file, offset);
+ }
+ catch (std::runtime_error x)
+ {
+ fprintf (stderr, gettext ("%s: offset %#" PRIx64 ": %s\n"),
+ name, offset, x.what ());
+ exit (1);
+ }
+}
+
+template<class dwarf1, class dwarf2>
+static inline bool
+quiet_compare (const dwarf1 &file1, const dwarf2 &file2,
+ const typename dwarf1::debug_info_entry &a,
+ const char *fname2, char **offsets, int noffsets)
+{
+ quiet_cmp<dwarf1, dwarf2> cmp;
+
+ bool same = true;
+
+ do
+ {
+ const dwarf::debug_info_entry b
+ = parse_offset (fname2, file2, *offsets++);
+
+ same = cmp (file1, file2, a, b);
+ }
+ while (--noffsets > 0);
+
+ return same;
+}
+
+// To be noisy, the talker wraps the standard tracker with verbosity hooks.
+template<class dwarf1, class dwarf2, bool print_all>
+struct noisy_cmp
+ : public cmp<dwarf1, dwarf2, talker<dwarf1, dwarf2, print_all> >
+{
+ typedef talker<dwarf1, dwarf2, print_all> my_tracker;
+
+ template<typename item1, typename item2>
+ inline bool compare (const item1 &a, const item2 &b)
+ {
+ return equals (a, b) && this->_m_tracker.result_;
+ }
+
+ inline void print_more_details ()
+ {
+ this->_m_tracker.print_reference_mismatches ();
+ this->_m_tracker.print_bad_context ();
+ }
+
+ inline bool operator () (const dwarf1 &a, const dwarf2 &b)
+ {
+ return compare (a, b);
+ }
+
+ inline bool operator () (const dwarf1 &file1, const dwarf2 &file2,
+ const typename dwarf1::debug_info_entry &a,
+ const typename dwarf2::debug_info_entry &b)
+ {
+ typename my_tracker::walk in (&this->_m_tracker,
+ find_cu (file1, a), find_cu (file2, b));
+ in.jump (a, b);
+ return compare (a, b);
+ }
+};
+
+template<class dwarf1, class dwarf2>
+static inline bool
+noisy_compare (const dwarf1 &a, const dwarf2 &b, bool print_all)
+{
+ return (print_all
+ ? noisy_cmp<dwarf1, dwarf2, true> () (a, b)
+ : noisy_cmp<dwarf1, dwarf2, false> () (a, b));
+}
+
+template<bool print_all>
+static inline bool
+noisy_compare (const dwarf &file1, const dwarf &file2,
+ const dwarf::debug_info_entry &a,
+ const char *fname2, char **offsets, int noffsets)
+{
+ noisy_cmp<dwarf, dwarf, print_all> cmp;
+
+ bool same = true;
+
+ do
+ {
+ const dwarf::debug_info_entry b
+ = parse_offset (fname2, file2, *offsets++);
+
+ if (cmp (file1, file2, a, b))
+ {
+ cmp._m_tracker.visit (a, b);
+ cmp._m_tracker.location () << "match" << endl;
+ }
+ else
+ same = false;
+ }
+ while (--noffsets > 0);
+
+ if (!same)
+ cmp.print_more_details ();
+
+ return same;
+}
+
+static inline bool
+noisy_compare (const dwarf &file1, const dwarf &file2,
+ const dwarf::debug_info_entry &a,
+ const char *fname2, char *offsets[], int noffsets,
+ bool print_all)
+{
+ return (print_all
+ ? noisy_compare<true> (file1, file2, a,
+ fname2, offsets, noffsets)
+ : noisy_compare<false> (file1, file2, a,
+ fname2, offsets, noffsets));
+}
+
+
+#ifdef TEST
+
+# include "c++/dwarf_edit"
+# include "c++/dwarf_output"
+
+// Test that one comparison works as expected.
+template<class dwarf1, class dwarf2>
+static void
+test_compare (const dwarf1 &file1, const dwarf2 &file2, bool expect)
+{
+ if (quiet_cmp<dwarf1, dwarf2> () (file1, file2) != expect)
+ {
+ if (expect)
+ noisy_compare (file1, file2, true);
+ throw std::logic_error (__PRETTY_FUNCTION__);
+ }
+}
+
+// Test all directions of two classes.
+template<class dwarf1, class dwarf2>
+static void
+test_classes (const dwarf1 &file1, const dwarf1 &file2,
+ const dwarf2 &out1, const dwarf2 &out2,
+ bool expect)
+{
+ // Compare self, same type.
+ test_compare (out1, out1, true);
+ test_compare (out2, out2, true);
+
+ // Compare self, output == input.
+ test_compare (out1, file1, true);
+ test_compare (out2, file2, true);
+
+ // Compare self, input == output.
+ test_compare (file1, out1, true);
+ test_compare (file2, out2, true);
+
+ // Compare files, output == output.
+ test_compare (out1, out2, expect);
+ test_compare (out2, out1, expect);
+
+ // Compare files, output vs input.
+ test_compare (out1, file2, expect);
+ test_compare (out2, file1, expect);
+
+ // Compare files, input vs output.
+ test_compare (file2, out1, expect);
+ test_compare (file1, out2, expect);
+}
+
+template<class input>
+static void
+test_output (const dwarf &file1, const dwarf &file2,
+ bool two_tests, const input &in1, const input &in2, bool same)
+{
+ dwarf_output_collector c1;
+ dwarf_output_collector c2;
+ dwarf_output out1 (in1, c1);
+ dwarf_output out2 (in2, c2);
+
+ test_classes (file1, file2, out1, out2, same);
+
+ if (two_tests)
+ test_classes (in1, in2, out1, out2, same);
+}
+
+static void
+do_writer_test (dwarf &file1, dwarf &file2, bool same)
+{
+ if (test_writer & 1)
+ test_output (file1, file2, false, file1, file2, same);
+ if (test_writer & 2)
+ {
+ dwarf_edit edit1 (file1);
+ dwarf_edit edit2 (file2);
+ test_classes (file1, file2, edit1, edit2, same);
+ if (test_writer & 1)
+ test_output (file1, file2, true, edit1, edit2, same);
+ }
+}
+
+#else
+
+static inline void do_writer_test (dwarf &, dwarf &, bool) {}
+
+#endif // TEST
+
+int
+main (int argc, char *argv[])
+{
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ (void) textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ int remaining;
+ (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* We expect exactly two non-option parameters. */
+ if (unlikely (remaining + 2 != argc)
+ && unlikely (argc - remaining < 4))
+ {
+ fputs (gettext ("Invalid number of parameters.\n"), stderr);
+ argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
+ exit (1);
+ }
+ const char *const fname1 = argv[remaining++];
+ int fd1;
+ Dwarf *dw1 = open_file (fname1, &fd1);
+
+ const char *const fname2 = argv[remaining++];
+ int fd2;
+ Dwarf *dw2 = open_file (fname2, &fd2);
+
+ int result = 0;
+
+ if (dw1 == NULL || dw2 == NULL)
+ {
+ result = (dw1 == NULL) != (dw2 == NULL);
+ if (result != 0 && !quiet)
+ {
+ if (dw1 == NULL)
+ cout << "unexpectedly has DWARF";
+ else
+ cout << "has no DWARF";
+ }
+ }
+ else
+ {
+ dwarf file1 (dw1);
+ dwarf file2 (dw2);
+
+ if (remaining < argc)
+ {
+ const dwarf::debug_info_entry a = parse_offset (fname1, file1,
+ argv[remaining++]);
+
+ bool same = (quiet
+ ? quiet_compare (file1, file2, a, fname2,
+ &argv[remaining], argc - remaining)
+ : noisy_compare (file1, file2, a, fname2,
+ &argv[remaining], argc - remaining,
+ verbose));
+
+ result = !same;
+ }
+ else
+ {
+ bool same = (quiet
+ ? quiet_compare (file1, file2)
+ : noisy_compare (file1, file2, verbose));
+
+ do_writer_test (file1, file2, same);
+
+ result = !same;
+ }
+ }
+
+ dwarf_end (dw1);
+ dwarf_end (dw2);
+
+ return result;
+}
+
+
+/* Print the version information. */
+static void
+print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
+{
+ fprintf (stream, "dwarfcmp (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+ fprintf (stream, gettext ("\
+Copyright (C) %s Red Hat, Inc.\n\
+This is free software; see the source for copying conditions. There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
+"), "2009");
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *, struct argp_state *)
+{
+ switch (key)
+ {
+ case 'q':
+ quiet = true;
+ break;
+
+ case 'l':
+ verbose = true;
+ break;
+
+ case 'i':
+ missing_ok = true;
+ break;
+
+#ifdef TEST
+ case 'T':
+ ++test_writer;
+ break;
+#endif
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
diff --git a/src/dwarflint-coverage.c b/src/dwarflint-coverage.c
new file mode 100644
index 00000000..5cabe7b9
--- /dev/null
+++ b/src/dwarflint-coverage.c
@@ -0,0 +1,334 @@
+/* Pedantic checking of DWARF files.
+ Copyright (C) 2008,2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+ Written by Petr Machata <[email protected]>, 2009.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "dwarflint-coverage.h"
+#include <stdbool.h>
+#include <assert.h>
+#include <system.h>
+#include <string.h>
+#include <inttypes.h>
+
+static struct cov_range *
+coverage_find (struct coverage *cov, uint64_t start)
+{
+ assert (cov->size > 0);
+
+ size_t a = 0;
+ size_t b = cov->size;
+
+ while (a < b)
+ {
+ size_t i = (a + b) / 2;
+ struct cov_range *r = cov->ranges + i;
+
+ if (r->start > start)
+ b = i;
+ else if (r->start < start)
+ a = i + 1;
+ else
+ return r;
+ }
+
+ return cov->ranges + a;
+}
+
+void
+coverage_add (struct coverage *cov, uint64_t start, uint64_t length)
+{
+ if (length == 0)
+ return;
+
+ struct cov_range nr = (struct cov_range){start, length};
+ if (cov->size == 0)
+ {
+ REALLOC (cov, ranges);
+ cov->ranges[cov->size++] = nr;
+ return;
+ }
+
+ struct cov_range *r = coverage_find (cov, start);
+
+ struct cov_range *insert = &nr;
+ struct cov_range *coalesce = &nr;
+ struct cov_range *end = cov->ranges + cov->size;
+
+ // Coalesce with previous range?
+ struct cov_range *p = r - 1;
+ if (r > cov->ranges && coalesce->start <= p->start + p->length)
+ {
+ uint64_t coalesce_end = coalesce->start + coalesce->length;
+ if (coalesce_end > p->start + p->length)
+ {
+ p->length = coalesce_end - p->start;
+ coalesce = p;
+ }
+ else
+ coalesce = NULL;
+ insert = NULL;
+ }
+
+ // Coalesce with one or more following ranges?
+ if (coalesce != NULL && coalesce != end)
+ {
+ p = r;
+ while (p != end && coalesce->start + coalesce->length >= p->start)
+ {
+ uint64_t p_end = p->start + p->length;
+ if (p_end > coalesce->start + coalesce->length)
+ coalesce->length = p_end - coalesce->start;
+ if (insert != NULL)
+ {
+ *p = *insert;
+ insert = NULL;
+ coalesce = p;
+ assert (p == r);
+ ++r; // when doing memory moves, don't overwrite this range
+ }
+ ++p;
+ }
+ if (p > r)
+ {
+ size_t rem = cov->size - (p - cov->ranges);
+ memmove (r, p, sizeof (*cov->ranges) * rem);
+ cov->size -= p - r;
+ }
+ }
+
+ if (insert != NULL)
+ {
+ size_t rem = end - r;
+ size_t idx = r - cov->ranges;
+ REALLOC (cov, ranges);
+ r = cov->ranges + idx;
+
+ cov->size++;
+ if (rem > 0)
+ memmove (r + 1, r, sizeof (*cov->ranges) * rem);
+ *r = nr;
+ }
+}
+
+bool
+coverage_remove (struct coverage *cov, uint64_t begin, uint64_t length)
+{
+ uint64_t end = begin + length;
+ if (cov->size == 0 || begin == end)
+ return false;
+
+ struct cov_range *r = coverage_find (cov, begin);
+ struct cov_range *erase_begin = NULL, *erase_end = r; // end exclusive
+ bool overlap = false;
+
+ // Cut from previous range?
+ struct cov_range *p = r - 1;
+ if (r > cov->ranges && begin < p->start + p->length)
+ {
+ uint64_t r_end = p->start + p->length;
+ // Do we cut the beginning of the range?
+ if (begin == p->start)
+ p->length = end >= r_end ? 0 : r_end - end;
+ else
+ {
+ p->length = begin - p->start;
+ // Do we shoot a hole in that range?
+ if (end < r_end)
+ {
+ coverage_add (cov, end, r_end - end);
+ return true;
+ }
+ }
+
+ overlap = true;
+ if (p->length == 0)
+ erase_begin = p;
+ }
+
+ if (erase_begin == NULL)
+ erase_begin = r;
+
+ // Cut from next range?
+ while (r < cov->ranges + cov->size
+ && r->start < end)
+ {
+ overlap = true;
+ if (end >= r->start + r->length)
+ {
+ ++erase_end;
+ ++r;
+ }
+ else
+ {
+ uint64_t end0 = r->start + r->length;
+ r->length = end0 - end;
+ r->start = end;
+ assert (end0 == r->start + r->length);
+ }
+ }
+
+ // Did we cut out anything completely?
+ if (erase_end > erase_begin)
+ {
+ struct cov_range *cov_end = cov->ranges + cov->size;
+ size_t rem = cov_end - erase_end;
+ if (rem > 0)
+ memmove (erase_begin, erase_end, sizeof (*cov->ranges) * rem);
+ cov->size -= erase_end - erase_begin;
+ }
+
+ return overlap;
+}
+
+bool
+coverage_is_covered (struct coverage *cov, uint64_t start, uint64_t length)
+{
+ assert (length > 0);
+
+ if (cov->size == 0)
+ return false;
+
+ struct cov_range *r = coverage_find (cov, start);
+ uint64_t end = start + length;
+ if (r < cov->ranges + cov->size)
+ if (start >= r->start)
+ return end <= r->start + r->length;
+
+ if (r > cov->ranges)
+ {
+ --r;
+ return end <= r->start + r->length;
+ }
+
+ return false;
+}
+
+bool
+coverage_is_overlap (struct coverage *cov, uint64_t start, uint64_t length)
+{
+ if (length == 0 || cov->size == 0)
+ return false;
+
+ uint64_t end = start + length;
+ bool overlaps (struct cov_range *r)
+ {
+ return (start >= r->start && start < r->start + r->length)
+ || (end > r->start && end <= r->start + r->length)
+ || (start < r->start && end > r->start + r->length);
+ }
+
+ struct cov_range *r = coverage_find (cov, start);
+
+ if (r < cov->ranges + cov->size && overlaps (r))
+ return true;
+
+ if (r > cov->ranges)
+ return overlaps (r - 1);
+
+ return false;
+}
+
+bool
+coverage_find_holes (struct coverage *cov, uint64_t start, uint64_t length,
+ bool (*hole)(uint64_t start, uint64_t length, void *data),
+ void *data)
+{
+ if (length == 0)
+ return true;
+
+ if (cov->size == 0)
+ return hole (start, length, data);
+
+ uint64_t end (size_t i)
+ {
+ return cov->ranges[i].start + cov->ranges[i].length;
+ }
+
+ if (start < cov->ranges[0].start)
+ if (!hole (start, cov->ranges[0].start - start, data))
+ return false;
+
+ for (size_t i = 0; i < cov->size - 1; ++i)
+ {
+ uint64_t end_i = end (i);
+ if (!hole (end_i, cov->ranges[i+1].start - end_i, data))
+ return false;
+ }
+
+ if (start + length > end (cov->size - 1))
+ {
+ uint64_t end_last = end (cov->size - 1);
+ return hole (end_last, start + length - end_last, data);
+ }
+
+ return true;
+}
+
+bool
+coverage_find_ranges (struct coverage *cov,
+ bool (*cb)(uint64_t start, uint64_t length, void *data),
+ void *data)
+{
+ for (size_t i = 0; i < cov->size; ++i)
+ if (!cb (cov->ranges[i].start, cov->ranges[i].length, data))
+ return false;
+
+ return true;
+}
+
+void
+coverage_free (struct coverage *cov)
+{
+ free (cov->ranges);
+}
+
+struct coverage *
+coverage_clone (struct coverage *cov)
+{
+ struct coverage *ret = xmalloc (sizeof (*ret));
+ WIPE (*ret);
+ coverage_add_all (ret, cov);
+ return ret;
+}
+
+void
+coverage_add_all (struct coverage *cov, struct coverage *other)
+{
+ for (size_t i = 0; i < other->size; ++i)
+ coverage_add (cov, other->ranges[i].start, other->ranges[i].length);
+}
+
+bool
+coverage_remove_all (struct coverage *cov, struct coverage *other)
+{
+ bool ret = false;
+ for (size_t i = 0; i < other->size; ++i)
+ if (coverage_remove (cov, other->ranges[i].start, other->ranges[i].length))
+ ret = true;
+ return ret;
+}
diff --git a/src/dwarflint-coverage.h b/src/dwarflint-coverage.h
new file mode 100644
index 00000000..9a6d364c
--- /dev/null
+++ b/src/dwarflint-coverage.h
@@ -0,0 +1,66 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define REALLOC(A, BUF) \
+ do { \
+ typeof ((A)) _a = (A); \
+ if (_a->size == _a->alloc) \
+ { \
+ if (_a->alloc == 0) \
+ _a->alloc = 8; \
+ else \
+ _a->alloc *= 2; \
+ _a->BUF = xrealloc (_a->BUF, \
+ sizeof (*_a->BUF) * _a->alloc); \
+ } \
+ } while (0)
+
+#define WIPE(OBJ) memset (&OBJ, 0, sizeof (OBJ))
+
+/* Functions and data structures for handling of address range
+ coverage. We use that to find holes of unused bytes in DWARF
+ string table. */
+
+struct cov_range
+{
+ uint64_t start;
+ uint64_t length;
+};
+
+struct coverage
+{
+ struct cov_range *ranges;
+ size_t size;
+ size_t alloc;
+};
+
+struct coverage *coverage_clone (struct coverage *cov) __attribute__ ((malloc));
+void coverage_free (struct coverage *cov);
+
+void coverage_add (struct coverage *cov, uint64_t start, uint64_t length);
+void coverage_add_all (struct coverage *cov, struct coverage *other);
+
+/* Returns true if something was actually removed, false if whole
+ range falls into hole in coverage. */
+bool coverage_remove (struct coverage *cov, uint64_t start, uint64_t length);
+
+/* Returns true if something was actually removed, false if whole
+ range falls into hole in coverage. */
+bool coverage_remove_all (struct coverage *cov, struct coverage *other);
+
+/* Returns true if whole range ADDRESS/LENGTH is covered by COV.
+ LENGTH may not be zero. */
+bool coverage_is_covered (struct coverage *cov, uint64_t start, uint64_t length);
+
+/* Returns true if at least some of the range ADDRESS/LENGTH is
+ covered by COV. Zero-LENGTH range never overlaps. */
+bool coverage_is_overlap (struct coverage *cov, uint64_t start, uint64_t length);
+
+bool coverage_find_holes (struct coverage *cov, uint64_t start, uint64_t length,
+ bool (*cb)(uint64_t start, uint64_t length, void *data),
+ void *data);
+bool coverage_find_ranges (struct coverage *cov,
+ bool (*cb)(uint64_t start, uint64_t length, void *data),
+ void *data);
diff --git a/src/dwarflint-expected-at.cc b/src/dwarflint-expected-at.cc
new file mode 100644
index 00000000..3c282928
--- /dev/null
+++ b/src/dwarflint-expected-at.cc
@@ -0,0 +1,760 @@
+/* Pedantic checking of DWARF files.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+ Written by Petr Machata <[email protected]>, 2009.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include <config.h>
+#include "dwarflint-expected.hh"
+#include "../libdw/dwarf.h"
+
+expected_at_map::expected_at_map ()
+{
+ std::set <int> at_set_decl;
+ at_set_decl.insert (DW_AT_decl_column);
+ at_set_decl.insert (DW_AT_decl_file);
+ at_set_decl.insert (DW_AT_decl_line);
+
+ m_map [DW_TAG_access_declaration]
+ .optional (at_set_decl)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map[DW_TAG_array_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_bit_stride)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_ordering)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_specification)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_base_type]
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_binary_scale)
+ .optional (DW_AT_bit_offset)
+ .optional (DW_AT_bit_size)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_decimal_scale)
+ .optional (DW_AT_decimal_sign)
+ .optional (DW_AT_description)
+ .optional (DW_AT_digit_count)
+ .optional (DW_AT_encoding)
+ .optional (DW_AT_endianity)
+ .optional (DW_AT_name)
+ .optional (DW_AT_picture_string)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_small)
+ ;
+
+ m_map [DW_TAG_catch_block]
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_high_pc)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_ranges)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map [DW_TAG_class_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_specification)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_visibility)
+ .optional (DW_AT_containing_type) // XXX added to reflect reality
+ ;
+
+ m_map [DW_TAG_common_block]
+ .optional (at_set_decl)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_location)
+ .optional (DW_AT_name)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_common_inclusion]
+ .optional (at_set_decl)
+ .optional (DW_AT_common_reference)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_compile_unit]
+ .optional (DW_AT_base_types)
+ .optional (DW_AT_comp_dir)
+ .optional (DW_AT_identifier_case)
+ .optional (DW_AT_high_pc)
+ .optional (DW_AT_language)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_macro_info)
+ .optional (DW_AT_name)
+ .optional (DW_AT_producer)
+ .optional (DW_AT_ranges)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_stmt_list)
+ .optional (DW_AT_use_UTF8)
+ .optional (DW_AT_entry_pc) // XXX added to reflect reality
+ ;
+
+ m_map [DW_TAG_condition]
+ .optional (at_set_decl)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map [DW_TAG_const_type]
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ ;
+
+ m_map [DW_TAG_constant]
+ .optional (at_set_decl)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_const_value)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_endianity)
+ .optional (DW_AT_external)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_dwarf_procedure]
+ .optional (DW_AT_location)
+ ;
+
+ m_map [DW_TAG_entry_point]
+ .optional (DW_AT_address_class)
+ .optional (DW_AT_description)
+ .optional (DW_AT_frame_base)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_name)
+ .optional (DW_AT_return_addr)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_static_link)
+ .optional (DW_AT_type)
+ ;
+
+ m_map [DW_TAG_enumeration_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_bit_stride)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_byte_stride)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_specification)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_enumerator]
+ .optional (at_set_decl)
+ .optional (DW_AT_const_value)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map [DW_TAG_file_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_formal_parameter]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_artificial)
+ .optional (DW_AT_const_value)
+ .optional (DW_AT_default_value)
+ .optional (DW_AT_description)
+ .optional (DW_AT_endianity)
+ .optional (DW_AT_is_optional)
+ .optional (DW_AT_location)
+ .optional (DW_AT_name)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ .optional (DW_AT_variable_parameter)
+ ;
+
+ m_map [DW_TAG_friend]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_friend)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map [DW_TAG_imported_declaration]
+ .optional (at_set_decl)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_description)
+ .optional (DW_AT_import)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_start_scope)
+ ;
+
+ m_map [DW_TAG_imported_module]
+ .optional (at_set_decl)
+ .optional (DW_AT_import)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_start_scope)
+ ;
+
+ m_map [DW_TAG_imported_unit]
+ .required (DW_AT_import)
+ ;
+
+ m_map [DW_TAG_inheritance]
+ .optional (at_set_decl)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_data_member_location)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ .optional (DW_AT_virtuality)
+ ;
+
+ m_map [DW_TAG_inlined_subroutine]
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_call_column)
+ .optional (DW_AT_call_file)
+ .optional (DW_AT_call_line)
+ .optional (DW_AT_entry_pc)
+ .optional (DW_AT_high_pc)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_ranges)
+ .optional (DW_AT_return_addr)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_trampoline)
+ ;
+
+ m_map [DW_TAG_interface_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_start_scope)
+ ;
+
+ m_map [DW_TAG_label]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_description)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_name)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map [DW_TAG_lexical_block]
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_description)
+ .optional (DW_AT_high_pc)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_name)
+ .optional (DW_AT_ranges)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map [DW_TAG_member]
+ .optional (at_set_decl)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_bit_offset)
+ .optional (DW_AT_bit_size)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_data_member_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_mutable)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ .optional (DW_AT_MIPS_linkage_name) // xxx
+ .optional (DW_AT_external) // xxx
+ .optional (DW_AT_const_value) // xxx
+ .optional (DW_AT_artificial) // xxx
+ ;
+
+ m_map [DW_TAG_module]
+ .optional (at_set_decl)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_entry_pc)
+ .optional (DW_AT_high_pc)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_name)
+ .optional (DW_AT_priority)
+ .optional (DW_AT_ranges)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_specification)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_namelist]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_namelist_item]
+ .optional (at_set_decl)
+ .optional (DW_AT_namelist_item)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map [DW_TAG_namespace]
+ .optional (at_set_decl)
+ .optional (DW_AT_description)
+ .optional (DW_AT_extension)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_start_scope)
+ ;
+
+ m_map [DW_TAG_packed_type]
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ ;
+
+ m_map [DW_TAG_partial_unit]
+ .optional (DW_AT_base_types)
+ .optional (DW_AT_comp_dir)
+ .optional (DW_AT_description)
+ .optional (DW_AT_identifier_case)
+ .optional (DW_AT_high_pc)
+ .optional (DW_AT_language)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_macro_info)
+ .optional (DW_AT_name)
+ .optional (DW_AT_producer)
+ .optional (DW_AT_ranges)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_stmt_list)
+ .optional (DW_AT_use_UTF8)
+ ;
+
+ m_map [DW_TAG_pointer_type]
+ .optional (DW_AT_address_class)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_specification)
+ .optional (DW_AT_type)
+ .optional (DW_AT_byte_size) // XXX added to reflect reality
+ ;
+
+ m_map [DW_TAG_ptr_to_member_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_address_class)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_containing_type)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ .optional (DW_AT_use_location)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_reference_type]
+ .optional (DW_AT_address_class)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ .optional (DW_AT_byte_size) // XXX added to reflect reality
+ ;
+
+ m_map [DW_TAG_restrict_type]
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ ;
+
+ m_map [DW_TAG_set_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_shared_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_count)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ ;
+
+ m_map [DW_TAG_string_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_string_length)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_structure_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_specification)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_visibility)
+ .optional (DW_AT_containing_type) // XXX added to reflect reality
+ ;
+
+ m_map [DW_TAG_subprogram]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_address_class)
+ .optional (DW_AT_artificial)
+ .optional (DW_AT_calling_convention)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_elemental)
+ .optional (DW_AT_entry_pc)
+ .optional (DW_AT_explicit)
+ .optional (DW_AT_external)
+ .optional (DW_AT_frame_base)
+ .optional (DW_AT_high_pc)
+ .optional (DW_AT_inline)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_name)
+ .optional (DW_AT_object_pointer)
+ .optional (DW_AT_prototyped)
+ .optional (DW_AT_pure)
+ .optional (DW_AT_ranges)
+ .optional (DW_AT_recursive)
+ .optional (DW_AT_return_addr)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_specification)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_static_link)
+ .optional (DW_AT_trampoline)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ .optional (DW_AT_virtuality)
+ .optional (DW_AT_vtable_elem_location)
+ .optional (DW_AT_MIPS_linkage_name) // XXX added to reflect reality
+ .optional (DW_AT_containing_type) // XXX added to reflect reality
+ ;
+
+ m_map [DW_TAG_subrange_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_bit_stride)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_byte_stride)
+ .optional (DW_AT_count)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_lower_bound)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_threads_scaled)
+ .optional (DW_AT_type)
+ .optional (DW_AT_upper_bound)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_subroutine_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_address_class)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_prototyped)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_template_type_parameter]
+ .optional (at_set_decl)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ ;
+
+ m_map [DW_TAG_template_value_parameter ]
+ .optional (at_set_decl)
+ .optional (DW_AT_const_value)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ ;
+
+ m_map [DW_TAG_thrown_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ ;
+
+ m_map [DW_TAG_try_block]
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_high_pc)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_ranges)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map [DW_TAG_typedef]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_union_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_byte_size)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_specification)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_visibility)
+ ;
+
+ m_map [DW_TAG_unspecified_parameters]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_artificial)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map [DW_TAG_unspecified_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_description)
+ .optional (DW_AT_name)
+ ;
+
+ m_map [DW_TAG_variable]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_const_value)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_description)
+ .optional (DW_AT_endianity)
+ .optional (DW_AT_external)
+ .optional (DW_AT_location)
+ .optional (DW_AT_name)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_specification)
+ .optional (DW_AT_start_scope)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ .optional (DW_AT_MIPS_linkage_name) // XXX added to reflect reality
+ .optional (DW_AT_artificial) // XXX added to reflect reality
+ ;
+
+ m_map [DW_TAG_variant]
+ .optional (at_set_decl)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_discr_list)
+ .optional (DW_AT_discr_value)
+ .optional (DW_AT_sibling)
+ ;
+
+ m_map [DW_TAG_variant_part]
+ .optional (at_set_decl)
+ .optional (DW_AT_abstract_origin)
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_discr)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ ;
+
+ m_map [DW_TAG_volatile_type]
+ .optional (at_set_decl)
+ .optional (DW_AT_allocated)
+ .optional (DW_AT_associated)
+ .optional (DW_AT_data_location)
+ .optional (DW_AT_name)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ ;
+
+ m_map [DW_TAG_with_stmt]
+ .optional (DW_AT_accessibility)
+ .optional (DW_AT_address_class)
+ .optional (DW_AT_declaration)
+ .optional (DW_AT_high_pc)
+ .optional (DW_AT_location)
+ .optional (DW_AT_low_pc)
+ .optional (DW_AT_ranges)
+ .optional (DW_AT_segment)
+ .optional (DW_AT_sibling)
+ .optional (DW_AT_type)
+ .optional (DW_AT_visibility)
+ ;
+}
diff --git a/src/dwarflint-expected.hh b/src/dwarflint-expected.hh
new file mode 100644
index 00000000..bcb0de5b
--- /dev/null
+++ b/src/dwarflint-expected.hh
@@ -0,0 +1,108 @@
+/* Pedantic checking of DWARF files.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+ Written by Petr Machata <[email protected]>, 2009.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include <map>
+#include <set>
+#include <stdexcept>
+#include <sstream>
+#include <cassert>
+
+#include "c++/dwarf"
+
+enum optionality
+{
+ opt_optional = 0, // may or may not be present
+ opt_required, // bogus if missing
+ opt_expected, // suspicious if missing
+};
+
+template <class T>
+std::string
+string_of (T x)
+{
+ std::ostringstream o;
+ o << x;
+ return o.str();
+}
+
+struct expected_set
+{
+ typedef std::map <int, optionality> expectation_map;
+
+private:
+ expectation_map m_map;
+
+public:
+#define DEF_FILLER(WHAT) \
+ expected_set &WHAT (int attribute) \
+ { \
+ assert (m_map.find (attribute) == m_map.end ()); \
+ m_map.insert (std::make_pair (attribute, opt_##WHAT)); \
+ return *this; \
+ } \
+ expected_set &WHAT (std::set <int> const &attributes) \
+ { \
+ for (std::set <int>::const_iterator it = attributes.begin (); \
+ it != attributes.end (); ++it) \
+ WHAT (*it); \
+ return *this; \
+ }
+
+ DEF_FILLER (required)
+ DEF_FILLER (expected)
+ DEF_FILLER (optional)
+#undef DEF_FILLER
+
+ expectation_map const &map () const
+ {
+ return m_map;
+ }
+};
+
+class expected_map
+{
+ typedef std::map <int, expected_set> expected_map_t;
+
+protected:
+ expected_map_t m_map;
+ expected_map () {}
+
+public:
+ expected_set::expectation_map const &map (int tag) const
+ {
+ expected_map_t::const_iterator it = m_map.find (tag);
+ if (it == m_map.end ())
+ throw std::runtime_error ("Unknown tag "
+ + elfutils::dwarf::tags::identifier (tag));
+ return it->second.map ();
+ }
+};
+
+struct expected_at_map
+ : public expected_map
+{
+ expected_at_map ();
+};
diff --git a/src/dwarflint-hl.cc b/src/dwarflint-hl.cc
new file mode 100644
index 00000000..25938467
--- /dev/null
+++ b/src/dwarflint-hl.cc
@@ -0,0 +1,300 @@
+/* Pedantic checking of DWARF files.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+ Written by Petr Machata <[email protected]>, 2009.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <iostream>
+#include <set>
+#include <algorithm>
+#include <cinttypes>
+#include <cstdarg>
+#include <cassert>
+#include <iterator>
+
+#include "dwarflint.h"
+#include "dwarflint-expected.hh"
+#include "dwarfstrings.h"
+#include "c++/dwarf"
+#include "../libdw/libdwP.h"
+#include "../libdw/c++/dwarf-knowledge.cc"
+
+namespace
+{
+ message_category cat (message_category c1,
+ message_category c2,
+ message_category c3 = mc_none)
+ {
+ return static_cast<message_category> (c1 | c2 | c3);
+ }
+}
+
+struct hl_ctx
+{
+ Dwarf *dwarf;
+ elfutils::dwarf dw;
+
+ hl_ctx (Elf *elf)
+ : dwarf (dwarf_begin_elf (elf, DWARF_C_READ, NULL))
+ , dw (dwarf)
+ {
+ // See if we can iterate compile units. If not, this throws an
+ // exception that gets caught in the C wrapper below.
+ dw.compile_units ().begin ();
+ }
+
+ ~hl_ctx ()
+ {
+ dwarf_end (dwarf);
+ }
+};
+
+hl_ctx *
+hl_ctx_new (Elf *elf)
+{
+ try
+ {
+ return new hl_ctx (elf);
+ }
+ catch (std::runtime_error &exc)
+ {
+ wr_error (NULL, "Cannot initialize high-level checking: %s.\n",
+ exc.what ());
+ return NULL;
+ }
+}
+
+void
+hl_ctx_delete (hl_ctx *hlctx)
+{
+ delete hlctx;
+}
+
+static const expected_at_map expected_at;
+//static const expected_children_map expected_children;
+
+bool
+check_matching_ranges (hl_ctx *hlctx)
+{
+ try
+ {
+ struct where where_ref = WHERE (sec_info, NULL);
+ struct where where_ar = WHERE (sec_aranges, NULL);
+ where_ar.ref = &where_ref;
+ struct where where_r = WHERE (sec_ranges, NULL);
+ where_r.ref = &where_ref;
+ char buf[128];
+
+ const elfutils::dwarf::aranges_map &aranges = hlctx->dw.aranges ();
+ for (elfutils::dwarf::aranges_map::const_iterator i = aranges.begin ();
+ i != aranges.end (); ++i)
+ {
+ const elfutils::dwarf::compile_unit &cu = i->first;
+ where_reset_1 (&where_ref, 0);
+ where_reset_2 (&where_ref, cu.offset ());
+
+ std::set<elfutils::dwarf::ranges::key_type>
+ cu_aranges = i->second,
+ cu_ranges = cu.ranges ();
+
+ typedef std::vector <elfutils::dwarf::arange_list::value_type>
+ range_vec;
+ range_vec missing;
+ std::back_insert_iterator <range_vec> i_missing (missing);
+
+ std::set_difference (cu_aranges.begin (), cu_aranges.end (),
+ cu_ranges.begin (), cu_ranges.end (),
+ i_missing);
+
+ for (range_vec::iterator it = missing.begin ();
+ it != missing.end (); ++it)
+ wr_message (cat (mc_ranges, mc_aranges, mc_impact_3), &where_r,
+ ": missing range %s, present in .debug_aranges.\n",
+ range_fmt (buf, sizeof buf, it->first, it->second));
+
+ missing.clear ();
+ std::set_difference (cu_ranges.begin (), cu_ranges.end (),
+ cu_aranges.begin (), cu_aranges.end (),
+ i_missing);
+
+ for (range_vec::iterator it = missing.begin ();
+ it != missing.end (); ++it)
+ wr_message (cat (mc_ranges, mc_aranges, mc_impact_3), &where_ar,
+ ": missing range %s, present in .debug_ranges.\n",
+ range_fmt (buf, sizeof buf, it->first, it->second));
+ }
+
+ return true;
+ }
+ // XXX more specific class when <dwarf> has it
+ catch (std::runtime_error &exc)
+ {
+ wr_error (NULL, "Error while checking matching ranges: %s.\n",
+ exc.what ());
+ return false;
+ }
+}
+
+struct name_extractor {
+ int operator () (elfutils::dwarf::attribute const &at) {
+ return at.first;
+ }
+} extract_name;
+
+std::ostream &
+operator << (std::ostream &o, elfutils::dwarf::value_space vs)
+{
+ using namespace elfutils;
+ switch (vs)
+ {
+ case dwarf::VS_flag: return o << "flag";
+ case dwarf::VS_dwarf_constant: return o << "dwarf_constant";
+ case dwarf::VS_discr_list: return o << "discr_list";
+ case dwarf::VS_reference: return o << "reference";
+ case dwarf::VS_lineptr: return o << "lineptr";
+ case dwarf::VS_macptr: return o << "macptr";
+ case dwarf::VS_rangelistptr: return o << "rangelistptr";
+ case dwarf::VS_identifier: return o << "identifier";
+ case dwarf::VS_string: return o << "string";
+ case dwarf::VS_source_file: return o << "source_file";
+ case dwarf::VS_source_line: return o << "source_line";
+ case dwarf::VS_source_column: return o << "source_column";
+ case dwarf::VS_address: return o << "address";
+ case dwarf::VS_constant: return o << "constant";
+ case dwarf::VS_location: return o << "location";
+ };
+
+ abort ();
+}
+
+static void
+recursively_validate (elfutils::dwarf::compile_unit const &cu,
+ elfutils::dwarf::debug_info_entry const &parent)
+{
+ struct where where = WHERE (sec_info, NULL);
+ where_reset_1 (&where, cu.offset ());
+ where_reset_2 (&where, parent.offset ());
+
+ int parent_tag = parent.tag ();
+
+ // Set of attributes of this DIE.
+ std::set <int> attributes;
+ std::transform (parent.attributes ().begin (),
+ parent.attributes ().end (),
+ std::inserter (attributes, attributes.end ()),
+ extract_name);
+
+ // Attributes that we expect at this DIE.
+ expected_set::expectation_map const &expect
+ = expected_at.map (parent_tag);
+
+ // Check missing attributes.
+ for (expected_set::expectation_map::const_iterator jt
+ = expect.begin (); jt != expect.end (); ++jt)
+ {
+ std::set <int>::iterator kt = attributes.find (jt->first);
+ if (kt == attributes.end ())
+ switch (jt->second)
+ {
+ case opt_required:
+ wr_message (cat (mc_impact_4, mc_info), &where,
+ ": %s lacks required attribute %s.\n",
+ dwarf_tag_string (parent_tag),
+ dwarf_attr_string (jt->first));
+ break;
+
+ case opt_expected:
+ wr_message (cat (mc_impact_2, mc_info), &where,
+ ": %s should contain attribute %s.\n",
+ dwarf_tag_string (parent_tag),
+ dwarf_attr_string (jt->first));
+ case opt_optional:
+ break;
+ };
+ }
+
+ // Check present attributes for expected-ness, and validate value space.
+ for (elfutils::dwarf::debug_info_entry::attributes_type::const_iterator jt
+ = parent.attributes ().begin (), jte = parent.attributes ().end ();
+ jt != jte; ++jt)
+ {
+ unsigned name = extract_name (*jt);
+
+ expected_set::expectation_map::const_iterator kt = expect.find (name);
+ if (kt == expect.end ())
+ wr_message (cat (mc_impact_3, mc_info), &where,
+ ": DIE \"%s\" has attribute \"%s\", which is not expected.\n",
+ dwarf_tag_string (parent_tag),
+ dwarf_attr_string (name));
+ try
+ {
+ unsigned exp_vs = expected_value_space (name, parent_tag);
+ elfutils::dwarf::value_space vs = (*jt).second.what_space ();
+ if ((exp_vs & (1U << vs)) == 0)
+ wr_message (cat (mc_impact_3, mc_info), &where,
+ ": in DIE \"%s\", attribute \"%s\" has value of unexpected type \"%u\".\n",
+ dwarf_tag_string (parent_tag),
+ dwarf_attr_string (name),
+ vs);
+ }
+ // XXX more specific class when <dwarf> has it
+ catch (...)
+ {
+ wr_message (cat (mc_impact_4, mc_info, mc_error), &where,
+ ": in DIE \"%s\", couldn't obtain type of attribute \"%s\".\n",
+ dwarf_tag_string (parent_tag),
+ dwarf_attr_string (name));
+ }
+ }
+
+ // Check children recursively.
+ elfutils::dwarf::debug_info_entry::children_type const &children
+ = parent.children ();
+ for (elfutils::dwarf::debug_info_entry::children_type::const_iterator jt
+ = children.begin (); jt != children.end (); ++jt)
+ recursively_validate (cu, *jt);
+}
+
+bool
+check_expected_trees (hl_ctx *hlctx)
+{
+ try
+ {
+ class elfutils::dwarf::compile_units const &cus = hlctx->dw.compile_units ();
+ for (elfutils::dwarf::compile_units::const_iterator it = cus.begin ();
+ it != cus.end (); ++it)
+ recursively_validate (*it, *it);
+ return true;
+ }
+ // XXX more specific class when <dwarf> has it
+ catch (std::runtime_error &exc)
+ {
+ wr_error (NULL, "Error while checking expected trees: %s.\n",
+ exc.what ());
+ return false;
+ }
+}
diff --git a/src/dwarflint-readctx.c b/src/dwarflint-readctx.c
new file mode 100644
index 00000000..2a99254a
--- /dev/null
+++ b/src/dwarflint-readctx.c
@@ -0,0 +1,317 @@
+/* Pedantic checking of DWARF files.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+ Written by Petr Machata <[email protected]>, 2009.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include "dwarflint-readctx.h"
+#include "dwarflint.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <byteswap.h>
+
+/* read_Xubyte_* is basically cut'n'paste from memory-access.h. */
+union unaligned
+ {
+ void *p;
+ uint16_t u2;
+ uint32_t u4;
+ uint64_t u8;
+ int16_t s2;
+ int32_t s4;
+ int64_t s8;
+ } __attribute__ ((packed));
+
+static uint16_t
+read_2ubyte_unaligned (struct elf_file *file, const void *p)
+{
+ const union unaligned *up = p;
+ if (file->other_byte_order)
+ return bswap_16 (up->u2);
+ return up->u2;
+}
+
+/* Prefix with dwarflint_ for export, so that it doesn't get confused
+ with functions and macros in memory-access.h. */
+uint32_t
+dwarflint_read_4ubyte_unaligned (struct elf_file *file, const void *p)
+{
+ const union unaligned *up = p;
+ if (file->other_byte_order)
+ return bswap_32 (up->u4);
+ return up->u4;
+}
+
+uint64_t
+dwarflint_read_8ubyte_unaligned (struct elf_file *file, const void *p)
+{
+ const union unaligned *up = p;
+ if (file->other_byte_order)
+ return bswap_64 (up->u8);
+ return up->u8;
+}
+
+
+#define read_2ubyte_unaligned_inc(Dbg, Addr) \
+ ({ uint16_t t_ = read_2ubyte_unaligned (Dbg, Addr); \
+ Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2); \
+ t_; })
+
+#define read_4ubyte_unaligned_inc(Dbg, Addr) \
+ ({ uint32_t t_ = dwarflint_read_4ubyte_unaligned (Dbg, Addr); \
+ Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 4); \
+ t_; })
+
+#define read_8ubyte_unaligned_inc(Dbg, Addr) \
+ ({ uint64_t t_ = dwarflint_read_8ubyte_unaligned (Dbg, Addr); \
+ Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8); \
+ t_; })
+
+
+
+void
+read_ctx_init (struct read_ctx *ctx, struct elf_file *file, Elf_Data *data)
+{
+ if (data == NULL)
+ abort ();
+
+ ctx->file = file;
+ ctx->data = data;
+ ctx->begin = data->d_buf;
+ ctx->end = data->d_buf + data->d_size;
+ ctx->ptr = data->d_buf;
+}
+
+bool
+read_ctx_init_sub (struct read_ctx *ctx, struct read_ctx *parent,
+ const unsigned char *begin, const unsigned char *end)
+{
+ if (parent == NULL)
+ abort ();
+
+ if (begin < parent->begin
+ || end > parent->end)
+ return false;
+
+ ctx->file = parent->file;
+ ctx->data = parent->data;
+ ctx->begin = begin;
+ ctx->end = end;
+ ctx->ptr = begin;
+ return true;
+}
+
+uint64_t
+read_ctx_get_offset (struct read_ctx *ctx)
+{
+ assert (ctx->ptr >= ctx->begin);
+ return (uint64_t)(ctx->ptr - ctx->begin);
+}
+
+bool
+read_ctx_need_data (struct read_ctx *ctx, size_t length)
+{
+ const unsigned char *ptr = ctx->ptr + length;
+ return ptr <= ctx->end && (length == 0 || ptr > ctx->ptr);
+}
+
+bool
+read_ctx_read_ubyte (struct read_ctx *ctx, unsigned char *ret)
+{
+ if (!read_ctx_need_data (ctx, 1))
+ return false;
+ if (ret != NULL)
+ *ret = *ctx->ptr;
+ ctx->ptr++;
+ return true;
+}
+
+int
+read_ctx_read_uleb128 (struct read_ctx *ctx, uint64_t *ret)
+{
+ uint64_t result = 0;
+ int shift = 0;
+ int size = 8 * sizeof (result);
+ bool zero_tail = false;
+
+ while (1)
+ {
+ uint8_t byte;
+ if (!read_ctx_read_ubyte (ctx, &byte))
+ return -1;
+
+ uint8_t payload = byte & 0x7f;
+ zero_tail = payload == 0 && shift > 0;
+ result |= (uint64_t)payload << shift;
+ shift += 7;
+ if (shift > size && byte != 0x1)
+ return -1;
+ if ((byte & 0x80) == 0)
+ break;
+ }
+
+ if (ret != NULL)
+ *ret = result;
+ return zero_tail ? 1 : 0;
+}
+
+int
+read_ctx_read_sleb128 (struct read_ctx *ctx, int64_t *ret)
+{
+ int64_t result = 0;
+ int shift = 0;
+ int size = 8 * sizeof (result);
+ bool zero_tail = false;
+ bool sign = false;
+
+ while (1)
+ {
+ uint8_t byte;
+ if (!read_ctx_read_ubyte (ctx, &byte))
+ return -1;
+
+ uint8_t payload = byte & 0x7f;
+ zero_tail = shift > 0 && ((payload == 0x7f && sign)
+ || (payload == 0 && !sign));
+ sign = (byte & 0x40) != 0; /* Set sign for rest of loop & next round. */
+ result |= (int64_t)payload << shift;
+ shift += 7;
+ if ((byte & 0x80) == 0)
+ {
+ if (shift < size && sign)
+ result |= -((int64_t)1 << shift);
+ break;
+ }
+ if (shift > size)
+ return -1;
+ }
+
+ if (ret != NULL)
+ *ret = result;
+ return zero_tail ? 1 : 0;
+}
+
+bool
+read_ctx_read_2ubyte (struct read_ctx *ctx, uint16_t *ret)
+{
+ if (!read_ctx_need_data (ctx, 2))
+ return false;
+ uint16_t val = read_2ubyte_unaligned_inc (ctx->file, ctx->ptr);
+ if (ret != NULL)
+ *ret = val;
+ return true;
+}
+
+bool
+read_ctx_read_4ubyte (struct read_ctx *ctx, uint32_t *ret)
+{
+ if (!read_ctx_need_data (ctx, 4))
+ return false;
+ uint32_t val = read_4ubyte_unaligned_inc (ctx->file, ctx->ptr);
+ if (ret != NULL)
+ *ret = val;
+ return true;
+}
+
+bool
+read_ctx_read_8ubyte (struct read_ctx *ctx, uint64_t *ret)
+{
+ if (!read_ctx_need_data (ctx, 8))
+ return false;
+ uint64_t val = read_8ubyte_unaligned_inc (ctx->file, ctx->ptr);
+ if (ret != NULL)
+ *ret = val;
+ return true;
+}
+
+bool
+read_ctx_read_offset (struct read_ctx *ctx, bool dwarf64, uint64_t *ret)
+{
+ if (dwarf64)
+ return read_ctx_read_8ubyte (ctx, ret);
+
+ uint32_t v;
+ if (!read_ctx_read_4ubyte (ctx, &v))
+ return false;
+
+ if (ret != NULL)
+ *ret = (uint64_t)v;
+ return true;
+}
+
+bool
+read_ctx_read_var (struct read_ctx *ctx, int width, uint64_t *ret)
+{
+ switch (width)
+ {
+ case 4:
+ case 8:
+ return read_ctx_read_offset (ctx, width == 8, ret);
+ case 2:
+ {
+ uint16_t val;
+ if (!read_ctx_read_2ubyte (ctx, &val))
+ return false;
+ *ret = val;
+ return true;
+ }
+ case 1:
+ {
+ uint8_t val;
+ if (!read_ctx_read_ubyte (ctx, &val))
+ return false;
+ *ret = val;
+ return true;
+ }
+ default:
+ return false;
+ };
+}
+
+const char *
+read_ctx_read_str (struct read_ctx *ctx)
+{
+ const char *ret = (const char *)ctx->ptr;
+ uint8_t byte;
+ do
+ if (!read_ctx_read_ubyte (ctx, &byte))
+ return NULL;
+ while (byte != 0);
+ return ret;
+}
+
+bool
+read_ctx_skip (struct read_ctx *ctx, uint64_t len)
+{
+ if (!read_ctx_need_data (ctx, len))
+ return false;
+ ctx->ptr += len;
+ return true;
+}
+
+bool
+read_ctx_eof (struct read_ctx *ctx)
+{
+ return !read_ctx_need_data (ctx, 1);
+}
diff --git a/src/dwarflint-readctx.h b/src/dwarflint-readctx.h
new file mode 100644
index 00000000..4adcdf56
--- /dev/null
+++ b/src/dwarflint-readctx.h
@@ -0,0 +1,47 @@
+#ifndef dwarflint_readctx_h
+#define dwarflint_readctx_h
+
+#include <stdbool.h>
+#include "../libelf/libelf.h"
+
+/* Functions and data structures related to bounds-checked
+ reading. */
+
+struct read_ctx
+{
+ struct elf_file *file;
+ Elf_Data *data;
+ const unsigned char *ptr;
+ const unsigned char *begin;
+ const unsigned char *end;
+};
+
+uint32_t dwarflint_read_4ubyte_unaligned (struct elf_file *file,
+ const void *p);
+uint64_t dwarflint_read_8ubyte_unaligned (struct elf_file *file,
+ const void *p);
+
+
+void read_ctx_init (struct read_ctx *ctx,
+ struct elf_file *file,
+ Elf_Data *data);
+bool read_ctx_init_sub (struct read_ctx *ctx,
+ struct read_ctx *parent,
+ const unsigned char *begin,
+ const unsigned char *end);
+uint64_t read_ctx_get_offset (struct read_ctx *ctx);
+bool read_ctx_need_data (struct read_ctx *ctx, size_t length);
+bool read_ctx_read_ubyte (struct read_ctx *ctx, unsigned char *ret);
+int read_ctx_read_uleb128 (struct read_ctx *ctx, uint64_t *ret);
+int read_ctx_read_sleb128 (struct read_ctx *ctx, int64_t *ret);
+bool read_ctx_read_2ubyte (struct read_ctx *ctx, uint16_t *ret);
+bool read_ctx_read_4ubyte (struct read_ctx *ctx, uint32_t *ret);
+bool read_ctx_read_8ubyte (struct read_ctx *ctx, uint64_t *ret);
+bool read_ctx_read_offset (struct read_ctx *ctx, bool dwarf64,
+ uint64_t *ret);
+bool read_ctx_read_var (struct read_ctx *ctx, int width, uint64_t *ret);
+const char *read_ctx_read_str (struct read_ctx *ctx);
+bool read_ctx_skip (struct read_ctx *ctx, uint64_t len);
+bool read_ctx_eof (struct read_ctx *ctx);
+
+#endif
diff --git a/src/dwarflint.c b/src/dwarflint.c
new file mode 100644
index 00000000..a2a42903
--- /dev/null
+++ b/src/dwarflint.c
@@ -0,0 +1,5238 @@
+/* Pedantic checking of DWARF files.
+ Copyright (C) 2008,2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+ Written by Petr Machata <[email protected]>, 2008.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <argp.h>
+#include <assert.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <system.h>
+#include <unistd.h>
+
+#include "../libdw/dwarf.h"
+#include "../libdw/known-dwarf.h"
+#include "../libebl/libebl.h"
+#include "dwarfstrings.h"
+#include "dwarflint.h"
+#include "dwarflint-readctx.h"
+
+/* Bug report address. */
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+
+#define ARGP_strict 300
+#define ARGP_gnu 301
+#define ARGP_tolerant 302
+#define ARGP_ref 303
+#define ARGP_nohl 304
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { "strict", ARGP_strict, NULL, 0,
+ N_("Be extremely strict, flag level 2 features."), 0 },
+ { "quiet", 'q', NULL, 0, N_("Do not print anything if successful"), 0 },
+ { "ignore-missing", 'i', NULL, 0,
+ N_("Don't complain if files have no DWARF at all"), 0 },
+ { "gnu", ARGP_gnu, NULL, 0,
+ N_("Binary has been created with GNU toolchain and is therefore known to be \
+broken in certain ways"), 0 },
+ { "tolerant", ARGP_tolerant, NULL, 0,
+ N_("Don't output certain common error messages"), 0 },
+ { "ref", ARGP_ref, NULL, 0,
+ N_("When validating .debug_loc and .debug_ranges, display information about \
+the DIE referring to the entry in consideration"), 0 },
+ { "nohl", ARGP_nohl, NULL, 0,
+ N_("Don't run high-level tests"), 0 },
+ { "verbose", 'v', NULL, 0,
+ N_("Be verbose"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("\
+Pedantic checking of DWARF stored in ELF files.");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("FILE...");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+/* If true, we accept silently files without debuginfo. */
+static bool tolerate_nodebug = false;
+
+static void process_file (Elf *elf, const char *fname, bool only_one);
+
+struct message_term
+{
+ /* Given a term like A && !B && C && !D, we decompose it thus: */
+ enum message_category positive; /* non-zero bits for plain predicates */
+ enum message_category negative; /* non-zero bits for negated predicates */
+};
+
+struct message_criteria
+{
+ struct message_term *terms;
+ size_t size;
+ size_t alloc;
+};
+
+static bool
+message_accept (struct message_criteria *cri, enum message_category cat)
+{
+ for (size_t i = 0; i < cri->size; ++i)
+ {
+ struct message_term *t = cri->terms + i;
+ if ((t->positive & cat) == t->positive
+ && (t->negative & cat) == 0)
+ return true;
+ }
+ return false;
+}
+
+static const char *
+message_term_str (struct message_term *t)
+{
+ static char *names[] = {
+#define MC(CAT, ID) [ID] = #CAT,
+ MESSAGE_CATEGORIES
+#undef MC
+ };
+
+ unsigned max = 0;
+#define MC(CAT, ID) max = ID;
+ MESSAGE_CATEGORIES
+#undef MC
+
+ static char buf[512];
+ char *ptr = buf;
+ ptr = stpcpy (ptr, "(");
+
+ bool got = false;
+ for (unsigned i = 0; i <= max; ++i)
+ {
+ unsigned mask = 1u << i;
+ if ((t->positive & mask) != 0
+ || (t->negative & mask) != 0)
+ {
+ if (got)
+ ptr = stpcpy (ptr, " & ");
+ if ((t->negative & (1u << i)) != 0)
+ ptr = stpcpy (ptr, "~");
+ ptr = stpcpy (ptr, names[i]);
+ got = true;
+ }
+ }
+
+ if (ptr == buf + 1)
+ ptr = stpcpy (ptr, "1");
+ ptr = stpcpy (ptr, ")");
+ return buf;
+}
+
+static const char *
+message_cri_str (struct message_criteria *cri)
+{
+ static char buf[512];
+ char *ptr = buf;
+ *ptr = 0;
+
+ for (size_t i = 0; i < cri->size; ++i)
+ {
+ struct message_term *t = cri->terms + i;
+ if (i > 0)
+ ptr = stpcpy (ptr, " | ");
+ ptr = stpcpy (ptr, message_term_str (t));
+ }
+
+ return buf;
+}
+
+static void
+message_cri_and (struct message_criteria *cri, struct message_term *term)
+{
+ assert ((term->positive & term->negative) == 0);
+ for (size_t i = 0; i < cri->size; )
+ {
+ struct message_term *t = cri->terms + i;
+ t->positive |= term->positive;
+ t->negative |= term->negative;
+ if ((t->positive & t->negative) != 0)
+ /* A ^ ~A -> drop the term. */
+ cri->terms[i] = cri->terms[--cri->size];
+ else
+ ++i;
+ }
+}
+
+static void
+message_cri_or (struct message_criteria *cri, struct message_term *term)
+{
+ assert ((term->positive & term->negative) == 0);
+ REALLOC (cri, terms);
+ cri->terms[cri->size++] = *term;
+}
+
+/* NEG(a&b&~c) -> (~a + ~b + c) */
+static struct message_criteria
+message_cri_neg (struct message_term *term)
+{
+ assert ((term->positive & term->negative) == 0);
+
+ unsigned max = 0;
+#define MC(CAT, ID) max = ID;
+ MESSAGE_CATEGORIES
+#undef MC
+
+ struct message_criteria ret;
+ WIPE (ret);
+ for (size_t i = 0; i < max; ++i)
+ {
+ unsigned mask = 1u << i;
+ if ((term->positive & mask) != 0)
+ message_cri_or (&ret, &(struct message_term){1u << i, mc_none});
+ else if ((term->negative & mask) != 0)
+ message_cri_or (&ret, &(struct message_term){mc_none, 1u << i});
+ }
+
+ return ret;
+}
+
+/* MUL((a&b + c&d), (e&f + g&h)) -> (a&b&e&f + a&b&g&h + c&d&e&f + c&d&g&h) */
+static void
+message_cri_mul (struct message_criteria *cri, struct message_criteria *rhs)
+{
+ struct message_criteria ret;
+ WIPE (ret);
+
+ for (size_t i = 0; i < cri->size; ++i)
+ for (size_t j = 0; j < rhs->size; ++j)
+ {
+ struct message_term t1 = cri->terms[i];
+ struct message_term *t2 = rhs->terms + j;
+ t1.positive |= t2->positive;
+ t1.negative |= t2->negative;
+ if (t1.positive & t1.negative)
+ /* A ^ ~A -> drop the term. */
+ continue;
+ message_cri_or (&ret, &t1);
+ }
+
+ free (cri->terms);
+ *cri = ret;
+}
+
+/* Reject message if TERM passes. */
+static void
+message_cri_and_not (struct message_criteria *cri, struct message_term *term)
+{
+ struct message_criteria tmp
+ = message_cri_neg (&(struct message_term) {term->negative, term->positive});
+ message_cri_mul (cri, &tmp);
+ free (tmp.terms);
+}
+
+/* Messages that are accepted (and made into warning). */
+static struct message_criteria warning_criteria;
+
+/* Accepted (warning) messages, that are turned into errors. */
+static struct message_criteria error_criteria;
+
+static unsigned error_count = 0;
+
+static bool
+check_category (enum message_category cat)
+{
+ return message_accept (&warning_criteria, cat);
+}
+
+static void
+wr_verror (const struct where *wh, const char *format, va_list ap)
+{
+ printf ("error: %s", where_fmt (wh, NULL));
+ vprintf (format, ap);
+ where_fmt_chain (wh, "error");
+ ++error_count;
+}
+
+static void
+wr_vwarning (const struct where *wh, const char *format, va_list ap)
+{
+ printf ("warning: %s", where_fmt (wh, NULL));
+ vprintf (format, ap);
+ where_fmt_chain (wh, "warning");
+ ++error_count;
+}
+
+void
+wr_error (const struct where *wh, const char *format, ...)
+{
+ va_list ap;
+ va_start (ap, format);
+ wr_verror (wh, format, ap);
+ va_end (ap);
+}
+
+void
+wr_warning (const struct where *wh, const char *format, ...)
+{
+ va_list ap;
+ va_start (ap, format);
+ wr_vwarning (wh, format, ap);
+ va_end (ap);
+}
+
+void
+wr_message (enum message_category category, const struct where *wh,
+ const char *format, ...)
+{
+ va_list ap;
+ va_start (ap, format);
+ if (message_accept (&warning_criteria, category))
+ {
+ if (message_accept (&error_criteria, category))
+ wr_verror (wh, format, ap);
+ else
+ wr_vwarning (wh, format, ap);
+ }
+ va_end (ap);
+}
+
+char *
+range_fmt (char *buf, size_t buf_size, uint64_t start, uint64_t end)
+{
+ snprintf (buf, buf_size, "[%#" PRIx64 ", %#" PRIx64 ")", start, end);
+ return buf;
+}
+
+void
+wr_format_padding_message (enum message_category category,
+ struct where *wh,
+ uint64_t start, uint64_t end, char *kind)
+{
+ char msg[128];
+ wr_message (category, wh, ": %s: %s.\n",
+ range_fmt (msg, sizeof msg, start, end), kind);
+}
+
+void
+wr_format_leb128_message (struct where *where, const char *what,
+ const char *purpose,
+ const unsigned char *begin, const unsigned char *end)
+{
+ enum message_category category = mc_leb128 | mc_acc_bloat | mc_impact_3;
+ char buf[(end - begin) * 3 + 1]; // 2 hexa digits+" " per byte, and term. 0
+ char *ptr = buf;
+ for (; begin < end; ++begin)
+ ptr += sprintf (ptr, " %02x", *begin);
+ wr_message (category, where,
+ ": %s: value %s encoded as `%s'.\n",
+ what, purpose, buf + 1);
+}
+
+void
+wr_message_padding_0 (enum message_category category,
+ struct where *wh,
+ uint64_t start, uint64_t end)
+{
+ wr_format_padding_message (category | mc_acc_bloat | mc_impact_1,
+ wh, start, end,
+ "unnecessary padding with zero bytes");
+}
+
+void
+wr_message_padding_n0 (enum message_category category,
+ struct where *wh,
+ uint64_t start, uint64_t end)
+{
+ wr_format_padding_message (category | mc_acc_bloat | mc_impact_1,
+ wh, start, end,
+ "unreferenced non-zero bytes");
+}
+
+/* True if no message is to be printed if the run is succesful. */
+static bool be_quiet = false; /* -q */
+static bool be_verbose = false; /* -v */
+static bool be_strict = false; /* --strict */
+static bool be_gnu = false; /* --gnu */
+static bool be_tolerant = false; /* --tolerant */
+static bool show_refs = false; /* --ref */
+static bool do_high_level = true; /* ! --nohl */
+
+/* True if coverage analysis of .debug_ranges vs. ELF sections should
+ be done. */
+static const bool do_range_coverage = false;
+
+static int
+layout_rel_file (Elf *elf)
+{
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr (elf, &ehdr) == NULL)
+ return 1;
+
+ if (ehdr.e_type != ET_REL)
+ return 0;
+
+ /* Taken from libdwfl. */
+ GElf_Addr base = 0;
+ GElf_Addr start = 0, end = 0, bias = 0;
+
+ bool first = true;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ return 1;
+
+ if (shdr->sh_flags & SHF_ALLOC)
+ {
+ const GElf_Xword align = shdr->sh_addralign ?: 1;
+ const GElf_Addr next = (end + align - 1) & -align;
+ if (shdr->sh_addr == 0
+ /* Once we've started doing layout we have to do it all,
+ unless we just layed out the first section at 0 when
+ it already was at 0. */
+ || (bias == 0 && end > start && end != next))
+ {
+ shdr->sh_addr = next;
+ if (end == base)
+ /* This is the first section assigned a location.
+ Use its aligned address as the module's base. */
+ start = base = shdr->sh_addr;
+ else if (unlikely (base & (align - 1)))
+ {
+ /* If BASE has less than the maximum alignment of
+ any section, we eat more than the optimal amount
+ of padding and so make the module's apparent
+ size come out larger than it would when placed
+ at zero. So reset the layout with a better base. */
+
+ start = end = base = (base + align - 1) & -align;
+ Elf_Scn *prev_scn = NULL;
+ do
+ {
+ prev_scn = elf_nextscn (elf, prev_scn);
+ GElf_Shdr prev_shdr_mem;
+ GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
+ &prev_shdr_mem);
+ if (unlikely (prev_shdr == NULL))
+ return 1;
+ if (prev_shdr->sh_flags & SHF_ALLOC)
+ {
+ const GElf_Xword prev_align
+ = prev_shdr->sh_addralign ?: 1;
+
+ prev_shdr->sh_addr
+ = (end + prev_align - 1) & -prev_align;
+ end = prev_shdr->sh_addr + prev_shdr->sh_size;
+
+ if (unlikely (! gelf_update_shdr (prev_scn,
+ prev_shdr)))
+ return 1;
+ }
+ }
+ while (prev_scn != scn);
+ continue;
+ }
+
+ end = shdr->sh_addr + shdr->sh_size;
+ if (likely (shdr->sh_addr != 0)
+ && unlikely (! gelf_update_shdr (scn, shdr)))
+ return 1;
+ }
+ else
+ {
+ /* The address is already assigned. Just track it. */
+ if (first || end < shdr->sh_addr + shdr->sh_size)
+ end = shdr->sh_addr + shdr->sh_size;
+ if (first || bias > shdr->sh_addr)
+ /* This is the lowest address in the module. */
+ bias = shdr->sh_addr;
+
+ if ((shdr->sh_addr - bias + base) & (align - 1))
+ /* This section winds up misaligned using BASE.
+ Adjust BASE upwards to make it congruent to
+ the lowest section address in the file modulo ALIGN. */
+ base = (((base + align - 1) & -align)
+ + (bias & (align - 1)));
+ }
+
+ first = false;
+ }
+ }
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ /* Set locale. */
+ setlocale (LC_ALL, "");
+
+ /* Initialize the message catalog. */
+ textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ int remaining;
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Initialize warning & error criteria. */
+ message_cri_or (&warning_criteria,
+ &(struct message_term){mc_none, mc_none});
+
+ message_cri_or (&error_criteria,
+ &(struct message_term){mc_impact_4, mc_none});
+ message_cri_or (&error_criteria,
+ &(struct message_term){mc_error, mc_none});
+
+ /* Configure warning & error criteria according to configuration. */
+ if (tolerate_nodebug)
+ message_cri_and (&warning_criteria,
+ &(struct message_term){mc_none, mc_elf});
+
+ if (be_gnu)
+ {
+ message_cri_and (&warning_criteria,
+ &(struct message_term){mc_none, mc_acc_bloat});
+ }
+
+ if (!be_strict)
+ {
+ message_cri_and (&warning_criteria,
+ &(struct message_term){mc_none, mc_strings});
+ message_cri_and_not (&warning_criteria,
+ &(struct message_term)
+ {mc_line | mc_header | mc_acc_bloat, mc_none});
+ message_cri_and (&warning_criteria,
+ &(struct message_term){mc_none, mc_pubtypes});
+ }
+
+ if (be_tolerant)
+ {
+ message_cri_and (&warning_criteria,
+ &(struct message_term){mc_none, mc_loc});
+ message_cri_and (&warning_criteria,
+ &(struct message_term){mc_none, mc_ranges});
+ }
+
+ if (be_verbose)
+ {
+ printf ("warning criteria: %s\n", message_cri_str (&warning_criteria));
+ printf ("error criteria: %s\n", message_cri_str (&error_criteria));
+ }
+
+ /* Before we start tell the ELF library which version we are using. */
+ elf_version (EV_CURRENT);
+
+ /* Now process all the files given at the command line. */
+ bool only_one = remaining + 1 == argc;
+ do
+ {
+ /* Open the file. */
+ int fd = open (argv[remaining], O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, gettext ("cannot open input file"));
+ continue;
+ }
+
+ /* Create an `Elf' descriptor. */
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+ if (elf == NULL)
+ invalid_elf:
+ wr_error (NULL,
+ gettext ("Error processing ELF file: %s\n"),
+ elf_errmsg (-1));
+ else
+ {
+ unsigned int prev_error_count = error_count;
+ if (layout_rel_file (elf))
+ goto invalid_elf;
+
+ process_file (elf, argv[remaining], only_one);
+
+ elf_errno (); /* Clear errno. */
+ elf_end (elf);
+ int err = elf_errno ();
+ if (err != 0)
+ wr_error (NULL,
+ gettext ("error while closing Elf descriptor: %s\n"),
+ elf_errmsg (err));
+
+ if (prev_error_count == error_count && !be_quiet)
+ puts (gettext ("No errors"));
+ }
+
+ close (fd);
+ }
+ while (++remaining < argc);
+
+ return error_count != 0;
+}
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg __attribute__ ((unused)),
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case ARGP_strict:
+ be_strict = true;
+ break;
+
+ case ARGP_gnu:
+ be_gnu = true;
+ break;
+
+ case ARGP_tolerant:
+ be_tolerant = true;
+ break;
+
+ case ARGP_ref:
+ show_refs = true;
+ break;
+
+ case ARGP_nohl:
+ do_high_level = false;
+ break;
+
+ case 'i':
+ tolerate_nodebug = true;
+ break;
+
+ case 'q':
+ be_quiet = true;
+ be_verbose = false;
+ break;
+
+ case 'v':
+ be_quiet = false;
+ be_verbose = true;
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ fputs (gettext ("Missing file name.\n"), stderr);
+ argp_help (&argp, stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
+ program_invocation_short_name);
+ exit (1);
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+#define PRI_CU "CU 0x%" PRIx64
+#define PRI_DIE "DIE 0x%" PRIx64
+#define PRI_NOT_ENOUGH ": not enough data for %s.\n"
+#define PRI_LACK_RELOCATION ": %s seems to lack a relocation.\n"
+
+
+/* Functions and data structures related to raw (i.e. unassisted by
+ libdw) Dwarf abbreviation handling. */
+
+struct abbrev
+{
+ uint64_t code;
+
+ /* While ULEB128 can hold numbers > 32bit, these are not legal
+ values of many enum types. So just use as large type as
+ necessary to cover valid values. */
+ uint16_t tag;
+ bool has_children;
+
+ struct where where;
+
+ /* Whether some DIE uses this abbrev. */
+ bool used;
+
+ /* Attributes. */
+ struct abbrev_attrib
+ {
+ uint16_t name;
+ uint8_t form;
+ struct where where;
+ } *attribs;
+ size_t size;
+ size_t alloc;
+};
+
+struct abbrev_table
+{
+ uint64_t offset;
+ bool used; /* There are CUs using this table. */
+ bool skip_check; /* There were errors during loading one of the
+ CUs that use this table. Check for unused
+ abbrevs should be skipped. */
+ struct abbrev *abbr;
+ size_t size;
+ size_t alloc;
+ struct abbrev_table *next;
+};
+
+static struct abbrev_table *abbrev_table_load (struct read_ctx *ctx);
+static void abbrev_table_free (struct abbrev_table *abbr);
+static struct abbrev *abbrev_table_find_abbrev (struct abbrev_table *abbrevs,
+ uint64_t abbrev_code);
+
+
+/* Functions and data structures for address record handling. We use
+ that to check that all DIE references actually point to an existing
+ die, not somewhere mid-DIE, where it just happens to be
+ interpretable as a DIE. */
+
+struct addr_record
+{
+ size_t size;
+ size_t alloc;
+ uint64_t *addrs;
+};
+
+static size_t addr_record_find_addr (struct addr_record *ar, uint64_t addr);
+static bool addr_record_has_addr (struct addr_record *ar, uint64_t addr);
+static void addr_record_add (struct addr_record *ar, uint64_t addr);
+static void addr_record_free (struct addr_record *ar);
+
+
+/* Functions and data structures for reference handling. Just like
+ the above, we use this to check validity of DIE references. Unlike
+ the above, this is not stored as sorted set, but simply as an array
+ of records, because duplicates are unlikely. */
+
+struct ref
+{
+ uint64_t addr; // Referree address
+ struct where who; // Referrer
+};
+
+struct ref_record
+{
+ size_t size;
+ size_t alloc;
+ struct ref *refs;
+};
+
+static void ref_record_add (struct ref_record *rr, uint64_t addr, struct where *referrer);
+static void ref_record_free (struct ref_record *rr);
+
+
+/* Functions and data structures for CU handling. */
+
+struct cu
+{
+ uint64_t offset;
+ uint64_t cudie_offset;
+ uint64_t length;
+ int address_size; // Address size in bytes on the target machine.
+ uint64_t low_pc; // DW_AT_low_pc value of CU DIE, -1 if not present.
+ struct addr_record die_addrs; // Addresses where DIEs begin in this CU.
+ struct ref_record die_refs; // DIE references into other CUs from this CU.
+ struct ref_record loc_refs; // references into .debug_loc from this CU.
+ struct ref_record range_refs; // references into .debug_ranges from this CU.
+ struct ref_record line_refs; // references into .debug_line from this CU.
+ struct where where; // Where was this section defined.
+ bool has_arange; // Whether we saw arange section pointing to this CU.
+ bool has_pubnames; // Likewise for pubnames.
+ bool has_pubtypes; // Likewise for pubtypes.
+ struct cu *next;
+};
+
+static void cu_free (struct cu *cu_chain);
+static struct cu *cu_find_cu (struct cu *cu_chain, uint64_t offset);
+
+struct cu_coverage
+{
+ struct coverage cov;
+ bool need_ranges; /* If all CU DIEs have high_pc/low_pc
+ attribute pair, we don't need separate
+ range pass. Otherwise we do. As soon as
+ ranges are projected into cov, the flag
+ is set to false again. */
+};
+
+
+/* Functions for checking of structural integrity. */
+
+static struct cu * check_info_structural (struct elf_file *file,
+ struct sec *sec,
+ struct abbrev_table *abbrev_chain,
+ Elf_Data *strings,
+ struct cu_coverage *cu_coverage);
+
+static bool check_aranges_structural (struct elf_file *file,
+ struct sec *sec,
+ struct cu *cu_chain,
+ struct coverage *coverage);
+
+static bool check_pub_structural (struct elf_file *file,
+ struct sec *sec,
+ struct cu *cu_chain);
+
+static bool check_location_expression (struct elf_file *file,
+ struct read_ctx *ctx,
+ uint64_t init_off,
+ struct relocation_data *reloc,
+ size_t length,
+ struct where *wh,
+ bool addr_64);
+
+static bool check_loc_or_range_structural (struct elf_file *file,
+ struct sec *sec,
+ struct cu *cu_chain,
+ struct cu_coverage *cu_coverage);
+
+static bool read_rel (struct elf_file *file,
+ struct sec *sec,
+ Elf_Data *reldata,
+ bool elf_64);
+
+static bool check_line_structural (struct elf_file *file,
+ struct sec *sec,
+ struct cu *cu_chain);
+
+const char *
+where_fmt (const struct where *wh, char *ptr)
+{
+ if (wh == NULL)
+ return "";
+
+ static char buf[256];
+
+ struct section_info
+ {
+ const char *name;
+ const char *addr1n;
+ const char *addr1f;
+ const char *addr2n;
+ const char *addr2f;
+ const char *addr3n;
+ const char *addr3f;
+ };
+
+ static struct section_info section_names[] =
+ {
+ [sec_info] = {".debug_info", "CU", "%"PRId64,
+ "DIE", "%#"PRIx64, NULL, NULL},
+
+ [sec_abbrev] = {".debug_abbrev", "section", "%"PRId64,
+ "abbreviation", "%"PRId64, "abbr. attribute", "%#"PRIx64},
+
+ [sec_aranges] = {".debug_aranges", "table", "%"PRId64,
+ "arange", "%#"PRIx64, NULL, NULL},
+
+ [sec_pubnames] = {".debug_pubnames", "pubname table", "%"PRId64,
+ "pubname", "%#"PRIx64, NULL, NULL},
+
+ [sec_pubtypes] = {".debug_pubtypes", "pubtype table", "%"PRId64,
+ "pubtype", "%#"PRIx64, NULL, NULL},
+
+ [sec_str] = {".debug_str", "offset", "%#"PRIx64,
+ NULL, NULL, NULL, NULL},
+
+ [sec_line] = {".debug_line", "table", "%"PRId64,
+ "offset", "%#"PRIx64, NULL, NULL},
+
+ [sec_loc] = {".debug_loc", "loclist", "%#"PRIx64,
+ "offset", "%#"PRIx64, NULL, NULL},
+
+ [sec_mac] = {".debug_mac", NULL, NULL, NULL, NULL, NULL, NULL},
+
+ [sec_ranges] = {".debug_ranges", "rangelist", "%#"PRIx64,
+ "offset", "%#"PRIx64, NULL, NULL},
+
+ [sec_locexpr] = {"location expression", "offset", "%#"PRIx64,
+ NULL, NULL, NULL, NULL},
+
+ [sec_rel] = {".rel", "relocation", "%"PRId64,
+ "offset", "%#"PRIx64, NULL, NULL},
+ [sec_rela] = {".rela", "relocation", "%"PRId64,
+ "offset", "%#"PRIx64, NULL, NULL},
+ };
+
+ static struct section_info special_formats[] =
+ {
+ [wf_cudie] = {".debug_info", "CU DIE", "%"PRId64, NULL, NULL, NULL, NULL}
+ };
+
+ assert (wh->section < sizeof (section_names) / sizeof (*section_names));
+ struct section_info *inf
+ = (wh->formatting == wf_plain)
+ ? section_names + wh->section
+ : special_formats + wh->formatting;
+
+ assert (inf->name);
+
+ assert ((inf->addr1n == NULL) == (inf->addr1f == NULL));
+ assert ((inf->addr2n == NULL) == (inf->addr2f == NULL));
+ assert ((inf->addr3n == NULL) == (inf->addr3f == NULL));
+
+ assert ((wh->addr1 != (uint64_t)-1) ? inf->addr1n != NULL : true);
+ assert ((wh->addr2 != (uint64_t)-1) ? inf->addr2n != NULL : true);
+ assert ((wh->addr3 != (uint64_t)-1) ? inf->addr3n != NULL : true);
+
+ assert ((wh->addr3 != (uint64_t)-1) ? (wh->addr2 != (uint64_t)-1) : true);
+ assert ((wh->addr2 != (uint64_t)-1) ? (wh->addr1 != (uint64_t)-1) : true);
+
+ /* GCC insists on checking format parameters and emits a warning
+ when we don't use string literal. With -Werror this ends up
+ being hard error. So instead we walk around this warning by
+ using function pointer. */
+ int (*x_asprintf)(char **strp, const char *fmt, ...) = asprintf;
+
+#define SETUP_ADDR(N) \
+ char *addr##N##s; \
+ if (wh->addr##N == (uint64_t)-1) \
+ addr##N##s = NULL; \
+ else if (x_asprintf (&addr##N##s, inf->addr##N##f, wh->addr##N) < 0) \
+ addr##N##s = "(fmt error)"
+
+ SETUP_ADDR (1);
+ SETUP_ADDR (2);
+ SETUP_ADDR (3);
+#undef SETUP_ADDR
+
+ char *orig = ptr;
+ bool is_reloc = wh->section == sec_rel || wh->section == sec_rela;
+ if (ptr == NULL)
+ {
+ ptr = stpcpy (buf, inf->name);
+ if (is_reloc)
+ {
+ struct where *ref = wh->ref;
+ assert (ref != NULL);
+ if (ref->section == sec_locexpr)
+ {
+ ref = ref->next;
+ assert (ref != NULL);
+ assert (ref->section != sec_locexpr);
+ }
+ ptr = stpcpy (ptr, section_names[ref->section].name);
+ }
+
+ if (addr1s != NULL)
+ ptr = stpcpy (ptr, ": ");
+ }
+
+ if (addr3s != NULL)
+ ptr = stpcpy (stpcpy (stpcpy (ptr, inf->addr3n), " "), addr3s);
+ else if (addr2s != NULL)
+ ptr = stpcpy (stpcpy (stpcpy (ptr, inf->addr2n), " "), addr2s);
+ else if (addr1s != NULL)
+ ptr = stpcpy (stpcpy (stpcpy (ptr, inf->addr1n), " "), addr1s);
+
+ if (wh->ref != NULL && !is_reloc)
+ {
+ ptr = stpcpy (ptr, " (");
+ ptr = (char *)where_fmt (wh->ref, ptr);
+ *ptr++ = ')';
+ *ptr = 0;
+ }
+
+ if (orig == NULL)
+ return buf;
+ else
+ return ptr;
+}
+
+void
+where_fmt_chain (const struct where *wh, const char *severity)
+{
+ if (wh != NULL && show_refs)
+ for (struct where *it = wh->next; it != NULL; it = it->next)
+ printf ("%s: %s: caused by this reference.\n",
+ severity, where_fmt (it, NULL));
+}
+
+void
+where_reset_1 (struct where *wh, uint64_t addr)
+{
+ wh->addr1 = addr;
+ wh->addr2 = wh->addr3 = (uint64_t)-1;
+}
+
+void
+where_reset_2 (struct where *wh, uint64_t addr)
+{
+ wh->addr2 = addr;
+ wh->addr3 = (uint64_t)-1;
+}
+
+void
+where_reset_3 (struct where *wh, uint64_t addr)
+{
+ wh->addr3 = addr;
+}
+
+static bool
+address_aligned (uint64_t addr, uint64_t align)
+{
+ return align < 2 || (addr % align == 0);
+}
+
+static bool
+necessary_alignment (uint64_t start, uint64_t length, uint64_t align)
+{
+ return address_aligned (start + length, align) && length < align;
+}
+
+static bool
+elf_file_init (struct elf_file *file, Elf *elf)
+{
+ WIPE (*file);
+
+ file->elf = elf;
+ file->ebl = ebl_openbackend (elf);
+
+ if (file->ebl == NULL
+ || gelf_getehdr (elf, &file->ehdr) == NULL)
+ return false;
+
+ file->addr_64 = file->ehdr.e_ident[EI_CLASS] == ELFCLASS64;
+
+ /* Taken from dwarf_begin_elf.c. */
+ if ((BYTE_ORDER == LITTLE_ENDIAN
+ && file->ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+ || (BYTE_ORDER == BIG_ENDIAN
+ && file->ehdr.e_ident[EI_DATA] == ELFDATA2LSB))
+ file->other_byte_order = true;
+
+ struct secinfo
+ {
+ const char *name;
+ enum section_id id; /* Section type. */
+ Elf_Data *reldata; /* Relocation data if any found. */
+ size_t reltype; /* SHT_REL or SHT_RELA. We need this
+ temporary store to be able to resolve
+ relocation section appearing before
+ relocated section. */
+ size_t secndx; /* Index into file->sec or 0 if not yet loaded. */
+ };
+ struct secinfo secinfo[] = {
+#define SEC(n) {".debug_" #n, sec_##n, NULL, 0, 0},
+ DEBUGINFO_SECTIONS
+#undef SEC
+ };
+
+ Elf_Scn *reloc_symtab = NULL;
+
+ struct secinfo *find_secentry (const char *secname)
+ {
+ for (size_t i = 0; i < sizeof (secinfo) / sizeof (*secinfo); ++i)
+ if (strcmp (secinfo[i].name, secname) == 0)
+ return secinfo + i;
+ return NULL;
+ }
+
+ /* Now find all necessary debuginfo sections and associated
+ relocation sections. */
+
+ /* Section 0 is special, skip it. */
+ REALLOC (file, sec);
+ file->sec[file->size++].id = sec_invalid;
+
+ bool check_rel = true;
+
+ for (Elf_Scn *scn = NULL; (scn = elf_nextscn (elf, scn)); )
+ {
+ REALLOC (file, sec);
+ size_t curndx = file->size++;
+ struct sec *cursec = file->sec + curndx;
+
+ GElf_Shdr *shdr = gelf_getshdr (scn, &cursec->shdr);
+ if (shdr == NULL)
+ {
+ invalid_elf:
+ wr_error (NULL, "Broken ELF.\n");
+ return false;
+ }
+
+ const char *scnname = elf_strptr (elf, file->ehdr.e_shstrndx,
+ shdr->sh_name);
+ if (scnname == NULL)
+ goto invalid_elf;
+
+ if (!address_aligned (shdr->sh_addr, shdr->sh_addralign))
+ wr_error (NULL, "Base address of section %s, %#" PRIx64
+ ", should have an alignment of %" PRId64 ".\n",
+ scnname, shdr->sh_addr, shdr->sh_addralign);
+
+ struct secinfo *secentry = find_secentry (scnname);
+ cursec->scn = scn;
+ cursec->id = secentry != NULL ? secentry->id : sec_invalid;
+ cursec->name = scnname;
+ cursec->rel = (struct relocation_data){NULL, SHT_NULL, NULL, 0, 0, 0};
+
+ /* Dwarf section. */
+ if (secentry != NULL)
+ {
+ if (unlikely (secentry->secndx != 0))
+ wr_error (NULL, "Multiple occurrences of section %s.\n", scnname);
+ else
+ {
+ /* Haven't seen a section of that name yet. */
+ cursec->data = elf_getdata (scn, NULL);
+ if (cursec->data == NULL || cursec->data->d_buf == NULL)
+ /* Don't print out a warning, we'll get to that in
+ process_file. */
+ cursec->data = NULL;
+ secentry->secndx = curndx;
+ }
+ }
+ /* Relocation section. */
+ else if (shdr->sh_type == SHT_RELA || shdr->sh_type == SHT_REL)
+ {
+ /* Get data of section that this REL(A) section relocates. */
+ Elf_Scn *relocated_scn = elf_getscn (elf, shdr->sh_info);
+ Elf_Scn *symtab_scn = elf_getscn (elf, shdr->sh_link);
+ if (relocated_scn == NULL || symtab_scn == NULL)
+ goto invalid_elf;
+
+ GElf_Shdr relocated_shdr_mem;
+ GElf_Shdr *relocated_shdr = gelf_getshdr (relocated_scn,
+ &relocated_shdr_mem);
+ if (relocated_shdr == NULL)
+ goto invalid_elf;
+
+ const char *relocated_scnname
+ = elf_strptr (elf, file->ehdr.e_shstrndx,
+ relocated_shdr->sh_name);
+
+ struct secinfo *relocated
+ = find_secentry (relocated_scnname);
+
+ if (relocated != NULL)
+ {
+ if (relocated->reldata != NULL)
+ wr_error (NULL,
+ "Several relocation sections for debug section %s."
+ " Ignoring %s.\n",
+ relocated_scnname, scnname);
+ else
+ {
+ relocated->reldata = elf_getdata (scn, NULL);
+ if (unlikely (relocated->reldata == NULL
+ || relocated->reldata->d_buf == NULL))
+ {
+ wr_error (NULL,
+ "Data-less relocation section %s.\n", scnname);
+ relocated->reldata = NULL;
+ }
+ else
+ relocated->reltype = shdr->sh_type;
+ }
+
+ if (reloc_symtab == NULL)
+ reloc_symtab = symtab_scn;
+ else if (reloc_symtab != symtab_scn)
+ wr_error (NULL,
+ "Relocation sections use multiple symbol tables.\n");
+ }
+ }
+ }
+
+ for (size_t i = 0; i < sizeof (secinfo) / sizeof (*secinfo); ++i)
+ if (secinfo[i].secndx != 0)
+ file->debugsec[secinfo[i].id] = file->sec + secinfo[i].secndx;
+
+ if (check_rel)
+ {
+ Elf_Data *reloc_symdata = NULL;
+ if (reloc_symtab != NULL)
+ {
+ reloc_symdata = elf_getdata (reloc_symtab, NULL);
+ if (reloc_symdata == NULL)
+ /* Not a show stopper, we can check a lot of stuff even
+ without a symbol table. */
+ wr_error (NULL,
+ "Couldn't obtain symtab data.\n");
+ }
+
+ /* Check relocation sections that we've got. */
+ for (size_t i = 0; i < sizeof (secinfo) / sizeof (*secinfo); ++i)
+ {
+ struct secinfo *cur = secinfo + i;
+ if (cur->secndx != 0 && cur->reldata != NULL)
+ {
+ struct sec *sec = file->sec + cur->secndx;
+ sec->rel.type = cur->reltype;
+ if (sec->data == NULL)
+ wr_error (&WHERE (sec->id, NULL),
+ ": this data-less section has a relocation section.\n");
+ else if (read_rel (file, sec, cur->reldata, file->addr_64))
+ sec->rel.symdata = reloc_symdata;
+ }
+ }
+
+ if (find_secentry (".debug_str")->reldata != NULL)
+ wr_message (mc_impact_2 | mc_elf, &WHERE (sec_str, NULL),
+ ": there's a relocation section associated with this section.\n");
+ }
+
+ return true;
+}
+
+static void
+process_file (Elf *elf, const char *fname, bool only_one)
+{
+ if (!only_one)
+ printf ("\n%s:\n", fname);
+
+ struct elf_file file;
+ if (!elf_file_init (&file, elf))
+ return;
+
+ struct abbrev_table *abbrev_chain = NULL;
+ struct cu *cu_chain = NULL;
+ struct read_ctx ctx;
+ /* Don't attempt to do high-level checks if we couldn't initialize
+ high-level context. The wrapper takes care of printing out error
+ messages if any. */
+ struct hl_ctx *hlctx = do_high_level ? hl_ctx_new (elf) : NULL;
+
+#define SEC(sec) (file.debugsec[sec_##sec])
+#define HAS_SEC(sec) (SEC(sec) != NULL && SEC(sec)->data != NULL)
+
+ if (likely (HAS_SEC(abbrev)))
+ {
+ read_ctx_init (&ctx, &file, SEC(abbrev)->data);
+ abbrev_chain = abbrev_table_load (&ctx);
+ }
+ else if (!tolerate_nodebug)
+ /* Hard error, not a message. We can't debug without this. */
+ wr_error (NULL, ".debug_abbrev data not found.\n");
+
+ Elf_Data *str_data = NULL;
+ if (SEC(str) != NULL)
+ {
+ str_data = SEC(str)->data;
+ if (str_data == NULL)
+ wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf,
+ &WHERE (sec_str, NULL),
+ ": the section is present but empty.\n");
+ }
+
+ struct cu_coverage *cu_coverage = NULL;
+ if (abbrev_chain != NULL)
+ {
+ if (likely (HAS_SEC(info)))
+ {
+ cu_coverage = calloc (1, sizeof (struct cu_coverage));
+ cu_chain = check_info_structural (&file, SEC(info), abbrev_chain,
+ str_data, cu_coverage);
+ if (cu_chain != NULL && hlctx != NULL)
+ check_expected_trees (hlctx);
+ }
+ else if (!tolerate_nodebug)
+ /* Hard error, not a message. We can't debug without this. */
+ wr_error (NULL, ".debug_info data not found.\n");
+ }
+
+ bool ranges_sound;
+ if (HAS_SEC(ranges) && cu_chain != NULL)
+ ranges_sound = check_loc_or_range_structural (&file, SEC(ranges),
+ cu_chain, cu_coverage);
+ else
+ ranges_sound = false;
+
+ if (HAS_SEC(loc) && cu_chain != NULL)
+ check_loc_or_range_structural (&file, SEC(loc), cu_chain, NULL);
+
+ if (HAS_SEC(aranges))
+ {
+ read_ctx_init (&ctx, &file, SEC(aranges)->data);
+
+ /* If ranges were needed and not loaded, don't pass them down
+ for CU/aranges coverage analysis. */
+ struct coverage *cov
+ = (cu_coverage != NULL
+ && cu_coverage->need_ranges) ? NULL : &cu_coverage->cov;
+
+ if (check_aranges_structural (&file, SEC(aranges), cu_chain, cov)
+ && ranges_sound && hlctx != NULL && !be_tolerant && !be_gnu)
+ check_matching_ranges (hlctx);
+ }
+
+ if (HAS_SEC(pubnames))
+ check_pub_structural (&file, SEC(pubnames), cu_chain);
+ else if (!tolerate_nodebug)
+ wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf,
+ &WHERE (sec_pubnames, NULL), ": data not found.\n");
+
+ if (HAS_SEC(pubtypes))
+ check_pub_structural (&file, SEC(pubtypes), cu_chain);
+ else if (!tolerate_nodebug)
+ wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf | mc_pubtypes,
+ &WHERE (sec_pubtypes, NULL), ": data not found.\n");
+
+ if (HAS_SEC(line))
+ check_line_structural (&file, SEC(line), cu_chain);
+ else if (!tolerate_nodebug)
+ wr_message (mc_impact_4 | mc_acc_suboptimal | mc_elf | mc_loc,
+ &WHERE (sec_line, NULL), ": data not found.\n");
+
+ cu_free (cu_chain);
+ abbrev_table_free (abbrev_chain);
+ if (file.ebl != NULL)
+ ebl_closebackend (file.ebl);
+ free (file.sec);
+ hl_ctx_delete (hlctx);
+
+#undef SEC
+#undef HAS_SEC
+}
+
+static bool
+checked_read_uleb128 (struct read_ctx *ctx, uint64_t *ret,
+ struct where *where, const char *what)
+{
+ const unsigned char *ptr = ctx->ptr;
+ int st = read_ctx_read_uleb128 (ctx, ret);
+ if (st < 0)
+ wr_error (where, ": can't read %s.\n", what);
+ else if (st > 0)
+ {
+ char buf[19]; // 16 hexa digits, "0x", terminating zero
+ sprintf (buf, "%#" PRIx64, *ret);
+ wr_format_leb128_message (where, what, buf, ptr, ctx->ptr);
+ }
+ return st >= 0;
+}
+
+static bool
+checked_read_sleb128 (struct read_ctx *ctx, int64_t *ret,
+ struct where *where, const char *what)
+{
+ const unsigned char *ptr = ctx->ptr;
+ int st = read_ctx_read_sleb128 (ctx, ret);
+ if (st < 0)
+ wr_error (where, ": can't read %s.\n", what);
+ else if (st > 0)
+ {
+ char buf[20]; // sign, "0x", 16 hexa digits, terminating zero
+ int64_t val = *ret;
+ sprintf (buf, "%s%#" PRIx64, val < 0 ? "-" : "", val < 0 ? -val : val);
+ wr_format_leb128_message (where, what, buf, ptr, ctx->ptr);
+ }
+ return st >= 0;
+}
+
+/* The value passed back in uint64_t VALUEP may actually be
+ type-casted int64_t. WHAT and WHERE describe error message and
+ context for LEB128 loading. */
+static bool
+read_ctx_read_form (struct read_ctx *ctx, bool addr_64, uint8_t form,
+ uint64_t *valuep, struct where *where, const char *what)
+{
+ switch (form)
+ {
+ case DW_FORM_addr:
+ return read_ctx_read_offset (ctx, addr_64, valuep);
+ case DW_FORM_udata:
+ return checked_read_uleb128 (ctx, valuep, where, what);
+ case DW_FORM_sdata:
+ return checked_read_sleb128 (ctx, (int64_t *)valuep, where, what);
+ case DW_FORM_data1:
+ {
+ uint8_t v;
+ if (!read_ctx_read_ubyte (ctx, &v))
+ return false;
+ if (valuep != NULL)
+ *valuep = v;
+ return true;
+ }
+ case DW_FORM_data2:
+ {
+ uint16_t v;
+ if (!read_ctx_read_2ubyte (ctx, &v))
+ return false;
+ if (valuep != NULL)
+ *valuep = v;
+ return true;
+ }
+ case DW_FORM_data4:
+ {
+ uint32_t v;
+ if (!read_ctx_read_4ubyte (ctx, &v))
+ return false;
+ if (valuep != NULL)
+ *valuep = v;
+ return true;
+ }
+ case DW_FORM_data8:
+ return read_ctx_read_8ubyte (ctx, valuep);
+ };
+
+ return false;
+}
+
+static bool
+attrib_form_valid (uint64_t form)
+{
+ return form > 0 && form <= DW_FORM_indirect;
+}
+
+static int
+check_sibling_form (uint64_t form)
+{
+ switch (form)
+ {
+ case DW_FORM_indirect:
+ /* Tolerate this in abbrev loading, even during the DIE loading.
+ We check that dereferenced indirect form yields valid form. */
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ case DW_FORM_ref8:
+ case DW_FORM_ref_udata:
+ return 0;
+
+ case DW_FORM_ref_addr:
+ return -1;
+
+ default:
+ return -2;
+ };
+}
+
+/* Check that given form may in fact be valid in some CU. */
+static bool
+check_abbrev_location_form (uint64_t form)
+{
+ switch (form)
+ {
+ case DW_FORM_indirect:
+
+ /* loclistptr */
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+
+ /* block */
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_block:
+ return true;
+
+ default:
+ return false;
+ };
+}
+
+static bool
+is_location_attrib (uint64_t name)
+{
+ switch (name)
+ {
+ case DW_AT_location:
+ case DW_AT_frame_base:
+ case DW_AT_data_location:
+ case DW_AT_data_member_location:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct abbrev_table *
+abbrev_table_load (struct read_ctx *ctx)
+{
+ struct abbrev_table *section_chain = NULL;
+ struct abbrev_table *section = NULL;
+ uint64_t first_attr_off = 0;
+ struct where where = WHERE (sec_abbrev, NULL);
+ where.addr1 = 0;
+
+ while (true)
+ {
+ inline bool check_no_abbreviations ()
+ {
+ bool ret = section_chain == NULL;
+ if (ret)
+ wr_error (&WHERE (sec_abbrev, NULL), ": no abbreviations.\n");
+ return ret;
+ }
+
+ /* If we get EOF at this point, either the CU was improperly
+ terminated, or there were no data to begin with. */
+ if (read_ctx_eof (ctx))
+ {
+ if (!check_no_abbreviations ())
+ wr_error (&where, ": missing zero to mark end-of-table.\n");
+ break;
+ }
+
+ uint64_t abbr_off;
+ uint64_t abbr_code;
+ {
+ uint64_t prev_abbr_off = (uint64_t)-1;
+ uint64_t prev_abbr_code = (uint64_t)-1;
+ uint64_t zero_seq_off = (uint64_t)-1;
+
+ do
+ {
+ abbr_off = read_ctx_get_offset (ctx);
+ where_reset_2 (&where, abbr_off);
+
+ /* Abbreviation code. */
+ if (!checked_read_uleb128 (ctx, &abbr_code, &where, "abbrev code"))
+ goto free_and_out;
+
+ /* Note: we generally can't tell the difference between
+ empty table and (excessive) padding. But NUL byte(s)
+ at the very beginning of section are almost certainly
+ the first case. */
+ if (zero_seq_off == (uint64_t)-1
+ && abbr_code == 0
+ && (prev_abbr_code == 0
+ || section_chain == NULL))
+ zero_seq_off = abbr_off;
+
+ if (abbr_code != 0)
+ break;
+ else
+ section = NULL;
+
+ prev_abbr_code = abbr_code;
+ prev_abbr_off = abbr_off;
+ }
+ while (!read_ctx_eof (ctx)
+ /* On EOF, shift the offset so that beyond-EOF
+ end-position is printed for padding warning.
+ Necessary as our end position is exclusive. */
+ || ((abbr_off += 1), false));
+
+ if (zero_seq_off != (uint64_t)-1)
+ wr_message_padding_0 (mc_abbrevs | mc_header,
+ &WHERE (where.section, NULL),
+ zero_seq_off, abbr_off);
+ }
+
+ if (read_ctx_eof (ctx))
+ {
+ /* It still may have been empty. */
+ check_no_abbreviations ();
+ break;
+ }
+
+ /* OK, we got some genuine abbreviation. See if we need to
+ allocate a new section. */
+ if (section == NULL)
+ {
+ section = xcalloc (1, sizeof (*section));
+ section->offset = abbr_off;
+ section->next = section_chain;
+ section_chain = section;
+
+ where_reset_1 (&where, abbr_off);
+ where_reset_2 (&where, abbr_off);
+ }
+
+ struct abbrev *original = abbrev_table_find_abbrev (section, abbr_code);
+ if (unlikely (original != NULL))
+ {
+ char *site1 = strdup (where_fmt (&original->where, NULL));
+ wr_error (&where, ": duplicate abbrev code %" PRId64
+ "; already defined at %s.\n", abbr_code, site1);
+ free (site1);
+ }
+
+ struct abbrev fake;
+ struct abbrev *cur;
+ /* Don't actually save this abbrev if it's duplicate. */
+ if (likely (original == NULL))
+ {
+ REALLOC (section, abbr);
+ cur = section->abbr + section->size++;
+ }
+ else
+ cur = &fake;
+ WIPE (*cur);
+
+ cur->code = abbr_code;
+ cur->where = where;
+
+ /* Abbreviation tag. */
+ uint64_t abbr_tag;
+ if (!checked_read_uleb128 (ctx, &abbr_tag, &where, "abbrev tag"))
+ goto free_and_out;
+
+ if (abbr_tag > DW_TAG_hi_user)
+ {
+ wr_error (&where, ": invalid abbrev tag 0x%" PRIx64 ".\n", abbr_tag);
+ goto free_and_out;
+ }
+ cur->tag = (typeof (cur->tag))abbr_tag;
+
+ /* Abbreviation has_children. */
+ uint8_t has_children;
+ if (!read_ctx_read_ubyte (ctx, &has_children))
+ {
+ wr_error (&where, ": can't read abbrev has_children.\n");
+ goto free_and_out;
+ }
+
+ if (has_children != DW_CHILDREN_no
+ && has_children != DW_CHILDREN_yes)
+ {
+ wr_error (&where,
+ ": invalid has_children value 0x%x.\n", cur->has_children);
+ goto free_and_out;
+ }
+ cur->has_children = has_children == DW_CHILDREN_yes;
+
+ bool null_attrib;
+ uint64_t sibling_attr = 0;
+ bool low_pc = false;
+ bool high_pc = false;
+ bool ranges = false;
+ do
+ {
+ uint64_t attr_off = read_ctx_get_offset (ctx);
+ uint64_t attrib_name, attrib_form;
+ if (first_attr_off == 0)
+ first_attr_off = attr_off;
+ /* Shift to match elfutils reporting. */
+ where_reset_3 (&where, attr_off - first_attr_off);
+
+ /* Load attribute name and form. */
+ if (!checked_read_uleb128 (ctx, &attrib_name, &where,
+ "attribute name"))
+ goto free_and_out;
+
+ if (!checked_read_uleb128 (ctx, &attrib_form, &where,
+ "attribute form"))
+ goto free_and_out;
+
+ null_attrib = attrib_name == 0 && attrib_form == 0;
+
+ /* Now if both are zero, this was the last attribute. */
+ if (!null_attrib)
+ {
+ /* Otherwise validate name and form. */
+ if (attrib_name > DW_AT_hi_user)
+ {
+ wr_error (&where,
+ ": invalid name 0x%" PRIx64 ".\n", attrib_name);
+ goto free_and_out;
+ }
+
+ if (!attrib_form_valid (attrib_form))
+ {
+ wr_error (&where,
+ ": invalid form 0x%" PRIx64 ".\n", attrib_form);
+ goto free_and_out;
+ }
+ }
+
+ REALLOC (cur, attribs);
+
+ struct abbrev_attrib *acur = cur->attribs + cur->size++;
+ WIPE (*acur);
+
+ /* We do structural checking of sibling attribute, so make
+ sure our assumptions in actual DIE-loading code are
+ right. We expect at most one DW_AT_sibling attribute,
+ with form from reference class, but only CU-local, not
+ DW_FORM_ref_addr. */
+ if (attrib_name == DW_AT_sibling)
+ {
+ if (sibling_attr != 0)
+ wr_error (&where,
+ ": Another DW_AT_sibling attribute in one abbreviation. "
+ "(First was 0x%" PRIx64 ".)\n", sibling_attr);
+ else
+ {
+ assert (attr_off > 0);
+ sibling_attr = attr_off;
+
+ if (!cur->has_children)
+ wr_message (mc_die_rel | mc_acc_bloat | mc_impact_1,
+ &where,
+ ": Excessive DW_AT_sibling attribute at childless abbrev.\n");
+ }
+
+ switch (check_sibling_form (attrib_form))
+ {
+ case -1:
+ wr_message (mc_die_rel | mc_impact_2, &where,
+ ": DW_AT_sibling attribute with form DW_FORM_ref_addr.\n");
+ break;
+
+ case -2:
+ wr_error (&where,
+ ": DW_AT_sibling attribute with non-reference form \"%s\".\n",
+ dwarf_form_string (attrib_form));
+ };
+ }
+ /* Similar for DW_AT_location and friends. */
+ else if (is_location_attrib (attrib_name))
+ {
+ if (!check_abbrev_location_form (attrib_form))
+ wr_error (&where,
+ ": %s with invalid form \"%s\".\n",
+ dwarf_attr_string (attrib_name),
+ dwarf_form_string (attrib_form));
+ }
+ /* Similar for DW_AT_ranges. */
+ else if (attrib_name == DW_AT_ranges
+ || attrib_name == DW_AT_stmt_list)
+ {
+ if (attrib_form != DW_FORM_data4
+ && attrib_form != DW_FORM_data8
+ && attrib_form != DW_FORM_indirect)
+ wr_error (&where,
+ ": %s with invalid form \"%s\".\n",
+ dwarf_attr_string (attrib_name),
+ dwarf_form_string (attrib_form));
+ if (attrib_name == DW_AT_ranges)
+ ranges = true;
+ }
+ /* Similar for DW_AT_{low,high}_pc, plus also make sure we
+ don't see high_pc without low_pc. */
+ else if (attrib_name == DW_AT_low_pc
+ || attrib_name == DW_AT_high_pc)
+ {
+ if (attrib_form != DW_FORM_addr
+ && attrib_form != DW_FORM_ref_addr)
+ wr_error (&where,
+ ": %s with invalid form \"%s\".\n",
+ dwarf_attr_string (attrib_name),
+ dwarf_form_string (attrib_form));
+
+ if (attrib_name == DW_AT_low_pc)
+ low_pc = true;
+ else if (attrib_name == DW_AT_high_pc)
+ high_pc = true;
+ }
+
+ acur->name = attrib_name;
+ acur->form = attrib_form;
+ acur->where = where;
+ }
+ while (!null_attrib);
+
+ where_reset_2 (&where, where.addr2); // drop addr 3
+ if (high_pc && !low_pc)
+ wr_error (&where,
+ ": the abbrev has DW_AT_high_pc without also having DW_AT_low_pc.\n");
+ else if (high_pc && ranges)
+ wr_error (&where,
+ ": the abbrev has DW_AT_high_pc & DW_AT_low_pc, but also has DW_AT_ranges.\n");
+ }
+
+ for (section = section_chain; section != NULL; section = section->next)
+ {
+ int cmp_abbrs (const void *a, const void *b)
+ {
+ struct abbrev *aa = (struct abbrev *)a;
+ struct abbrev *bb = (struct abbrev *)b;
+ return aa->code - bb->code;
+ }
+
+ /* The array is most likely already sorted in the file, but just
+ to be sure... */
+ qsort (section->abbr, section->size, sizeof (*section->abbr), cmp_abbrs);
+ }
+
+ return section_chain;
+
+ free_and_out:
+ abbrev_table_free (section_chain);
+ return NULL;
+}
+
+static void
+abbrev_table_free (struct abbrev_table *abbr)
+{
+ for (struct abbrev_table *it = abbr; it != NULL; )
+ {
+ for (size_t i = 0; i < it->size; ++i)
+ free (it->abbr[i].attribs);
+ free (it->abbr);
+
+ struct abbrev_table *temp = it;
+ it = it->next;
+ free (temp);
+ }
+}
+
+static struct abbrev *
+abbrev_table_find_abbrev (struct abbrev_table *abbrevs, uint64_t abbrev_code)
+{
+ size_t a = 0;
+ size_t b = abbrevs->size;
+ struct abbrev *ab = NULL;
+
+ while (a < b)
+ {
+ size_t i = (a + b) / 2;
+ ab = abbrevs->abbr + i;
+
+ if (ab->code > abbrev_code)
+ b = i;
+ else if (ab->code < abbrev_code)
+ a = i + 1;
+ else
+ return ab;
+ }
+
+ return NULL;
+}
+
+static size_t
+addr_record_find_addr (struct addr_record *ar, uint64_t addr)
+{
+ size_t a = 0;
+ size_t b = ar->size;
+
+ while (a < b)
+ {
+ size_t i = (a + b) / 2;
+ uint64_t v = ar->addrs[i];
+
+ if (v > addr)
+ b = i;
+ else if (v < addr)
+ a = i + 1;
+ else
+ return i;
+ }
+
+ return a;
+}
+
+static bool
+addr_record_has_addr (struct addr_record *ar, uint64_t addr)
+{
+ if (ar->size == 0
+ || addr < ar->addrs[0]
+ || addr > ar->addrs[ar->size - 1])
+ return false;
+
+ size_t a = addr_record_find_addr (ar, addr);
+ return a < ar->size && ar->addrs[a] == addr;
+}
+
+static void
+addr_record_add (struct addr_record *ar, uint64_t addr)
+{
+ size_t a = addr_record_find_addr (ar, addr);
+ if (a >= ar->size || ar->addrs[a] != addr)
+ {
+ REALLOC (ar, addrs);
+ size_t len = ar->size - a;
+ memmove (ar->addrs + a + 1, ar->addrs + a, len * sizeof (*ar->addrs));
+
+ ar->addrs[a] = addr;
+ ar->size++;
+ }
+}
+
+static void
+addr_record_free (struct addr_record *ar)
+{
+ if (ar != NULL)
+ free (ar->addrs);
+}
+
+
+static void
+ref_record_add (struct ref_record *rr, uint64_t addr, struct where *referrer)
+{
+ REALLOC (rr, refs);
+ struct ref *ref = rr->refs + rr->size++;
+ ref->addr = addr;
+ ref->who = *referrer;
+}
+
+static void
+ref_record_free (struct ref_record *rr)
+{
+ if (rr != NULL)
+ free (rr->refs);
+}
+
+bool
+found_hole (uint64_t start, uint64_t length, void *data)
+{
+ struct hole_info *info = (struct hole_info *)data;
+ bool all_zeroes = true;
+ for (uint64_t i = start; i < start + length; ++i)
+ if (((char*)info->data)[i] != 0)
+ {
+ all_zeroes = false;
+ break;
+ }
+
+ uint64_t end = start + length;
+ if (all_zeroes)
+ {
+ /* Zero padding is valid, if it aligns on the bounds of
+ info->align bytes, and is not excessive. */
+ if (!(info->align != 0 && info->align != 1
+ && (end % info->align == 0) && (start % 4 != 0)
+ && (length < info->align)))
+ wr_message_padding_0 (info->category, &WHERE (info->section, NULL),
+ start, end);
+ }
+ else
+ /* XXX: This actually lies when the unreferenced portion is
+ composed of sequences of zeroes and non-zeroes. */
+ wr_message_padding_n0 (info->category, &WHERE (info->section, NULL),
+ start, end);
+
+ return true;
+}
+
+/* begin is inclusive, end is exclusive. */
+bool
+coverage_map_found_hole (uint64_t begin, uint64_t end,
+ struct section_coverage *sco, void *user)
+{
+ struct coverage_map_hole_info *info = (struct coverage_map_hole_info *)user;
+
+ struct where where = WHERE (info->info.section, NULL);
+ const char *scnname = sco->sec->name;
+
+ struct sec *sec = sco->sec;
+ GElf_Xword align = sec->shdr.sh_addralign;
+
+ /* We don't expect some sections to be covered. But if they
+ are at least partially covered, we expect the same
+ coverage criteria as for .text. */
+ if (!sco->hit
+ && ((sco->sec->shdr.sh_flags & SHF_EXECINSTR) == 0
+ || strcmp (scnname, ".init") == 0
+ || strcmp (scnname, ".fini") == 0
+ || strcmp (scnname, ".plt") == 0))
+ return true;
+
+ /* For REL files, don't print addresses mangled by our layout. */
+ uint64_t base = info->elf->ehdr.e_type == ET_REL ? 0 : sco->sec->shdr.sh_addr;
+
+ /* If the hole is filled with NUL bytes, don't report it. But if we
+ get stripped debuginfo file, the data may not be available. In
+ that case don't report the hole, if it seems to be alignment
+ padding. */
+ if (sco->sec->data->d_buf != NULL)
+ {
+ bool zeroes = true;
+ for (uint64_t j = begin; j < end; ++j)
+ if (((char *)sco->sec->data->d_buf)[j] != 0)
+ {
+ zeroes = false;
+ break;
+ }
+ if (zeroes)
+ return true;
+ }
+ else if (necessary_alignment (base + begin, end - begin, align))
+ return true;
+
+ char buf[128];
+ wr_message (info->info.category | mc_acc_suboptimal | mc_impact_4, &where,
+ ": addresses %s of section %s are not covered.\n",
+ range_fmt (buf, sizeof buf, begin + base, end + base), scnname);
+ return true;
+}
+
+
+void
+section_coverage_init (struct section_coverage *sco,
+ struct sec *sec, bool warn)
+{
+ assert (sco != NULL);
+ assert (sec != NULL);
+
+ sco->sec = sec;
+ WIPE (sco->cov);
+ sco->hit = false;
+ sco->warn = warn;
+}
+
+bool
+coverage_map_init (struct coverage_map *coverage_map,
+ struct elf_file *elf,
+ Elf64_Xword mask,
+ Elf64_Xword warn_mask,
+ bool allow_overlap)
+{
+ assert (coverage_map != NULL);
+ assert (elf != NULL);
+
+ WIPE (*coverage_map);
+ coverage_map->elf = elf;
+ coverage_map->allow_overlap = allow_overlap;
+
+ for (size_t i = 1; i < elf->size; ++i)
+ {
+ struct sec *sec = elf->sec + i;
+
+ bool normal = (sec->shdr.sh_flags & mask) == mask;
+ bool warn = (sec->shdr.sh_flags & warn_mask) == warn_mask;
+ if (normal || warn)
+ {
+ REALLOC (coverage_map, scos);
+ section_coverage_init
+ (coverage_map->scos + coverage_map->size++, sec, !normal);
+ }
+ }
+
+ return true;
+}
+
+void
+coverage_map_add (struct coverage_map *coverage_map,
+ uint64_t address,
+ uint64_t length,
+ struct where *where,
+ enum message_category cat)
+{
+ bool found = false;
+ bool crosses_boundary = false;
+ bool overlap = false;
+ uint64_t end = address + length;
+ char buf[128]; // for messages
+
+ /* This is for analyzing how much of the current range falls into
+ sections in coverage map. Whatever is left uncovered doesn't
+ fall anywhere and is reported. */
+ struct coverage range_cov;
+ WIPE (range_cov);
+
+ for (size_t i = 0; i < coverage_map->size; ++i)
+ {
+ struct section_coverage *sco = coverage_map->scos + i;
+ GElf_Shdr *shdr = &sco->sec->shdr;
+ struct coverage *cov = &sco->cov;
+
+ Elf64_Addr s_end = shdr->sh_addr + shdr->sh_size;
+ if (end <= shdr->sh_addr || address >= s_end)
+ /* no overlap */
+ continue;
+
+ if (found && !crosses_boundary)
+ {
+ /* While probably not an error, it's very suspicious. */
+ wr_message (cat | mc_impact_2, where,
+ ": the range %s crosses section boundaries.\n",
+ range_fmt (buf, sizeof buf, address, end));
+ crosses_boundary = true;
+ }
+
+ found = true;
+
+ if (length == 0)
+ /* Empty range. That means no actual coverage, and we can
+ also be sure that there are no more sections that this one
+ falls into. */
+ break;
+
+ uint64_t cov_begin
+ = address < shdr->sh_addr ? 0 : address - shdr->sh_addr;
+ uint64_t cov_end
+ = end < s_end ? end - shdr->sh_addr : shdr->sh_size;
+ assert (cov_begin < cov_end);
+
+ uint64_t r_delta = shdr->sh_addr - address;
+ uint64_t r_cov_begin = cov_begin + r_delta;
+ uint64_t r_cov_end = cov_end + r_delta;
+
+ if (!overlap && !coverage_map->allow_overlap
+ && coverage_is_overlap (cov, cov_begin, cov_end - cov_begin))
+ {
+ /* Not a show stopper, this shouldn't derail high-level. */
+ wr_message (cat | mc_impact_2 | mc_error, where,
+ ": the range %s overlaps with another one.\n",
+ range_fmt (buf, sizeof buf, address, end));
+ overlap = true;
+ }
+
+ if (sco->warn)
+ wr_message (cat | mc_impact_2, where,
+ ": the range %s covers section %s.\n",
+ range_fmt (buf, sizeof buf, address, end), sco->sec->name);
+
+ /* Section coverage... */
+ coverage_add (cov, cov_begin, cov_end - cov_begin);
+ sco->hit = true;
+
+ /* And range coverage... */
+ coverage_add (&range_cov, r_cov_begin, r_cov_end - r_cov_begin);
+ }
+
+ if (!found)
+ /* Not a show stopper. */
+ wr_error (where,
+ ": couldn't find a section that the range %s covers.\n",
+ range_fmt (buf, sizeof buf, address, end));
+ else if (length > 0)
+ {
+ bool range_hole (uint64_t h_start, uint64_t h_length,
+ void *user __attribute__ ((unused)))
+ {
+ char buf2[128];
+ assert (h_length != 0);
+ wr_error (where,
+ ": portion %s of the range %s "
+ "doesn't fall into any ALLOC section.\n",
+ range_fmt (buf, sizeof buf,
+ h_start + address, h_start + address + h_length),
+ range_fmt (buf2, sizeof buf2, address, end));
+ return true;
+ }
+ coverage_find_holes (&range_cov, 0, length, range_hole, NULL);
+ }
+
+ coverage_free (&range_cov);
+}
+
+bool
+coverage_map_find_holes (struct coverage_map *coverage_map,
+ bool (*cb) (uint64_t begin, uint64_t end,
+ struct section_coverage *, void *),
+ void *user)
+{
+ for (size_t i = 0; i < coverage_map->size; ++i)
+ {
+ struct section_coverage *sco = coverage_map->scos + i;
+
+ bool wrap_cb (uint64_t h_start, uint64_t h_length, void *h_user)
+ {
+ return cb (h_start, h_start + h_length, sco, h_user);
+ }
+
+ if (!coverage_find_holes (&sco->cov, 0, sco->sec->shdr.sh_size,
+ wrap_cb, user))
+ return false;
+ }
+
+ return true;
+}
+
+void
+coverage_map_free (struct coverage_map *coverage_map)
+{
+ for (size_t i = 0; i < coverage_map->size; ++i)
+ coverage_free (&coverage_map->scos[i].cov);
+ free (coverage_map->scos);
+}
+
+
+static void
+cu_free (struct cu *cu_chain)
+{
+ for (struct cu *it = cu_chain; it != NULL; )
+ {
+ addr_record_free (&it->die_addrs);
+
+ struct cu *temp = it;
+ it = it->next;
+ free (temp);
+ }
+}
+
+static struct cu *
+cu_find_cu (struct cu *cu_chain, uint64_t offset)
+{
+ for (struct cu *it = cu_chain; it != NULL; it = it->next)
+ if (it->offset == offset)
+ return it;
+ return NULL;
+}
+
+
+static bool
+check_die_references (struct cu *cu,
+ struct ref_record *die_refs)
+{
+ bool retval = true;
+ for (size_t i = 0; i < die_refs->size; ++i)
+ {
+ struct ref *ref = die_refs->refs + i;
+ if (!addr_record_has_addr (&cu->die_addrs, ref->addr))
+ {
+ wr_error (&ref->who,
+ ": unresolved reference to " PRI_DIE ".\n", ref->addr);
+ retval = false;
+ }
+ }
+ return retval;
+}
+
+static bool
+check_global_die_references (struct cu *cu_chain)
+{
+ bool retval = true;
+ for (struct cu *it = cu_chain; it != NULL; it = it->next)
+ for (size_t i = 0; i < it->die_refs.size; ++i)
+ {
+ struct ref *ref = it->die_refs.refs + i;
+ struct cu *ref_cu = NULL;
+ for (struct cu *jt = cu_chain; jt != NULL; jt = jt->next)
+ if (addr_record_has_addr (&jt->die_addrs, ref->addr))
+ {
+ ref_cu = jt;
+ break;
+ }
+
+ if (ref_cu == NULL)
+ {
+ wr_error (&ref->who,
+ ": unresolved (non-CU-local) reference to " PRI_DIE ".\n",
+ ref->addr);
+ retval = false;
+ }
+ else if (ref_cu == it)
+ /* This is technically not a problem, so long as the
+ reference is valid, which it is. But warn about this
+ anyway, perhaps local reference could be formed on fewer
+ number of bytes. */
+ wr_message (mc_impact_2 | mc_acc_suboptimal | mc_die_rel,
+ &ref->who,
+ ": local reference to " PRI_DIE " formed as global.\n",
+ ref->addr);
+ }
+
+ return retval;
+}
+
+static bool
+read_size_extra (struct read_ctx *ctx, uint32_t size32, uint64_t *sizep,
+ bool *dwarf_64p, struct where *wh)
+{
+ if (size32 == DWARF3_LENGTH_64_BIT)
+ {
+ if (!read_ctx_read_8ubyte (ctx, sizep))
+ {
+ wr_error (wh, ": can't read 64bit CU length.\n");
+ return false;
+ }
+
+ *dwarf_64p = true;
+ }
+ else if (size32 >= DWARF3_LENGTH_MIN_ESCAPE_CODE)
+ {
+ wr_error (wh, ": unrecognized CU length escape value: "
+ "%" PRIx32 ".\n", size32);
+ return false;
+ }
+ else
+ {
+ *sizep = size32;
+ *dwarf_64p = false;
+ }
+
+ return true;
+}
+
+static bool
+check_zero_padding (struct read_ctx *ctx,
+ enum message_category category,
+ struct where *wh)
+{
+ assert (ctx->ptr != ctx->end);
+ const unsigned char *save_ptr = ctx->ptr;
+ while (!read_ctx_eof (ctx))
+ if (*ctx->ptr++ != 0)
+ {
+ ctx->ptr = save_ptr;
+ return false;
+ }
+
+ wr_message_padding_0 (category, wh,
+ (uint64_t)(save_ptr - ctx->begin),
+ (uint64_t)(ctx->end - ctx->begin));
+ return true;
+}
+
+static struct where
+where_from_reloc (struct relocation_data *reloc, struct where *ref)
+{
+ struct where where
+ = WHERE (reloc->type == SHT_REL ? sec_rel : sec_rela, NULL);
+ where_reset_1 (&where, reloc->rel[reloc->index].offset);
+ where.ref = ref;
+ return where;
+}
+
+enum skip_type
+{
+ skip_unref = 0,
+ skip_mismatched = 1,
+ skip_ok,
+};
+
+static struct relocation *
+relocation_next (struct relocation_data *reloc, uint64_t offset,
+ struct where *where, enum skip_type st)
+{
+ if (reloc == NULL || reloc->rel == NULL)
+ return NULL;
+
+ while (reloc->index < reloc->size)
+ {
+ struct relocation *rel = reloc->rel + reloc->index;
+
+ /* This relocation entry is ahead of us. */
+ if (rel->offset > offset)
+ return NULL;
+
+ reloc->index++;
+
+ if (rel->invalid)
+ continue;
+
+ if (rel->offset < offset)
+ {
+ if (st != skip_ok)
+ {
+ struct where reloc_where = where_from_reloc (reloc, where);
+ where_reset_2 (&reloc_where, rel->offset);
+ void (*w) (const struct where *, const char *, ...) = wr_error;
+ (*w) (&reloc_where,
+ ((const char *[])
+ {": relocation targets unreferenced portion of the section.\n",
+ ": relocation is mismatched.\n"})[st]);
+ }
+ continue;
+ }
+
+ return rel;
+ }
+
+ return NULL;
+}
+
+/* Skip all relocation up to offset, and leave cursor pointing at that
+ relocation, so that next time relocation_next is called, relocation
+ matching that offset is immediately yielded. */
+static void
+relocation_skip (struct relocation_data *reloc, uint64_t offset,
+ struct where *where, enum skip_type st)
+{
+ if (reloc != NULL && reloc->rel != NULL)
+ relocation_next (reloc, offset - 1, where, st);
+}
+
+/* Skip all the remaining relocations. */
+static void
+relocation_skip_rest (struct sec *sec)
+{
+ if (sec->rel.rel != NULL)
+ relocation_next (&sec->rel, (uint64_t)-1, &WHERE (sec->id, NULL),
+ skip_mismatched);
+}
+
+/* SYMPTR may be NULL, otherwise (**SYMPTR) has to yield valid memory
+ location. When the function returns, (*SYMPTR) is either NULL, in
+ which case we failed or didn't get around to obtain the symbol from
+ symbol table, or non-NULL, in which case the symbol was initialized. */
+static void
+relocate_one (struct elf_file *file,
+ struct relocation_data *reloc,
+ struct relocation *rel,
+ unsigned width, uint64_t *value, struct where *where,
+ enum section_id offset_into, GElf_Sym **symptr)
+{
+ if (rel->invalid)
+ return;
+
+ struct where reloc_where = where_from_reloc (reloc, where);
+ where_reset_2 (&reloc_where, rel->offset);
+ struct where reloc_ref_where = reloc_where;
+ reloc_ref_where.next = where;
+
+ GElf_Sym symbol_mem, *symbol;
+ if (symptr != NULL)
+ {
+ symbol = *symptr;
+ *symptr = NULL;
+ }
+ else
+ symbol = &symbol_mem;
+
+ if (offset_into == sec_invalid)
+ {
+ wr_message (mc_impact_3 | mc_reloc, &reloc_ref_where,
+ ": relocates a datum that shouldn't be relocated.\n");
+ return;
+ }
+
+ Elf_Type type = ebl_reloc_simple_type (file->ebl, rel->type);
+
+ unsigned rel_width;
+ switch (type)
+ {
+ case ELF_T_BYTE:
+ rel_width = 1;
+ break;
+
+ case ELF_T_HALF:
+ rel_width = 2;
+ break;
+
+ case ELF_T_WORD:
+ case ELF_T_SWORD:
+ rel_width = 4;
+ break;
+
+ case ELF_T_XWORD:
+ case ELF_T_SXWORD:
+ rel_width = 8;
+ break;
+
+ default:
+ /* This has already been diagnosed during the isolated
+ validation of relocation section. */
+ return;
+ };
+
+ if (rel_width != width)
+ wr_error (&reloc_ref_where,
+ ": %d-byte relocation relocates %d-byte datum.\n",
+ rel_width, width);
+
+ /* Tolerate that we might have failed to obtain the symbol table. */
+ if (reloc->symdata != NULL)
+ {
+ symbol = gelf_getsym (reloc->symdata, rel->symndx, symbol);
+ if (symptr != NULL)
+ *symptr = symbol;
+ if (symbol == NULL)
+ {
+ wr_error (&reloc_where,
+ ": couldn't obtain symbol #%d: %s.\n",
+ rel->symndx, elf_errmsg (-1));
+ return;
+ }
+
+ uint64_t section_index = symbol->st_shndx;
+ /* XXX We should handle SHN_XINDEX here. Or, instead, maybe it
+ would be possible to use dwfl, which already does XINDEX
+ translation. */
+
+ /* For ET_REL files, we do section layout manually. But we
+ don't update symbol table doing that. So instead of looking
+ at symbol value, look at section address. */
+ uint64_t sym_value = symbol->st_value;
+ if (file->ehdr.e_type == ET_REL
+ && ELF64_ST_TYPE (symbol->st_info) == STT_SECTION)
+ {
+ assert (sym_value == 0);
+ sym_value = file->sec[section_index].shdr.sh_addr;
+ }
+
+ /* It's target value, not section offset. */
+ if (offset_into == rel_value
+ || offset_into == rel_address
+ || offset_into == rel_exec)
+ {
+ /* If a target value is what's expected, then complain if
+ it's not either SHN_ABS, an SHF_ALLOC section, or
+ SHN_UNDEF. For data forms of address_size, an SHN_UNDEF
+ reloc is acceptable, otherwise reject it. */
+ if (!(section_index == SHN_ABS
+ || (offset_into == rel_address
+ && (section_index == SHN_UNDEF
+ || section_index == SHN_COMMON))))
+ {
+ if (offset_into != rel_address && section_index == SHN_UNDEF)
+ wr_error (&reloc_where,
+ ": relocation of an address is formed against SHN_UNDEF symbol"
+ " (symtab index %d).\n", rel->symndx);
+ else
+ {
+ GElf_Shdr *shdr = &file->sec[section_index].shdr;
+ if ((shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC)
+ wr_message (mc_reloc | mc_impact_3, &reloc_where,
+ ": associated section %s isn't SHF_ALLOC.\n",
+ file->sec[section_index].name);
+ if (offset_into == rel_exec
+ && (shdr->sh_flags & SHF_EXECINSTR) != SHF_EXECINSTR)
+ /* This may still be kosher, but it's suspicious. */
+ wr_message (mc_reloc | mc_impact_2, &reloc_where,
+ ": relocation against %s is suspicious, expected executable section.\n",
+ file->sec[section_index].name);
+ }
+ }
+ }
+ else
+ {
+ enum section_id id;
+ /* If symtab[symndx].st_shndx does not match the expected
+ debug section's index, complain. */
+ if (section_index >= file->size)
+ wr_error (&reloc_where,
+ ": invalid associated section #%" PRId64 ".\n",
+ section_index);
+ else if ((id = file->sec[section_index].id) != offset_into)
+ {
+ char *wh1 = id != sec_invalid
+ ? strdup (where_fmt (&WHERE (id, NULL), NULL))
+ : (char *)file->sec[section_index].name;
+ char *wh2 = strdup (where_fmt (&WHERE (offset_into, NULL), NULL));
+ wr_error (&reloc_where,
+ ": relocation references section %s, but %s was expected.\n",
+ wh1, wh2);
+ free (wh2);
+ if (id != sec_invalid)
+ free (wh1);
+ }
+ }
+
+ /* Only do the actual relocation if we have ET_REL files. For
+ non-ET_REL files, only do the above checking. */
+ if (file->ehdr.e_type == ET_REL)
+ {
+ *value = rel->addend + sym_value;
+ if (rel_width == 4)
+ *value = *value & (uint64_t)(uint32_t)-1;
+ }
+ }
+}
+
+static enum section_id
+reloc_target (uint8_t form, struct abbrev_attrib *at)
+{
+ switch (form)
+ {
+ case DW_FORM_strp:
+ return sec_str;
+
+ case DW_FORM_addr:
+
+ switch (at->name)
+ {
+ case DW_AT_low_pc:
+ case DW_AT_high_pc:
+ case DW_AT_entry_pc:
+ return rel_exec;
+
+ case DW_AT_const_value:
+ /* Appears in some kernel modules. It's not allowed by the
+ standard, but leave that for high-level checks. */
+ return rel_address;
+ };
+
+ break;
+
+ case DW_FORM_ref_addr:
+ return sec_info;
+
+ case DW_FORM_data1:
+ case DW_FORM_data2:
+ /* While these are technically legal, they are never used in
+ DWARF sections. So better mark them as illegal, and have
+ dwarflint flag them. */
+ return sec_invalid;
+
+ case DW_FORM_data4:
+ case DW_FORM_data8:
+
+ switch (at->name)
+ {
+ case DW_AT_stmt_list:
+ return sec_line;
+
+ case DW_AT_location:
+ case DW_AT_string_length:
+ case DW_AT_return_addr:
+ case DW_AT_data_member_location:
+ case DW_AT_frame_base:
+ case DW_AT_segment:
+ case DW_AT_static_link:
+ case DW_AT_use_location:
+ case DW_AT_vtable_elem_location:
+ return sec_loc;
+
+ case DW_AT_mac_info:
+ return sec_mac;
+
+ case DW_AT_ranges:
+ return sec_ranges;
+ }
+
+ break;
+
+ case DW_FORM_string:
+ case DW_FORM_ref1:
+ case DW_FORM_ref2:
+ case DW_FORM_ref4:
+ /* Shouldn't be relocated. */
+ return sec_invalid;
+
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_flag:
+ case DW_FORM_ref_udata:
+ assert (!"Can't be relocated!");
+
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_block:
+ assert (!"Should be handled specially!");
+ };
+
+ printf ("XXX don't know how to handle form=%s, at=%s\n",
+ dwarf_form_string (form), dwarf_attr_string (at->name));
+
+ return rel_value;
+}
+
+static enum section_id
+reloc_target_loc (uint8_t opcode)
+{
+ switch (opcode)
+ {
+ case DW_OP_call2:
+ case DW_OP_call4:
+ return sec_info;
+
+ case DW_OP_addr:
+ return rel_address;
+
+ case DW_OP_call_ref:
+ assert (!"Can't handle call_ref!");
+ };
+
+ printf ("XXX don't know how to handle opcode=%s\n",
+ dwarf_locexpr_opcode_string (opcode));
+
+ return rel_value;
+}
+
+static bool
+supported_version (unsigned version,
+ size_t num_supported, struct where *where, ...)
+{
+ bool retval = false;
+ va_list ap;
+ va_start (ap, where);
+ for (size_t i = 0; i < num_supported; ++i)
+ {
+ unsigned v = va_arg (ap, unsigned);
+ if (version == v)
+ {
+ retval = true;
+ break;
+ }
+ }
+ va_end (ap);
+
+ if (!retval)
+ wr_error (where, ": unsupported version %d.\n", version);
+
+ return retval;
+}
+
+static void
+check_range_relocations (enum message_category cat,
+ struct where *where,
+ struct elf_file *file,
+ GElf_Sym *begin_symbol,
+ GElf_Sym *end_symbol,
+ const char *description)
+{
+ if (begin_symbol != NULL
+ && end_symbol != NULL
+ && begin_symbol->st_shndx != end_symbol->st_shndx)
+ wr_message (cat | mc_impact_2 | mc_reloc, where,
+ ": %s relocated against different sections (%s and %s).\n",
+ description,
+ file->sec[begin_symbol->st_shndx].name,
+ file->sec[end_symbol->st_shndx].name);
+}
+
+/*
+ Returns:
+ -1 in case of error
+ +0 in case of no error, but the chain only consisted of a
+ terminating zero die.
+ +1 in case some dies were actually loaded
+ */
+static int
+read_die_chain (struct elf_file *file,
+ struct read_ctx *ctx,
+ struct cu *cu,
+ struct abbrev_table *abbrevs,
+ Elf_Data *strings,
+ bool dwarf_64, bool addr_64, bool ref_64,
+ struct ref_record *local_die_refs,
+ struct coverage *strings_coverage,
+ struct relocation_data *reloc,
+ struct cu_coverage *cu_coverage)
+{
+ bool got_die = false;
+ uint64_t sibling_addr = 0;
+ uint64_t die_off, prev_die_off = 0;
+ struct abbrev *abbrev = NULL;
+ struct abbrev *prev_abbrev = NULL;
+ struct where where = WHERE (sec_info, NULL);
+
+ while (!read_ctx_eof (ctx))
+ {
+ where = cu->where;
+ die_off = read_ctx_get_offset (ctx);
+ /* Shift reported DIE offset by CU offset, to match the way
+ readelf reports DIEs. */
+ where_reset_2 (&where, die_off + cu->offset);
+
+ uint64_t abbr_code;
+
+ if (!checked_read_uleb128 (ctx, &abbr_code, &where, "abbrev code"))
+ return -1;
+
+ /* Check sibling value advertised last time through the loop. */
+ if (sibling_addr != 0)
+ {
+ if (abbr_code == 0)
+ wr_error (&where,
+ ": is the last sibling in chain, but has a DW_AT_sibling attribute.\n");
+ else if (sibling_addr != die_off)
+ wr_error (&where, ": This DIE should have had its sibling at 0x%"
+ PRIx64 ", but it's at 0x%" PRIx64 " instead.\n",
+ sibling_addr, die_off);
+ sibling_addr = 0;
+ }
+ else if (abbr_code != 0
+ && abbrev != NULL && abbrev->has_children)
+ {
+ /* Even if it has children, the DIE can't have a sibling
+ attribute if it's the last DIE in chain. That's the
+ reason we can't simply check this when loading
+ abbrevs. */
+ struct where prev_where = where;
+ where_reset_2 (&prev_where, prev_die_off + cu->offset);
+ wr_message (mc_die_rel | mc_acc_suboptimal | mc_impact_4, &prev_where,
+ ": This DIE had children, but no DW_AT_sibling attribute.\n");
+ }
+
+ prev_die_off = die_off;
+
+ /* The section ended. */
+ if (abbr_code == 0)
+ break;
+ if (read_ctx_eof (ctx))
+ {
+ wr_error (&where, ": DIE chain not terminated with DIE with zero abbrev code.\n");
+ break;
+ }
+
+ prev_die_off = die_off;
+ got_die = true;
+
+ /* Find the abbrev matching the code. */
+ prev_abbrev = abbrev;
+ abbrev = abbrev_table_find_abbrev (abbrevs, abbr_code);
+ if (abbrev == NULL)
+ {
+ wr_error (&where,
+ ": abbrev section at 0x%" PRIx64
+ " doesn't contain code %" PRIu64 ".\n",
+ abbrevs->offset, abbr_code);
+ return -1;
+ }
+ abbrev->used = true;
+
+ addr_record_add (&cu->die_addrs, cu->offset + die_off);
+
+ uint64_t low_pc = (uint64_t)-1, high_pc = (uint64_t)-1;
+ bool low_pc_relocated = false, high_pc_relocated = false;
+ GElf_Sym low_pc_symbol_mem, *low_pc_symbol = &low_pc_symbol_mem;
+ GElf_Sym high_pc_symbol_mem, *high_pc_symbol = &high_pc_symbol_mem;
+
+ /* Attribute values. */
+ for (struct abbrev_attrib *it = abbrev->attribs;
+ it->name != 0; ++it)
+ {
+ where.ref = &it->where;
+
+ void record_ref (uint64_t addr, struct where *who, bool local)
+ {
+ struct ref_record *record = &cu->die_refs;
+ if (local)
+ {
+ assert (ctx->end > ctx->begin);
+ if (addr > (uint64_t)(ctx->end - ctx->begin))
+ {
+ wr_error (&where,
+ ": invalid reference outside the CU: 0x%" PRIx64 ".\n",
+ addr);
+ return;
+ }
+
+ /* Address holds a CU-local reference, so add CU
+ offset to turn it into section offset. */
+ addr += cu->offset;
+ record = local_die_refs;
+ }
+
+ if (record != NULL)
+ ref_record_add (record, addr, who);
+ }
+
+ uint8_t form = it->form;
+ bool indirect = form == DW_FORM_indirect;
+ if (indirect)
+ {
+ uint64_t value;
+ if (!checked_read_uleb128 (ctx, &value, &where,
+ "indirect attribute form"))
+ return -1;
+
+ if (!attrib_form_valid (value))
+ {
+ wr_error (&where,
+ ": invalid indirect form 0x%" PRIx64 ".\n", value);
+ return -1;
+ }
+ form = value;
+
+ if (it->name == DW_AT_sibling)
+ switch (check_sibling_form (form))
+ {
+ case -1:
+ wr_message (mc_die_rel | mc_impact_2, &where,
+ ": DW_AT_sibling attribute with (indirect) form DW_FORM_ref_addr.\n");
+ break;
+
+ case -2:
+ wr_error (&where,
+ ": DW_AT_sibling attribute with non-reference (indirect) form \"%s\".\n",
+ dwarf_form_string (value));
+ };
+ }
+
+ enum check_what_t
+ {
+ check_nothing = 0,
+ check_locptr,
+ check_lineptr,
+ check_rangeptr
+ };
+ static enum message_category mc_check[] =
+ {
+ [check_nothing] = mc_none,
+ [check_locptr] = mc_loc,
+ [check_lineptr] = mc_line,
+ [check_rangeptr] = mc_ranges
+ };
+
+ void do_check_ptr (enum check_what_t what, uint64_t value)
+ {
+ assert (what != check_nothing);
+
+ if (what == check_rangeptr && ((value % cu->address_size) != 0))
+ wr_message (mc_ranges | mc_impact_2, &where,
+ ": rangeptr value %#" PRIx64
+ " not aligned to CU address size.\n", value);
+
+ struct ref_record *ref = NULL;
+ switch (what)
+ {
+ case check_rangeptr:
+ ref = &cu->range_refs;
+ cu_coverage->need_ranges = true;
+ break;
+ case check_lineptr:
+ ref = &cu->line_refs;
+ break;
+ case check_locptr:
+ ref = &cu->loc_refs;
+ case check_nothing:
+ break;
+ };
+
+ ref_record_add (ref, value, &where);
+ }
+
+ enum check_what_t check_ptr = check_nothing;
+
+ if (is_location_attrib (it->name))
+ {
+ switch (form)
+ {
+ case DW_FORM_data8:
+ if (!dwarf_64)
+ wr_error (&where,
+ ": location attribute with form \"%s\" in 32-bit CU.\n",
+ dwarf_form_string (form));
+ /* fall-through */
+ case DW_FORM_data4:
+ check_ptr = check_locptr;
+ /* fall-through */
+ case DW_FORM_block1:
+ case DW_FORM_block2:
+ case DW_FORM_block4:
+ case DW_FORM_block:
+ break;
+
+ default:
+ /* Only print error if it's indirect. Otherwise we
+ gave diagnostic during abbrev loading. */
+ if (indirect)
+ wr_error (&where,
+ ": location attribute with invalid (indirect) form \"%s\".\n",
+ dwarf_form_string (form));
+ };
+ }
+ else if (it->name == DW_AT_ranges
+ || it->name == DW_AT_stmt_list)
+ switch (form)
+ {
+ case DW_FORM_data8:
+ if (!dwarf_64)
+ wr_error (&where,
+ ": %s with form DW_FORM_data8 in 32-bit CU.\n",
+ dwarf_attr_string (it->name));
+ /* fall-through */
+ case DW_FORM_data4:
+ if (it->name == DW_AT_ranges)
+ check_ptr = check_rangeptr;
+ else
+ {
+ assert (it->name == DW_AT_stmt_list);
+ check_ptr = check_lineptr;
+ }
+ break;
+
+ default:
+ /* Only print error if it's indirect. Otherwise we
+ gave diagnostic during abbrev loading. */
+ if (indirect)
+ wr_error (&where,
+ ": %s with invalid (indirect) form \"%s\".\n",
+ dwarf_attr_string (it->name),
+ dwarf_form_string (form));
+ };
+
+ uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->offset;
+ struct relocation *rel;
+ bool type_is_rel = file->ehdr.e_type == ET_REL;
+
+ switch (form)
+ {
+ case DW_FORM_strp:
+ {
+ uint64_t addr;
+ if (!read_ctx_read_offset (ctx, dwarf_64, &addr))
+ {
+ cant_read:
+ wr_error (&where, ": can't read attribute value.\n");
+ return -1;
+ }
+
+ if ((rel = relocation_next (reloc, ctx_offset,
+ &where, skip_mismatched)))
+ relocate_one (file, reloc, rel, dwarf_64 ? 8 : 4,
+ &addr, &where, sec_str, NULL);
+ else if (type_is_rel)
+ wr_message (mc_impact_2 | mc_die_other | mc_reloc | mc_strings,
+ &where, PRI_LACK_RELOCATION, "DW_FORM_strp");
+
+ if (strings == NULL)
+ wr_error (&where,
+ ": strp attribute, but no .debug_str data.\n");
+ else if (addr >= strings->d_size)
+ wr_error (&where,
+ ": Invalid offset outside .debug_str: 0x%" PRIx64 ".\n",
+ addr);
+ else
+ {
+ /* Record used part of .debug_str. */
+ const char *strp = (const char *)strings->d_buf + addr;
+
+ if (strings_coverage != NULL)
+ coverage_add (strings_coverage, addr, strlen (strp) + 1);
+ }
+
+ break;
+ }
+
+ case DW_FORM_string:
+ {
+ if (!read_ctx_read_str (ctx))
+ goto cant_read;
+ break;
+ }
+
+ case DW_FORM_addr:
+ case DW_FORM_ref_addr:
+ {
+ bool is_64 = form == DW_FORM_addr ? addr_64 : ref_64;
+ uint64_t addr;
+ if (!read_ctx_read_offset (ctx, is_64, &addr))
+ goto cant_read;
+
+ uint64_t *addrp = NULL;
+ bool *relocatedp = NULL;
+ GElf_Sym **symbolp = NULL;
+
+ switch (it->name)
+ {
+ case DW_AT_low_pc:
+ relocatedp = &low_pc_relocated;
+ symbolp = &low_pc_symbol;
+ addrp = &low_pc;
+ break;
+
+ case DW_AT_high_pc:
+ relocatedp = &high_pc_relocated;
+ symbolp = &high_pc_symbol;
+ addrp = &high_pc;
+ };
+
+ if ((rel = relocation_next (reloc, ctx_offset,
+ &where, skip_mismatched)))
+ {
+ relocate_one (file, reloc, rel, is_64 ? 8 : 4, &addr,
+ &where, reloc_target (form, it), symbolp);
+ if (relocatedp != NULL)
+ *relocatedp = true;
+ }
+ else
+ {
+ if (symbolp != NULL)
+ WIPE (*symbolp);
+ if (type_is_rel && addr != 0)
+ /* In non-rel files, neither addr, nor ref_addr
+ /need/ a relocation. We at least check that
+ ref_addr points to sensible datum by recording
+ the reference below. */
+ wr_message (mc_impact_2 | mc_die_rel | mc_reloc, &where,
+ PRI_LACK_RELOCATION, dwarf_form_string (form));
+ }
+ if (addrp != NULL)
+ *addrp = addr;
+
+ if (form == DW_FORM_ref_addr)
+ record_ref (addr, &where, false);
+
+ if (abbrev->tag == DW_TAG_compile_unit
+ || abbrev->tag == DW_TAG_partial_unit)
+ {
+ if (it->name == DW_AT_low_pc)
+ cu->low_pc = addr;
+
+ if (low_pc != (uint64_t)-1 && high_pc != (uint64_t)-1)
+ coverage_add (&cu_coverage->cov, low_pc, high_pc - low_pc);
+ }
+
+ break;
+ }
+
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ {
+ uint64_t value;
+ if (!checked_read_uleb128 (ctx, &value, &where,
+ "attribute value"))
+ return -1;
+
+ if (it->name == DW_AT_sibling)
+ sibling_addr = value;
+ else if (form == DW_FORM_ref_udata)
+ record_ref (value, &where, true);
+ break;
+ }
+
+ case DW_FORM_flag:
+ case DW_FORM_data1:
+ case DW_FORM_ref1:
+ {
+ /* Neither of these should be relocated. */
+ uint8_t value;
+ if (!read_ctx_read_ubyte (ctx, &value))
+ goto cant_read;
+
+ if (it->name == DW_AT_sibling)
+ sibling_addr = value;
+ else if (form == DW_FORM_ref1)
+ record_ref (value, &where, true);
+ break;
+ }
+
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ {
+ /* Neither of these should be relocated. */
+ uint16_t value;
+ if (!read_ctx_read_2ubyte (ctx, &value))
+ goto cant_read;
+
+ if (it->name == DW_AT_sibling)
+ sibling_addr = value;
+ else if (form == DW_FORM_ref2)
+ record_ref (value, &where, true);
+ break;
+ }
+
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ {
+ uint32_t raw_value;
+ if (!read_ctx_read_4ubyte (ctx, &raw_value))
+ goto cant_read;
+
+ /* DW_FORM_ref4 shouldn't be relocated. */
+ uint64_t value = raw_value;
+ if (form == DW_FORM_data4)
+ {
+ if ((rel = relocation_next (reloc, ctx_offset,
+ &where, skip_mismatched)))
+ relocate_one (file, reloc, rel, 4, &value, &where,
+ reloc_target (form, it), NULL);
+ else if (type_is_rel && check_ptr != check_nothing)
+ wr_message (mc_impact_2 | mc_die_other | mc_reloc
+ | mc_check[check_ptr],
+ &where, PRI_LACK_RELOCATION,
+ dwarf_form_string (form));
+ }
+
+ if (it->name == DW_AT_sibling)
+ sibling_addr = value;
+ else if (check_ptr != check_nothing)
+ do_check_ptr (check_ptr, value);
+ else if (form == DW_FORM_ref4)
+ record_ref (value, &where, true);
+ break;
+ }
+
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ {
+ uint64_t value;
+ if (!read_ctx_read_8ubyte (ctx, &value))
+ goto cant_read;
+
+ /* DW_FORM_ref8 shouldn't be relocated. */
+ if (form == DW_FORM_data8)
+ {
+ if ((rel = relocation_next (reloc, ctx_offset,
+ &where, skip_mismatched)))
+ relocate_one (file, reloc, rel, 8, &value, &where,
+ reloc_target (form, it), NULL);
+ else if (type_is_rel && check_ptr != check_nothing)
+ wr_message (mc_impact_2 | mc_die_other | mc_reloc
+ | mc_check[check_ptr],
+ &where, PRI_LACK_RELOCATION,
+ dwarf_form_string (form));
+ }
+
+ if (it->name == DW_AT_sibling)
+ sibling_addr = value;
+ else if (check_ptr != check_nothing)
+ do_check_ptr (check_ptr, value);
+ else if (form == DW_FORM_ref8)
+ record_ref (value, &where, true);
+ break;
+ }
+
+ case DW_FORM_sdata:
+ {
+ int64_t value;
+ if (!checked_read_sleb128 (ctx, &value, &where,
+ "attribute value"))
+ return -1;
+ break;
+ }
+
+ case DW_FORM_block:
+ {
+ int width = 0;
+ uint64_t length;
+ goto process_DW_FORM_block;
+
+ case DW_FORM_block1:
+ width = 1;
+ goto process_DW_FORM_block;
+
+ case DW_FORM_block2:
+ width = 2;
+ goto process_DW_FORM_block;
+
+ case DW_FORM_block4:
+ width = 4;
+
+ process_DW_FORM_block:
+ if (width == 0)
+ {
+ if (!checked_read_uleb128 (ctx, &length, &where,
+ "attribute value"))
+ return -1;
+ }
+ else if (!read_ctx_read_var (ctx, width, &length))
+ goto cant_read;
+
+ if (is_location_attrib (it->name))
+ {
+ uint64_t expr_start = cu->offset + read_ctx_get_offset (ctx);
+ if (!check_location_expression (file, ctx, expr_start,
+ reloc, length,
+ &where, addr_64))
+ return -1;
+ }
+ else
+ relocation_skip (reloc,
+ read_ctx_get_offset (ctx) + length,
+ &where, skip_mismatched);
+
+ if (!read_ctx_skip (ctx, length))
+ goto cant_read;
+
+ break;
+ }
+
+ case DW_FORM_indirect:
+ wr_error (&where, ": indirect form is again indirect.\n");
+ return -1;
+
+ default:
+ wr_error (&where,
+ ": internal error: unhandled form 0x%x.\n", form);
+ }
+ }
+ where.ref = NULL;
+
+ if (high_pc != (uint64_t)-1 && low_pc != (uint64_t)-1)
+ {
+ if (high_pc_relocated != low_pc_relocated)
+ wr_message (mc_die_other | mc_impact_2 | mc_reloc, &where,
+ ": only one of DW_AT_low_pc and DW_AT_high_pc is relocated.\n");
+ else
+ check_range_relocations (mc_die_other, &where,
+ file,
+ low_pc_symbol, high_pc_symbol,
+ "DW_AT_low_pc and DW_AT_high_pc");
+ }
+
+ where.ref = &abbrev->where;
+
+ if (abbrev->has_children)
+ {
+ int st = read_die_chain (file, ctx, cu, abbrevs, strings,
+ dwarf_64, addr_64, ref_64,
+ local_die_refs,
+ strings_coverage, reloc,
+ cu_coverage);
+ if (st == -1)
+ return -1;
+ else if (st == 0)
+ wr_message (mc_impact_3 | mc_acc_suboptimal | mc_die_rel,
+ &where,
+ ": abbrev has_children, but the chain was empty.\n");
+ }
+ }
+
+ if (sibling_addr != 0)
+ wr_error (&where,
+ ": this DIE should have had its sibling at 0x%"
+ PRIx64 ", but the DIE chain ended.\n", sibling_addr);
+
+ return got_die ? 1 : 0;
+}
+
+static bool
+check_cu_structural (struct elf_file *file,
+ struct read_ctx *ctx,
+ struct cu *const cu,
+ struct abbrev_table *abbrev_chain,
+ Elf_Data *strings,
+ bool dwarf_64,
+ struct coverage *strings_coverage,
+ struct relocation_data *reloc,
+ struct cu_coverage *cu_coverage)
+{
+ uint8_t address_size;
+ bool retval = true;
+
+ /* Version. */
+ uint16_t version;
+ if (!read_ctx_read_2ubyte (ctx, &version))
+ {
+ wr_error (&cu->where, ": can't read version.\n");
+ return false;
+ }
+ if (!supported_version (version, 2, &cu->where, 2, 3))
+ return false;
+ if (version == 2 && dwarf_64)
+ /* Keep going. It's a standard violation, but we may still be
+ able to read the unit under consideration and do high-level
+ checks. */
+ wr_error (&cu->where, ": invalid 64-bit unit in DWARF 2 format.\n");
+
+ /* Abbrev offset. */
+ uint64_t abbrev_offset;
+ uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->offset;
+ if (!read_ctx_read_offset (ctx, dwarf_64, &abbrev_offset))
+ {
+ wr_error (&cu->where, ": can't read abbrev offset.\n");
+ return false;
+ }
+
+ struct relocation *rel
+ = relocation_next (reloc, ctx_offset, &cu->where, skip_mismatched);
+ if (rel != NULL)
+ relocate_one (file, reloc, rel, dwarf_64 ? 8 : 4,
+ &abbrev_offset, &cu->where, sec_abbrev, NULL);
+ else if (file->ehdr.e_type == ET_REL)
+ wr_message (mc_impact_2 | mc_info | mc_reloc, &cu->where,
+ PRI_LACK_RELOCATION, "abbrev offset");
+
+ /* Address size. */
+ if (!read_ctx_read_ubyte (ctx, &address_size))
+ {
+ wr_error (&cu->where, ": can't read address size.\n");
+ return false;
+ }
+ if (address_size != 4 && address_size != 8)
+ {
+ wr_error (&cu->where,
+ ": invalid address size: %d (only 4 or 8 allowed).\n",
+ address_size);
+ return false;
+ }
+ cu->address_size = address_size;
+
+ /* Look up Abbrev table for this CU. */
+ struct abbrev_table *abbrevs = abbrev_chain;
+ for (; abbrevs != NULL; abbrevs = abbrevs->next)
+ if (abbrevs->offset == abbrev_offset)
+ break;
+
+ if (abbrevs == NULL)
+ {
+ wr_error (&cu->where,
+ ": couldn't find abbrev section with offset 0x%" PRIx64 ".\n",
+ abbrev_offset);
+ return false;
+ }
+
+ abbrevs->used = true;
+
+ /* Read DIEs. */
+ struct ref_record local_die_refs;
+ WIPE (local_die_refs);
+
+ cu->cudie_offset = read_ctx_get_offset (ctx) + cu->offset;
+ bool addr_64 = address_size == 8;
+ bool ref_64 = version == 2 ? addr_64 : (assert (version == 3), dwarf_64);
+ if (read_die_chain (file, ctx, cu, abbrevs, strings,
+ dwarf_64, addr_64, ref_64,
+ &local_die_refs, strings_coverage,
+ (reloc != NULL && reloc->size > 0) ? reloc : NULL,
+ cu_coverage) < 0)
+ {
+ abbrevs->skip_check = true;
+ retval = false;
+ }
+ else if (!check_die_references (cu, &local_die_refs))
+ retval = false;
+
+ ref_record_free (&local_die_refs);
+ return retval;
+}
+
+static struct cu *
+check_info_structural (struct elf_file *file,
+ struct sec *sec,
+ struct abbrev_table *abbrev_chain,
+ Elf_Data *strings,
+ struct cu_coverage *cu_coverage)
+{
+ struct read_ctx ctx;
+ read_ctx_init (&ctx, file, sec->data);
+
+ struct ref_record die_refs;
+ WIPE (die_refs);
+
+ struct cu *cu_chain = NULL;
+
+ bool success = true;
+
+ struct coverage strings_coverage_mem, *strings_coverage = NULL;
+ if (strings != NULL && check_category (mc_strings))
+ {
+ WIPE (strings_coverage_mem);
+ strings_coverage = &strings_coverage_mem;
+ }
+
+ struct relocation_data *reloc = sec->rel.size > 0 ? &sec->rel : NULL;
+ while (!read_ctx_eof (&ctx))
+ {
+ const unsigned char *cu_begin = ctx.ptr;
+ struct where where = WHERE (sec_info, NULL);
+ where_reset_1 (&where, read_ctx_get_offset (&ctx));
+
+ struct cu *cur = xcalloc (1, sizeof (*cur));
+ cur->offset = where.addr1;
+ cur->next = cu_chain;
+ cur->where = where;
+ cur->low_pc = (uint64_t)-1;
+ cu_chain = cur;
+
+ uint32_t size32;
+ uint64_t size;
+ bool dwarf_64 = false;
+
+ /* Reading CU header is a bit tricky, because we don't know if
+ we have run into (superfluous but allowed) zero padding. */
+ if (!read_ctx_need_data (&ctx, 4)
+ && check_zero_padding (&ctx, mc_info | mc_header, &where))
+ break;
+
+ /* CU length. */
+ if (!read_ctx_read_4ubyte (&ctx, &size32))
+ {
+ wr_error (&where, ": can't read CU length.\n");
+ success = false;
+ break;
+ }
+ if (size32 == 0 && check_zero_padding (&ctx, mc_info | mc_header, &where))
+ break;
+
+ if (!read_size_extra (&ctx, size32, &size, &dwarf_64, &where))
+ {
+ success = false;
+ break;
+ }
+
+ if (!read_ctx_need_data (&ctx, size))
+ {
+ wr_error (&where,
+ ": section doesn't have enough data"
+ " to read CU of size %" PRId64 ".\n", size);
+ ctx.ptr = ctx.end;
+ success = false;
+ break;
+ }
+
+ const unsigned char *cu_end = ctx.ptr + size;
+ cur->length = cu_end - cu_begin; // Length including the length field.
+
+ /* version + debug_abbrev_offset + address_size */
+ uint64_t cu_header_size = 2 + (dwarf_64 ? 8 : 4) + 1;
+ if (size < cu_header_size)
+ {
+ wr_error (&where, ": claimed length of %" PRIx64
+ " doesn't even cover CU header.\n", size);
+ success = false;
+ break;
+ }
+ else
+ {
+ /* Make CU context begin just before the CU length, so that DIE
+ offsets are computed correctly. */
+ struct read_ctx cu_ctx;
+ if (!read_ctx_init_sub (&cu_ctx, &ctx, cu_begin, cu_end))
+ {
+ not_enough:
+ wr_error (&where, PRI_NOT_ENOUGH, "next CU");
+ success = false;
+ break;
+ }
+ cu_ctx.ptr = ctx.ptr;
+
+ if (!check_cu_structural (file, &cu_ctx, cur, abbrev_chain,
+ strings, dwarf_64,
+ strings_coverage, reloc,
+ cu_coverage))
+ {
+ success = false;
+ break;
+ }
+ if (cu_ctx.ptr != cu_ctx.end
+ && !check_zero_padding (&cu_ctx, mc_info, &where))
+ wr_message_padding_n0 (mc_info, &where,
+ read_ctx_get_offset (&ctx),
+ read_ctx_get_offset (&ctx) + size);
+ }
+
+ if (!read_ctx_skip (&ctx, size))
+ goto not_enough;
+ }
+
+ if (success)
+ {
+ if (ctx.ptr != ctx.end)
+ /* Did we read up everything? */
+ wr_message (mc_die_other | mc_impact_4,
+ &WHERE (sec_info, NULL),
+ ": CU lengths don't exactly match Elf_Data contents.");
+ else
+ /* Did we consume all the relocations? */
+ relocation_skip_rest (sec);
+
+ /* If we managed to read up everything, now do abbrev usage
+ analysis. */
+ for (struct abbrev_table *abbrevs = abbrev_chain;
+ abbrevs != NULL; abbrevs = abbrevs->next)
+ {
+ if (!abbrevs->used)
+ {
+ struct where wh = WHERE (sec_abbrev, NULL);
+ where_reset_1 (&wh, abbrevs->offset);
+ wr_message (mc_impact_4 | mc_acc_bloat | mc_abbrevs, &wh,
+ ": abbreviation table is never used.\n");
+ }
+ else if (!abbrevs->skip_check)
+ for (size_t i = 0; i < abbrevs->size; ++i)
+ if (!abbrevs->abbr[i].used)
+ wr_message (mc_impact_3 | mc_acc_bloat | mc_abbrevs,
+ &abbrevs->abbr[i].where,
+ ": abbreviation is never used.\n");
+ }
+ }
+
+
+ int address_size = 0;
+ if (cu_chain != NULL)
+ {
+ uint64_t offset = 0;
+ for (struct cu *it = cu_chain; it != NULL; it = it->next)
+ if (address_size == 0)
+ {
+ address_size = it->address_size;
+ offset = it->where.addr1;
+ }
+ else if (address_size != it->address_size)
+ {
+ /* XXX would be nice to check consistency of CU address
+ size declared in various other .debug_* sections. */
+ wr_message (mc_info, &it->where,
+ ": has different address size than CU 0x%"
+ PRIx64 ".\n", offset);
+ address_size = 0;
+ break;
+ }
+ }
+
+ bool references_sound = check_global_die_references (cu_chain);
+ ref_record_free (&die_refs);
+
+ if (strings_coverage != NULL)
+ {
+ if (success)
+ coverage_find_holes (strings_coverage, 0, strings->d_size, found_hole,
+ &((struct hole_info)
+ {sec_str, mc_strings, 0, strings->d_buf}));
+ coverage_free (strings_coverage);
+ }
+
+ if (!success || !references_sound)
+ {
+ cu_free (cu_chain);
+ cu_chain = NULL;
+ }
+
+ /* Reverse the chain, so that it's organized "naturally". Has
+ significant impact on performance when handling loc_ref and
+ range_ref fields in loc/range validation. */
+ struct cu *last = NULL;
+ for (struct cu *it = cu_chain; it != NULL; )
+ {
+ struct cu *next = it->next;
+ it->next = last;
+ last = it;
+ it = next;
+ }
+ cu_chain = last;
+
+ return cu_chain;
+}
+
+static struct coverage_map *
+coverage_map_alloc_XA (struct elf_file *elf, bool allow_overlap)
+{
+ struct coverage_map *ret = xmalloc (sizeof (*ret));
+ if (!coverage_map_init (ret, elf,
+ SHF_EXECINSTR | SHF_ALLOC,
+ SHF_ALLOC,
+ allow_overlap))
+ {
+ free (ret);
+ return NULL;
+ }
+ return ret;
+}
+
+static void
+coverage_map_free_XA (struct coverage_map *coverage_map)
+{
+ if (coverage_map != NULL)
+ {
+ coverage_map_free (coverage_map);
+ free (coverage_map);
+ }
+}
+
+static void
+compare_coverage (struct elf_file *file,
+ struct coverage *coverage, struct coverage *other,
+ enum section_id id, char const *what)
+{
+ struct coverage *cov = coverage_clone (coverage);
+ coverage_remove_all (cov, other);
+
+ bool hole (uint64_t start, uint64_t length, void *user)
+ {
+ /* We need to check alignment vs. the covered section. Find
+ where the hole lies. */
+ struct elf_file *elf = user;
+ struct sec *sec = NULL;
+ for (size_t i = 1; i < elf->size; ++i)
+ {
+ struct sec *it = elf->sec + i;
+ GElf_Shdr *shdr = &it->shdr;
+ Elf64_Addr s_end = shdr->sh_addr + shdr->sh_size;
+ if (start >= shdr->sh_addr && start + length < s_end)
+ {
+ sec = it;
+ /* Simply assume the first section that the hole
+ intersects. */
+ break;
+ }
+ }
+
+ if (sec == NULL
+ || !necessary_alignment (start, length, sec->shdr.sh_addralign))
+ {
+ char buf[128];
+ wr_message (mc_aranges | mc_impact_3, &WHERE (id, NULL),
+ ": addresses %s are covered with CUs, but not with %s.\n",
+ range_fmt (buf, sizeof buf, start, start + length), what);
+ }
+
+ if (sec == NULL)
+ wr_error (NULL, "Couldn't find the section containing the above hole.\n");
+
+ return true;
+ }
+
+ coverage_find_ranges (cov, &hole, file);
+ coverage_free (cov);
+}
+
+/* COVERAGE is portion of address space covered by CUs (either via
+ low_pc/high_pc pairs, or via DW_AT_ranges references). If
+ non-NULL, analysis of arange coverage is done against that set. */
+static bool
+check_aranges_structural (struct elf_file *file,
+ struct sec *sec,
+ struct cu *cu_chain,
+ struct coverage *coverage)
+{
+ struct read_ctx ctx;
+ read_ctx_init (&ctx, file, sec->data);
+
+ bool retval = true;
+
+ struct coverage *aranges_coverage
+ = coverage != NULL ? calloc (1, sizeof (struct coverage)) : NULL;
+
+ while (!read_ctx_eof (&ctx))
+ {
+ struct where where = WHERE (sec_aranges, NULL);
+ where_reset_1 (&where, read_ctx_get_offset (&ctx));
+ const unsigned char *atab_begin = ctx.ptr;
+
+ inline void aranges_coverage_add (uint64_t begin, uint64_t length)
+ {
+ if (coverage_is_overlap (aranges_coverage, begin, length)
+ && !be_gnu && !be_tolerant)
+ {
+ char buf[128];
+ /* Not a show stopper, this shouldn't derail high-level. */
+ wr_message (mc_aranges | mc_impact_2 | mc_error, &where,
+ ": the range %s overlaps with another one.\n",
+ range_fmt (buf, sizeof buf, begin, begin + length));
+ }
+
+ coverage_add (aranges_coverage, begin, length);
+ }
+
+ /* Size. */
+ uint32_t size32;
+ uint64_t size;
+ bool dwarf_64;
+ if (!read_ctx_read_4ubyte (&ctx, &size32))
+ {
+ wr_error (&where, ": can't read table length.\n");
+ return false;
+ }
+ if (!read_size_extra (&ctx, size32, &size, &dwarf_64, &where))
+ return false;
+
+ struct read_ctx sub_ctx;
+ const unsigned char *atab_end = ctx.ptr + size;
+ if (!read_ctx_init_sub (&sub_ctx, &ctx, atab_begin, atab_end))
+ {
+ not_enough:
+ wr_error (&where, PRI_NOT_ENOUGH, "next table");
+ return false;
+ }
+
+ sub_ctx.ptr = ctx.ptr;
+
+ /* Version. */
+ uint16_t version;
+ if (!read_ctx_read_2ubyte (&sub_ctx, &version))
+ {
+ wr_error (&where, ": can't read version.\n");
+ retval = false;
+ goto next;
+ }
+ if (!supported_version (version, 1, &where, 2))
+ {
+ retval = false;
+ goto next;
+ }
+
+ /* CU offset. */
+ uint64_t cu_offset;
+ uint64_t ctx_offset = sub_ctx.ptr - ctx.begin;
+ if (!read_ctx_read_offset (&sub_ctx, dwarf_64, &cu_offset))
+ {
+ wr_error (&where, ": can't read debug info offset.\n");
+ retval = false;
+ goto next;
+ }
+
+ struct relocation *rel;
+ if ((rel = relocation_next (&sec->rel, ctx_offset,
+ &where, skip_mismatched)))
+ relocate_one (file, &sec->rel, rel, dwarf_64 ? 8 : 4,
+ &cu_offset, &where, sec_info, NULL);
+ else if (file->ehdr.e_type == ET_REL)
+ wr_message (mc_impact_2 | mc_aranges | mc_reloc | mc_header, &where,
+ PRI_LACK_RELOCATION, "debug info offset");
+
+ struct cu *cu = NULL;
+ if (cu_chain != NULL && (cu = cu_find_cu (cu_chain, cu_offset)) == NULL)
+ wr_error (&where, ": unresolved reference to " PRI_CU ".\n", cu_offset);
+
+ struct where where_cudie;
+ if (cu != NULL)
+ {
+ where_cudie = WHERE (sec_info, NULL);
+ where_reset_1 (&where_cudie, cu->cudie_offset);
+ where.ref = &where_cudie;
+ where_cudie.formatting = wf_cudie;
+ if (cu->has_arange)
+ wr_message (mc_impact_2 | mc_aranges | mc_header, &where,
+ ": there has already been arange section for this CU.\n");
+ else
+ cu->has_arange = true;
+ }
+
+ /* Address size. */
+ uint8_t address_size;
+ if (!read_ctx_read_ubyte (&sub_ctx, &address_size))
+ {
+ wr_error (&where, ": can't read address size.\n");
+ retval = false;
+ goto next;
+ }
+ if (cu != NULL)
+ {
+ if (address_size != cu->address_size)
+ {
+ wr_error (&where,
+ ": address size %d doesn't match referred CU.\n",
+ address_size);
+ retval = false;
+ }
+ }
+ /* Try to parse it anyway, unless the address size is wacky. */
+ else if (address_size != 4 && address_size != 8)
+ {
+ wr_error (&where, ": invalid address size: %d.\n", address_size);
+ retval = false;
+ goto next;
+ }
+
+ /* Segment size. */
+ uint8_t segment_size;
+ if (!read_ctx_read_ubyte (&sub_ctx, &segment_size))
+ {
+ wr_error (&where, ": can't read unit segment size.\n");
+ retval = false;
+ goto next;
+ }
+ if (segment_size != 0)
+ {
+ wr_warning (&where, ": dwarflint can't handle segment_size != 0.\n");
+ retval = false;
+ goto next;
+ }
+
+
+ /* 7.20: The first tuple following the header in each set begins
+ at an offset that is a multiple of the size of a single tuple
+ (that is, twice the size of an address). The header is
+ padded, if necessary, to the appropriate boundary. */
+ const uint8_t tuple_size = 2 * address_size;
+ uint64_t off = read_ctx_get_offset (&sub_ctx);
+ if ((off % tuple_size) != 0)
+ {
+ uint64_t noff = ((off / tuple_size) + 1) * tuple_size;
+ for (uint64_t i = off; i < noff; ++i)
+ {
+ uint8_t c;
+ if (!read_ctx_read_ubyte (&sub_ctx, &c))
+ {
+ wr_error (&where,
+ ": section ends after the header, "
+ "but before the first entry.\n");
+ retval = false;
+ goto next;
+ }
+ if (c != 0)
+ wr_message (mc_impact_2 | mc_aranges | mc_header, &where,
+ ": non-zero byte at 0x%" PRIx64
+ " in padding before the first entry.\n",
+ read_ctx_get_offset (&sub_ctx));
+ }
+ }
+ assert ((read_ctx_get_offset (&sub_ctx) % tuple_size) == 0);
+
+ while (!read_ctx_eof (&sub_ctx))
+ {
+ /* We would like to report aranges the same way that readelf
+ does. But readelf uses index of the arange in the array
+ as returned by dwarf_getaranges, which sorts the aranges
+ beforehand. We don't want to disturb the memory this
+ way, the better to catch structural errors accurately.
+ So report arange offset instead. If this becomes a
+ problem, we will achieve this by two-pass analysis. */
+ where_reset_2 (&where, read_ctx_get_offset (&sub_ctx));
+
+ /* Record address. */
+ uint64_t address;
+ ctx_offset = sub_ctx.ptr - ctx.begin;
+ bool address_relocated = false;
+ if (!read_ctx_read_var (&sub_ctx, address_size, &address))
+ {
+ wr_error (&where, ": can't read address field.\n");
+ retval = false;
+ goto next;
+ }
+
+ if ((rel = relocation_next (&sec->rel, ctx_offset,
+ &where, skip_mismatched)))
+ {
+ address_relocated = true;
+ relocate_one (file, &sec->rel, rel, address_size,
+ &address, &where, rel_address, NULL);
+ }
+ else if (file->ehdr.e_type == ET_REL && address != 0)
+ wr_message (mc_impact_2 | mc_aranges | mc_reloc, &where,
+ PRI_LACK_RELOCATION, "address field");
+
+ /* Record length. */
+ uint64_t length;
+ if (!read_ctx_read_var (&sub_ctx, address_size, &length))
+ {
+ wr_error (&where, ": can't read length field.\n");
+ retval = false;
+ goto next;
+ }
+
+ if (address == 0 && length == 0 && !address_relocated)
+ break;
+
+ if (length == 0)
+ /* DWARF 3 spec, 6.1.2 Lookup by Address: Each descriptor
+ is a pair consisting of the beginning address [...],
+ followed by the _non-zero_ length of that range. */
+ wr_error (&where, ": zero-length address range.\n");
+ /* Skip coverage analysis if we have errors. */
+ else if (retval && aranges_coverage)
+ aranges_coverage_add (address, length);
+ }
+
+ if (sub_ctx.ptr != sub_ctx.end
+ && !check_zero_padding (&sub_ctx, mc_aranges,
+ &WHERE (where.section, NULL)))
+ {
+ wr_message_padding_n0 (mc_aranges | mc_error,
+ &WHERE (where.section, NULL),
+ read_ctx_get_offset (&sub_ctx),
+ read_ctx_get_offset (&sub_ctx) + size);
+ retval = false;
+ }
+
+ next:
+ if (!read_ctx_skip (&ctx, size))
+ /* A "can't happen" error. */
+ goto not_enough;
+ }
+
+ if (aranges_coverage != NULL)
+ {
+ compare_coverage (file, coverage, aranges_coverage,
+ sec_aranges, "aranges");
+ coverage_free (aranges_coverage);
+ }
+
+ return retval;
+}
+
+static bool
+check_pub_structural (struct elf_file *file,
+ struct sec *sec,
+ struct cu *cu_chain)
+{
+ struct read_ctx ctx;
+ read_ctx_init (&ctx, file, sec->data);
+ bool retval = true;
+
+ while (!read_ctx_eof (&ctx))
+ {
+ struct where where = WHERE (sec->id, NULL);
+ where_reset_1 (&where, read_ctx_get_offset (&ctx));
+ const unsigned char *set_begin = ctx.ptr;
+
+ /* Size. */
+ uint32_t size32;
+ uint64_t size;
+ bool dwarf_64;
+ if (!read_ctx_read_4ubyte (&ctx, &size32))
+ {
+ wr_error (&where, ": can't read table length.\n");
+ return false;
+ }
+ if (!read_size_extra (&ctx, size32, &size, &dwarf_64, &where))
+ return false;
+
+ struct read_ctx sub_ctx;
+ const unsigned char *set_end = ctx.ptr + size;
+ if (!read_ctx_init_sub (&sub_ctx, &ctx, set_begin, set_end))
+ {
+ not_enough:
+ wr_error (&where, PRI_NOT_ENOUGH, "next set");
+ return false;
+ }
+ sub_ctx.ptr = ctx.ptr;
+
+ /* Version. */
+ uint16_t version;
+ if (!read_ctx_read_2ubyte (&sub_ctx, &version))
+ {
+ wr_error (&where, ": can't read set version.\n");
+ retval = false;
+ goto next;
+ }
+ if (!supported_version (version, 1, &where, 2))
+ {
+ retval = false;
+ goto next;
+ }
+
+ /* CU offset. */
+ uint64_t cu_offset; /* Offset of related CU. */
+ uint64_t ctx_offset = sub_ctx.ptr - ctx.begin;
+ if (!read_ctx_read_offset (&sub_ctx, dwarf_64, &cu_offset))
+ {
+ wr_error (&where, ": can't read debug info offset.\n");
+ retval = false;
+ goto next;
+ }
+
+ struct relocation *rel;
+ if ((rel = relocation_next (&sec->rel, ctx_offset,
+ &where, skip_mismatched)))
+ relocate_one (file, &sec->rel, rel, dwarf_64 ? 8 : 4,
+ &cu_offset, &where, sec_info, NULL);
+ else if (file->ehdr.e_type == ET_REL)
+ wr_message (mc_impact_2 | mc_pubtables | mc_reloc | mc_header, &where,
+ PRI_LACK_RELOCATION, "debug info offset");
+
+ struct cu *cu = NULL;
+ if (cu_chain != NULL && (cu = cu_find_cu (cu_chain, cu_offset)) == NULL)
+ wr_error (&where, ": unresolved reference to " PRI_CU ".\n", cu_offset);
+ if (cu != NULL)
+ {
+ where.ref = &cu->where;
+ bool *has = sec->id == sec_pubnames
+ ? &cu->has_pubnames : &cu->has_pubtypes;
+ if (*has)
+ wr_message (mc_impact_2 | mc_pubtables | mc_header, &where,
+ ": there has already been section for this CU.\n");
+ else
+ *has = true;
+ }
+
+ /* Covered length. */
+ uint64_t cu_len;
+ if (!read_ctx_read_offset (&sub_ctx, dwarf_64, &cu_len))
+ {
+ wr_error (&where, ": can't read covered length.\n");
+ retval = false;
+ goto next;
+ }
+ if (cu != NULL && cu_len != cu->length)
+ {
+ wr_error (&where,
+ ": the table covers length %" PRId64
+ " but CU has length %" PRId64 ".\n", cu_len, cu->length);
+ retval = false;
+ goto next;
+ }
+
+ /* Records... */
+ while (!read_ctx_eof (&sub_ctx))
+ {
+ ctx_offset = sub_ctx.ptr - ctx.begin;
+ where_reset_2 (&where, ctx_offset);
+
+ uint64_t offset;
+ if (!read_ctx_read_offset (&sub_ctx, dwarf_64, &offset))
+ {
+ wr_error (&where, ": can't read offset field.\n");
+ retval = false;
+ goto next;
+ }
+ if (offset == 0)
+ break;
+
+ if (cu != NULL
+ && !addr_record_has_addr (&cu->die_addrs, offset + cu->offset))
+ {
+ wr_error (&where,
+ ": unresolved reference to " PRI_DIE ".\n", offset);
+ retval = false;
+ goto next;
+ }
+
+ uint8_t c;
+ do
+ if (!read_ctx_read_ubyte (&sub_ctx, &c))
+ {
+ wr_error (&where, ": can't read symbol name.\n");
+ retval = false;
+ goto next;
+ }
+ while (c);
+ }
+
+ if (sub_ctx.ptr != sub_ctx.end
+ && !check_zero_padding (&sub_ctx, mc_pubtables,
+ &WHERE (sec->id, NULL)))
+ {
+ wr_message_padding_n0 (mc_pubtables | mc_error,
+ &WHERE (sec->id, NULL),
+ read_ctx_get_offset (&sub_ctx),
+ read_ctx_get_offset (&sub_ctx) + size);
+ retval = false;
+ }
+
+ next:
+ if (!read_ctx_skip (&ctx, size))
+ goto not_enough;
+ }
+
+ if (retval)
+ relocation_skip_rest (sec);
+
+ return retval;
+}
+
+
+/* Operands are passed back as attribute forms. In particular,
+ DW_FORM_dataX for X-byte operands, DW_FORM_[us]data for
+ ULEB128/SLEB128 operands, and DW_FORM_addr for 32b/64b operands.
+ If the opcode takes no operands, 0 is passed.
+
+ Return value is false if we couldn't determine (i.e. invalid
+ opcode).
+ */
+static bool
+get_location_opcode_operands (uint8_t opcode, uint8_t *op1, uint8_t *op2)
+{
+ switch (opcode)
+ {
+#define DEF_DW_OP(OPCODE, OP1, OP2) \
+ case OPCODE: *op1 = OP1; *op2 = OP2; return true;
+# include "expr_opcodes.h"
+#undef DEF_DW_OP
+ default:
+ return false;
+ };
+}
+
+static bool
+check_location_expression (struct elf_file *file,
+ struct read_ctx *parent_ctx,
+ uint64_t init_off,
+ struct relocation_data *reloc,
+ size_t length,
+ struct where *wh,
+ bool addr_64)
+{
+ struct read_ctx ctx;
+ if (!read_ctx_init_sub (&ctx, parent_ctx, parent_ctx->ptr,
+ parent_ctx->ptr + length))
+ {
+ wr_error (wh, PRI_NOT_ENOUGH, "location expression");
+ return false;
+ }
+
+ struct ref_record oprefs;
+ WIPE (oprefs);
+
+ struct addr_record opaddrs;
+ WIPE (opaddrs);
+
+ while (!read_ctx_eof (&ctx))
+ {
+ struct where where = WHERE (sec_locexpr, wh);
+ uint64_t opcode_off = read_ctx_get_offset (&ctx) + init_off;
+ where_reset_1 (&where, opcode_off);
+ addr_record_add (&opaddrs, opcode_off);
+
+ uint8_t opcode;
+ if (!read_ctx_read_ubyte (&ctx, &opcode))
+ {
+ wr_error (&where, ": can't read opcode.\n");
+ break;
+ }
+
+ uint8_t op1, op2;
+ if (!get_location_opcode_operands (opcode, &op1, &op2))
+ {
+ wr_error (&where, ": can't decode opcode \"%s\".\n",
+ dwarf_locexpr_opcode_string (opcode));
+ break;
+ }
+
+#define READ_FORM(OP, STR, PTR) \
+ do { \
+ if (OP != 0) \
+ { \
+ uint64_t _off = read_ctx_get_offset (&ctx) + init_off; \
+ uint64_t *_ptr = (PTR); \
+ if (!read_ctx_read_form (&ctx, addr_64, (OP), \
+ _ptr, &where, STR " operand")) \
+ { \
+ wr_error (&where, ": opcode \"%s\"" \
+ ": can't read " STR " operand (form \"%s\").\n", \
+ dwarf_locexpr_opcode_string (opcode), \
+ dwarf_form_string ((OP))); \
+ goto out; \
+ } \
+ struct relocation *_rel; \
+ if ((_rel = relocation_next (reloc, _off, \
+ &where, skip_mismatched))) \
+ relocate_one (file, reloc, _rel, \
+ addr_64 ? 8 : 4, _ptr, &where, \
+ reloc_target_loc (opcode), NULL); \
+ } \
+ } while (0)
+
+ uint64_t value1, value2;
+ READ_FORM (op1, "1st", &value1);
+ READ_FORM (op2, "2st", &value2);
+#undef READ_FORM
+
+ switch (opcode)
+ {
+ case DW_OP_bra:
+ case DW_OP_skip:
+ {
+ int16_t skip = (uint16_t)value1;
+
+ if (skip == 0)
+ wr_message (mc_loc | mc_acc_bloat | mc_impact_3, &where,
+ ": %s with skip 0.\n",
+ dwarf_locexpr_opcode_string (opcode));
+ else if (skip > 0 && !read_ctx_need_data (&ctx, (size_t)skip))
+ wr_error (&where, ": %s branches out of location expression.\n",
+ dwarf_locexpr_opcode_string (opcode));
+ /* Compare with the offset after the two-byte skip value. */
+ else if (skip < 0 && ((uint64_t)-skip) > read_ctx_get_offset (&ctx))
+ wr_error (&where,
+ ": %s branches before the beginning of location expression.\n",
+ dwarf_locexpr_opcode_string (opcode));
+ else
+ ref_record_add (&oprefs, opcode_off + skip, &where);
+
+ break;
+ }
+
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ if (!addr_64)
+ wr_error (&where, ": %s on 32-bit machine.\n",
+ dwarf_locexpr_opcode_string (opcode));
+ break;
+
+ default:
+ if (!addr_64
+ && (opcode == DW_OP_constu
+ || opcode == DW_OP_consts
+ || opcode == DW_OP_deref_size
+ || opcode == DW_OP_plus_uconst)
+ && (value1 > (uint64_t)(uint32_t)-1))
+ wr_message (mc_loc | mc_acc_bloat | mc_impact_3, &where,
+ ": %s with operand %#" PRIx64 " on 32-bit machine.\n",
+ dwarf_locexpr_opcode_string (opcode), value1);
+ };
+ }
+
+ out:
+ for (size_t i = 0; i < oprefs.size; ++i)
+ {
+ struct ref *ref = oprefs.refs + i;
+ if (!addr_record_has_addr (&opaddrs, ref->addr))
+ wr_error (&ref->who,
+ ": unresolved reference to opcode at %#" PRIx64 ".\n",
+ ref->addr);
+ }
+
+ addr_record_free (&opaddrs);
+ ref_record_free (&oprefs);
+
+ return true;
+}
+
+static bool
+check_loc_or_range_ref (struct elf_file *file,
+ const struct read_ctx *parent_ctx,
+ struct cu *cu,
+ struct sec *sec,
+ struct coverage *coverage,
+ struct coverage_map *coverage_map,
+ struct cu_coverage *cu_coverage,
+ uint64_t addr,
+ bool addr_64,
+ struct where *wh,
+ enum message_category cat)
+{
+ char buf[128]; // messages
+
+ assert (sec->id == sec_loc || sec->id == sec_ranges);
+ assert (cat == mc_loc || cat == mc_ranges);
+ assert ((sec->id == sec_loc) == (cat == mc_loc));
+ assert (coverage != NULL);
+
+ struct read_ctx ctx;
+ read_ctx_init (&ctx, parent_ctx->file, parent_ctx->data);
+ if (!read_ctx_skip (&ctx, addr))
+ {
+ wr_error (wh, ": invalid reference outside the section "
+ "%#" PRIx64 ", size only %#tx.\n",
+ addr, ctx.end - ctx.begin);
+ return false;
+ }
+
+ bool retval = true;
+ bool contains_locations = sec->id == sec_loc;
+
+ if (coverage_is_covered (coverage, addr, 1))
+ {
+ wr_error (wh, ": reference to %#" PRIx64
+ " points into another location or range list.\n", addr);
+ retval = false;
+ }
+
+ uint64_t escape = addr_64 ? (uint64_t)-1 : (uint64_t)(uint32_t)-1;
+
+ bool overlap = false;
+ uint64_t base = cu->low_pc;
+ while (!read_ctx_eof (&ctx))
+ {
+ struct where where = WHERE (sec->id, wh);
+ where_reset_1 (&where, read_ctx_get_offset (&ctx));
+
+#define HAVE_OVERLAP \
+ do { \
+ wr_error (&where, ": range definitions overlap.\n"); \
+ retval = false; \
+ overlap = true; \
+ } while (0)
+
+ /* begin address */
+ uint64_t begin_addr;
+ uint64_t begin_off = read_ctx_get_offset (&ctx);
+ GElf_Sym begin_symbol_mem, *begin_symbol = &begin_symbol_mem;
+ bool begin_relocated = false;
+ if (!overlap
+ && coverage_is_overlap (coverage, begin_off, addr_64 ? 8 : 4))
+ HAVE_OVERLAP;
+
+ if (!read_ctx_read_offset (&ctx, addr_64, &begin_addr))
+ {
+ wr_error (&where, ": can't read address range beginning.\n");
+ return false;
+ }
+
+ struct relocation *rel;
+ if ((rel = relocation_next (&sec->rel, begin_off,
+ &where, skip_mismatched)))
+ {
+ begin_relocated = true;
+ relocate_one (file, &sec->rel, rel, addr_64 ? 8 : 4,
+ &begin_addr, &where, rel_value, &begin_symbol);
+ }
+
+ /* end address */
+ uint64_t end_addr;
+ uint64_t end_off = read_ctx_get_offset (&ctx);
+ GElf_Sym end_symbol_mem, *end_symbol = &end_symbol_mem;
+ bool end_relocated = false;
+ if (!overlap
+ && coverage_is_overlap (coverage, end_off, addr_64 ? 8 : 4))
+ HAVE_OVERLAP;
+
+ if (!read_ctx_read_offset (&ctx, addr_64, &end_addr))
+ {
+ wr_error (&where, ": can't read address range ending.\n");
+ return false;
+ }
+
+ if ((rel = relocation_next (&sec->rel, end_off,
+ &where, skip_mismatched)))
+ {
+ end_relocated = true;
+ relocate_one (file, &sec->rel, rel, addr_64 ? 8 : 4,
+ &end_addr, &where, rel_value, &end_symbol);
+ if (begin_addr != escape)
+ {
+ if (!begin_relocated)
+ wr_message (cat | mc_impact_2 | mc_reloc, &where,
+ ": end of address range is relocated, but the beginning wasn't.\n");
+ else
+ check_range_relocations (cat, &where, file,
+ begin_symbol, end_symbol,
+ "begin and end address");
+ }
+ }
+ else if (begin_relocated)
+ wr_message (cat | mc_impact_2 | mc_reloc, &where,
+ ": end of address range is not relocated, but the beginning was.\n");
+
+ bool done = false;
+ if (begin_addr == 0 && end_addr == 0 && !begin_relocated && !end_relocated)
+ done = true;
+ else if (begin_addr != escape)
+ {
+ if (base == (uint64_t)-1)
+ {
+ wr_error (&where,
+ ": address range with no base address set: %s.\n",
+ range_fmt (buf, sizeof buf, begin_addr, end_addr));
+ /* This is not something that would derail high-level,
+ so carry on. */
+ }
+
+ if (end_addr < begin_addr)
+ wr_message (cat | mc_error, &where, ": has negative range %s.\n",
+ range_fmt (buf, sizeof buf, begin_addr, end_addr));
+ else if (begin_addr == end_addr)
+ /* 2.6.6: A location list entry [...] whose beginning
+ and ending addresses are equal has no effect. */
+ wr_message (cat | mc_acc_bloat | mc_impact_3, &where,
+ ": entry covers no range.\n");
+ /* Skip coverage analysis if we have errors or have no base
+ (or just don't do coverage analysis at all). */
+ else if (base < (uint64_t)-2 && retval
+ && (coverage_map != NULL || cu_coverage != NULL))
+ {
+ uint64_t address = begin_addr + base;
+ uint64_t length = end_addr - begin_addr;
+ if (coverage_map != NULL)
+ coverage_map_add (coverage_map, address, length, &where, cat);
+ if (cu_coverage != NULL)
+ coverage_add (&cu_coverage->cov, address, length);
+ }
+
+ if (contains_locations)
+ {
+ /* location expression length */
+ uint16_t len;
+ if (!overlap
+ && coverage_is_overlap (coverage,
+ read_ctx_get_offset (&ctx), 2))
+ HAVE_OVERLAP;
+
+ if (!read_ctx_read_2ubyte (&ctx, &len))
+ {
+ wr_error (&where, ": can't read length of location expression.\n");
+ return false;
+ }
+
+ /* location expression itself */
+ uint64_t expr_start = read_ctx_get_offset (&ctx);
+ if (!check_location_expression (file, &ctx, expr_start,
+ &sec->rel, len, &where, addr_64))
+ return false;
+ uint64_t expr_end = read_ctx_get_offset (&ctx);
+ if (!overlap
+ && coverage_is_overlap (coverage,
+ expr_start, expr_end - expr_start))
+ HAVE_OVERLAP;
+
+ if (!read_ctx_skip (&ctx, len))
+ {
+ /* "can't happen" */
+ wr_error (&where, PRI_NOT_ENOUGH, "location expression");
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (end_addr == base)
+ wr_message (cat | mc_acc_bloat | mc_impact_3, &where,
+ ": base address selection doesn't change base address"
+ " (%#" PRIx64 ").\n", base);
+ else
+ base = end_addr;
+ }
+#undef HAVE_OVERLAP
+
+ coverage_add (coverage, where.addr1, read_ctx_get_offset (&ctx) - where.addr1);
+ if (done)
+ break;
+ }
+
+ return retval;
+}
+
+static bool
+check_loc_or_range_structural (struct elf_file *file,
+ struct sec *sec,
+ struct cu *cu_chain,
+ struct cu_coverage *cu_coverage)
+{
+ assert (sec->id == sec_loc || sec->id == sec_ranges);
+ assert (cu_chain != NULL);
+
+ struct read_ctx ctx;
+ read_ctx_init (&ctx, file, sec->data);
+
+ bool retval = true;
+
+ /* For .debug_ranges, we optionally do ranges vs. ELF sections
+ coverage analysis. */
+ struct coverage_map *coverage_map = NULL;
+ if (do_range_coverage && sec->id == sec_ranges
+ && (coverage_map
+ = coverage_map_alloc_XA (file, sec->id == sec_loc)) == NULL)
+ {
+ wr_error (&WHERE (sec->id, NULL),
+ ": couldn't read ELF, skipping coverage analysis.\n");
+ retval = false;
+ }
+
+ /* Overlap discovery. */
+ struct coverage coverage;
+ WIPE (coverage);
+
+ enum message_category cat = sec->id == sec_loc ? mc_loc : mc_ranges;
+
+ /* Relocation checking in the followings assumes that all the
+ references are organized in monotonously increasing order. That
+ doesn't have to be the case. So merge all the references into
+ one sorted array. */
+ size_t size = 0;
+ for (struct cu *cu = cu_chain; cu != NULL; cu = cu->next)
+ {
+ struct ref_record *rec
+ = sec->id == sec_loc ? &cu->loc_refs : &cu->range_refs;
+ size += rec->size;
+ }
+ struct ref_cu
+ {
+ struct ref ref;
+ struct cu *cu;
+ };
+ struct ref_cu *refs = xmalloc (sizeof (*refs) * size);
+ struct ref_cu *refptr = refs;
+ for (struct cu *cu = cu_chain; cu != NULL; cu = cu->next)
+ {
+ struct ref_record *rec
+ = sec->id == sec_loc ? &cu->loc_refs : &cu->range_refs;
+ for (size_t i = 0; i < rec->size; ++i)
+ *refptr++ = ((struct ref_cu){.ref = rec->refs[i], .cu = cu});
+ }
+ int compare_refs (const void *a, const void *b)
+ {
+ const struct ref_cu *ref_a = (const struct ref_cu *)a;
+ const struct ref_cu *ref_b = (const struct ref_cu *)b;
+
+ if (ref_a->ref.addr > ref_b->ref.addr)
+ return 1;
+ else if (ref_a->ref.addr < ref_b->ref.addr)
+ return -1;
+ else
+ return 0;
+ }
+ qsort (refs, size, sizeof (*refs), compare_refs);
+
+ uint64_t last_off = 0;
+ for (size_t i = 0; i < size; ++i)
+ {
+ uint64_t off = refs[i].ref.addr;
+ if (i > 0)
+ {
+ if (off == last_off)
+ continue;
+ relocation_skip (&sec->rel, off,
+ &WHERE (sec->id, NULL), skip_unref);
+ }
+
+ /* XXX We pass cu_coverage down for all ranges. That means all
+ ranges get recorded, not only those belonging to CUs.
+ Perhaps that's undesirable. */
+ if (!check_loc_or_range_ref (file, &ctx, refs[i].cu, sec,
+ &coverage, coverage_map,
+ sec->id == sec_ranges ? cu_coverage : NULL,
+ off, refs[i].cu->address_size == 8,
+ &refs[i].ref.who, cat))
+ retval = false;
+ last_off = off;
+ }
+
+ if (retval)
+ {
+ relocation_skip_rest (sec);
+
+ /* We check that all CUs have the same address size when building
+ the CU chain. So just take the address size of the first CU in
+ chain. */
+ coverage_find_holes (&coverage, 0, ctx.data->d_size, found_hole,
+ &((struct hole_info)
+ {sec->id, cat, cu_chain->address_size,
+ ctx.data->d_buf}));
+
+ if (coverage_map)
+ coverage_map_find_holes (coverage_map, &coverage_map_found_hole,
+ &(struct coverage_map_hole_info)
+ {{sec->id, cat, 0, NULL},
+ coverage_map->elf});
+ }
+
+ coverage_free (&coverage);
+ coverage_map_free_XA (coverage_map);
+
+ if (retval && cu_coverage != NULL)
+ /* Only drop the flag if we were successful, so that the coverage
+ analysis isn't later done against incomplete data. */
+ cu_coverage->need_ranges = false;
+
+ return retval;
+}
+
+static GElf_Rela *
+get_rel_or_rela (Elf_Data *data, int ndx,
+ GElf_Rela *dst, size_t type)
+{
+ if (type == SHT_RELA)
+ return gelf_getrela (data, ndx, dst);
+ else
+ {
+ assert (type == SHT_REL);
+ GElf_Rel rel_mem;
+ if (gelf_getrel (data, ndx, &rel_mem) == NULL)
+ return NULL;
+ dst->r_offset = rel_mem.r_offset;
+ dst->r_info = rel_mem.r_info;
+ dst->r_addend = 0;
+ return dst;
+ }
+}
+
+static bool
+read_rel (struct elf_file *file,
+ struct sec *sec,
+ Elf_Data *reldata,
+ bool elf_64)
+{
+ assert (sec->rel.type == SHT_REL
+ || sec->rel.type == SHT_RELA);
+ bool is_rela = sec->rel.type == SHT_RELA;
+
+ struct read_ctx ctx;
+ read_ctx_init (&ctx, file, sec->data);
+
+ size_t entrysize
+ = elf_64
+ ? (is_rela ? sizeof (Elf64_Rela) : sizeof (Elf64_Rel))
+ : (is_rela ? sizeof (Elf32_Rela) : sizeof (Elf32_Rel));
+ size_t count = reldata->d_size / entrysize;
+
+ struct where parent = WHERE (sec->id, NULL);
+ struct where where = WHERE (is_rela ? sec_rela : sec_rel, NULL);
+ where.ref = &parent;
+
+ for (unsigned i = 0; i < count; ++i)
+ {
+ where_reset_1 (&where, i);
+
+ REALLOC (&sec->rel, rel);
+ struct relocation *cur = sec->rel.rel + sec->rel.size++;
+ WIPE (*cur);
+
+ GElf_Rela rela_mem, *rela
+ = get_rel_or_rela (reldata, i, &rela_mem, sec->rel.type);
+ if (rela == NULL)
+ {
+ wr_error (&where, ": couldn't read relocation.\n");
+ skip:
+ cur->invalid = true;
+ continue;
+ }
+
+ int cur_type = GELF_R_TYPE (rela->r_info);
+ if (cur_type == 0) /* No relocation. */
+ {
+ wr_message (mc_impact_3 | mc_reloc | mc_acc_bloat, &where,
+ ": NONE relocation is superfluous.\n");
+ goto skip;
+ }
+
+ cur->offset = rela->r_offset;
+ cur->symndx = GELF_R_SYM (rela->r_info);
+ cur->type = cur_type;
+
+ where_reset_2 (&where, cur->offset);
+
+ Elf_Type type = ebl_reloc_simple_type (file->ebl, cur->type);
+ int width;
+
+ switch (type)
+ {
+ case ELF_T_WORD:
+ case ELF_T_SWORD:
+ width = 4;
+ break;
+
+ case ELF_T_XWORD:
+ case ELF_T_SXWORD:
+ width = 8;
+ break;
+
+ case ELF_T_BYTE:
+ case ELF_T_HALF:
+ /* Technically legal, but never used. Better have dwarflint
+ flag them as erroneous, because it's more likely these
+ are a result of a bug than actually being used. */
+ {
+ char buf[64];
+ wr_error (&where, ": 8 or 16-bit relocation type %s.\n",
+ ebl_reloc_type_name (file->ebl, cur->type,
+ buf, sizeof (buf)));
+ goto skip;
+ }
+
+ default:
+ {
+ char buf[64];
+ wr_error (&where, ": invalid relocation %d (%s).\n",
+ cur->type,
+ ebl_reloc_type_name (file->ebl, cur->type,
+ buf, sizeof (buf)));
+ goto skip;
+ }
+ };
+
+ if (cur->offset + width >= sec->data->d_size)
+ {
+ wr_error (&where,
+ ": relocation doesn't fall into relocated section.\n");
+ goto skip;
+ }
+
+ uint64_t value;
+ if (width == 4)
+ value = dwarflint_read_4ubyte_unaligned
+ (file, sec->data->d_buf + cur->offset);
+ else
+ {
+ assert (width == 8);
+ value = dwarflint_read_8ubyte_unaligned
+ (file, sec->data->d_buf + cur->offset);
+ }
+
+ if (is_rela)
+ {
+ if (value != 0)
+ wr_message (mc_impact_2 | mc_reloc, &where,
+ ": SHR_RELA relocates a place with non-zero value (addend=%#"
+ PRIx64", value=%#"PRIx64").\n", rela->r_addend, value);
+ cur->addend = rela->r_addend;
+ }
+ else
+ cur->addend = value;
+ }
+
+ /* Sort the reloc section so that the applicable addresses of
+ relocation entries are monotonously increasing. */
+ int compare (const void *a, const void *b)
+ {
+ return ((struct relocation *)a)->offset
+ - ((struct relocation *)b)->offset;
+ }
+
+ qsort (sec->rel.rel, sec->rel.size,
+ sizeof (*sec->rel.rel), &compare);
+ return true;
+}
+
+static bool
+check_line_structural (struct elf_file *file,
+ struct sec *sec,
+ struct cu *cu_chain)
+{
+ struct read_ctx ctx;
+ read_ctx_init (&ctx, file, sec->data);
+ bool retval = true;
+
+ struct addr_record line_tables;
+ WIPE (line_tables);
+
+ while (!read_ctx_eof (&ctx))
+ {
+ struct where where = WHERE (sec->id, NULL);
+ uint64_t set_offset = read_ctx_get_offset (&ctx);
+ where_reset_1 (&where, set_offset);
+ addr_record_add (&line_tables, set_offset);
+ const unsigned char *set_begin = ctx.ptr;
+
+ /* Size. */
+ uint32_t size32;
+ uint64_t size;
+ bool dwarf_64;
+ if (!read_ctx_read_4ubyte (&ctx, &size32))
+ {
+ wr_error (&where, ": can't read table length.\n");
+ return false;
+ }
+ if (!read_size_extra (&ctx, size32, &size, &dwarf_64, &where))
+ return false;
+
+ struct read_ctx sub_ctx;
+ const unsigned char *set_end = ctx.ptr + size;
+ if (!read_ctx_init_sub (&sub_ctx, &ctx, set_begin, set_end))
+ {
+ not_enough:
+ wr_error (&where, PRI_NOT_ENOUGH, "next unit");
+ return false;
+ }
+ sub_ctx.ptr = ctx.ptr;
+ sub_ctx.begin = ctx.begin;
+
+ {
+ /* Version. */
+ uint16_t version;
+ if (!read_ctx_read_2ubyte (&sub_ctx, &version))
+ {
+ wr_error (&where, ": can't read set version.\n");
+ skip:
+ retval = false;
+ goto next;
+ }
+ if (!supported_version (version, 2, &where, 2, 3))
+ goto skip;
+
+ /* Header length. */
+ uint64_t header_length;
+ if (!read_ctx_read_offset (&sub_ctx, dwarf_64, &header_length))
+ {
+ wr_error (&where, ": can't read attribute value.\n");
+ goto skip;
+ }
+ const unsigned char *program_start = sub_ctx.ptr + header_length;
+
+ /* Minimum instruction length. */
+ uint8_t minimum_i_length;
+ if (!read_ctx_read_ubyte (&sub_ctx, &minimum_i_length))
+ {
+ wr_error (&where, ": can't read minimum instruction length.\n");
+ goto skip;
+ }
+
+ /* Default value of is_stmt. */
+ uint8_t default_is_stmt;
+ if (!read_ctx_read_ubyte (&sub_ctx, &default_is_stmt))
+ {
+ wr_error (&where, ": can't read default_is_stmt.\n");
+ goto skip;
+ }
+ /* 7.21: The boolean values "true" and "false" used by the line
+ number information program are encoded as a single byte
+ containing the value 0 for "false," and a non-zero value for
+ "true." [But give a notice if it's not 0 or 1.] */
+ if (default_is_stmt != 0
+ && default_is_stmt != 1)
+ wr_message (mc_line | mc_impact_2 | mc_header, &where,
+ ": default_is_stmt should be 0 or 1, not %ud\n",
+ default_is_stmt);
+
+ /* Line base. */
+ int8_t line_base;
+ if (!read_ctx_read_ubyte (&sub_ctx, (uint8_t *)&line_base))
+ {
+ wr_error (&where, ": can't read line_base.\n");
+ goto skip;
+ }
+
+ /* Line range. */
+ uint8_t line_range;
+ if (!read_ctx_read_ubyte (&sub_ctx, &line_range))
+ {
+ wr_error (&where, ": can't read line_range.\n");
+ goto skip;
+ }
+
+ /* Opcode base. */
+ uint8_t opcode_base;
+ if (!read_ctx_read_ubyte (&sub_ctx, &opcode_base))
+ {
+ wr_error (&where, ": can't read opcode_base.\n");
+ goto skip;
+ }
+
+ /* Standard opcode lengths. */
+ if (opcode_base == 0)
+ {
+ wr_error (&where, ": opcode base set to 0.\n");
+ opcode_base = 1; // so that in following, our -1s don't underrun
+ }
+ uint8_t std_opc_lengths[opcode_base - 1]; /* -1, opcodes go from 1. */
+ for (unsigned i = 0; i < (unsigned)(opcode_base - 1); ++i)
+ if (!read_ctx_read_ubyte (&sub_ctx, std_opc_lengths + i))
+ {
+ wr_error (&where,
+ ": can't read length of standard opcode #%d.\n", i);
+ goto skip;
+ }
+
+ /* Include directories. */
+ struct include_directory_t
+ {
+ const char *name;
+ bool used;
+ };
+ struct include_directories_t
+ {
+ size_t size;
+ size_t alloc;
+ struct include_directory_t *dirs;
+ } include_directories;
+ WIPE (include_directories);
+
+ while (!read_ctx_eof (&sub_ctx))
+ {
+ const char *name = read_ctx_read_str (&sub_ctx);
+ if (name == NULL)
+ {
+ wr_error (&where,
+ ": can't read name of include directory #%zd.\n",
+ include_directories.size + 1); /* Numbered from 1. */
+ goto skip;
+ }
+ if (*name == 0)
+ break;
+
+ REALLOC (&include_directories, dirs);
+ include_directories.dirs[include_directories.size++] =
+ (struct include_directory_t){name, false};
+ }
+
+ /* File names. */
+ struct file_t
+ {
+ const char *name;
+ uint64_t dir_idx;
+ bool used;
+ };
+ struct files_t
+ {
+ size_t size;
+ size_t alloc;
+ struct file_t *files;
+ } files;
+ WIPE (files);
+
+ /* Directory index. */
+ bool read_directory_index (const char *name, uint64_t *ptr)
+ {
+ if (!checked_read_uleb128 (&sub_ctx, ptr,
+ &where, "directory index"))
+ return false;
+ if (*name == '/' && *ptr != 0)
+ wr_message (mc_impact_2 | mc_line | mc_header, &where,
+ ": file #%zd has absolute pathname, but refers to directory != 0.\n",
+ files.size + 1);
+ if (*ptr > include_directories.size) /* Not >=, dirs indexed from 1. */
+ {
+ wr_message (mc_impact_4 | mc_line | mc_header, &where,
+ ": file #%zd refers to directory #%" PRId64 ", which wasn't defined.\n",
+ files.size + 1, *ptr);
+ /* Consumer might choke on that. */
+ retval = false;
+ }
+ else if (*ptr != 0)
+ include_directories.dirs[*ptr - 1].used = true;
+ return true;
+ }
+
+ while (1)
+ {
+ const char *name = read_ctx_read_str (&sub_ctx);
+ if (name == NULL)
+ {
+ wr_error (&where,
+ ": can't read name of file #%zd.\n",
+ files.size + 1); /* Numbered from 1. */
+ goto skip;
+ }
+ if (*name == 0)
+ break;
+
+ uint64_t dir_idx;
+ if (!read_directory_index (name, &dir_idx))
+ goto skip;
+
+ /* Time of last modification. */
+ uint64_t timestamp;
+ if (!checked_read_uleb128 (&sub_ctx, &timestamp,
+ &where, "timestamp of file entry"))
+ goto skip;
+
+ /* Size of the file. */
+ uint64_t file_size;
+ if (!checked_read_uleb128 (&sub_ctx, &file_size,
+ &where, "file size of file entry"))
+ goto skip;
+
+ REALLOC (&files, files);
+ files.files[files.size++]
+ = (struct file_t){name, dir_idx, false};
+ }
+
+ /* Skip the rest of the header. */
+ if (sub_ctx.ptr > program_start)
+ {
+ wr_error (&where,
+ ": header claims that it has a size of %#" PRIx64
+ ", but in fact it has a size of %#" PRIx64 ".\n",
+ header_length, sub_ctx.ptr - program_start + header_length);
+ /* Assume that the header lies, and what follows is in
+ fact line number program. */
+ retval = false;
+ }
+ else if (sub_ctx.ptr < program_start)
+ {
+ if (!check_zero_padding (&sub_ctx, mc_line | mc_header, &where))
+ wr_message_padding_n0 (mc_line | mc_header, &WHERE (sec_line, NULL),
+ read_ctx_get_offset (&sub_ctx),
+ program_start - sub_ctx.begin);
+ sub_ctx.ptr = program_start;
+ }
+
+ bool terminated = false;
+ bool first_file = true;
+ bool seen_opcode = false;
+ while (!read_ctx_eof (&sub_ctx))
+ {
+ where_reset_2 (&where, read_ctx_get_offset (&sub_ctx));
+ uint8_t opcode;
+ if (!read_ctx_read_ubyte (&sub_ctx, &opcode))
+ {
+ wr_error (&where, ": can't read opcode.\n");
+ goto skip;
+ }
+
+ void use_file (uint64_t file_idx)
+ {
+ if (file_idx == 0 || file_idx > files.size)
+ {
+ wr_error (&where,
+ ": DW_LNS_set_file: invalid file index %" PRId64 ".\n",
+ file_idx);
+ retval = false;
+ }
+ else
+ files.files[file_idx - 1].used = true;
+ }
+
+ unsigned operands = 0;
+ uint8_t extended = 0;
+ switch (opcode)
+ {
+ /* Extended opcodes. */
+ case 0:
+ {
+ uint64_t skip_len;
+ if (!checked_read_uleb128 (&sub_ctx, &skip_len, &where,
+ "length of extended opcode"))
+ goto skip;
+ const unsigned char *next = sub_ctx.ptr + skip_len;
+ if (!read_ctx_read_ubyte (&sub_ctx, &extended))
+ {
+ wr_error (&where, ": can't read extended opcode.\n");
+ goto skip;
+ }
+
+ bool handled = true;
+ switch (extended)
+ {
+ case DW_LNE_end_sequence:
+ terminated = true;
+ break;
+
+ case DW_LNE_set_address:
+ {
+ uint64_t ctx_offset = read_ctx_get_offset (&sub_ctx);
+ uint64_t addr;
+ if (!read_ctx_read_offset (&sub_ctx,
+ file->addr_64, &addr))
+ {
+ wr_error (&where, ": can't read operand of DW_LNE_set_address.\n");
+ goto skip;
+ }
+
+ struct relocation *rel;
+ if ((rel = relocation_next (&sec->rel, ctx_offset,
+ &where, skip_mismatched)))
+ relocate_one (file, &sec->rel, rel,
+ file->addr_64 ? 8 : 4,
+ &addr, &where, rel_address, NULL);
+ else if (file->ehdr.e_type == ET_REL)
+ wr_message (mc_impact_2 | mc_line | mc_reloc, &where,
+ PRI_LACK_RELOCATION, "DW_LNE_set_address");
+ break;
+ }
+
+ case DW_LNE_define_file:
+ {
+ const char *name;
+ if ((name = read_ctx_read_str (&sub_ctx)) == NULL)
+ {
+ wr_error (&where,
+ ": can't read filename operand of DW_LNE_define_file.\n");
+ goto skip;
+ }
+ uint64_t dir_idx;
+ if (!read_directory_index (name, &dir_idx))
+ goto skip;
+ REALLOC (&files, files);
+ files.files[files.size++] =
+ (struct file_t){name, dir_idx, false};
+ operands = 2; /* Skip mtime & size of the file. */
+ }
+
+ /* See if we know about any other standard opcodes. */
+ default:
+ handled = false;
+ switch (extended)
+ {
+#define ONE_KNOWN_DW_LNE(NAME, CODE) case CODE: break;
+ ALL_KNOWN_DW_LNE
+#undef ONE_KNOWN_DW_LNE
+ default:
+ /* No we don't, emit a warning. */
+ wr_message (mc_impact_2 | mc_line, &where,
+ ": unknown extended opcode #%d.\n", extended);
+ };
+ };
+
+ if (sub_ctx.ptr > next)
+ {
+ wr_error (&where,
+ ": opcode claims that it has a size of %#" PRIx64
+ ", but in fact it has a size of %#" PRIx64 ".\n",
+ skip_len, skip_len + (next - sub_ctx.ptr));
+ retval = false;
+ }
+ else if (sub_ctx.ptr < next)
+ {
+ if (handled
+ && !check_zero_padding (&sub_ctx, mc_line, &where))
+ wr_message_padding_n0 (mc_line, &WHERE (sec_line, NULL),
+ read_ctx_get_offset (&sub_ctx),
+ next - sub_ctx.begin);
+ sub_ctx.ptr = next;
+ }
+ break;
+ }
+
+ /* Standard opcodes that need validation or have
+ non-ULEB operands. */
+ case DW_LNS_fixed_advance_pc:
+ {
+ uint16_t a;
+ if (!read_ctx_read_2ubyte (&sub_ctx, &a))
+ {
+ wr_error (&where, ": can't read operand of DW_LNS_fixed_advance_pc.\n");
+ goto skip;
+ }
+ break;
+ }
+
+ case DW_LNS_set_file:
+ {
+ uint64_t file_idx;
+ if (!checked_read_uleb128 (&sub_ctx, &file_idx, &where,
+ "DW_LNS_set_file operand"))
+ goto skip;
+ use_file (file_idx);
+ first_file = false;
+ }
+ break;
+
+ case DW_LNS_set_isa:
+ // XXX is it possible to validate this?
+ operands = 1;
+ break;
+
+ /* All the other opcodes. */
+ default:
+ if (opcode < opcode_base)
+ operands = std_opc_lengths[opcode - 1];
+
+ switch (opcode)
+ {
+#define ONE_KNOWN_DW_LNS(NAME, CODE) case CODE: break;
+ ALL_KNOWN_DW_LNS
+#undef ONE_KNOWN_DW_LNS
+
+ default:
+ if (opcode < opcode_base)
+ wr_message (mc_impact_2 | mc_line, &where,
+ ": unknown standard opcode #%d.\n", opcode);
+ };
+ };
+
+ for (unsigned i = 0; i < operands; ++i)
+ {
+ uint64_t operand;
+ char buf[128];
+ if (opcode != 0)
+ sprintf (buf, "operand #%d of DW_LNS_%s",
+ i, dwarf_locexpr_opcode_string (opcode));
+ else
+ sprintf (buf, "operand #%d of extended opcode %d",
+ i, extended);
+ if (!checked_read_uleb128 (&sub_ctx, &operand, &where, buf))
+ goto skip;
+ }
+
+ if (first_file)
+ {
+ use_file (1);
+ first_file = false;
+ }
+
+ if (opcode != 0 || extended != DW_LNE_end_sequence)
+ seen_opcode = true;
+ }
+
+ for (size_t i = 0; i < include_directories.size; ++i)
+ if (!include_directories.dirs[i].used)
+ wr_message (mc_impact_3 | mc_acc_bloat | mc_line | mc_header,
+ &where, ": the include #%zd `%s' is not used.\n",
+ i + 1, include_directories.dirs[i].name);
+
+ for (size_t i = 0; i < files.size; ++i)
+ if (!files.files[i].used)
+ wr_message (mc_impact_3 | mc_acc_bloat | mc_line | mc_header,
+ &where, ": the file #%zd `%s' is not used.\n",
+ i + 1, files.files[i].name);
+
+ if (!seen_opcode)
+ wr_message (mc_line | mc_acc_bloat | mc_impact_3, &where,
+ ": empty line number program.\n");
+ if (!terminated)
+ {
+ if (seen_opcode)
+ wr_error (&where,
+ ": sequence of opcodes not terminated with DW_LNE_end_sequence.\n");
+ }
+ else if (sub_ctx.ptr != sub_ctx.end
+ && !check_zero_padding (&sub_ctx, mc_line,
+ &WHERE (sec_line, NULL)))
+ wr_message_padding_n0 (mc_line, &WHERE (sec_line, NULL),
+ /*begin*/read_ctx_get_offset (&sub_ctx),
+ /*end*/sub_ctx.end - sub_ctx.begin);
+ }
+
+ /* XXX overlaps in defined addresses are probably OK, one
+ instruction can be derived from several statements. But
+ certain flags in table should be consistent in that case,
+ namely is_stmt, basic_block, end_sequence, prologue_end,
+ epilogue_begin, isa. */
+
+ next:
+ if (!read_ctx_skip (&ctx, size))
+ goto not_enough;
+ }
+
+ if (retval)
+ {
+ relocation_skip_rest (sec);
+
+ for (struct cu *cu = cu_chain; cu != NULL; cu = cu->next)
+ for (size_t i = 0; i < cu->line_refs.size; ++i)
+ {
+ struct ref *ref = cu->line_refs.refs + i;
+ if (!addr_record_has_addr (&line_tables, ref->addr))
+ wr_error (&ref->who,
+ ": unresolved reference to .debug_line table %#" PRIx64 ".\n",
+ ref->addr);
+ }
+ }
+
+ return retval;
+}
diff --git a/src/dwarflint.h b/src/dwarflint.h
new file mode 100644
index 00000000..4de91e92
--- /dev/null
+++ b/src/dwarflint.h
@@ -0,0 +1,287 @@
+#ifndef DWARFLINT_HL_H
+#define DWARFLINT_HL_H
+
+#include "../libdw/libdw.h"
+#include "../libebl/libebl.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#else
+# include <stdbool.h>
+#endif
+
+ /* Entry points for high-level checks. */
+
+ struct hl_ctx;
+
+ /* Check that .debug_aranges and .debug_ranges match. */
+ extern struct hl_ctx *hl_ctx_new (Elf *elf);
+ extern void hl_ctx_delete (struct hl_ctx *hlctx);
+ extern bool check_matching_ranges (struct hl_ctx *hlctx);
+ extern bool check_expected_trees (struct hl_ctx *hlctx);
+
+
+ /* Functions and data structures describing location in Dwarf. */
+
+#define DEBUGINFO_SECTIONS \
+ SEC (info) \
+ SEC (abbrev) \
+ SEC (aranges) \
+ SEC (pubnames) \
+ SEC (pubtypes) \
+ SEC (str) \
+ SEC (line) \
+ SEC (loc) \
+ SEC (mac) \
+ SEC (ranges)
+
+ enum section_id
+ {
+ sec_invalid = 0,
+
+ /* Debuginfo sections: */
+#define SEC(n) sec_##n,
+ DEBUGINFO_SECTIONS
+ count_debuginfo_sections,
+#undef SEC
+
+ /* Non-debuginfo sections: */
+ sec_rel = count_debuginfo_sections,
+ sec_rela,
+
+ /* Non-sections: */
+ sec_locexpr, /* Not a section, but a portion of file that
+ contains a location expression. */
+ rel_value, /* For relocations, this denotes that the
+ relocation is applied to taget value, not a
+ section offset. */
+ rel_address, /* Same as above, but for addresses. */
+ rel_exec, /* Some as above, but we expect EXEC bit. */
+ };
+
+ enum where_formatting
+ {
+ wf_plain = 0, /* Default formatting for given section. */
+ wf_cudie,
+ };
+
+ struct where
+ {
+ enum section_id section;
+ uint64_t addr1; // E.g. a CU offset.
+ uint64_t addr2; // E.g. a DIE address.
+ uint64_t addr3; // E.g. an attribute.
+ enum where_formatting formatting;
+ struct where *ref; // Related reference, e.g. an abbrev related to given DIE.
+ struct where *next; // For forming "caused-by" chains.
+ };
+
+# define WHERE(SECTION, NEXT) \
+ ((struct where) \
+ {(SECTION), (uint64_t)-1, (uint64_t)-1, (uint64_t)-1, wf_plain, NULL, NEXT})
+
+ extern const char *where_fmt (const struct where *wh, char *ptr);
+ extern void where_fmt_chain (const struct where *wh, const char *severity);
+ extern void where_reset_1 (struct where *wh, uint64_t addr);
+ extern void where_reset_2 (struct where *wh, uint64_t addr);
+ extern void where_reset_3 (struct where *wh, uint64_t addr);
+
+
+ /* Functions and data structures for emitting various types of
+ messages. */
+
+#define MESSAGE_CATEGORIES \
+ /* Severity: */ \
+ MC (impact_1, 0) /* no impact on the consumer */ \
+ MC (impact_2, 1) /* still no impact, but suspicious or worth mentioning */ \
+ MC (impact_3, 2) /* some impact */ \
+ MC (impact_4, 3) /* high impact */ \
+ \
+ /* Accuracy: */ \
+ MC (acc_bloat, 4) /* unnecessary constructs (e.g. unreferenced strings) */ \
+ MC (acc_suboptimal, 5) /* suboptimal construct (e.g. lack of siblings) */ \
+ \
+ /* Various: */ \
+ MC (error, 6) /* turn the message into an error */ \
+ \
+ /* Area: */ \
+ MC (leb128, 7) /* ULEB/SLEB storage */ \
+ MC (abbrevs, 8) /* abbreviations and abbreviation tables */ \
+ MC (die_rel, 9) /* DIE relationship */ \
+ MC (die_other, 10) /* other messages related to DIEs */ \
+ MC (info, 11) /* messages related to .debug_info, but not particular DIEs */ \
+ MC (strings, 12) /* string table */ \
+ MC (aranges, 13) /* address ranges table */ \
+ MC (elf, 14) /* ELF structure, e.g. missing optional sections */ \
+ MC (pubtables, 15) /* table of public names/types */ \
+ MC (pubtypes, 16) /* .debug_pubtypes presence */ \
+ MC (loc, 17) /* messages related to .debug_loc */ \
+ MC (ranges, 18) /* messages related to .debug_ranges */ \
+ MC (line, 19) /* messages related to .debug_line */ \
+ MC (reloc, 20) /* messages related to relocation handling */ \
+ MC (header, 21) /* messages related to header portions in general */ \
+ MC (other, 31) /* messages unrelated to any of the above */
+
+ enum message_category
+ {
+ mc_none = 0,
+
+#define MC(CAT, ID)\
+ mc_##CAT = 1u << ID,
+ MESSAGE_CATEGORIES
+#undef MC
+ };
+
+ extern void wr_error (const struct where *wh, const char *format, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+ extern void wr_warning (const struct where *wh, const char *format, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+ extern void wr_message (enum message_category category, const struct where *wh,
+ const char *format, ...)
+ __attribute__ ((format (printf, 3, 4)));
+
+ extern void wr_format_padding_message (enum message_category category,
+ struct where *wh,
+ uint64_t start, uint64_t end,
+ char *kind);
+
+ extern void wr_format_leb128_message (struct where *where, const char *what,
+ const char *purpose,
+ const unsigned char *begin,
+ const unsigned char *end);
+
+ extern void wr_message_padding_0 (enum message_category category,
+ struct where *wh,
+ uint64_t start, uint64_t end);
+
+ extern void wr_message_padding_n0 (enum message_category category,
+ struct where *wh,
+ uint64_t start, uint64_t end);
+
+# include "dwarflint-coverage.h"
+
+ extern char *range_fmt (char *buf, size_t buf_size, uint64_t start, uint64_t end);
+
+ struct relocation
+ {
+ uint64_t offset;
+ uint64_t addend;
+ int symndx;
+ int type;
+ bool invalid; /* Whether this one relocation should be
+ ignored. Necessary so that we don't
+ double-report invalid & missing
+ relocation. */
+ };
+
+ struct relocation_data
+ {
+ Elf_Data *symdata; /* Symbol table associated with this
+ relocation section. */
+ size_t type; /* SHT_REL or SHT_RELA. */
+
+ struct relocation *rel; /* Array of relocations. May be NULL
+ if there are no associated
+ relocation data. */
+ size_t size;
+ size_t alloc;
+ size_t index; /* Current index. */
+ };
+
+ struct sec
+ {
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+ enum section_id id;
+ const char *name;
+
+ Elf_Data *data; /* May be NULL if data in this section are
+ missing or not substantial. */
+ struct relocation_data rel;
+ };
+
+ struct elf_file
+ {
+ Elf *elf;
+ Ebl *ebl;
+ GElf_Ehdr ehdr; /* Header of underlying Elf. */
+ bool addr_64; /* True if it's 64-bit Elf. */
+ bool other_byte_order; /* True if the file has a byte order
+ different from the host. */
+
+ struct sec *sec; /* Array of sections. */
+ size_t size;
+ size_t alloc;
+
+ /* Pointers into SEC above. Maps section_id to section. */
+ struct sec *debugsec[count_debuginfo_sections];
+ };
+
+ struct section_coverage
+ {
+ struct sec *sec;
+ struct coverage cov;
+ bool hit; /* true if COV is not pristine. */
+ bool warn; /* dwarflint should emit a warning if a coverage
+ appears in this section */
+ };
+
+ struct coverage_map
+ {
+ struct elf_file *elf;
+ bool allow_overlap;
+
+ struct section_coverage *scos;
+ size_t size;
+ size_t alloc;
+ };
+
+ void section_coverage_init (struct section_coverage *sco,
+ struct sec *sec, bool warn);
+ bool coverage_map_init (struct coverage_map *coverage_map,
+ struct elf_file *elf,
+ Elf64_Xword mask,
+ Elf64_Xword warn_mask,
+ bool allow_overlap);
+ void coverage_map_add (struct coverage_map *coverage_map,
+ uint64_t address, uint64_t length,
+ struct where *where, enum message_category cat);
+ bool coverage_map_find_holes (struct coverage_map *coverage_map,
+ bool (*cb) (uint64_t, uint64_t,
+ struct section_coverage *, void *),
+ void *user);
+ void coverage_map_free (struct coverage_map *coverage_map);
+
+
+ struct hole_info
+ {
+ enum section_id section;
+ enum message_category category;
+ unsigned align;
+ void *data;
+ };
+
+ /* DATA has to be a pointer to an instance of struct hole_info.
+ DATA->data has to point at d_buf of section in question. */
+ bool found_hole (uint64_t begin, uint64_t end, void *data);
+
+ struct coverage_map_hole_info
+ {
+ struct hole_info info;
+ struct elf_file *elf;
+ };
+
+ /* DATA has to be a pointer to an instance of struct hole_info.
+ DATA->info.data has to be NULL, it is used by the callback. */
+ bool coverage_map_found_hole (uint64_t begin, uint64_t end,
+ struct section_coverage *sco, void *data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif/*DWARFLINT_HL_H*/
diff --git a/src/dwarfstrings.c b/src/dwarfstrings.c
new file mode 100644
index 00000000..34c86d34
--- /dev/null
+++ b/src/dwarfstrings.c
@@ -0,0 +1,628 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "../libdw/known-dwarf.h"
+#include "../libdw/dwarf.h"
+#include "dwarfstrings.h"
+#include <inttypes.h>
+#include <stdio.h>
+#include <libintl.h>
+
+const char *
+dwarf_tag_string (unsigned int tag)
+{
+ static const char *const known_tags[] =
+ {
+ [DW_TAG_array_type] = "array_type",
+ [DW_TAG_class_type] = "class_type",
+ [DW_TAG_entry_point] = "entry_point",
+ [DW_TAG_enumeration_type] = "enumeration_type",
+ [DW_TAG_formal_parameter] = "formal_parameter",
+ [DW_TAG_imported_declaration] = "imported_declaration",
+ [DW_TAG_label] = "label",
+ [DW_TAG_lexical_block] = "lexical_block",
+ [DW_TAG_member] = "member",
+ [DW_TAG_pointer_type] = "pointer_type",
+ [DW_TAG_reference_type] = "reference_type",
+ [DW_TAG_compile_unit] = "compile_unit",
+ [DW_TAG_string_type] = "string_type",
+ [DW_TAG_structure_type] = "structure_type",
+ [DW_TAG_subroutine_type] = "subroutine_type",
+ [DW_TAG_typedef] = "typedef",
+ [DW_TAG_union_type] = "union_type",
+ [DW_TAG_unspecified_parameters] = "unspecified_parameters",
+ [DW_TAG_variant] = "variant",
+ [DW_TAG_common_block] = "common_block",
+ [DW_TAG_common_inclusion] = "common_inclusion",
+ [DW_TAG_inheritance] = "inheritance",
+ [DW_TAG_inlined_subroutine] = "inlined_subroutine",
+ [DW_TAG_module] = "module",
+ [DW_TAG_ptr_to_member_type] = "ptr_to_member_type",
+ [DW_TAG_set_type] = "set_type",
+ [DW_TAG_subrange_type] = "subrange_type",
+ [DW_TAG_with_stmt] = "with_stmt",
+ [DW_TAG_access_declaration] = "access_declaration",
+ [DW_TAG_base_type] = "base_type",
+ [DW_TAG_catch_block] = "catch_block",
+ [DW_TAG_const_type] = "const_type",
+ [DW_TAG_constant] = "constant",
+ [DW_TAG_enumerator] = "enumerator",
+ [DW_TAG_file_type] = "file_type",
+ [DW_TAG_friend] = "friend",
+ [DW_TAG_namelist] = "namelist",
+ [DW_TAG_namelist_item] = "namelist_item",
+ [DW_TAG_packed_type] = "packed_type",
+ [DW_TAG_subprogram] = "subprogram",
+ [DW_TAG_template_type_parameter] = "template_type_parameter",
+ [DW_TAG_template_value_parameter] = "template_value_parameter",
+ [DW_TAG_thrown_type] = "thrown_type",
+ [DW_TAG_try_block] = "try_block",
+ [DW_TAG_variant_part] = "variant_part",
+ [DW_TAG_variable] = "variable",
+ [DW_TAG_volatile_type] = "volatile_type",
+ [DW_TAG_dwarf_procedure] = "dwarf_procedure",
+ [DW_TAG_restrict_type] = "restrict_type",
+ [DW_TAG_interface_type] = "interface_type",
+ [DW_TAG_namespace] = "namespace",
+ [DW_TAG_imported_module] = "imported_module",
+ [DW_TAG_unspecified_type] = "unspecified_type",
+ [DW_TAG_partial_unit] = "partial_unit",
+ [DW_TAG_imported_unit] = "imported_unit",
+ [DW_TAG_mutable_type] = "mutable_type",
+ [DW_TAG_condition] = "condition",
+ [DW_TAG_shared_type] = "shared_type",
+ };
+ const unsigned int nknown_tags = (sizeof (known_tags)
+ / sizeof (known_tags[0]));
+ static char buf[40];
+ const char *result = NULL;
+
+ if (likely (tag < nknown_tags))
+ result = known_tags[tag];
+
+ if (unlikely (result == NULL))
+ /* There are a few known extensions. */
+ switch (tag)
+ {
+ case DW_TAG_MIPS_loop:
+ result = "MIPS_loop";
+ break;
+
+ case DW_TAG_format_label:
+ result = "format_label";
+ break;
+
+ case DW_TAG_function_template:
+ result = "function_template";
+ break;
+
+ case DW_TAG_class_template:
+ result = "class_template";
+ break;
+
+ default:
+ if (tag < DW_TAG_lo_user)
+ snprintf (buf, sizeof buf, gettext ("unknown tag %hx"), tag);
+ else
+ snprintf (buf, sizeof buf, gettext ("unknown user tag %hx"), tag);
+ result = buf;
+ break;
+ }
+
+ return result;
+}
+
+
+const char *
+dwarf_attr_string (unsigned int attrnum)
+{
+ static const char *const known_attrs[] =
+ {
+ [DW_AT_sibling] = "sibling",
+ [DW_AT_location] = "location",
+ [DW_AT_name] = "name",
+ [DW_AT_ordering] = "ordering",
+ [DW_AT_subscr_data] = "subscr_data",
+ [DW_AT_byte_size] = "byte_size",
+ [DW_AT_bit_offset] = "bit_offset",
+ [DW_AT_bit_size] = "bit_size",
+ [DW_AT_element_list] = "element_list",
+ [DW_AT_stmt_list] = "stmt_list",
+ [DW_AT_low_pc] = "low_pc",
+ [DW_AT_high_pc] = "high_pc",
+ [DW_AT_language] = "language",
+ [DW_AT_member] = "member",
+ [DW_AT_discr] = "discr",
+ [DW_AT_discr_value] = "discr_value",
+ [DW_AT_visibility] = "visibility",
+ [DW_AT_import] = "import",
+ [DW_AT_string_length] = "string_length",
+ [DW_AT_common_reference] = "common_reference",
+ [DW_AT_comp_dir] = "comp_dir",
+ [DW_AT_const_value] = "const_value",
+ [DW_AT_containing_type] = "containing_type",
+ [DW_AT_default_value] = "default_value",
+ [DW_AT_inline] = "inline",
+ [DW_AT_is_optional] = "is_optional",
+ [DW_AT_lower_bound] = "lower_bound",
+ [DW_AT_producer] = "producer",
+ [DW_AT_prototyped] = "prototyped",
+ [DW_AT_return_addr] = "return_addr",
+ [DW_AT_start_scope] = "start_scope",
+ [DW_AT_bit_stride] = "bit_stride",
+ [DW_AT_upper_bound] = "upper_bound",
+ [DW_AT_abstract_origin] = "abstract_origin",
+ [DW_AT_accessibility] = "accessibility",
+ [DW_AT_address_class] = "address_class",
+ [DW_AT_artificial] = "artificial",
+ [DW_AT_base_types] = "base_types",
+ [DW_AT_calling_convention] = "calling_convention",
+ [DW_AT_count] = "count",
+ [DW_AT_data_member_location] = "data_member_location",
+ [DW_AT_decl_column] = "decl_column",
+ [DW_AT_decl_file] = "decl_file",
+ [DW_AT_decl_line] = "decl_line",
+ [DW_AT_declaration] = "declaration",
+ [DW_AT_discr_list] = "discr_list",
+ [DW_AT_encoding] = "encoding",
+ [DW_AT_external] = "external",
+ [DW_AT_frame_base] = "frame_base",
+ [DW_AT_friend] = "friend",
+ [DW_AT_identifier_case] = "identifier_case",
+ [DW_AT_macro_info] = "macro_info",
+ [DW_AT_namelist_item] = "namelist_item",
+ [DW_AT_priority] = "priority",
+ [DW_AT_segment] = "segment",
+ [DW_AT_specification] = "specification",
+ [DW_AT_static_link] = "static_link",
+ [DW_AT_type] = "type",
+ [DW_AT_use_location] = "use_location",
+ [DW_AT_variable_parameter] = "variable_parameter",
+ [DW_AT_virtuality] = "virtuality",
+ [DW_AT_vtable_elem_location] = "vtable_elem_location",
+ [DW_AT_allocated] = "allocated",
+ [DW_AT_associated] = "associated",
+ [DW_AT_data_location] = "data_location",
+ [DW_AT_byte_stride] = "byte_stride",
+ [DW_AT_entry_pc] = "entry_pc",
+ [DW_AT_use_UTF8] = "use_UTF8",
+ [DW_AT_extension] = "extension",
+ [DW_AT_ranges] = "ranges",
+ [DW_AT_trampoline] = "trampoline",
+ [DW_AT_call_column] = "call_column",
+ [DW_AT_call_file] = "call_file",
+ [DW_AT_call_line] = "call_line",
+ [DW_AT_description] = "description",
+ [DW_AT_binary_scale] = "binary_scale",
+ [DW_AT_decimal_scale] = "decimal_scale",
+ [DW_AT_small] = "small",
+ [DW_AT_decimal_sign] = "decimal_sign",
+ [DW_AT_digit_count] = "digit_count",
+ [DW_AT_picture_string] = "picture_string",
+ [DW_AT_mutable] = "mutable",
+ [DW_AT_threads_scaled] = "threads_scaled",
+ [DW_AT_explicit] = "explicit",
+ [DW_AT_object_pointer] = "object_pointer",
+ [DW_AT_endianity] = "endianity",
+ [DW_AT_elemental] = "elemental",
+ [DW_AT_pure] = "pure",
+ [DW_AT_recursive] = "recursive",
+ };
+ const unsigned int nknown_attrs = (sizeof (known_attrs)
+ / sizeof (known_attrs[0]));
+ static char buf[40];
+ const char *result = NULL;
+
+ if (likely (attrnum < nknown_attrs))
+ result = known_attrs[attrnum];
+
+ if (unlikely (result == NULL))
+ /* There are a few known extensions. */
+ switch (attrnum)
+ {
+ case DW_AT_MIPS_fde:
+ result = "MIPS_fde";
+ break;
+
+ case DW_AT_MIPS_loop_begin:
+ result = "MIPS_loop_begin";
+ break;
+
+ case DW_AT_MIPS_tail_loop_begin:
+ result = "MIPS_tail_loop_begin";
+ break;
+
+ case DW_AT_MIPS_epilog_begin:
+ result = "MIPS_epilog_begin";
+ break;
+
+ case DW_AT_MIPS_loop_unroll_factor:
+ result = "MIPS_loop_unroll_factor";
+ break;
+
+ case DW_AT_MIPS_software_pipeline_depth:
+ result = "MIPS_software_pipeline_depth";
+ break;
+
+ case DW_AT_MIPS_linkage_name:
+ result = "MIPS_linkage_name";
+ break;
+
+ case DW_AT_MIPS_stride:
+ result = "MIPS_stride";
+ break;
+
+ case DW_AT_MIPS_abstract_name:
+ result = "MIPS_abstract_name";
+ break;
+
+ case DW_AT_MIPS_clone_origin:
+ result = "MIPS_clone_origin";
+ break;
+
+ case DW_AT_MIPS_has_inlines:
+ result = "MIPS_has_inlines";
+ break;
+
+ case DW_AT_MIPS_stride_byte:
+ result = "MIPS_stride_byte";
+ break;
+
+ case DW_AT_MIPS_stride_elem:
+ result = "MIPS_stride_elem";
+ break;
+
+ case DW_AT_MIPS_ptr_dopetype:
+ result = "MIPS_ptr_dopetype";
+ break;
+
+ case DW_AT_MIPS_allocatable_dopetype:
+ result = "MIPS_allocatable_dopetype";
+ break;
+
+ case DW_AT_MIPS_assumed_shape_dopetype:
+ result = "MIPS_assumed_shape_dopetype";
+ break;
+
+ case DW_AT_MIPS_assumed_size:
+ result = "MIPS_assumed_size";
+ break;
+
+ case DW_AT_sf_names:
+ result = "sf_names";
+ break;
+
+ case DW_AT_src_info:
+ result = "src_info";
+ break;
+
+ case DW_AT_mac_info:
+ result = "mac_info";
+ break;
+
+ case DW_AT_src_coords:
+ result = "src_coords";
+ break;
+
+ case DW_AT_body_begin:
+ result = "body_begin";
+ break;
+
+ case DW_AT_body_end:
+ result = "body_end";
+ break;
+
+ default:
+ if (attrnum < DW_AT_lo_user)
+ snprintf (buf, sizeof buf, gettext ("unknown attribute %hx"),
+ attrnum);
+ else
+ snprintf (buf, sizeof buf, gettext ("unknown user attribute %hx"),
+ attrnum);
+ result = buf;
+ break;
+ }
+
+ return result;
+}
+
+
+const char *
+dwarf_form_string (unsigned int form)
+{
+ static const char *const known_forms[] =
+ {
+ [DW_FORM_addr] = "addr",
+ [DW_FORM_block2] = "block2",
+ [DW_FORM_block4] = "block4",
+ [DW_FORM_data2] = "data2",
+ [DW_FORM_data4] = "data4",
+ [DW_FORM_data8] = "data8",
+ [DW_FORM_string] = "string",
+ [DW_FORM_block] = "block",
+ [DW_FORM_block1] = "block1",
+ [DW_FORM_data1] = "data1",
+ [DW_FORM_flag] = "flag",
+ [DW_FORM_sdata] = "sdata",
+ [DW_FORM_strp] = "strp",
+ [DW_FORM_udata] = "udata",
+ [DW_FORM_ref_addr] = "ref_addr",
+ [DW_FORM_ref1] = "ref1",
+ [DW_FORM_ref2] = "ref2",
+ [DW_FORM_ref4] = "ref4",
+ [DW_FORM_ref8] = "ref8",
+ [DW_FORM_ref_udata] = "ref_udata",
+ [DW_FORM_indirect] = "indirect"
+ };
+ const unsigned int nknown_forms = (sizeof (known_forms)
+ / sizeof (known_forms[0]));
+ static char buf[40];
+ const char *result = NULL;
+
+ if (likely (form < nknown_forms))
+ result = known_forms[form];
+
+ if (unlikely (result == NULL))
+ {
+ snprintf (buf, sizeof buf, gettext ("unknown form %" PRIx64),
+ (uint64_t) form);
+ result = buf;
+ }
+
+ return result;
+}
+
+
+const char *
+dwarf_lang_string (unsigned int lang)
+{
+ static const char *const known[] =
+ {
+ [DW_LANG_C89] = "ISO C89",
+ [DW_LANG_C] = "C",
+ [DW_LANG_Ada83] = "Ada83",
+ [DW_LANG_C_plus_plus] = "C++",
+ [DW_LANG_Cobol74] = "Cobol74",
+ [DW_LANG_Cobol85] = "Cobol85",
+ [DW_LANG_Fortran77] = "Fortran77",
+ [DW_LANG_Fortran90] = "Fortran90",
+ [DW_LANG_Pascal83] = "Pascal83",
+ [DW_LANG_Modula2] = "Modula2",
+ [DW_LANG_Java] = "Java",
+ [DW_LANG_C99] = "ISO C99",
+ [DW_LANG_Ada95] = "Ada95",
+ [DW_LANG_Fortran95] = "Fortran95",
+ [DW_LANG_PL1] = "PL1",
+ [DW_LANG_Objc] = "Objective C",
+ [DW_LANG_ObjC_plus_plus] = "Objective C++",
+ [DW_LANG_UPC] = "UPC",
+ [DW_LANG_D] = "D",
+ };
+
+ if (likely (lang < sizeof (known) / sizeof (known[0])))
+ return known[lang];
+ else if (lang == DW_LANG_Mips_Assembler)
+ /* This language tag is used for assembler in general. */
+ return "Assembler";
+
+ if (lang >= DW_LANG_lo_user && lang <= DW_LANG_hi_user)
+ {
+ static char buf[30];
+ snprintf (buf, sizeof (buf), "lo_user+%u", lang - DW_LANG_lo_user);
+ return buf;
+ }
+
+ return "???";
+}
+
+
+const char *
+dwarf_inline_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ [DW_INL_not_inlined] = "not_inlined",
+ [DW_INL_inlined] = "inlined",
+ [DW_INL_declared_not_inlined] = "declared_not_inlined",
+ [DW_INL_declared_inlined] = "declared_inlined"
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return "???";
+}
+
+
+const char *
+dwarf_encoding_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ [DW_ATE_void] = "void",
+ [DW_ATE_address] = "address",
+ [DW_ATE_boolean] = "boolean",
+ [DW_ATE_complex_float] = "complex_float",
+ [DW_ATE_float] = "float",
+ [DW_ATE_signed] = "signed",
+ [DW_ATE_signed_char] = "signed_char",
+ [DW_ATE_unsigned] = "unsigned",
+ [DW_ATE_unsigned_char] = "unsigned_char",
+ [DW_ATE_imaginary_float] = "imaginary_float",
+ [DW_ATE_packed_decimal] = "packed_decimal",
+ [DW_ATE_numeric_string] = "numeric_string",
+ [DW_ATE_edited] = "edited",
+ [DW_ATE_signed_fixed] = "signed_fixed",
+ [DW_ATE_unsigned_fixed] = "unsigned_fixed",
+ [DW_ATE_decimal_float] = "decimal_float",
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ if (code >= DW_ATE_lo_user && code <= DW_ATE_hi_user)
+ {
+ static char buf[30];
+ snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_ATE_lo_user);
+ return buf;
+ }
+
+ return "???";
+}
+
+
+const char *
+dwarf_access_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ [DW_ACCESS_public] = "public",
+ [DW_ACCESS_protected] = "protected",
+ [DW_ACCESS_private] = "private"
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return "???";
+}
+
+
+const char *
+dwarf_visibility_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ [DW_VIS_local] = "local",
+ [DW_VIS_exported] = "exported",
+ [DW_VIS_qualified] = "qualified"
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return "???";
+}
+
+
+const char *
+dwarf_virtuality_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ [DW_VIRTUALITY_none] = "none",
+ [DW_VIRTUALITY_virtual] = "virtual",
+ [DW_VIRTUALITY_pure_virtual] = "pure_virtual"
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return "???";
+}
+
+
+const char *
+dwarf_identifier_case_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ [DW_ID_case_sensitive] = "sensitive",
+ [DW_ID_up_case] = "up_case",
+ [DW_ID_down_case] = "down_case",
+ [DW_ID_case_insensitive] = "insensitive"
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return "???";
+}
+
+
+const char *
+dwarf_calling_convention_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ [DW_CC_normal] = "normal",
+ [DW_CC_program] = "program",
+ [DW_CC_nocall] = "nocall",
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ if (code >= DW_CC_lo_user && code <= DW_CC_hi_user)
+ {
+ static char buf[30];
+ snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_CC_lo_user);
+ return buf;
+ }
+
+ return "???";
+}
+
+
+const char *
+dwarf_ordering_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ [DW_ORD_row_major] = "row_major",
+ [DW_ORD_col_major] = "col_major"
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return "???";
+}
+
+
+const char *
+dwarf_discr_list_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ [DW_DSC_label] = "label",
+ [DW_DSC_range] = "range"
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return "???";
+}
+
+
+const char *
+dwarf_locexpr_opcode_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ /* Normally we can't affort building huge table of 64K entries,
+ most of them zero, just because there are a couple defined
+ values at the far end. In case of opcodes, it's OK. */
+#define ONE_KNOWN_DW_OP_DESC(NAME, CODE, DESC) ONE_KNOWN_DW_OP(NAME, CODE)
+#define ONE_KNOWN_DW_OP(NAME, CODE) [CODE] = #NAME,
+ ALL_KNOWN_DW_OP
+#undef ONE_KNOWN_DW_OP
+#undef ONE_KNOWN_DW_OP_DESC
+ };
+
+ const char *ret = NULL;
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ ret = known[code];
+
+ if (ret == NULL)
+ {
+ static char buf[40];
+ snprintf (buf, sizeof buf, gettext ("unknown opcode %" PRIx64),
+ (uint64_t) code);
+ ret = buf;
+ }
+
+ return ret;
+}
diff --git a/src/dwarfstrings.h b/src/dwarfstrings.h
new file mode 100644
index 00000000..77d63b39
--- /dev/null
+++ b/src/dwarfstrings.h
@@ -0,0 +1,41 @@
+#ifndef DWARFSTRINGS_H
+#define DWARFSTRINGS_H 1
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+const char *dwarf_tag_string (unsigned int tag);
+
+const char *dwarf_attr_string (unsigned int attrnum);
+
+const char *dwarf_form_string (unsigned int form);
+
+const char *dwarf_lang_string (unsigned int lang);
+
+const char *dwarf_inline_string (unsigned int code);
+
+const char *dwarf_encoding_string (unsigned int code);
+
+const char *dwarf_access_string (unsigned int code);
+
+const char *dwarf_visibility_string (unsigned int code);
+
+const char *dwarf_virtuality_string (unsigned int code);
+
+const char *dwarf_identifier_case_string (unsigned int code);
+
+const char *dwarf_calling_convention_string (unsigned int code);
+
+const char *dwarf_ordering_string (unsigned int code);
+
+const char *dwarf_discr_list_string (unsigned int code);
+
+const char *dwarf_locexpr_opcode_string (unsigned int code);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* dwarfstrings.h */
diff --git a/src/expr_opcodes.h b/src/expr_opcodes.h
new file mode 100644
index 00000000..b38f218a
--- /dev/null
+++ b/src/expr_opcodes.h
@@ -0,0 +1,154 @@
+DEF_DW_OP (DW_OP_addr, DW_FORM_addr, 0)
+DEF_DW_OP (DW_OP_deref, 0, 0)
+DEF_DW_OP (DW_OP_xderef, 0, 0)
+DEF_DW_OP (DW_OP_deref_size, DW_FORM_data1, 0)
+DEF_DW_OP (DW_OP_xderef_size, DW_FORM_data1, 0)
+DEF_DW_OP (DW_OP_const1u, DW_FORM_data1, 0)
+DEF_DW_OP (DW_OP_const1s, DW_FORM_data1, 0)
+DEF_DW_OP (DW_OP_const2u, DW_FORM_data2, 0)
+DEF_DW_OP (DW_OP_const2s, DW_FORM_data2, 0)
+DEF_DW_OP (DW_OP_const4u, DW_FORM_data4, 0)
+DEF_DW_OP (DW_OP_const4s, DW_FORM_data4, 0)
+DEF_DW_OP (DW_OP_const8u, DW_FORM_data8, 0)
+DEF_DW_OP (DW_OP_const8s, DW_FORM_data8, 0)
+DEF_DW_OP (DW_OP_constu, DW_FORM_udata, 0)
+DEF_DW_OP (DW_OP_consts, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_dup, 0, 0)
+DEF_DW_OP (DW_OP_drop, 0, 0)
+DEF_DW_OP (DW_OP_over, 0, 0)
+DEF_DW_OP (DW_OP_pick, DW_FORM_data1, 0)
+DEF_DW_OP (DW_OP_swap, 0, 0)
+DEF_DW_OP (DW_OP_rot, 0, 0)
+DEF_DW_OP (DW_OP_abs, 0, 0)
+DEF_DW_OP (DW_OP_and, 0, 0)
+DEF_DW_OP (DW_OP_div, 0, 0)
+DEF_DW_OP (DW_OP_minus, 0, 0)
+DEF_DW_OP (DW_OP_mod, 0, 0)
+DEF_DW_OP (DW_OP_mul, 0, 0)
+DEF_DW_OP (DW_OP_neg, 0, 0)
+DEF_DW_OP (DW_OP_not, 0, 0)
+DEF_DW_OP (DW_OP_or, 0, 0)
+DEF_DW_OP (DW_OP_plus, 0, 0)
+DEF_DW_OP (DW_OP_plus_uconst, DW_FORM_udata, 0)
+DEF_DW_OP (DW_OP_shl, 0, 0)
+DEF_DW_OP (DW_OP_shr, 0, 0)
+DEF_DW_OP (DW_OP_shra, 0, 0)
+DEF_DW_OP (DW_OP_xor, 0, 0)
+DEF_DW_OP (DW_OP_bra, DW_FORM_data2, 0)
+DEF_DW_OP (DW_OP_eq, 0, 0)
+DEF_DW_OP (DW_OP_ge, 0, 0)
+DEF_DW_OP (DW_OP_gt, 0, 0)
+DEF_DW_OP (DW_OP_le, 0, 0)
+DEF_DW_OP (DW_OP_lt, 0, 0)
+DEF_DW_OP (DW_OP_ne, 0, 0)
+DEF_DW_OP (DW_OP_skip, DW_FORM_data2, 0)
+DEF_DW_OP (DW_OP_lit0, 0, 0)
+DEF_DW_OP (DW_OP_lit1, 0, 0)
+DEF_DW_OP (DW_OP_lit2, 0, 0)
+DEF_DW_OP (DW_OP_lit3, 0, 0)
+DEF_DW_OP (DW_OP_lit4, 0, 0)
+DEF_DW_OP (DW_OP_lit5, 0, 0)
+DEF_DW_OP (DW_OP_lit6, 0, 0)
+DEF_DW_OP (DW_OP_lit7, 0, 0)
+DEF_DW_OP (DW_OP_lit8, 0, 0)
+DEF_DW_OP (DW_OP_lit9, 0, 0)
+DEF_DW_OP (DW_OP_lit10, 0, 0)
+DEF_DW_OP (DW_OP_lit11, 0, 0)
+DEF_DW_OP (DW_OP_lit12, 0, 0)
+DEF_DW_OP (DW_OP_lit13, 0, 0)
+DEF_DW_OP (DW_OP_lit14, 0, 0)
+DEF_DW_OP (DW_OP_lit15, 0, 0)
+DEF_DW_OP (DW_OP_lit16, 0, 0)
+DEF_DW_OP (DW_OP_lit17, 0, 0)
+DEF_DW_OP (DW_OP_lit18, 0, 0)
+DEF_DW_OP (DW_OP_lit19, 0, 0)
+DEF_DW_OP (DW_OP_lit20, 0, 0)
+DEF_DW_OP (DW_OP_lit21, 0, 0)
+DEF_DW_OP (DW_OP_lit22, 0, 0)
+DEF_DW_OP (DW_OP_lit23, 0, 0)
+DEF_DW_OP (DW_OP_lit24, 0, 0)
+DEF_DW_OP (DW_OP_lit25, 0, 0)
+DEF_DW_OP (DW_OP_lit26, 0, 0)
+DEF_DW_OP (DW_OP_lit27, 0, 0)
+DEF_DW_OP (DW_OP_lit28, 0, 0)
+DEF_DW_OP (DW_OP_lit29, 0, 0)
+DEF_DW_OP (DW_OP_lit30, 0, 0)
+DEF_DW_OP (DW_OP_lit31, 0, 0)
+DEF_DW_OP (DW_OP_reg0, 0, 0)
+DEF_DW_OP (DW_OP_reg1, 0, 0)
+DEF_DW_OP (DW_OP_reg2, 0, 0)
+DEF_DW_OP (DW_OP_reg3, 0, 0)
+DEF_DW_OP (DW_OP_reg4, 0, 0)
+DEF_DW_OP (DW_OP_reg5, 0, 0)
+DEF_DW_OP (DW_OP_reg6, 0, 0)
+DEF_DW_OP (DW_OP_reg7, 0, 0)
+DEF_DW_OP (DW_OP_reg8, 0, 0)
+DEF_DW_OP (DW_OP_reg9, 0, 0)
+DEF_DW_OP (DW_OP_reg10, 0, 0)
+DEF_DW_OP (DW_OP_reg11, 0, 0)
+DEF_DW_OP (DW_OP_reg12, 0, 0)
+DEF_DW_OP (DW_OP_reg13, 0, 0)
+DEF_DW_OP (DW_OP_reg14, 0, 0)
+DEF_DW_OP (DW_OP_reg15, 0, 0)
+DEF_DW_OP (DW_OP_reg16, 0, 0)
+DEF_DW_OP (DW_OP_reg17, 0, 0)
+DEF_DW_OP (DW_OP_reg18, 0, 0)
+DEF_DW_OP (DW_OP_reg19, 0, 0)
+DEF_DW_OP (DW_OP_reg20, 0, 0)
+DEF_DW_OP (DW_OP_reg21, 0, 0)
+DEF_DW_OP (DW_OP_reg22, 0, 0)
+DEF_DW_OP (DW_OP_reg23, 0, 0)
+DEF_DW_OP (DW_OP_reg24, 0, 0)
+DEF_DW_OP (DW_OP_reg25, 0, 0)
+DEF_DW_OP (DW_OP_reg26, 0, 0)
+DEF_DW_OP (DW_OP_reg27, 0, 0)
+DEF_DW_OP (DW_OP_reg28, 0, 0)
+DEF_DW_OP (DW_OP_reg29, 0, 0)
+DEF_DW_OP (DW_OP_reg30, 0, 0)
+DEF_DW_OP (DW_OP_reg31, 0, 0)
+DEF_DW_OP (DW_OP_breg0, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg1, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg2, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg3, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg4, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg5, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg6, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg7, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg8, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg9, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg10, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg11, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg12, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg13, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg14, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg15, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg16, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg17, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg18, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg19, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg20, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg21, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg22, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg23, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg24, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg25, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg26, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg27, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg28, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg29, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg30, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_breg31, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_regx, DW_FORM_udata, 0)
+DEF_DW_OP (DW_OP_fbreg, DW_FORM_sdata, 0)
+DEF_DW_OP (DW_OP_bregx, DW_FORM_udata, DW_FORM_sdata)
+DEF_DW_OP (DW_OP_piece, DW_FORM_udata, 0)
+DEF_DW_OP (DW_OP_nop, 0, 0)
+DEF_DW_OP (DW_OP_push_object_address, 0, 0)
+DEF_DW_OP (DW_OP_call2, DW_FORM_data2, 0)
+DEF_DW_OP (DW_OP_call4, DW_FORM_data4, 0)
+DEF_DW_OP (DW_OP_call_ref, DW_FORM_addr, 0)
+DEF_DW_OP (DW_OP_form_tls_address, 0, 0)
+DEF_DW_OP (DW_OP_GNU_push_tls_address, 0, 0)
+DEF_DW_OP (DW_OP_call_frame_cfa, 0, 0)
+DEF_DW_OP (DW_OP_bit_piece, DW_FORM_udata, DW_FORM_udata)
+DEF_DW_OP (DW_OP_GNU_uninit, 0, 0)
diff --git a/src/readelf.c b/src/readelf.c
index 52558602..299c13e5 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -58,6 +58,7 @@
#include "../libdw/libdwP.h"
#include "../libdwfl/libdwflP.h"
#include "../libdw/memory-access.h"
+#include "dwarfstrings.h"
/* Name and version of program. */
@@ -3163,599 +3164,6 @@ format_dwarf_addr (Dwfl_Module *dwflmod,
return result;
}
-static const char *
-dwarf_tag_string (unsigned int tag)
-{
- static const char *const known_tags[] =
- {
- [DW_TAG_array_type] = "array_type",
- [DW_TAG_class_type] = "class_type",
- [DW_TAG_entry_point] = "entry_point",
- [DW_TAG_enumeration_type] = "enumeration_type",
- [DW_TAG_formal_parameter] = "formal_parameter",
- [DW_TAG_imported_declaration] = "imported_declaration",
- [DW_TAG_label] = "label",
- [DW_TAG_lexical_block] = "lexical_block",
- [DW_TAG_member] = "member",
- [DW_TAG_pointer_type] = "pointer_type",
- [DW_TAG_reference_type] = "reference_type",
- [DW_TAG_compile_unit] = "compile_unit",
- [DW_TAG_string_type] = "string_type",
- [DW_TAG_structure_type] = "structure_type",
- [DW_TAG_subroutine_type] = "subroutine_type",
- [DW_TAG_typedef] = "typedef",
- [DW_TAG_union_type] = "union_type",
- [DW_TAG_unspecified_parameters] = "unspecified_parameters",
- [DW_TAG_variant] = "variant",
- [DW_TAG_common_block] = "common_block",
- [DW_TAG_common_inclusion] = "common_inclusion",
- [DW_TAG_inheritance] = "inheritance",
- [DW_TAG_inlined_subroutine] = "inlined_subroutine",
- [DW_TAG_module] = "module",
- [DW_TAG_ptr_to_member_type] = "ptr_to_member_type",
- [DW_TAG_set_type] = "set_type",
- [DW_TAG_subrange_type] = "subrange_type",
- [DW_TAG_with_stmt] = "with_stmt",
- [DW_TAG_access_declaration] = "access_declaration",
- [DW_TAG_base_type] = "base_type",
- [DW_TAG_catch_block] = "catch_block",
- [DW_TAG_const_type] = "const_type",
- [DW_TAG_constant] = "constant",
- [DW_TAG_enumerator] = "enumerator",
- [DW_TAG_file_type] = "file_type",
- [DW_TAG_friend] = "friend",
- [DW_TAG_namelist] = "namelist",
- [DW_TAG_namelist_item] = "namelist_item",
- [DW_TAG_packed_type] = "packed_type",
- [DW_TAG_subprogram] = "subprogram",
- [DW_TAG_template_type_parameter] = "template_type_parameter",
- [DW_TAG_template_value_parameter] = "template_value_parameter",
- [DW_TAG_thrown_type] = "thrown_type",
- [DW_TAG_try_block] = "try_block",
- [DW_TAG_variant_part] = "variant_part",
- [DW_TAG_variable] = "variable",
- [DW_TAG_volatile_type] = "volatile_type",
- [DW_TAG_dwarf_procedure] = "dwarf_procedure",
- [DW_TAG_restrict_type] = "restrict_type",
- [DW_TAG_interface_type] = "interface_type",
- [DW_TAG_namespace] = "namespace",
- [DW_TAG_imported_module] = "imported_module",
- [DW_TAG_unspecified_type] = "unspecified_type",
- [DW_TAG_partial_unit] = "partial_unit",
- [DW_TAG_imported_unit] = "imported_unit",
- [DW_TAG_mutable_type] = "mutable_type",
- [DW_TAG_condition] = "condition",
- [DW_TAG_shared_type] = "shared_type",
- };
- const unsigned int nknown_tags = (sizeof (known_tags)
- / sizeof (known_tags[0]));
- static char buf[40];
- const char *result = NULL;
-
- if (likely (tag < nknown_tags))
- result = known_tags[tag];
-
- if (unlikely (result == NULL))
- /* There are a few known extensions. */
- switch (tag)
- {
- case DW_TAG_MIPS_loop:
- result = "MIPS_loop";
- break;
-
- case DW_TAG_format_label:
- result = "format_label";
- break;
-
- case DW_TAG_function_template:
- result = "function_template";
- break;
-
- case DW_TAG_class_template:
- result = "class_template";
- break;
-
- default:
- if (tag < DW_TAG_lo_user)
- snprintf (buf, sizeof buf, gettext ("unknown tag %hx"), tag);
- else
- snprintf (buf, sizeof buf, gettext ("unknown user tag %hx"), tag);
- result = buf;
- break;
- }
-
- return result;
-}
-
-
-static const char *
-dwarf_attr_string (unsigned int attrnum)
-{
- static const char *const known_attrs[] =
- {
- [DW_AT_sibling] = "sibling",
- [DW_AT_location] = "location",
- [DW_AT_name] = "name",
- [DW_AT_ordering] = "ordering",
- [DW_AT_subscr_data] = "subscr_data",
- [DW_AT_byte_size] = "byte_size",
- [DW_AT_bit_offset] = "bit_offset",
- [DW_AT_bit_size] = "bit_size",
- [DW_AT_element_list] = "element_list",
- [DW_AT_stmt_list] = "stmt_list",
- [DW_AT_low_pc] = "low_pc",
- [DW_AT_high_pc] = "high_pc",
- [DW_AT_language] = "language",
- [DW_AT_member] = "member",
- [DW_AT_discr] = "discr",
- [DW_AT_discr_value] = "discr_value",
- [DW_AT_visibility] = "visibility",
- [DW_AT_import] = "import",
- [DW_AT_string_length] = "string_length",
- [DW_AT_common_reference] = "common_reference",
- [DW_AT_comp_dir] = "comp_dir",
- [DW_AT_const_value] = "const_value",
- [DW_AT_containing_type] = "containing_type",
- [DW_AT_default_value] = "default_value",
- [DW_AT_inline] = "inline",
- [DW_AT_is_optional] = "is_optional",
- [DW_AT_lower_bound] = "lower_bound",
- [DW_AT_producer] = "producer",
- [DW_AT_prototyped] = "prototyped",
- [DW_AT_return_addr] = "return_addr",
- [DW_AT_start_scope] = "start_scope",
- [DW_AT_bit_stride] = "bit_stride",
- [DW_AT_upper_bound] = "upper_bound",
- [DW_AT_abstract_origin] = "abstract_origin",
- [DW_AT_accessibility] = "accessibility",
- [DW_AT_address_class] = "address_class",
- [DW_AT_artificial] = "artificial",
- [DW_AT_base_types] = "base_types",
- [DW_AT_calling_convention] = "calling_convention",
- [DW_AT_count] = "count",
- [DW_AT_data_member_location] = "data_member_location",
- [DW_AT_decl_column] = "decl_column",
- [DW_AT_decl_file] = "decl_file",
- [DW_AT_decl_line] = "decl_line",
- [DW_AT_declaration] = "declaration",
- [DW_AT_discr_list] = "discr_list",
- [DW_AT_encoding] = "encoding",
- [DW_AT_external] = "external",
- [DW_AT_frame_base] = "frame_base",
- [DW_AT_friend] = "friend",
- [DW_AT_identifier_case] = "identifier_case",
- [DW_AT_macro_info] = "macro_info",
- [DW_AT_namelist_item] = "namelist_item",
- [DW_AT_priority] = "priority",
- [DW_AT_segment] = "segment",
- [DW_AT_specification] = "specification",
- [DW_AT_static_link] = "static_link",
- [DW_AT_type] = "type",
- [DW_AT_use_location] = "use_location",
- [DW_AT_variable_parameter] = "variable_parameter",
- [DW_AT_virtuality] = "virtuality",
- [DW_AT_vtable_elem_location] = "vtable_elem_location",
- [DW_AT_allocated] = "allocated",
- [DW_AT_associated] = "associated",
- [DW_AT_data_location] = "data_location",
- [DW_AT_byte_stride] = "byte_stride",
- [DW_AT_entry_pc] = "entry_pc",
- [DW_AT_use_UTF8] = "use_UTF8",
- [DW_AT_extension] = "extension",
- [DW_AT_ranges] = "ranges",
- [DW_AT_trampoline] = "trampoline",
- [DW_AT_call_column] = "call_column",
- [DW_AT_call_file] = "call_file",
- [DW_AT_call_line] = "call_line",
- [DW_AT_description] = "description",
- [DW_AT_binary_scale] = "binary_scale",
- [DW_AT_decimal_scale] = "decimal_scale",
- [DW_AT_small] = "small",
- [DW_AT_decimal_sign] = "decimal_sign",
- [DW_AT_digit_count] = "digit_count",
- [DW_AT_picture_string] = "picture_string",
- [DW_AT_mutable] = "mutable",
- [DW_AT_threads_scaled] = "threads_scaled",
- [DW_AT_explicit] = "explicit",
- [DW_AT_object_pointer] = "object_pointer",
- [DW_AT_endianity] = "endianity",
- [DW_AT_elemental] = "elemental",
- [DW_AT_pure] = "pure",
- [DW_AT_recursive] = "recursive",
- };
- const unsigned int nknown_attrs = (sizeof (known_attrs)
- / sizeof (known_attrs[0]));
- static char buf[40];
- const char *result = NULL;
-
- if (likely (attrnum < nknown_attrs))
- result = known_attrs[attrnum];
-
- if (unlikely (result == NULL))
- /* There are a few known extensions. */
- switch (attrnum)
- {
- case DW_AT_MIPS_fde:
- result = "MIPS_fde";
- break;
-
- case DW_AT_MIPS_loop_begin:
- result = "MIPS_loop_begin";
- break;
-
- case DW_AT_MIPS_tail_loop_begin:
- result = "MIPS_tail_loop_begin";
- break;
-
- case DW_AT_MIPS_epilog_begin:
- result = "MIPS_epilog_begin";
- break;
-
- case DW_AT_MIPS_loop_unroll_factor:
- result = "MIPS_loop_unroll_factor";
- break;
-
- case DW_AT_MIPS_software_pipeline_depth:
- result = "MIPS_software_pipeline_depth";
- break;
-
- case DW_AT_MIPS_linkage_name:
- result = "MIPS_linkage_name";
- break;
-
- case DW_AT_MIPS_stride:
- result = "MIPS_stride";
- break;
-
- case DW_AT_MIPS_abstract_name:
- result = "MIPS_abstract_name";
- break;
-
- case DW_AT_MIPS_clone_origin:
- result = "MIPS_clone_origin";
- break;
-
- case DW_AT_MIPS_has_inlines:
- result = "MIPS_has_inlines";
- break;
-
- case DW_AT_MIPS_stride_byte:
- result = "MIPS_stride_byte";
- break;
-
- case DW_AT_MIPS_stride_elem:
- result = "MIPS_stride_elem";
- break;
-
- case DW_AT_MIPS_ptr_dopetype:
- result = "MIPS_ptr_dopetype";
- break;
-
- case DW_AT_MIPS_allocatable_dopetype:
- result = "MIPS_allocatable_dopetype";
- break;
-
- case DW_AT_MIPS_assumed_shape_dopetype:
- result = "MIPS_assumed_shape_dopetype";
- break;
-
- case DW_AT_MIPS_assumed_size:
- result = "MIPS_assumed_size";
- break;
-
- case DW_AT_sf_names:
- result = "sf_names";
- break;
-
- case DW_AT_src_info:
- result = "src_info";
- break;
-
- case DW_AT_mac_info:
- result = "mac_info";
- break;
-
- case DW_AT_src_coords:
- result = "src_coords";
- break;
-
- case DW_AT_body_begin:
- result = "body_begin";
- break;
-
- case DW_AT_body_end:
- result = "body_end";
- break;
-
- case DW_AT_GNU_vector:
- result = "GNU_vector";
- break;
-
- case DW_AT_GNU_template_name:
- result = "GNU_template_name";
- break;
-
- default:
- if (attrnum < DW_AT_lo_user)
- snprintf (buf, sizeof buf, gettext ("unknown attribute %hx"),
- attrnum);
- else
- snprintf (buf, sizeof buf, gettext ("unknown user attribute %hx"),
- attrnum);
- result = buf;
- break;
- }
-
- return result;
-}
-
-
-static const char *
-dwarf_form_string (unsigned int form)
-{
- static const char *const known_forms[] =
- {
- [DW_FORM_addr] = "addr",
- [DW_FORM_block2] = "block2",
- [DW_FORM_block4] = "block4",
- [DW_FORM_data2] = "data2",
- [DW_FORM_data4] = "data4",
- [DW_FORM_data8] = "data8",
- [DW_FORM_string] = "string",
- [DW_FORM_block] = "block",
- [DW_FORM_block1] = "block1",
- [DW_FORM_data1] = "data1",
- [DW_FORM_flag] = "flag",
- [DW_FORM_sdata] = "sdata",
- [DW_FORM_strp] = "strp",
- [DW_FORM_udata] = "udata",
- [DW_FORM_ref_addr] = "ref_addr",
- [DW_FORM_ref1] = "ref1",
- [DW_FORM_ref2] = "ref2",
- [DW_FORM_ref4] = "ref4",
- [DW_FORM_ref8] = "ref8",
- [DW_FORM_ref_udata] = "ref_udata",
- [DW_FORM_indirect] = "indirect"
- };
- const unsigned int nknown_forms = (sizeof (known_forms)
- / sizeof (known_forms[0]));
- static char buf[40];
- const char *result = NULL;
-
- if (likely (form < nknown_forms))
- result = known_forms[form];
-
- if (unlikely (result == NULL))
- snprintf (buf, sizeof buf, gettext ("unknown form %" PRIx64),
- (uint64_t) form);
-
- return result;
-}
-
-
-static const char *
-dwarf_lang_string (unsigned int lang)
-{
- static const char *const known[] =
- {
- [DW_LANG_C89] = "ISO C89",
- [DW_LANG_C] = "C",
- [DW_LANG_Ada83] = "Ada83",
- [DW_LANG_C_plus_plus] = "C++",
- [DW_LANG_Cobol74] = "Cobol74",
- [DW_LANG_Cobol85] = "Cobol85",
- [DW_LANG_Fortran77] = "Fortran77",
- [DW_LANG_Fortran90] = "Fortran90",
- [DW_LANG_Pascal83] = "Pascal83",
- [DW_LANG_Modula2] = "Modula2",
- [DW_LANG_Java] = "Java",
- [DW_LANG_C99] = "ISO C99",
- [DW_LANG_Ada95] = "Ada95",
- [DW_LANG_Fortran95] = "Fortran95",
- [DW_LANG_PL1] = "PL1",
- [DW_LANG_Objc] = "Objective C",
- [DW_LANG_ObjC_plus_plus] = "Objective C++",
- [DW_LANG_UPC] = "UPC",
- [DW_LANG_D] = "D",
- };
-
- if (likely (lang < sizeof (known) / sizeof (known[0])))
- return known[lang];
- else if (lang == DW_LANG_Mips_Assembler)
- /* This language tag is used for assembler in general. */
- return "Assembler";
-
- if (lang >= DW_LANG_lo_user && lang <= DW_LANG_hi_user)
- {
- static char buf[30];
- snprintf (buf, sizeof (buf), "lo_user+%u", lang - DW_LANG_lo_user);
- return buf;
- }
-
- return "???";
-}
-
-
-static const char *
-dwarf_inline_string (unsigned int code)
-{
- static const char *const known[] =
- {
- [DW_INL_not_inlined] = "not_inlined",
- [DW_INL_inlined] = "inlined",
- [DW_INL_declared_not_inlined] = "declared_not_inlined",
- [DW_INL_declared_inlined] = "declared_inlined"
- };
-
- if (likely (code < sizeof (known) / sizeof (known[0])))
- return known[code];
-
- return "???";
-}
-
-
-static const char *
-dwarf_encoding_string (unsigned int code)
-{
- static const char *const known[] =
- {
- [DW_ATE_void] = "void",
- [DW_ATE_address] = "address",
- [DW_ATE_boolean] = "boolean",
- [DW_ATE_complex_float] = "complex_float",
- [DW_ATE_float] = "float",
- [DW_ATE_signed] = "signed",
- [DW_ATE_signed_char] = "signed_char",
- [DW_ATE_unsigned] = "unsigned",
- [DW_ATE_unsigned_char] = "unsigned_char",
- [DW_ATE_imaginary_float] = "imaginary_float",
- [DW_ATE_packed_decimal] = "packed_decimal",
- [DW_ATE_numeric_string] = "numeric_string",
- [DW_ATE_edited] = "edited",
- [DW_ATE_signed_fixed] = "signed_fixed",
- [DW_ATE_unsigned_fixed] = "unsigned_fixed",
- [DW_ATE_decimal_float] = "decimal_float",
- };
-
- if (likely (code < sizeof (known) / sizeof (known[0])))
- return known[code];
-
- if (code >= DW_ATE_lo_user && code <= DW_ATE_hi_user)
- {
- static char buf[30];
- snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_ATE_lo_user);
- return buf;
- }
-
- return "???";
-}
-
-
-static const char *
-dwarf_access_string (unsigned int code)
-{
- static const char *const known[] =
- {
- [DW_ACCESS_public] = "public",
- [DW_ACCESS_protected] = "protected",
- [DW_ACCESS_private] = "private"
- };
-
- if (likely (code < sizeof (known) / sizeof (known[0])))
- return known[code];
-
- return "???";
-}
-
-
-static const char *
-dwarf_visibility_string (unsigned int code)
-{
- static const char *const known[] =
- {
- [DW_VIS_local] = "local",
- [DW_VIS_exported] = "exported",
- [DW_VIS_qualified] = "qualified"
- };
-
- if (likely (code < sizeof (known) / sizeof (known[0])))
- return known[code];
-
- return "???";
-}
-
-
-static const char *
-dwarf_virtuality_string (unsigned int code)
-{
- static const char *const known[] =
- {
- [DW_VIRTUALITY_none] = "none",
- [DW_VIRTUALITY_virtual] = "virtual",
- [DW_VIRTUALITY_pure_virtual] = "pure_virtual"
- };
-
- if (likely (code < sizeof (known) / sizeof (known[0])))
- return known[code];
-
- return "???";
-}
-
-
-static const char *
-dwarf_identifier_case_string (unsigned int code)
-{
- static const char *const known[] =
- {
- [DW_ID_case_sensitive] = "sensitive",
- [DW_ID_up_case] = "up_case",
- [DW_ID_down_case] = "down_case",
- [DW_ID_case_insensitive] = "insensitive"
- };
-
- if (likely (code < sizeof (known) / sizeof (known[0])))
- return known[code];
-
- return "???";
-}
-
-
-static const char *
-dwarf_calling_convention_string (unsigned int code)
-{
- static const char *const known[] =
- {
- [DW_CC_normal] = "normal",
- [DW_CC_program] = "program",
- [DW_CC_nocall] = "nocall",
- };
-
- if (likely (code < sizeof (known) / sizeof (known[0])))
- return known[code];
-
- if (code >= DW_CC_lo_user && code <= DW_CC_hi_user)
- {
- static char buf[30];
- snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_CC_lo_user);
- return buf;
- }
-
- return "???";
-}
-
-
-static const char *
-dwarf_ordering_string (unsigned int code)
-{
- static const char *const known[] =
- {
- [DW_ORD_row_major] = "row_major",
- [DW_ORD_col_major] = "col_major"
- };
-
- if (likely (code < sizeof (known) / sizeof (known[0])))
- return known[code];
-
- return "???";
-}
-
-
-static const char *
-dwarf_discr_list_string (unsigned int code)
-{
- static const char *const known[] =
- {
- [DW_DSC_label] = "label",
- [DW_DSC_range] = "range"
- };
-
- if (likely (code < sizeof (known) / sizeof (known[0])))
- return known[code];
-
- return "???";
-}
-
-
static void
print_block (size_t n, const void *block)
{
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 60b0847a..e2fe66f2 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -18,6 +18,112 @@
* Makefile.am (noinst_PROGRAMS, TESTS): Add them.
(test_flag_nobits_LDADD): New variable.
+2009-12-13 Roland McGrath <[email protected]>
+
+ * run-dwarfcmp-self.sh: Move the hairiest/largest cases to last.
+
+2009-09-15 Roland McGrath <[email protected]>
+
+ * print-die.cc: Grok --refs-shared-cu, --refs-shared-file options
+ to share the refs_map across CUs or across files.
+
+2009-08-28 Roland McGrath <[email protected]>
+
+ * run-dwarfcmp-self.sh: Test dwarfcmp-test binary too.
+
+ * print-die.cc: Grok --stats to dump collector stats.
+
+2009-08-27 Roland McGrath <[email protected]>
+
+ * run-dwarfcmp-self.sh: Parameterize. Take out -T runs.
+ * run-dwarfcmp-test-self.sh: New file. Do just -T runs here.
+ * Makefile.am (TESTS, EXTRA_DIST): Add it.
+
+2009-08-25 Roland McGrath <[email protected]>
+
+ * print-die.hh: New file with extern decls. Contents moved to ...
+ * print-die.cc: ... here, new file.
+ * Makefile.am (dwarf_print_SOURCES, dwarf_edit_SOURCES): Build it.
+
+2009-08-21 Roland McGrath <[email protected]>
+
+ * print-die.hh (print_die_main, print_file): Add --silent option.
+
+2009-08-19 Roland McGrath <[email protected]>
+
+ * print-die.hh: Handle --norefs, --dump-refs.
+
+2009-07-03 Roland McGrath <[email protected]>
+
+ * print-die.hh: Grok --sort-attrs option to canonicalize order.
+ Fix ref assignment to produce consistent numbering.
+ * run-dwarf_edit.sh: Update expected output.
+ * run-dwarf-attributes.sh: Likewise.
+
+ * Makefile.am (AM_CXXFLAGS): New variable (from ../src/Makefile.am).
+
+ * print-die.hh: Grok --edit/--output options to print a copied object.
+
+2009-07-02 Roland McGrath <[email protected]>
+
+ * run-dwarf_edit.sh: New file.
+ * dwarf_edit.cc: New file.
+ * Makefile.am (noinst_PROGRAMS, TESTS, EXTRA_DIST): Add them.
+ (dwarf_edit_SOURCES, dwarf_edit_LDADD): New variables.
+
+ * dwarf-print.cc (print_die): Templatify and move ...
+ * print-die.hh: ... here, new file.
+ * Makefile.am (noinst_HEADERS): New variable, add it.
+
+2009-07-01 Roland McGrath <[email protected]>
+
+ * run-dwarfcmp-self.sh: Run on dwarflint too.
+ Fiddle failure output.
+
+2009-06-15 Roland McGrath <[email protected]>
+
+ * dwarf-print.cc: Include <cstdio>.
+
+2009-03-25 Petr Machata <[email protected]>
+
+ * dwarf-attributes.cc: New file.
+ * Makefile.am (noinst_PROGRAMS): Add it.
+ (dwarf_attributes_SOURCES, dwarf_attributes_LDADD): New variables.
+
+2009-03-25 Roland McGrath <[email protected]>
+
+ * dwarf-print.cc (print_die, process_file): Take LIMIT argument.
+ Punt recursion at that depth.
+ (main): Grok first argument --depth=N to set it.
+
+2009-03-24 Roland McGrath <[email protected]>
+
+ * dwarf-print.cc: New file.
+ * Makefile.am (noinst_PROGRAMS): Add it.
+ (dwarf_print_SOURCES, dwarf_print_LDADD): New variables.
+
+2009-01-10 Roland McGrath <[email protected]>
+
+ * run-dwarfcmp-self.sh: Also run with -T.
+
+ * run-dwarflint-self.sh: --no-debug -> -i
+
+ * run-dwarfcmp-self.sh: Run both with and without -q.
+
+2009-01-10 Petr Machata <[email protected]>
+
+ * run-dwarflint-self.sh: Pass new option --no-debug to dwarflint.
+
+2009-01-10 Roland McGrath <[email protected]>
+
+ * run-dwarflint-self.sh: New file.
+ * Makefile.am (TESTS, EXTRA_DIST) Add it.
+
+2009-01-05 Roland McGrath <[email protected]>
+
+ * run-dwarfcmp-self.sh: New file.
+ * Makefile.am (TESTS, EXTRA_DIST) Add it.
+
2010-02-15 Roland McGrath <[email protected]>
* Makefile.am: Use config/eu.am for common stuff.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 008008ac..b3ae88fb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -48,6 +48,8 @@ else
tests_rpath = no
endif
+noinst_HEADERS = print-die.hh
+
noinst_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
showptable update1 update2 update3 update4 test-nlist \
show-die-info get-files get-lines get-pubnames \
@@ -57,7 +59,9 @@ noinst_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
dwfl-bug-addr-overflow arls dwfl-bug-fd-leak \
dwfl-addr-sect dwfl-bug-report early-offscn \
dwfl-bug-getmodules dwarf-getmacros addrcfi \
+ dwarf-print dwarf_edit \
test-flag-nobits
+
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
asm-tst6 asm-tst7 asm-tst8 asm-tst9
@@ -83,6 +87,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
run-dwfl-bug-offline-rel.sh run-dwfl-addr-sect.sh \
run-disasm-x86.sh run-disasm-x86-64.sh \
run-early-offscn.sh run-dwarf-getmacros.sh \
+ run-dwarfcmp-self.sh run-dwarflint-self.sh run-dwarf-attributes.sh \
+ run-dwarf_edit.sh run-dwarfcmp-test-self.sh \
run-test-flag-nobits.sh
# run-show-ciefde.sh
@@ -117,6 +123,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-addrname-test.sh run-dwfl-bug-offline-rel.sh \
run-dwfl-addr-sect.sh run-early-offscn.sh \
run-dwarf-getmacros.sh run-test-flag-nobits.sh \
+ run-dwarfcmp-self.sh run-dwarfcmp-test-self.sh \
+ run-dwarflint-self.sh run-dwarf-attributes.sh run-dwarf_edit.sh \
testfile15.bz2 testfile15.debug.bz2 \
testfile16.bz2 testfile16.debug.bz2 \
testfile17.bz2 testfile17.debug.bz2 \
@@ -180,6 +188,15 @@ libebl = ../libebl/libebl.a
libeu = ../lib/libeu.a
endif !STANDALONE
+# XXX later the C++ stuff will be in libdw.so directly
+libdwpp = ../libdw/libdwpp.a $(libdw)
+
+dwarf_print_SOURCES = dwarf-print.cc print-die.cc
+dwarf_print_LDADD = $(libdwpp) $(libmudflap) -ldl
+
+dwarf_edit_SOURCES = dwarf_edit.cc print-die.cc
+dwarf_edit_LDADD = $(libdwpp) $(libmudflap) -ldl
+
arextract_LDADD = $(libelf) $(libmudflap)
arsymtest_LDADD = $(libelf) $(libmudflap)
newfile_LDADD = $(libelf) $(libmudflap)
diff --git a/tests/dwarf-print.cc b/tests/dwarf-print.cc
new file mode 100644
index 00000000..698dd0da
--- /dev/null
+++ b/tests/dwarf-print.cc
@@ -0,0 +1,68 @@
+/* Test program for elfutils::dwarf basics.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <libintl.h>
+
+#include "c++/dwarf"
+
+using namespace elfutils;
+using namespace std;
+
+#include "print-die.hh"
+
+static Dwarf *
+open_file (const char *fname)
+{
+ int fd = open (fname, O_RDONLY);
+ if (unlikely (fd == -1))
+ error (2, errno, gettext ("cannot open '%s'"), fname);
+ Dwarf *dw = dwarf_begin (fd, DWARF_C_READ);
+ if (dw == NULL)
+ {
+ error (2, 0,
+ gettext ("cannot create DWARF descriptor for '%s': %s"),
+ fname, dwarf_errmsg (-1));
+ }
+ return dw;
+}
+
+int
+main (int argc, char *argv[])
+{
+ unsigned int depth;
+ print_die_main (argc, argv, depth);
+
+ for (int i = 1; i < argc; ++i)
+ print_file (argv[i], dwarf (open_file (argv[i])), depth);
+
+ return 0;
+}
diff --git a/tests/dwarf_edit.cc b/tests/dwarf_edit.cc
new file mode 100644
index 00000000..67460533
--- /dev/null
+++ b/tests/dwarf_edit.cc
@@ -0,0 +1,66 @@
+/* Test program for elfutils::dwarf_edit basics.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "c++/dwarf_edit"
+
+using namespace elfutils;
+using namespace std;
+
+#include "print-die.hh"
+
+
+int
+main (int argc, char **argv)
+{
+ unsigned int depth;
+ print_die_main (argc, argv, depth);
+
+ dwarf_edit f;
+
+ dwarf_edit::compile_unit &cu = f.add_unit ();
+
+ cu.attributes ()[DW_AT_name].source_file () = "source-file.c";
+
+ dwarf_edit::debug_info_entry::pointer bt = cu.add_entry (DW_TAG_base_type);
+ bt->attributes ()[DW_AT_name].identifier () = "int";
+
+ dwarf_edit::debug_info_entry &ent = *cu.add_entry (DW_TAG_subprogram);
+
+ ent.attributes ()[DW_AT_name].identifier () = "foo";
+
+ ent.attributes ()[DW_AT_description] = ent.attributes ()[DW_AT_name];
+
+ ent.attributes ()[DW_AT_external].flag ();
+
+ ent.attributes ()[DW_AT_type].reference () = bt;
+
+ print_file ("consed", f, depth);
+
+ return 0;
+}
diff --git a/tests/print-die.cc b/tests/print-die.cc
new file mode 100644
index 00000000..96f8338c
--- /dev/null
+++ b/tests/print-die.cc
@@ -0,0 +1,364 @@
+/* Pseudo-XMLish printing for elfutils::dwarf* tests.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+#include <clocale>
+#include <libintl.h>
+#include <ostream>
+#include <iomanip>
+#include <tr1/unordered_map>
+#include <functional>
+#include <algorithm>
+
+#include "c++/dwarf_edit"
+#include "c++/dwarf_output"
+
+using namespace elfutils;
+using namespace std;
+
+static bool print_offset;
+static bool sort_attrs;
+static bool elide_refs;
+static bool dump_refs;
+static bool no_print;
+static bool output_stats;
+static bool refs_shared_cu;
+static bool refs_shared_file;
+
+static enum { copy_none, copy_edit, copy_output } make_copy;
+
+void
+print_die_main (int &argc, char **&argv, unsigned int &depth)
+{
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ (void) textdomain (PACKAGE_TARNAME);
+
+ cout << hex << setiosflags (ios::showbase);
+
+ if (argc > 1 && !strcmp (argv[1], "--offsets"))
+ {
+ print_offset = true;
+ --argc;
+ ++argv;
+ }
+
+ if (argc > 1 && !strcmp (argv[1], "--norefs"))
+ {
+ elide_refs = true;
+ --argc;
+ ++argv;
+ }
+
+ if (argc > 1 && !strcmp (argv[1], "--dump-refs"))
+ {
+ dump_refs = true;
+ --argc;
+ ++argv;
+ }
+
+ if (argc > 1 && !strcmp (argv[1], "--refs-shared-cu"))
+ {
+ refs_shared_cu = true;
+ --argc;
+ ++argv;
+ }
+
+ if (argc > 1 && !strcmp (argv[1], "--refs-shared-file"))
+ {
+ refs_shared_file = refs_shared_cu = true;
+ --argc;
+ ++argv;
+ }
+
+ if (argc > 1 && !strcmp (argv[1], "--sort-attrs"))
+ {
+ sort_attrs = true;
+ --argc;
+ ++argv;
+ }
+
+ if (argc > 1 && !strcmp (argv[1], "--edit"))
+ {
+ make_copy = copy_edit;
+ --argc;
+ ++argv;
+ }
+ else if (argc > 1 && !strcmp (argv[1], "--output"))
+ {
+ make_copy = copy_output;
+ --argc;
+ ++argv;
+ }
+
+ if (argc > 1 && !strcmp (argv[1], "--stats"))
+ {
+ output_stats = true;
+ --argc;
+ ++argv;
+ }
+
+ if (argc > 1 && !strcmp (argv[1], "--silent"))
+ {
+ no_print = true;
+ --argc;
+ ++argv;
+ }
+
+ depth = 0;
+ if (argc > 1 && sscanf (argv[1], "--depth=%u", &depth) == 1)
+ {
+ --argc;
+ ++argv;
+ }
+}
+
+static int next_ref = 1;
+typedef tr1::unordered_map<dwarf::debug_info_entry::identity_type,
+ int> refs_map;
+
+template<typename attrs_type,
+ void (*act) (const typename attrs_type::value_type &, refs_map &)
+ >
+class attr_walker
+{
+private:
+ refs_map &refs;
+ inline attr_walker (refs_map &r) : refs (r) {}
+
+ typedef typename attrs_type::const_iterator iterator;
+ typedef typename iterator::value_type attr_type;
+
+public:
+ inline void operator () (const pair<int, iterator> &p) const
+ {
+ (*act) (*p.second, refs);
+ }
+
+ static inline void walk (const attrs_type &attrs, refs_map &r)
+ {
+ if (attrs_type::ordered () || !sort_attrs)
+ for (iterator i = attrs.begin (); i != attrs.end (); ++i)
+ (*act) (*i, r);
+ else
+ {
+ map<int, iterator> sorted;
+ for (iterator i = attrs.begin (); i != attrs.end (); ++i)
+ sorted[(*i).first] = i;
+ for_each (sorted.begin (), sorted.end (),
+ attr_walker<attrs_type, act> (r));
+ }
+ }
+};
+
+template<typename attrs_type>
+void
+print_attr (const typename attrs_type::value_type &attr, refs_map &refs)
+{
+ if (!print_offset && attr.second.what_space () == dwarf::VS_reference)
+ {
+ if (elide_refs)
+ cout << " " << dwarf::attributes::name (attr.first) << "=\"ref\"";
+ else
+ cout << " " << dwarf::attributes::name (attr.first) << "=\"#ref"
+ << dec << refs[attr.second.reference ()->identity ()] << "\"";
+ }
+ else
+ cout << " " << to_string (attr);
+}
+
+template<typename attrs_type>
+static void
+print_attrs (const attrs_type &attrs, refs_map &refs)
+{
+ attr_walker<attrs_type, print_attr<attrs_type> >::walk (attrs, refs);
+}
+
+template<typename attrs_type>
+void
+prewalk_attr (const typename attrs_type::value_type &attr, refs_map &refs)
+{
+ if (attr.second.what_space () == dwarf::VS_reference
+ && refs.insert (make_pair (attr.second.reference ()->identity (),
+ next_ref)).second)
+ ++next_ref;
+}
+
+template<typename attrs_type>
+static void
+prewalk_attrs (const attrs_type &attrs, refs_map &refs)
+{
+ attr_walker<attrs_type, prewalk_attr<attrs_type> >::walk (attrs, refs);
+}
+
+template<typename file>
+static void
+prewalk_die (const typename file::debug_info_entry &die, refs_map &refs)
+{
+ for (typename file::debug_info_entry::children_type::const_iterator i
+ = die.children ().begin (); i != die.children ().end (); ++i)
+ prewalk_die<file> (*i, refs);
+
+ prewalk_attrs (die.attributes (), refs);
+}
+
+static int nth;
+static std::map<int, int> nth_ref;
+
+template<typename file>
+static void
+print_die (const typename file::debug_info_entry &die,
+ unsigned int indent, unsigned int limit, refs_map &refs)
+{
+ string prefix (indent, ' ');
+ const string tag = dwarf::tags::name (die.tag ());
+
+ ++nth;
+ if (dump_refs)
+ cout << dec << nth << ": ";
+
+ cout << prefix << "<" << tag;
+ if (print_offset)
+ cout << " offset=[" << hex << die.offset () << "]";
+ else if (!elide_refs)
+ {
+ refs_map::const_iterator it = refs.find (die.identity ());
+ if (it != refs.end ())
+ {
+ cout << " ref=\"ref" << dec << it->second << "\"";
+ nth_ref[nth] = it->second;
+ }
+ }
+
+ print_attrs (die.attributes (), refs);
+
+ if (die.has_children ())
+ {
+ if (limit != 0 && indent >= limit)
+ {
+ cout << ">...\n";
+ return;
+ }
+
+ cout << ">\n";
+
+ for (typename file::debug_info_entry::children_type::const_iterator i
+ = die.children ().begin (); i != die.children ().end (); ++i)
+ print_die<file> (*i, indent + 1, limit, refs);
+
+ cout << prefix << "</" << tag << ">\n";
+ }
+ else
+ cout << "/>\n";
+}
+
+static inline void
+dump_nth (pair<int, int> p)
+{
+ cout << dec << p.first << ": ref" << p.second << "\n";
+}
+
+template<typename file>
+static void
+print_cu (const typename file::compile_unit &cu, const unsigned int limit,
+ refs_map &refs)
+{
+ const typename file::debug_info_entry &die
+ = static_cast<const typename file::debug_info_entry &> (cu);
+
+ if (!print_offset && !elide_refs)
+ prewalk_die<file> (die, refs);
+
+ print_die<file> (die, 1, limit, refs);
+}
+
+template<typename file>
+static void
+print_file (const file &dw, const unsigned int limit)
+{
+ if (no_print)
+ return;
+
+ static refs_map common_refs;
+ refs_map file_refs;
+
+ for (typename file::compile_units::const_iterator i
+ = dw.compile_units ().begin (); i != dw.compile_units ().end (); ++i)
+ if (refs_shared_cu)
+ print_cu<file> (*i, limit, refs_shared_file ? common_refs : file_refs);
+ else
+ {
+ refs_map refs;
+ print_cu<file> (*i, limit, refs);
+ }
+
+ if (dump_refs)
+ for_each (nth_ref.begin (), nth_ref.end (), dump_nth);
+}
+
+template<typename file>
+void
+print_file (const char *name, const file &dw, const unsigned int limit)
+{
+ cout << name << ":\n";
+
+ switch (make_copy)
+ {
+ case copy_none:
+ print_file (dw, limit);
+ break;
+ case copy_edit:
+ print_file (dwarf_edit (dw), limit);
+ break;
+ case copy_output:
+ {
+ dwarf_output_collector c; // We'll just throw it away.
+ dwarf_output out (dw, c);
+ if (output_stats)
+ c.stats ();
+ print_file (out, limit);
+ }
+ break;
+ default:
+ abort ();
+ }
+}
+
+// Explicit instantiations.
+template void print_file (const char *, const dwarf &,
+ const unsigned int);
+template void print_file (const char *, const dwarf_edit &,
+ const unsigned int);
diff --git a/tests/print-die.hh b/tests/print-die.hh
new file mode 100644
index 00000000..ea256e53
--- /dev/null
+++ b/tests/print-die.hh
@@ -0,0 +1,40 @@
+/* Pseudo-XMLish printing for elfutils::dwarf* tests.
+ Copyright (C) 2009 Red Hat, Inc.
+ This file is part of Red Hat elfutils.
+
+ Red Hat elfutils 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; version 2 of the License.
+
+ Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+
+ Red Hat elfutils is an included package of the Open Invention Network.
+ An included package of the Open Invention Network is a package for which
+ Open Invention Network licensees cross-license their patents. No patent
+ license is granted, either expressly or impliedly, by designation as an
+ included package. Should you wish to participate in the Open Invention
+ Network licensing program, please visit www.openinventionnetwork.com
+ <http://www.openinventionnetwork.com>. */
+
+#include "c++/dwarf"
+#include "c++/dwarf_edit"
+#include "c++/dwarf_output"
+
+extern void print_die_main (int &argc, char **&argv, unsigned int &depth);
+
+template<typename file>
+extern void
+print_file (const char *name, const file &dw, const unsigned int limit);
+
+// Explicit instantiations.
+extern template void print_file (const char *, const dwarf &,
+ const unsigned int);
+extern template void print_file (const char *, const dwarf_edit &,
+ const unsigned int);
diff --git a/tests/run-dwarf-attributes.sh b/tests/run-dwarf-attributes.sh
new file mode 100755
index 00000000..61472148
--- /dev/null
+++ b/tests/run-dwarf-attributes.sh
@@ -0,0 +1,37 @@
+#! /bin/sh
+# Copyright (C) 2009 Red Hat, Inc.
+# This file is part of Red Hat elfutils.
+#
+# Red Hat elfutils 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; version 2 of the License.
+#
+# Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+#
+# Red Hat elfutils is an included package of the Open Invention Network.
+# An included package of the Open Invention Network is a package for which
+# Open Invention Network licensees cross-license their patents. No patent
+# license is granted, either expressly or impliedly, by designation as an
+# included package. Should you wish to participate in the Open Invention
+# Network licensing program, please visit www.openinventionnetwork.com
+# <http://www.openinventionnetwork.com>.
+
+. $srcdir/test-subr.sh
+
+testfiles testfile
+
+testrun_compare ./dwarf-print --offsets --depth=1 testfile <<\EOF
+testfile:
+ <compile_unit offset=[0xb] stmt_list=[{1 dirs}, {5 line entries}] high_pc=0x804845a low_pc=0x804842c name="m.c" comp_dir="/home/drepper/gnu/new-bu/build/ttt" producer="GNU C 2.96 20000731 (Red Hat Linux 7.0)" language=C89>...
+ <compile_unit offset=[0xca] stmt_list=[{1 dirs}, {4 line entries}] high_pc=0x8048466 low_pc=0x804845c name="b.c" comp_dir="/home/drepper/gnu/new-bu/build/ttt" producer="GNU C 2.96 20000731 (Red Hat Linux 7.0)" language=C89>...
+ <compile_unit offset=[0x15fc] stmt_list=[{1 dirs}, {4 line entries}] high_pc=0x8048472 low_pc=0x8048468 name="f.c" comp_dir="/home/drepper/gnu/new-bu/build/ttt" producer="GNU C 2.96 20000731 (Red Hat Linux 7.0)" language=C89>...
+EOF
+
+exit 0
diff --git a/tests/run-dwarf_edit.sh b/tests/run-dwarf_edit.sh
new file mode 100755
index 00000000..88c103df
--- /dev/null
+++ b/tests/run-dwarf_edit.sh
@@ -0,0 +1,36 @@
+#! /bin/sh
+# Copyright (C) 2009 Red Hat, Inc.
+# This file is part of Red Hat elfutils.
+#
+# Red Hat elfutils 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; version 2 of the License.
+#
+# Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+#
+# Red Hat elfutils is an included package of the Open Invention Network.
+# An included package of the Open Invention Network is a package for which
+# Open Invention Network licensees cross-license their patents. No patent
+# license is granted, either expressly or impliedly, by designation as an
+# included package. Should you wish to participate in the Open Invention
+# Network licensing program, please visit www.openinventionnetwork.com
+# <http://www.openinventionnetwork.com>.
+
+. $srcdir/test-subr.sh
+
+testrun_compare ./dwarf_edit <<\EOF
+consed:
+ <compile_unit name="source-file.c">
+ <base_type ref="ref1" name="int"/>
+ <subprogram name="foo" external=1 type="#ref1" description="foo"/>
+ </compile_unit>
+EOF
+
+exit 0
diff --git a/tests/run-dwarfcmp-self.sh b/tests/run-dwarfcmp-self.sh
new file mode 100755
index 00000000..efc14047
--- /dev/null
+++ b/tests/run-dwarfcmp-self.sh
@@ -0,0 +1,69 @@
+#! /bin/sh
+# Copyright (C) 2009 Red Hat, Inc.
+# This file is part of Red Hat elfutils.
+#
+# Red Hat elfutils 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; version 2 of the License.
+#
+# Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+#
+# Red Hat elfutils is an included package of the Open Invention Network.
+# An included package of the Open Invention Network is a package for which
+# Open Invention Network licensees cross-license their patents. No patent
+# license is granted, either expressly or impliedly, by designation as an
+# included package. Should you wish to participate in the Open Invention
+# Network licensing program, please visit www.openinventionnetwork.com
+# <http://www.openinventionnetwork.com>.
+
+. $srcdir/test-subr.sh
+
+dwarfcmp=${dwarfcmp:-../src/dwarfcmp}
+try_switches=${try_switches:- -iq -i -il}
+
+status=0
+run_one()
+{
+ file="$1"; shift
+ testrun "$dwarfcmp" "$@" "$file" "$file" ||
+ { echo "*** failure in $dwarfcmp $* on $file"; status=1; }
+}
+
+runtest()
+{
+ for file; do
+ if [ -f $file ]; then
+ for try in $try_switches; do
+ run_one "$file" $try
+ done
+ fi
+ done
+}
+
+runtest ../src/addr2line
+runtest ../src/ar
+runtest ../src/elfcmp
+runtest ../src/elflint
+runtest ../src/findtextrel
+runtest ../src/ld
+runtest ../src/nm
+runtest ../src/objdump
+runtest ../src/readelf
+runtest ../src/size
+runtest ../src/strip
+runtest ../src/unstrip
+runtest ../*/*.so
+
+# These are the biggest ones.
+runtest ../src/dwarflint
+runtest ../src/dwarfcmp
+runtest ../src/dwarfcmp-test
+
+exit $status
diff --git a/tests/run-dwarfcmp-test-self.sh b/tests/run-dwarfcmp-test-self.sh
new file mode 100755
index 00000000..823bb9fc
--- /dev/null
+++ b/tests/run-dwarfcmp-test-self.sh
@@ -0,0 +1,4 @@
+dwarfcmp=../src/dwarfcmp-test
+try_switches=-iT
+
+. $srcdir/run-dwarfcmp-self.sh
diff --git a/tests/run-dwarflint-self.sh b/tests/run-dwarflint-self.sh
new file mode 100755
index 00000000..d1f6ca25
--- /dev/null
+++ b/tests/run-dwarflint-self.sh
@@ -0,0 +1,53 @@
+#! /bin/sh
+# Copyright (C) 2009 Red Hat, Inc.
+# This file is part of Red Hat elfutils.
+#
+# Red Hat elfutils 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; version 2 of the License.
+#
+# Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
+#
+# Red Hat elfutils is an included package of the Open Invention Network.
+# An included package of the Open Invention Network is a package for which
+# Open Invention Network licensees cross-license their patents. No patent
+# license is granted, either expressly or impliedly, by designation as an
+# included package. Should you wish to participate in the Open Invention
+# Network licensing program, please visit www.openinventionnetwork.com
+# <http://www.openinventionnetwork.com>.
+
+. $srcdir/test-subr.sh
+
+status=0
+runtest()
+{
+ for file; do
+ if [ -f $file ]; then
+ testrun ../src/dwarflint -q -i --gnu $file ||
+ { echo "*** failure in $file"; status=1; }
+ fi
+ done
+}
+
+runtest ../src/addr2line
+runtest ../src/dwarfcmp
+runtest ../src/elfcmp
+runtest ../src/elflint
+runtest ../src/findtextrel
+runtest ../src/ld
+runtest ../src/nm
+runtest ../src/objdump
+runtest ../src/readelf
+runtest ../src/size
+runtest ../src/strip
+runtest ../src/unstrip
+runtest ../*/*.so
+
+exit $status