From 558cab8e4f0d1793e7be21cc118aed5b293c022a Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Tue, 8 May 2018 15:09:38 +1000 Subject: 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 --- 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 (limited to 'rtemstoolkit') 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 + * + * 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 +#include + +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 + * + * 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 + +#include +#include +#include +#include + +#include +#include +#include + +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 + * + * 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 +#include + +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', -- cgit v1.2.3