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

509 lines
16 KiB
C++

/* dwarfpp: C++ binding for a useful subset of libdwarf, plus extra goodies.
*
* spec.hpp: definitions describing DWARF standards and vendor extensions.
*
* Copyright (c) 2008--17, Stephen Kell. For licensing information, see the
* LICENSE file in the root of the libdwarfpp tree.
*/
#ifndef DWARFPP_SPEC_HPP_
#define DWARFPP_SPEC_HPP_
#include "dwarfpp/util.hpp"
#include <map>
#include <string>
#include <utility>
#include <cstdlib>
#include <iostream>
#include <cassert>
#include <algorithm>
#include <type_traits>
/* Basic idea of this file:
*
* - every variant of the standard gets its own namespace
under dwarf::spec::
* - each such namespace defines a bunch of maps
* - we instantiate a table_def using these maps *and*, optionally, an extended def
* - these maps only record the increment over the extended def
* - we build the final abstract_def object
as a singleton of a fresh class (so it can get its own code, inlined etc..)
built CRTP-wise as an extension of a predecessor standard
* - each such class includes (as static consts) its tables as std::maps
* FIXME: get rid of const methods in abstract_def?
* Or will that stop us from passing them by ref?
* I don't think the ref-to-temporary case is important, so no. Get rid!
*/
namespace dwarf
{
namespace spec
{
using std::string;
using dwarf::core::debug;
extern "C"
{
#include "dwarf-onlystd.h"
}
struct abstract_def
{
virtual const char *tag_lookup(int tag) const = 0;
virtual int tag_for_name(const char *name) const = 0;
virtual bool tag_is_type(int tag) const = 0;
virtual bool tag_is_type_chain(int tag) const = 0;
virtual bool tag_has_named_children(int tag) const = 0;
virtual const char *attr_lookup(int attr) const = 0;
virtual int attr_for_name(const char *name) const = 0;
virtual const int *attr_get_classes(int attr) const = 0;
virtual bool attr_describes_location(int attr) const = 0;
virtual const char *form_lookup(int form) const = 0;
virtual int form_for_name(const char *name) const = 0;
virtual const int *form_get_classes(int form) const = 0;
virtual const char *encoding_lookup(int encoding) const = 0;
virtual int encoding_for_name(const char *name) const = 0;
virtual const char *op_lookup(int op) const = 0;
virtual int op_for_name(const char *name) const = 0;
virtual bool op_reads_register(int op) const = 0;
virtual size_t op_operand_count(int op) const = 0;
virtual const int *op_operand_form_list(int op) const = 0;
virtual int get_explicit_interp(int attr, int form) const = 0;
virtual int get_interp(int attr, int form) const = 0;
virtual const char *interp_lookup(int interp) const = 0;
friend std::ostream& operator<<(std::ostream& o, const abstract_def& a);
virtual std::ostream& print(std::ostream& o) const = 0;
virtual ~abstract_def() {}
};
typedef abstract_def spec;
struct string_comparator
{
bool operator()(const char *arg1, const char *arg2) const
{
return string(arg1) < string(arg2);
}
};
#define DECLARE_MAPS \
static const std::map<const char *, int, string_comparator> tag_forward_map; \
static const std::map<int, const char *> tag_inverse_map; \
static const std::map<const char *, int, string_comparator> form_forward_map; \
static const std::map<int, const char *> form_inverse_map; \
static const std::map<const char *, int, string_comparator> attr_forward_map; \
static const std::map<int, const char *> attr_inverse_map; \
static const std::map<const char *, int, string_comparator> encoding_forward_map; \
static const std::map<int, const char *> encoding_inverse_map; \
static const std::map<const char *, int, string_comparator> op_forward_map; \
static const std::map<int, const char *> op_inverse_map; \
static const std::map<const char *, int, string_comparator> interp_forward_map; \
static const std::map<int, const char *> interp_inverse_map; \
static const std::map<int, const int *> op_operand_forms_map; \
static const std::map<int, const int *> attr_class_map; \
static const std::map<int, const int *> form_class_map;
#define DECLARE_BOILERPLATE(typename) \
static typename inst; \
std::ostream& print(std::ostream& o) { \
/*return print< typename >(o); */ return o; \
}
#define DECLARE_LOCAL_PREDS \
bool local_tag_is_type(int tag) const; \
bool local_tag_is_type_chain(int tag) const; \
bool local_tag_has_named_children(int tag) const; \
bool local_attr_describes_location(int attr) const; \
bool local_op_reads_register(int op) const;
template <class DefWithMaps>
std::ostream& print(std::ostream& o);
/* DWARF defines a set of "classes" which determine how attributes
* may be interpreted, in a fashion not fully determined by their
* FORM (storage). */
class interp
{
interp() {} // non-instantiable -- interp just defines a namespace
public:
enum cls
{
EOL = 0,
reference,
block,
loclistptr,
string,
constant,
lineptr,
address,
flag,
macptr,
rangelistptr,
exprloc,
block_as_dwarf_expr = 0x20,
constant_to_make_location_expr,
FLAGS = 0x7f000000,
UNSIGNED = 0x01000000,
SIGNED = 0x02000000
};
};
class empty_def_t : public virtual abstract_def
{
static const int empty_operand_form_list[];
static const int empty_class_list[];
public:
DECLARE_MAPS
DECLARE_BOILERPLATE(empty_def_t)
/* Override all the lookups to print a warning
* if we hit them, because it means a lookup
* has failed. We also don't bother to consult
* our empty maps. */
virtual const char *tag_lookup(int tag) const
{ debug() << "Saw unknown tag 0x" << std::hex << tag << std::dec << std::endl;
return "(unknown tag)"; }
virtual int tag_for_name(const char *name) const
{
debug() << "Saw unknown tag name "<< name << std::endl;
return -1;
}
virtual const char *attr_lookup(int attr) const
{
debug() << "Saw unknown attr 0x" << std::hex << attr << std::dec << std::endl;
return "(unknown attribute)";
}
virtual int attr_for_name(const char *name) const
{
debug() << "Saw unknown attr name "<< name << std::endl;
return -1;
}
virtual const char *form_lookup(int form) const
{
debug() << "Saw unknown form 0x" << std::hex << form << std::dec << std::endl;
return "(unknown form)";
}
virtual int form_for_name(const char *name) const
{
debug() << "Saw unknown form name "<< name << std::endl;
return -1;
}
virtual const char *encoding_lookup(int encoding) const
{
debug() << "Saw unknown encoding 0x" << std::hex << encoding << std::dec << std::endl;
return "(unknown encoding)";
}
virtual int encoding_for_name(const char *name) const
{
debug() << "Saw unknown encoding name "<< name << std::endl;
return -1;
}
virtual const char *op_lookup(int op) const
{
debug() << "Saw unknown opcode 0x" << std::hex << op << std::dec << std::endl;
return "(unknown opcode)";
}
virtual int op_for_name(const char *name) const
{
debug() << "Saw unknown op name "<< name << std::endl;
return -1;
}
virtual const char *interp_lookup(int interp) const
{ return "(unknown class)"; }
/* Define our preds trivially. */
virtual bool tag_is_type(int tag) const
{ return false; }
virtual bool tag_is_type_chain(int tag) const
{ return false; }
virtual bool tag_has_named_children(int tag) const
{ return false; }
virtual bool attr_describes_location(int attr) const
{ return false; }
virtual bool op_reads_register(int op) const { return false; }
/* Define our empty lists. */
virtual const int *attr_get_classes(int attr) const
{ return empty_class_list; }
virtual size_t op_operand_count(int op) const { return 0; }
virtual const int *op_operand_form_list(int op) const
{ return empty_operand_form_list; }
virtual const int *form_get_classes(int form) const
{ return empty_class_list; }
virtual int get_explicit_interp(int attr, int form) const { return interp::EOL; }
virtual int get_interp(int attr, int form) const { return interp::EOL; }
virtual std::ostream& print(std::ostream& o) const
{
o << "Tags:\nAttributes:\nForms:\nEncodings:\nOperators:\nInterpretations:\nAttribute classes:\nForm classes:\n";
return o;
}
};
extern empty_def_t empty_def;
template <typename Extended, typename Extending>
struct extension_of : public virtual abstract_def
{
// try Extending's maps, else delegate to Extended's lookup method
/* Our maps use const char * but our methods take const string&,
* so we want to allow convertibility between these. This means that
* K (method signature) and MappedK (map type parameters)
* are not necessarily the same. */
template <typename K, typename V, typename Cmp>
V
map_union_lookup(
const std::map<K, V, Cmp>& m1,
V (Extended::*method)(K) const,
const K& k) const
{
typename std::map<
K,
V
>::const_iterator found1 = m1.find(k);
if (found1 != m1.end())
{
return found1->second;
} else return (Extended::inst.*method)(k);
}
const char *tag_lookup(int tag) const
{
return map_union_lookup(Extending::tag_inverse_map, &Extended::tag_lookup, tag);
}
int tag_for_name(const char *name) const
{
return map_union_lookup(Extending::tag_forward_map, &Extended::tag_for_name, name);
}
const char *attr_lookup(int attr) const
{
return map_union_lookup(Extending::attr_inverse_map, &Extended::attr_lookup, attr);
}
int attr_for_name(const char *name) const
{
return map_union_lookup(Extending::attr_forward_map, &Extended::attr_for_name, name);
}
const char *form_lookup(int form) const
{
return map_union_lookup(Extending::form_inverse_map, &Extended::form_lookup, form);
}
int form_for_name(const char *name) const
{
return map_union_lookup(Extending::form_forward_map, &Extended::form_for_name, name);
}
const char *encoding_lookup(int encoding) const
{
return map_union_lookup(Extending::encoding_inverse_map, &Extended::encoding_lookup, encoding);
}
int encoding_for_name(const char *name) const
{
return map_union_lookup(Extending::encoding_forward_map, &Extended::encoding_for_name, name);
}
const char *op_lookup(int op) const
{
return map_union_lookup(Extending::op_inverse_map, &Extended::op_lookup, op);
}
int op_for_name(const char *name) const
{
return map_union_lookup(Extending::op_forward_map, &Extended::op_for_name, name);
}
const char *interp_lookup(int interp) const
{
return map_union_lookup(Extending::interp_inverse_map, &Extended::interp_lookup, interp);
}
// union Extending's pred with Extended's
template <typename Arg>
bool
member_pred_union(bool(Extending::* pred1)(Arg) const, bool(Extended::* pred2)(Arg) const, const Arg& arg) const
{
return (Extending::inst.*pred1)(arg)
|| (Extended::inst.*pred2)(arg);
}
bool tag_is_type(int tag) const
{
return member_pred_union(&Extending::local_tag_is_type, &Extended::tag_is_type, tag);
}
bool tag_is_type_chain(int tag) const
{
return member_pred_union(&Extending::local_tag_is_type_chain, &Extended::tag_is_type_chain, tag);
}
bool tag_has_named_children(int tag) const
{
return member_pred_union(&Extending::local_tag_has_named_children, &Extended::tag_has_named_children, tag);
}
bool attr_describes_location(int attr) const
{
return member_pred_union(
&Extending::local_attr_describes_location,
&Extended::attr_describes_location,
attr);
}
bool op_reads_register(int op) const
{
return member_pred_union(&Extending::local_op_reads_register, &Extended::op_reads_register, op);
}
// lists
const int *attr_get_classes(int attr) const
{
return map_union_lookup(Extending::attr_class_map, &Extended::attr_get_classes, attr);
}
const int *form_get_classes(int form) const
{
return map_union_lookup(Extending::form_class_map, &Extended::form_get_classes, form);
}
size_t op_operand_count(int op) const
{
int count = 0;
std::map<int, const int *>::const_iterator found = Extending::inst.op_operand_forms_map.find(op);
if (found != Extending::inst.op_operand_forms_map.end())
{
// linear count-up
for (const int *p_form = found->second; *p_form != 0; p_form++) count++;
return count;
} else return Extended::inst.op_operand_count(op);
}
const int *op_operand_form_list(int op) const
{
return map_union_lookup(Extending::op_operand_forms_map, &Extended::op_operand_form_list, op);
}
int get_explicit_interp(int attr, int form) const
{
return explicit_interp(*const_cast<abstract_def*>(static_cast<const abstract_def*>(this)), attr, attr_get_classes(attr), form, form_get_classes(form));
}
int get_interp(int attr, int form) const
{
return get_explicit_interp(attr, form);
}
std::ostream& print(std::ostream& o) const
{
o << "Tags:\nAttributes:\nForms:\nEncodings:\nOperators:\nInterpretations:\nAttribute classes:\nForm classes:\n";
return o;
}
};
/* define DWARF4 as an extension of the empty def */
class dwarf4_t : public extension_of<empty_def_t, dwarf4_t >//,
{
public:
/* Q. How do we populate the maps?
* A. They're static const, so we define them out-of-line. */
DECLARE_MAPS
DECLARE_BOILERPLATE(dwarf4_t)
/* Q. How do we define our preds so that they don't hide
the ones we inherit (that call ours)?
* A. Prefix them with "local". */
DECLARE_LOCAL_PREDS
/* We also need to hack the get_interp logic a little,
* so declare that too. */
int get_interp(int attr, int form) const;
};
extern dwarf4_t& dwarf4;
/* define the 'head' DWARF as an extension of DWARF4. */
class dwarf_head_t: public extension_of<dwarf4_t, dwarf_head_t >,
public virtual abstract_def
{
public:
DECLARE_MAPS
DECLARE_BOILERPLATE(dwarf_head_t)
DECLARE_LOCAL_PREDS
};
extern dwarf_head_t dwarf_head;
namespace gnu
{
#include "dwarf-ext-GNU.h"
class dwarf4_plus_gnu_t : public extension_of<dwarf4_t, dwarf4_plus_gnu_t >,
public virtual abstract_def
{
public:
DECLARE_MAPS
DECLARE_BOILERPLATE(dwarf4_plus_gnu_t)
DECLARE_LOCAL_PREDS;
};
extern dwarf4_plus_gnu_t& dwarf4_plus_gnu;
}
// standalone helper
int explicit_interp(abstract_def& def, int attr, const int *attr_possible_classes, int form, const int *form_possible_classes);
void print_symmetric_map_pair(
std::ostream& o,
const std::map<const char *, int>& forward_map,
const std::map<int, const char *>& inverse_map);
template <class DefWithMaps>
std::ostream& print(std::ostream& o)
{
o << "Tags: " << std::endl;
print_symmetric_map_pair(o, DefWithMaps::tag_forward_map, DefWithMaps::tag_inverse_map);
o << "Attributes: " << std::endl;
print_symmetric_map_pair(o, DefWithMaps::attr_forward_map, DefWithMaps::attr_inverse_map);
o << "Forms: " << std::endl;
print_symmetric_map_pair(o, DefWithMaps::form_forward_map, DefWithMaps::form_inverse_map);
o << "Encodings: " << std::endl;
print_symmetric_map_pair(o, DefWithMaps::encoding_forward_map, DefWithMaps::encoding_inverse_map);
o << "Opcodes: " << std::endl;
print_symmetric_map_pair(o, DefWithMaps::op_forward_map, DefWithMaps::op_inverse_map);
o << "Interpretations: " << std::endl;
print_symmetric_map_pair(o, DefWithMaps::interp_forward_map, DefWithMaps::interp_inverse_map);
o << "Attribute classes: " << std::endl;
for (std::map<int, const int *>::const_iterator i = DefWithMaps::attr_class_tbl.begin();
i != DefWithMaps::attr_class_tbl.end();
i++)
{
o << DefWithMaps::attr_lookup(i->first) << ": ";
for (const int *p = i->second; *p != interp::EOL; p++)
{
o << DefWithMaps::interp_lookup(*p && ~interp::FLAGS);
if (*(p+1) != interp::EOL) o << ", ";
}
o << std::endl;
}
o << "Form classes: " << std::endl;
for (std::map<int, const int *>::const_iterator i = DefWithMaps::form_class_tbl.begin();
i != DefWithMaps::form_class_tbl.end();
i++)
{
o << DefWithMaps::form_lookup(i->first) << ": ";
for (const int *p = i->second; *p != interp::EOL; p++)
{
o << DefWithMaps::interp_lookup(*p && ~interp::FLAGS);
if (*(p+1) != interp::EOL) o << ", ";
}
o << std::endl;
}
return o;
}
extern abstract_def& DEFAULT_DWARF_SPEC;
extern abstract_def& dwarf_current; // HACK
extern dwarf4_t& dwarf4;
}
}
#endif