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

997 lines
40 KiB
C++

/* dwarfpp: C++ binding for a useful subset of libdwarf, plus extra goodies.
*
* dies.hpp: classes and methods specific to each DIE tag
*
* Copyright (c) 2008--17, Stephen Kell. For licensing information, see the
* LICENSE file in the root of the libdwarfpp tree.
*/
#ifndef DWARFPP_DIES_HPP_
#define DWARFPP_DIES_HPP_
#include <iostream>
#include <utility>
#include <memory>
#include <boost/icl/interval_map.hpp>
#include <boost/algorithm/string.hpp>
#include <unordered_set>
#include <stack>
#include <set>
#include <boost/optional/optional.hpp> // until we are using C++17
#include <srk31/rotate.hpp>
#include "dwarfpp/root.hpp"
#include "dwarfpp/expr.hpp"
#include "dwarfpp/iter.hpp"
namespace dwarf
{
using std::string;
using std::vector;
using std::stack;
using std::set;
using std::unordered_set;
using std::unordered_map;
using std::endl;
using boost::optional;
using std::shared_ptr;
namespace core
{
/****************************************************************/
/* begin generated ADT includes */
/****************************************************************/
#define forward_decl(t) class t ## _die;
#define declare_base(base) base ## _die
#define initialize_base(fragment) /*fragment ## _die(s, std::move(h))*/
#define constructor(fragment, ...) /* "..." is base inits */ \
fragment ## _die(spec& s, Die&& h) : basic_die(s, std::move(h)) {}
/* #define declare_bases(first_base, ...) first_base , ##__VA_ARGS__ */
#define begin_class(fragment, base_inits, ...) \
struct fragment ## _die : virtual __VA_ARGS__ { \
friend struct dwarf_current_factory_t; \
protected: /* dummy constructor used only to construct dummy instances */\
fragment ## _die(spec& s) : basic_die(s) {} \
/* constructor used only to construct in-memory instances */\
fragment ## _die(spec& s, root_die& r) : basic_die(s, r) {} \
public: /* main constructor */ \
constructor(fragment, base_inits /* NOTE: base_inits is expanded via ',' into varargs list */) \
protected: /* protected constructor that doesn't touch basic_die */ \
fragment ## _die() {} \
public: /* extra decls should be public */
#define base_initializations(...) __VA_ARGS__
#define end_class(fragment) \
};
#define stored_type_string std::string
#define stored_type_flag bool
#define stored_type_unsigned Dwarf_Unsigned
#define stored_type_signed Dwarf_Signed
#define stored_type_offset Dwarf_Off
#define stored_type_half Dwarf_Half
#define stored_type_ref Dwarf_Off
#define stored_type_tag Dwarf_Half
#define stored_type_loclist dwarf::encap::loclist
#define stored_type_address dwarf::encap::attribute_value::address
#define stored_type_refiter iterator_df<basic_die>
#define stored_type_refiter_is_type iterator_df<type_die>
#define stored_type_rangelist dwarf::encap::rangelist
/* This is libdwarf-specific. This might be okay --
* depends if we want a separate class hierarchy for the
* encap-style ones (like in encap::). Yes, we probably do.
* BUT things like type_die should still be the supertype, right?
* ARGH.
* I think we can use virtuality to deal with this in one go.
* Define a new encapsulated_die base class (off of basic_die)
* which redefines all the get_() stuff in one go.
* and then an encapsulated version of any type_die can use
* virtual inheritance to wire its getters up to those versions.
* ARGH: no, we need another round of these macros to enumerate all the getters.
*/
#define attr_optional(name, stored_t) \
opt<stored_type_ ## stored_t> get_ ## name() const \
{ if (has_attr(DW_AT_ ## name)) \
{ /* we have to check the form matches our expectations */ \
encap::attribute_value a = attr(DW_AT_ ## name); \
if (!a.is_ ## stored_t ()) { \
debug() << "Warning: attribute " #name " of DIE at 0x" << std::hex << get_offset() << std::dec << " not a " #stored_t << endl; \
return opt<stored_type_ ## stored_t>(); \
} else return a.get_ ## stored_t (); \
} \
else return opt<stored_type_ ## stored_t>(); } \
opt<stored_type_ ## stored_t> find_ ## name() const \
{ encap::attribute_value found = find_attr(DW_AT_ ## name); \
if (found.get_form() != encap::attribute_value::NO_ATTR) { \
if (!found.is_ ## stored_t ()) { \
debug() << "Warning: attribute " #name " of DIE at 0x" << std::hex << get_offset() << std::dec << " not a " #stored_t << endl; \
return opt<stored_type_ ## stored_t>(); \
} else return found.get_ ## stored_t (); \
} else return opt<stored_type_ ## stored_t>(); }
#define super_attr_optional(name, stored_t) attr_optional(name, stored_t)
#define attr_mandatory(name, stored_t) \
stored_type_ ## stored_t get_ ## name() const \
{ assert(has_attr(DW_AT_ ## name)); \
return attr(DW_AT_ ## name).get_ ## stored_t (); } \
stored_type_ ## stored_t find_ ## name() const \
{ encap::attribute_value found = find_attr(DW_AT_ ## name); \
assert(found.get_form() != encap::attribute_value::NO_ATTR); \
return found.get_ ## stored_t (); }
#define super_attr_mandatory(name, stored_t) attr_mandatory(name, stored_t)
#define child_tag(arg)
struct type_die;
struct with_static_location_die : public virtual basic_die
{
struct sym_binding_t
{
Dwarf_Off file_relative_start_addr;
Dwarf_Unsigned size;
};
typedef std::function<sym_binding_t(const std::string&, void *)> sym_resolver_t;
virtual encap::loclist get_static_location() const;
opt<Dwarf_Off> spans_addr(
Dwarf_Addr file_relative_addr,
root_die& r,
sym_resolver_t sym_resolve = sym_resolver_t(),
void *arg = 0) const;
virtual boost::icl::interval_map<Dwarf_Addr, Dwarf_Unsigned>
file_relative_intervals(
root_die& r,
sym_resolver_t sym_resolve /* = sym_resolver_t() */,
void *arg /* = 0 */) const;
};
struct with_named_children_die : public virtual basic_die
{
/* We most most of the resolution stuff into iterator_base,
* but leave this here so that payload implementations can
* provide a faster-than-default (i.e. faster than linear search)
* way to look up named children (e.g. hash table). */
virtual
iterator_base
named_child(const std::string& name) const;
};
/* program_element_die */
begin_class(program_element, base_initializations(basic), declare_base(basic))
attr_optional(decl_file, unsigned)
attr_optional(decl_line, unsigned)
attr_optional(decl_column, unsigned)
attr_optional(prototyped, flag)
attr_optional(declaration, flag)
attr_optional(external, flag)
attr_optional(visibility, unsigned)
attr_optional(artificial, flag)
end_class(program_element)
/* type_die */
template <typename BaseType>
struct summary_code_word
{
opt<BaseType> val;
typedef summary_code_word<BaseType> self;
void invalidate() { val = opt<BaseType>(); }
void zero_check()
{
if (val && *val == 0)
{
debug() << "Warning: output_word value hit zero again." << endl;
*val = (BaseType) -1;
}
}
self& operator<<(BaseType arg)
{
if (val)
{
*val = rotate_left(*val, 8) ^ arg;
zero_check();
}
return *this;
}
self& operator<<(const string& s)
{
if (val)
{
for (auto i = s.begin(); i != s.end(); ++i)
{
*this << static_cast<BaseType>(*i);
}
}
zero_check();
return *this;
}
self& operator<<(opt<BaseType> o)
{
if (!o)
{
val = opt<BaseType>(); // FIXME: do I want this to "lock" at the invalid state? for incomplete types etc.
return *this;
}
else return this->operator<<(*o);
}
summary_code_word() : val(0) {}
};
/* To do summary codes efficiently, we need to make them compositional.
* This means that given the summary codes for the types we depend on,
* we should be able to re-use those to compute the code our own type.
* Unfortunately, type graphs are cyclic, and so the naive approach
* (which we used to use) of doing a depth-first search and skipping
* back-edges, is non-compositional: depending on where you start the
* exploration, different edges are back-edges, and so a depended-on type's
* code can include/exclude different edges than the depending type does.
* To avoid this problem, we need to identify the strongly-connected
* components in the type graph. Then, any type that participates in a
* cycle can summarised by a pair: its strongly-connected component, and
* its own identity.
*
* Two problems: how do we represent strongly-connected components (SCCs)
* and how do we identify nodes? To answer the first: an SCC is just a set
* of edges -- including the back-edges.
* Nodes themselves are tricky. There is no obvious good way to identify
* nodes in the graph, since we don't want to be sensitive to their DWARF
* offsets, or even their relative ordering in the DWARF info section.
* We define an "abstract name". It is definitely not unique -- we are
* still building up to that, by defining summary codes -- but it should
* satisfy the property that
*
* - no DIE cycle includes two distinct types with the same abstract name;
*
* - distinct types with the same abstract name will summarise differently,
* either because they are not cyclic (and their tree-structures are
* different in the obvious way) or because they participate in cycles
* that are distinct *even* after names are abstracted.
*
* Whether the last one is true depends a lot on how an SCC, as a set of
* edges, gets crumpled down into a summary code.
*/
struct type_edge;
struct type_edge_compare : std::function<bool(const type_edge&, const type_edge&)>
{
type_edge_compare(); // initializes the std::function with a lambda
};
struct type_scc_t : public set<type_edge, type_edge_compare>
{
using set::set;
// FIXME: we could try to maintain this on inserts.
// if we remove an edge, we have to invalidate it and recompute.
summary_code_word<uint32_t> edges_summary;
};
bool types_abstractly_equal(iterator_df<type_die> t1, iterator_df<type_die> t2);
std::ostream& print_type_abstract_name(std::ostream& s, iterator_df<type_die> t);
string abstract_name_for_type(iterator_df<type_die> t);
opt<uint32_t> summary_code_for_type(iterator_df<type_die> t);
opt<uint16_t> containment_summary_code_for_type(iterator_df<type_die> t);
opt<uint16_t> traversal_summary_code_for_type(iterator_df<type_die> t);
begin_class(type, base_initializations(initialize_base(program_element)), declare_base(program_element))
attr_optional(byte_size, unsigned)
mutable opt<uint32_t> cached_summary_code;
protected:
string arbitrary_name() const;
template <typename BaseType> opt<BaseType> containment_summary_code(
/* Take an argument for the recursive call. By default it's unset. */
std::function<opt<BaseType>(iterator_df<type_die>)> recursive_call
= std::function<opt<BaseType>(iterator_df<type_die>)>()
) const;
opt<uint16_t> traversal_summary_code() const;
friend opt<uint16_t> containment_summary_code_for_type(iterator_df<type_die> t);
friend opt<uint16_t> traversal_summary_code_for_type(iterator_df<type_die> t);
public:
mutable optional<shared_ptr<type_scc_t> > opt_cached_scc; // HACK: should be private, but test-scc needs it
virtual opt<Dwarf_Unsigned> calculate_byte_size() const;
// virtual bool is_rep_compatible(iterator_df<type_die> arg) const;
virtual iterator_df<type_die> get_concrete_type() const;
virtual iterator_df<type_die> get_unqualified_type() const;
virtual bool abstractly_equals(core::iterator_df<core::type_die> t) const;
virtual std::ostream& print_abstract_name(std::ostream& s) const ;
virtual opt<type_scc_t> get_scc() const;
virtual opt<uint32_t> summary_code() const;
/* FIXME: temporary side-by-side impls while we compare / bug-fix. */
template <typename BaseType>
opt<BaseType> combined_summary_code_using_iterators() const;
virtual opt<uint32_t> summary_code_using_old_method() const;
virtual opt<uint32_t> summary_code_using_walk_type() const;
virtual bool may_equal(core::iterator_df<core::type_die> t,
const std::set< std::pair<core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
bool equal(core::iterator_df<core::type_die> t,
const std::set< std::pair<core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
bool operator==(const dwarf::core::type_die& t) const;
end_class(type)
struct type_edge : public pair< pair<iterator_df<type_die>, iterator_df<program_element_die> >, iterator_df<type_die> >
{
using pair::pair;
using pair::operator=;
const iterator_df<type_die>& source() const { return first.first; }
iterator_df<type_die>& source() { return first.first; }
const iterator_df<program_element_die>& label() const { return first.second; }
iterator_df<program_element_die>& label() { return first.second; }
const iterator_df<program_element_die>& reason() const { return first.second; }
iterator_df<program_element_die>& reason() { return first.second; }
bool reason_is_traversal() const;
const iterator_df<type_die>& target() const { return second; }
iterator_df<type_die>& target() { return second; }
};
/* "Type iterators" actually walk *edges* in the type DIE graph,
* not types per se: the target DIE of an edge is the iterator's
* "position" if you dereference it, and the DIE which models the
* edge itself is the "reason". The source edge is implied by the
* reason, e.g. for a member_die it's the containing structure;
* for a formal_parameter_die it's the containing subprogram type,
* etc..
* If you walk a type, you get the edges in the depth-first traversal,
* without the back-edges. What if you want to know about the back-edges?
* Bit of a HACK: I have added the "back_edges_here()" method which
* gives you a vector of (reason, target) pairs. We use this when
* computing the SCC of a type DIE.
*
* Another wart: we have type_iterator_df which avoids repeat visits
* to any node; and we have type_iterator_edge_df which will visit
* the same node as many times as there are distinct-labelled incoming
* edges to it. */
struct type_iterator_df_base : public iterator_base
{
typedef type_iterator_df_base self;
friend class boost::iterator_core_access;
/* The stack records the grey nodes. The back of the stack
* may or may not be our current position. */
deque< pair<iterator_df<type_die>, iterator_df<program_element_die> > > m_stack;
struct black_offsets_set_t : std::unordered_set<Dwarf_Off>
{
bool contains(const iterator_base& i) const
{
return this->find(i.is_end_position() ? (Dwarf_Off)-1 : i.offset_here())
!= this->end();
}
void insert(const iterator_base& i)
{
this->unordered_set::insert(i.is_end_position() ? (Dwarf_Off)-1 : i.offset_here());
}
using unordered_set::unordered_set;
} black_offsets;
iterator_df<program_element_die> m_reason;
iterator_base& base_reference()
{ return static_cast<iterator_base&>(*this); }
const iterator_base& base() const
{ return static_cast<const iterator_base&>(*this); }
type_iterator_df_base() : iterator_base() {}
type_iterator_df_base(const iterator_base& arg)
: iterator_base(arg) {}// this COPIES so avoid
type_iterator_df_base(iterator_base&& arg)
: iterator_base(std::move(arg)) {} // FIXME: use std::move here, and test
type_iterator_df_base(const self& arg)
: iterator_base(arg), m_stack(arg.m_stack),
black_offsets(arg.black_offsets), m_reason(arg.m_reason) {}
type_iterator_df_base(self&& arg)
: iterator_base(arg), m_stack(std::move(arg.m_stack)),
black_offsets(std::move(arg.black_offsets)), m_reason(std::move(arg.m_reason)) {}
bool is_grey(const iterator_base& i) const
{
return !black_offsets.contains(i)
&& std::find_if(m_stack.begin(), m_stack.end(),
[i](const pair<iterator_df<type_die>, iterator_df<program_element_die> >& pair)
{ return pair.first == i; }
) != m_stack.end();
}
bool pos_is_grey() const { return is_grey(base()); }
//bool is_latest_grey(const iterator_base& i) const
//{ return i == m_stack.back().first; }
//bool pos_is_latest_grey() const { return is_latest_grey(base()); }
// for if we moved (for edge-reflecting purposes) to a non-latest-grey position...
bool is_black(const iterator_base& i) const
{ return black_offsets.contains(i); }
bool pos_is_black() const
{ return is_black(base()); }
bool pos_is_white() const { return !pos_is_black() && !pos_is_grey(); }
enum colour { WHITE, GREY, BLACK };
colour colour_of(const iterator_base& i) const
{ return is_black(i) ? BLACK : is_grey(i) ? GREY : WHITE; }
colour pos_colour() const { return colour_of(base()); }
self& operator=(const iterator_base& arg) // assign fresh from an iterator; starts white
{ this->base_reference() = arg;
m_stack.clear();
this->black_offsets.clear(); // = std::move(std::set<Dwarf_Off>());
this->m_reason = END;
return *this; }
self& operator=(iterator_base&& arg)
{ this->base_reference() = std::move(arg);
m_stack.clear();
black_offsets.clear(); // = std::move(std::set<Dwarf_Off>());
this->m_reason = END;
return *this; }
self& operator=(const self& arg)
{ self tmp(arg);
std::swap(this->base_reference(), tmp.base_reference());
std::swap(this->m_stack, tmp.m_stack);
std::swap(this->black_offsets, tmp.black_offsets);
std::swap(this->m_reason, tmp.m_reason);
return *this; }
self& operator=(self&& arg)
{ this->base_reference() = std::move(arg);
this->m_stack = std::move(arg.m_stack);
this->black_offsets = std::move(arg.black_offsets);
this->m_reason = std::move(arg.m_reason);
return *this; }
iterator_df<program_element_die> reason() const
{ return m_reason; }
protected: /* helpers */
iterator_df<type_die> source_vertex_for(const iterator_df<type_die>&, const iterator_df<program_element_die>& p) const;
friend class type_die;
public:
iterator_df<type_die> source_vertex() const
{ return source_vertex_for(base(), reason()); }
iterator_df<program_element_die> edge_label() const { return reason(); }
type_edge as_incoming_edge() const
{
return make_pair(
make_pair(
source_vertex(),
edge_label()
),
*this
);
}
enum edge_kind { TREE, BACK, CROSS };
edge_kind incoming_edge_kind() const
{
if (pos_is_white()) return TREE;
if (pos_is_grey()) return BACK;
assert(pos_is_black()); return CROSS;
// if we're walking an incoming edge to any node, that node is not white
}
/* Since we want to walk "void", which has no representation,
* we're only at the end if we're both "no DIE" and "no reason". */
operator bool() const { return !(!this->base() && !this->reason()); }
/* Primitive operations that our subclasses will use. */
type_die& dereference() const
{ return dynamic_cast<type_die&>(this->iterator_base::dereference()); }
/* Nobody implements this. */
void decrement();
/* "go deeper" means find a white or black successor of the current node.
-- if it's black, we may or may not want to reexplore */
pair<iterator_df<type_die>, iterator_df<program_element_die> >
first_outgoing_edge_target() const;
/* "go sideways" means find a white or black successor of the grey
* predecessor of the current node.
* -- there is only one grey predecessor, by construction (depth-first)
* -- the current node become black when we do this.
*/
pair<iterator_df<type_die>, iterator_df<program_element_die> >
predecessor_node_next_outgoing_edge_target() const;
/* "backtrack" means find a white or black successor of the next grey predecessor,
* i.e. iterate the go-sideways thing one grey node up the stack.
* The client can open-code this. */
/* What about a nice interface to back edges and cross edges?
* Since the edge iterator will follow these -- it just won't
* push them on the stack -- we don't need a separate interface. */
void increment_not_back(bool skip_dependencies = false);
void increment_to_unseen_edge();
void increment_to_unseen_node();
};
/* The idea of this one is simply to do the same thing as walk_type,
* for better or worse. */
struct type_iterator_df_walk : public type_iterator_df_base,
public boost::iterator_facade<
type_iterator_df_walk
, type_die
, boost::forward_traversal_tag
, type_die& /* Reference */
, Dwarf_Signed /* difference */
>
{
typedef type_iterator_df_walk self;
friend class boost::iterator_core_access;
using type_iterator_df_base::type_iterator_df_base;
void increment(bool skip_dependencies = false) { increment_not_back(skip_dependencies); }
void increment_skipping_dependencies() { increment(true); }
};
struct type_iterator_df_edges : public type_iterator_df_base,
public boost::iterator_facade<
type_iterator_df_edges
, type_die
, boost::forward_traversal_tag
, type_die& /* Reference */
, Dwarf_Signed /* difference */
>
{
typedef type_iterator_df_edges self;
friend class boost::iterator_core_access;
type_iterator_df_edges(const iterator_base& arg)
{ base_reference() = arg; m_reason = END; }
type_iterator_df_edges(iterator_base&& arg)
{ base_reference() = std::move(arg); m_reason = END; }
type_iterator_df_edges(const self& arg)
: type_iterator_df_base(arg) {}
type_iterator_df_edges(self&& arg) : type_iterator_df_base(std::move(arg)) {}
void increment();
};
struct type_iterator_outgoing_edges : public type_iterator_df_base,
public boost::iterator_facade<
type_iterator_outgoing_edges
, type_die
, boost::forward_traversal_tag
, type_die& /* Reference */
, Dwarf_Signed /* difference */
>
{
typedef type_iterator_outgoing_edges self;
typedef type_iterator_df_edges super;
private:
void init()
{
m_stack.clear();
black_offsets.clear();
// we have nowhere to pop to, so if no outgoing edges, will hit END
if (this->operator bool()) this->increment_to_unseen_edge();
}
public:
type_iterator_outgoing_edges()
: type_iterator_df_base() {}
explicit type_iterator_outgoing_edges(const type_iterator_df_base& arg)
: type_iterator_df_base(arg) { init(); }
explicit type_iterator_outgoing_edges(type_iterator_df_base&& arg)
: type_iterator_df_base(std::move(arg)) { init(); }
type_iterator_outgoing_edges(const self& arg)
: type_iterator_df_base(arg) { /* don't init */ }
type_iterator_outgoing_edges(self&& arg) : type_iterator_df_base(std::move(arg))
{ /* don't init */ }
explicit type_iterator_outgoing_edges(
const deque< pair<iterator_df<type_die>, iterator_df<program_element_die> > >& stack)
{ /* This constructor means
* "the stack position is black; start from the predecessor's next outgoing edge". */
this->base_reference() = stack.back().first;
this->m_reason = stack.back().second;
this->m_stack.clear();
// don't need stack for next outgoing edge
auto found_next = predecessor_node_next_outgoing_edge_target();
if (found_next.first || found_next.second)
{
this->base_reference() = std::move(found_next.first);
this->m_reason = std::move(found_next.second);
}
else
{
this->base_reference() = END;
this->m_reason = END;
}
}
// assigning from any DF iterator will walk its children?
// NO; this would be confusing (changes position)
// we made the constructors above "explicit" for the same reason
// self& operator=(const type_iterator_df_base& arg)
// { static_cast<type_iterator_df_base&>(*this)
// = static_cast<const type_iterator_df_base&>(arg);
// init();
// return *this; }
// self& operator=(type_iterator_df_base&& arg)
// { static_cast<type_iterator_df_base&&>(*this)
// = static_cast<type_iterator_df_base&&>(arg);
// init();
// return *this; }
// assigning from ourselves will just copy-assign -- OK
self& operator=(const self& arg)
{ static_cast<type_iterator_df_base&>(*this)
= static_cast<const type_iterator_df_base&>(arg);
return *this; }
self& operator=(self&& arg)
{ static_cast<type_iterator_df_base&&>(*this)
= static_cast<type_iterator_df_base&&>(arg);
return *this; }
//type_iterator_outgoing_edges() : type_iterator_df_edges() { init(); }
//type_iterator_outgoing_edges(const type_iterator_df_base& arg)
// : type_iterator_df_edges(arg) { init(); }// this COPIES so avoid
//type_iterator_outgoing_edges(type_iterator_df_edges&& arg)
// : type_iterator_df_edges(std::move(arg)) { init(); }
//type_iterator_outgoing_edges(const self& arg)
// : type_iterator_df_edges(arg) { /* don't init */ }
//type_iterator_outgoing_edges(self&& arg)
// : type_iterator_df_edges(std::move(arg)) { /* don't init */ }
/* Basic idea: we're an iterator that yields each immediate successor
* of the start type_die. Our initial stack consists of that DIE
* and the null reason. The first thing we do is increment our position,
* moving us to the first outgoing edge's target, if any. */
void increment()
{
auto found_next = predecessor_node_next_outgoing_edge_target();
this->base_reference() = std::move(found_next.first);
this->m_reason = std::move(found_next.second);
}
};
/* type_set and related utilities. */
size_t type_hash_fn(iterator_df<type_die> t);
bool type_eq_fn(iterator_df<type_die> t1, iterator_df<type_die> t2);
struct type_set : public unordered_set<
/* Key */ iterator_df<type_die>,
/* Hash */ std::function<size_t(iterator_df<type_die>)>,
/* Equal */ std::function<bool(iterator_df<type_die> t1, iterator_df<type_die> t2)>
>
{
type_set() : unordered_set({}, 0, type_hash_fn, type_eq_fn) {}
};
typedef set< opt<Dwarf_Off> > dieloc_set;
template <typename Value>
struct type_map : public unordered_map<
/* Key */ iterator_df<type_die>,
Value,
/* Hash */ std::function<size_t(iterator_df<type_die>)>,
/* Equal */ std::function<bool(iterator_df<type_die> t1, iterator_df<type_die> t2)>
>
{
typedef unordered_map<
/* Key */ iterator_df<type_die>,
Value,
/* Hash */ std::function<size_t(iterator_df<type_die>)>,
/* Equal */ std::function<bool(iterator_df<type_die> t1, iterator_df<type_die> t2)>
> super;
type_map() : super({}, 0, type_hash_fn, type_eq_fn) {}
};
void walk_type(core::iterator_df<core::type_die> t,
core::iterator_df<core::program_element_die> origin,
const std::function<bool(core::iterator_df<core::type_die>, core::iterator_df<core::program_element_die>)>& pre_f,
const std::function<void(core::iterator_df<core::type_die>, core::iterator_df<core::program_element_die>)>& post_f
= std::function<void(core::iterator_df<core::type_die>, core::iterator_df<core::program_element_die>)>(),
const dieloc_set& currently_walking = dieloc_set());
/* with_type_describing_layout_die */
struct with_type_describing_layout_die : public virtual program_element_die
{
virtual opt<iterator_df<type_die> > get_type() const = 0;
virtual opt<iterator_df<type_die> > find_type() const = 0;
};
/* with_dynamic_location_die */
struct with_dynamic_location_die : public virtual with_type_describing_layout_die
{
virtual opt<Dwarf_Off> spans_addr(
Dwarf_Addr absolute_addr,
Dwarf_Signed instantiating_instance_addr,
root_die& r,
Dwarf_Off dieset_relative_ip,
dwarf::expr::regs *p_regs = 0) const = 0;
/* We define two variants of the spans_addr logic:
* one suitable for stack-based locations (fp/variable)
* and another for object-based locations (member/inheritance)
* and each derived class should pick one! */
protected:
opt<Dwarf_Off> spans_stack_addr(
Dwarf_Addr absolute_addr,
Dwarf_Signed instantiating_instance_addr,
root_die& r,
Dwarf_Off dieset_relative_ip,
dwarf::expr::regs *p_regs = 0) const;
opt<Dwarf_Off> spans_addr_in_object(
Dwarf_Addr absolute_addr,
Dwarf_Signed instantiating_instance_addr,
root_die& r,
Dwarf_Off dieset_relative_ip,
dwarf::expr::regs *p_regs = 0) const;
public:
virtual iterator_df<program_element_die>
get_instantiating_definition() const;
virtual Dwarf_Addr calculate_addr(
Dwarf_Addr instantiating_instance_location,
root_die& r,
Dwarf_Off dieset_relative_ip,
dwarf::expr::regs *p_regs = 0) const = 0;
/** This gets a location list describing the location of the thing,
assuming that the instantiating_instance_location has been pushed
onto the operand stack. */
virtual encap::loclist get_dynamic_location() const = 0;
protected:
/* ditto */
virtual Dwarf_Addr calculate_addr_on_stack(
Dwarf_Addr instantiating_instance_location,
root_die& r,
Dwarf_Off dieset_relative_ip,
dwarf::expr::regs *p_regs = 0) const;
virtual Dwarf_Addr calculate_addr_in_object(
Dwarf_Addr instantiating_instance_location,
root_die& r,
Dwarf_Off dieset_relative_ip,
dwarf::expr::regs *p_regs = 0) const;
public:
virtual bool location_requires_object_base() const = 0;
/* virtual Dwarf_Addr calculate_addr(
Dwarf_Signed frame_base_addr,
Dwarf_Off dieset_relative_ip,
dwarf::expr::regs *p_regs = 0) const;*/
};
#define has_stack_based_location \
bool location_requires_object_base() const { return false; } \
opt<Dwarf_Off> spans_addr( \
Dwarf_Addr aa, \
Dwarf_Signed fb, \
root_die& r, \
Dwarf_Off dr_ip, \
dwarf::expr::regs *p_regs) const \
{ return spans_stack_addr(aa, fb, r, dr_ip, p_regs); } \
Dwarf_Addr calculate_addr( \
Dwarf_Addr fb, \
root_die& r, \
Dwarf_Off dr_ip, \
dwarf::expr::regs *p_regs = 0) const \
{ return calculate_addr_on_stack(fb, r, dr_ip, p_regs); } \
encap::loclist get_dynamic_location() const;
#define has_object_based_location \
bool location_requires_object_base() const { return true; } \
opt<Dwarf_Off> spans_addr( \
Dwarf_Addr aa, \
Dwarf_Signed io, \
root_die& r, \
Dwarf_Off dr_ip, \
dwarf::expr::regs *p_regs) const \
{ return spans_addr_in_object(aa, io, r, dr_ip, p_regs); } \
Dwarf_Addr calculate_addr( \
Dwarf_Addr io, \
root_die& r, \
Dwarf_Off dr_ip, \
dwarf::expr::regs *p_regs = 0) const \
{ return calculate_addr_in_object(io, r, dr_ip, p_regs); } \
encap::loclist get_dynamic_location() const;
/* data_member_die */
begin_class(data_member, base_initializations(initialize_base(with_dynamic_location)), declare_base(with_dynamic_location))
has_object_based_location
attr_optional(data_member_location, loclist)
virtual iterator_df<type_die> find_or_create_type_handling_bitfields() const;
/** This gets an offset in an enclosing object. */
virtual opt<Dwarf_Unsigned> byte_offset_in_enclosing_type(bool assume_packed_if_no_location = false) const;
end_class(data_member)
/* type_chain_die */
begin_class(type_chain, base_initializations(initialize_base(type)), declare_base(type))
attr_optional(type, refiter_is_type)
opt<Dwarf_Unsigned> calculate_byte_size() const;
iterator_df<type_die> get_concrete_type() const;
bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
end_class(type_chain)
/* type_describing_subprogram_die */
begin_class(type_describing_subprogram, base_initializations(initialize_base(type)), declare_base(type))
attr_optional(type, refiter_is_type)
virtual iterator_df<type_die> get_return_type() const = 0;
virtual bool is_variadic() const;
bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
bool abstractly_equals(iterator_df<type_die> t) const;
std::ostream& print_abstract_name(std::ostream& s) const;
end_class(type_describing_subprogram)
/* address_holding_type_die */
begin_class(address_holding_type, base_initializations(initialize_base(type_chain)), declare_base(type_chain))
attr_optional(address_class, unsigned)
iterator_df<type_die> get_concrete_type() const;
opt<Dwarf_Unsigned> calculate_byte_size() const;
bool abstractly_equals(iterator_df<type_die> t) const;
pair<unsigned, iterator_df<type_die> > get_ultimate_reached_type() const;
pair<unsigned, iterator_df<type_die> > find_ultimate_reached_type() const;
std::ostream& print_abstract_name(std::ostream& s) const;
end_class(type_chain)
/* qualified_type_die */
begin_class(qualified_type, base_initializations(initialize_base(type_chain)), declare_base(type_chain))
iterator_df<type_die> get_unqualified_type() const;
end_class(qualified_type)
/* with_data_members_die */
begin_class(with_data_members, base_initializations(initialize_base(type)), declare_base(type))
child_tag(member)
protected:
mutable optional< iterator_df<> > maybe_cached_definition;
// really use boost::optional, to distinguish "cached END" from "no cache"
public:
iterator_base find_definition() const; // for turning declarations into defns
bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
bool abstractly_equals(iterator_df<type_die> t) const;
std::ostream& print_abstract_name(std::ostream& s) const;
end_class(with_data_members)
#define extra_decls_subprogram \
opt< std::pair<Dwarf_Off, iterator_df<with_dynamic_location_die> > > \
spans_addr_in_frame_locals_or_args( \
Dwarf_Addr absolute_addr, \
root_die& r, \
Dwarf_Off dieset_relative_ip, \
Dwarf_Signed *out_frame_base, \
dwarf::expr::regs *p_regs = 0) const; \
iterator_df<type_die> get_return_type() const;
#define extra_decls_variable \
bool has_static_storage() const; \
has_stack_based_location
#define extra_decls_formal_parameter \
has_stack_based_location
#define extra_decls_array_type \
opt<Dwarf_Unsigned> element_count() const; \
vector<opt<Dwarf_Unsigned> > dimension_element_counts() const; \
opt<Dwarf_Unsigned> calculate_byte_size() const; \
/* bool is_rep_compatible(iterator_df<type_die> arg) const; */ \
iterator_df<type_die> ultimate_element_type() const; \
opt<Dwarf_Unsigned> ultimate_element_count() const; \
bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; \
iterator_df<type_die> get_concrete_type() const; \
bool abstractly_equals(iterator_df<type_die> t) const; \
std::ostream& print_abstract_name(std::ostream& s) const;
#define extra_decls_string_type \
bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; \
opt<Dwarf_Unsigned> fixed_length_in_bytes() const; \
opt<encap::loclist> dynamic_length_in_bytes() const; \
bool abstractly_equals(iterator_df<type_die> t) const; \
std::ostream& print_abstract_name(std::ostream& s) const; \
opt<Dwarf_Unsigned> calculate_byte_size() const;
#define extra_decls_pointer_type \
/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_reference_type \
/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_base_type \
bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; \
opt<Dwarf_Unsigned> calculate_byte_size() const; \
pair<Dwarf_Unsigned, Dwarf_Unsigned> bit_size_and_offset() const; \
bool is_bitfield_type() const; \
bool abstractly_equals(iterator_df<type_die> t) const; \
std::ostream& print_abstract_name(std::ostream& s) const; \
string get_canonical_name() const; \
static string canonical_name_for(spec& spec, unsigned encoding, \
unsigned byte_size, unsigned bit_size, unsigned bit_offset);
/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_structure_type \
opt<Dwarf_Unsigned> calculate_byte_size() const; \
/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_union_type \
/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_class_type \
/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_enumeration_type \
bool abstractly_equals(iterator_df<type_die> t) const; \
std::ostream& print_abstract_name(std::ostream& s) const; \
bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; \
/* bool is_rep_compatible(iterator_df<type_die> arg) const; */
#define extra_decls_subrange_type \
bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const; \
bool abstractly_equals(iterator_df<type_die> t) const; \
std::ostream& print_abstract_name(std::ostream& s) const;
#define extra_decls_set_type \
bool abstractly_equals(iterator_df<type_die> t) const; \
std::ostream& print_abstract_name(std::ostream& s) const;
#define extra_decls_file_type \
bool abstractly_equals(iterator_df<type_die> t) const; \
std::ostream& print_abstract_name(std::ostream& s) const;
#define extra_decls_ptr_to_member_type \
bool abstractly_equals(iterator_df<type_die> t) const; \
std::ostream& print_abstract_name(std::ostream& s) const; \
bool may_equal(core::iterator_df<core::type_die> t, const std::set< std::pair< core::iterator_df<core::type_die>, core::iterator_df<core::type_die> > >& assuming_equal) const;
#define extra_decls_member \
iterator_df<type_die> find_or_create_type_handling_bitfields() const;
#define extra_decls_subroutine_type \
/* bool is_rep_compatible(iterator_df<type_die> arg) const; */ \
core::iterator_df<core::type_die> get_return_type() const;
#define extra_decls_unspecified_type \
std::ostream& print_abstract_name(std::ostream& s) const;
#define extra_decls_compile_unit \
/* We define fields and getters for the per-CU source file info */ \
/* (which is *separate* from decl_file and decl_line attributes). */ \
public: \
inline std::string source_file_name(unsigned o) const; \
opt<std::string> source_file_fq_pathname(unsigned o) const; \
inline unsigned source_file_count() const; \
/* We define fields and getters for the per-CU info (NOT attributes) */ \
/* available from libdwarf. These will be filled in by root_die::make_payload(). */ \
protected: \
Dwarf_Unsigned cu_header_length; \
Dwarf_Half version_stamp; \
Dwarf_Unsigned abbrev_offset; \
Dwarf_Half address_size; \
Dwarf_Half offset_size; \
Dwarf_Half extension_size; \
Dwarf_Unsigned next_cu_header; \
public: \
Dwarf_Unsigned get_cu_header_length() const { return cu_header_length; } \
Dwarf_Half get_version_stamp() const { return version_stamp; } \
Dwarf_Unsigned get_abbrev_offset() const { return abbrev_offset; } \
Dwarf_Half get_address_size() const { return address_size; } \
Dwarf_Half get_offset_size() const { return offset_size; } \
Dwarf_Half get_extension_size() const { return extension_size; } \
Dwarf_Unsigned get_next_cu_header() const { return next_cu_header; } \
opt<Dwarf_Unsigned> implicit_array_base() const; \
mutable iterator_df<type_die> cached_implicit_enum_base_type; \
iterator_df<type_die> implicit_enum_base_type() const; \
mutable iterator_df<type_die> cached_implicit_subrange_base_type; \
iterator_df<type_die> implicit_subrange_base_type() const; \
encap::rangelist normalize_rangelist(const encap::rangelist& rangelist) const; \
bool is_generic_pointee_type(iterator_df<type_die> t) const; \
friend class iterator_base; \
friend class factory;
#include "dwarf-current-adt.h"
#undef extra_decls_subprogram
#undef extra_decls_compile_unit
#undef extra_decls_array_type
#undef extra_decls_string_type
#undef extra_decls_variable
#undef extra_decls_structure_type
#undef extra_decls_union_type
#undef extra_decls_class_type
#undef extra_decls_enumeration_type
#undef extra_decls_subroutine_type
#undef extra_decls_member
#undef extra_decls_inheritance
#undef extra_decls_pointer_type
#undef extra_decls_reference_type
#undef extra_decls_base_type
#undef extra_decls_formal_parameter
#undef extra_decls_set_type
#undef extra_decls_file_type
#undef extra_decls_ptr_to_member_type
#undef extra_decls_unspecified_type
#undef extra_decls_subrange_type
#undef has_stack_based_location
#undef has_object_based_location
#undef forward_decl
#undef declare_base
#undef declare_bases
#undef base_fragment
#undef initialize_base
#undef constructor
#undef begin_class
#undef base_initializations
#undef declare_base
#undef end_class
#undef stored_type
#undef stored_type_string
#undef stored_type_flag
#undef stored_type_unsigned
#undef stored_type_signed
#undef stored_type_offset
#undef stored_type_half
#undef stored_type_ref
#undef stored_type_tag
#undef stored_type_loclist
#undef stored_type_address
#undef stored_type_refiter
#undef stored_type_refiter_is_type
#undef stored_type_rangelist
#undef attr_optional
#undef attr_mandatory
#undef super_attr_optional
#undef super_attr_mandatory
#undef child_tag
/****************************************************************/
/* end generated ADT includes */
/****************************************************************/
}
}
#endif