[PATCH v3 01/22] rtemstoolkit: Add libdwarf C++ interface.

Chris Johns chrisj at rtems.org
Mon Jun 18 02:28:59 UTC 2018


Provide a C++ interface to libdwarf to:
 - Manage DWARF debug data
 - Manage CU
 - Manage DIE
 - Handle CU line addresses
 - Handle CU source files

Update #3417
---
 rtemstoolkit/rld-dwarf-types.h |  57 +++
 rtemstoolkit/rld-dwarf.cpp     | 938 +++++++++++++++++++++++++++++++++++++++++
 rtemstoolkit/rld-dwarf.h       | 469 +++++++++++++++++++++
 rtemstoolkit/rld-elf.cpp       |  16 +
 rtemstoolkit/rld-elf.h         |  12 +
 rtemstoolkit/wscript           |   5 +-
 6 files changed, 1495 insertions(+), 2 deletions(-)
 create mode 100644 rtemstoolkit/rld-dwarf-types.h
 create mode 100644 rtemstoolkit/rld-dwarf.cpp
 create mode 100644 rtemstoolkit/rld-dwarf.h

diff --git a/rtemstoolkit/rld-dwarf-types.h b/rtemstoolkit/rld-dwarf-types.h
new file mode 100644
index 0000000..4a11639
--- /dev/null
+++ b/rtemstoolkit/rld-dwarf-types.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018, Chris Johns <chrisj at rtems.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/**
+ * @file
+ *
+ * @ingroup rtems-ld
+ *
+ * @brief RTEMS Linker DWARF types.
+  *
+ */
+
+#if !defined (_RLD_DWARF_TYPES_H_)
+#define _RLD_DWARF_TYPES_H_
+
+#include <dwarf.h>
+#include <libdwarf.h>
+
+namespace rld
+{
+  namespace dwarf
+  {
+    /**
+     * Hide the types from libdwarf we use.
+     */
+    typedef ::Dwarf_Debug    dwarf;
+    typedef ::Dwarf_Handler  dwarf_handler;
+    typedef ::Dwarf_Error    dwarf_error;
+    typedef ::Dwarf_Die      dwarf_die;
+    typedef ::Dwarf_Line     dwarf_line;
+    typedef ::Dwarf_Ptr      dwarf_pointer;
+    typedef ::Dwarf_Addr     dwarf_address;
+    typedef ::Dwarf_Off      dwarf_offset;
+    typedef ::Dwarf_Half     dwarf_half;
+    typedef ::Dwarf_Signed   dwarf_signed;
+    typedef ::Dwarf_Unsigned dwarf_unsigned;
+    typedef ::Dwarf_Bool     dwarf_bool;
+    typedef ::Dwarf_Sig8     dwarf_sig8;
+    typedef ::Dwarf_Line     dwarf_line;
+    typedef ::Dwarf_Half     dwarf_tag;
+    typedef ::Dwarf_Half     dwarf_attr;
+  }
+}
+
+#endif
diff --git a/rtemstoolkit/rld-dwarf.cpp b/rtemstoolkit/rld-dwarf.cpp
new file mode 100644
index 0000000..ed135d9
--- /dev/null
+++ b/rtemstoolkit/rld-dwarf.cpp
@@ -0,0 +1,938 @@
+/*
+ * Copyright (c) 2018, Chris Johns <chrisj at rtems.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/**
+ * @file
+ *
+ * @ingroup rtems-ld
+ *
+ * @brief RTEMS Linker DWARF module manages the DWARF format images.
+ *
+ */
+
+#include <string.h>
+
+#include <iostream>
+#include <iomanip>
+#include <list>
+#include <map>
+
+#include <rld.h>
+#include <rld-path.h>
+#include <rld-dwarf.h>
+
+namespace rld
+{
+  namespace dwarf
+  {
+    /**
+     * The libdwarf error.
+     */
+    void libdwarf_error_check (const std::string where,
+                               int               result,
+                               dwarf_error&      error)
+    {
+      if (result != DW_DLV_OK)
+      {
+        std::ostringstream exe_where;
+        std::string        what;
+        exe_where << "dwarf:" << where;
+        what = dwarf_errmsg (error);
+        throw rld::error (what, exe_where.str ());
+      }
+    }
+
+    address::address (const sources& source, dwarf_line& line)
+      : addr (0),
+        source (&source),
+        source_index (-1),
+        source_line (-1),
+        begin_statement (false),
+        block (false),
+        end_sequence (false)
+    {
+        dwarf_error de;
+        int         dr;
+        dr = ::dwarf_lineaddr(line, &addr, &de);
+        libdwarf_error_check ("address::address", dr, de);
+        dr = ::dwarf_line_srcfileno(line, &source_index, &de);
+        libdwarf_error_check ("address::address", dr, de);
+        dr = ::dwarf_lineno(line, &source_line, &de);
+        libdwarf_error_check ("address::address", dr, de);
+        dwarf_bool b;
+        dr = ::dwarf_linebeginstatement(line, &b, &de);
+        libdwarf_error_check ("address::address", dr, de);
+        begin_statement = b ? true : false;
+        dr = ::dwarf_lineblock(line, &b, &de);
+        libdwarf_error_check ("address::address", dr, de);
+        block = b ? true : false;
+        dr = ::dwarf_lineendsequence(line, &b, &de);
+        libdwarf_error_check ("address::address", dr, de);
+        end_sequence = b ? true : false;
+    }
+
+    address::address (const address& orig)
+      : addr (orig.addr),
+        source (orig.source),
+        source_index (orig.source_index),
+        source_line (orig.source_line),
+        begin_statement (orig.begin_statement),
+        block (orig.block),
+        end_sequence (orig.end_sequence)
+    {
+    }
+
+    address::address ()
+      : addr (0),
+        source (nullptr),
+        source_index (-1),
+        source_line (-1),
+        begin_statement (false),
+        block (false),
+        end_sequence (false)
+    {
+    }
+
+    address::~address ()
+    {
+      source = nullptr;
+    }
+
+    bool
+    address::valid () const
+    {
+      return source_line > 0;
+    }
+
+    dwarf_address
+    address::location () const
+    {
+      return addr;
+    }
+
+    std::string
+    address::path () const
+    {
+      if (source == nullptr)
+        throw rld::error ("invalid source pointer", "dwarf:address:path");
+      return (*source)[source_index];
+    }
+
+    int
+    address::line () const
+    {
+      return source_line;
+    }
+
+    bool
+    address::is_a_begin_statement () const
+    {
+      return begin_statement;
+    }
+
+    bool
+    address::is_in_a_block () const
+    {
+      return block;
+    }
+
+    bool
+    address::is_an_end_sequence () const
+    {
+      return end_sequence;
+    }
+
+    address&
+    address::operator = (address& rhs)
+    {
+      if (this != &rhs)
+      {
+        addr = rhs.addr;
+        source = rhs.source;
+        source_index = rhs.source_index;
+        source_line = rhs.source_line;
+        begin_statement = rhs.begin_statement;
+        block = rhs.block;
+        end_sequence = rhs.end_sequence;
+      }
+      return *this;
+    }
+
+    line_addresses::line_addresses (file&             debug,
+                                    debug_info_entry& die)
+      : debug (debug),
+        lines (nullptr),
+        count_ (0)
+    {
+      if (!die.source_lines (lines, count_))
+      {
+        lines = nullptr;
+        count_ = 0;
+      }
+    }
+
+    line_addresses::line_addresses (line_addresses&& orig)
+      : debug (orig.debug),
+        lines (orig.lines),
+        count_ (orig.count_)
+    {
+      orig.lines = nullptr;
+      orig.count_ = 0;
+    }
+
+    line_addresses::~line_addresses ()
+    {
+      if (lines && count_ > 0)
+      {
+        ::dwarf_srclines_dealloc (debug, lines, count_);
+        lines = nullptr;
+        count_ = 0;
+      }
+    }
+
+    size_t
+    line_addresses::count () const
+    {
+      return count_;
+    }
+
+    dwarf_line&
+    line_addresses::operator [] (const int index)
+    {
+      if (index < 0 || index >= count_)
+        throw rld::error ("index out of range",
+                          "line_addresses:indexing");
+      return lines[index];
+    }
+
+    sources::sources (file& debug, debug_info_entry& die)
+      : debug (debug),
+        source (nullptr),
+        count (0),
+        die_offset (die.offset ())
+    {
+      die.source_files (source, count);
+    }
+
+    sources::sources (const sources& orig)
+      : debug (orig.debug),
+        source (nullptr),
+        count (0),
+        die_offset (orig.die_offset)
+    {
+      /*
+       * In a copy constructor we need to get our own copy of the strings. To
+       * do that we need to get the DIE at the offset in the original.
+       */
+      debug_info_entry die (debug, die_offset);
+      die.source_files (source, count);
+    }
+
+    sources::sources (sources&& orig)
+      : debug (orig.debug),
+        source (orig.source),
+        count (orig.count),
+        die_offset (orig.die_offset)
+    {
+      orig.source = nullptr;
+      orig.count = 0;
+    }
+
+    sources::~sources ()
+    {
+      dealloc ();
+    }
+
+    std::string
+    sources::operator [] (const int index) const
+    {
+      if (index <= 0 || index > count)
+        return "unknown";
+      return source[index - 1];
+    }
+
+    void
+    sources::dealloc ()
+    {
+      if (source && count > 0)
+      {
+        /*
+         * The elftoolchain cleans the memory up and there is no compatible
+         * call we can put here so adding the required code causes is a double
+         * free results in a crash.
+         */
+        if (false)
+        {
+          for (int s = 0; s < count; ++s)
+            ::dwarf_dealloc (debug, source[s], DW_DLA_STRING);
+          ::dwarf_dealloc (debug, source, DW_DLA_LIST);
+        }
+        source = nullptr;
+        count = 0;
+      }
+    }
+
+    sources&
+    sources::operator = (sources&& rhs)
+    {
+      if (this != &rhs)
+      {
+        debug = rhs.debug;
+        source = rhs.source;
+        count = rhs.count;
+        die_offset = rhs.die_offset;
+        rhs.source = nullptr;
+        rhs.count = 0;
+      }
+      return *this;
+    }
+
+    debug_info_entry::debug_info_entry (file& debug)
+      : debug (debug),
+        die (nullptr),
+        tag_ (0),
+        offset_ (0)
+    {
+    }
+
+    debug_info_entry::debug_info_entry (file& debug, dwarf_die& die)
+      : debug (debug),
+        die (die),
+        tag_ (0),
+        offset_ (0)
+    {
+    }
+
+    debug_info_entry::debug_info_entry (file& debug, dwarf_offset offset__)
+      : debug (debug),
+        die (nullptr),
+        tag_ (0),
+        offset_ (offset__)
+    {
+        dwarf_error de;
+        int         dr;
+        dr = ::dwarf_offdie (debug, offset_, &die, &de);
+        libdwarf_error_check ("debug_info_entry:debug_info_entry", dr, de);
+    }
+
+    debug_info_entry::debug_info_entry (debug_info_entry&& orig)
+      : debug (orig.debug),
+        die (orig.die),
+        tag_ (orig.tag_),
+        offset_ (orig.offset_)
+    {
+      orig.die = nullptr;
+      orig.tag_ = 0;
+      orig.offset_ = 0;
+    }
+
+    debug_info_entry::~debug_info_entry ()
+    {
+      if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
+        std::cout << "dwarf::debug_info_entry::~debug_info_entry" << std::endl;
+      dealloc ();
+    }
+
+    dwarf_die
+    debug_info_entry::get () const
+    {
+      return die;
+    }
+
+    debug_info_entry::operator dwarf_die& ()
+    {
+      return die;
+    }
+
+    debug_info_entry::operator dwarf_die* ()
+    {
+      return ¨
+    }
+
+    debug_info_entry&
+    debug_info_entry::operator = (debug_info_entry& rhs)
+    {
+      if (this != &rhs)
+      {
+        if (debug != rhs.debug)
+          throw rld::error ("DIE debug info mismatch",
+                            "dwarf:debug_info_entry:operator=");
+        dealloc ();
+        die = rhs.die;
+        tag_ = rhs.tag_;
+        offset_ = rhs.offset_;
+        rhs.die = nullptr;
+      }
+      return *this;
+    }
+
+    bool
+    debug_info_entry::operator == (debug_info_entry& rhs) const
+    {
+      return debug == rhs.debug && die == rhs.die &&
+        tag_ == rhs.tag_ && offset_ == rhs.offset_;
+    }
+
+    bool
+    debug_info_entry::operator == (const dwarf_die rhs) const
+    {
+      return die == rhs;
+    }
+
+    dwarf_tag
+    debug_info_entry::tag ()
+    {
+      if (tag_ == 0)
+      {
+        dwarf_error de;
+        int         dr;
+        dr = ::dwarf_tag(die, &tag_, &de);
+        libdwarf_error_check ("debug_info_entry:debug_info_entry", dr, de);
+      }
+      return tag_;
+    }
+
+    dwarf_offset
+    debug_info_entry::offset ()
+    {
+      if (offset_ == 0)
+      {
+        dwarf_error de;
+        int         dr;
+        dr = ::dwarf_dieoffset (die, &offset_, &de);
+        libdwarf_error_check ("debug_info_entry:debug_info_entry", dr, de);
+      }
+      return offset_;
+    }
+
+    bool
+    debug_info_entry::attribute (dwarf_attr      attr,
+                                 dwarf_unsigned& value,
+                                 bool            error) const
+    {
+      dwarf_error de;
+      int         dr;
+      dr = ::dwarf_attrval_unsigned (die, attr, &value, &de);
+      if (error)
+        libdwarf_error_check ("debug_info_entry:attribute ", dr, de);
+      return dr == DW_DLV_OK;
+    }
+
+    bool
+    debug_info_entry::attribute (dwarf_attr   attr,
+                                 std::string& value,
+                                 bool         error) const
+    {
+      dwarf_error de;
+      int         dr;
+      const char* s = nullptr;
+      value.clear ();
+      dr = ::dwarf_attrval_string (die, attr, &s, &de);
+      if (error)
+        libdwarf_error_check ("debug_info_entry:attribute ", dr, de);
+      if (s != nullptr)
+        value = s;
+      return dr == DW_DLV_OK;
+    }
+
+    bool
+    debug_info_entry::source_lines (dwarf_line*&  lines,
+                                    dwarf_signed& linecount) const
+    {
+      dwarf_error de;
+      int         dr;
+      if (lines != nullptr)
+        throw rld::error ("lines is not null", "debug_info_entry:source_lines");
+      linecount = 0;
+      dr = ::dwarf_srclines (die, &lines, &linecount, &de);
+      if (dr ==  DW_DLV_NO_ENTRY)
+        return false;
+      libdwarf_error_check ("debug_info_entry:source_lines ", dr, de);
+      return true;
+    }
+
+    void
+    debug_info_entry::source_files (char**&       sources,
+                                    dwarf_signed& sourcecount) const
+    {
+      dwarf_error de;
+      int         dr;
+      dr = ::dwarf_srcfiles (die, &sources, &sourcecount, &de);
+      libdwarf_error_check ("debug_info_entry:source_files ", dr, de);
+    }
+
+    void
+    debug_info_entry::dealloc ()
+    {
+      if (die != nullptr) {
+        ::dwarf_dealloc (debug, die, DW_DLA_DIE);
+        die = nullptr;
+      }
+    }
+
+    compilation_unit::compilation_unit (file&             debug,
+                                        debug_info_entry& die,
+                                        dwarf_unsigned    offset)
+      : debug (debug),
+        offset_ (offset),
+        pc_low_ (0),
+        pc_high_ (0),
+        source_ (debug, die),
+        die_offset (die.offset ())
+    {
+      die.attribute (DW_AT_name, name_);
+      name_ = name_;
+
+      die.attribute (DW_AT_producer, producer_);
+
+      die.attribute (DW_AT_low_pc, pc_low_, false);
+
+      if (!die.attribute (DW_AT_high_pc, pc_high_, false))
+        pc_high_ = ~0U;
+
+      if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
+      {
+        std::cout << std::hex << std::setfill ('0')
+                  << "dwarf::compilation_unit: "
+                  << rld::path::basename (name_)
+                  << ": (0x" << std::setw (8) << offset_ << ") ";
+        if (pc_low_ != 0 && pc_high_ != ~0U)
+          std::cout << "pc_low = " << std::setw (8) << pc_low_
+                    << " pc_high = " << std::setw (8) << pc_high_;
+       std::cout  << std::setfill (' ') << std::dec
+                  << std::endl
+                  << " ] " << producer_
+                  << std::endl;
+      }
+
+      line_addresses lines (debug, die);
+      dwarf_address  pc = 0;
+
+      for (size_t l = 0; l < lines.count (); ++l)
+      {
+        address       addr (source_, lines[l]);
+        dwarf_address loc = addr.location ();
+        if (inside (loc) && loc >= pc)
+        {
+          pc = loc;
+          addr_lines_[addr.location ()] = addr;
+          if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
+          {
+            std::cout << "dwarf::compilation_unit: "
+                      << std::hex << std::setw (8) << addr.location () << std::dec
+                      << " - "
+                      << (char) (addr.is_a_begin_statement () ? 'B' : '.')
+                      << (char) (addr.is_in_a_block () ? 'I' : '.')
+                      << (char) (addr.is_an_end_sequence () ? 'E' : '.')
+                      << " - "
+                      << rld::path::basename (addr.path ())
+                      << ':' << addr.line ()
+                      << std::endl;
+          }
+        }
+      }
+    }
+
+    compilation_unit::compilation_unit (compilation_unit&& orig)
+      : debug (orig.debug),
+        offset_ (orig.offset_),
+        name_ (orig.name_),
+        producer_ (orig.producer_),
+        pc_low_ (orig.pc_low_),
+        pc_high_ (orig.pc_high_),
+        source_ (std::move (orig.source_)),
+        addr_lines_ (orig.addr_lines_),
+        die_offset (orig.die_offset)
+    {
+      orig.name_.clear ();
+      orig.producer_.clear ();
+      orig.offset_ = 0;
+      orig.die_offset = 0;
+      orig.pc_low_ = 0;
+      orig.pc_high_ = 0;
+    }
+
+    compilation_unit::compilation_unit (const compilation_unit& orig)
+      : debug (orig.debug),
+        offset_ (orig.offset_),
+        name_ (orig.name_),
+        producer_ (orig.producer_),
+        pc_low_ (orig.pc_low_),
+        pc_high_ (orig.pc_high_),
+        source_ (orig.source_),
+        addr_lines_ (orig.addr_lines_),
+        die_offset (orig.die_offset)
+    {
+    }
+
+    compilation_unit::~compilation_unit ()
+    {
+      if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
+        std::cout << "dwarf::compilation_unit::~compilation_unit: " << name_ << std::endl;
+    }
+
+    std::string
+    compilation_unit::name () const
+    {
+      return name_;
+    }
+
+    std::string
+    compilation_unit::producer () const
+    {
+      return producer_;
+    }
+
+    unsigned int
+    compilation_unit::pc_low () const
+    {
+      return pc_low_;
+    }
+
+    unsigned int
+    compilation_unit::pc_high () const
+    {
+      return pc_high_;
+    }
+
+    bool
+    compilation_unit::get_source (const dwarf_address addr,
+                                  address&            addr_line)
+    {
+      if (addr_lines_.find (addr) == addr_lines_.end ())
+        return false;
+      addr_line = addr_lines_[addr];
+      return true;
+    }
+
+    bool
+    compilation_unit::inside (dwarf_unsigned addr) const
+    {
+      return addr >= pc_low_ && addr < pc_high_;
+    }
+
+    compilation_unit&
+    compilation_unit::operator = (compilation_unit&& rhs)
+    {
+      if (this != &rhs)
+      {
+        debug = rhs.debug;
+        offset_ = rhs.offset_;
+        name_ = rhs.name_;
+        producer_ = rhs.producer_;
+        source_ = std::move (rhs.source_);
+        addr_lines_ = std::move (rhs.addr_lines_),
+        pc_low_ = rhs.pc_low_;
+        pc_high_ = rhs.pc_high_;
+        die_offset = rhs.die_offset;
+        rhs.offset_ = 0;
+        rhs.name_.clear ();
+        rhs.pc_low_ = -1;
+        rhs.pc_high_ = -1;
+        rhs.die_offset = 0;
+      }
+      return *this;
+    }
+
+    compilation_unit&
+    compilation_unit::operator = (const compilation_unit& rhs)
+    {
+      if (this != &rhs)
+      {
+        debug = rhs.debug;
+
+        /*
+         * This is a copy operator so we need to get a new copy of the strings,
+         * we cannot steal the other copy.
+         */
+        offset_ = rhs.offset_;
+        name_ = rhs.name_;
+        producer_ = rhs.producer_;
+        debug_info_entry die (debug, rhs.offset_);
+        source_ = sources (debug, die);
+        addr_lines_ = addresses (rhs.addr_lines_);
+        pc_low_ = rhs.pc_low_;
+        pc_high_ = rhs.pc_high_;
+        die_offset = rhs.die_offset;
+      }
+      return *this;
+    }
+
+    source_flags::source_flags (const std::string& source)
+      : source (source)
+    {
+    }
+
+    bool
+    source_flags_compare::operator () (const source_flags& a,
+                                       const source_flags& b) const
+    {
+      if (by_basename)
+        return rld::path::basename (a.source) < rld::path::basename (b.source);
+      return a.source < b.source;
+    }
+
+    source_flags_compare:: source_flags_compare (bool by_basename)
+      : by_basename (by_basename)
+    {
+    }
+
+    producer_source::producer_source (const std::string& producer)
+      : producer (producer)
+    {
+    }
+
+    producer_source::producer_source ()
+    {
+    }
+
+    file::file ()
+      : debug (nullptr),
+        elf_ (nullptr)
+      {
+    }
+
+    file::~file ()
+    {
+      try
+      {
+        end ();
+      }
+      catch (rld::error re)
+      {
+        std::cerr << "error: rld::dwarf::file::~file: "
+                  << re.where << ": " << re.what
+                  << std::endl;
+      }
+      catch (...)
+      {
+        std::cerr << "error: rld::dwarf::file::~file: unhandled exception"
+                  << std::endl;
+      }
+    }
+
+    void
+    file::begin (rld::elf::file& elf__)
+    {
+      if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
+        std::cout << "dwarf::begin: " << elf__.name () << std::endl;
+
+      if (debug != nullptr || elf_ != nullptr)
+        throw rld::error ("Already called", "dwarf:file:begin");
+
+      /*
+       * DWARF data is not writable.
+       */
+      if (elf__.is_writable ())
+        throw rld::error ("Cannot write DWARF info", "dwarf:file:begin");
+
+      /*
+       * Initialise the DWARF instance.
+       */
+      dwarf_error de;
+      int dr = ::dwarf_elf_init (elf__.get_elf (),
+                                 DW_DLC_READ,
+                                 nullptr,
+                                 this,
+                                 &debug,
+                                 &de);
+      libdwarf_error_check ("file:begin", dr, de);
+
+      /*
+       * Record the ELF instance and obtain a reference to it. The ELF file
+       * cannot end while the DWARF file has not ended.
+       */
+      elf__.reference_obtain ();
+      elf_ = &elf__;
+    }
+
+    void
+    file::end ()
+    {
+      if (debug)
+      {
+        if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
+          std::cout << "dwarf::end: " << name () << std::endl;
+
+        cus.clear ();
+
+        ::dwarf_finish (debug, 0);
+        if (elf_)
+          elf_->reference_release ();
+        elf_ = nullptr;
+        debug = nullptr;
+      }
+    }
+
+    void
+    file::load_debug ()
+    {
+      dwarf_unsigned cu_offset = 0;
+
+      while (true)
+      {
+        dwarf_unsigned cu_next_offset = 0;
+        dwarf_error    de;
+        int            dr;
+
+        dr = ::dwarf_next_cu_header_c(debug, 1,
+                                      nullptr, nullptr, nullptr,  nullptr,
+                                      nullptr, nullptr, nullptr,  nullptr,
+                                      &cu_next_offset, &de);
+        if (dr != DW_DLV_OK)
+          break;
+
+        /*
+         * Fnd the CU DIE.
+         */
+        debug_info_entry die (*this);
+        debug_info_entry ret_die (*this);
+
+        while (true)
+        {
+          dr = ::dwarf_siblingof(debug, die, ret_die, &de);
+          if (dr != DW_DLV_OK)
+            break;
+
+          if (ret_die.tag () == DW_TAG_compile_unit)
+          {
+            cus.push_back (compilation_unit (*this, ret_die, cu_offset));
+            break;
+          }
+
+          die = ret_die;
+        }
+
+        cu_offset = cu_next_offset;
+      }
+    }
+
+    bool
+    file::get_source (const unsigned int addr,
+                      std::string&       source_file,
+                      int&               source_line)
+    {
+      bool r = false;
+
+      /*
+       * Search the CU's collecting the addresses. An address can appear in
+       * more than one CU. It may be the last address and the first.
+       */
+      source_file = "unknown";
+      source_line = -1;
+
+      address match;
+
+      for (auto& cu : cus)
+      {
+        address line;
+        r = cu.get_source (addr, line);
+        if (r)
+        {
+          if (match.valid ())
+          {
+            if (match.is_an_end_sequence ())
+            {
+              match = line;
+            }
+            else if (!line.is_an_end_sequence ())
+            {
+              match = line;
+            }
+          }
+        }
+      }
+
+      if (match.valid ())
+      {
+        source_file = match.path ();
+        source_line = match.line ();
+        r = true;
+      }
+
+      return r;
+    }
+
+    void
+    file::get_producer_sources (producer_sources& producers)
+    {
+      for (auto& cu : cus)
+      {
+        std::string     producer = cu.producer ();
+        producer_source new_producer;
+        source_flags    sf (cu.name ());
+        rld::strings    parts;
+
+        rld::split (parts, producer);
+
+        for (auto& s : parts)
+        {
+          if (s[0] == '-')
+            sf.flags.push_back (s);
+          else
+            new_producer.producer +=  ' ' + s;
+        }
+
+        bool add = true;
+
+        for (auto& p : producers)
+        {
+          if (p.producer == new_producer.producer)
+          {
+            p.sources.push_back (sf);
+            add = false;
+            break;
+          }
+        }
+        if (add)
+        {
+          new_producer.sources.push_back (sf);
+          producers.push_back (new_producer);
+        }
+      }
+    }
+
+    dwarf&
+    file::get_debug ()
+    {
+      return debug;
+    }
+
+    compilation_units&
+    file::get_cus ()
+    {
+      return cus;
+    }
+
+    const std::string&
+    file::name () const
+    {
+      if (!elf_)
+        throw rld::error ("No begin called", "dwarf:fie:name");
+      return elf_->name ();
+    }
+
+    void
+    file::check (const char* where) const
+    {
+      if (!debug || !elf_)
+      {
+        std::string w = where;
+        throw rld::error ("No DWARF or ELF file", "dwarf:file:" + w);
+      }
+    }
+
+  }
+}
diff --git a/rtemstoolkit/rld-dwarf.h b/rtemstoolkit/rld-dwarf.h
new file mode 100644
index 0000000..8a61755
--- /dev/null
+++ b/rtemstoolkit/rld-dwarf.h
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2018, Chris Johns <chrisj at rtems.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/**
+ * @file
+ *
+ * @ingroup rtems-ld
+ *
+ * @brief RTEMS Linker DWARF module manages the libdwarf interface.
+ *
+ */
+
+#if !defined (_RLD_DWARF_H_)
+#define _RLD_DWARF_H_
+
+#include <rld.h>
+#include <rld-dwarf-types.h>
+
+namespace rld
+{
+  namespace dwarf
+  {
+    /**
+     * Forward decls
+     */
+    class sources;
+    class debug_info_entry;
+    class file;
+
+    /**
+     * Address.
+     */
+    class address
+    {
+    public:
+      address (const sources& source, dwarf_line& line);
+      address (const address& orig);
+      address ();
+      ~address ();
+
+      /**
+       * Is this address valid?
+       */
+      bool valid () const;
+
+      /**
+       * The location in the address space.
+       */
+      dwarf_address location () const;
+
+      /**
+       * Source file path. This is a full path.
+       */
+      std::string path () const;
+
+      /**
+       * Line number.
+       */
+      int line () const;
+
+      /**
+       * Is a begin statement?
+       */
+      bool is_a_begin_statement () const;
+
+      /**
+       * Is in a block?
+       */
+      bool is_in_a_block () const;
+
+      /**
+       * Is an end sequence?
+       */
+      bool is_an_end_sequence () const;
+
+      /**
+       * Assigment operator.
+       */
+      address& operator = (address& rhs);
+
+    private:
+
+      dwarf_address  addr;
+      sources const* source;
+      dwarf_unsigned source_index;
+      dwarf_unsigned source_line;
+      bool           begin_statement;
+      bool           block;
+      bool           end_sequence;
+    };
+
+    /**
+     * The addresses table is a map of the addresses in a CU to their line
+     * number.
+     */
+    typedef std::map < const dwarf_address, address > addresses;
+
+
+    /**
+     * Line addresses.
+     */
+    class line_addresses
+    {
+    public:
+      line_addresses (file& debug, debug_info_entry& die);
+      line_addresses (line_addresses&& orig);
+      ~line_addresses ();
+
+      /**
+       * Count of lines.
+       */
+      size_t count () const;
+
+      /**
+       * Index operator.
+       */
+      dwarf_line& operator [] (const int index);
+
+    private:
+
+      file&        debug;
+      dwarf_line*  lines;
+      dwarf_signed count_;
+    };
+
+    /**
+     * Sources.
+     *
+     * This is a CU table of sources. The address's contain an index to a
+     * string in this table.
+     */
+    class sources
+    {
+    public:
+      sources (file& debug, debug_info_entry& die);
+      sources (const sources& orig);
+      sources (sources&& orig);
+      ~sources ();
+
+      /**
+       * Index operator.
+       */
+      std::string operator [] (const int index) const;
+
+      /**
+       * Deallocate.
+       */
+      void dealloc ();
+
+      /**
+       * Move assignment operator.
+       */
+      sources& operator = (sources&& rhs);
+
+    private:
+
+      file&        debug;
+      char**       source;
+      dwarf_signed count;
+      dwarf_offset die_offset;
+    };
+
+    /**
+     * Debug Information Element (DIE).
+     *
+     * This class clean up and deallocations a DIE when it desctructs.
+     */
+    class debug_info_entry
+    {
+    public:
+      /**
+       * Construct the DIE, we need to be careful not to share the DIE pointer.
+       */
+      debug_info_entry (file& debug);
+      debug_info_entry (file& debug, dwarf_die& die);
+      debug_info_entry (file& debug, dwarf_offset offset);
+      debug_info_entry (debug_info_entry&& orig);
+
+      /**
+       * Destruct and clean up.
+       */
+      ~debug_info_entry ();
+
+      /**
+       * Get the DIE.
+       */
+      dwarf_die get () const;
+
+      /**
+       * Casting operators to get the DIE.
+       */
+      operator dwarf_die& ();
+      operator dwarf_die* ();
+
+      /**
+       * Assignment operators.
+       */
+      debug_info_entry& operator = (debug_info_entry& rhs);
+
+      /**
+       * Compare operators.
+       */
+      bool operator == (debug_info_entry& rhs) const;
+      bool operator == (const dwarf_die rhs) const;
+
+      /**
+       * Get the tag.
+       */
+      dwarf_tag tag ();
+
+      /**
+       * Get the offset.
+       */
+      dwarf_offset offset ();
+
+      /**
+       * Get an unsigned attribute.
+       */
+      bool attribute (dwarf_attr      attr,
+                      dwarf_unsigned& value,
+                      bool            error = true) const;
+
+      /**
+       * Get a string attribute.
+       */
+      bool attribute (dwarf_attr   attr,
+                      std::string& value,
+                      bool         error = true) const;
+
+      /**
+       * Get source lines. Returns the CU line table with all columns.
+       *
+       * You need to clean this up.
+       */
+      bool source_lines (dwarf_line*&  lines,
+                         dwarf_signed& linecount) const;
+
+      /**
+       * Get the source files. This is a table of source files in a CU
+       */
+      void source_files (char**&       source,
+                         dwarf_signed& sourcecount) const;
+
+      /**
+       * deallocate the DIE.
+       */
+      void dealloc ();
+
+    private:
+
+      file&        debug;
+      dwarf_die    die;
+      dwarf_tag    tag_;
+      dwarf_offset offset_;
+
+    };
+
+    /**
+     * Compilation Unit.
+     */
+    class compilation_unit
+    {
+    public:
+      compilation_unit (file&             debug,
+                        debug_info_entry& die,
+                        dwarf_offset      offset);
+      compilation_unit (const compilation_unit& orig);
+      compilation_unit (compilation_unit&& orig);
+      ~compilation_unit ();
+
+      /**
+       * Name of the CU.
+       */
+      std::string name () const;
+
+      /**
+       * Producer of the CL, the tool that compiled it.
+       */
+      std::string producer () const;
+
+      /**
+       * The low PC value, 0 if there is no attribute.
+       */
+      unsigned int pc_low () const;
+
+      /**
+       * The high PC value, ~0 if there is no attribute.
+       */
+      unsigned int pc_high () const;
+
+      /**
+       * Get the source and line for an address. If the address does not match
+       * false is returned the file is set to 'unknown' and the line is set to
+       * 0.
+       */
+      bool get_source (const dwarf_address addr,
+                       address&            addr_line);
+
+      /**
+       * Is the address inside the CU? Becareful using this because not all CUs
+       * have these attributes set and the address range will be the entire
+       * address space.
+       */
+      bool inside (dwarf_unsigned addr) const;
+
+      /**
+       * Move assignment operator.
+       */
+      compilation_unit& operator = (compilation_unit&& rhs);
+
+      /**
+       * Copy assignment operator.
+       */
+      compilation_unit& operator = (const compilation_unit& rhs);
+
+    private:
+
+      file&          debug;       ///< The DWARF debug handle.
+      dwarf_unsigned offset_;     ///< The CU offset in .debug_info
+      std::string    name_;       ///< The name of the CU.
+      std::string    producer_;   ///< The producer of the CU.
+      dwarf_unsigned pc_low_;     ///< The PC low address
+      dwarf_unsigned pc_high_;    ///< The PC high address.
+
+      sources        source_;     ///< Sources table for this CU.
+      addresses      addr_lines_; ///< Address table.
+
+      dwarf_offset   die_offset;  ///< The offset of the DIE in the image.
+    };
+
+    typedef std::list < compilation_unit > compilation_units;
+
+    /**
+     * A source and flags.
+     */
+    struct source_flags
+    {
+      std::string  source;  ///< The source file.
+      rld::strings flags;   ///< the flags used to build the code.
+
+      source_flags (const std::string& source);
+    };
+
+    typedef std::vector < source_flags > sources_flags;
+
+    /**
+     * Worker to sort the sources.
+     */
+    struct source_flags_compare
+    {
+      const bool by_basename;
+
+      bool operator () (const source_flags& a, const source_flags& b) const;
+
+      source_flags_compare (bool by_basename = true);
+    };
+
+    /**
+     * A container of producers and the source they build.
+     */
+    struct producer_source
+    {
+      std::string   producer; ///< The producer
+      sources_flags sources;  ///< The sources built by the producer with
+                              ///  flags.
+
+      producer_source (const std::string& producer);
+      producer_source ();
+    };
+
+    typedef std::list < producer_source > producer_sources;
+
+    /**
+     * A DWARF file.
+     */
+    class file
+    {
+    public:
+     /**
+       * Construct an DWARF file.
+       */
+      file ();
+
+      /**
+       * Destruct the DWARF file object.
+       */
+      ~file ();
+
+      /**
+       * Begin using the DWARF information in an ELF file.
+       *
+       * @param elf The ELF file.
+       */
+      void begin (rld::elf::file& elf);
+
+      /**
+       * End using the DWARF file.
+       */
+      void end ();
+
+      /**
+       * Load the DWARF debug information.
+       */
+      void load_debug ();
+
+      /**
+       * Get the source location given an address.
+       */
+      bool get_source (const unsigned int address,
+                       std::string&       source_file,
+                       int&               source_line);
+
+      /**
+       * Get the producer sources from the compilation units.
+       */
+      void get_producer_sources (producer_sources& producers);
+
+      /**
+       * Get the DWARF debug information reference.
+       */
+      dwarf& get_debug ();
+
+      /**
+       * Get the compilation units.
+       */
+      compilation_units& get_cus ();
+
+      /*
+       * The DWARF debug conversion operator.
+       */
+      operator dwarf () { return get_debug (); }
+
+      /**
+       * Get the name of the file.
+       */
+      const std::string& name () const;
+
+    private:
+
+      /**
+       * Check if the file is usable. Throw an exception if not.
+       *
+       * @param where Where the check is performed.
+       */
+      void check (const char* where) const;
+
+      dwarf           debug;   ///< The libdwarf debug data
+      rld::elf::file* elf_;    ///< The libelf reference used to access the
+                               ///  DWARF data.
+
+      compilation_units cus;   ///< Image's compilation units
+    };
+
+  }
+}
+
+#endif
diff --git a/rtemstoolkit/rld-elf.cpp b/rtemstoolkit/rld-elf.cpp
index 8b2ac5e..2d3d090 100644
--- a/rtemstoolkit/rld-elf.cpp
+++ b/rtemstoolkit/rld-elf.cpp
@@ -421,6 +421,7 @@ namespace rld
 
     file::file ()
       : fd_ (-1),
+        refs (0),
         archive (false),
         writable (false),
         elf_ (0),
@@ -437,6 +438,18 @@ namespace rld
       end ();
     }
 
+    void
+    file::reference_obtain ()
+    {
+      ++refs;
+    }
+
+    void
+    file::reference_release ()
+    {
+      --refs;
+    }
+
     void
     file::begin (const std::string& name__, int fd__, const bool writable_)
     {
@@ -551,6 +564,9 @@ namespace rld
     void
     file::end ()
     {
+      if (refs > 0)
+        throw rld::error ("References still held", "elf:file:end: " + name_);
+
       if (elf_)
       {
         if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
diff --git a/rtemstoolkit/rld-elf.h b/rtemstoolkit/rld-elf.h
index fffe036..92e92c1 100644
--- a/rtemstoolkit/rld-elf.h
+++ b/rtemstoolkit/rld-elf.h
@@ -644,6 +644,17 @@ namespace rld
        */
       bool is_writable () const;
 
+      /**
+       * Obtain a reference to this object. End fails while references are
+       * held.
+       */
+      void reference_obtain ();
+
+      /**
+       * Release the reference to this object.
+       */
+      void reference_release ();
+
     private:
 
       /**
@@ -697,6 +708,7 @@ namespace rld
       void error (const char* where) const;
 
       int                  fd_;        //< The file handle.
+      int                  refs;       //< The reference count.
       std::string          name_;      //< The name of the file.
       bool                 archive;    //< The ELF file is part of an archive.
       bool                 writable;   //< The file is writeable.
diff --git a/rtemstoolkit/wscript b/rtemstoolkit/wscript
index 808d974..4c30935 100644
--- a/rtemstoolkit/wscript
+++ b/rtemstoolkit/wscript
@@ -70,8 +70,8 @@ def build(bld):
     #
     conf['warningflags'] = ['-Wall', '-Wextra', '-pedantic']
     conf['optflags'] = bld.env.C_OPTS
-    conf['cflags'] = ['-pipe', '-g'] + conf['optflags']
-    conf['cxxflags'] = ['-pipe', '-g'] + conf['optflags']
+    conf['cflags'] = list(set(['-pipe', '-g'] + conf['optflags']))
+    conf['cxxflags'] = list(set(['-pipe', '-g', '-std=c++11'] + conf['optflags']))
     conf['linkflags'] = ['-g']
 
     #
@@ -90,6 +90,7 @@ def build(bld):
                   'rld-cc.cpp',
                   'rld-compression.cpp',
                   'rld-config.cpp',
+                  'rld-dwarf.cpp',
                   'rld-elf.cpp',
                   'rld-files.cpp',
                   'rld-outputter.cpp',
-- 
2.15.1



More information about the devel mailing list