/* 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 #include #include #include #include #include #include #include /* 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 tag_forward_map; \ static const std::map tag_inverse_map; \ static const std::map form_forward_map; \ static const std::map form_inverse_map; \ static const std::map attr_forward_map; \ static const std::map attr_inverse_map; \ static const std::map encoding_forward_map; \ static const std::map encoding_inverse_map; \ static const std::map op_forward_map; \ static const std::map op_inverse_map; \ static const std::map interp_forward_map; \ static const std::map interp_inverse_map; \ static const std::map op_operand_forms_map; \ static const std::map attr_class_map; \ static const std::map 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 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 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 V map_union_lookup( const std::map& 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 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::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(static_cast(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//, { 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, 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, 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& forward_map, const std::map& inverse_map); template 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::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::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