compilation-bundle/dwarf-compilation.base/contrib/libdwarfpp/include/dwarfpp/attr.hpp

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