mirror of
https://git.rtems.org/rtems-tools/
synced 2025-05-14 07:19:34 +08:00
2138 lines
54 KiB
C++
2138 lines
54 KiB
C++
/*
|
|
* 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>
|
|
#include <rld-symbols.h>
|
|
|
|
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.
|
|
*/
|
|
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);
|
|
dwarf_unsigned src_line;
|
|
dr = ::dwarf_lineno(line, &src_line, &de);
|
|
libdwarf_error_check ("address::address", dr, de);
|
|
source_line = src_line;
|
|
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, const sources& source)
|
|
: addr (orig.addr),
|
|
source (&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 (const address& orig, dwarf_address addr)
|
|
: addr (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 (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;
|
|
source_line = -1;
|
|
}
|
|
|
|
bool
|
|
address::valid () const
|
|
{
|
|
return source != nullptr && 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 = (const 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;
|
|
}
|
|
|
|
bool
|
|
address::operator < (const address& rhs) const
|
|
{
|
|
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 (std::ostream& out) const
|
|
{
|
|
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_];
|
|
out << type_s << '-'
|
|
<< std::hex << std::setfill ('0')
|
|
<< "0x" << std::setw (8) << addr1 ()
|
|
<< ":0x" << std::setw (8) << addr2 ()
|
|
<< std::dec << std::setfill (' ');
|
|
}
|
|
|
|
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),
|
|
dranges (nullptr),
|
|
dranges_count (0)
|
|
{
|
|
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);
|
|
|
|
ranges_.clear ();
|
|
|
|
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 (std::ostream& out) const
|
|
{
|
|
bool first = true;
|
|
out << '[';
|
|
for (auto& r : ranges_)
|
|
{
|
|
if (!first)
|
|
out << ',';
|
|
r.dump (out);
|
|
first = false;
|
|
}
|
|
out << ']';
|
|
}
|
|
|
|
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 ()
|
|
{
|
|
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, dwarf_offset die_offset)
|
|
: debug (debug),
|
|
source (nullptr),
|
|
count (0),
|
|
die_offset (die_offset)
|
|
{
|
|
debug_info_entry die (debug, 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 ()
|
|
{
|
|
dealloc ();
|
|
}
|
|
|
|
std::string
|
|
sources::operator [] (const int index) const
|
|
{
|
|
if (index <= 0 || index > count)
|
|
return "unknown";
|
|
return std::string (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 resulting 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;
|
|
}
|
|
|
|
|
|
function::function (file& debug, debug_info_entry& die)
|
|
: debug (debug),
|
|
machine_code_ (false),
|
|
external_ (false),
|
|
declaration_ (false),
|
|
inline_ (DW_INL_not_inlined),
|
|
entry_pc_ (0),
|
|
has_entry_pc_ (false),
|
|
pc_low_ (0),
|
|
pc_high_ (0),
|
|
ranges_ (debug),
|
|
call_line_ (0)
|
|
{
|
|
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;
|
|
|
|
if (die.attribute (DW_AT_entry_pc, entry_pc_, false))
|
|
has_entry_pc_ = true;
|
|
|
|
die.attribute (DW_AT_linkage_name, linkage_name_, false);
|
|
die.attribute (DW_AT_call_file, decl_file_, false);
|
|
die.attribute (DW_AT_call_line, decl_line_, false);
|
|
die.attribute (DW_AT_call_file, call_file_, false);
|
|
die.attribute (DW_AT_call_line, call_line_, false);
|
|
|
|
if (!die.attribute (DW_AT_inline, inline_, false))
|
|
inline_ = DW_INL_not_inlined;
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
abst_at_die.attribute (DW_AT_inline, inline_, false);
|
|
if (abst_at_die.attribute (DW_AT_external, db, false))
|
|
external_ = db ? true : false;
|
|
if (abst_at_die.attribute (DW_AT_declaration, db, false))
|
|
declaration_ = db ? true : false;
|
|
abst_at_die.attribute (DW_AT_linkage_name, linkage_name_, false);
|
|
abst_at_die.attribute (DW_AT_decl_file, decl_file_, false);
|
|
abst_at_die.attribute (DW_AT_decl_line, decl_line_, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 (spec, &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 (spec_die.attribute (DW_AT_external, db, false))
|
|
external_ = db ? true : false;
|
|
if (spec_die.attribute (DW_AT_declaration, db, false))
|
|
declaration_ = db ? true : false;
|
|
spec_die.attribute (DW_AT_linkage_name, linkage_name_, false);
|
|
spec_die.attribute (DW_AT_decl_file, decl_file_, false);
|
|
spec_die.attribute (DW_AT_decl_line, decl_line_, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!linkage_name_.empty() && name_.empty())
|
|
rld::symbols::demangle_name(linkage_name_, name_);
|
|
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
{
|
|
std::cout << "dwarf::function: ";
|
|
dump (std::cout);
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
function::function (const function& orig)
|
|
: debug (orig.debug),
|
|
machine_code_ (orig.machine_code_),
|
|
external_ (orig.external_),
|
|
declaration_ (orig.declaration_),
|
|
inline_ (orig.inline_),
|
|
entry_pc_ (orig.entry_pc_),
|
|
has_entry_pc_ (orig.has_entry_pc_),
|
|
pc_low_ (orig.pc_low_),
|
|
pc_high_ (orig.pc_high_),
|
|
ranges_ (orig.ranges_),
|
|
name_ (orig.name_),
|
|
linkage_name_ (orig.linkage_name_),
|
|
call_file_ (orig.call_file_),
|
|
call_line_ (orig.call_line_)
|
|
{
|
|
}
|
|
|
|
function::~function ()
|
|
{
|
|
}
|
|
|
|
std::string
|
|
function::name () const
|
|
{
|
|
return name_;
|
|
}
|
|
|
|
const address_ranges&
|
|
function::get_ranges () const
|
|
{
|
|
return ranges_;
|
|
}
|
|
|
|
dwarf_unsigned
|
|
function::pc_low () const
|
|
{
|
|
dwarf_address addr = pc_low_;
|
|
if (!ranges_.empty ())
|
|
{
|
|
addr = ~0;
|
|
for (auto& r : ranges_.get ())
|
|
{
|
|
if (!r.end () && !r.empty () && r.addr1 () < addr)
|
|
addr = r.addr1 ();
|
|
}
|
|
}
|
|
if (has_entry_pc_ && addr < entry_pc_)
|
|
addr += entry_pc_;
|
|
return addr;
|
|
}
|
|
|
|
dwarf_unsigned
|
|
function::pc_high () const
|
|
{
|
|
dwarf_address addr = pc_high_;
|
|
if (!ranges_.empty ())
|
|
{
|
|
addr = 0;
|
|
for (auto& r : ranges_.get ())
|
|
{
|
|
if (!r.end () && !r.empty () && r.addr2 () > addr)
|
|
addr = r.addr2 ();
|
|
}
|
|
}
|
|
if (has_entry_pc_ && addr < entry_pc_)
|
|
addr += entry_pc_;
|
|
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_inlined || inline_ == DW_INL_declared_inlined;
|
|
}
|
|
|
|
function::inlined
|
|
function::get_inlined () const
|
|
{
|
|
inlined i = inl_not_inlined;
|
|
switch (inline_)
|
|
{
|
|
default:
|
|
case DW_INL_not_inlined:
|
|
break;
|
|
case DW_INL_inlined:
|
|
i = inl_inline;
|
|
break;
|
|
case DW_INL_declared_not_inlined:
|
|
i = inl_declared_not_inlined;
|
|
break;
|
|
case DW_INL_declared_inlined:
|
|
i = inl_declared_inlined;
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
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 ();
|
|
}
|
|
|
|
size_t
|
|
function::size () const
|
|
{
|
|
size_t s = 0;
|
|
if (!name_.empty () && has_machine_code ())
|
|
{
|
|
if (ranges_.empty ())
|
|
s = pc_high () - pc_low ();
|
|
else
|
|
{
|
|
for (auto& r : ranges_.get ())
|
|
{
|
|
if (!r.end () && !r.empty ())
|
|
s += r.addr2 () - r.addr1 ();
|
|
}
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
function&
|
|
function::operator = (const function& rhs)
|
|
{
|
|
if (this != &rhs)
|
|
{
|
|
debug = rhs.debug;
|
|
machine_code_ = rhs.machine_code_;
|
|
external_ = rhs.external_;
|
|
declaration_ = rhs.declaration_;
|
|
inline_ = rhs.inline_;
|
|
entry_pc_ = rhs.entry_pc_;
|
|
has_entry_pc_ = rhs.has_entry_pc_;
|
|
pc_low_ = rhs.pc_low_;
|
|
pc_high_ = rhs.pc_high_;
|
|
ranges_ = rhs.ranges_;
|
|
name_ = rhs.name_;
|
|
linkage_name_ = rhs.linkage_name_;
|
|
call_file_ = rhs.call_file_;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void
|
|
function::dump (std::ostream& out) const
|
|
{
|
|
if (name_.empty ())
|
|
out << "NO-NAME";
|
|
else
|
|
out << name_;
|
|
out << " ["
|
|
<< (char) (machine_code_ ? 'M' : '-')
|
|
<< (char) (external_ ? 'E' : '-')
|
|
<< (char) (declaration_ ? 'D' : '-')
|
|
<< (char) (is_inlined () ? 'I' : '-')
|
|
<< (char) (has_entry_pc_ ? 'P' : '-')
|
|
<< "] size=" << size ()
|
|
<< std::hex << std::setfill ('0')
|
|
<< " (0x" << size () << ')';
|
|
if (has_entry_pc_)
|
|
out << " epc=0x" << entry_pc_;
|
|
out << " pc_low=0x" << pc_low_
|
|
<< " pc_high=0x" << pc_high_;
|
|
if (!linkage_name_.empty ())
|
|
{
|
|
std::string show_name;
|
|
rld::symbols::demangle_name(linkage_name_, show_name);
|
|
out << " ln=" << show_name;
|
|
}
|
|
out << std::dec << std::setfill (' ');
|
|
if (!call_file_.empty ())
|
|
out << " cf=" << call_file_ << ':' << call_line_;
|
|
if (!ranges_.empty ())
|
|
{
|
|
out << " ranges=";
|
|
ranges_.dump (out);
|
|
}
|
|
}
|
|
|
|
bool
|
|
function_compare::operator () (const function& a,
|
|
const function& b) const
|
|
{
|
|
bool r = true;
|
|
|
|
switch (by)
|
|
{
|
|
case fc_by_name:
|
|
default:
|
|
r = a.name () < b.name ();
|
|
break;
|
|
case fc_by_size:
|
|
r = a.size () < b.size ();
|
|
break;
|
|
case fc_by_address:
|
|
r = a.pc_low () < b.pc_low ();
|
|
break;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
function_compare:: function_compare (const function_compare::sort_by by)
|
|
: by (by)
|
|
{
|
|
}
|
|
|
|
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)
|
|
{
|
|
update ();
|
|
}
|
|
|
|
debug_info_entry::debug_info_entry (file& debug, dwarf_offset offset__)
|
|
: debug (debug),
|
|
die (nullptr),
|
|
tag_ (0),
|
|
offset_ (offset__)
|
|
{
|
|
update ();
|
|
}
|
|
|
|
debug_info_entry::debug_info_entry (const debug_info_entry& orig)
|
|
: debug (orig.debug),
|
|
die (nullptr),
|
|
tag_ (orig.tag_),
|
|
offset_ (orig.offset_)
|
|
{
|
|
update ();
|
|
}
|
|
|
|
debug_info_entry::~debug_info_entry ()
|
|
{
|
|
dealloc ();
|
|
}
|
|
|
|
void
|
|
debug_info_entry::update ()
|
|
{
|
|
dwarf_error de;
|
|
int dr;
|
|
if (offset_ == 0 && die != nullptr)
|
|
{
|
|
dr = ::dwarf_dieoffset (die, &offset_, &de);
|
|
libdwarf_error_check ("debug_info_entry:update", dr, de);
|
|
}
|
|
if (offset_ != 0 && die == nullptr)
|
|
{
|
|
dwarf_die ddie;
|
|
dr = ::dwarf_offdie (debug, offset_, &ddie, &de);
|
|
libdwarf_error_check ("debug_info_entry:update", dr, de);
|
|
die = ddie;
|
|
}
|
|
valid ();
|
|
}
|
|
|
|
bool
|
|
debug_info_entry::valid (bool fatal) const
|
|
{
|
|
bool r = die == nullptr || offset_ == 0;
|
|
if (r && fatal)
|
|
{
|
|
std::string what = "no DIE and offset";
|
|
if (offset_ != 0)
|
|
what = "no DIE";
|
|
else if (die != nullptr)
|
|
what = "no offset";
|
|
throw rld::error (what, "debug_info_entry:valid");
|
|
}
|
|
return r;
|
|
}
|
|
|
|
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;
|
|
if (offset_ != 0 || die != nullptr)
|
|
update ();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
debug_info_entry&
|
|
debug_info_entry::operator = (dwarf_offset offset__)
|
|
{
|
|
dealloc ();
|
|
if (offset__ != 0)
|
|
{
|
|
offset_ = offset__;
|
|
tag_ = 0;
|
|
update ();
|
|
}
|
|
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:tag", 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:offset", dr, de);
|
|
}
|
|
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,
|
|
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(unsigned)", 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(string)", 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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
debug_info_entry ret_die (get_debug ());
|
|
dwarf_error de;
|
|
int dr;
|
|
dr = ::dwarf_child (die, ret_die, &de);
|
|
if (dr == DW_DLV_OK)
|
|
{
|
|
ret_die.update ();
|
|
child_die = ret_die;
|
|
child_die.valid ();
|
|
}
|
|
return dr == DW_DLV_OK;
|
|
}
|
|
|
|
bool
|
|
debug_info_entry::has_child () const
|
|
{
|
|
debug_info_entry ret_die (get_debug ());
|
|
dwarf_error de;
|
|
int dr;
|
|
dr = ::dwarf_child (die, ret_die, &de);
|
|
return dr == DW_DLV_OK;
|
|
}
|
|
|
|
bool
|
|
debug_info_entry::get_sibling (debug_info_entry& sibling_die)
|
|
{
|
|
debug_info_entry ret_die (get_debug ());
|
|
dwarf_error de;
|
|
int dr;
|
|
dr = ::dwarf_siblingof (debug, die, ret_die, &de);
|
|
if (dr == DW_DLV_NO_ENTRY)
|
|
return false;
|
|
libdwarf_error_check ("compilation_unit::sibling", dr, de);
|
|
sibling_die = ret_die;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
debug_info_entry::has_sibling () const
|
|
{
|
|
debug_info_entry ret_die (get_debug ());
|
|
dwarf_error de;
|
|
int dr;
|
|
dr = ::dwarf_siblingof (debug, die, ret_die, &de);
|
|
return dr == DW_DLV_OK;
|
|
}
|
|
|
|
file&
|
|
debug_info_entry::get_debug () const
|
|
{
|
|
return debug;
|
|
}
|
|
|
|
void
|
|
debug_info_entry::dealloc ()
|
|
{
|
|
if (die != nullptr) {
|
|
::dwarf_dealloc (debug, die, DW_DLA_DIE);
|
|
die = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
debug_info_entry::dump (std::ostream& out,
|
|
std::string prefix,
|
|
bool newline)
|
|
{
|
|
std::string level_prefix;
|
|
|
|
for (auto c : prefix)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '+':
|
|
c = '|';
|
|
break;
|
|
case '-':
|
|
c = ' ';
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
level_prefix += c;
|
|
}
|
|
|
|
const char* s;
|
|
::dwarf_get_TAG_name (tag (), &s);
|
|
out << level_prefix.substr (0, level_prefix.length () - 1)
|
|
<< "+- " << 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)
|
|
out << std::endl;
|
|
out << level_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);
|
|
s = "";
|
|
switch (attr)
|
|
{
|
|
case DW_AT_inline:
|
|
dwarf_get_INL_name(v_unsigned, &s);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
out << " : "
|
|
<< std::hex << std::setfill ('0')
|
|
<< std::setw (8) << v_unsigned
|
|
<< std::dec << std::setfill (' ')
|
|
<< " (" << v_unsigned << ") " << s;
|
|
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);
|
|
out << " : "
|
|
<< 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;
|
|
out << std::endl;
|
|
v_die.dump (out, 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);
|
|
out << " : " << 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);
|
|
out << " : " << s;
|
|
if (rld::symbols::is_cplusplus(s))
|
|
{
|
|
std::string cpps;
|
|
rld::symbols::demangle_name(s, cpps);
|
|
out << " `" << cpps << '`';
|
|
}
|
|
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);
|
|
out << ' ';
|
|
v_ranges.load (v_offset);
|
|
v_ranges.dump (out);
|
|
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)
|
|
out << std::endl;
|
|
}
|
|
}
|
|
|
|
void
|
|
die_dump_children (debug_info_entry& die,
|
|
std::ostream& out,
|
|
std::string prefix,
|
|
int depth,
|
|
int nesting)
|
|
{
|
|
debug_info_entry child (die.get_debug ());
|
|
if (die.get_child (child))
|
|
die_dump (child, out, prefix, depth, nesting);
|
|
}
|
|
|
|
void
|
|
die_dump (debug_info_entry& die,
|
|
std::ostream& out,
|
|
std::string prefix,
|
|
int depth,
|
|
int nesting)
|
|
{
|
|
++nesting;
|
|
|
|
while (true)
|
|
{
|
|
char v = die.has_sibling () || die.has_child () ? '+' : ' ';
|
|
|
|
die.dump (out, prefix + v);
|
|
|
|
if (depth < 0 || nesting < depth)
|
|
die_dump_children (die, out, prefix + "+ ", depth, nesting);
|
|
|
|
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)
|
|
: debug (debug),
|
|
offset_ (offset),
|
|
pc_low_ (0),
|
|
pc_high_ (0),
|
|
ranges_ (debug),
|
|
die_offset (die.offset ()),
|
|
source_ (debug, die_offset)
|
|
{
|
|
die.attribute (DW_AT_name, name_);
|
|
name_ = name_;
|
|
|
|
die.attribute (DW_AT_producer, producer_);
|
|
|
|
ranges_.load (die, false);
|
|
|
|
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)
|
|
{
|
|
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 = 0x" << std::setw (8) << pc_low_
|
|
<< " pc_high = 0x" << 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;
|
|
bool seq_check = true;
|
|
dwarf_address seq_base = 0;
|
|
|
|
for (size_t l = 0; l < lines.count (); ++l)
|
|
{
|
|
address daddr (source_, lines[l]);
|
|
dwarf_address loc = daddr.location ();
|
|
/*
|
|
* A CU's line program can have some sequences at the start where the
|
|
* address is incorrectly set to 0. Ignore these entries.
|
|
*/
|
|
if (pc == 0)
|
|
{
|
|
if (!seq_check)
|
|
{
|
|
seq_check = daddr.is_an_end_sequence ();
|
|
continue;
|
|
}
|
|
if (loc == 0)
|
|
{
|
|
seq_check = false;
|
|
continue;
|
|
}
|
|
}
|
|
/*
|
|
* A sequence of line program instruction may set the address to 0. Use
|
|
* the last location from the previous sequence as the sequence's base
|
|
* address. All locations will be offset from the that base until the
|
|
* end of this sequence.
|
|
*/
|
|
if (loc == 0 && seq_base == 0)
|
|
seq_base = pc;
|
|
if (seq_base != 0)
|
|
loc += seq_base;
|
|
if (daddr.is_an_end_sequence ())
|
|
seq_base = 0;
|
|
address addr (daddr, loc);
|
|
if (loc >= pc_low_ && loc < pc_high_)
|
|
{
|
|
pc = loc;
|
|
addr_lines_.push_back (addr);
|
|
}
|
|
}
|
|
|
|
if (!addr_lines_.empty ())
|
|
{
|
|
std::stable_sort (addr_lines_.begin (), addr_lines_.end ());
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
{
|
|
auto first = addr_lines_.begin ();
|
|
auto last = addr_lines_.end () - 1;
|
|
std::cout << "dwarf::compilation_unit: line_low=0x"
|
|
<< std::hex << std::setfill('0')
|
|
<< std::setw (8) << first->location ()
|
|
<< ", line_high=0x"
|
|
<< std::setw (8) << last->location ()
|
|
<< std::dec << std::setfill(' ')
|
|
<< std::endl;
|
|
}
|
|
}
|
|
|
|
if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
|
|
{
|
|
int lc = 0;
|
|
for (auto& l : addr_lines_)
|
|
{
|
|
std::cout << "dwarf::compilation_unit: " << std::setw (3) << ++lc
|
|
<< ": 0x"
|
|
<< std::hex << std::setfill('0')
|
|
<< std::setw (8) << l.location ()
|
|
<< std::dec << std::setfill(' ')
|
|
<< " - "
|
|
<< (char) (l.is_a_begin_statement () ? 'B' : '.')
|
|
<< (char) (l.is_in_a_block () ? 'I' : '.')
|
|
<< (char) (l.is_an_end_sequence () ? 'E' : '.')
|
|
<< " - "
|
|
<< rld::path::basename (l.path ())
|
|
<< ':' <<l.line ()
|
|
<< std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
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_),
|
|
ranges_ (orig.ranges_),
|
|
die_offset (orig.die_offset),
|
|
source_ (debug, die_offset)
|
|
{
|
|
for (auto& line : orig.addr_lines_)
|
|
addr_lines_.push_back (address (line, source_));
|
|
std::stable_sort (addr_lines_.begin (), addr_lines_.end ());
|
|
}
|
|
|
|
compilation_unit::~compilation_unit ()
|
|
{
|
|
}
|
|
|
|
void
|
|
compilation_unit::load_types ()
|
|
{
|
|
//dump_die ();
|
|
}
|
|
|
|
void
|
|
compilation_unit::load_functions ()
|
|
{
|
|
debug_info_entry die (debug, die_offset);
|
|
debug_info_entry child (debug);
|
|
if (die.get_child (child))
|
|
load_functions (child);
|
|
}
|
|
|
|
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);
|
|
functions_.push_back (func);
|
|
}
|
|
|
|
debug_info_entry next (die.get_debug ());
|
|
|
|
if (die.get_child (next))
|
|
load_functions (next);
|
|
|
|
if (!die.get_sibling (next))
|
|
break;
|
|
|
|
die = next;
|
|
}
|
|
}
|
|
|
|
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_.empty () && inside (addr))
|
|
{
|
|
address last_loc;
|
|
for (auto& loc : addr_lines_)
|
|
{
|
|
if (addr <= loc.location ())
|
|
{
|
|
if (addr == loc.location ())
|
|
addr_line = loc;
|
|
else
|
|
addr_line = last_loc;
|
|
return addr_line.valid ();
|
|
}
|
|
last_loc = loc;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
functions&
|
|
compilation_unit::get_functions ()
|
|
{
|
|
return functions_;
|
|
}
|
|
|
|
bool
|
|
compilation_unit::inside (dwarf_unsigned addr) const
|
|
{
|
|
return addr >= pc_low_ && addr < pc_high_;
|
|
}
|
|
|
|
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_;
|
|
source_ = sources (debug, die_offset);
|
|
for (auto& line : rhs.addr_lines_)
|
|
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 (std::ostream& out,
|
|
const std::string prefix,
|
|
int depth)
|
|
{
|
|
debug_info_entry die (debug, die_offset);
|
|
out << "CU @ 0x" << std::hex << offset_ << std::dec << std::endl;
|
|
die_dump (die, out, prefix, depth);
|
|
}
|
|
|
|
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::dump (std::ostream& out,
|
|
const std::string prefix,
|
|
int depth)
|
|
{
|
|
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;
|
|
|
|
/*
|
|
* Find the CU DIE by asking the CU for it's first DIE.
|
|
*/
|
|
debug_info_entry die (*this);
|
|
|
|
while (true)
|
|
{
|
|
debug_info_entry sibling (*this);
|
|
if (!die.get_sibling (sibling))
|
|
break;
|
|
if (sibling.tag () == DW_TAG_compile_unit)
|
|
die_dump (sibling, out, prefix, depth);
|
|
die = sibling;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
/*
|
|
* Find 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;
|
|
}
|
|
}
|
|
|
|
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,
|
|
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 () &&
|
|
(match.is_an_end_sequence () || !!line.is_an_end_sequence ()))
|
|
{
|
|
match = line;
|
|
}
|
|
else
|
|
{
|
|
match = line;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (match.valid ())
|
|
{
|
|
source_file = match.path ();
|
|
source_line = match.line ();
|
|
r = true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|