/* 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 #include #include "spec.hpp" #include "libdwarf.hpp" /* includes libdwarf.h, Error, No_entry, some fwddecls */ #include #include /* 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 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 *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(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(v_u); } bool is_block() const { return f == BLOCK; } const std::vector *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(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 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&); //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 { typedef std::map 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 insert(const value_type& val) { return this->base::insert(val); } virtual std::pair insert(value_type&& val) // was template ... (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& v); std::ostream& operator<<(std::ostream& s, const attribute_value::address& a); } } #endif