]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
unpolished first milestone for C++ and dwarfcmp
authorRoland McGrath <roland@redhat.com>
Sun, 4 Jan 2009 13:11:23 +0000 (05:11 -0800)
committerRoland McGrath <roland@redhat.com>
Sun, 4 Jan 2009 13:11:23 +0000 (05:11 -0800)
configure.ac
libdw++/dwarf [new file with mode: 0644]
src/Makefile.am
src/dwarfcmp.cc [new file with mode: 0644]

index 25804a8baaa02446b05e3fa52a8dd6a4b44b721e..a40949ee8229c2abf0917f2c02870fb52400b9c1 100644 (file)
@@ -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 (file)
index 0000000..c34d7a9
--- /dev/null
@@ -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
+   <http://www.openinventionnetwork.com>.  */
+
+#ifndef _ELFUTILS_DWARF
+#define _ELFUTILS_DWARF        1
+
+#include "libdw.h"
+#include "dwarf.h"
+#include <stdexcept>
+#include <iterator>
+
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <list>
+#include <map>
+#include <stack>
+#include <algorithm>
+#include <functional>
+
+// DWARF reader interfaces: front end to <libdw.h> routines
+namespace elfutils
+{
+  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;
+  }
+
+  // One DWARF object file.
+  class dwarf
+  {
+  private:
+    template<const char *lookup_known (int)>
+    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<known_attribute> (code);
+    }
+    static inline std::string tag_name (int code)
+    {
+      return known_name<known_tag> (code);
+    }
+
+    template<class attribute>
+    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 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;
+
+    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<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:
+
+       // 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<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 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<class die>
+      bool operator== (const die &other) const
+      {
+       return (attributes () == other.attributes ()
+               && children () == other.children ());
+      }
+      template<class die>
+      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<debug_info_entry>.
+    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<std::input_iterator_tag, debug_info_entry>
+      {
+       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<class other_children>
+      bool operator== (const other_children &other) const
+      {
+       return std::equal (begin (), end (), other.begin ());
+      }
+      template<class 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
+    {
+      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<std::input_iterator_tag, attribute>
+      {
+       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<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) {}
+
+      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<class debug_info_entry::raw_attributes,
+                            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 : 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<int, attr_value> () const
+      {
+       return std::map<int, attr_value> (begin (), end ());
+      }
+
+      template<class 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 std::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, attr_value> &his = other;
+       return mine == his;
+      }
+      template<class attrs>
+      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<const int, attr_value>.
+    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 int, attr_value> () const
+      {
+       return std::make_pair (static_cast<int> (first), 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);
+      }
+    };
+
+    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<std::input_iterator_tag, debug_info_entry>
+       {
+         friend class children;
+       private:
+
+         typedef raw_compile_unit::children::const_iterator raw_iterator;
+         std::stack<raw_iterator> _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<class other_children>
+       bool operator== (const other_children &other) const
+       {
+         return std::equal (begin (), end (), other.begin ());
+       }
+       template<class other_children>
+       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<raw_compile_unit>.
+    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<std::input_iterator_tag, raw_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 ()
+         : _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<class raw_compile_units,
+                            raw_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:
+
+      template<class units>
+      bool operator== (const units &other) const
+      {
+       return std::equal (begin (), end (), other.begin ());
+      }
+      template<class 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 ());
+    }
+
+    /*
+      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<class file>
+    inline bool operator== (const file &other) const
+    {
+      return compile_units () == other.compile_units ();
+    }
+    template<class file>
+    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<debug_info_entry>
+      {
+       friend class debug_info_entry;
+      private:
+        children () {}
+
+       template<class childrens>
+       children (const childrens &other)
+         : std::list<debug_info_entry>  (other.begin (), other.end ()) {}
+      };
+      class attributes : public std::map<int, attr_value>
+      {
+       friend class debug_info_entry;
+      private:
+       attributes () {}
+
+       template<class attrs>
+       attributes (const attrs &other)
+         : std::map<int, attr_value> (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<class die>
+      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<class die>
+      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<class die>
+      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<compile_unit>
+    {
+      friend class dwarf_output;
+    private:
+      // Default constructor: an empty container, no CUs.
+      inline compile_units () {}
+
+      // Constructor copying CUs from input container.
+      template<class input>
+      compile_units(const input &units)
+       : std::list<compile_unit> (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<class input>
+    dwarf_output (const input &dw) : _m_units (dw.compile_units ()) {}
+  };
+};
+
+#endif // <elfutils/dwarf>
index f72bb4589049a8ac80242fa3e228364bd82700fa..071d7a22a57a3dd54604fc22a1f8966b400b8568 100644 (file)
@@ -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 (file)
index 0000000..fcd1b20
--- /dev/null
@@ -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
+   <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 "../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<typename container1, typename container2>
+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<const dwarf::debug_info_entry &> (a),
+                           static_cast<const dwarf::debug_info_entry &> (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;
+}