From d8eef0a463d4998d221ff318dd952dab18e5dfbf Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Fri, 11 May 2018 06:12:27 +1200 Subject: rtemstoolkit: Add DWARF function support. Load the functions in each CU. --- rtemstoolkit/defaults.mc | 122 ++++ rtemstoolkit/elftoolchain/libdwarf/dwarf_die.c | 5 +- rtemstoolkit/rld-dwarf-types.h | 37 +- rtemstoolkit/rld-dwarf.cpp | 926 ++++++++++++++++++++++++- rtemstoolkit/rld-dwarf.h | 292 +++++++- 5 files changed, 1344 insertions(+), 38 deletions(-) create mode 100644 rtemstoolkit/defaults.mc (limited to 'rtemstoolkit') diff --git a/rtemstoolkit/defaults.mc b/rtemstoolkit/defaults.mc new file mode 100644 index 0000000..36996c3 --- /dev/null +++ b/rtemstoolkit/defaults.mc @@ -0,0 +1,122 @@ +# +# RTEMS Tools Project (http://www.rtems.org/) +# Copyright 2010-2015 Chris Johns (chrisj@rtems.org) +# All rights reserved. +# +# This file is part of the RTEMS Tools package in 'rtems-tools'. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +# +# All paths in defaults must be Unix format. Do not store any Windows format +# paths in the defaults. +# +# Every entry must describe the type of checking a host must pass. +# +# Records: +# key: type, attribute, value +# type : none, dir, exe, triplet +# attribute: none, required, optional +# value : 'single line', '''multi line''' +# + +# +# Global defaults +# +[global] + +# Nothing +nil: none, none, '' + +# Paths +_topdir: dir, required, '%{_prefix}' +_docdir: dir, none, '%{_defaultdocdir}' +_tmppath: dir, none, '%{_topdir}/build/tmp' +_tmproot: dir, none, '%{_tmppath}/rt/%{_bset}' +_datadir: dir, none, '%{_prefix}/share' +_defaultdocdir: dir, none, '%{_prefix}/share/doc' +_exeext: none, none, '' +_exec_prefix: dir, none, '%{_prefix}' +_bindir: dir, none, '%{_exec_prefix}/bin' +_sbindir: dir, none, '%{_exec_prefix}/sbin' +_libexecdir: dir, none, '%{_exec_prefix}/libexec' +_datarootdir: dir, none, '%{_prefix}/share' +_datadir: dir, none, '%{_datarootdir}' +_sysconfdir: dir, none, '%{_prefix}/etc' +_sharedstatedir: dir, none, '%{_prefix}/com' +_localstatedir: dir, none, '%{prefix}/var' +_includedir: dir, none, '%{_prefix}/include' +_lib: dir, none, 'lib' +_libdir: dir, none, '%{_exec_prefix}/%{_lib}' +_libexecdir: dir, none, '%{_exec_prefix}/libexec' +_mandir: dir, none, '%{_datarootdir}/man' +_infodir: dir, none, '%{_datarootdir}/info' +_localedir: dir, none, '%{_datarootdir}/locale' +_localedir: dir, none, '%{_datadir}/locale' +_localstatedir: dir, none, '%{_prefix}/var' +_usr: dir, none, '/usr/local' +_usrsrc: dir, none, '%{_usr}/src' +_var: dir, none, '/usr/local/var' +_varrun: dir, none, '%{_var}/run' + +# Defaults, override in platform specific modules. +__arch_install_post: exe, none, '%{nil}' +__bash: exe, optional, '/bin/bash' +__bzip2: exe, required, '/usr/bin/bzip2' +__cat: exe, required, '/bin/cat' +__chgrp: exe, required, '/usr/bin/chgrp' +__chmod: exe, required, '/bin/chmod' +__chown: exe, required, '/usr/sbin/chown' +__cp: exe, required, '/bin/cp' +__git: exe, required, '/usr/bin/git' +__grep: exe, required, '/usr/bin/grep' +__gzip: exe, required, '/usr/bin/gzip' +__id: exe, required, '/usr/bin/id' +__id_u: exe, none, '%{__id} -u' +__ln_s: exe, none, 'ln -s' +__make: exe, required, 'make' +__mkdir: exe, required, '/bin/mkdir' +__mkdir_p: exe, none, '/bin/mkdir -p' +__mv: exe, required, '/bin/mv' +__patch_bin: exe, required, '/usr/bin/patch' +__patch_opts: none, none, '%{nil}' +__patch: exe, none, '%{__patch_bin} %{__patch_opts}' +__svn: exe, optional, '/usr/bin/svn' +__rm: exe, required, '/bin/rm' +__rmfile: exe, none, '%{__rm} -f' +__rmdir: exe, none, '%{__rm} -rf' +__sed: exe, required, '/usr/bin/sed' +__sh: exe, required, '/bin/sh' +__tar: exe, required, '/usr/bin/tar' +__tar_extract: exe, none, '%{__tar} -xvvf' +__touch: exe, required, '/usr/bin/touch' +__unzip: exe, required, '/usr/bin/unzip' +__xz: exe, required, '/usr/bin/xz' + +# Default settings +_target: none, none, '%{nil}' + +# Paths +_rtbase: none, none, '%{_rtdir}' +_configdir: none, none, '%{_rtbase}/config:%{_rtbase}' diff --git a/rtemstoolkit/elftoolchain/libdwarf/dwarf_die.c b/rtemstoolkit/elftoolchain/libdwarf/dwarf_die.c index de6351a..4b46d85 100644 --- a/rtemstoolkit/elftoolchain/libdwarf/dwarf_die.c +++ b/rtemstoolkit/elftoolchain/libdwarf/dwarf_die.c @@ -78,7 +78,10 @@ dwarf_siblingof_b(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Die *ret_die, } ds = is_info ? dbg->dbg_info_sec : dbg->dbg_types_sec; - cu = is_info ? dbg->dbg_cu_current : dbg->dbg_tu_current; + if (die != NULL && die->die_cu != NULL) + cu = die->die_cu; + else + cu = is_info ? dbg->dbg_cu_current : dbg->dbg_tu_current; if (cu == NULL) { DWARF_SET_ERROR(dbg, error, DW_DLE_DIE_NO_CU_CONTEXT); diff --git a/rtemstoolkit/rld-dwarf-types.h b/rtemstoolkit/rld-dwarf-types.h index 4a11639..410011c 100644 --- a/rtemstoolkit/rld-dwarf-types.h +++ b/rtemstoolkit/rld-dwarf-types.h @@ -35,22 +35,27 @@ namespace rld /** * 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; + 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_Attribute dwarf_attribute; + typedef ::Dwarf_Half dwarf_attr; + typedef ::Dwarf_Ranges dwarf_ranges; + typedef enum ::Dwarf_Ranges_Entry_Type dwarf_ranges_type; + typedef enum ::Dwarf_Form_Class dwarf_form_class; + typedef ::Dwarf_Type dwarf_type; } } diff --git a/rtemstoolkit/rld-dwarf.cpp b/rtemstoolkit/rld-dwarf.cpp index 674ac8f..f06bbad 100644 --- a/rtemstoolkit/rld-dwarf.cpp +++ b/rtemstoolkit/rld-dwarf.cpp @@ -37,6 +37,39 @@ namespace rld { namespace dwarf { + typedef std::vector < dwarf_die > dies_active; + dies_active active_dies; + + bool active_dies_present (dwarf_die die) + { + return std::find (active_dies.begin(), active_dies.end(), die) != active_dies.end(); + } + + void dies_active_add (dwarf_die die) + { + if (active_dies_present (die)) + { + std::cout << "DDdd : dup : " << die << std::endl; + } + else + { + active_dies.push_back (die); + } + } + + void dies_active_remove (dwarf_die die) + { + dies_active::iterator di = std::find (active_dies.begin(), active_dies.end(), die); + if (di == active_dies.end ()) + { + std::cout << "DDdd : no found : " << die << std::endl; + } + else + { + active_dies.erase (di); + } + } + /** * The libdwarf error. */ @@ -201,6 +234,230 @@ namespace rld return addr < rhs.addr; } + range::range (const dwarf_ranges* range) + : range_ (range) + { + } + + range::range (const range& orig) + : range_ (orig.range_) + { + } + + range::~range () + { + } + + dwarf_unsigned + range::addr1 () const + { + if (range_ == nullptr) + throw rld::error ("No valid range", "rld:dwarf:range:addr1"); + return range_->dwr_addr1; + } + + dwarf_unsigned + range::addr2 () const + { + if (range_ == nullptr) + throw rld::error ("No valid range", "rld:dwarf:range:addr2"); + return range_->dwr_addr2; + } + + dwarf_ranges_type + range::type () const + { + if (range_ == nullptr) + throw rld::error ("No valid range", "rld:dwarf:range:type"); + return range_->dwr_type; + } + + bool + range::empty () const + { + /** + * See DWARF 2.17.3. + * + * A bounded range entry whose beginning and ending address offsets are + * equal (including zero) indicates an empty range and may be ignored. + */ + return type () == DW_RANGES_ENTRY && addr1 () == addr2 (); + } + + bool + range::end () const + { + return type () == DW_RANGES_END; + } + + range& + range::operator = (const range& rhs) + { + if (this != &rhs) + range_ = rhs.range_; + return *this; + } + + void + range::dump () + { + dwarf_ranges_type type_ = type (); + const char* type_s = "invalid"; + const char* type_labels[] = { + "BOUNDED", + "BASE", + "END" + }; + if (type_ <= DW_RANGES_END) + type_s = type_labels[type_]; + std::cout << type_s << '-' + << std::hex << std::setfill ('0') + << "0x" << std::setw (8) << addr1 () + << ":0x" << std::setw (8) << addr2 (); + } + + address_ranges::address_ranges (file& debug) + : debug (debug), + offset (-1), + dranges (nullptr), + dranges_count (0) + { + } + + address_ranges::address_ranges (debug_info_entry& die) + : debug (die.get_debug ()), + offset (-1), + dranges (nullptr), + dranges_count (0) + { + load (die); + } + + address_ranges::address_ranges (file& debug, dwarf_offset offset) + : debug (debug), + offset (offset), + dranges (nullptr), + dranges_count (0) + { + load (offset); + } + + address_ranges::address_ranges (const address_ranges& orig) + : debug (orig.debug), + offset (orig.offset) + { + load (orig.offset); + } + + address_ranges::~address_ranges () + { + if (dranges != nullptr) + { + ::dwarf_ranges_dealloc (debug, dranges, dranges_count); + dranges = nullptr; + dranges_count = 0; + ranges_.clear (); + } + } + + bool + address_ranges::load (debug_info_entry& die, bool error) + { + dwarf_attribute attr; + dwarf_error de; + int dr; + dr = ::dwarf_attr (die, DW_AT_ranges, &attr, &de); + if (dr != DW_DLV_OK) + { + if (!error) + return false; + libdwarf_error_check ("rld:dwarf::address_ranges:load", dr, de); + } + dr = ::dwarf_global_formref (attr, &offset, &de); + if (dr != DW_DLV_OK) + { + if (!error) + return false; + libdwarf_error_check ("rld:dwarf::address_ranges:load", dr, de); + } + load (offset); + return true; + } + + bool + address_ranges::load (dwarf_offset offset_, bool error) + { + if (offset_ > 0) + { + if (dranges != nullptr) + ::dwarf_ranges_dealloc (debug, dranges, dranges_count); + + dranges = nullptr; + dranges_count = 0; + + offset = offset_; + + dwarf_error de; + int dr; + + dr = ::dwarf_get_ranges (debug, offset, + &dranges, &dranges_count, nullptr, &de); + if (dr != DW_DLV_OK) + { + if (!error) + return false; + libdwarf_error_check ("rld:dwarf::ranges:load", dr, de); + } + + if (dranges != nullptr && dranges_count > 0) + { + for (dwarf_signed r = 0; r < dranges_count; ++r) + ranges_.push_back (range (&dranges[r])); + } + } + + return true; + } + + const ranges& + address_ranges::get () const + { + return ranges_; + } + + bool + address_ranges::empty () const + { + return ranges_.empty (); + } + + address_ranges& + address_ranges::operator = (const address_ranges& rhs) + { + if (this != &rhs) + { + if (debug != rhs.debug) + throw rld::error ("invalid debug", "address_ranges:="); + load (rhs.offset); + } + return *this; + } + + void + address_ranges::dump () + { + bool first = true; + std::cout << '['; + for (auto& r : ranges_) + { + if (!first) + std::cout << ','; + r.dump (); + first = false; + } + std::cout << ']'; + } + line_addresses::line_addresses (file& debug, debug_info_entry& die) : debug (debug), @@ -312,6 +569,223 @@ namespace rld return *this; } + + function::function (file& debug, debug_info_entry& die) + : debug (debug), + machine_code_ (false), + external_ (false), + declaration_ (false), + inline_ (DW_INL_not_inlined), + pc_low_ (0), + pc_high_ (0), + ranges_ (debug) + { + dwarf_bool db; + + if (die.attribute (DW_AT_external, db, false)) + external_ = db ? true : false; + + if (die.attribute (DW_AT_declaration, db, false)) + declaration_ = db ? true : false; + + die.attribute (DW_AT_linkage_name, linkage_name_, false); + + if (!die.attribute (DW_AT_inline, inline_, false)) + inline_ = DW_INL_not_inlined; + + if (inline_ == DW_INL_declared_inlined) + { + die_dump_children (die, " +"); + } + + /* + * If ranges are not found see if the PC low and PC high attributes + * can be found. + */ + ranges_.load (die, false); + if (ranges_.empty ()) + { + bool is_address; + if (die.get_lowpc (pc_low_) && die.get_highpc (pc_high_, is_address)) + { + machine_code_ = true; + if (!is_address) + pc_high_ += pc_low_; + } + } + else + { + for (auto& r : ranges_.get ()) + { + if (!r.end () && !r.empty ()) + { + machine_code_ = true; + break; + } + } + } + + if (declaration_) + { + die.attribute (DW_AT_name, name_); + } + else + { + /* + * Get the name attribute. (if present) + */ + if (!die.attribute (DW_AT_name, name_, false)) + { + bool found = false; + + /* + * For inlined function, the actual name is probably in the DIE + * referenced by DW_AT_abstract_origin. (if present) + */ + dwarf_attribute abst_at; + if (die.attribute (DW_AT_abstract_origin, abst_at, false)) + { + dwarf_offset abst_at_die_offset; + dwarf_error de; + int dr; + dr = ::dwarf_global_formref (abst_at, &abst_at_die_offset, &de); + if (dr == DW_DLV_OK) + { + debug_info_entry abst_at_die (debug, abst_at_die_offset); + if (abst_at_die.attribute (DW_AT_name, name_, false)) + found = true; + } + } + + /* + * If DW_AT_name is not present, but DW_AT_specification is present, + * then probably the actual name is in the DIE referenced by + * DW_AT_specification. + */ + if (!found) + { + dwarf_attribute spec; + if (die.attribute (DW_AT_specification, spec, false)) + { + dwarf_offset spec_die_offset; + dwarf_error de; + int dr; + dr = ::dwarf_global_formref (abst_at, &spec_die_offset, &de); + if (dr == DW_DLV_OK) + { + debug_info_entry spec_die (debug, spec_die_offset); + if (spec_die.attribute (DW_AT_name, name_, false)) + found = true; + } + } + } + } + + if (die.tag () == DW_TAG_inlined_subroutine) + { + die.attribute (DW_AT_call_file, call_file_, false); + } + } + + if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) + { + std::cout << "dwarf::function: "; + if (name_.empty ()) + std::cout << "NO NAME"; + else + std::cout << name_; + if (!has_machine_code ()) + std::cout << " NO MACHINE CODE"; + else + std::cout << std::hex << std::setfill ('0') + << " pc_low = 0x" << std::setw (8) << pc_low () + << " pc_high = 0x" << std::setw (8) << pc_high () + << std::dec << std::setfill (' '); + std::cout << std::endl; + } + } + + function::~function () + { + } + + std::string + function::name () const + { + return name_; + } + + const address_ranges& + function::get_ranges () const + { + return ranges_; + } + + dwarf_unsigned + function::pc_low () const + { + if (ranges_.empty ()) + return pc_low_; + dwarf_address addr = ~0; + for (auto& r : ranges_.get ()) + { + if (!r.end () && !r.empty () && r.addr1 () < addr) + addr = r.addr1 (); + } + return addr; + } + + dwarf_unsigned + function::pc_high () const + { + if (ranges_.empty ()) + return pc_high_; + dwarf_address addr = 0; + for (auto& r : ranges_.get ()) + { + if (!r.end () && !r.empty () && r.addr2 () > addr) + addr = r.addr1 (); + } + return addr; + } + + bool + function::has_machine_code () const + { + return machine_code_; + } + + bool + function::is_external () const + { + return external_; + } + + bool + function::is_declaration () const + { + return declaration_; + } + + bool + function::is_inlined () const + { + return inline_ == DW_INL_declared_inlined; + } + + std::string + function::call_file () const + { + return call_file_; + } + + bool + function::inside (dwarf_address addr) const + { + return !name_.empty () && has_machine_code () && + addr >= pc_low () && addr <= pc_high (); + } + debug_info_entry::debug_info_entry (file& debug) : debug (debug), die (nullptr), @@ -334,10 +808,29 @@ namespace rld tag_ (0), offset_ (offset__) { + dwarf_die ddie; + dwarf_error de; + int dr; + dr = ::dwarf_offdie (debug, offset_, &ddie, &de); + libdwarf_error_check ("debug_info_entry:debug_info_entry", dr, de); + die = ddie; + } + + debug_info_entry::debug_info_entry (const debug_info_entry& orig) + : debug (orig.debug), + die (nullptr), + tag_ (orig.tag_), + offset_ (orig.offset_) + { + if (offset_ != 0) + { + dwarf_die ddie; dwarf_error de; int dr; - dr = ::dwarf_offdie (debug, offset_, &die, &de); + dr = ::dwarf_offdie (debug, offset_, &ddie, &de); libdwarf_error_check ("debug_info_entry:debug_info_entry", dr, de); + die = ddie; + } } debug_info_entry::~debug_info_entry () @@ -378,6 +871,24 @@ namespace rld return *this; } + debug_info_entry& + debug_info_entry::operator = (dwarf_offset offset__) + { + dealloc (); + if (offset__ != 0) + { + dwarf_die ddie; + dwarf_error de; + int dr; + offset_ = offset__; + tag_ = 0; + dr = ::dwarf_offdie (debug, offset_, &ddie, &de); + libdwarf_error_check ("debug_info_entry:operator=", dr, de); + die = ddie; + } + return *this; + } + bool debug_info_entry::operator == (debug_info_entry& rhs) const { @@ -398,7 +909,7 @@ namespace rld { dwarf_error de; int dr; - dr = ::dwarf_tag(die, &tag_, &de); + dr = ::dwarf_tag (die, &tag_, &de); libdwarf_error_check ("debug_info_entry:debug_info_entry", dr, de); } return tag_; @@ -417,6 +928,59 @@ namespace rld return offset_; } + bool + debug_info_entry::get_lowpc (dwarf_address& addr, bool error) const + { + dwarf_error de; + int dr; + dr = ::dwarf_lowpc (die, &addr, &de); + if (error) + libdwarf_error_check ("debug_info_entry:lowpc", dr, de); + return dr == DW_DLV_OK; + } + + bool + debug_info_entry::get_highpc (dwarf_address& addr, + bool& is_address, + bool error) const + { + dwarf_half form; + dwarf_form_class class_; + dwarf_error de; + int dr; + dr = ::dwarf_highpc_b (die, &addr, &form, &class_, &de); + if (error) + libdwarf_error_check ("debug_info_entry:highpc", dr, de); + is_address = class_ == DW_FORM_CLASS_ADDRESS; + return dr == DW_DLV_OK; + } + + bool + debug_info_entry::attribute (dwarf_attr attr, + dwarf_attribute& value, + bool error) const + { + dwarf_error de; + int dr; + dr = ::dwarf_attr (die, attr, &value, &de); + if (error) + libdwarf_error_check ("debug_info_entry:attribute(attr)", dr, de); + return dr == DW_DLV_OK; + } + + bool + debug_info_entry::attribute (dwarf_attr attr, + dwarf_bool& value, + bool error) const + { + dwarf_error de; + int dr; + dr = ::dwarf_attrval_flag (die, attr, &value, &de); + if (error) + libdwarf_error_check ("debug_info_entry:attribute(flag)", dr, de); + return dr == DW_DLV_OK; + } + bool debug_info_entry::attribute (dwarf_attr attr, dwarf_unsigned& value, @@ -426,7 +990,7 @@ namespace rld int dr; dr = ::dwarf_attrval_unsigned (die, attr, &value, &de); if (error) - libdwarf_error_check ("debug_info_entry:attribute ", dr, de); + libdwarf_error_check ("debug_info_entry:attribute(unsigned)", dr, de); return dr == DW_DLV_OK; } @@ -441,7 +1005,7 @@ namespace rld value.clear (); dr = ::dwarf_attrval_string (die, attr, &s, &de); if (error) - libdwarf_error_check ("debug_info_entry:attribute ", dr, de); + libdwarf_error_check ("debug_info_entry:attribute(string)", dr, de); if (s != nullptr) value = s; return dr == DW_DLV_OK; @@ -473,6 +1037,50 @@ namespace rld libdwarf_error_check ("debug_info_entry:source_files ", dr, de); } + bool + debug_info_entry::ranges (dwarf_ranges*& ranges, + dwarf_signed& rangescount) const + { + dwarf_unsigned ranges_off; + if (attribute (DW_AT_ranges, ranges_off, false)) + { + dwarf_error de; + int dr; + dr = ::dwarf_get_ranges (debug, ranges_off, + &ranges, &rangescount, nullptr, &de); + libdwarf_error_check ("debug_info_entry:ranges ", dr, de); + return ranges != nullptr && rangescount > 0; + } + return false; + } + + bool + debug_info_entry::get_child (debug_info_entry& child_die) + { + dwarf_error de; + int dr; + dr = ::dwarf_child (die, child_die, &de); + return dr == DW_DLV_OK; + } + + bool + debug_info_entry::get_sibling (debug_info_entry& sibling_die) + { + dwarf_error de; + int dr; + dr = ::dwarf_siblingof (debug, die, sibling_die, &de); + if (dr == DW_DLV_NO_ENTRY) + return false; + libdwarf_error_check ("compilation_unit::sibling", dr, de); + return true; + } + + file& + debug_info_entry::get_debug () + { + return debug; + } + void debug_info_entry::dealloc () { @@ -482,6 +1090,164 @@ namespace rld } } + void + debug_info_entry::dump (std::string prefix, bool newline) + { + const char* s; + ::dwarf_get_TAG_name (tag (), &s); + std::cout << prefix << s << std::endl; + + dwarf_attribute* attributes; + dwarf_signed attr_count; + dwarf_error de; + int dr; + + dr = ::dwarf_attrlist (die, &attributes, &attr_count, &de); + if (dr == DW_DLV_OK) + { + for (int a = 0; a < attr_count; ++a) + { + dwarf_attr attr; + dr = ::dwarf_whatattr (attributes[a], &attr, &de); + libdwarf_error_check ("debug_info_entry::dump", dr, de); + dwarf_half form; + dr = ::dwarf_whatform (attributes[a], &form, &de); + libdwarf_error_check ("debug_info_entry::dump", dr, de); + const char* f; + dwarf_get_FORM_name (form, &f); + dwarf_get_AT_name (attr, &s); + if (a > 0) + std::cout << std::endl; + std::cout << prefix << " - " << s << " (" << attr << ") [" << f << ']'; + debug_info_entry v_die (debug); + address_ranges v_ranges (debug); + dwarf_unsigned v_unsigned; + dwarf_bool v_bool; + dwarf_offset v_offset; + switch (form) + { + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + break; + case DW_FORM_addr: + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_data8: + case DW_FORM_udata: + dr = ::dwarf_attrval_unsigned (die, attr, &v_unsigned, &de); + libdwarf_error_check ("debug_info_entry::dump", dr, de); + std::cout << " : " + << std::hex << std::setfill ('0') + << std::setw (8) << v_unsigned + << std::dec << std::setfill (' ') + << " (" << v_unsigned << ')'; + break; + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + dr = ::dwarf_global_formref (attributes[a], &v_offset, &de); + libdwarf_error_check ("debug_info_entry::dump", dr, de); + std::cout << " : " + << std::hex << std::setfill ('0') + << std::setw (8) << v_offset + << std::dec << std::setfill (' ') + << " (" << v_offset << ')'; + switch (attr) + { + case DW_AT_abstract_origin: + case DW_AT_specification: + v_die = v_offset; + std::cout << std::endl; + v_die.dump (' ' + prefix, false); + break; + default: + break; + } + break; + case DW_FORM_exprloc: + break; + case DW_FORM_flag: + case DW_FORM_flag_present: + dr = ::dwarf_attrval_flag (die, attr, &v_bool, &de); + libdwarf_error_check ("debug_info_entry::dump", dr, de); + std::cout << " : " << v_bool; + break; + break; + case DW_FORM_string: + case DW_FORM_strp: + dr = ::dwarf_attrval_string (die, attr, &s, &de); + libdwarf_error_check ("debug_info_entry::dump", dr, de); + std::cout << " : " << s; + break; + case DW_FORM_sec_offset: + switch (attr) + { + case DW_AT_ranges: + dr = ::dwarf_global_formref (attributes[a], &v_offset, &de); + libdwarf_error_check ("debug_info_entry::dump", dr, de); + std::cout << ' '; + v_ranges.load (v_offset); + v_ranges.dump (); + break; + default: + break; + } + break; + case DW_FORM_indirect: + case DW_FORM_ref_addr: + case DW_FORM_ref_sig8: + case DW_FORM_sdata: + break; + } + } + if (newline) + std::cout << std::endl; + } + } + + void + die_dump_children (debug_info_entry die, + std::string prefix, + int nesting, + int depth) + { + debug_info_entry child (die.get_debug ()); + if (die.get_child (child)) + die_dump (child, prefix, nesting, depth); + } + + void + die_dump (debug_info_entry die, + std::string prefix, + int nesting, + int depth) + { + ++nesting; + + for (int n = 0; n < nesting; ++n) + prefix += ' '; + + while (true) + { + die.dump (prefix); + + if (depth < 0 || nesting < depth) + die_dump_children (die, prefix); + + debug_info_entry next (die.get_debug ()); + + if (!die.get_sibling (next)) + break; + + die = next; + } + } + compilation_unit::compilation_unit (file& debug, debug_info_entry& die, dwarf_unsigned offset) @@ -489,6 +1255,7 @@ namespace rld offset_ (offset), pc_low_ (0), pc_high_ (0), + ranges_ (debug), die_offset (die.offset ()), source_ (debug, die_offset) { @@ -497,13 +1264,35 @@ namespace rld die.attribute (DW_AT_producer, producer_); - die.attribute (DW_AT_low_pc, pc_low_, false); + ranges_.load (die, false); - if (!die.attribute (DW_AT_high_pc, pc_high_, false)) - pc_high_ = ~0U; - - if (pc_high_ < pc_low_) - pc_high_ += pc_low_; + if (ranges_.empty ()) + { + bool is_address; + die.get_lowpc (pc_low_); + if (die.get_highpc (pc_high_, is_address)) + { + if (!is_address) + pc_high_ += pc_low_; + } + else + pc_high_ = ~0U; + } + else + { + pc_low_ = ~0U; + for (auto& r : ranges_.get ()) + { + if (!r.end () && !r.empty () && r.addr1 () < pc_low_) + pc_low_ = r.addr1 (); + } + pc_high_ = 0U; + for (auto& r : ranges_.get ()) + { + if (!r.end () && !r.empty () && r.addr2 () > pc_high_) + pc_high_ = r.addr2 (); + } + } if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG) { @@ -512,8 +1301,8 @@ namespace rld << 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 << "pc_low = 0x" << std::setw (8) << pc_low_ + << " pc_high = 0x" << std::setw (8) << pc_high_; std::cout << std::setfill (' ') << std::dec << std::endl << " ] " << producer_ @@ -610,6 +1399,7 @@ namespace rld producer_ (orig.producer_), pc_low_ (orig.pc_low_), pc_high_ (orig.pc_high_), + ranges_ (orig.ranges_), die_offset (orig.die_offset), source_ (debug, die_offset) { @@ -622,6 +1412,58 @@ namespace rld { } + void + compilation_unit::load_types () + { + //dump_die (); + } + + void + compilation_unit::load_functions () + { + debug_info_entry die (debug, die_offset); + debug_info_entry ret_die (debug); + dwarf_error de; + int dr; + dr = ::dwarf_child(die, ret_die, &de); + if (dr == DW_DLV_OK) + load_functions (ret_die); + } + + void + compilation_unit::load_functions (debug_info_entry& die) + { + while (true) + { + if (die.tag () == DW_TAG_subprogram || + die.tag () == DW_TAG_entry_point || + die.tag () == DW_TAG_inlined_subroutine) + { + function func (debug, die); + if (func.has_machine_code () && + func.pc_low () >= pc_low_ && func.pc_high () <= pc_high_) + { + functions_.push_back (func); + } + } + + debug_info_entry ret_die (debug); + dwarf_error de; + int dr; + + dr = ::dwarf_child(die, ret_die, &de); + if (dr == DW_DLV_OK) + load_functions (ret_die); + + dr = ::dwarf_siblingof (debug, die, ret_die, &de); + if (dr == DW_DLV_NO_ENTRY) + break; + libdwarf_error_check ("compilation_unit:load_functions", dr, de); + + die = ret_die; + } + } + std::string compilation_unit::name () const { @@ -669,15 +1511,15 @@ namespace rld return false; } + functions& + compilation_unit::get_functions () + { + return functions_; + } + bool compilation_unit::inside (dwarf_unsigned addr) const { - if (!addr_lines_.empty ()) - { - auto first = addr_lines_.begin (); - auto last = addr_lines_.end () - 1; - return first->location () <= addr && addr <= last->location (); - } return addr >= pc_low_ && addr < pc_high_; } @@ -700,11 +1542,20 @@ namespace rld addr_lines_.push_back (address (line, source_)); pc_low_ = rhs.pc_low_; pc_high_ = rhs.pc_high_; + ranges_ = rhs.ranges_; die_offset = rhs.die_offset; } return *this; } + void + compilation_unit::dump_die () + { + debug_info_entry die (debug, die_offset); + std::cout << "CU @ 0x" << std::hex << offset_ << std::dec << std::endl; + die_dump_children (die, ""); + } + source_flags::source_flags (const std::string& source) : source (source) { @@ -736,7 +1587,7 @@ namespace rld file::file () : debug (nullptr), elf_ (nullptr) - { + { } file::~file () @@ -830,7 +1681,7 @@ namespace rld break; /* - * Fnd the CU DIE. + * Find the CU DIE. */ debug_info_entry die (*this); debug_info_entry ret_die (*this); @@ -854,6 +1705,20 @@ namespace rld } } + void + file::load_types () + { + for (auto& cu : cus) + cu.load_types (); + } + + void + file::load_functions () + { + for (auto& cu : cus) + cu.load_functions (); + } + bool file::get_source (const unsigned int addr, std::string& source_file, @@ -898,6 +1763,27 @@ namespace rld return r; } + bool + file::get_function (const unsigned int addr, + std::string& name) + { + name = "unknown"; + + for (auto& cu : cus) + { + for (auto& func : cu.get_functions ()) + { + if (func.inside (addr)) + { + name = func.name (); + return true; + } + } + } + + return false; + } + void file::get_producer_sources (producer_sources& producers) { diff --git a/rtemstoolkit/rld-dwarf.h b/rtemstoolkit/rld-dwarf.h index 1207728..cfca9a6 100644 --- a/rtemstoolkit/rld-dwarf.h +++ b/rtemstoolkit/rld-dwarf.h @@ -113,6 +113,105 @@ namespace rld */ typedef std::vector < address > addresses; + /** + * Range, one entry in an address range container. + */ + class range + { + public: + range (const dwarf_ranges* range); + range (const range& orig); + ~range (); + + /** + * Address 1 in the range. + */ + dwarf_unsigned addr1 () const; + dwarf_unsigned addr2 () const; + + /** + * Get the type of range. + */ + dwarf_ranges_type type () const; + + /** + * Is the range the end? + */ + bool end () const; + + /** + * Is the range empty? See DWARF 2.17.3. + */ + bool empty () const; + + /** + * Assigment operator. + */ + range& operator = (const range& rhs); + + /** + * Dump the range. + */ + void dump (); + + private: + + const dwarf_ranges* range_; + }; + + typedef std::vector < range > ranges; + + /** + * Address ranges, is a range of addresses. + */ + class address_ranges + { + public: + address_ranges (file& debug); + address_ranges (debug_info_entry& die); + address_ranges (file& debug, dwarf_offset offset); + address_ranges (const address_ranges& orig); + ~address_ranges (); + + /** + * Load the ranges from the DIE. + */ + bool load (debug_info_entry& die, bool error = true); + + /** + * Load the ranges from the debug info. + */ + bool load (dwarf_offset offset, bool error = true); + + /** + * Get the container. + */ + const ranges& get () const; + + /** + * Address range empty? + */ + bool empty () const; + + /** + * Assigment operator. + */ + address_ranges& operator = (const address_ranges& rhs); + + /** + * Dump the address ranges. + */ + void dump (); + + private: + + file& debug; + dwarf_offset offset; + dwarf_ranges* dranges; + dwarf_signed dranges_count; + ranges ranges_; + }; + /** * Line addresses. */ @@ -150,7 +249,6 @@ namespace rld public: sources (file& debug, dwarf_offset die_offset); sources (const sources& orig); - //sources (sources&& orig); ~sources (); /** @@ -176,6 +274,88 @@ namespace rld dwarf_offset die_offset; }; + /** + * Function. + */ + class function + { + public: + function (file& debug, debug_info_entry& die); + ~function (); + + /** + * Get the name of the function. + */ + std::string name () const; + + /** + * Get the linkage name of the function. + */ + std::string linkage_name () const; + + /** + * Get the ranges for the funcion, if empty the PC low and PC high values + * will be valid. + */ + const address_ranges& get_ranges () const; + + /** + * Get the PC low address, valid if ranges is empty. + */ + dwarf_unsigned pc_low () const; + + /** + * Get the PC high address, valid if ranges is empty. + */ + dwarf_unsigned pc_high () const; + + /** + * Does the function have machine code in the image? + */ + bool has_machine_code () const; + + /** + * Is the function external? + */ + bool is_external () const; + + /** + * Is this just a declaration? + */ + bool is_declaration () const; + + /** + * Is the function inlined? + */ + bool is_inlined () const; + + /** + * Get the call file of the inlined function. + */ + std::string call_file () const; + + /** + * Is the address inside the function. + */ + bool inside (dwarf_address addr) const; + + private: + + file& debug; + bool machine_code_; + bool external_; + bool declaration_; + dwarf_unsigned inline_; + dwarf_unsigned pc_low_; + dwarf_unsigned pc_high_; + address_ranges ranges_; + std::string name_; + std::string linkage_name_; + std::string call_file_; + }; + + typedef std::vector < function > functions; + /** * Debug Information Element (DIE). * @@ -190,6 +370,7 @@ namespace rld debug_info_entry (file& debug); debug_info_entry (file& debug, dwarf_die& die); debug_info_entry (file& debug, dwarf_offset offset); + debug_info_entry (const debug_info_entry& orig); /** * Destruct and clean up. @@ -211,6 +392,7 @@ namespace rld * Assignment operators. */ debug_info_entry& operator = (debug_info_entry& rhs); + debug_info_entry& operator = (dwarf_offset offset); /** * Compare operators. @@ -228,6 +410,32 @@ namespace rld */ dwarf_offset offset (); + /** + * Get the low PC. + */ + bool get_lowpc (dwarf_address& addr, bool error = false) const; + + /** + * Get the high PC. + */ + bool get_highpc (dwarf_address& addr, + bool& is_address, + bool error = false) const; + + /** + * Get an attribute. + */ + bool attribute (dwarf_attr attr, + dwarf_attribute& value, + bool error = true) const; + + /** + * Get a flag. + */ + bool attribute (dwarf_attr attr, + dwarf_bool& value, + bool error = true) const; + /** * Get an unsigned attribute. */ @@ -256,11 +464,36 @@ namespace rld void source_files (char**& source, dwarf_signed& sourcecount) const; + /** + * Get the ranges. + */ + bool ranges (dwarf_ranges*& ranges, dwarf_signed& rangescount) const; + + /** + * Get the child. + */ + bool get_child (debug_info_entry& child_die); + + /** + * Get the silbing + */ + bool get_sibling (debug_info_entry& sibling_die); + + /** + * Get the debug info for this DIE. + */ + file& get_debug (); + /** * deallocate the DIE. */ void dealloc (); + /** + * Dump this DIE. + */ + void dump (std::string prefix, bool newline = true); + private: file& debug; @@ -270,6 +503,22 @@ namespace rld }; + /** + * Dump the DIE and all it's children and siblings. + */ + void die_dump_children (debug_info_entry die, + std::string prefix, + int nesting = 0, + int depth = -1); + + /** + * Dump the DIE and all it's children and siblings. + */ + void die_dump (debug_info_entry die, + std::string prefix, + int nesting = 0, + int depth = -1); + /** * Compilation Unit. */ @@ -282,6 +531,16 @@ namespace rld compilation_unit (const compilation_unit& orig); ~compilation_unit (); + /** + * Load the types. + */ + void load_types (); + + /** + * Load the functions. + */ + void load_functions (); + /** * Name of the CU. */ @@ -310,6 +569,11 @@ namespace rld bool get_source (const dwarf_address addr, address& addr_line); + /** + * Get the functions. + */ + functions& get_functions (); + /** * Is the address inside the CU? If the PC low and high attributes are * valid they are used or the lines are checked. @@ -321,19 +585,29 @@ namespace rld */ compilation_unit& operator = (const compilation_unit& rhs); + /** + * Output the DIE tree. + */ + void dump_die (); + private: + void load_functions (debug_info_entry& die); + 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. + address_ranges ranges_; ///< Non-continous address range. dwarf_offset die_offset; ///< The offset of the DIE in the image. sources source_; ///< Sources table for this CU. addresses addr_lines_; ///< Address table. + + functions functions_; ///< The functions in the CU. }; typedef std::list < compilation_unit > compilation_units; @@ -411,6 +685,16 @@ namespace rld */ void load_debug (); + /** + * Load the DWARF type information. + */ + void load_types (); + + /** + * Load the DWARF functions information. + */ + void load_functions (); + /** * Get the source location given an address. */ @@ -418,6 +702,12 @@ namespace rld std::string& source_file, int& source_line); + /** + * Get the function given an address. + */ + bool get_function (const unsigned int address, + std::string& name); + /** * Get the producer sources from the compilation units. */ -- cgit v1.2.3