257 lines
12 KiB
C++
257 lines
12 KiB
C++
/* dwarfpp: C++ binding for a useful subset of libdwarf, plus extra goodies.
|
|
*
|
|
* attr.hpp: transparently-allocated, mutable representations
|
|
* of libdwarf-like structures.
|
|
*
|
|
* Copyright (c) 2010--17, Stephen Kell. For licensing information, see the
|
|
* LICENSE file in the root of the libdwarfpp tree.
|
|
*/
|
|
|
|
#ifndef DWARFPP_ATTR_HPP_
|
|
#define DWARFPP_ATTR_HPP_
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include "spec.hpp"
|
|
#include "libdwarf.hpp" /* includes libdwarf.h, Error, No_entry, some fwddecls */
|
|
|
|
#include <boost/optional.hpp>
|
|
#include <srk31/util.hpp> /* for forward_constructors */
|
|
|
|
namespace dwarf
|
|
{
|
|
namespace core
|
|
{
|
|
// libdwarf handle stuff -- FIXME: avoid depending where possible
|
|
struct Attribute;
|
|
struct AttributeList;
|
|
struct Debug;
|
|
struct Die;
|
|
|
|
struct root_die;
|
|
struct iterator_base;
|
|
struct basic_die;
|
|
struct type_die;
|
|
template <typename DerefAs = basic_die> struct iterator_df;
|
|
}
|
|
namespace encap
|
|
{
|
|
using namespace dwarf::lib;
|
|
class rangelist;
|
|
class loclist;
|
|
using core::root_die;
|
|
using core::debug;
|
|
|
|
class attribute_value {
|
|
friend class core::basic_die; // for use of the NO_ATTR constructor in find_attr
|
|
friend class core::iterator_base; // the same in iterator_base::attr()
|
|
public:
|
|
struct weak_ref {
|
|
friend class attribute_value;
|
|
|
|
Dwarf_Off off;
|
|
bool abs;
|
|
Dwarf_Off referencing_off; // off of DIE keeping the reference
|
|
root_die *p_root; // FIXME: hmm
|
|
Dwarf_Half referencing_attr; // attr# of attribute holding the reference
|
|
weak_ref(root_die& r, Dwarf_Off off, bool abs,
|
|
Dwarf_Off referencing_off, Dwarf_Half referencing_attr)
|
|
: off(off), abs(abs),
|
|
referencing_off(referencing_off),
|
|
p_root(&r), referencing_attr(referencing_attr) {}
|
|
// weak_ref is default-constructible
|
|
weak_ref() : off(0UL), abs(false), referencing_off(0), p_root(0), referencing_attr(0)
|
|
{ debug() << "Warning: default-constructed a weak_ref!" << std::endl; }
|
|
weak_ref(bool arg) : off(0UL), abs(false), referencing_off(0), p_root(0), referencing_attr(0)
|
|
{ /* Same as above but extra dummy argument, to suppress warning (see clone()). */}
|
|
virtual weak_ref& operator=(const weak_ref& r); // assignment
|
|
virtual weak_ref *clone() const {
|
|
weak_ref *n = new weak_ref(true); // FIXME: deleted how? containing attribute's destructor? yes, I think so
|
|
n->p_root = this->p_root;
|
|
*n = *this;
|
|
return n;
|
|
}
|
|
weak_ref(const weak_ref& r); // copy constructor
|
|
virtual ~weak_ref() {}
|
|
bool operator==(const weak_ref& arg) const {
|
|
return off == arg.off && abs == arg.abs
|
|
&& referencing_off == arg.referencing_off
|
|
&& referencing_attr == arg.referencing_attr
|
|
&& p_root == arg.p_root;
|
|
}
|
|
bool operator!=(const weak_ref& arg) const {
|
|
return !(*this == arg);
|
|
}
|
|
};
|
|
struct address
|
|
/* Why do we have this? It's because of overload resolution.
|
|
* Dwarf_Addr is just a typedef for some integer type, and one
|
|
* of the other constructors of attribute_value also takes that
|
|
* integer type. To disambiguate, we create an encapsulated
|
|
* address data type. */
|
|
{
|
|
Dwarf_Addr addr;
|
|
address(Dwarf_Addr val) { this->addr = val; }
|
|
bool operator==(const address& arg) const { return this->addr == arg.addr; }
|
|
bool operator!=(const address& arg) const { return !(*this == arg); }
|
|
bool operator<(const address& arg) const { return this->addr < arg.addr; }
|
|
bool operator<=(const address& arg) const { return this->addr <= arg.addr; }
|
|
bool operator>(const address& arg) const { return this->addr > arg.addr; }
|
|
bool operator>=(const address& arg) const { return this->addr >= arg.addr; }
|
|
bool operator==(Dwarf_Addr arg) const { return this->addr == arg; }
|
|
bool operator!=(Dwarf_Addr arg) const { return !(*this == arg); }
|
|
bool operator<(Dwarf_Addr arg) const { return this->addr < arg; }
|
|
bool operator<=(Dwarf_Addr arg) const { return this->addr <= arg; }
|
|
bool operator>(Dwarf_Addr arg) const { return this->addr > arg; }
|
|
bool operator>=(Dwarf_Addr arg) const { return this->addr >= arg; }
|
|
//Dwarf_Addr operator Dwarf_Addr() { return addr; }
|
|
/* FIXME: why *not* have the above? There must be a good reason....
|
|
* ambiguity maybe? */
|
|
};
|
|
enum form { NO_ATTR, ADDR, FLAG, UNSIGNED, SIGNED, BLOCK, STRING, REF, LOCLIST, RANGELIST, UNRECOG }; // TODO: complete?
|
|
form get_form() const { return f; }
|
|
private:
|
|
Dwarf_Half orig_form;
|
|
form f; // discriminant
|
|
union {
|
|
Dwarf_Bool v_flag;
|
|
Dwarf_Unsigned v_u;
|
|
Dwarf_Signed v_s;
|
|
address v_addr;
|
|
// pointees are RAII-allocated with new/delete -- because non-PODs can't go in union
|
|
std::vector<unsigned char> *v_block;
|
|
std::string *v_string;
|
|
weak_ref *v_ref;
|
|
encap::loclist *v_loclist;
|
|
encap::rangelist *v_rangelist;
|
|
};
|
|
static form dwarf_form_to_form(const Dwarf_Half form); // helper hack
|
|
// -- the operator<< is a friend (WHY?)
|
|
friend std::ostream& ::dwarf::lib::operator<<(std::ostream& s, const dwarf::lib::Dwarf_Loc& l);
|
|
|
|
private:
|
|
attribute_value() : orig_form(0), f(NO_ATTR) { v_u = 0U; }
|
|
// the following constructor is a HACK to re-use formatting logic when printing Dwarf_Locs
|
|
attribute_value(Dwarf_Unsigned data, Dwarf_Half o_form)
|
|
: orig_form(o_form), f(dwarf_form_to_form(o_form)), v_u(data) {}
|
|
// the following is a temporary HACK to allow core:: to create attribute_values
|
|
public:
|
|
attribute_value(const dwarf::core::Attribute& attr,
|
|
const dwarf::core::Die& d,
|
|
root_die& r/*,
|
|
spec::abstract_def& = spec::DEFAULT_DWARF_SPEC*/);
|
|
// spec is no longer passed because it's deducible from r and d.get_offset()
|
|
public:
|
|
explicit attribute_value(Dwarf_Bool b) : orig_form(DW_FORM_flag), f(FLAG), v_flag(b) {}
|
|
explicit attribute_value(address addr) : orig_form(DW_FORM_addr), f(ADDR), v_addr(addr) {}
|
|
explicit attribute_value(Dwarf_Unsigned u) : orig_form(DW_FORM_udata), f(UNSIGNED), v_u(u) {}
|
|
explicit attribute_value(Dwarf_Signed s) : orig_form(DW_FORM_sdata), f(SIGNED), v_s(s) {}
|
|
attribute_value(const char *s) : orig_form(DW_FORM_string), f(STRING), v_string(new std::string(s)) {}
|
|
attribute_value(const std::string& s) : orig_form(DW_FORM_string), f(STRING), v_string(new std::string(s)) {}
|
|
attribute_value(const weak_ref& r) : orig_form(DW_FORM_ref_addr), f(REF), v_ref(r.clone()) {}
|
|
|
|
public:
|
|
bool is_flag() const { return f == FLAG; }
|
|
Dwarf_Bool get_flag() const { assert(is_flag()); return v_flag; }
|
|
// allow mix-and-match among signed and unsigned
|
|
bool is_unsigned() const { return f == UNSIGNED || f == SIGNED; }
|
|
Dwarf_Unsigned get_unsigned() const
|
|
{ assert(is_unsigned()); return (f == UNSIGNED) ? v_u : static_cast<Dwarf_Unsigned>(v_s); }
|
|
bool is_signed() const { return f == SIGNED || f == UNSIGNED; }
|
|
Dwarf_Signed get_signed() const
|
|
{ assert(is_signed()); return (f == SIGNED) ? v_s : static_cast<Dwarf_Signed>(v_u); }
|
|
bool is_block() const { return f == BLOCK; }
|
|
const std::vector<unsigned char> *get_block() const { assert(is_block()); return v_block; }
|
|
bool is_string() const { return f == STRING; }
|
|
const std::string& get_string() const { assert(is_string()); return *v_string; }
|
|
/* I added the tolerance of UNSIGNED here because sometimes high_pc is an address,
|
|
* other times it's unsigned... BUT it means something different in the latter
|
|
* case (lopc-relative) so it's best to handle this difference higher up. */
|
|
bool is_address() const { return f == ADDR /* || f == UNSIGNED*/; }
|
|
address get_address() const { assert(is_address()); return/* (f == ADDR) ?*/ v_addr /*: address(static_cast<Dwarf_Addr>(v_u))*/; }
|
|
bool is_loclist() const { return f == LOCLIST; }
|
|
const loclist& get_loclist() const { assert(is_loclist()); return *v_loclist; }
|
|
bool is_rangelist() const { return f == RANGELIST; }
|
|
const rangelist& get_rangelist() const { assert(is_rangelist()); return *v_rangelist; }
|
|
bool is_ref() const { return f == REF; }
|
|
weak_ref& get_ref() const { assert(is_ref()); return *v_ref; }
|
|
Dwarf_Off get_refoff() const { assert(is_ref()); return v_ref->off; }
|
|
Dwarf_Off get_refoff_is_type() const { assert(is_ref()); return v_ref->off; }
|
|
bool is_refiter() const { return f == REF; }
|
|
core::iterator_df<> get_refiter() const;// { assert(f == REF); return v_ref->off; }
|
|
bool is_refiter_is_type() const { return f == REF; /* FIXME */ }
|
|
core::iterator_df<core::type_die> get_refiter_is_type() const;// { assert(f == REF); return v_ref->off; }
|
|
bool is_refdie() const { return f == REF; /* FIXME */ }
|
|
|
|
bool operator==(const attribute_value& v) const;
|
|
bool operator!=(const attribute_value &v) const { return !(*this == v); }
|
|
|
|
void print_raw(std::ostream& s) const;
|
|
void print_as(std::ostream& s, int cls) const;
|
|
friend std::ostream& operator<<(std::ostream& s, const attribute_value v);
|
|
friend std::ostream& operator<<(std::ostream& s, std::pair<const Dwarf_Half, attribute_value>&);
|
|
//friend std::ostream& operator<<(std::ostream& o, const dwarf::encap::die& d);
|
|
// copy constructor
|
|
attribute_value(const attribute_value& av);
|
|
|
|
virtual ~attribute_value();
|
|
}; // end class attribute_value
|
|
|
|
struct attribute_map : public std::map<Dwarf_Half, attribute_value>
|
|
{
|
|
typedef std::map<Dwarf_Half, attribute_value> base;
|
|
// forward constructors
|
|
//forward_constructors(base, attribute_map)
|
|
// hmm -- this messes with overload resolution; just forward default for now
|
|
attribute_map() : base() {}
|
|
|
|
// also construct from AttributeList
|
|
attribute_map(const core::AttributeList& a, const core::Die& d, root_die& r,
|
|
spec::abstract_def &p_spec = spec::DEFAULT_DWARF_SPEC);
|
|
|
|
// static 3-arg print function
|
|
//static std::ostream&
|
|
//print_to(std::ostream& s, const AttributeList& attrs, root_die& r);
|
|
|
|
void print(std::ostream& s, unsigned indent_level) const;
|
|
|
|
/* In order of insert() to preserve the invariant in root_die,
|
|
* we restrict the insertion interface a bit and make it polymorphic.
|
|
* The invariant we're talking about here concerns root_die's
|
|
* visible_named_grandchildren_is_complete: if we add a global name to a
|
|
* depth-2 DIE, we need to update the cache or invalidate it. We do
|
|
* this by overriding some stuff in a derived class (in in_memory_abstract_die). */
|
|
virtual
|
|
std::pair<iterator, bool> insert(const value_type& val)
|
|
{
|
|
return this->base::insert(val);
|
|
}
|
|
virtual
|
|
std::pair<iterator,bool> insert(value_type&& val) // was template <class P> ... (P&& val)
|
|
{
|
|
return this->base::insert(val);
|
|
}
|
|
virtual
|
|
iterator insert(const_iterator position, const value_type& val)
|
|
{
|
|
return this->base::insert(position, val);
|
|
}
|
|
};
|
|
std::ostream& operator<<(std::ostream& s, const attribute_map& arg);
|
|
bool operator==(Dwarf_Addr arg, attribute_value::address a);
|
|
bool operator!=(Dwarf_Addr arg, attribute_value::address a);
|
|
bool operator<(Dwarf_Addr arg, attribute_value::address a);
|
|
bool operator<=(Dwarf_Addr arg, attribute_value::address a);
|
|
bool operator>(Dwarf_Addr arg, attribute_value::address a);
|
|
bool operator>=(Dwarf_Addr arg, attribute_value::address a);
|
|
Dwarf_Addr operator-(Dwarf_Addr arg, attribute_value::address a);
|
|
Dwarf_Addr operator-(attribute_value::address a, Dwarf_Addr arg);
|
|
|
|
std::ostream& operator<<(std::ostream& s, const attribute_value v);
|
|
std::ostream& operator<<(std::ostream& s, std::pair<const Dwarf_Half, attribute_value>& v);
|
|
std::ostream& operator<<(std::ostream& s, const attribute_value::address& a);
|
|
}
|
|
}
|
|
|
|
#endif
|