From: Roland McGrath Date: Sun, 4 Jan 2009 13:11:23 +0000 (-0800) Subject: unpolished first milestone for C++ and dwarfcmp X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d3c0611ae6427a3035940a3899798980763012b1;p=thirdparty%2Felfutils.git unpolished first milestone for C++ and dwarfcmp --- diff --git a/configure.ac b/configure.ac index 25804a8ba..a40949ee8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. dnl Configure input file for elfutils. -*-autoconf-*- dnl -dnl Copyright (C) 1996-2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc. +dnl Copyright (C) 1996-2009 Red Hat, Inc. dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by @@ -63,6 +63,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" diff --git a/libdw++/dwarf b/libdw++/dwarf new file mode 100644 index 000000000..c34d7a903 --- /dev/null +++ b/libdw++/dwarf @@ -0,0 +1,1107 @@ +/* -*- 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 + . */ + +#ifndef _ELFUTILS_DWARF +#define _ELFUTILS_DWARF 1 + +#include "libdw.h" +#include "dwarf.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// DWARF reader interfaces: front end to routines +namespace elfutils +{ + template + inline bool operator== (const std::pair &a, const pair2 &b) + { + return a.first == b.first && a.second == b.second; + } + + // One DWARF object file. + class dwarf + { + private: + template + static inline std::string known_name (int code) + { + const char *known = lookup_known (code); + if (known != NULL) + return std::string (known); + std::ostringstream os; + os.setf(std::ios::hex, std::ios::basefield); + os << code; + return os.str (); + } + + public: + static const char *known_attribute (int); + static const char *known_tag (int); + + static inline std::string attribute_name (int code) + { + return known_name (code); + } + static inline std::string tag_name (int code) + { + return known_name (code); + } + + template + static inline std::string attribute_name (const attribute &attr) + { + int code = attr.first; + return attribute_name (code); + } + + private: + // XXX make this an instance method to include irritant context + static void throw_libdw (void) // XXX raises (...) + { + throw std::runtime_error(::dwarf_errmsg (-1)); + } + static inline void xif (bool fail) + { + if (unlikely (fail)) + throw_libdw (); + } + + template + class skipping_wrapper + { + protected: + typedef typename raw::const_iterator raw_iterator; + + raw _m_raw; + + public: + inline skipping_wrapper (raw raw) : _m_raw (raw) {} + + public: + /* + iterator: wraps raw iterator, skips DW_AT_sibling + size/empty: search for DW_AT_sibling, adjust raw size + */ + + class const_iterator + : public std::iterator + { + friend class skipping_wrapper; + 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: + + // 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); + } + + 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 (*_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 debug_info_entry + { + private: + ::Dwarf_Die _m_die; + inline ::Dwarf_Die *thisdie () const + { + return const_cast< ::Dwarf_Die *> (&_m_die); + } + + friend class dwarf; + protected: + + inline debug_info_entry () + { + memset (&_m_die, 0, sizeof _m_die); + } + + inline debug_info_entry (::Dwarf *dw, ::Dwarf_Off off) + { + xif (::dwarf_offdie (dw, off, &_m_die) == NULL); + } + + public: + // Containers, see class definitions below. + class children; + inline children children () const; + class raw_attributes; + raw_attributes raw_attributes () const; + class attributes; + attributes attributes () 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 + bool operator== (const die &other) const + { + return (attributes () == other.attributes () + && children () == other.children ()); + } + template + bool operator!= (const die &other) const + { + return !(*this == other); + } + + ::Dwarf_Off offset () const + { + return ::dwarf_dieoffset (thisdie ()); + } + }; + + // Container for list of child DIEs, intended to be a compatible with + // a read-only, unidirectional subset of std::list. + class debug_info_entry::children + { + friend class debug_info_entry; + protected: + const debug_info_entry &_m_die; + + inline children (const debug_info_entry &die) : _m_die (die) {} + + public: + + bool empty () const + { + return begin () == end (); + } + + class const_iterator + : public std::iterator + { + friend class debug_info_entry; + 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); + xif (result < 0); + } + + public: + + inline const debug_info_entry &operator* () const + { + return _m_die; + } + + inline const_iterator &operator= (const const_iterator &other) + { + _m_die = other._m_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); + } + + inline const_iterator &operator++ () // prefix + { + int result = ::dwarf_siblingof (&_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); + } + inline const_iterator end () const + { + return const_iterator (); + } + + template + bool operator== (const other_children &other) const + { + return std::equal (begin (), end (), other.begin ()); + } + template + 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>. + class debug_info_entry::raw_attributes + { + friend class debug_info_entry; + private: + const debug_info_entry &_m_die; + + raw_attributes (const debug_info_entry &die) : _m_die (die) {} + + public: + + size_t size () const; + inline bool empty () const + { + return size () == 0; + } + + class const_iterator + : public std::iterator + { + friend class raw_attributes; + 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 (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) {} + + public: + + 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_offset == other._m_offset); + } + 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); + xif (result < 0); + _m_offset = result; + return *this; + } + inline const_iterator operator++ (int) // postfix + { + const_iterator prev = *this; + ++*this; + return prev; + } + + inline attribute operator* () const + { + return attribute (_m_attr); + } + }; + inline const_iterator begin () const + { + const_iterator i = const_iterator (_m_die, 0); + return ++i; + } + inline const_iterator end () const + { + return const_iterator (_m_die, 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; + } + }; + + private: + static inline bool skip_sibling (const attribute &attr) + { + return attr.first == ::DW_AT_sibling; + } + + typedef skipping_wrapper + attributes_base; + + public: + // Container for attributes, indexed by name, intended to be compatible + // with a read-only subset of std::unordered_map. + // This excludes DW_AT_sibling. + class debug_info_entry::attributes : public attributes_base + { + friend class dwarf; + private: + inline attributes (class raw_attributes raw) : attributes_base (raw) {} + + public: + 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 () const + { + return std::map (begin (), end ()); + } + + template + 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 std::equal algorithm will + compare corresponding elements in order. So we need an ordered + map of our attributes for the comparison. */ + const std::map mine = *this; + const std::map &his = other; + return mine == his; + } + template + bool operator!= (const attrs &other) const + { + return !(*this == other); + } + }; + + // This describes the value of an attribute. + // XXX dummy for now + class attr_value + { + public: + inline bool operator== (const attr_value &other) const + { + return true; // XXX dummy value comparison always true + } + inline bool operator!= (const attr_value &other) const + { + return !(*this == other); + } + + debug_info_entry ref () const + { + return debug_info_entry (); // XXX dummy hack + } + }; + + // This describes one attribute, equivalent to pair. + class attribute + { + friend class debug_info_entry::raw_attributes::const_iterator; + private: + const ::Dwarf_Attribute _m_attr; + inline ::Dwarf_Attribute *thisattr () const + { + return const_cast< ::Dwarf_Attribute *> (&_m_attr); + } + + 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 ::Dwarf_Attribute &attr) + : _m_attr (attr), first (*this) {} + + public: + lhs first; + attr_value second; + + // This lets pair<...> x = (attribute) y work. + operator std::pair () const + { + return std::make_pair (static_cast (first), second); + } + + template + inline bool operator== (const pair &other) const + { + return first == other.first && second == other.second; + } + template + inline bool operator!= (const pair &other) const + { + return !(*this == other); + } + }; + + class raw_compile_unit : public debug_info_entry + { + public: + inline raw_compile_unit (const debug_info_entry &die) + : debug_info_entry (die) {} + + /* + containers/iterators: + + lines + macros + + abbrevs (punt) + + */ + }; + + // Same as raw_compile_unit, but the compile_unit::children + // container flattens DW_TAG_imported_unit children. + class compile_unit : public raw_compile_unit + { + public: + compile_unit (raw_compile_unit raw) : raw_compile_unit (raw) {} + + class children : public raw_compile_unit::children + { + friend class compile_unit; + private: + + inline children (const compile_unit &cu) + : raw_compile_unit::children::children (cu) {} + + public: + + class const_iterator + : public std::iterator + { + friend class children; + private: + + typedef raw_compile_unit::children::const_iterator raw_iterator; + std::stack _m_stack; + const raw_iterator _m_end; + + /* Push and pop until either _m_stack.top () == _m_end or + it's looking at a DIE other than DW_TAG_imported_unit. */ + inline void jiggle () + { + while (true) + { + raw_iterator &i = _m_stack.top (); + + if (i == _m_end) + { + /* We're at the end of this raw_compile_unit. + 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) + .ref ().children ().begin ()); + else + // This is some other DIE. Iterate on it. + break; + } + } + + inline const_iterator (const raw_iterator &end) : _m_end (end) {} + + inline const_iterator (const raw_iterator &end, const raw_iterator &i) + : _m_end (end) + { + _m_stack.push (i); + jiggle (); + } + + public: + + inline const_iterator &operator= (const const_iterator &other) + { + _m_stack = other._m_stack; + return *this; + } + + inline bool operator== (const_iterator &other) const + { + return _m_stack == other._m_stack; + } + inline bool operator!= (const_iterator &other) const + { + return !(*this == other); + } + + inline const debug_info_entry &operator* () const + { + return *_m_stack.top (); + } + + inline const_iterator &operator++ () // prefix + { + ++_m_stack.top (); + jiggle (); + return *this; + } + inline const_iterator operator++ (int) // postfix + { + const_iterator prev = *this; + ++*this; + return prev; + } + }; + + const_iterator begin () const + { + return const_iterator (_m_die.children ().end (), + _m_die.children ().begin ()); + } + const_iterator end () const + { + return const_iterator (_m_die.children ().end ()); + } + + template + bool operator== (const other_children &other) const + { + return std::equal (begin (), end (), other.begin ()); + } + template + bool operator!= (const other_children &other) const + { + return !(*this == other); + } + }; + inline children children () const + { + return children::children (*this); + } + }; + + // Container for raw CUs in file order, intended to be compatible + // with a read-only subset of std::list. + class raw_compile_units + { + friend class dwarf; + private: + const dwarf &_m_file; + + raw_compile_units (const dwarf &file) : _m_file (file) {} + + public: + class const_iterator + : public std::iterator + { + friend class raw_compile_units; + private: + debug_info_entry _m_die; + const dwarf *_m_file; // XXX + ::Dwarf_Off _m_next; // XXX + + inline const_iterator () + : _m_file (NULL), _m_next (-1) {} // end () value + inline const_iterator (const dwarf &file) : _m_file (&file), _m_next (0) + { + ++*this; + } + + public: + + inline const debug_info_entry &operator* () const + { + return _m_die; + } + + 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; + } + 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 ()); + return *this; + } + inline const_iterator operator++ (int) // postfix + { + const_iterator prev = *this; + ++*this; + return prev; + } + }; + + const_iterator begin () const + { + return const_iterator (_m_file); + } + inline const_iterator end () const + { + 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 raw_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 compile_units_base; + + public: + + // Container for logical CUs in file order, intended to be compatible + // with a read-only subset of std::list. + class compile_units : public compile_units_base + { + friend class dwarf; + private: + compile_units (class raw_compile_units raw) : compile_units_base (raw) {} + + public: + + template + bool operator== (const units &other) const + { + return std::equal (begin (), end (), other.begin ()); + } + template + bool operator!= (const units &other) const + { + return !(*this == other); + } + }; + inline class compile_units compile_units () const + { + return compile_units::compile_units (raw_compile_units ()); + } + + /* + raw CU: compile_unit or partial_unit, raw DIE children + logical CU: compile_unit, DIE children with imported_unit replaced + + containers/iterators: + raw CU in file order + pubnames: list of (CU, DIE, FQ name string) + aranges: list of (start, len, CU file offset) + raw CU by addr (getarange_addr) + + logical CU in file order + logical CU by addr + + */ + + 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; + } + + dwarf (::Dwarf *dw) : _m_dw (dw) {}; + + template + inline bool operator== (const file &other) const + { + return compile_units () == other.compile_units (); + } + template + inline bool operator!= (const file &other) const + { + return !(*this == other); + } + }; + + inline class dwarf::debug_info_entry::children + dwarf::debug_info_entry::children () const + { + return children::children (*this); + } + + inline class dwarf::debug_info_entry::raw_attributes + dwarf::debug_info_entry::raw_attributes () const + { + return raw_attributes::raw_attributes (*this); + } + + inline class dwarf::debug_info_entry::attributes + dwarf::debug_info_entry::attributes () const + { + return attributes::attributes (raw_attributes ()); + } +}; + +// DWARF writer interfaces (pure object construction) +namespace elfutils +{ + class dwarf_output + { + public: + class compile_units; + + class attr_value : public dwarf::attr_value {}; // XXX later + + class debug_info_entry + { + public: + class children : public std::list + { + friend class debug_info_entry; + private: + children () {} + + template + children (const childrens &other) + : std::list (other.begin (), other.end ()) {} + }; + class attributes : public std::map + { + friend class debug_info_entry; + private: + attributes () {} + + template + attributes (const attrs &other) + : std::map (other.begin (), other.end ()) {} + }; + + private: + const int _m_tag; + attributes _m_attributes; + children _m_children; + + public: + debug_info_entry (int t) : _m_tag (t) + { + 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 + debug_info_entry (const die &die) + : _m_tag (die.tag ()), + _m_attributes (die.attributes ()), + _m_children (die.children ()) + {} + + inline int tag () const + { + return _m_tag; + } + + inline bool has_children () const + { + return !_m_children.empty (); + } + + inline children &children () + { + return _m_children; + } + + inline attributes &attributes () + { + return _m_attributes; + } + + template + bool operator== (const die &other) const + { + return (other.attributes () == attributes () + && other.children () == children ()); + } + }; + + typedef debug_info_entry::attributes::value_type attribute; + + class compile_unit : public debug_info_entry + { + friend class compile_units; + private: + inline compile_unit () : debug_info_entry (::DW_TAG_compile_unit) {} + + template + compile_unit (const die &die) : debug_info_entry (die) + { + if (die.tag () != ::DW_TAG_compile_unit) + throw std::invalid_argument ("not a compile_unit entry"); + } + }; + + // Main container anchoring all the output. + class compile_units : public std::list + { + friend class dwarf_output; + private: + // Default constructor: an empty container, no CUs. + inline compile_units () {} + + // Constructor copying CUs from input container. + template + compile_units(const input &units) + : std::list (units.begin (), units.end ()) + {} + + public: + inline compile_unit &new_unit () + { + compile_unit nu; + push_back (nu); + return back (); + } + }; + + private: + compile_units _m_units; + + public: + class compile_units &compile_units () + { + return _m_units; + } + + public: + // Default constructor: an empty container, no CUs. + inline dwarf_output () {} + + // Constructor copying CUs from an input file (dwarf or dwarf_output). + template + dwarf_output (const input &dw) : _m_units (dw.compile_units ()) {} + }; +}; + +#endif // diff --git a/src/Makefile.am b/src/Makefile.am index f72bb4589..071d7a22a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in ## -## Copyright (C) 1996-2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. +## Copyright (C) 1996-2002,2003,2004,2005,2006,2007,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 @@ -40,6 +40,9 @@ INCLUDES = -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ -I$(srcdir)/../libdw -I$(srcdir)/../libdwfl \ -I$(srcdir)/../libasm -I$(srcdir)/../lib -I.. +AM_CXXFLAGS = $(AM_CFLAGS:gnu99=gnu++0x) \ + -Wno-unused-parameter + AM_LDFLAGS = -Wl,-rpath-link,../libelf:../libdw YACC = @YACC@ -d @@ -52,7 +55,8 @@ 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 \ + dwarfcmp ld_dsos = libld_elf_i386_pic.a @@ -78,6 +82,8 @@ 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 + noinst_HEADERS = ld.h symbolhash.h sectionhash.h versionhash.h \ ldscript.h xelf.h unaligned.h @@ -110,6 +116,7 @@ size_no_Wformat = yes strings_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 @@ -125,6 +132,7 @@ elflint_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl findtextrel_LDADD = $(libdw) $(libelf) $(libmudflap) addr2line_LDADD = $(libdw) $(libmudflap) elfcmp_LDADD = $(libebl) $(libelf) $(libmudflap) -ldl +dwarfcmp_LDADD = $(libdw) $(libmudflap) -ldl objdump_LDADD = $(libasm) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl ranlib_LDADD = libar.a $(libelf) $(libeu) $(libmudflap) strings_LDADD = $(libelf) $(libeu) $(libmudflap) diff --git a/src/dwarfcmp.cc b/src/dwarfcmp.cc new file mode 100644 index 000000000..fcd1b20aa --- /dev/null +++ b/src/dwarfcmp.cc @@ -0,0 +1,313 @@ +/* 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 + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../libdw++/dwarf" + +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 }, + + { 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."); + +/* Strings for arguments in help texts. */ +static const char args_doc[] = N_("FILE1 FILE2"); + +/* 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; + + +static Dwarf * +open_file (const char *fname, int *fdp) +{ + int fd = open (fname, O_RDONLY); + if (unlikely (fd == -1)) + error (EXIT_FAILURE, errno, gettext ("cannot open '%s'"), fname); + Dwarf *dw = dwarf_begin (fd, DWARF_C_READ); + if (dw == NULL) + error (EXIT_FAILURE, 0, + gettext ("cannot create DWARF descriptor for '%s': %s"), + fname, dwarf_errmsg (-1)); + *fdp = fd; + return dw; +} + + +// XXX make translation-friendly +struct context +{ + const dwarf::debug_info_entry *a_; + const dwarf::debug_info_entry *b_; + const char *container_; + + context (const dwarf::debug_info_entry &a, const dwarf::debug_info_entry &b) + : a_ (&a), b_ (&b), container_ (NULL) {} + context () : a_ (NULL), b_ (NULL), container_ ("compilation units") {} + + ostream &location () const + { + if (a_ == NULL) + cout << "files differ: "; + else + cout << hex << a_->offset () << " vs " << b_->offset () << ": "; + return cout; + } + + void container (const char *msg) const + { + location () << msg << " " << container_ << endl; + } + + void missing () const + { + container ("missing"); + } + + void extra () const + { + container ("extra"); + } + + void tag () const + { + location () << "different tag" << endl; + } + + void attributes () const + { + location () << "different attributes" << endl; + } + + void values (int name) const + { + location () << "different values for attribute 0x" << name << endl; + } +}; + +template +static int +describe_mismatch (const container1 &a, const container2 &b, const context &say) +{ + typename container1::const_iterator i = a.begin (); + typename container2::const_iterator j = b.begin (); + int result = 0; + while (i != a.end ()) + { + if (j == b.end ()) + { + say.missing (); // b lacks some of a. + result = 1; + break; + } + result = describe_mismatch (*i, *j, say); + assert ((result != 0) == (*i != *j)); + if (result != 0) + break; + ++i; + ++j; + } + if (result == 0 && j != b.end ()) + { + say.extra (); // a lacks some of b. + result = 1; + } + return result; +} + +template<> +int +describe_mismatch (const dwarf::debug_info_entry &a, + const dwarf::debug_info_entry &b, + const context &ctx) +{ + context here (a, b); + + int result = a.tag () != b.tag (); + if (result != 0) + here.tag (); + + if (result == 0) + { + here.container_ = "attributes"; + result = describe_mismatch (a.attributes (), b.attributes (), here); + assert ((result != 0) == (a.attributes () != b.attributes ())); + } + if (result == 0) + { + here.container_ = "children"; + result = describe_mismatch (a.children (), b.children (), here); + assert ((result != 0) == (a.children () != b.children ())); + } + return result; +} + +template<> +int +describe_mismatch (const dwarf::compile_unit &a, const dwarf::compile_unit &b, + const context &ctx) +{ + return describe_mismatch (static_cast (a), + static_cast (b), + ctx); +} + +template<> +int describe_mismatch (const dwarf::attribute &a, const dwarf::attribute &b, + const context &say) +{ + int result = a.first != b.first; + if (result != 0) + say.attributes (); + else + { + result = a.second != b.second; + if (result != 0) + say.values (a.first); + } + return result; +} + +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)) + { + 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 + 1]; + int fd2; + Dwarf *dw2 = open_file (fname2, &fd2); + + elfutils::dwarf file1 (dw1); + elfutils::dwarf file2 (dw2); + + int result = 0; + + if (quiet) + result = !(file1 == file2); + else + result = describe_mismatch (file1.compile_units (), file2.compile_units (), + context ()); + + 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 *arg, + struct argp_state *state __attribute__ ((unused))) +{ + switch (key) + { + case 'q': + quiet = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +}