diff options
52 files changed, 22999 insertions, 608 deletions
@@ -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 © (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 ¤t_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 ©) + : _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, ×tamp, + &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 |
