/*
* Copyright (c) 2018, Chris Johns <chrisj@rtems.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* @file
*
* @ingroup rtems-ld
*
* @brief RTEMS Linker DWARF module manages the libdwarf interface.
*
*/
#if !defined (_RLD_DWARF_H_)
#define _RLD_DWARF_H_
#include <iostream>
#include <rld.h>
#include <rld-dwarf-types.h>
namespace rld
{
namespace dwarf
{
/**
* Forward decls
*/
class sources;
class debug_info_entry;
class file;
/**
* Address.
*/
class address
{
public:
address (const sources& source, dwarf_line& line);
address (const address& orig, const sources& source);
address (const address& orig, dwarf_address addr);
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 = (const address& rhs);
/**
* Less than operator to allow sorting.
*/
bool operator < (const address& rhs) const;
private:
dwarf_address addr;
sources const* source;
dwarf_unsigned source_index;
dwarf_signed source_line;
bool begin_statement;
bool block;
bool end_sequence;
};
/**
* The addresses table is a vector sorted from low to high addresses..
*/
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 (std::ostream& out) const;
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 (std::ostream& out) const;
private:
file& debug;
dwarf_offset offset;
dwarf_ranges* dranges;
dwarf_signed dranges_count;
ranges ranges_;
};
/**
* Line addresses.
*/
class line_addresses
{
public:
line_addresses (file& debug, debug_info_entry& die);
~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, dwarf_offset die_offset);
sources (const 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;
};
/**
* Variable.
*/
class variable
{
public:
variable (file& debug, debug_info_entry& die);
variable (const variable& orig);
~variable ();
/**
* Get the name of the variable.
*/
std::string name () const;
/**
* Is the variable external?
*/
bool is_external () const;
/**
* Is this just a declaration?
*/
bool is_declaration () const;
/**
* Size of the variable.
*/
size_t size () const;
/**
* Assigment operator.
*/
variable& operator = (const variable& rhs);
/**
* Dump the variable.
*/
void dump (std::ostream& out) const;
private:
file& debug;
bool external_;
bool declaration_;
std::string name_;
std::string decl_file_;
dwarf_unsigned decl_line_;
};
typedef std::vector < variable > variables;
/**
* Function.
*/
class function
{
public:
/**
* The various inline states. See Table 3.4 DWARF 5 standard.
*/
enum inlined {
inl_not_inlined = 0, /**< Not declared inline nore inlined. */
inl_inline = 1, /**< Not declared inline but inlined. */
inl_declared_not_inlined = 2, /**< Declared inline but not inlined. */
inl_declared_inlined = 3 /**< Declared inline and inlined */
};
function (file& debug, debug_info_entry& die);
function (const function& orig);
~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 an entry PC?
*/
bool has_entry_pc () 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 inlined state.
*/
inlined get_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;
/**
* Size of the function.
*/
size_t size () const;
/**
* Assigment operator.
*/
function& operator = (const function& rhs);
/**
* Dump the function.
*/
void dump (std::ostream& out) const;
private:
file& debug;
bool machine_code_;
bool external_;
bool declaration_;
bool prototyped_;
dwarf_unsigned inline_;
dwarf_unsigned entry_pc_;
bool has_entry_pc_;
dwarf_unsigned pc_low_;
dwarf_unsigned pc_high_;
address_ranges ranges_;
std::string name_;
std::string linkage_name_;
std::string decl_file_;
dwarf_unsigned decl_line_;
std::string call_file_;
dwarf_unsigned call_line_;
};
typedef std::vector < function > functions;
/**
* Worker to sort the functions.
*/
struct function_compare
{
enum sort_by
{
fc_by_name,
fc_by_size,
fc_by_address
};
const sort_by by;
bool operator () (const function& a, const function& b) const;
function_compare (sort_by by = fc_by_name);
};
/**
* 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 (const debug_info_entry& orig);
/**
* Destruct and clean up.
*/
~debug_info_entry ();
/**
* Is the DIE valid?
*/
bool valid (bool fatal = true) const;
/**
* 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);
debug_info_entry& operator = (dwarf_offset offset);
/**
* 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 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.
*/
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;
/**
* Get the ranges.
*/
bool ranges (dwarf_ranges*& ranges, dwarf_signed& rangescount) const;
/**
* Get the child.
*/
bool get_child (debug_info_entry& child_die);
/**
* Has a child?
*/
bool has_child () const;
/**
* Get the silbing
*/
bool get_sibling (debug_info_entry& sibling_die);
/**
* Has a silbing?
*/
bool has_sibling () const;
/**
* Get the debug info for this DIE.
*/
file& get_debug () const;
/**
* deallocate the DIE.
*/
void dealloc ();
/**
* Dump this DIE.
*/
void dump (std::ostream& out,
std::string prefix,
bool newline = true);
private:
/**
* Update the internal DIE and offset values.
*/
void update ();
file& debug;
dwarf_die die;
dwarf_tag tag_;
dwarf_offset offset_;
};
/**
* Dump the DIE and all it's children and siblings.
*/
void die_dump_children (debug_info_entry& die,
std::ostream& out,
std::string prefix,
int depth = -1,
int nesting = 0);
/**
* Dump the DIE and all it's children and siblings.
*/
void die_dump (debug_info_entry& die,
std::ostream& out,
std::string prefix,
int depth = -1,
int nesting = 0);
/**
* 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 ();
/**
* Load the types.
*/
void load_types ();
/**
* Load the variables.
*/
void load_variables ();
/**
* Load the functions.
*/
void load_functions ();
/**
* 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);
/**
* 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.
*/
bool inside (dwarf_unsigned addr) const;
/**
* Copy assignment operator.
*/
compilation_unit& operator = (const compilation_unit& rhs);
/**
* Output the DIE tree.
*/
void dump_die (std::ostream& out,
const std::string prefix = " ",
int depth = -1);
private:
void load_variables (debug_info_entry& die);
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;
/**
* 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 ();
/**
* Load the DWARF type information.
*/
void load_types ();
/**
* Load the DWARF functions information.
*/
void load_functions ();
/**
* Load the DWARF variables information.
*/
void load_variables ();
/**
* 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 variable given a name. Raises an exception if not found.
*/
variable& get_variable (std::string& name);
/**
* Does the function exist.
*/
bool function_valid (std::string&name);
/**
* Get the function given a name. Raises an exception if not found.
*/
function& get_function (std::string& name);
/**
* Get the function given an address.
*/
bool get_function (const unsigned int address,
std::string& name);
/**
* 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;
/**
* Dump the DWARF data.
*/
void dump (std::ostream& out,
const std::string prefix = " ",
int depth = -1);
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