summaryrefslogtreecommitdiff
path: root/rtemstoolkit
diff options
context:
space:
mode:
authorChris Johns <chrisj@rtems.org>2018-05-08 15:09:38 +1000
committerChris Johns <chrisj@rtems.org>2018-06-15 15:54:25 +1000
commit558cab8e4f0d1793e7be21cc118aed5b293c022a (patch)
treeb6947c683e48f41d12f6d7480081a52c624c0fa7 /rtemstoolkit
parent1a89c3d78900e8c4d60afcf9978aa2f283121320 (diff)
rtemstoolkit: Add libdwarf C++ interface.
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
Diffstat (limited to 'rtemstoolkit')
-rw-r--r--rtemstoolkit/rld-dwarf-types.h57
-rw-r--r--rtemstoolkit/rld-dwarf.cpp938
-rw-r--r--rtemstoolkit/rld-dwarf.h469
-rw-r--r--rtemstoolkit/rld-elf.cpp16
-rw-r--r--rtemstoolkit/rld-elf.h12
-rw-r--r--rtemstoolkit/wscript5
6 files changed, 1495 insertions, 2 deletions
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@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@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 &die;
+ }
+
+ 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@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),
@@ -438,6 +439,18 @@ namespace rld
}
void
+ file::reference_obtain ()
+ {
+ ++refs;
+ }
+
+ void
+ file::reference_release ()
+ {
+ --refs;
+ }
+
+ void
file::begin (const std::string& name__, int fd__, const bool writable_)
{
begin (name__, fd__, writable_, 0, 0);
@@ -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',