| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // -*- Mode: C++ -*- |
| // |
| // Copyright (C) 2013-2024 Red Hat, Inc. |
| // |
| // Author: Dodji Seketeli |
| |
| /// @file |
| /// |
| /// This file contains the definitions of the entry points to |
| /// de-serialize an instance of @ref abigail::corpus from a file in |
| /// elf format, containing dwarf information. |
| |
| #include "abg-internal.h" |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <libgen.h> |
| #include <assert.h> |
| #include <limits.h> |
| #include <elfutils/libdwfl.h> |
| #include <dwarf.h> |
| #include <algorithm> |
| #include <cmath> |
| #include <cstring> |
| #include <deque> |
| #include <list> |
| #include <memory> |
| #include <ostream> |
| #include <sstream> |
| #include <stack> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <map> |
| |
| #include "abg-ir-priv.h" |
| #include "abg-suppression-priv.h" |
| #include "abg-corpus-priv.h" |
| #include "abg-symtab-reader.h" |
| |
| // <headers defining libabigail's API go under here> |
| ABG_BEGIN_EXPORT_DECLARATIONS |
| |
| #include "abg-dwarf-reader.h" |
| #include "abg-elf-based-reader.h" |
| #include "abg-sptr-utils.h" |
| #include "abg-tools-utils.h" |
| #include "abg-elf-helpers.h" |
| |
| ABG_END_EXPORT_DECLARATIONS |
| // </headers defining libabigail's API> |
| |
| #ifndef UINT64_MAX |
| #define UINT64_MAX 0xffffffffffffffff |
| #endif |
| |
| using std::string; |
| |
| namespace abigail |
| { |
| |
| using std::cerr; |
| |
| /// The namespace for the DWARF reader. |
| namespace dwarf |
| { |
| |
| using std::dynamic_pointer_cast; |
| using std::static_pointer_cast; |
| using std::unordered_map; |
| using std::unordered_set; |
| using std::stack; |
| using std::deque; |
| using std::list; |
| using std::map; |
| using abg_compat::optional; |
| |
| using namespace elf_helpers; // TODO: avoid using namespace |
| |
| /// Where a DIE comes from. For instance, a DIE can come from the main |
| /// debug info section, the alternate debug info section or from the |
| /// type unit section. |
| enum die_source |
| { |
| NO_DEBUG_INFO_DIE_SOURCE, |
| PRIMARY_DEBUG_INFO_DIE_SOURCE, |
| ALT_DEBUG_INFO_DIE_SOURCE, |
| TYPE_UNIT_DIE_SOURCE, |
| NUMBER_OF_DIE_SOURCES, // This one must always be the latest |
| // enumerator |
| }; |
| |
| |
| /// A convenience typedef for a vector of Dwarf_Off. |
| typedef vector<Dwarf_Off> dwarf_offsets_type; |
| |
| /// Convenience typedef for a map which key is the offset of a dwarf |
| /// die and which value is the corresponding artefact. |
| typedef unordered_map<Dwarf_Off, type_or_decl_base_sptr> die_artefact_map_type; |
| |
| /// Convenience typedef for a map which key is the offset of a dwarf |
| /// die, (given by dwarf_dieoffset()) and which value is the |
| /// corresponding class_decl. |
| typedef unordered_map<Dwarf_Off, class_decl_sptr> die_class_map_type; |
| |
| /// Convenience typedef for a map which key is the offset of a dwarf |
| /// die, (given by dwarf_dieoffset()) and which value is the |
| /// corresponding class_or_union_sptr. |
| typedef unordered_map<Dwarf_Off, class_or_union_sptr> die_class_or_union_map_type; |
| |
| /// Convenience typedef for a map which key the offset of a dwarf die |
| /// and which value is the corresponding function_decl. |
| typedef unordered_map<Dwarf_Off, function_decl_sptr> die_function_decl_map_type; |
| |
| /// Convenience typedef for a map which key is the offset of a dwarf |
| /// die and which value is the corresponding function_type. |
| typedef unordered_map<Dwarf_Off, function_type_sptr> die_function_type_map_type; |
| |
| /// Convenience typedef for a map which key is the offset of a |
| /// DW_TAG_compile_unit and the value is the corresponding @ref |
| /// translation_unit_sptr. |
| typedef unordered_map<Dwarf_Off, translation_unit_sptr> die_tu_map_type; |
| |
| /// Convenience typedef for a map which key is the offset of a DIE and |
| /// the value is the corresponding qualified name of the DIE. |
| typedef unordered_map<Dwarf_Off, interned_string> die_istring_map_type; |
| |
| /// Convenience typedef for a map which is an interned_string and |
| /// which value is a vector of offsets. |
| typedef unordered_map<interned_string, |
| dwarf_offsets_type, |
| hash_interned_string> |
| istring_dwarf_offsets_map_type; |
| |
| /// A hasher for a pair of Dwarf_Off. This is used as a hasher for |
| /// the type @ref dwarf_offset_pair_set_type. |
| struct dwarf_offset_pair_hash |
| { |
| size_t |
| operator()(const std::pair<Dwarf_Off, Dwarf_Off>& p) const |
| {return abigail::hashing::combine_hashes(p.first, p.second);} |
| };// end struct dwarf_offset_pair_hash |
| |
| typedef unordered_set<std::pair<Dwarf_Off, |
| Dwarf_Off>, |
| dwarf_offset_pair_hash> dwarf_offset_pair_set_type; |
| |
| /// An abstraction of a DIE offset that also encapsulate the source of |
| /// the DIE. |
| struct offset_type |
| { |
| die_source source_; |
| Dwarf_Off offset_; |
| |
| offset_type() |
| : source_(PRIMARY_DEBUG_INFO_DIE_SOURCE), |
| offset_(0) |
| {} |
| |
| offset_type(die_source source, Dwarf_Off offset) |
| : source_(source), |
| offset_(offset) |
| {} |
| |
| offset_type(Dwarf_Off offset) |
| : source_(PRIMARY_DEBUG_INFO_DIE_SOURCE), |
| offset_(offset) |
| {} |
| |
| bool operator==(const offset_type& o) const |
| {return source_ == o.source_ && offset_ == o.offset_;} |
| |
| operator Dwarf_Off() const |
| {return offset_;} |
| }; // end struct offset_type |
| |
| /// A convenience typedef for a pair of offset_type. |
| typedef std::pair<offset_type, offset_type> offset_pair_type; |
| |
| /// A hasher for an instance of offset_type. |
| struct offset_hash |
| { |
| size_t |
| operator()(const offset_type& p) const |
| {return abigail::hashing::combine_hashes(p.source_, p.offset_);} |
| };// end struct offset_hash |
| |
| /// A hasher for a pair of offset_type. This is used as a hasher for |
| /// the type @ref offset_pair_set_type, for instance. |
| struct offset_pair_hash |
| { |
| size_t |
| operator()(const std::pair<offset_type, offset_type>& p) const |
| { |
| size_t h1 = abigail::hashing::combine_hashes(p.first.source_, |
| p.first.offset_); |
| size_t h2 = abigail::hashing::combine_hashes(p.second.source_, |
| p.second.offset_); |
| return abigail::hashing::combine_hashes(h1, h2); |
| } |
| };// end struct offset_pair_hash |
| |
| /// A convenience typedef for an unordered set of DIE offsets. |
| typedef unordered_set<offset_type, offset_hash> offset_set_type; |
| |
| ///A convenience typedef for an unordered set of pairs of offset_type. |
| typedef unordered_set<std::pair<offset_type, |
| offset_type>, |
| offset_pair_hash> offset_pair_set_type; |
| |
| /// A convenience typedef for a vector of pairs of offset_type. |
| typedef vector<std::pair<offset_type, offset_type>> offset_pair_vector_type; |
| |
| /// A convenience typedef for an unordered map that associates a pair |
| /// of offset_type to a vector of pairs offset_type. |
| typedef unordered_map<std::pair<offset_type, offset_type>, |
| offset_pair_vector_type, |
| offset_pair_hash> offset_pair_vect_map_type; |
| |
| /// A convenience typedef for an unordered_map that associates a pair |
| /// of offset_type to a set of pairs of offset_type. |
| typedef unordered_map<std::pair<offset_type, offset_type>, |
| offset_pair_set_type, |
| offset_pair_hash> offset_pair_set_map_type; |
| |
| /// A convenience typedef for a vector of pairs of offset_type. |
| typedef vector<std::pair<offset_type, offset_type>> offset_pair_vector_type; |
| |
| class reader; |
| |
| static translation_unit_sptr |
| build_translation_unit_and_add_to_ir(reader& rdr, |
| Dwarf_Die* die, |
| char address_size); |
| |
| static void |
| maybe_propagate_canonical_type(const reader& rdr, |
| const Dwarf_Die* l, |
| const Dwarf_Die* r); |
| |
| static void |
| propagate_canonical_type(const reader& rdr, |
| const Dwarf_Die* l, |
| const Dwarf_Die* r); |
| |
| /// Convenience typedef for a shared pointer to an |
| /// addr_elf_symbol_sptr_map_type. |
| typedef shared_ptr<addr_elf_symbol_sptr_map_type> addr_elf_symbol_sptr_map_sptr; |
| |
| /// Convenience typedef for a map that associates an @ref |
| /// interned_string to a @ref function_type_sptr. |
| typedef unordered_map<interned_string, |
| function_type_sptr, |
| hash_interned_string> istring_fn_type_map_type; |
| |
| /// Convenience typedef for a stack containing the scopes up to the |
| /// current point in the abigail Internal Representation (aka IR) tree |
| /// that is being built. |
| typedef stack<scope_decl*> scope_stack_type; |
| |
| /// Convenience typedef for a map which key is a dwarf offset. The |
| /// value is also a dwarf offset. |
| typedef unordered_map<Dwarf_Off, Dwarf_Off> offset_offset_map_type; |
| |
| /// Convenience typedef for a map which key is a string and which |
| /// value is a vector of smart pointer to a class_or_union_sptr. |
| typedef unordered_map<string, classes_or_unions_type> string_classes_or_unions_map; |
| |
| /// Convenience typedef for a map which key is a string and which |
| /// value is a vector of smart pointer to a class. |
| typedef unordered_map<string, classes_type> string_classes_map; |
| |
| /// Convenience typedef for a map which key is a string and which |
| /// value is a vector of smart pointer to a enum. |
| typedef unordered_map<string, enums_type> string_enums_map; |
| |
| /// The abstraction of the place where a partial unit has been |
| /// imported. This is what the DW_TAG_imported_unit DIE expresses. |
| /// |
| /// This type thus contains: |
| /// - the offset to which the partial unit is imported |
| /// - the offset of the imported partial unit. |
| /// - the offset of the imported partial unit. |
| struct imported_unit_point |
| { |
| Dwarf_Off offset_of_import; |
| // The boolean below is true iff the imported unit comes from the |
| // alternate debug info file. |
| die_source imported_unit_die_source; |
| Dwarf_Off imported_unit_die_off; |
| Dwarf_Off imported_unit_cu_off; |
| Dwarf_Off imported_unit_child_off; |
| |
| /// Default constructor for @ref the type imported_unit_point. |
| imported_unit_point() |
| : offset_of_import(), |
| imported_unit_die_source(PRIMARY_DEBUG_INFO_DIE_SOURCE), |
| imported_unit_die_off(), |
| imported_unit_cu_off(), |
| imported_unit_child_off() |
| {} |
| |
| /// Constructor of @ref the type imported_unit_point. |
| /// |
| /// @param import_off the offset of the point at which the unit has |
| /// been imported. |
| imported_unit_point(Dwarf_Off import_off) |
| : offset_of_import(import_off), |
| imported_unit_die_source(PRIMARY_DEBUG_INFO_DIE_SOURCE), |
| imported_unit_die_off(), |
| imported_unit_cu_off(), |
| imported_unit_child_off() |
| {} |
| |
| /// Constructor of @ref the type imported_unit_point. |
| /// |
| /// @param import_off the offset of the point at which the unit has |
| /// been imported. |
| /// |
| /// @param from where the imported DIE comes from. |
| /// |
| /// @param imported_die the die of the unit that has been imported. |
| imported_unit_point(Dwarf_Off import_off, |
| const Dwarf_Die& imported_die, |
| die_source from) |
| : offset_of_import(import_off), |
| imported_unit_die_source(from), |
| imported_unit_die_off(dwarf_dieoffset |
| (const_cast<Dwarf_Die*>(&imported_die))), |
| imported_unit_cu_off(), |
| imported_unit_child_off() |
| { |
| Dwarf_Die imported_unit_child; |
| |
| ABG_ASSERT(dwarf_child(const_cast<Dwarf_Die*>(&imported_die), |
| &imported_unit_child) == 0); |
| |
| imported_unit_child_off = |
| dwarf_dieoffset(const_cast<Dwarf_Die*>(&imported_unit_child)); |
| |
| Dwarf_Die cu_die_memory; |
| Dwarf_Die *cu_die; |
| |
| cu_die = dwarf_diecu(const_cast<Dwarf_Die*>(&imported_unit_child), |
| &cu_die_memory, 0, 0); |
| imported_unit_cu_off = dwarf_dieoffset(cu_die); |
| } |
| }; // struct imported_unit_point |
| |
| /// Convenience typedef for a vector of @ref imported_unit_point. |
| typedef vector<imported_unit_point> imported_unit_points_type; |
| |
| /// Convenience typedef for a vector of @ref imported_unit_point. |
| typedef unordered_map<Dwarf_Off, imported_unit_points_type> |
| tu_die_imported_unit_points_map_type; |
| |
| /// "Less than" operator for instances of @ref imported_unit_point |
| /// type. |
| /// |
| /// @param the left hand side operand of the "Less than" operator. |
| /// |
| /// @param the right hand side operand of the "Less than" operator. |
| /// |
| /// @return true iff @p l is less than @p r. |
| static bool |
| operator<(const imported_unit_point& l, const imported_unit_point& r) |
| {return l.offset_of_import < r.offset_of_import;} |
| |
| static bool |
| get_parent_die(const reader& rdr, |
| const Dwarf_Die* die, |
| Dwarf_Die& parent_die, |
| size_t where_offset); |
| |
| static bool |
| get_scope_die(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset, |
| Dwarf_Die& scope_die); |
| |
| static bool |
| get_die_language(const Dwarf_Die *die, translation_unit::language &lang) ; |
| |
| static bool |
| die_is_in_c(const Dwarf_Die *die); |
| |
| static bool |
| die_is_in_cplus_plus(const Dwarf_Die *die); |
| |
| static bool |
| die_is_in_c_or_cplusplus(const Dwarf_Die *die); |
| |
| static bool |
| die_is_anonymous(const Dwarf_Die* die); |
| |
| static bool |
| die_is_anonymous_data_member(const Dwarf_Die* die); |
| |
| static bool |
| die_is_type(const Dwarf_Die* die); |
| |
| static bool |
| die_is_decl(const Dwarf_Die* die); |
| |
| static bool |
| die_is_declaration_only(Dwarf_Die* die); |
| |
| static bool |
| die_is_variable_decl(const Dwarf_Die *die); |
| |
| static bool |
| die_is_function_decl(const Dwarf_Die *die); |
| |
| static bool |
| die_has_size_attribute(const Dwarf_Die *die); |
| |
| static bool |
| die_has_no_child(const Dwarf_Die *die); |
| |
| static bool |
| die_is_namespace(const Dwarf_Die* die); |
| |
| static bool |
| die_is_unspecified(Dwarf_Die* die); |
| |
| static bool |
| die_is_void_type(Dwarf_Die* die); |
| |
| static bool |
| die_is_pointer_type(const Dwarf_Die* die); |
| |
| static bool |
| pointer_or_qual_die_of_anonymous_class_type(const Dwarf_Die* die); |
| |
| static bool |
| die_is_reference_type(const Dwarf_Die* die); |
| |
| static bool |
| die_is_pointer_array_or_reference_type(const Dwarf_Die* die); |
| |
| static bool |
| die_is_pointer_or_reference_type(const Dwarf_Die* die); |
| |
| static bool |
| die_is_pointer_reference_or_typedef_type(const Dwarf_Die* die); |
| |
| static bool |
| die_is_class_type(const Dwarf_Die* die); |
| |
| static bool |
| die_is_qualified_type(const Dwarf_Die* die); |
| |
| static bool |
| die_is_function_type(const Dwarf_Die *die); |
| |
| static bool |
| die_has_object_pointer(const Dwarf_Die* die, |
| Dwarf_Die& object_pointer); |
| |
| static bool |
| die_has_children(const Dwarf_Die* die); |
| |
| static bool |
| fn_die_first_parameter_die(const Dwarf_Die* die, Dwarf_Die& first_parm_die); |
| |
| static bool |
| member_fn_die_has_this_pointer(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset, |
| Dwarf_Die& class_die, |
| Dwarf_Die& object_pointer_die); |
| |
| static bool |
| die_this_pointer_from_object_pointer(Dwarf_Die* die, |
| Dwarf_Die& this_pointer); |
| |
| static bool |
| die_this_pointer_is_const(Dwarf_Die* die); |
| |
| static bool |
| die_object_pointer_is_for_const_method(Dwarf_Die* die); |
| |
| static bool |
| is_type_die_to_be_canonicalized(const Dwarf_Die *die); |
| |
| static bool |
| die_is_at_class_scope(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset, |
| Dwarf_Die& class_scope_die); |
| static bool |
| eval_last_constant_dwarf_sub_expr(Dwarf_Op* expr, |
| size_t expr_len, |
| int64_t& value, |
| bool& is_tls_address); |
| |
| static translation_unit::language |
| dwarf_language_to_tu_language(size_t l); |
| |
| static bool |
| die_unsigned_constant_attribute(const Dwarf_Die* die, |
| unsigned attr_name, |
| uint64_t& cst); |
| |
| static bool |
| die_signed_constant_attribute(const Dwarf_Die*die, |
| unsigned attr_name, |
| int64_t& cst); |
| |
| static bool |
| die_constant_attribute(const Dwarf_Die *die, |
| unsigned attr_name, |
| bool is_signed, |
| array_type_def::subrange_type::bound_value &value); |
| |
| static bool |
| die_member_offset(const reader& rdr, |
| const Dwarf_Die* die, |
| int64_t& offset); |
| |
| static bool |
| form_is_DW_FORM_strx(unsigned form); |
| |
| static bool |
| form_is_DW_FORM_line_strp(unsigned form); |
| |
| static bool |
| die_address_attribute(Dwarf_Die* die, unsigned attr_name, Dwarf_Addr& result); |
| |
| static string |
| die_name(const Dwarf_Die* die); |
| |
| static void |
| die_name_and_linkage_name(const Dwarf_Die* die, |
| string& name, |
| string& linkage_name); |
| static location |
| die_location(const reader& rdr, const Dwarf_Die* die); |
| |
| static bool |
| die_location_address(Dwarf_Die* die, |
| Dwarf_Addr& address, |
| bool& is_tls_address); |
| |
| static bool |
| die_die_attribute(const Dwarf_Die* die, |
| unsigned attr_name, |
| Dwarf_Die& result, |
| bool recursively = true); |
| |
| static bool |
| die_origin_die(const Dwarf_Die* die, Dwarf_Die& origin_die); |
| |
| static bool |
| subrange_die_indirect_bound_value(const Dwarf_Die *die, |
| unsigned attr_name, |
| array_type_def::subrange_type::bound_value& v, |
| bool& is_signed); |
| |
| static bool |
| subrange_die_indirectly_references_subrange_die(const Dwarf_Die *die, |
| unsigned attr_name, |
| Dwarf_Die& referenced_subrange); |
| static string |
| get_internal_anonymous_die_prefix_name(const Dwarf_Die *die); |
| |
| static string |
| build_internal_anonymous_die_name(const string &base_name, |
| size_t anonymous_type_index); |
| |
| static string |
| get_internal_anonymous_die_name(Dwarf_Die *die, |
| size_t anonymous_type_index); |
| |
| static string |
| die_qualified_type_name(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where); |
| |
| static string |
| die_qualified_decl_name(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where); |
| |
| static string |
| die_qualified_name(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where); |
| |
| static bool |
| die_qualified_type_name_empty(const reader& rdr, |
| const Dwarf_Die* die, size_t where, |
| string &qualified_name); |
| |
| static void |
| die_return_and_parm_names_from_fn_type_die(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset, |
| bool pretty_print, |
| string &return_type_name, |
| string &class_name, |
| vector<string>& parm_names, |
| bool& is_const, |
| bool& is_static); |
| |
| static string |
| die_function_signature(const reader& rdr, |
| const Dwarf_Die *die, |
| size_t where_offset); |
| |
| static bool |
| die_peel_qual_ptr(Dwarf_Die *die, Dwarf_Die& peeled_die); |
| |
| static bool |
| die_peel_qualified(Dwarf_Die *die, Dwarf_Die& peeled_die); |
| |
| static bool |
| die_peel_typedef(Dwarf_Die *die, Dwarf_Die& peeled_die); |
| |
| static bool |
| die_function_type_is_method_type(const reader& rdr, |
| const Dwarf_Die *die, |
| size_t where_offset, |
| Dwarf_Die& object_pointer_die, |
| Dwarf_Die& class_die, |
| bool& is_static); |
| |
| static string |
| die_pretty_print_type(reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset); |
| |
| static string |
| die_pretty_print_decl(reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset); |
| |
| static string |
| die_pretty_print(reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset); |
| |
| static void |
| maybe_canonicalize_type(const type_base_sptr& t, |
| reader& rdr); |
| |
| static uint64_t |
| get_default_array_lower_bound(translation_unit::language l); |
| |
| static bool |
| find_lower_bound_in_imported_unit_points(const imported_unit_points_type&, |
| Dwarf_Off, |
| imported_unit_points_type::const_iterator&); |
| |
| static array_type_def::subrange_sptr |
| build_subrange_type(reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset, |
| bool associate_type_to_die = true); |
| |
| static void |
| build_subranges_from_array_type_die(reader& rdr, |
| const Dwarf_Die* die, |
| array_type_def::subranges_type& subranges, |
| size_t where_offset, |
| bool associate_type_to_die = true); |
| |
| static comparison_result |
| compare_dies(const reader& rdr, |
| const Dwarf_Die *l, const Dwarf_Die *r, |
| bool update_canonical_dies_on_the_fly); |
| |
| static bool |
| compare_dies_during_canonicalization(reader& rdr, |
| const Dwarf_Die *l, const Dwarf_Die *r, |
| bool update_canonical_dies_on_the_fly); |
| |
| static bool |
| get_member_child_die(const Dwarf_Die *die, Dwarf_Die *child); |
| |
| /// Get the language used to generate a given DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param lang the resulting language. |
| /// |
| /// @return true iff the language of the DIE was found. |
| static bool |
| get_die_language(const Dwarf_Die *die, translation_unit::language &lang) |
| { |
| Dwarf_Die cu_die; |
| ABG_ASSERT(dwarf_diecu(const_cast<Dwarf_Die*>(die), &cu_die, 0, 0)); |
| |
| uint64_t l = 0; |
| if (!die_unsigned_constant_attribute(&cu_die, DW_AT_language, l)) |
| return false; |
| |
| lang = dwarf_language_to_tu_language(l); |
| return true; |
| } |
| |
| /// Test if a given DIE originates from a program written in the C |
| /// language. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die originates from a program in the C |
| /// language. |
| static bool |
| die_is_in_c(const Dwarf_Die *die) |
| { |
| translation_unit::language l = translation_unit::LANG_UNKNOWN; |
| if (!get_die_language(die, l)) |
| return false; |
| return is_c_language(l); |
| } |
| |
| /// Test if a given DIE originates from a program written in the C++ |
| /// language. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die originates from a program in the C++ |
| /// language. |
| static bool |
| die_is_in_cplus_plus(const Dwarf_Die *die) |
| { |
| translation_unit::language l = translation_unit::LANG_UNKNOWN; |
| if (!get_die_language(die, l)) |
| return false; |
| return is_cplus_plus_language(l); |
| } |
| |
| /// Test if a given DIE originates from a program written either in |
| /// C or C++. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die originates from a program written either in |
| /// C or C++. |
| static bool |
| die_is_in_c_or_cplusplus(const Dwarf_Die *die) |
| { |
| translation_unit::language l = translation_unit::LANG_UNKNOWN; |
| if (!get_die_language(die, l)) |
| return false; |
| return (is_cplus_plus_language(l) || is_c_language(l)); |
| } |
| |
| /// Compare a symbol name against another name, possibly demangling |
| /// the symbol_name before performing the comparison. |
| /// |
| /// @param symbol_name the symbol_name to take in account. |
| /// |
| /// @param name the second name to take in account. |
| /// |
| /// @param demangle if true, demangle @p symbol_name and compare the |
| /// result of the demangling with @p name. |
| /// |
| /// @return true iff symbol_name equals name. |
| static bool |
| compare_symbol_name(const string& symbol_name, |
| const string& name, |
| bool demangle) |
| { |
| if (demangle) |
| { |
| string m = demangle_cplus_mangled_name(symbol_name); |
| return m == name; |
| } |
| return symbol_name == name; |
| } |
| |
| /// Lookup a symbol using the SysV ELF hash table. |
| /// |
| /// Note that this function hasn't been tested. So it hasn't been |
| /// debugged yet. IOW, it is not known to work. Or rather, it's |
| /// almost like it's surely doesn't work ;-) |
| /// |
| /// Use it at your own risks. :-) |
| /// |
| ///@parm env the environment we are operating from. |
| /// |
| /// @param elf_handle the elf_handle to use. |
| /// |
| /// @param sym_name the symbol name to look for. |
| /// |
| /// @param ht_index the index (in the section headers table) of the |
| /// hash table section to use. |
| /// |
| /// @param sym_tab_index the index (in the section headers table) of |
| /// the symbol table to use. |
| /// |
| /// @param demangle if true, demangle @p sym_name before comparing it |
| /// to names from the symbol table. |
| /// |
| /// @param syms_found a vector of symbols found with the name @p |
| /// sym_name. table. |
| static bool |
| lookup_symbol_from_sysv_hash_tab(const environment& env, |
| Elf* elf_handle, |
| const string& sym_name, |
| size_t ht_index, |
| size_t sym_tab_index, |
| bool demangle, |
| vector<elf_symbol_sptr>& syms_found) |
| { |
| Elf_Scn* sym_tab_section = elf_getscn(elf_handle, sym_tab_index); |
| ABG_ASSERT(sym_tab_section); |
| |
| Elf_Data* sym_tab_data = elf_getdata(sym_tab_section, 0); |
| ABG_ASSERT(sym_tab_data); |
| |
| GElf_Shdr sheader_mem; |
| GElf_Shdr* sym_tab_section_header = gelf_getshdr(sym_tab_section, |
| &sheader_mem); |
| Elf_Scn* hash_section = elf_getscn(elf_handle, ht_index); |
| ABG_ASSERT(hash_section); |
| |
| // Poke at the different parts of the hash table and get them ready |
| // to be used. |
| unsigned long hash = elf_hash(sym_name.c_str()); |
| Elf_Data* ht_section_data = elf_getdata(hash_section, 0); |
| Elf32_Word* ht_data = reinterpret_cast<Elf32_Word*>(ht_section_data->d_buf); |
| size_t nb_buckets = ht_data[0]; |
| size_t nb_chains = ht_data[1]; |
| |
| if (nb_buckets == 0) |
| // An empty hash table. Not sure if that is possible, but it |
| // would mean an empty table of exported symbols. |
| return false; |
| |
| //size_t nb_chains = ht_data[1]; |
| Elf32_Word* ht_buckets = &ht_data[2]; |
| Elf32_Word* ht_chains = &ht_buckets[nb_buckets]; |
| |
| // Now do the real work. |
| size_t bucket = hash % nb_buckets; |
| size_t symbol_index = ht_buckets[bucket]; |
| |
| GElf_Sym symbol; |
| const char* sym_name_str; |
| size_t sym_size; |
| elf_symbol::type sym_type; |
| elf_symbol::binding sym_binding; |
| elf_symbol::visibility sym_visibility; |
| bool found = false; |
| |
| do |
| { |
| ABG_ASSERT(gelf_getsym(sym_tab_data, symbol_index, &symbol)); |
| sym_name_str = elf_strptr(elf_handle, |
| sym_tab_section_header->sh_link, |
| symbol.st_name); |
| if (sym_name_str |
| && compare_symbol_name(sym_name_str, sym_name, demangle)) |
| { |
| sym_type = stt_to_elf_symbol_type(GELF_ST_TYPE(symbol.st_info)); |
| sym_binding = stb_to_elf_symbol_binding(GELF_ST_BIND(symbol.st_info)); |
| sym_visibility = |
| stv_to_elf_symbol_visibility(GELF_ST_VISIBILITY(symbol.st_other)); |
| sym_size = symbol.st_size; |
| elf_symbol::version ver; |
| if (get_version_for_symbol(elf_handle, symbol_index, |
| /*get_def_version=*/true, ver)) |
| ABG_ASSERT(!ver.str().empty()); |
| elf_symbol_sptr symbol_found = |
| elf_symbol::create(env, |
| symbol_index, |
| sym_size, |
| sym_name_str, |
| sym_type, |
| sym_binding, |
| symbol.st_shndx != SHN_UNDEF, |
| symbol.st_shndx == SHN_COMMON, |
| ver, sym_visibility); |
| syms_found.push_back(symbol_found); |
| found = true; |
| } |
| symbol_index = ht_chains[symbol_index]; |
| } while (symbol_index != STN_UNDEF || symbol_index >= nb_chains); |
| |
| return found; |
| } |
| |
| /// Get the size of the elf class, in bytes. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @return the size computed. |
| static char |
| get_elf_class_size_in_bytes(Elf* elf_handle) |
| { |
| char result = 0; |
| GElf_Ehdr hdr; |
| |
| ABG_ASSERT(gelf_getehdr(elf_handle, &hdr)); |
| int c = hdr.e_ident[EI_CLASS]; |
| |
| switch (c) |
| { |
| case ELFCLASS32: |
| result = 4; |
| break; |
| case ELFCLASS64: |
| result = 8; |
| break; |
| default: |
| ABG_ASSERT_NOT_REACHED; |
| } |
| |
| return result; |
| } |
| |
| /// Get a given word of a bloom filter, referred to by the index of |
| /// the word. |
| /// |
| /// The bloom word size depends on the current elf class (32 bits for |
| /// an ELFCLASS32 or 64 bits for an ELFCLASS64 one) and this function |
| /// abstracts that nicely. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @param bloom_filter the bloom filter to consider. |
| /// |
| /// @param index the index of the bloom filter to return. |
| /// |
| /// @return a 64 bits work containing the bloom word found at index @p |
| /// index. Note that if we are looking at an ELFCLASS32 binary, the 4 |
| /// most significant bytes of the result are going to be zero. |
| static Elf64_Xword |
| bloom_word_at(Elf* elf_handle, |
| Elf32_Word* bloom_filter, |
| size_t index) |
| { |
| Elf64_Xword result = 0; |
| GElf_Ehdr h; |
| ABG_ASSERT(gelf_getehdr(elf_handle, &h)); |
| int c; |
| c = h.e_ident[EI_CLASS]; |
| |
| switch(c) |
| { |
| case ELFCLASS32: |
| result = bloom_filter[index]; |
| break ; |
| case ELFCLASS64: |
| { |
| Elf64_Xword* f= reinterpret_cast<Elf64_Xword*>(bloom_filter); |
| result = f[index]; |
| } |
| break; |
| default: |
| abort(); |
| } |
| |
| return result; |
| } |
| |
| /// The abstraction of the gnu elf hash table. |
| /// |
| /// The members of this struct are explained at |
| /// - https://sourceware.org/ml/binutils/2006-10/msg00377.html |
| /// - https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections. |
| struct gnu_ht |
| { |
| size_t nb_buckets; |
| Elf32_Word* buckets; |
| Elf32_Word* chain; |
| size_t first_sym_index; |
| size_t bf_nwords; |
| size_t bf_size; |
| Elf32_Word* bloom_filter; |
| size_t shift; |
| size_t sym_count; |
| Elf_Scn* sym_tab_section; |
| GElf_Shdr sym_tab_section_header; |
| |
| gnu_ht() |
| : nb_buckets(0), |
| buckets(0), |
| chain(0), |
| first_sym_index(0), |
| bf_nwords(0), |
| bf_size(0), |
| bloom_filter(0), |
| shift(0), |
| sym_count(0), |
| sym_tab_section(0) |
| {} |
| }; // end struct gnu_ht |
| |
| /// Setup the members of the gnu hash table. |
| /// |
| /// @param elf_handle a handle on the elf file to use. |
| /// |
| /// @param ht_index the index (into the elf section headers table) of |
| /// the hash table section to use. |
| /// |
| /// @param sym_tab_index the index (into the elf section headers |
| /// table) of the symbol table the gnu hash table is about. |
| /// |
| /// @param ht the resulting hash table. |
| /// |
| /// @return true iff the hash table @ ht could be setup. |
| static bool |
| setup_gnu_ht(Elf* elf_handle, |
| size_t ht_index, |
| size_t sym_tab_index, |
| gnu_ht& ht) |
| { |
| ht.sym_tab_section = elf_getscn(elf_handle, sym_tab_index); |
| ABG_ASSERT(ht.sym_tab_section); |
| ABG_ASSERT(gelf_getshdr(ht.sym_tab_section, &ht.sym_tab_section_header)); |
| ht.sym_count = |
| ht.sym_tab_section_header.sh_size / ht.sym_tab_section_header.sh_entsize; |
| Elf_Scn* hash_section = elf_getscn(elf_handle, ht_index); |
| ABG_ASSERT(hash_section); |
| |
| // Poke at the different parts of the hash table and get them ready |
| // to be used. |
| Elf_Data* ht_section_data = elf_getdata(hash_section, 0); |
| Elf32_Word* ht_data = reinterpret_cast<Elf32_Word*>(ht_section_data->d_buf); |
| |
| ht.nb_buckets = ht_data[0]; |
| if (ht.nb_buckets == 0) |
| // An empty hash table. Not sure if that is possible, but it |
| // would mean an empty table of exported symbols. |
| return false; |
| ht.first_sym_index = ht_data[1]; |
| // The number of words used by the bloom filter. A size of a word |
| // is ELFCLASS. |
| ht.bf_nwords = ht_data[2]; |
| // The shift used by the bloom filter code. |
| ht.shift = ht_data[3]; |
| // The data of the bloom filter proper. |
| ht.bloom_filter = &ht_data[4]; |
| // The size of the bloom filter in 4 bytes word. This is going to |
| // be used to index the 'bloom_filter' above, which is of type |
| // Elf32_Word*; thus we need that bf_size be expressed in 4 bytes |
| // words. |
| ht.bf_size = (get_elf_class_size_in_bytes(elf_handle) / 4) * ht.bf_nwords; |
| // The buckets of the hash table. |
| ht.buckets = ht.bloom_filter + ht.bf_size; |
| // The chain of the hash table. |
| ht.chain = ht.buckets + ht.nb_buckets; |
| |
| return true; |
| } |
| |
| /// Look into the symbol tables of the underlying elf file and find |
| /// the symbol we are being asked. |
| /// |
| /// This function uses the GNU hash table for the symbol lookup. |
| /// |
| /// The reference of for the implementation of this function can be |
| /// found at: |
| /// - https://sourceware.org/ml/binutils/2006-10/msg00377.html |
| /// - https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @param sym_name the name of the symbol to look for. |
| /// |
| /// @param ht_index the index of the hash table header to use. |
| /// |
| /// @param sym_tab_index the index of the symbol table header to use |
| /// with this hash table. |
| /// |
| /// @param demangle if true, demangle @p sym_name. |
| /// |
| /// @param syms_found the vector of symbols found with the name @p |
| /// sym_name. |
| /// |
| /// @return true if a symbol was actually found. |
| static bool |
| lookup_symbol_from_gnu_hash_tab(const environment& env, |
| Elf* elf_handle, |
| const string& sym_name, |
| size_t ht_index, |
| size_t sym_tab_index, |
| bool demangle, |
| vector<elf_symbol_sptr>& syms_found) |
| { |
| gnu_ht ht; |
| if (!setup_gnu_ht(elf_handle, ht_index, sym_tab_index, ht)) |
| return false; |
| |
| // Now do the real work. |
| |
| // Compute bloom hashes (GNU hash and second bloom specific hashes). |
| size_t h1 = elf_gnu_hash(sym_name.c_str()); |
| size_t h2 = h1 >> ht.shift; |
| // The size of one of the words used in the bloom |
| // filter, in bits. |
| int c = get_elf_class_size_in_bytes(elf_handle) * 8; |
| int n = (h1 / c) % ht.bf_nwords; |
| // The bitmask of the bloom filter has a size of either 32-bits on |
| // ELFCLASS32 binaries or 64-bits on ELFCLASS64 binaries. So we |
| // need a 64-bits type to hold the bitmap, hence the Elf64_Xword |
| // type used here. When dealing with 32bits binaries, the upper |
| // bits of the bitmask will be zero anyway. |
| Elf64_Xword bitmask = (1ul << (h1 % c)) | (1ul << (h2 % c)); |
| |
| // Test if the symbol is *NOT* present in this ELF file. |
| if ((bloom_word_at(elf_handle, ht.bloom_filter, n) & bitmask) != bitmask) |
| return false; |
| |
| size_t i = ht.buckets[h1 % ht.nb_buckets]; |
| if (i == STN_UNDEF) |
| return false; |
| |
| Elf32_Word stop_word, *stop_wordp; |
| elf_symbol::version ver; |
| GElf_Sym symbol; |
| const char* sym_name_str; |
| bool found = false; |
| |
| elf_symbol::type sym_type; |
| elf_symbol::binding sym_binding; |
| elf_symbol::visibility sym_visibility; |
| |
| // Let's walk the hash table and record the versions of all the |
| // symbols which name equal sym_name. |
| for (i = ht.buckets[h1 % ht.nb_buckets], |
| stop_wordp = &ht.chain[i - ht.first_sym_index]; |
| i != STN_UNDEF |
| && (stop_wordp |
| < ht.chain + (ht.sym_count - ht.first_sym_index)); |
| ++i, ++stop_wordp) |
| { |
| stop_word = *stop_wordp; |
| if ((stop_word & ~ 1)!= (h1 & ~1)) |
| // A given bucket can reference several hashes. Here we |
| // stumbled across a hash value different from the one we are |
| // looking for. Let's keep walking. |
| continue; |
| |
| ABG_ASSERT(gelf_getsym(elf_getdata(ht.sym_tab_section, 0), |
| i, &symbol)); |
| sym_name_str = elf_strptr(elf_handle, |
| ht.sym_tab_section_header.sh_link, |
| symbol.st_name); |
| if (sym_name_str |
| && compare_symbol_name(sym_name_str, sym_name, demangle)) |
| { |
| // So we found a symbol (in the symbol table) that equals |
| // sym_name. Now lets try to get its version and record it. |
| sym_type = stt_to_elf_symbol_type(GELF_ST_TYPE(symbol.st_info)); |
| sym_binding = stb_to_elf_symbol_binding(GELF_ST_BIND(symbol.st_info)); |
| sym_visibility = |
| stv_to_elf_symbol_visibility(GELF_ST_VISIBILITY(symbol.st_other)); |
| |
| if (get_version_for_symbol(elf_handle, i, |
| /*get_def_version=*/true, |
| ver)) |
| ABG_ASSERT(!ver.str().empty()); |
| |
| elf_symbol_sptr symbol_found = |
| elf_symbol::create(env, i, |
| symbol.st_size, |
| sym_name_str, |
| sym_type, sym_binding, |
| symbol.st_shndx != SHN_UNDEF, |
| symbol.st_shndx == SHN_COMMON, |
| ver, sym_visibility); |
| syms_found.push_back(symbol_found); |
| found = true; |
| } |
| |
| if (stop_word & 1) |
| // The last bit of the stop_word is 1. That means we need to |
| // stop here. We reached the end of the chain of values |
| // referenced by the hask bucket. |
| break; |
| } |
| return found; |
| } |
| |
| /// Look into the symbol tables of the underlying elf file and find |
| /// the symbol we are being asked. |
| /// |
| /// This function uses the elf hash table (be it the GNU hash table or |
| /// the sysv hash table) for the symbol lookup. |
| /// |
| /// @param env the environment we are operating from. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @param ht_kind the kind of hash table to use. This is returned by |
| /// the function function find_hash_table_section_index. |
| /// |
| /// @param ht_index the index (in the section headers table) of the |
| /// hash table section to use. |
| /// |
| /// @param sym_tab_index the index (in section headers table) of the |
| /// symbol table index to use with this hash table. |
| /// |
| /// @param symbol_name the name of the symbol to look for. |
| /// |
| /// @param demangle if true, demangle @p sym_name. |
| /// |
| /// @param syms_found the symbols that were actually found with the |
| /// name @p symbol_name. |
| /// |
| /// @return true iff the function found the symbol from the elf hash |
| /// table. |
| static bool |
| lookup_symbol_from_elf_hash_tab(const environment& env, |
| Elf* elf_handle, |
| hash_table_kind ht_kind, |
| size_t ht_index, |
| size_t symtab_index, |
| const string& symbol_name, |
| bool demangle, |
| vector<elf_symbol_sptr>& syms_found) |
| { |
| if (elf_handle == 0 || symbol_name.empty()) |
| return false; |
| |
| if (ht_kind == NO_HASH_TABLE_KIND) |
| return false; |
| |
| if (ht_kind == SYSV_HASH_TABLE_KIND) |
| return lookup_symbol_from_sysv_hash_tab(env, |
| elf_handle, symbol_name, |
| ht_index, |
| symtab_index, |
| demangle, |
| syms_found); |
| else if (ht_kind == GNU_HASH_TABLE_KIND) |
| return lookup_symbol_from_gnu_hash_tab(env, |
| elf_handle, symbol_name, |
| ht_index, |
| symtab_index, |
| demangle, |
| syms_found); |
| return false; |
| } |
| |
| /// Lookup a symbol from the symbol table directly. |
| /// |
| /// |
| /// @param env the environment we are operating from. |
| /// |
| /// @param elf_handle the elf handle to use. |
| /// |
| /// @param sym_name the name of the symbol to look up. |
| /// |
| /// @param sym_tab_index the index (in the section headers table) of |
| /// the symbol table section. |
| /// |
| /// @param demangle if true, demangle the names found in the symbol |
| /// table before comparing them with @p sym_name. |
| /// |
| /// @param sym_name_found the actual name of the symbol found. |
| /// |
| /// @param sym_type the type of the symbol found. |
| /// |
| /// @param sym_binding the binding of the symbol found. |
| /// |
| /// @param sym_versions the versions of the symbol found. |
| /// |
| /// @return true iff the symbol was found. |
| static bool |
| lookup_symbol_from_symtab(const environment& env, |
| Elf* elf_handle, |
| const string& sym_name, |
| size_t sym_tab_index, |
| bool demangle, |
| vector<elf_symbol_sptr>& syms_found) |
| { |
| // TODO: read all of the symbol table, store it in memory in a data |
| // structure that associates each symbol with its versions and in |
| // which lookups of a given symbol is fast. |
| Elf_Scn* sym_tab_section = elf_getscn(elf_handle, sym_tab_index); |
| ABG_ASSERT(sym_tab_section); |
| |
| GElf_Shdr header_mem; |
| GElf_Shdr * sym_tab_header = gelf_getshdr(sym_tab_section, |
| &header_mem); |
| |
| size_t symcount = sym_tab_header->sh_size / sym_tab_header->sh_entsize; |
| Elf_Data* symtab = elf_getdata(sym_tab_section, NULL); |
| GElf_Sym* sym; |
| char* name_str = 0; |
| elf_symbol::version ver; |
| bool found = false; |
| |
| for (size_t i = 0; i < symcount; ++i) |
| { |
| GElf_Sym sym_mem; |
| sym = gelf_getsym(symtab, i, &sym_mem); |
| name_str = elf_strptr(elf_handle, |
| sym_tab_header->sh_link, |
| sym->st_name); |
| |
| if (name_str && compare_symbol_name(name_str, sym_name, demangle)) |
| { |
| elf_symbol::type sym_type = |
| stt_to_elf_symbol_type(GELF_ST_TYPE(sym->st_info)); |
| elf_symbol::binding sym_binding = |
| stb_to_elf_symbol_binding(GELF_ST_BIND(sym->st_info)); |
| elf_symbol::visibility sym_visibility = |
| stv_to_elf_symbol_visibility(GELF_ST_VISIBILITY(sym->st_other)); |
| bool sym_is_defined = sym->st_shndx != SHN_UNDEF; |
| bool sym_is_common = sym->st_shndx == SHN_COMMON; |
| |
| if (get_version_for_symbol(elf_handle, i, |
| /*get_def_version=*/sym_is_defined, |
| ver)) |
| ABG_ASSERT(!ver.str().empty()); |
| elf_symbol_sptr symbol_found = |
| elf_symbol::create(env, i, sym->st_size, |
| name_str, sym_type, |
| sym_binding, sym_is_defined, |
| sym_is_common, ver, sym_visibility); |
| syms_found.push_back(symbol_found); |
| found = true; |
| } |
| } |
| |
| if (found) |
| return true; |
| |
| return false; |
| } |
| |
| /// Look into the symbol tables of the underlying elf file and see |
| /// if we find a given symbol. |
| /// |
| /// @param env the environment we are operating from. |
| /// |
| /// @param symbol_name the name of the symbol to look for. |
| /// |
| /// @param demangle if true, try to demangle the symbol name found in |
| /// the symbol table before comparing it to @p symbol_name. |
| /// |
| /// @param syms_found the list of symbols found, with the name @p |
| /// symbol_name. |
| /// |
| /// @param sym_type this is set to the type of the symbol found. This |
| /// shall b a standard elf.h value for symbol types, that is SHT_OBJECT, |
| /// STT_FUNC, STT_IFUNC, etc ... |
| /// |
| /// Note that this parameter is set iff the function returns true. |
| /// |
| /// @param sym_binding this is set to the binding of the symbol found. |
| /// This is a standard elf.h value of the symbol binding kind, that |
| /// is, STB_LOCAL, STB_GLOBAL, or STB_WEAK. |
| /// |
| /// @param symbol_versions the versions of the symbol @p symbol_name, |
| /// if it was found. |
| /// |
| /// @return true iff a symbol with the name @p symbol_name was found. |
| static bool |
| lookup_symbol_from_elf(const environment& env, |
| Elf* elf_handle, |
| const string& symbol_name, |
| bool demangle, |
| vector<elf_symbol_sptr>& syms_found) |
| { |
| size_t hash_table_index = 0, symbol_table_index = 0; |
| hash_table_kind ht_kind = NO_HASH_TABLE_KIND; |
| |
| if (!demangle) |
| ht_kind = find_hash_table_section_index(elf_handle, |
| hash_table_index, |
| symbol_table_index); |
| |
| if (ht_kind == NO_HASH_TABLE_KIND) |
| { |
| if (!find_symbol_table_section_index(elf_handle, symbol_table_index)) |
| return false; |
| |
| return lookup_symbol_from_symtab(env, |
| elf_handle, |
| symbol_name, |
| symbol_table_index, |
| demangle, |
| syms_found); |
| } |
| |
| return lookup_symbol_from_elf_hash_tab(env, |
| elf_handle, |
| ht_kind, |
| hash_table_index, |
| symbol_table_index, |
| symbol_name, |
| demangle, |
| syms_found); |
| } |
| |
| /// Look into the symbol tables of the underlying elf file and see if |
| /// we find a given public (global or weak) symbol of function type. |
| /// |
| /// @param env the environment we are operating from. |
| /// |
| /// @param elf_handle the elf handle to use for the query. |
| /// |
| /// @param symbol_name the function symbol to look for. |
| /// |
| /// @param func_syms the vector of public functions symbols found, if |
| /// any. |
| /// |
| /// @return true iff the symbol was found. |
| static bool |
| lookup_public_function_symbol_from_elf(environment& env, |
| Elf* elf_handle, |
| const string& symbol_name, |
| vector<elf_symbol_sptr>& func_syms) |
| { |
| vector<elf_symbol_sptr> syms_found; |
| bool found = false; |
| |
| if (lookup_symbol_from_elf(env, elf_handle, symbol_name, |
| /*demangle=*/false, syms_found)) |
| { |
| for (vector<elf_symbol_sptr>::const_iterator i = syms_found.begin(); |
| i != syms_found.end(); |
| ++i) |
| { |
| elf_symbol::type type = (*i)->get_type(); |
| elf_symbol::binding binding = (*i)->get_binding(); |
| |
| if ((type == elf_symbol::FUNC_TYPE |
| || type == elf_symbol::GNU_IFUNC_TYPE |
| || type == elf_symbol::COMMON_TYPE) |
| && (binding == elf_symbol::GLOBAL_BINDING |
| || binding == elf_symbol::WEAK_BINDING)) |
| { |
| func_syms.push_back(*i); |
| found = true; |
| } |
| } |
| } |
| |
| return found; |
| } |
| |
| // --------------------------------------- |
| // <location expression evaluation types> |
| // --------------------------------------- |
| |
| /// An abstraction of a value representing the result of the |
| /// evaluation of a dwarf expression. This is abstraction represents |
| /// a partial view on the possible values because we are only |
| /// interested in extracting the latest and longuest constant |
| /// sub-expression of a given dwarf expression. |
| class expr_result |
| { |
| bool is_const_; |
| int64_t const_value_; |
| |
| public: |
| expr_result() |
| : is_const_(true), |
| const_value_(0) |
| {} |
| |
| expr_result(bool is_const) |
| : is_const_(is_const), |
| const_value_(0) |
| {} |
| |
| explicit expr_result(int64_t v) |
| :is_const_(true), |
| const_value_(v) |
| {} |
| |
| /// @return true if the value is a constant. Otherwise, return |
| /// false, meaning the value represents a quantity for which we need |
| /// inferior (a running program) state to determine the value. |
| bool |
| is_const() const |
| {return is_const_;} |
| |
| |
| /// @param f a flag saying if the value is set to a constant or not. |
| void |
| is_const(bool f) |
| {is_const_ = f;} |
| |
| /// Get the current constant value iff this represents a |
| /// constant. |
| /// |
| /// @param value the out parameter. Is set to the constant value of |
| /// the @ref expr_result. This is set iff the function return true. |
| /// |
| ///@return true if this has a constant value, false otherwise. |
| bool |
| const_value(int64_t& value) |
| { |
| if (is_const()) |
| { |
| value = const_value_; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Getter of the constant value of the current @ref expr_result. |
| /// |
| /// Note that the current @ref expr_result must be constant, |
| /// otherwise the current process is aborted. |
| /// |
| /// @return the constant value of the current @ref expr_result. |
| int64_t |
| const_value() const |
| { |
| ABG_ASSERT(is_const()); |
| return const_value_; |
| } |
| |
| operator int64_t() const |
| {return const_value();} |
| |
| expr_result& |
| operator=(const int64_t v) |
| { |
| const_value_ = v; |
| return *this; |
| } |
| |
| bool |
| operator==(const expr_result& o) const |
| {return const_value_ == o.const_value_ && is_const_ == o.is_const_;} |
| |
| bool |
| operator>=(const expr_result& o) const |
| {return const_value_ >= o.const_value_;} |
| |
| bool |
| operator<=(const expr_result& o) const |
| {return const_value_ <= o.const_value_;} |
| |
| bool |
| operator>(const expr_result& o) const |
| {return const_value_ > o.const_value_;} |
| |
| bool |
| operator<(const expr_result& o) const |
| {return const_value_ < o.const_value_;} |
| |
| expr_result |
| operator+(const expr_result& v) const |
| { |
| expr_result r(*this); |
| r.const_value_ += v.const_value_; |
| r.is_const_ = r.is_const_ && v.is_const_; |
| return r; |
| } |
| |
| expr_result& |
| operator+=(int64_t v) |
| { |
| const_value_ += v; |
| return *this; |
| } |
| |
| expr_result |
| operator-(const expr_result& v) const |
| { |
| expr_result r(*this); |
| r.const_value_ -= v.const_value_; |
| r.is_const_ = r.is_const_ && v.is_const_; |
| return r; |
| } |
| |
| expr_result |
| operator%(const expr_result& v) const |
| { |
| expr_result r(*this); |
| r.const_value_ %= v.const_value_; |
| r.is_const_ = r.is_const_ && v.is_const(); |
| return r; |
| } |
| |
| expr_result |
| operator*(const expr_result& v) const |
| { |
| expr_result r(*this); |
| r.const_value_ *= v.const_value_; |
| r.is_const_ = r.is_const_ && v.is_const(); |
| return r; |
| } |
| |
| expr_result |
| operator|(const expr_result& v) const |
| { |
| expr_result r(*this); |
| r.const_value_ |= v.const_value_; |
| r.is_const_ = r.is_const_ && v.is_const_; |
| return r; |
| } |
| |
| expr_result |
| operator^(const expr_result& v) const |
| { |
| expr_result r(*this); |
| r.const_value_ ^= v.const_value_; |
| r.is_const_ = r.is_const_ && v.is_const_; |
| return r; |
| } |
| |
| expr_result |
| operator>>(const expr_result& v) const |
| { |
| expr_result r(*this); |
| r.const_value_ = r.const_value_ >> v.const_value_; |
| r.is_const_ = r.is_const_ && v.is_const_; |
| return r; |
| } |
| |
| expr_result |
| operator<<(const expr_result& v) const |
| { |
| expr_result r(*this); |
| r.const_value_ = r.const_value_ << v.const_value_; |
| r.is_const_ = r.is_const_ && v.is_const_; |
| return r; |
| } |
| |
| expr_result |
| operator~() const |
| { |
| expr_result r(*this); |
| r.const_value_ = ~r.const_value_; |
| return r; |
| } |
| |
| expr_result |
| neg() const |
| { |
| expr_result r(*this); |
| r.const_value_ = -r.const_value_; |
| return r; |
| } |
| |
| expr_result |
| abs() const |
| { |
| expr_result r = *this; |
| r.const_value_ = std::abs(static_cast<long double>(r.const_value())); |
| return r; |
| } |
| |
| expr_result |
| operator&(const expr_result& o) |
| { |
| expr_result r(*this); |
| r.const_value_ &= o.const_value_; |
| r.is_const_ = r.is_const_ && o.is_const_; |
| return r; |
| } |
| |
| expr_result |
| operator/(const expr_result& o) |
| { |
| expr_result r(*this); |
| r.is_const_ = r.is_const_ && o.is_const_; |
| return r.const_value() / o.const_value(); |
| } |
| };// class end expr_result; |
| |
| /// A class that implements a stack of @ref expr_result, to be used in |
| /// the engine evaluating DWARF expressions. |
| class expr_result_stack_type |
| { |
| vector<expr_result> elems_; |
| |
| public: |
| |
| expr_result_stack_type() |
| {elems_.reserve(4);} |
| |
| expr_result& |
| operator[](unsigned i) |
| { |
| unsigned s = elems_.size(); |
| ABG_ASSERT(s > i); |
| return elems_[s - 1 -i]; |
| } |
| |
| const expr_result& |
| operator[](unsigned i) const |
| {return const_cast<expr_result_stack_type*>(this)->operator[](i);} |
| |
| unsigned |
| size() const |
| {return elems_.size();} |
| |
| vector<expr_result>::reverse_iterator |
| begin() |
| {return elems_.rbegin();} |
| |
| const vector<expr_result>::reverse_iterator |
| begin() const |
| {return const_cast<expr_result_stack_type*>(this)->begin();} |
| |
| vector<expr_result>::reverse_iterator |
| end() |
| {return elems_.rend();} |
| |
| const vector<expr_result>::reverse_iterator |
| end() const |
| {return const_cast<expr_result_stack_type*>(this)->end();} |
| |
| expr_result& |
| front() |
| {return elems_.back();} |
| |
| const expr_result& |
| front() const |
| {return const_cast<expr_result_stack_type*>(this)->front();} |
| |
| void |
| push_front(expr_result e) |
| {elems_.push_back(e);} |
| |
| expr_result |
| pop_front() |
| { |
| expr_result r = front(); |
| elems_.pop_back(); |
| return r; |
| } |
| |
| void |
| erase(vector<expr_result>::reverse_iterator i) |
| {elems_.erase(--i.base());} |
| |
| void |
| clear() |
| {elems_.clear();} |
| }; // end class expr_result_stack_type |
| |
| /// Abstraction of the evaluation context of a dwarf expression. |
| struct dwarf_expr_eval_context |
| { |
| expr_result accum; |
| expr_result_stack_type stack; |
| // Is set to true if the result of the expression that got evaluated |
| // is a TLS address. |
| bool set_tls_addr; |
| |
| dwarf_expr_eval_context() |
| : accum(/*is_const=*/false), |
| set_tls_addr(false) |
| { |
| stack.push_front(expr_result(true)); |
| } |
| |
| void |
| reset() |
| { |
| stack.clear(); |
| stack.push_front(expr_result(true)); |
| accum = expr_result(false); |
| set_tls_addr = false; |
| } |
| |
| /// Set a flag to to tell that the result of the expression that got |
| /// evaluated is a TLS address. |
| /// |
| /// @param f true iff the result of the expression that got |
| /// evaluated is a TLS address, false otherwise. |
| void |
| set_tls_address(bool f) |
| {set_tls_addr = f;} |
| |
| /// Getter for the flag that tells if the result of the expression |
| /// that got evaluated is a TLS address. |
| /// |
| /// @return true iff the result of the expression that got evaluated |
| /// is a TLS address. |
| bool |
| set_tls_address() const |
| {return set_tls_addr;} |
| |
| expr_result |
| pop() |
| { |
| expr_result r = stack.front(); |
| stack.pop_front(); |
| return r; |
| } |
| |
| void |
| push(const expr_result& v) |
| {stack.push_front(v);} |
| };//end class dwarf_expr_eval_context |
| |
| // --------------------------------------- |
| // </location expression evaluation types> |
| // --------------------------------------- |
| |
| class reader; |
| |
| typedef shared_ptr<reader> reader_sptr; |
| |
| /// The DWARF reader used to build the ABI corpus from debug info in |
| /// DWARF format. |
| /// |
| /// This type is to be instanciated |
| /// abigail::dwarf::reader::create(). |
| class reader : public elf_based_reader |
| { |
| public: |
| |
| /// A set of containers that contains one container per kind of @ref |
| /// die_source. This allows to associate DIEs to things, depending |
| /// on the source of the DIE. |
| template <typename ContainerType> |
| class die_source_dependant_container_set |
| { |
| ContainerType primary_debug_info_container_; |
| ContainerType alt_debug_info_container_; |
| ContainerType type_unit_container_; |
| |
| public: |
| |
| /// Getter for the container associated to DIEs coming from a |
| /// given @ref die_source. |
| /// |
| /// @param source the die_source for which we want the container. |
| /// |
| /// @return the container that associates DIEs coming from @p |
| /// source to something. |
| ContainerType& |
| get_container(die_source source) |
| { |
| ContainerType *result = 0; |
| switch (source) |
| { |
| case PRIMARY_DEBUG_INFO_DIE_SOURCE: |
| result = &primary_debug_info_container_; |
| break; |
| case ALT_DEBUG_INFO_DIE_SOURCE: |
| result = &alt_debug_info_container_; |
| break; |
| case TYPE_UNIT_DIE_SOURCE: |
| result = &type_unit_container_; |
| break; |
| case NO_DEBUG_INFO_DIE_SOURCE: |
| case NUMBER_OF_DIE_SOURCES: |
| ABG_ASSERT_NOT_REACHED; |
| } |
| return *result; |
| } |
| |
| /// Getter for the container associated to DIEs coming from a |
| /// given @ref die_source. |
| /// |
| /// @param source the die_source for which we want the container. |
| /// |
| /// @return the container that associates DIEs coming from @p |
| /// source to something. |
| const ContainerType& |
| get_container(die_source source) const |
| { |
| return const_cast<die_source_dependant_container_set*>(this)-> |
| get_container(source); |
| } |
| |
| /// Getter for the container associated to DIEs coming from the |
| /// same source as a given DIE. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE which should have the same source as the |
| /// source of the container we want. |
| /// |
| /// @return the container that associates DIEs coming from the |
| /// same source as @p die. |
| ContainerType& |
| get_container(const reader& rdr, const Dwarf_Die *die) |
| { |
| const die_source source = rdr.get_die_source(die); |
| return get_container(source); |
| } |
| |
| /// Getter for the container associated to DIEs coming from the |
| /// same source as a given DIE. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE which should have the same source as the |
| /// source of the container we want. |
| /// |
| /// @return the container that associates DIEs coming from the |
| /// same source as @p die. |
| const ContainerType& |
| get_container(const reader& rdr, const Dwarf_Die *die) const |
| { |
| return const_cast<die_source_dependant_container_set*>(this)-> |
| get_container(rdr, die); |
| } |
| |
| /// Clear the container set. |
| void |
| clear() |
| { |
| primary_debug_info_container_.clear(); |
| alt_debug_info_container_.clear(); |
| type_unit_container_.clear(); |
| } |
| }; // end die_dependant_container_set |
| |
| unsigned short dwarf_version_; |
| Dwarf_Die* cur_tu_die_; |
| mutable dwarf_expr_eval_context dwarf_expr_eval_context_; |
| // A set of maps (one per kind of die source) that associates a decl |
| // string representation with the DIEs (offsets) representing that |
| // decl. |
| mutable die_source_dependant_container_set<istring_dwarf_offsets_map_type> |
| decl_die_repr_die_offsets_maps_; |
| // A set of maps (one per kind of die source) that associates a type |
| // string representation with the DIEs (offsets) representing that |
| // type. |
| mutable die_source_dependant_container_set<istring_dwarf_offsets_map_type> |
| type_die_repr_die_offsets_maps_; |
| mutable die_source_dependant_container_set<die_istring_map_type> |
| die_qualified_name_maps_; |
| mutable die_source_dependant_container_set<die_istring_map_type> |
| die_pretty_repr_maps_; |
| mutable die_source_dependant_container_set<die_istring_map_type> |
| die_pretty_type_repr_maps_; |
| // A set of maps (one per kind of die source) that associates the |
| // offset of a decl die to its corresponding decl artifact. |
| mutable die_source_dependant_container_set<die_artefact_map_type> |
| decl_die_artefact_maps_; |
| // A set of maps (one per kind of die source) that associates the |
| // offset of a type die to its corresponding type artifact. |
| mutable die_source_dependant_container_set<die_artefact_map_type> |
| type_die_artefact_maps_; |
| /// A set of vectors (one per kind of die source) that associates |
| /// the offset of a type DIE to the offset of its canonical DIE. |
| mutable die_source_dependant_container_set<offset_offset_map_type> |
| canonical_type_die_offsets_; |
| /// A set of vectors (one per kind of die source) that associates |
| /// the offset of a decl DIE to the offset of its canonical DIE. |
| mutable die_source_dependant_container_set<offset_offset_map_type> |
| canonical_decl_die_offsets_; |
| /// A map that associates a function type representations to |
| /// function types, inside a translation unit. |
| mutable istring_fn_type_map_type per_tu_repr_to_fn_type_maps_; |
| /// A map that associates a pair of DIE offsets to the result of the |
| /// comparison of that pair. |
| mutable std::unordered_map<std::pair<offset_type,offset_type>, |
| abigail::ir::comparison_result, |
| dwarf_offset_pair_hash> die_comparison_results_; |
| // The set of types pair that have been canonical-type-propagated. |
| mutable offset_pair_set_type propagated_types_; |
| die_class_or_union_map_type die_wip_classes_map_; |
| die_class_or_union_map_type alternate_die_wip_classes_map_; |
| die_class_or_union_map_type type_unit_die_wip_classes_map_; |
| die_function_type_map_type die_wip_function_types_map_; |
| die_function_type_map_type alternate_die_wip_function_types_map_; |
| die_function_type_map_type type_unit_die_wip_function_types_map_; |
| die_function_decl_map_type die_function_with_no_symbol_map_; |
| vector<type_base_sptr> types_to_canonicalize_; |
| string_classes_or_unions_map decl_only_classes_map_; |
| string_enums_map decl_only_enums_map_; |
| die_tu_map_type die_tu_map_; |
| translation_unit_sptr cur_tu_; |
| scope_decl_sptr nil_scope_; |
| scope_stack_type scope_stack_; |
| offset_offset_map_type primary_die_parent_map_; |
| // A map that associates each tu die to a vector of unit import |
| // points, in the main debug info |
| tu_die_imported_unit_points_map_type tu_die_imported_unit_points_map_; |
| // A map that associates each tu die to a vector of unit import |
| // points, in the alternate debug info |
| tu_die_imported_unit_points_map_type alt_tu_die_imported_unit_points_map_; |
| tu_die_imported_unit_points_map_type type_units_tu_die_imported_unit_points_map_; |
| // A DIE -> parent map for DIEs coming from the alternate debug info |
| // file. |
| offset_offset_map_type alternate_die_parent_map_; |
| offset_offset_map_type type_section_die_parent_map_; |
| list<var_decl_sptr> var_decls_to_add_; |
| #ifdef WITH_DEBUG_TYPE_CANONICALIZATION |
| bool debug_die_canonicalization_is_on_; |
| bool use_canonical_die_comparison_; |
| #endif |
| mutable size_t compare_count_; |
| mutable size_t canonical_propagated_count_; |
| mutable size_t cancelled_propagation_count_; |
| mutable optional<bool> leverage_dwarf_factorization_; |
| |
| protected: |
| |
| reader() = delete; |
| |
| /// Constructor of reader. |
| /// |
| /// @param elf_path the path to the elf file the context is to be |
| /// used for. |
| /// |
| /// @param debug_info_root_paths a vector of pointers to the path to |
| /// the root directory under which the debug info is to be found for |
| /// @p elf_path. Leave this empty if the debug info is not in a |
| /// split file. |
| /// |
| /// @param environment the environment used by the current context. |
| /// This environment contains resources needed by the DWARF reader and by |
| /// the types and declarations that are to be created later. Note |
| /// that ABI artifacts that are to be compared all need to be |
| /// created within the same environment. |
| /// |
| /// Please also note that the life time of this environment object |
| /// must be greater than the life time of the resulting @ref |
| /// reader the context uses resources that are allocated in |
| /// the environment. |
| /// |
| /// @param load_all_types if set to false only the types that are |
| /// reachable from publicly exported declarations (of functions and |
| /// variables) are read. If set to true then all types found in the |
| /// debug information are loaded. |
| /// |
| /// @param linux_kernel_mode if set to true, then consider the special |
| /// linux kernel symbol tables when determining if a symbol is |
| /// exported or not. |
| reader(const string& elf_path, |
| const vector<char**>& debug_info_root_paths, |
| environment& environment, |
| bool load_all_types, |
| bool linux_kernel_mode) |
| : elf_based_reader(elf_path, |
| debug_info_root_paths, |
| environment) |
| { |
| initialize(load_all_types, linux_kernel_mode); |
| } |
| |
| public: |
| |
| /// Initializer of reader. |
| /// |
| /// Resets the reader so that it can be re-used to read another binary. |
| /// |
| /// @param load_all_types if set to false only the types that are |
| /// reachable from publicly exported declarations (of functions and |
| /// variables) are read. If set to true then all types found in the |
| /// debug information are loaded. |
| /// |
| /// @param linux_kernel_mode if set to true, then consider the |
| /// special linux kernel symbol tables when determining if a symbol |
| /// is exported or not. |
| void |
| initialize(bool load_all_types, bool linux_kernel_mode) |
| { |
| dwarf_version_ = 0; |
| cur_tu_die_ = 0; |
| decl_die_repr_die_offsets_maps_.clear(); |
| type_die_repr_die_offsets_maps_.clear(); |
| die_qualified_name_maps_.clear(); |
| die_pretty_repr_maps_.clear(); |
| die_pretty_type_repr_maps_.clear(); |
| decl_die_artefact_maps_.clear(); |
| type_die_artefact_maps_.clear(); |
| canonical_type_die_offsets_.clear(); |
| canonical_decl_die_offsets_.clear(); |
| die_wip_classes_map_.clear(); |
| alternate_die_wip_classes_map_.clear(); |
| type_unit_die_wip_classes_map_.clear(); |
| die_wip_function_types_map_.clear(); |
| alternate_die_wip_function_types_map_.clear(); |
| type_unit_die_wip_function_types_map_.clear(); |
| die_function_with_no_symbol_map_.clear(); |
| types_to_canonicalize_.clear(); |
| decl_only_classes_map_.clear(); |
| die_tu_map_.clear(); |
| corpus().reset(); |
| corpus_group().reset(); |
| cur_tu_.reset(); |
| primary_die_parent_map_.clear(); |
| tu_die_imported_unit_points_map_.clear(); |
| alt_tu_die_imported_unit_points_map_.clear(); |
| type_units_tu_die_imported_unit_points_map_.clear(); |
| alternate_die_parent_map_.clear(); |
| type_section_die_parent_map_.clear(); |
| var_decls_to_add_.clear(); |
| clear_per_translation_unit_data(); |
| options().load_in_linux_kernel_mode = linux_kernel_mode; |
| options().load_all_types = load_all_types; |
| #ifdef WITH_DEBUG_TYPE_CANONICALIZATION |
| debug_die_canonicalization_is_on_ = |
| env().debug_die_canonicalization_is_on(); |
| use_canonical_die_comparison_ = true; |
| #endif |
| compare_count_ = 0; |
| canonical_propagated_count_ = 0; |
| cancelled_propagation_count_ = 0; |
| load_in_linux_kernel_mode(linux_kernel_mode); |
| } |
| |
| /// Initializer of reader. |
| /// |
| /// Resets the reader so that it can be re-used to read another binary. |
| /// |
| /// @param elf_path the path to the new ELF file. |
| /// |
| /// @param debug_info_root_paths the vector of debug-info path to |
| /// look for split debug info. |
| /// |
| /// @param load_all_types if set to false only the types that are |
| /// reachable from publicly exported declarations (of functions and |
| /// variables) are read. If set to true then all types found in the |
| /// debug information are loaded. |
| /// |
| /// @param linux_kernel_mode if set to true, then consider the |
| /// special linux kernel symbol tables when determining if a symbol |
| /// is exported or not. |
| void |
| initialize(const string& elf_path, |
| const vector<char**>& debug_info_root_paths, |
| bool load_all_types, |
| bool linux_kernel_mode) |
| { |
| elf_based_reader::initialize(elf_path, debug_info_root_paths); |
| initialize(load_all_types, linux_kernel_mode); |
| } |
| |
| /// Create an instance of DWARF Reader. |
| /// |
| /// @param elf_path the path to the ELF file to read from. |
| /// |
| /// @param debug_info_root_paths a vector of paths where to look up |
| /// split debug info files. |
| /// |
| /// @param environment the environment to be used by the reader. |
| /// |
| /// @param load_all_types if set to false only the types that are |
| /// reachable from publicly exported declarations (of functions and |
| /// variables) are read. If set to true then all types found in the |
| /// debug information are loaded. |
| /// |
| /// @param linux_kernel_mode if set to true, then consider the |
| /// special linux kernel symbol tables when determining if a symbol |
| /// is exported or not. |
| static dwarf::reader_sptr |
| create(const std::string& elf_path, |
| const vector<char**>& debug_info_root_paths, |
| environment& environment, |
| bool load_all_types, |
| bool linux_kernel_mode) |
| { |
| reader_sptr result(new reader(elf_path, debug_info_root_paths, |
| environment, load_all_types, |
| linux_kernel_mode)); |
| return result; |
| } |
| |
| /// Destructor of the @ref reader type. |
| ~reader() |
| { |
| } |
| |
| /// Read and analyze the ELF and DWARF information associated with |
| /// the underlying ELF file and build an ABI corpus out of it. |
| /// |
| /// @param status output parameter. This is set to the status of |
| /// the analysis of the debug info. |
| /// |
| /// @return the resulting ABI corpus. |
| corpus_sptr |
| read_corpus(status& status) |
| { |
| status = STATUS_UNKNOWN; |
| |
| // Load the generic ELF parts of the corpus. |
| elf::reader::read_corpus(status); |
| |
| if (!(status & STATUS_OK)) |
| { |
| // Something went badly wrong. There is nothing we can do |
| // with this ELF file. Bail out. |
| return corpus_sptr(); |
| } |
| |
| // If we couldn't find debug info from the elf path, then say it. |
| if (dwarf_debug_info() == nullptr) |
| status |= STATUS_DEBUG_INFO_NOT_FOUND; |
| |
| { |
| string alt_di_path; |
| if (refers_to_alt_debug_info(alt_di_path) |
| && !alternate_dwarf_debug_info()) |
| status |= STATUS_ALT_DEBUG_INFO_NOT_FOUND; |
| } |
| |
| if (// If debug info was found but not the required alternate debug |
| // info ... |
| ((status & STATUS_ALT_DEBUG_INFO_NOT_FOUND) |
| && !(status & STATUS_DEBUG_INFO_NOT_FOUND))) |
| // ... then we cannot handle the binary. |
| return corpus_sptr(); |
| |
| // Read the variable and function descriptions from the debug info |
| // we have, through the dwfl handle. |
| corpus_sptr corp = read_debug_info_into_corpus(); |
| |
| status |= STATUS_OK; |
| |
| return corp; |
| } |
| |
| /// Read an analyze the DWARF information. |
| /// |
| /// Construct an ABI corpus from it. |
| /// |
| /// This is a sub-routine of abigail::dwarf::reader::read_corpus(). |
| /// |
| /// @return the resulting ABI corpus. |
| corpus_sptr |
| read_debug_info_into_corpus() |
| { |
| clear_per_corpus_data(); |
| |
| // First set some mundane properties of the corpus gathered from |
| // ELF. |
| corpus::origin origin = corpus()->get_origin(); |
| origin |= corpus::DWARF_ORIGIN; |
| corpus()->set_origin(origin); |
| |
| if (origin & corpus::LINUX_KERNEL_BINARY_ORIGIN |
| && !env().user_set_analyze_exported_interfaces_only()) |
| // So we are looking at the Linux Kernel and the user has not set |
| // any particular option regarding the amount of types to analyse. |
| // In that case, we need to only analyze types that are reachable |
| // from exported interfaces otherwise we get such a massive amount |
| // of type DIEs to look at that things are just too slow down the |
| // road. |
| env().analyze_exported_interfaces_only(true); |
| |
| corpus()->set_soname(dt_soname()); |
| corpus()->set_needed(dt_needed()); |
| corpus()->set_architecture_name(elf_architecture()); |
| // Set symbols information to the corpus. |
| corpus()->set_symtab(symtab()); |
| |
| // Get out now if no debug info is found or if the symbol table is |
| // empty. |
| if (!dwarf_debug_info() |
| || !corpus()->get_symtab() |
| || !corpus()->get_symtab()->has_symbols()) |
| return corpus(); |
| |
| uint8_t address_size = 0; |
| size_t header_size = 0; |
| |
| #ifdef WITH_DEBUG_SELF_COMPARISON |
| if (env().self_comparison_debug_is_on()) |
| env().set_self_comparison_debug_input(corpus()); |
| #endif |
| |
| env().priv_->do_log(do_log()); |
| |
| // Walk all the DIEs of the debug info to build a DIE -> parent map |
| // useful for get_die_parent() to work. |
| { |
| tools_utils::timer t; |
| if (do_log()) |
| { |
| cerr << "building die -> parent maps ..."; |
| t.start(); |
| } |
| |
| build_die_parent_maps(); |
| |
| if (do_log()) |
| { |
| t.stop(); |
| cerr << " DONE@" << corpus()->get_path() |
| << ":" |
| << t |
| << "\n"; |
| } |
| } |
| |
| env().canonicalization_is_done(false); |
| |
| { |
| tools_utils::timer t; |
| if (do_log()) |
| { |
| cerr << "building the libabigail internal representation ...\n"; |
| t.start(); |
| } |
| // And now walk all the DIEs again to build the libabigail IR. |
| Dwarf_Half dwarf_vers = 0; |
| for (Dwarf_Off offset = 0, next_offset = 0; |
| (dwarf_next_unit(const_cast<Dwarf*>(dwarf_debug_info()), |
| offset, &next_offset, &header_size, |
| &dwarf_vers, NULL, &address_size, NULL, |
| NULL, NULL) == 0); |
| offset = next_offset) |
| { |
| Dwarf_Off die_offset = offset + header_size; |
| Dwarf_Die unit; |
| if (!dwarf_offdie(const_cast<Dwarf*>(dwarf_debug_info()), |
| die_offset, &unit) |
| || dwarf_tag(&unit) != DW_TAG_compile_unit) |
| continue; |
| |
| dwarf_version(dwarf_vers); |
| |
| address_size *= 8; |
| |
| // Build a translation_unit IR node from cu; note that cu must |
| // be a DW_TAG_compile_unit die. |
| translation_unit_sptr ir_node = |
| build_translation_unit_and_add_to_ir(*this, &unit, address_size); |
| ABG_ASSERT(ir_node); |
| } |
| if (do_log()) |
| { |
| t.stop(); |
| cerr << "building the libabigail internal representation " |
| << "DONE for corpus << corpus()->get_path()" |
| << " in :" |
| << t |
| << "\n"; |
| |
| cerr << "Number of aggregate types compared: " |
| << compare_count_ << "\n" |
| << "Number of canonical types propagated: " |
| << canonical_propagated_count_ << "\n" |
| << "Number of cancelled propagated canonical types:" |
| << cancelled_propagation_count_ << "\n"; |
| } |
| } |
| |
| { |
| tools_utils::timer t; |
| if (do_log()) |
| { |
| cerr << "resolving declaration only classes ..."; |
| t.start(); |
| } |
| resolve_declaration_only_classes(); |
| if (do_log()) |
| { |
| t.stop(); |
| cerr << " DONE@" << corpus()->get_path() |
| << ":" |
| << t |
| <<"\n"; |
| } |
| } |
| |
| { |
| tools_utils::timer t; |
| if (do_log()) |
| { |
| cerr << "resolving declaration only enums ..."; |
| t.start(); |
| } |
| resolve_declaration_only_enums(); |
| if (do_log()) |
| { |
| t.stop(); |
| cerr << " DONE@" << corpus()->get_path() |
| << ":" |
| << t |
| <<"\n"; |
| } |
| } |
| |
| { |
| tools_utils::timer t; |
| if (do_log()) |
| { |
| cerr << "fixing up functions with linkage name but " |
| << "no advertised underlying symbols ...."; |
| t.start(); |
| } |
| fixup_functions_with_no_symbols(); |
| if (do_log()) |
| { |
| t.stop(); |
| cerr << " DONE@" << corpus()->get_path() |
| <<":" |
| << t |
| <<"\n"; |
| } |
| } |
| |
| merge_member_functions_in_classes_of_same_names(); |
| |
| /// Now, look at the types that needs to be canonicalized after the |
| /// translation has been constructed (which is just now) and |
| /// canonicalize them. |
| /// |
| /// These types need to be constructed at the end of the translation |
| /// unit reading phase because some types are modified by some DIEs |
| /// even after the principal DIE describing the type has been read; |
| /// this happens for clones of virtual destructors (for instance) or |
| /// even for some static data members. We need to do that for types |
| /// are in the alternate debug info section and for types that in |
| /// the main debug info section. |
| { |
| tools_utils::timer t; |
| if (do_log()) |
| { |
| cerr << "perform late type canonicalizing ...\n"; |
| t.start(); |
| } |
| |
| perform_late_type_canonicalizing(); |
| if (do_log()) |
| { |
| t.stop(); |
| cerr << "late type canonicalizing DONE for " |
| << corpus()->get_path() |
| << " in :" |
| << t |
| << "\n"; |
| } |
| } |
| |
| env().canonicalization_is_done(true); |
| |
| { |
| tools_utils::timer t; |
| if (do_log()) |
| { |
| cerr << "sort functions and variables ..."; |
| t.start(); |
| } |
| corpus()->sort_functions(); |
| corpus()->sort_variables(); |
| if (do_log()) |
| { |
| t.stop(); |
| cerr << " DONE@" << corpus()->get_path() |
| << ":" |
| << t |
| <<" \n"; |
| } |
| } |
| |
| return corpus(); |
| } |
| |
| /// Clear the data that is relevant only for the current translation |
| /// unit being read. The rest of the data is relevant for the |
| /// entire ABI corpus. |
| void |
| clear_per_translation_unit_data() |
| { |
| while (!scope_stack().empty()) |
| scope_stack().pop(); |
| var_decls_to_re_add_to_tree().clear(); |
| per_tu_repr_to_fn_type_maps().clear(); |
| } |
| |
| /// Clear the data that is relevant for the current corpus being |
| /// read. |
| void |
| clear_per_corpus_data() |
| { |
| die_qualified_name_maps_.clear(); |
| die_pretty_repr_maps_.clear(); |
| die_pretty_type_repr_maps_.clear(); |
| clear_types_to_canonicalize(); |
| } |
| |
| /// Getter for the current environment. |
| /// |
| /// @return the current environment. |
| environment& |
| env() |
| {return options().env;} |
| |
| /// Getter for the current environment. |
| /// |
| /// @return the current environment. |
| const environment& |
| env() const |
| {return const_cast<reader*>(this)->env();} |
| |
| /// Getter for the flag that tells us if we are dropping functions |
| /// and variables that have undefined symbols. |
| /// |
| /// @return true iff we are dropping functions and variables that have |
| /// undefined symbols. |
| bool |
| drop_undefined_syms() const |
| {return options().drop_undefined_syms;} |
| |
| /// Setter for the flag that tells us if we are dropping functions |
| /// and variables that have undefined symbols. |
| /// |
| /// @param f the new value of the flag. |
| void |
| drop_undefined_syms(bool f) |
| {options().drop_undefined_syms = f;} |
| |
| /// Getter of the DWARF version. |
| unsigned short |
| dwarf_version() const |
| {return dwarf_version_;} |
| |
| void |
| dwarf_version(unsigned short v) |
| {dwarf_version_ = v;} |
| |
| /// Return the ELF descriptor used for DWARF access. |
| /// |
| /// This can be the same as reader::elf_handle() above, if the |
| /// DWARF info is in the same ELF file as the one of the binary we |
| /// are analizing. It is different if e.g, the debug info is split |
| /// from the ELF file we are analizing. |
| /// |
| /// @return a pointer to the ELF descriptor used to access debug |
| /// info. |
| Elf* |
| dwarf_elf_handle() const |
| {return dwarf_getelf(const_cast<Dwarf*>(dwarf_debug_info()));} |
| |
| /// Test if the debug information is in a separate ELF file wrt the |
| /// main ELF file of the program (application or shared library) we |
| /// are analizing. |
| /// |
| /// @return true if the debug information is in a separate ELF file |
| /// compared to the main ELF file of the program (application or |
| /// shared library) that we are looking at. |
| bool |
| dwarf_is_splitted() const |
| {return dwarf_elf_handle() != elf_handle();} |
| |
| /// Return the correct debug info, depending on the DIE source we |
| /// are looking at. |
| /// |
| /// @param source the DIE source to consider. |
| /// |
| /// @return the right debug info, depending on @p source. |
| const Dwarf* |
| dwarf_per_die_source(die_source source) const |
| { |
| const Dwarf *result = 0; |
| switch(source) |
| { |
| case PRIMARY_DEBUG_INFO_DIE_SOURCE: |
| case TYPE_UNIT_DIE_SOURCE: |
| result = dwarf_debug_info(); |
| break; |
| case ALT_DEBUG_INFO_DIE_SOURCE: |
| result = alternate_dwarf_debug_info(); |
| break; |
| case NO_DEBUG_INFO_DIE_SOURCE: |
| case NUMBER_OF_DIE_SOURCES: |
| ABG_ASSERT_NOT_REACHED; |
| } |
| return result; |
| } |
| |
| /// Return the path to the ELF path we are reading. |
| /// |
| /// @return the elf path. |
| const string& |
| elf_path() const |
| {return corpus_path();} |
| |
| const Dwarf_Die* |
| cur_tu_die() const |
| {return cur_tu_die_;} |
| |
| void |
| cur_tu_die(Dwarf_Die* cur_tu_die) |
| {cur_tu_die_ = cur_tu_die;} |
| |
| dwarf_expr_eval_context& |
| dwarf_expr_eval_ctxt() const |
| {return dwarf_expr_eval_context_;} |
| |
| /// Getter of the maps set that associates a representation of a |
| /// decl DIE to a vector of offsets of DIEs having that representation. |
| /// |
| /// @return the maps set that associates a representation of a decl |
| /// DIE to a vector of offsets of DIEs having that representation. |
| const die_source_dependant_container_set<istring_dwarf_offsets_map_type>& |
| decl_die_repr_die_offsets_maps() const |
| {return decl_die_repr_die_offsets_maps_;} |
| |
| /// Getter of the maps set that associates a representation of a |
| /// decl DIE to a vector of offsets of DIEs having that representation. |
| /// |
| /// @return the maps set that associates a representation of a decl |
| /// DIE to a vector of offsets of DIEs having that representation. |
| die_source_dependant_container_set<istring_dwarf_offsets_map_type>& |
| decl_die_repr_die_offsets_maps() |
| {return decl_die_repr_die_offsets_maps_;} |
| |
| /// Getter of the maps set that associate a representation of a type |
| /// DIE to a vector of offsets of DIEs having that representation. |
| /// |
| /// @return the maps set that associate a representation of a type |
| /// DIE to a vector of offsets of DIEs having that representation. |
| const die_source_dependant_container_set<istring_dwarf_offsets_map_type>& |
| type_die_repr_die_offsets_maps() const |
| {return type_die_repr_die_offsets_maps_;} |
| |
| /// Getter of the maps set that associate a representation of a type |
| /// DIE to a vector of offsets of DIEs having that representation. |
| /// |
| /// @return the maps set that associate a representation of a type |
| /// DIE to a vector of offsets of DIEs having that representation. |
| die_source_dependant_container_set<istring_dwarf_offsets_map_type>& |
| type_die_repr_die_offsets_maps() |
| {return type_die_repr_die_offsets_maps_;} |
| |
| |
| /// Compute the offset of the canonical DIE of a given DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param canonical_die_offset out parameter. This is set to the |
| /// resulting canonical DIE that was computed. |
| /// |
| /// @param die_as_type if yes, it means @p die has to be considered |
| /// as a type. |
| void |
| compute_canonical_die_offset(const Dwarf_Die *die, |
| Dwarf_Off &canonical_die_offset, |
| bool die_as_type) const |
| { |
| offset_offset_map_type &canonical_dies = |
| die_as_type |
| ? const_cast<reader*>(this)->canonical_type_die_offsets_. |
| get_container(*this, die) |
| : const_cast<reader*>(this)->canonical_decl_die_offsets_. |
| get_container(*this, die); |
| |
| Dwarf_Die canonical_die; |
| compute_canonical_die(die, canonical_dies, canonical_die, die_as_type); |
| |
| canonical_die_offset = dwarf_dieoffset(&canonical_die); |
| } |
| |
| /// Compute (find) the canonical DIE of a given DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param canonical_dies the vector in which the canonical dies ar |
| /// stored. The index of each element is the offset of the DIE we |
| /// want the canonical DIE for. And the value of the element at |
| /// that index is the canonical DIE offset we are looking for. |
| /// |
| /// @param canonical_die_offset out parameter. This is set to the |
| /// resulting canonical DIE that was computed. |
| /// |
| /// @param die_as_type if yes, it means @p die has to be considered |
| /// as a type. |
| void |
| compute_canonical_die(const Dwarf_Die *die, |
| offset_offset_map_type& canonical_dies, |
| Dwarf_Die &canonical_die, |
| bool die_as_type) const |
| { |
| const die_source source = get_die_source(die); |
| |
| Dwarf_Off die_offset = dwarf_dieoffset(const_cast<Dwarf_Die*>(die)); |
| |
| compute_canonical_die(die_offset, source, |
| canonical_dies, |
| canonical_die, die_as_type); |
| } |
| |
| /// Compute (find) the canonical DIE of a given DIE. |
| /// |
| /// @param die_offset the offset of the DIE to consider. |
| /// |
| /// @param source the source of the DIE to consider. |
| /// |
| /// @param canonical_dies the vector in which the canonical dies ar |
| /// stored. The index of each element is the offset of the DIE we |
| /// want the canonical DIE for. And the value of the element at |
| /// that index is the canonical DIE offset we are looking for. |
| /// |
| /// @param canonical_die_offset out parameter. This is set to the |
| /// resulting canonical DIE that was computed. |
| /// |
| /// @param die_as_type if yes, it means @p die has to be considered |
| /// as a type. |
| void |
| compute_canonical_die(Dwarf_Off die_offset, |
| die_source source, |
| offset_offset_map_type& canonical_dies, |
| Dwarf_Die &canonical_die, |
| bool die_as_type) const |
| { |
| // The map that associates the string representation of 'die' |
| // with a vector of offsets of potentially equivalent DIEs. |
| istring_dwarf_offsets_map_type& map = |
| die_as_type |
| ? (const_cast<reader*>(this)-> |
| type_die_repr_die_offsets_maps().get_container(source)) |
| : (const_cast<reader*>(this)-> |
| decl_die_repr_die_offsets_maps().get_container(source)); |
| |
| Dwarf_Die die; |
| ABG_ASSERT(dwarf_offdie(const_cast<Dwarf*>(dwarf_per_die_source(source)), |
| die_offset, &die)); |
| |
| // The variable repr is the the string representation of 'die'. |
| // |
| // Even if die_as_type is true -- which means that 'die' is said |
| // to be considered as a type -- we always consider a |
| // DW_TAG_subprogram DIE as a decl here, as far as its string |
| // representation is concerned. |
| interned_string name = |
| (die_as_type) |
| ? get_die_pretty_type_representation(&die, /*where=*/0) |
| : get_die_pretty_representation(&die, /*where=*/0); |
| |
| Dwarf_Off canonical_die_offset = 0; |
| istring_dwarf_offsets_map_type::iterator i = map.find(name); |
| if (i == map.end()) |
| { |
| dwarf_offsets_type offsets; |
| offsets.push_back(die_offset); |
| map[name] = offsets; |
| set_canonical_die_offset(canonical_dies, die_offset, die_offset); |
| get_die_from_offset(source, die_offset, &canonical_die); |
| return; |
| } |
| |
| Dwarf_Off cur_die_offset; |
| Dwarf_Die potential_canonical_die; |
| for (dwarf_offsets_type::const_iterator o = i->second.begin(); |
| o != i->second.end(); |
| ++o) |
| { |
| cur_die_offset = *o; |
| get_die_from_offset(source, cur_die_offset, &potential_canonical_die); |
| if (compare_dies(*this, &die, &potential_canonical_die, |
| /*update_canonical_dies_on_the_fly=*/false)) |
| { |
| canonical_die_offset = cur_die_offset; |
| set_canonical_die_offset(canonical_dies, die_offset, |
| canonical_die_offset); |
| get_die_from_offset(source, canonical_die_offset, &canonical_die); |
| return; |
| } |
| } |
| |
| canonical_die_offset = die_offset; |
| i->second.push_back(die_offset); |
| set_canonical_die_offset(canonical_dies, die_offset, die_offset); |
| get_die_from_offset(source, canonical_die_offset, &canonical_die); |
| } |
| |
| /// Getter of the canonical DIE of a given DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param canonical_die output parameter. Is set to the resulting |
| /// canonical die, if this function returns true. |
| /// |
| /// @param where the offset of the logical DIE we are supposed to be |
| /// calling this function from. If set to zero this means this is |
| /// to be ignored. |
| /// |
| /// @param die_as_type if set to yes, it means @p die is to be |
| /// considered as a type DIE. |
| /// |
| /// @return true iff a canonical DIE was found for @p die. |
| bool |
| get_canonical_die(const Dwarf_Die *die, |
| Dwarf_Die &canonical_die, |
| size_t where, |
| bool die_as_type) |
| { |
| const die_source source = get_die_source(die); |
| |
| offset_offset_map_type &canonical_dies = |
| die_as_type |
| ? const_cast<reader*>(this)->canonical_type_die_offsets_. |
| get_container(source) |
| : const_cast<reader*>(this)->canonical_decl_die_offsets_. |
| get_container(source); |
| |
| Dwarf_Off die_offset = dwarf_dieoffset(const_cast<Dwarf_Die*>(die)); |
| if (Dwarf_Off canonical_die_offset = |
| get_canonical_die_offset(canonical_dies, die_offset)) |
| { |
| get_die_from_offset(source, canonical_die_offset, &canonical_die); |
| return true; |
| } |
| |
| // The map that associates the string representation of 'die' |
| // with a vector of offsets of potentially equivalent DIEs. |
| istring_dwarf_offsets_map_type& map = |
| die_as_type |
| ? (const_cast<reader*>(this)-> |
| type_die_repr_die_offsets_maps().get_container(*this, die)) |
| : (const_cast<reader*>(this)-> |
| decl_die_repr_die_offsets_maps().get_container(*this, die)); |
| |
| // The variable repr is the the string representation of 'die'. |
| // |
| // Even if die_as_type is true -- which means that 'die' is said |
| // to be considered as a type -- we always consider a |
| // DW_TAG_subprogram DIE as a decl here, as far as its string |
| // representation is concerned. |
| interned_string name = |
| (die_as_type /*&& dwarf_tag(die) != DW_TAG_subprogram*/) |
| ? get_die_pretty_type_representation(die, where) |
| : get_die_pretty_representation(die, where); |
| |
| istring_dwarf_offsets_map_type::iterator i = map.find(name); |
| if (i == map.end()) |
| return false; |
| |
| Dwarf_Off cur_die_offset; |
| for (dwarf_offsets_type::const_iterator o = i->second.begin(); |
| o != i->second.end(); |
| ++o) |
| { |
| cur_die_offset = *o; |
| get_die_from_offset(source, cur_die_offset, &canonical_die); |
| // compare die and canonical_die. |
| if (compare_dies_during_canonicalization(const_cast<reader&>(*this), |
| die, &canonical_die, |
| /*update_canonical_dies_on_the_fly=*/true)) |
| { |
| set_canonical_die_offset(canonical_dies, |
| die_offset, |
| cur_die_offset); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /// Retrieve the canonical DIE of a given DIE. |
| /// |
| /// The canonical DIE is a DIE that is structurally equivalent to |
| /// this one. |
| /// |
| /// Note that this function caches the canonical DIE that was |
| /// computed. Subsequent invocations of this function on the same |
| /// DIE return the same cached DIE. |
| /// |
| /// @param die the DIE to get a canonical type for. |
| /// |
| /// @param canonical_die the resulting canonical DIE. |
| /// |
| /// @param where the offset of the logical DIE we are supposed to be |
| /// calling this function from. If set to zero this means this is |
| /// to be ignored. |
| /// |
| /// @param die_as_type if true, consider DIE is a type. |
| /// |
| /// @return true if an *existing* canonical DIE was found. |
| /// Otherwise, @p die is considered as being a canonical DIE for |
| /// itself. @p canonical_die is thus set to the canonical die in |
| /// either cases. |
| bool |
| get_or_compute_canonical_die(const Dwarf_Die* die, |
| Dwarf_Die& canonical_die, |
| size_t where, |
| bool die_as_type) const |
| { |
| const die_source source = get_die_source(die); |
| |
| offset_offset_map_type &canonical_dies = |
| die_as_type |
| ? const_cast<reader*>(this)->canonical_type_die_offsets_. |
| get_container(source) |
| : const_cast<reader*>(this)->canonical_decl_die_offsets_. |
| get_container(source); |
| |
| Dwarf_Off initial_die_offset = dwarf_dieoffset(const_cast<Dwarf_Die*>(die)); |
| |
| if (Dwarf_Off canonical_die_offset = |
| get_canonical_die_offset(canonical_dies, |
| initial_die_offset)) |
| { |
| get_die_from_offset(source, canonical_die_offset, &canonical_die); |
| return true; |
| } |
| |
| if (!is_type_die_to_be_canonicalized(die)) |
| return false; |
| |
| // The map that associates the string representation of 'die' |
| // with a vector of offsets of potentially equivalent DIEs. |
| istring_dwarf_offsets_map_type& map = |
| die_as_type |
| ? (const_cast<reader*>(this)-> |
| type_die_repr_die_offsets_maps().get_container(*this, die)) |
| : (const_cast<reader*>(this)-> |
| decl_die_repr_die_offsets_maps().get_container(*this, die)); |
| |
| // The variable repr is the the string representation of 'die'. |
| // |
| // Even if die_as_type is true -- which means that 'die' is said |
| // to be considered as a type -- we always consider a |
| // DW_TAG_subprogram DIE as a decl here, as far as its string |
| // representation is concerned. |
| interned_string name = |
| (die_as_type) |
| ? get_die_pretty_type_representation(die, where) |
| : get_die_pretty_representation(die, where); |
| |
| istring_dwarf_offsets_map_type::iterator i = map.find(name); |
| if (i == map.end()) |
| { |
| dwarf_offsets_type offsets; |
| offsets.push_back(initial_die_offset); |
| map[name] = offsets; |
| get_die_from_offset(source, initial_die_offset, &canonical_die); |
| set_canonical_die_offset(canonical_dies, |
| initial_die_offset, |
| initial_die_offset); |
| return false; |
| } |
| |
| // walk i->second without any iterator (using a while loop rather |
| // than a for loop) because compare_dies might add new content to |
| // the end of the i->second vector during the walking. |
| dwarf_offsets_type::size_type n = 0, s = i->second.size(); |
| while (n < s) |
| { |
| Dwarf_Off die_offset = i->second[n]; |
| get_die_from_offset(source, die_offset, &canonical_die); |
| // compare die and canonical_die. |
| if (compare_dies_during_canonicalization(const_cast<reader&>(*this), |
| die, &canonical_die, |
| /*update_canonical_dies_on_the_fly=*/true)) |
| { |
| set_canonical_die_offset(canonical_dies, |
| initial_die_offset, |
| die_offset); |
| return true; |
| } |
| ++n; |
| } |
| |
| // We didn't find a canonical DIE for 'die'. So let's consider |
| // that it is its own canonical DIE. |
| get_die_from_offset(source, initial_die_offset, &canonical_die); |
| i->second.push_back(initial_die_offset); |
| set_canonical_die_offset(canonical_dies, |
| initial_die_offset, |
| initial_die_offset); |
| |
| return false; |
| } |
| |
| /// Get the source of the DIE. |
| /// |
| /// The function returns an enumerator value saying if the DIE comes |
| /// from the .debug_info section of the primary debug info file, the |
| /// .debug_info section of the alternate debug info file, or the |
| /// .debug_types section. |
| /// |
| /// @param die the DIE to get the source of. |
| /// |
| /// @return the source of the DIE if it could be determined, |
| /// NO_DEBUG_INFO_DIE_SOURCE otherwise. |
| die_source |
| get_die_source(const Dwarf_Die *die) const |
| { |
| die_source source = NO_DEBUG_INFO_DIE_SOURCE; |
| ABG_ASSERT(die); |
| ABG_ASSERT(get_die_source(*die, source)); |
| return source; |
| } |
| |
| /// Get the source of the DIE. |
| /// |
| /// The function returns an enumerator value saying if the DIE comes |
| /// from the .debug_info section of the primary debug info file, the |
| /// .debug_info section of the alternate debug info file, or the |
| /// .debug_types section. |
| /// |
| /// @param die the DIE to get the source of. |
| /// |
| /// @param source out parameter. The function sets this parameter |
| /// to the source of the DIE @p iff it returns true. |
| /// |
| /// @return true iff the source of the DIE could be determined and |
| /// returned. |
| bool |
| get_die_source(const Dwarf_Die &die, die_source &source) const |
| { |
| Dwarf_Die cu_die; |
| Dwarf_Die cu_kind; |
| uint8_t address_size = 0, offset_size = 0; |
| if (!dwarf_diecu(const_cast<Dwarf_Die*>(&die), |
| &cu_die, &address_size, |
| &offset_size)) |
| return false; |
| |
| Dwarf_Half version = 0; |
| Dwarf_Off abbrev_offset = 0; |
| uint64_t type_signature = 0; |
| Dwarf_Off type_offset = 0; |
| if (!dwarf_cu_die(cu_die.cu, &cu_kind, |
| &version, &abbrev_offset, |
| &address_size, &offset_size, |
| &type_signature, &type_offset)) |
| return false; |
| |
| int tag = dwarf_tag(&cu_kind); |
| |
| if (tag == DW_TAG_compile_unit |
| || tag == DW_TAG_partial_unit) |
| { |
| const Dwarf *die_dwarf = dwarf_cu_getdwarf(cu_die.cu); |
| if (dwarf_debug_info() == die_dwarf) |
| source = PRIMARY_DEBUG_INFO_DIE_SOURCE; |
| else if (alternate_dwarf_debug_info() == die_dwarf) |
| source = ALT_DEBUG_INFO_DIE_SOURCE; |
| else |
| ABG_ASSERT_NOT_REACHED; |
| } |
| else if (tag == DW_TAG_type_unit) |
| source = TYPE_UNIT_DIE_SOURCE; |
| else |
| return false; |
| |
| return true; |
| } |
| |
| /// Getter for the DIE designated by an offset. |
| /// |
| /// @param source the source of the DIE to get. |
| /// |
| /// @param offset the offset of the DIE to get. |
| /// |
| /// @param die the resulting DIE. The pointer has to point to an |
| /// allocated memory region. |
| void |
| get_die_from_offset(die_source source, Dwarf_Off offset, Dwarf_Die *die) const |
| { |
| if (source == TYPE_UNIT_DIE_SOURCE) |
| ABG_ASSERT(dwarf_offdie_types(const_cast<Dwarf*>(dwarf_per_die_source(source)), |
| offset, die)); |
| else |
| ABG_ASSERT(dwarf_offdie(const_cast<Dwarf*>(dwarf_per_die_source(source)), |
| offset, die)); |
| } |
| |
| public: |
| |
| /// Add an entry to the relevant die->decl map. |
| /// |
| /// @param die the DIE to add the the map. |
| /// |
| /// @param decl the decl to consider. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| /// |
| /// @param do_associate_by_repr if true then this function |
| /// associates the representation string of @p die with the |
| /// declaration @p decl, in a corpus-wide manner. That is, in the |
| /// entire current corpus, there is going to be just one declaration |
| /// associated with a DIE of the string representation of @p die. |
| /// |
| /// @param do_associate_by_repr_per_tu if true, then this function |
| /// associates the representation string of @p die with the |
| /// declaration @p decl in a translation unit wide manner. That is, |
| /// in the entire current translation unit, there is going to be |
| /// just one declaration associated with a DIE of the string |
| /// representation of @p die. |
| void |
| associate_die_to_decl(Dwarf_Die* die, |
| decl_base_sptr decl, |
| size_t where_offset, |
| bool do_associate_by_repr = false) |
| { |
| const die_source source = get_die_source(die); |
| |
| die_artefact_map_type& m = |
| decl_die_artefact_maps().get_container(source); |
| |
| size_t die_offset; |
| if (do_associate_by_repr) |
| { |
| Dwarf_Die equiv_die; |
| if (!get_or_compute_canonical_die(die, equiv_die, where_offset, |
| /*die_as_type=*/false)) |
| return; |
| die_offset = dwarf_dieoffset(&equiv_die); |
| } |
| else |
| die_offset = dwarf_dieoffset(die); |
| |
| m[die_offset] = decl; |
| } |
| |
| /// Lookup the decl for a given DIE. |
| /// |
| /// The returned decl is either the decl of the DIE that as the |
| /// exact offset @p die_offset |
| /// die_offset, or |
| /// give |
| /// |
| /// @param die_offset the offset of the DIE to consider. |
| /// |
| /// @param source where the DIE represented by @p die_offset comes |
| /// from. |
| /// |
| /// Note that "alternate debug info sections" is a GNU extension as |
| /// of DWARF4 and is described at |
| /// http://www.dwarfstd.org/ShowIssue.php?issue=120604.1 |
| /// |
| /// @return the resulting decl, or null if no decl is associated to |
| /// the DIE represented by @p die_offset. |
| decl_base_sptr |
| lookup_decl_from_die_offset(Dwarf_Off die_offset, die_source source) |
| { |
| decl_base_sptr result = |
| is_decl(lookup_artifact_from_die_offset(die_offset, source, |
| /*die_as_type=*/false)); |
| |
| return result; |
| } |
| |
| /// Get the qualified name of a given DIE. |
| /// |
| /// If the name of the DIE was already computed before just return |
| /// that name from a cache. Otherwise, build the name, cache it and |
| /// return it. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| /// |
| /// @return the interned string representing the qualified name of |
| /// @p die. |
| interned_string |
| get_die_qualified_name(Dwarf_Die *die, size_t where_offset) |
| { |
| ABG_ASSERT(die); |
| die_istring_map_type& map = |
| die_qualified_name_maps_.get_container(*this, die); |
| |
| size_t die_offset = dwarf_dieoffset(die); |
| die_istring_map_type::const_iterator i = map.find(die_offset); |
| |
| if (i == map.end()) |
| { |
| reader& rdr = *const_cast<reader*>(this); |
| string qualified_name = die_qualified_name(rdr, die, where_offset); |
| interned_string istr = env().intern(qualified_name); |
| map[die_offset] = istr; |
| return istr; |
| } |
| |
| return i->second; |
| } |
| |
| /// Get the qualified name of a given DIE. |
| /// |
| /// If the name of the DIE was already computed before just return |
| /// that name from a cache. Otherwise, build the name, cache it and |
| /// return it. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| /// |
| /// @return the interned string representing the qualified name of |
| /// @p die. |
| interned_string |
| get_die_qualified_name(Dwarf_Die *die, size_t where_offset) const |
| { |
| return const_cast<reader*>(this)-> |
| get_die_qualified_name(die, where_offset); |
| } |
| |
| /// Get the qualified name of a given DIE which is considered to be |
| /// the DIE for a type. |
| /// |
| /// For instance, for a DW_TAG_subprogram DIE, this function |
| /// computes the name of the function *type* that corresponds to the |
| /// function. |
| /// |
| /// If the name of the DIE was already computed before just return |
| /// that name from a cache. Otherwise, build the name, cache it and |
| /// return it. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| /// |
| /// @return the interned string representing the qualified name of |
| /// @p die. |
| interned_string |
| get_die_qualified_type_name(const Dwarf_Die *die, size_t where_offset) const |
| { |
| ABG_ASSERT(die); |
| |
| // The name of the translation unit die is "". |
| if (die == cur_tu_die()) |
| return env().intern(""); |
| |
| die_istring_map_type& map = |
| die_qualified_name_maps_.get_container(*const_cast<reader*>(this), |
| die); |
| |
| size_t die_offset = dwarf_dieoffset(const_cast<Dwarf_Die*>(die)); |
| die_istring_map_type::const_iterator i = |
| map.find(die_offset); |
| |
| if (i == map.end()) |
| { |
| reader& rdr = *const_cast<reader*>(this); |
| string qualified_name; |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if ((tag == DW_TAG_structure_type |
| || tag == DW_TAG_class_type |
| || tag == DW_TAG_union_type) |
| && die_is_anonymous(die)) |
| { |
| location l = die_location(*this, die); |
| qualified_name = l ? l.expand() : "noloc"; |
| qualified_name = "unnamed-at-" + qualified_name; |
| } |
| else |
| qualified_name = |
| die_qualified_type_name(rdr, die, where_offset); |
| |
| interned_string istr = env().intern(qualified_name); |
| map[die_offset] = istr; |
| return istr; |
| } |
| |
| return i->second; |
| } |
| |
| /// Get the pretty representation of a DIE that represents a type. |
| /// |
| /// For instance, for the DW_TAG_subprogram, this function computes |
| /// the pretty representation of the type of the function, not the |
| /// pretty representation of the function declaration. |
| /// |
| /// Once the pretty representation is computed, it's stored in a |
| /// cache. Subsequent invocations of this function on the same DIE |
| /// will yield the cached name. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| /// |
| /// @return the interned_string that represents the pretty |
| /// representation. |
| interned_string |
| get_die_pretty_type_representation(const Dwarf_Die *die, |
| size_t where_offset) const |
| { |
| ABG_ASSERT(die); |
| die_istring_map_type& map = |
| die_pretty_type_repr_maps_.get_container(*const_cast<reader*>(this), |
| die); |
| |
| size_t die_offset = dwarf_dieoffset(const_cast<Dwarf_Die*>(die)); |
| die_istring_map_type::const_iterator i = map.find(die_offset); |
| |
| if (i == map.end()) |
| { |
| reader& rdr = *const_cast<reader*>(this); |
| string pretty_representation = |
| die_pretty_print_type(rdr, die, where_offset); |
| interned_string istr = env().intern(pretty_representation); |
| map[die_offset] = istr; |
| return istr; |
| } |
| |
| return i->second; |
| } |
| |
| /// Get the pretty representation of a DIE. |
| /// |
| /// Once the pretty representation is computed, it's stored in a |
| /// cache. Subsequent invocations of this function on the same DIE |
| /// will yield the cached name. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| /// |
| /// @return the interned_string that represents the pretty |
| /// representation. |
| interned_string |
| get_die_pretty_representation(const Dwarf_Die *die, size_t where_offset) const |
| { |
| ABG_ASSERT(die); |
| |
| die_istring_map_type& map = |
| die_pretty_repr_maps_.get_container(*const_cast<reader*>(this), |
| die); |
| |
| size_t die_offset = dwarf_dieoffset(const_cast<Dwarf_Die*>(die)); |
| die_istring_map_type::const_iterator i = map.find(die_offset); |
| |
| if (i == map.end()) |
| { |
| reader& rdr = *const_cast<reader*>(this); |
| string pretty_representation = |
| die_pretty_print(rdr, die, where_offset); |
| interned_string istr = env().intern(pretty_representation); |
| map[die_offset] = istr; |
| return istr; |
| } |
| |
| return i->second; |
| } |
| |
| /// Lookup the artifact that was built to represent a type that has |
| /// the same pretty representation as the type denoted by a given |
| /// DIE. |
| /// |
| /// Note that the DIE must have previously been associated with the |
| /// artifact using the functions associate_die_to_decl or |
| /// associate_die_to_type. |
| /// |
| /// Also, note that the scope of the lookup is the current ABI |
| /// corpus. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| /// |
| /// @return the type artifact found. |
| type_or_decl_base_sptr |
| lookup_type_artifact_from_die(Dwarf_Die *die) const |
| { |
| type_or_decl_base_sptr artifact = |
| lookup_artifact_from_die(die, /*type_as_die=*/true); |
| if (function_decl_sptr fn = is_function_decl(artifact)) |
| return fn->get_type(); |
| return artifact; |
| } |
| |
| /// Lookup the artifact that was built to represent a type or a |
| /// declaration that has the same pretty representation as the type |
| /// denoted by a given DIE. |
| /// |
| /// Note that the DIE must have previously been associated with the |
| /// artifact using the functions associate_die_to_decl or |
| /// associate_die_to_type. |
| /// |
| /// Also, note that the scope of the lookup is the current ABI |
| /// corpus. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| /// |
| /// @param die_as_type if true, it means the DIE is to be considered |
| /// as a type. |
| /// |
| /// @return the artifact found. |
| type_or_decl_base_sptr |
| lookup_artifact_from_die(const Dwarf_Die *die, bool die_as_type = false) const |
| { |
| Dwarf_Die equiv_die; |
| if (!get_or_compute_canonical_die(die, equiv_die, /*where=*/0, die_as_type)) |
| return type_or_decl_base_sptr(); |
| |
| const die_artefact_map_type& m = |
| die_as_type |
| ? type_die_artefact_maps().get_container(*this, &equiv_die) |
| : decl_die_artefact_maps().get_container(*this, &equiv_die); |
| |
| size_t die_offset = dwarf_dieoffset(&equiv_die); |
| die_artefact_map_type::const_iterator i = m.find(die_offset); |
| |
| if (i == m.end()) |
| return type_or_decl_base_sptr(); |
| return i->second; |
| } |
| |
| /// Lookup the artifact that was built to represent a type or a |
| /// declaration that has the same pretty representation as the type |
| /// denoted by the offset of a given DIE. |
| /// |
| /// Note that the DIE must have previously been associated with the |
| /// artifact using either associate_die_to_decl or |
| /// associate_die_to_type. |
| /// |
| /// Also, note that the scope of the lookup is the current ABI |
| /// corpus. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| /// |
| /// @param die_as_type if true, it means the DIE is to be considered |
| /// as a type. |
| /// |
| /// @return the artifact found. |
| type_or_decl_base_sptr |
| lookup_artifact_from_die_offset(Dwarf_Off die_offset, |
| die_source source, |
| bool die_as_type = false) const |
| { |
| const die_artefact_map_type& m = |
| die_as_type |
| ? type_die_artefact_maps().get_container(source) |
| : decl_die_artefact_maps().get_container(source); |
| |
| die_artefact_map_type::const_iterator i = m.find(die_offset); |
| if (i == m.end()) |
| return type_or_decl_base_sptr(); |
| return i->second; |
| } |
| |
| /// Check if we can assume the One Definition Rule[1] to be relevant |
| /// for the current translation unit. |
| /// |
| /// [1]: https://en.wikipedia.org/wiki/One_Definition_Rule |
| /// |
| /// At the moment this returns true if the current translation unit |
| /// is in C++ language. In that case, it's relevant to assume that |
| /// we use optimizations based on the ODR. |
| bool |
| odr_is_relevant() const |
| {return odr_is_relevant(cur_transl_unit()->get_language());} |
| |
| /// Check if we can assume the One Definition Rule[1] to be relevant |
| /// for a given language. |
| /// |
| /// [1]: https://en.wikipedia.org/wiki/One_Definition_Rule |
| /// |
| /// At the moment this returns true if the language considered |
| /// is C++, Java or Ada. |
| bool |
| odr_is_relevant(translation_unit::language l) const |
| { |
| return (is_cplus_plus_language(l) |
| || is_java_language(l) |
| || is_ada_language(l)); |
| } |
| |
| /// Check if we can assume the One Definition Rule to be relevant |
| /// for a given DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true if the ODR is relevant for @p die. |
| bool |
| odr_is_relevant(Dwarf_Off die_offset, die_source source) const |
| { |
| Dwarf_Die die; |
| ABG_ASSERT(dwarf_offdie(const_cast<Dwarf*>(dwarf_per_die_source(source)), |
| die_offset, &die)); |
| return odr_is_relevant(&die); |
| } |
| |
| /// Check if we can assume the One Definition Rule to be relevant |
| /// for a given DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true if the ODR is relevant for @p die. |
| bool |
| odr_is_relevant(const Dwarf_Die *die) const |
| { |
| translation_unit::language lang; |
| if (!get_die_language(die, lang)) |
| return odr_is_relevant(); |
| |
| return odr_is_relevant(lang); |
| } |
| |
| /// Getter for the maps set that associates a decl DIE offset to an |
| /// artifact. |
| /// |
| /// @return the maps set that associates a decl DIE offset to an |
| /// artifact. |
| die_source_dependant_container_set<die_artefact_map_type>& |
| decl_die_artefact_maps() |
| {return decl_die_artefact_maps_;} |
| |
| /// Getter for the maps set that associates a decl DIE offset to an |
| /// artifact. |
| /// |
| /// @return the maps set that associates a decl DIE offset to an |
| /// artifact. |
| const die_source_dependant_container_set<die_artefact_map_type>& |
| decl_die_artefact_maps() const |
| {return decl_die_artefact_maps_;} |
| |
| /// Getter for the maps set that associates a type DIE offset to an |
| /// artifact. |
| /// |
| /// @return the maps set that associates a type DIE offset to an |
| /// artifact. |
| die_source_dependant_container_set<die_artefact_map_type>& |
| type_die_artefact_maps() |
| {return type_die_artefact_maps_;} |
| |
| /// Getter for the maps set that associates a type DIE offset to an |
| /// artifact. |
| /// |
| /// @return the maps set that associates a type DIE offset to an |
| /// artifact. |
| const die_source_dependant_container_set<die_artefact_map_type>& |
| type_die_artefact_maps() const |
| {return type_die_artefact_maps_;} |
| |
| /// Getter of the maps that associates function type representations |
| /// to function types, inside a translation unit. |
| /// |
| /// @return the maps that associates function type representations |
| /// to function types, inside a translation unit. |
| istring_fn_type_map_type& |
| per_tu_repr_to_fn_type_maps() |
| {return per_tu_repr_to_fn_type_maps_;} |
| |
| /// Getter of the maps that associates function type representations |
| /// to function types, inside a translation unit. |
| /// |
| /// @return the maps that associates function type representations |
| /// to function types, inside a translation unit. |
| const istring_fn_type_map_type& |
| per_tu_repr_to_fn_type_maps() const |
| {return per_tu_repr_to_fn_type_maps_;} |
| |
| /// Associate the representation of a function type DIE to a given |
| /// function type, inside the current translation unit. |
| /// |
| /// @param die the DIE to associate to the function type, using its |
| /// representation. |
| /// |
| /// @param fn_type the function type to associate to @p die. |
| void |
| associate_die_repr_to_fn_type_per_tu(const Dwarf_Die *die, |
| const function_type_sptr &fn_type) |
| { |
| if (!die_is_function_type(die)) |
| return; |
| |
| interned_string repr = |
| get_die_pretty_type_representation(die, /*where=*/0); |
| ABG_ASSERT(!repr.empty()); |
| |
| per_tu_repr_to_fn_type_maps()[repr]= fn_type; |
| } |
| |
| /// Lookup the function type associated to a given function type |
| /// DIE, in the current translation unit. |
| /// |
| /// @param die the DIE of function type to consider. |
| /// |
| /// @return the @ref function_type_sptr associated to @p die, or nil |
| /// of no function_type is associated to @p die. |
| function_type_sptr |
| lookup_fn_type_from_die_repr_per_tu(const Dwarf_Die *die) |
| { |
| if (!die_is_function_type(die)) |
| return function_type_sptr(); |
| |
| interned_string repr = die_name(die).empty() ? |
| get_die_pretty_type_representation(die, /*where=*/0) |
| : get_die_pretty_representation(die, /*where=*/0); |
| ABG_ASSERT(!repr.empty()); |
| |
| istring_fn_type_map_type::const_iterator i = |
| per_tu_repr_to_fn_type_maps().find(repr); |
| |
| if (i == per_tu_repr_to_fn_type_maps().end()) |
| return function_type_sptr(); |
| |
| return i->second; |
| } |
| |
| /// Set the canonical DIE offset of a given DIE. |
| /// |
| /// @param canonical_dies the vector that holds canonical DIEs. |
| /// |
| /// @param die_offset the offset of the DIE to set the canonical DIE |
| /// for. |
| /// |
| /// @param canonical_die_offset the canonical DIE offset to |
| /// associate to @p die_offset. |
| void |
| set_canonical_die_offset(offset_offset_map_type &canonical_dies, |
| Dwarf_Off die_offset, |
| Dwarf_Off canonical_die_offset) const |
| { |
| canonical_dies[die_offset] = canonical_die_offset;} |
| |
| /// Set the canonical DIE offset of a given DIE. |
| /// |
| /// |
| /// @param die_offset the offset of the DIE to set the canonical DIE |
| /// for. |
| /// |
| /// @param source the source of the DIE denoted by @p die_offset. |
| /// |
| /// @param canonical_die_offset the canonical DIE offset to |
| /// associate to @p die_offset. |
| /// |
| /// @param die_as_type if true, it means that @p die_offset has to |
| /// be considered as a type. |
| void |
| set_canonical_die_offset(Dwarf_Off die_offset, |
| die_source source, |
| Dwarf_Off canonical_die_offset, |
| bool die_as_type) const |
| { |
| offset_offset_map_type &canonical_dies = |
| die_as_type |
| ? const_cast<reader*>(this)->canonical_type_die_offsets_. |
| get_container(source) |
| : const_cast<reader*>(this)->canonical_decl_die_offsets_. |
| get_container(source); |
| |
| set_canonical_die_offset(canonical_dies, |
| die_offset, |
| canonical_die_offset); |
| } |
| |
| /// Set the canonical DIE offset of a given DIE. |
| /// |
| /// |
| /// @param die the DIE to set the canonical DIE for. |
| /// |
| /// @param canonical_die_offset the canonical DIE offset to |
| /// associate to @p die_offset. |
| /// |
| /// @param die_as_type if true, it means that @p die has to be |
| /// considered as a type. |
| void |
| set_canonical_die_offset(const Dwarf_Die *die, |
| Dwarf_Off canonical_die_offset, |
| bool die_as_type) const |
| { |
| const die_source source = get_die_source(die); |
| |
| Dwarf_Off die_offset = dwarf_dieoffset(const_cast<Dwarf_Die*>(die)); |
| |
| set_canonical_die_offset(die_offset, source, |
| canonical_die_offset, |
| die_as_type); |
| } |
| |
| /// Get the canonical DIE offset of a given DIE. |
| /// |
| /// @param canonical_dies the vector that contains canonical DIES. |
| /// |
| /// @param die_offset the offset of the DIE to consider. |
| /// |
| /// @return the canonical of the DIE denoted by @p die_offset, or |
| /// zero if no canonical DIE was found. |
| Dwarf_Off |
| get_canonical_die_offset(offset_offset_map_type &canonical_dies, |
| Dwarf_Off die_offset) const |
| { |
| offset_offset_map_type::const_iterator it = canonical_dies.find(die_offset); |
| if (it == canonical_dies.end()) |
| return 0; |
| return it->second; |
| } |
| |
| /// Get the canonical DIE offset of a given DIE. |
| /// |
| /// @param die_offset the offset of the DIE to consider. |
| /// |
| /// @param source the source of the DIE denoted by @p die_offset. |
| /// |
| /// @param die_as_type if true, it means that @p is to be considered |
| /// as a type DIE. |
| /// |
| /// @return the canonical of the DIE denoted by @p die_offset, or |
| /// zero if no canonical DIE was found. |
| Dwarf_Off |
| get_canonical_die_offset(Dwarf_Off die_offset, |
| die_source source, |
| bool die_as_type) const |
| { |
| offset_offset_map_type &canonical_dies = |
| die_as_type |
| ? const_cast<reader*>(this)->canonical_type_die_offsets_. |
| get_container(source) |
| : const_cast<reader*>(this)->canonical_decl_die_offsets_. |
| get_container(source); |
| |
| return get_canonical_die_offset(canonical_dies, die_offset); |
| } |
| |
| /// Erase the canonical type of a given DIE. |
| /// |
| /// @param die_offset the offset of the DIE to consider. |
| /// |
| /// @param source the source of the canonical type. |
| /// |
| /// @param die_as_type if true, it means that @p is to be considered |
| /// as a type DIE. |
| /// |
| /// @return the canonical of the DIE denoted by @p die_offset, or |
| /// zero if no canonical DIE was found and erased.. |
| bool |
| erase_canonical_die_offset(Dwarf_Off die_offset, |
| die_source source, |
| bool die_as_type) const |
| { |
| offset_offset_map_type &canonical_dies = |
| die_as_type |
| ? const_cast<reader*>(this)->canonical_type_die_offsets_. |
| get_container(source) |
| : const_cast<reader*>(this)->canonical_decl_die_offsets_. |
| get_container(source); |
| |
| return canonical_dies.erase(die_offset); |
| } |
| |
| |
| /// Associate a DIE (representing a type) to the type that it |
| /// represents. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param type the type to associate the DIE to. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| void |
| associate_die_to_type(const Dwarf_Die *die, |
| type_base_sptr type, |
| size_t where) |
| { |
| if (!type) |
| return; |
| |
| Dwarf_Die equiv_die; |
| if (!get_or_compute_canonical_die(die, equiv_die, where, |
| /*die_as_type=*/true)) |
| return; |
| |
| die_artefact_map_type& m = |
| type_die_artefact_maps().get_container(*this, &equiv_die); |
| |
| size_t die_offset = dwarf_dieoffset(&equiv_die); |
| m[die_offset] = type; |
| } |
| |
| /// Lookup the type associated to a given DIE. |
| /// |
| /// Note that the DIE must have been associated to type by a |
| /// previous invocation of the function |
| /// reader::associate_die_to_type(). |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return the type associated to the DIE or NULL if no type is |
| /// associated to the DIE. |
| type_base_sptr |
| lookup_type_from_die(const Dwarf_Die* die) const |
| { |
| type_or_decl_base_sptr artifact = |
| lookup_artifact_from_die(die, /*die_as_type=*/true); |
| if (function_decl_sptr fn = is_function_decl(artifact)) |
| return fn->get_type(); |
| return is_type(artifact); |
| } |
| |
| /// Lookup the type associated to a DIE at a given offset, from a |
| /// given source. |
| /// |
| /// Note that the DIE must have been associated to type by a |
| /// previous invocation of the function |
| /// reader::associate_die_to_type(). |
| /// |
| /// @param die_offset the offset of the DIE to consider. |
| /// |
| /// @param source the source of the DIE to consider. |
| /// |
| /// @return the type associated to the DIE or NULL if no type is |
| /// associated to the DIE. |
| type_base_sptr |
| lookup_type_from_die_offset(size_t die_offset, die_source source) const |
| { |
| type_base_sptr result; |
| const die_artefact_map_type& m = |
| type_die_artefact_maps().get_container(source); |
| die_artefact_map_type::const_iterator i = m.find(die_offset); |
| if (i != m.end()) |
| { |
| if (function_decl_sptr fn = is_function_decl(i->second)) |
| return fn->get_type(); |
| result = is_type(i->second); |
| } |
| |
| if (!result) |
| { |
| // Maybe we are looking for a class type being constructed? |
| const die_class_or_union_map_type& m = die_wip_classes_map(source); |
| die_class_or_union_map_type::const_iterator i = m.find(die_offset); |
| |
| if (i != m.end()) |
| result = i->second; |
| } |
| |
| if (!result) |
| { |
| // Maybe we are looking for a function type being constructed? |
| const die_function_type_map_type& m = |
| die_wip_function_types_map(source); |
| die_function_type_map_type::const_iterator i = m.find(die_offset); |
| |
| if (i != m.end()) |
| result = i->second; |
| } |
| |
| return result; |
| } |
| |
| /// Getter of a map that associates a die that represents a |
| /// class/struct with the declaration of the class, while the class |
| /// is being constructed. |
| /// |
| /// @param source where the DIE is from. |
| /// |
| /// @return the map that associates a DIE to the class that is being |
| /// built. |
| const die_class_or_union_map_type& |
| die_wip_classes_map(die_source source) const |
| {return const_cast<reader*>(this)->die_wip_classes_map(source);} |
| |
| /// Getter of a map that associates a die that represents a |
| /// class/struct with the declaration of the class, while the class |
| /// is being constructed. |
| /// |
| /// @param source where the DIE comes from. |
| /// |
| /// @return the map that associates a DIE to the class that is being |
| /// built. |
| die_class_or_union_map_type& |
| die_wip_classes_map(die_source source) |
| { |
| switch (source) |
| { |
| case PRIMARY_DEBUG_INFO_DIE_SOURCE: |
| break; |
| case ALT_DEBUG_INFO_DIE_SOURCE: |
| return alternate_die_wip_classes_map_; |
| case TYPE_UNIT_DIE_SOURCE: |
| return type_unit_die_wip_classes_map_; |
| case NO_DEBUG_INFO_DIE_SOURCE: |
| case NUMBER_OF_DIE_SOURCES: |
| ABG_ASSERT_NOT_REACHED; |
| } |
| return die_wip_classes_map_; |
| } |
| |
| /// Getter for a map that associates a die (that represents a |
| /// function type) whith a function type, while the function type is |
| /// being constructed (WIP == work in progress). |
| /// |
| /// @param source where the DIE comes from.n |
| /// |
| /// @return the map of wip function types. |
| const die_function_type_map_type& |
| die_wip_function_types_map(die_source source) const |
| {return const_cast<reader*>(this)->die_wip_function_types_map(source);} |
| |
| /// Getter for a map that associates a die (that represents a |
| /// function type) whith a function type, while the function type is |
| /// being constructed (WIP == work in progress). |
| /// |
| /// @param source where DIEs of the map come from. |
| /// |
| /// @return the map of wip function types. |
| die_function_type_map_type& |
| die_wip_function_types_map(die_source source) |
| { |
| switch (source) |
| { |
| case PRIMARY_DEBUG_INFO_DIE_SOURCE: |
| break; |
| case ALT_DEBUG_INFO_DIE_SOURCE: |
| return alternate_die_wip_function_types_map_; |
| case TYPE_UNIT_DIE_SOURCE: |
| return type_unit_die_wip_function_types_map_; |
| case NO_DEBUG_INFO_DIE_SOURCE: |
| case NUMBER_OF_DIE_SOURCES: |
| ABG_ASSERT_NOT_REACHED; |
| } |
| return die_wip_function_types_map_; |
| } |
| |
| /// Getter for a map that associates a die with a function decl |
| /// which has a linkage name but no elf symbol yet. |
| /// |
| /// This is to fixup function decls with linkage names, but with no |
| /// link to their underlying elf symbol. There are some DIEs like |
| /// that in DWARF sometimes, especially when the compiler optimizes |
| /// stuff aggressively. |
| die_function_decl_map_type& |
| die_function_decl_with_no_symbol_map() |
| {return die_function_with_no_symbol_map_;} |
| |
| /// Return true iff a given offset is for the DIE of a class that is |
| /// being built, but that is not fully built yet. WIP == "work in |
| /// progress". |
| /// |
| /// @param offset the DIE offset to consider. |
| /// |
| /// @param source where the DIE of the map come from. |
| /// |
| /// @return true iff @p offset is the offset of the DIE of a class |
| /// that is being currently built. |
| bool |
| is_wip_class_die_offset(Dwarf_Off offset, die_source source) const |
| { |
| die_class_or_union_map_type::const_iterator i = |
| die_wip_classes_map(source).find(offset); |
| return (i != die_wip_classes_map(source).end()); |
| } |
| |
| /// Return true iff a given offset is for the DIE of a function type |
| /// that is being built at the moment, but is not fully built yet. |
| /// WIP == work in progress. |
| /// |
| /// @param offset DIE offset to consider. |
| /// |
| /// @param source where the DIE comes from. |
| /// |
| /// @return true iff @p offset is the offset of the DIE of a |
| /// function type that is being currently built. |
| bool |
| is_wip_function_type_die_offset(Dwarf_Off offset, die_source source) const |
| { |
| die_function_type_map_type::const_iterator i = |
| die_wip_function_types_map(source).find(offset); |
| return (i != die_wip_function_types_map(source).end()); |
| } |
| |
| /// Sometimes, a data member die can erroneously have an empty name as |
| /// a result of a bug of the DWARF emitter. |
| /// |
| /// This is what happens in |
| /// https://sourceware.org/bugzilla/show_bug.cgi?id=29934. |
| /// |
| /// In that case, this function constructs an artificial name for that |
| /// data member. The pattern of the name is as follows: |
| /// |
| /// "unnamed-@-<location>". |
| /// |
| ///location is either the value of the data member location of the |
| ///data member if it has one or concatenation of its source location |
| ///if it has none. If no location can be calculated then the function |
| ///returns the empty string. |
| string |
| build_name_for_buggy_anonymous_data_member(Dwarf_Die *die) |
| { |
| string result; |
| // Let's make sure we are looking at a data member with an empty |
| // name ... |
| if (!die |
| || dwarf_tag(die) != DW_TAG_member |
| || !die_name(die).empty()) |
| return result; |
| |
| // ... and yet, it's not an anonymous data member (aka unnamed |
| // field) as described in |
| // https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html. |
| if (die_is_anonymous_data_member(die)) |
| return result; |
| |
| // If we come this far, it means we are looking at a buggy data |
| // member with no name. Let's build a name for it so that it can be |
| // addressed. |
| int64_t offset_in_bits = 0; |
| bool has_offset = die_member_offset(*this, die, offset_in_bits); |
| location loc; |
| if (!has_offset) |
| { |
| loc = die_location(*this, die); |
| if (!loc) |
| return result; |
| } |
| |
| std::ostringstream o; |
| o << "unnamed-dm-@-"; |
| if (has_offset) |
| o << "offset-" << offset_in_bits << "bits"; |
| else |
| o << "loc-" << loc.expand(); |
| |
| return o.str(); |
| } |
| |
| /// Getter for the map of declaration-only classes that are to be |
| /// resolved to their definition classes by the end of the corpus |
| /// loading. |
| /// |
| /// @return a map of string -> vector of classes where the key is |
| /// the fully qualified name of the class and the value is the |
| /// vector of declaration-only class. |
| const string_classes_or_unions_map& |
| declaration_only_classes() const |
| {return decl_only_classes_map_;} |
| |
| /// Getter for the map of declaration-only classes that are to be |
| /// resolved to their definition classes by the end of the corpus |
| /// loading. |
| /// |
| /// @return a map of string -> vector of classes where the key is |
| /// the fully qualified name of the class and the value is the |
| /// vector of declaration-only class. |
| string_classes_or_unions_map& |
| declaration_only_classes() |
| {return decl_only_classes_map_;} |
| |
| /// If a given class is a declaration-only class then stash it on |
| /// the side so that at the end of the corpus reading we can resolve |
| /// it to its definition. |
| /// |
| /// @param klass the class to consider. |
| void |
| maybe_schedule_declaration_only_class_for_resolution(const class_or_union_sptr& cou) |
| { |
| if (cou->get_is_declaration_only() |
| && cou->get_definition_of_declaration() == 0 |
| // Make sure the class is not anonymous. Anonymous classes |
| // are usually later named by a typedef. At that time, after |
| // being named by a typedef, this method is going to be called |
| // with the class being named by the typedef. |
| && !cou->get_qualified_name().empty()) |
| { |
| string qn = cou->get_qualified_name(); |
| string_classes_or_unions_map::iterator record = |
| declaration_only_classes().find(qn); |
| if (record == declaration_only_classes().end()) |
| declaration_only_classes()[qn].push_back(cou); |
| else |
| record->second.push_back(cou); |
| } |
| } |
| |
| /// Test if a given declaration-only class has been scheduled for |
| /// resolution to a defined class. |
| /// |
| /// @param klass the class to consider for the test. |
| /// |
| /// @return true iff @p klass is a declaration-only class and if |
| /// it's been scheduled for resolution to a defined class. |
| bool |
| is_decl_only_class_scheduled_for_resolution(const class_or_union_sptr& cou) |
| { |
| if (cou->get_is_declaration_only()) |
| return ((declaration_only_classes().find(cou->get_qualified_name()) |
| != declaration_only_classes().end()) |
| || (declaration_only_classes().find(cou->get_name()) |
| != declaration_only_classes().end())); |
| |
| return false; |
| } |
| |
| /// Compare two ABI artifacts in a context which canonicalization |
| /// has not be done yet. |
| /// |
| /// @param l the left-hand-side operand of the comparison |
| /// |
| /// @param r the right-hand-side operand of the comparison. |
| /// |
| /// @return true if @p l equals @p r. |
| bool |
| compare_before_canonicalisation(const type_or_decl_base_sptr &l, |
| const type_or_decl_base_sptr &r) |
| { |
| if (!l || !r) |
| return !!l == !!r; |
| |
| const environment& e = l->get_environment(); |
| ABG_ASSERT(!e.canonicalization_is_done()); |
| |
| e.priv_->allow_type_comparison_results_caching(true); |
| bool s0 = e.decl_only_class_equals_definition(); |
| e.decl_only_class_equals_definition(true); |
| bool equal = l == r; |
| e.decl_only_class_equals_definition(s0); |
| e.priv_->clear_type_comparison_results_cache(); |
| e.priv_->allow_type_comparison_results_caching(false); |
| return equal; |
| } |
| |
| /// Walk the declaration-only classes that have been found during |
| /// the building of the corpus and resolve them to their definitions. |
| void |
| resolve_declaration_only_classes() |
| { |
| vector<string> resolved_classes; |
| |
| for (string_classes_or_unions_map::iterator i = |
| declaration_only_classes().begin(); |
| i != declaration_only_classes().end(); |
| ++i) |
| { |
| bool to_resolve = false; |
| for (classes_or_unions_type::iterator j = i->second.begin(); |
| j != i->second.end(); |
| ++j) |
| if ((*j)->get_is_declaration_only() |
| && ((*j)->get_definition_of_declaration() == 0)) |
| to_resolve = true; |
| |
| if (!to_resolve) |
| { |
| resolved_classes.push_back(i->first); |
| continue; |
| } |
| |
| // Now, for each decl-only class that have the current name |
| // 'i->first', let's try to poke at the fully defined class |
| // that is defined in the same translation unit as the |
| // declaration. |
| // |
| // If we find one class (defined in the TU of the declaration) |
| // that defines the declaration, then the declaration can be |
| // resolved to that class. |
| // |
| // If no defining class is found in the TU of the declaration, |
| // then there are possibly three cases to consider: |
| // |
| // 1/ There is exactly one class that defines the |
| // declaration and that class is defined in another TU. In |
| // this case, the declaration is resolved to that |
| // definition. |
| // |
| // 2/ There are more than one class that define that |
| // declaration and none of them is defined in the TU of the |
| // declaration. If those classes are all different, then |
| // the declaration is left unresolved. |
| // |
| // 3/ No class defines the declaration. In this case, the |
| // declaration is left unresoved. |
| |
| // So get the classes that might define the current |
| // declarations which name is i->first. |
| const type_base_wptrs_type *classes = |
| lookup_class_types(i->first, *corpus()); |
| if (!classes) |
| classes = lookup_union_types(i->first, *corpus()); |
| |
| if (!classes) |
| continue; |
| |
| // This is a map that associates the translation unit path to |
| // the class (that potentially defines the declarations that |
| // we consider) that are defined in that translation unit. It |
| // should stay ordered by using the TU path as key to ensure |
| // stability of the order of classe definitions in ABIXML |
| // output. |
| map<string, class_or_union_sptr> per_tu_class_map; |
| for (type_base_wptrs_type::const_iterator c = classes->begin(); |
| c != classes->end(); |
| ++c) |
| { |
| class_or_union_sptr klass = is_class_or_union_type(type_base_sptr(*c)); |
| ABG_ASSERT(klass); |
| |
| klass = is_class_or_union_type(look_through_decl_only_class(klass)); |
| if (klass->get_is_declaration_only()) |
| continue; |
| |
| string tu_path = klass->get_translation_unit()->get_absolute_path(); |
| if (tu_path.empty()) |
| continue; |
| |
| // Build a map that associates the translation unit path |
| // to the class (that potentially defines the declarations |
| // that we consider) that are defined in that translation unit. |
| per_tu_class_map[tu_path] = klass; |
| } |
| |
| if (!per_tu_class_map.empty()) |
| { |
| // Walk the declarations to resolve and resolve them |
| // either to the definitions that are in the same TU as |
| // the declaration, or to the definition found elsewhere, |
| // if there is only one such definition. |
| for (classes_or_unions_type::iterator j = i->second.begin(); |
| j != i->second.end(); |
| ++j) |
| { |
| if ((*j)->get_is_declaration_only() |
| && ((*j)->get_definition_of_declaration() == 0)) |
| { |
| string tu_path = |
| (*j)->get_translation_unit()->get_absolute_path(); |
| map<string, class_or_union_sptr>::const_iterator e = |
| per_tu_class_map.find(tu_path); |
| if (e != per_tu_class_map.end()) |
| (*j)->set_definition_of_declaration(e->second); |
| else if (per_tu_class_map.size() == 1) |
| (*j)->set_definition_of_declaration |
| (per_tu_class_map.begin()->second); |
| else |
| { |
| // We are in case where there are more than |
| // one definition for the declaration. Let's |
| // see if they are all equal. If they are, |
| // then the declaration resolves to the |
| // definition. Otherwise, we are in the case |
| // 3/ described above. |
| map<string, |
| class_or_union_sptr>::const_iterator it; |
| class_or_union_sptr first_class = |
| per_tu_class_map.begin()->second; |
| bool all_class_definitions_are_equal = true; |
| for (it = per_tu_class_map.begin(); |
| it != per_tu_class_map.end(); |
| ++it) |
| { |
| if (it == per_tu_class_map.begin()) |
| continue; |
| else |
| { |
| if (!compare_before_canonicalisation(it->second, |
| first_class)) |
| { |
| all_class_definitions_are_equal = false; |
| break; |
| } |
| } |
| } |
| if (all_class_definitions_are_equal) |
| (*j)->set_definition_of_declaration(first_class); |
| } |
| } |
| } |
| resolved_classes.push_back(i->first); |
| } |
| } |
| |
| size_t num_decl_only_classes = declaration_only_classes().size(), |
| num_resolved = resolved_classes.size(); |
| if (show_stats()) |
| cerr << "resolved " << num_resolved |
| << " class declarations out of " |
| << num_decl_only_classes |
| << "\n"; |
| |
| for (vector<string>::const_iterator i = resolved_classes.begin(); |
| i != resolved_classes.end(); |
| ++i) |
| declaration_only_classes().erase(*i); |
| |
| if (show_stats() && !declaration_only_classes().empty()) |
| { |
| cerr << "Here are the " |
| << num_decl_only_classes - num_resolved |
| << " unresolved class declarations:\n"; |
| for (string_classes_or_unions_map::iterator i = |
| declaration_only_classes().begin(); |
| i != declaration_only_classes().end(); |
| ++i) |
| cerr << " " << i->first << "\n"; |
| } |
| } |
| |
| /// Getter for the map of declaration-only enums that are to be |
| /// resolved to their definition enums by the end of the corpus |
| /// loading. |
| /// |
| /// @return a map of string -> vector of enums where the key is |
| /// the fully qualified name of the enum and the value is the |
| /// vector of declaration-only enum. |
| const string_enums_map& |
| declaration_only_enums() const |
| {return decl_only_enums_map_;} |
| |
| /// Getter for the map of declaration-only enums that are to be |
| /// resolved to their definition enums by the end of the corpus |
| /// loading. |
| /// |
| /// @return a map of string -> vector of enums where the key is |
| /// the fully qualified name of the enum and the value is the |
| /// vector of declaration-only enum. |
| string_enums_map& |
| declaration_only_enums() |
| {return decl_only_enums_map_;} |
| |
| /// If a given enum is a declaration-only enum then stash it on |
| /// the side so that at the end of the corpus reading we can resolve |
| /// it to its definition. |
| /// |
| /// @param enom the enum to consider. |
| void |
| maybe_schedule_declaration_only_enum_for_resolution(const enum_type_decl_sptr& enom) |
| { |
| if (enom->get_is_declaration_only() |
| && enom->get_definition_of_declaration() == 0 |
| // Make sure the enum is not anonymous. Anonymous enums are |
| // usually later named by a typedef. At that time, after |
| // being named by a typedef, this method is going to be called |
| // with the enum being named by the typedef. |
| && !enom->get_qualified_name().empty()) |
| { |
| string qn = enom->get_qualified_name(); |
| string_enums_map::iterator record = |
| declaration_only_enums().find(qn); |
| if (record == declaration_only_enums().end()) |
| declaration_only_enums()[qn].push_back(enom); |
| else |
| record->second.push_back(enom); |
| } |
| } |
| |
| /// Test if a given declaration-only enum has been scheduled for |
| /// resolution to a defined enum. |
| /// |
| /// @param enom the enum to consider for the test. |
| /// |
| /// @return true iff @p enom is a declaration-only enum and if |
| /// it's been scheduled for resolution to a defined enum. |
| bool |
| is_decl_only_enum_scheduled_for_resolution(enum_type_decl_sptr& enom) |
| { |
| if (enom->get_is_declaration_only()) |
| return (declaration_only_enums().find(enom->get_qualified_name()) |
| != declaration_only_enums().end()); |
| |
| return false; |
| } |
| |
| /// Walk the declaration-only enums that have been found during |
| /// the building of the corpus and resolve them to their definitions. |
| /// |
| /// TODO: Do away with this function by factorizing it with |
| /// resolve_declaration_only_classes. All declaration-only decls |
| /// could be handled the same way as declaration-only-ness is a |
| /// property of abigail::ir::decl_base now. |
| void |
| resolve_declaration_only_enums() |
| { |
| vector<string> resolved_enums; |
| |
| for (string_enums_map::iterator i = |
| declaration_only_enums().begin(); |
| i != declaration_only_enums().end(); |
| ++i) |
| { |
| bool to_resolve = false; |
| for (enums_type::iterator j = i->second.begin(); |
| j != i->second.end(); |
| ++j) |
| if ((*j)->get_is_declaration_only() |
| && ((*j)->get_definition_of_declaration() == 0)) |
| to_resolve = true; |
| |
| if (!to_resolve) |
| { |
| resolved_enums.push_back(i->first); |
| continue; |
| } |
| |
| // Now, for each decl-only enum that have the current name |
| // 'i->first', let's try to poke at the fully defined enum |
| // that is defined in the same translation unit as the |
| // declaration. |
| // |
| // If we find one enum (defined in the TU of the declaration) |
| // that defines the declaration, then the declaration can be |
| // resolved to that enum. |
| // |
| // If no defining enum is found in the TU of the declaration, |
| // then there are possibly three cases to consider: |
| // |
| // 1/ There is exactly one enum that defines the |
| // declaration and that enum is defined in another TU. In |
| // this case, the declaration is resolved to that |
| // definition. |
| // |
| // 2/ There are more than one enum that define that |
| // declaration and none of them is defined in the TU of the |
| // declaration. In this case, the declaration is left |
| // unresolved. |
| // |
| // 3/ No enum defines the declaration. In this case, the |
| // declaration is left unresoved. |
| |
| // So get the enums that might define the current |
| // declarations which name is i->first. |
| const type_base_wptrs_type *enums = |
| lookup_enum_types(i->first, *corpus()); |
| if (!enums) |
| continue; |
| |
| // This is a map that associates the translation unit path to |
| // the enum (that potentially defines the declarations that |
| // we consider) that are defined in that translation unit. It |
| // should stay ordered by using the TU path as key to ensure |
| // stability of the order of enum definitions in ABIXML |
| // output. |
| map<string, enum_type_decl_sptr> per_tu_enum_map; |
| for (type_base_wptrs_type::const_iterator c = enums->begin(); |
| c != enums->end(); |
| ++c) |
| { |
| enum_type_decl_sptr enom = is_enum_type(type_base_sptr(*c)); |
| ABG_ASSERT(enom); |
| |
| enom = is_enum_type(look_through_decl_only_enum(enom)); |
| if (enom->get_is_declaration_only()) |
| continue; |
| |
| string tu_path = enom->get_translation_unit()->get_absolute_path(); |
| if (tu_path.empty()) |
| continue; |
| |
| // Build a map that associates the translation unit path |
| // to the enum (that potentially defines the declarations |
| // that we consider) that are defined in that translation unit. |
| per_tu_enum_map[tu_path] = enom; |
| } |
| |
| if (!per_tu_enum_map.empty()) |
| { |
| // Walk the declarations to resolve and resolve them |
| // either to the definitions that are in the same TU as |
| // the declaration, or to the definition found elsewhere, |
| // if there is only one such definition. |
| for (enums_type::iterator j = i->second.begin(); |
| j != i->second.end(); |
| ++j) |
| { |
| if ((*j)->get_is_declaration_only() |
| && ((*j)->get_definition_of_declaration() == 0)) |
| { |
| string tu_path = |
| (*j)->get_translation_unit()->get_absolute_path(); |
| map<string, enum_type_decl_sptr>::const_iterator e = |
| per_tu_enum_map.find(tu_path); |
| if (e != per_tu_enum_map.end()) |
| (*j)->set_definition_of_declaration(e->second); |
| else if (per_tu_enum_map.size() == 1) |
| (*j)->set_definition_of_declaration |
| (per_tu_enum_map.begin()->second); |
| else |
| { |
| // We are in case where there are more than |
| // one definition for the declaration. Let's |
| // see if they are all equal. If they are, |
| // then the declaration resolves to the |
| // definition. Otherwise, we are in the case |
| // 3/ described above. |
| map<string, |
| enum_type_decl_sptr>::const_iterator it; |
| enum_type_decl_sptr first_enum = |
| per_tu_enum_map.begin()->second; |
| bool all_enum_definitions_are_equal = true; |
| for (it = per_tu_enum_map.begin(); |
| it != per_tu_enum_map.end(); |
| ++it) |
| { |
| if (it == per_tu_enum_map.begin()) |
| continue; |
| else |
| { |
| if (!compare_before_canonicalisation(it->second, |
| first_enum)) |
| { |
| all_enum_definitions_are_equal = false; |
| break; |
| } |
| } |
| } |
| if (all_enum_definitions_are_equal) |
| (*j)->set_definition_of_declaration(first_enum); |
| } |
| } |
| } |
| resolved_enums.push_back(i->first); |
| } |
| } |
| |
| size_t num_decl_only_enums = declaration_only_enums().size(), |
| num_resolved = resolved_enums.size(); |
| if (show_stats()) |
| cerr << "resolved " << num_resolved |
| << " enum declarations out of " |
| << num_decl_only_enums |
| << "\n"; |
| |
| for (vector<string>::const_iterator i = resolved_enums.begin(); |
| i != resolved_enums.end(); |
| ++i) |
| declaration_only_enums().erase(*i); |
| |
| if (show_stats() && !declaration_only_enums().empty()) |
| { |
| cerr << "Here are the " |
| << num_decl_only_enums - num_resolved |
| << " unresolved enum declarations:\n"; |
| for (string_enums_map::iterator i = declaration_only_enums().begin(); |
| i != declaration_only_enums().end(); |
| ++i) |
| cerr << " " << i->first << "\n"; |
| } |
| } |
| |
| /// Test if a symbol belongs to a function of the current ABI |
| /// corpus. |
| /// |
| /// This is a sub-routine of fixup_functions_with_no_symbols. |
| /// |
| /// @param fn the function symbol to consider. |
| /// |
| /// @returnt true if @p fn belongs to a function of the current ABI |
| /// corpus. |
| bool |
| symbol_already_belongs_to_a_function(elf_symbol_sptr& fn) |
| { |
| corpus_sptr corp = corpus(); |
| if (!corp) |
| return false; |
| |
| interned_string id = corp->get_environment().intern(fn->get_id_string()); |
| |
| const std::unordered_set<function_decl*> *fns = corp->lookup_functions(id); |
| if (!fns) |
| return false; |
| |
| for (auto f : *fns) |
| if (f->get_symbol()) |
| return true; |
| |
| return false; |
| } |
| |
| /// Some functions described by DWARF may have their linkage name |
| /// set, but no link to their actual underlying elf symbol. When |
| /// these are virtual member functions, comparing the enclosing type |
| /// against another one which has its underlying symbol properly set |
| /// might lead to spurious type changes. |
| /// |
| /// If the corpus contains a symbol with the same name as the |
| /// linkage name of the function, then set up the link between the |
| /// function and its underlying symbol. |
| /// |
| /// Note that for the moment, only virtual member functions are |
| /// fixed up like this. This is because they really are the only |
| /// fuctions of functions that can affect types (in spurious ways). |
| void |
| fixup_functions_with_no_symbols() |
| { |
| corpus_sptr corp = corpus(); |
| if (!corp) |
| return; |
| |
| die_function_decl_map_type &fns_with_no_symbol = |
| die_function_decl_with_no_symbol_map(); |
| |
| if (do_log()) |
| cerr << fns_with_no_symbol.size() |
| << " functions to fixup, potentially\n"; |
| |
| for (die_function_decl_map_type::iterator i = fns_with_no_symbol.begin(); |
| i != fns_with_no_symbol.end(); |
| ++i) |
| if (elf_symbol_sptr sym = |
| corp->lookup_function_symbol(i->second->get_linkage_name())) |
| { |
| // So i->second is a virtual member function that was |
| // previously scheduled to be set a function symbol. |
| // |
| // But if it appears that it now has a symbol already set, |
| // then do not set a symbol to it again. |
| // |
| // Or if it appears that another virtual member function |
| // from the current ABI Corpus, with the same linkage |
| // (mangled) name has already been set a symbol, then do not |
| // set a symbol to this function either. Otherwise, there |
| // will be two virtual member functions with the same symbol |
| // in the class and that leads to spurious hard-to-debug |
| // change reports later down the road. |
| if (i->second->get_symbol() |
| || symbol_already_belongs_to_a_function(sym)) |
| continue; |
| |
| ABG_ASSERT(is_member_function(i->second)); |
| ABG_ASSERT(get_member_function_is_virtual(i->second)); |
| i->second->set_symbol(sym); |
| |
| if (do_log()) |
| cerr << "fixed up '" |
| << i->second->get_pretty_representation() |
| << "' with symbol '" |
| << sym->get_id_string() |
| << "'\n"; |
| } |
| |
| fns_with_no_symbol.clear(); |
| } |
| |
| /// Copy missing member functions from a source @ref class_decl to a |
| /// destination one. |
| /// |
| /// If a function is present on the source @ref class_decl and not |
| /// on the destination one, then it's copied from the source class |
| /// to the destination one. |
| void |
| copy_missing_member_functions(const class_decl_sptr& dest_class, |
| const class_decl_sptr& src_class) |
| { |
| for (auto method : src_class->get_member_functions()) |
| if (!method->get_linkage_name().empty()) |
| if (!dest_class->find_member_function(method->get_linkage_name())) |
| { |
| method_decl_sptr copied_method = |
| copy_member_function(dest_class, method); |
| ABG_ASSERT(copied_method); |
| schedule_type_for_late_canonicalization(copied_method->get_type()); |
| } |
| } |
| |
| /// Test if there is an interator in a given range that points to |
| /// an anonymous class. |
| /// |
| /// @param begin the start of the iterator range to consider. |
| /// |
| /// @param end the end of the iterator range to consider. This |
| /// points to after the range. |
| template <typename iterator_type> |
| bool |
| contains_anonymous_class(const iterator_type& begin, |
| const iterator_type& end) |
| { |
| for (auto i = begin; i < end; ++i) |
| { |
| type_base_sptr t(*i); |
| class_decl_sptr c = is_class_type(t); |
| if (c && c->get_is_anonymous()) |
| return true; |
| } |
| return false; |
| } |
| |
| /// Ensure that all classes of the same name have the same virtual |
| /// member functions. So copy the virtual member functions from a |
| /// class C that have them to another class C that doesn't. |
| /// |
| /// @param begin an iterator to the first member of the set of |
| /// classes which to merge virtual member functions for. |
| /// |
| /// @param end an iterator to the last member (one past the end |
| /// actually) of the set of classes which to merge virtual member |
| /// functions for. |
| template <typename iterator_type> |
| void |
| merge_member_functions_of_classes(const iterator_type& begin, |
| const iterator_type& end) |
| { |
| if (contains_anonymous_class(begin, end)) |
| return; |
| |
| for (auto i = begin; i < end; ++i) |
| { |
| type_base_sptr t(*i); |
| class_decl_sptr reference_class = is_class_type(t); |
| if (!reference_class) |
| continue; |
| |
| string n1 = reference_class->get_pretty_representation(true, true); |
| string n2; |
| for (auto j = begin; j < end; ++j) |
| { |
| if (j == i) |
| continue; |
| |
| type_base_sptr type(*j); |
| class_decl_sptr klass = is_class_type(type); |
| if (!klass) |
| continue; |
| |
| n2 = klass->get_pretty_representation(true, true); |
| ABG_ASSERT(n1 == n2); |
| |
| copy_missing_member_functions(reference_class, klass); |
| copy_missing_member_functions(klass, reference_class); |
| } |
| } |
| } |
| |
| /// Ensure that all classes of the same name have the same virtual |
| /// member functions. So copy the virtual member functions from a |
| /// class C that have them to another class C that doesn't. |
| void |
| merge_member_functions_in_classes_of_same_names() |
| { |
| corpus_sptr abi = corpus(); |
| if (!abi) |
| return; |
| |
| istring_type_base_wptrs_map_type& class_types = |
| abi->get_types().class_types(); |
| |
| for (auto entry : class_types) |
| { |
| auto& classes = entry.second; |
| if (classes.size() > 1) |
| { |
| bool a_class_has_member_fns = false; |
| for (auto& c : classes) |
| { |
| type_base_sptr t(c); |
| if (class_decl_sptr klass = is_class_type(t)) |
| if (!klass->get_member_functions().empty()) |
| { |
| a_class_has_member_fns = true; |
| break; |
| } |
| } |
| if (a_class_has_member_fns) |
| merge_member_functions_of_classes(classes.begin(), |
| classes.end()); |
| } |
| } |
| } |
| |
| /// @return vectors of types created during the analysis of the |
| /// DWARF and in the need of being canonicalized. |
| const vector<type_base_sptr>& |
| types_to_canonicalize() const |
| {return types_to_canonicalize_;} |
| |
| /// @return vectors of types created during the analysis of the |
| /// DWARF and in the need of being canonicalized. |
| vector<type_base_sptr>& |
| types_to_canonicalize() |
| {return types_to_canonicalize_;} |
| |
| /// Clear the containers holding types to canonicalize. |
| void |
| clear_types_to_canonicalize() |
| { |
| types_to_canonicalize_.clear(); |
| } |
| |
| /// Types that were created but not tied to a particular DIE, must |
| /// be scheduled for late canonicalization using this method. |
| /// |
| /// @param t the type to schedule for late canonicalization. |
| void |
| schedule_type_for_late_canonicalization(const type_base_sptr &t) |
| { |
| types_to_canonicalize_.push_back(t); |
| } |
| |
| /// Canonicalize types which DIE offsets are stored in vectors on |
| /// the side. This is a sub-routine of |
| /// reader::perform_late_type_canonicalizing(). |
| /// |
| /// @param source where the DIE of the types to canonicalize are |
| /// from. |
| void |
| canonicalize_types_scheduled() |
| { |
| tools_utils::timer cn_timer; |
| if (do_log()) |
| { |
| cerr << "DWARF Reader is going to canonicalize types"; |
| corpus_sptr c = corpus(); |
| if (c) |
| cerr << " of corpus " << corpus()->get_path() << "\n"; |
| cn_timer.start(); |
| } |
| |
| if (!types_to_canonicalize().empty()) |
| canonicalize_types(types_to_canonicalize().begin(), |
| types_to_canonicalize().end(), |
| [](const vector<type_base_sptr>::const_iterator& i) |
| {return *i;}); |
| |
| if (do_log()) |
| { |
| cn_timer.stop(); |
| cerr << "finished canonicalizing types"; |
| corpus_sptr c = corpus(); |
| if (c) |
| cerr << " of corpus " << corpus()->get_path(); |
| cerr << ": (" << cn_timer << ")\n"; |
| } |
| } |
| |
| /// Compute the number of canonicalized and missed types in the late |
| /// canonicalization phase. |
| /// |
| /// @param source where the DIEs of the canonicalized types are |
| /// from. |
| /// |
| /// @param canonicalized the number of types that got canonicalized |
| /// is added to the value already present in this parameter. |
| /// |
| /// @param missed the number of types scheduled for late |
| /// canonicalization and which couldn't be canonicalized (for a |
| /// reason) is added to the value already present in this parameter. |
| void |
| add_late_canonicalized_types_stats(size_t& canonicalized, |
| size_t& missed) const |
| { |
| for (auto t : types_to_canonicalize()) |
| { |
| if (t->get_canonical_type()) |
| ++canonicalized; |
| else |
| ++missed; |
| } |
| } |
| |
| // Look at the types that need to be canonicalized after the |
| // translation unit has been constructed and canonicalize them. |
| void |
| perform_late_type_canonicalizing() |
| { |
| canonicalize_types_scheduled(); |
| |
| if (show_stats()) |
| { |
| size_t num_canonicalized = 0, num_missed = 0, total = 0; |
| add_late_canonicalized_types_stats(num_canonicalized, |
| num_missed); |
| total = num_canonicalized + num_missed; |
| cerr << "binary: " |
| << elf_path() |
| << "\n"; |
| cerr << " # late canonicalized types: " |
| << num_canonicalized; |
| if (total) |
| cerr << " (" << num_canonicalized * 100 / total << "%)"; |
| cerr << "\n" |
| << " # missed canonicalization opportunities: " |
| << num_missed; |
| if (total) |
| cerr << " (" << num_missed * 100 / total << "%)"; |
| cerr << "\n"; |
| } |
| |
| } |
| |
| const die_tu_map_type& |
| die_tu_map() const |
| {return die_tu_map_;} |
| |
| die_tu_map_type& |
| die_tu_map() |
| {return die_tu_map_;} |
| |
| /// Getter for the map that associates a translation unit DIE to the |
| /// vector of imported unit points that it contains. |
| /// |
| /// @param source where the DIEs are from. |
| /// |
| /// @return the map. |
| const tu_die_imported_unit_points_map_type& |
| tu_die_imported_unit_points_map(die_source source) const |
| {return const_cast<reader*>(this)->tu_die_imported_unit_points_map(source);} |
| |
| /// Getter for the map that associates a translation unit DIE to the |
| /// vector of imported unit points that it contains. |
| /// |
| /// @param source where the DIEs are from. |
| /// |
| /// @return the map. |
| tu_die_imported_unit_points_map_type& |
| tu_die_imported_unit_points_map(die_source source) |
| { |
| switch (source) |
| { |
| case PRIMARY_DEBUG_INFO_DIE_SOURCE: |
| break; |
| case ALT_DEBUG_INFO_DIE_SOURCE: |
| return alt_tu_die_imported_unit_points_map_; |
| case TYPE_UNIT_DIE_SOURCE: |
| return type_units_tu_die_imported_unit_points_map_; |
| case NO_DEBUG_INFO_DIE_SOURCE: |
| case NUMBER_OF_DIE_SOURCES: |
| // We cannot reach this point. |
| ABG_ASSERT_NOT_REACHED; |
| } |
| return tu_die_imported_unit_points_map_; |
| } |
| |
| /// Reset the current corpus being constructed. |
| /// |
| /// This actually deletes the current corpus being constructed. |
| void |
| reset_corpus() |
| {corpus().reset();} |
| |
| /// Get the map that associates each DIE to its parent DIE. This is |
| /// for DIEs coming from the main debug info sections. |
| /// |
| /// @param source where the DIEs in the map come from. |
| /// |
| /// @return the DIE -> parent map. |
| const offset_offset_map_type& |
| die_parent_map(die_source source) const |
| {return const_cast<reader*>(this)->die_parent_map(source);} |
| |
| /// Get the map that associates each DIE to its parent DIE. This is |
| /// for DIEs coming from the main debug info sections. |
| /// |
| /// @param source where the DIEs in the map come from. |
| /// |
| /// @return the DIE -> parent map. |
| offset_offset_map_type& |
| die_parent_map(die_source source) |
| { |
| switch (source) |
| { |
| case PRIMARY_DEBUG_INFO_DIE_SOURCE: |
| break; |
| case ALT_DEBUG_INFO_DIE_SOURCE: |
| return alternate_die_parent_map_; |
| case TYPE_UNIT_DIE_SOURCE: |
| return type_section_die_parent_map(); |
| case NO_DEBUG_INFO_DIE_SOURCE: |
| case NUMBER_OF_DIE_SOURCES: |
| ABG_ASSERT_NOT_REACHED; |
| } |
| return primary_die_parent_map_; |
| } |
| |
| const offset_offset_map_type& |
| type_section_die_parent_map() const |
| {return type_section_die_parent_map_;} |
| |
| offset_offset_map_type& |
| type_section_die_parent_map() |
| {return type_section_die_parent_map_;} |
| |
| /// Getter of the current translation unit. |
| /// |
| /// @return the current translation unit being constructed. |
| const translation_unit_sptr& |
| cur_transl_unit() const |
| {return cur_tu_;} |
| |
| /// Getter of the current translation unit. |
| /// |
| /// @return the current translation unit being constructed. |
| translation_unit_sptr& |
| cur_transl_unit() |
| {return cur_tu_;} |
| |
| /// Setter of the current translation unit. |
| /// |
| /// @param tu the current translation unit being constructed. |
| void |
| cur_transl_unit(translation_unit_sptr tu) |
| { |
| if (tu) |
| cur_tu_ = tu; |
| } |
| |
| /// Return the global scope of the current translation unit. |
| /// |
| /// @return the global scope of the current translation unit. |
| const scope_decl_sptr& |
| global_scope() const |
| {return cur_transl_unit()->get_global_scope();} |
| |
| /// Return a scope that is nil. |
| /// |
| /// @return a scope that is nil. |
| const scope_decl_sptr& |
| nil_scope() const |
| {return nil_scope_;} |
| |
| const scope_stack_type& |
| scope_stack() const |
| {return scope_stack_;} |
| |
| scope_stack_type& |
| scope_stack() |
| {return scope_stack_;} |
| |
| scope_decl* |
| current_scope() |
| { |
| if (scope_stack().empty()) |
| { |
| if (cur_transl_unit()) |
| scope_stack().push(cur_transl_unit()->get_global_scope().get()); |
| } |
| return scope_stack().top(); |
| } |
| |
| list<var_decl_sptr>& |
| var_decls_to_re_add_to_tree() |
| {return var_decls_to_add_;} |
| |
| /// Test if a DIE represents a decl (function or variable) that has |
| /// a symbol that is exported, whatever that means. This is |
| /// supposed to work for Linux Kernel binaries as well. |
| /// |
| /// This is useful to limit the amount of DIEs taken into account to |
| /// the strict limit of what an ABI actually means. Limiting the |
| /// volume of DIEs analyzed this way is an important optimization to |
| /// keep big binaries "manageable" by libabigail. |
| /// |
| /// @param DIE the die to consider. |
| bool |
| is_decl_die_with_exported_symbol(const Dwarf_Die *die) const |
| { |
| if (!die || !die_is_decl(die)) |
| return false; |
| |
| bool result = false, address_found = false, symbol_is_exported = false;; |
| Dwarf_Addr decl_symbol_address = 0; |
| |
| if (die_is_variable_decl(die)) |
| { |
| if ((address_found = get_variable_address(die, decl_symbol_address))) |
| symbol_is_exported = |
| !!variable_symbol_is_exported(decl_symbol_address); |
| } |
| else if (die_is_function_decl(die)) |
| { |
| if ((address_found = get_function_address(die, decl_symbol_address))) |
| symbol_is_exported = |
| !!function_symbol_is_exported(decl_symbol_address); |
| } |
| |
| if (address_found) |
| result = symbol_is_exported; |
| |
| return result; |
| } |
| |
| /// Test if a DIE is a variable or function DIE which name denotes |
| /// an undefined ELF symbol. |
| /// |
| /// @return true iff @p die represents a function or variable that |
| /// has an undefined symbol. |
| bool |
| is_decl_die_with_undefined_symbol(const Dwarf_Die *die) const |
| { |
| if (is_decl_die_with_exported_symbol(die)) |
| return false; |
| |
| string name, linkage_name; |
| die_name_and_linkage_name(die, name, linkage_name); |
| if (linkage_name.empty()) |
| linkage_name = name; |
| |
| bool result = false; |
| if ((die_is_variable_decl(die) |
| && symtab()->variable_symbol_is_undefined(linkage_name)) |
| || |
| (die_is_function_decl(die) |
| && symtab()->function_symbol_is_undefined(linkage_name))) |
| result = true; |
| |
| return result; |
| } |
| |
| /// This is a sub-routine of maybe_adjust_fn_sym_address and |
| /// maybe_adjust_var_sym_address. |
| /// |
| /// Given an address that we got by looking at some debug |
| /// information (e.g, a symbol's address referred to by a DWARF |
| /// TAG), If the ELF file we are interested in is a shared library |
| /// or an executable, then adjust the address to be coherent with |
| /// where the executable (or shared library) is loaded. That way, |
| /// the address can be used to look for symbols in the executable or |
| /// shared library. |
| /// |
| /// @return the adjusted address, or the same address as @p addr if |
| /// it didn't need any adjustment. |
| Dwarf_Addr |
| maybe_adjust_address_for_exec_or_dyn(Dwarf_Addr addr) const |
| { |
| if (addr == 0) |
| return addr; |
| |
| GElf_Ehdr eh_mem; |
| GElf_Ehdr *elf_header = gelf_getehdr(elf_handle(), &eh_mem); |
| |
| if (elf_header->e_type == ET_DYN || elf_header->e_type == ET_EXEC) |
| { |
| Dwarf_Addr dwarf_elf_load_address = 0, elf_load_address = 0; |
| ABG_ASSERT(get_binary_load_address(dwarf_elf_handle(), |
| dwarf_elf_load_address)); |
| ABG_ASSERT(get_binary_load_address(elf_handle(), |
| elf_load_address)); |
| if (dwarf_is_splitted() |
| && (dwarf_elf_load_address != elf_load_address)) |
| // This means that in theory the DWARF and the executable are |
| // not loaded at the same address. And addr is meaningful |
| // only in the context of the DWARF. |
| // |
| // So let's transform addr into an offset relative to where |
| // the DWARF is loaded, and let's add that relative offset |
| // to the load address of the executable. That way, addr |
| // becomes meaningful in the context of the executable and |
| // can thus be used to compare against the address of |
| // symbols of the executable, for instance. |
| addr = addr - dwarf_elf_load_address + elf_load_address; |
| } |
| |
| return addr; |
| } |
| |
| /// For a relocatable (*.o) elf file, this function expects an |
| /// absolute address, representing a function symbol. It then |
| /// extracts the address of the .text section from the symbol |
| /// absolute address to get the relative address of the function |
| /// from the beginning of the .text section. |
| /// |
| /// For executable or shared library, this function expects an |
| /// address of a function symbol that was retrieved by looking at a |
| /// DWARF "file". The function thus adjusts the address to make it |
| /// be meaningful in the context of the ELF file. |
| /// |
| /// In both cases, the address can then be compared against the |
| /// st_value field of a function symbol from the ELF file. |
| /// |
| /// @param addr an adress for a function symbol that was retrieved |
| /// from a DWARF file. |
| /// |
| /// @return the (possibly) adjusted address, or just @p addr if no |
| /// adjustment took place. |
| Dwarf_Addr |
| maybe_adjust_fn_sym_address(Dwarf_Addr addr) const |
| { |
| if (addr == 0) |
| return addr; |
| |
| Elf* elf = elf_handle(); |
| GElf_Ehdr eh_mem; |
| GElf_Ehdr* elf_header = gelf_getehdr(elf, &eh_mem); |
| |
| if (elf_header->e_type == ET_REL) |
| // We are looking at a relocatable file. In this case, we don't |
| // do anything because: |
| // |
| // 1/ the addresses from DWARF are absolute (relative to the |
| // beginning of the relocatable file) |
| // |
| // 2/ The ELF symbol addresses that we store in our lookup |
| // tables are translated from section-related to absolute as |
| // well. So we don't have anything to do at this point for |
| // ET_REL files. |
| ; |
| else |
| addr = maybe_adjust_address_for_exec_or_dyn(addr); |
| |
| return addr; |
| } |
| |
| /// For a relocatable (*.o) elf file, this function expects an |
| /// absolute address, representing a global variable symbol. It |
| /// then extracts the address of the {.data,.data1,.rodata,.bss} |
| /// section from the symbol absolute address to get the relative |
| /// address of the variable from the beginning of the data section. |
| /// |
| /// For executable or shared library, this function expects an |
| /// address of a variable symbol that was retrieved by looking at a |
| /// DWARF "file". The function thus adjusts the address to make it |
| /// be meaningful in the context of the ELF file. |
| /// |
| /// In both cases, the address can then be compared against the |
| /// st_value field of a function symbol from the ELF file. |
| /// |
| /// @param addr an address for a global variable symbol that was |
| /// retrieved from a DWARF file. |
| /// |
| /// @return the (possibly) adjusted address, or just @p addr if no |
| /// adjustment took place. |
| Dwarf_Addr |
| maybe_adjust_var_sym_address(Dwarf_Addr addr) const |
| { |
| Elf* elf = elf_handle(); |
| GElf_Ehdr eh_mem; |
| GElf_Ehdr* elf_header = gelf_getehdr(elf, &eh_mem); |
| |
| if (elf_header->e_type == ET_REL) |
| // We are looking at a relocatable file. In this case, we don't |
| // do anything because: |
| // |
| // 1/ the addresses from DWARF are absolute (relative to the |
| // beginning of the relocatable file) |
| // |
| // 2/ The ELF symbol addresses that we store in our lookup |
| // tables are translated from section-related to absolute as |
| // well. So we don't have anything to do at this point for |
| // ET_REL files. |
| ; |
| else |
| addr = maybe_adjust_address_for_exec_or_dyn(addr); |
| |
| return addr; |
| } |
| |
| /// Get the first exported function address in the set of addresses |
| /// referred to by the DW_AT_ranges attribute of a given DIE. |
| /// |
| /// @param die the DIE we are considering. |
| /// |
| /// @param address output parameter. This is set to the first |
| /// address found in the sequence pointed to by the DW_AT_ranges |
| /// attribute found on the DIE @p die, iff the function returns |
| /// true. Otherwise, no value is set into this output parameter. |
| /// |
| /// @return true iff the DIE @p die does have a DW_AT_ranges |
| /// attribute and an address of an exported function was found in |
| /// its sequence value. |
| bool |
| get_first_exported_fn_address_from_DW_AT_ranges(Dwarf_Die* die, |
| Dwarf_Addr& address) const |
| { |
| Dwarf_Addr base; |
| Dwarf_Addr end_addr; |
| ptrdiff_t offset = 0; |
| |
| do |
| { |
| Dwarf_Addr addr = 0, fn_addr = 0; |
| if ((offset = dwarf_ranges(die, offset, &base, &addr, &end_addr)) >= 0) |
| { |
| fn_addr = maybe_adjust_fn_sym_address(addr); |
| if (function_symbol_is_exported(fn_addr)) |
| { |
| address = fn_addr; |
| return true; |
| } |
| } |
| } while (offset > 0); |
| return false; |
| } |
| |
| /// Get the address of the function. |
| /// |
| /// The address of the function is considered to be the value of the |
| /// DW_AT_low_pc attribute, possibly adjusted (in relocatable files |
| /// only) to not point to an absolute address anymore, but rather to |
| /// the address of the function inside the .text segment. |
| /// |
| /// @param function_die the die of the function to consider. |
| /// |
| /// @param address the resulting address iff the function returns |
| /// true. |
| /// |
| /// @return true if the function address was found. |
| bool |
| get_function_address(const Dwarf_Die* function_die, Dwarf_Addr& address) const |
| { |
| if (!die_address_attribute(const_cast<Dwarf_Die*>(function_die), |
| DW_AT_low_pc, address)) |
| // So no DW_AT_low_pc was found. Let's see if the function DIE |
| // has got a DW_AT_ranges attribute instead. If it does, the |
| // first address of the set of addresses represented by the |
| // value of that DW_AT_ranges represents the function (symbol) |
| // address we are looking for. |
| if (!get_first_exported_fn_address_from_DW_AT_ranges |
| (const_cast<Dwarf_Die*>(function_die), |
| address)) |
| return false; |
| |
| address = maybe_adjust_fn_sym_address(address); |
| return true; |
| } |
| |
| /// Get the address of the global variable. |
| /// |
| /// The address of the global variable is considered to be the value |
| /// of the DW_AT_location attribute, possibly adjusted (in |
| /// relocatable files only) to not point to an absolute address |
| /// anymore, but rather to the address of the global variable inside |
| /// the data segment. |
| /// |
| /// @param variable_die the die of the function to consider. |
| /// |
| /// @param address the resulting address iff this function returns |
| /// true. |
| /// |
| /// @return true if the variable address was found. |
| bool |
| get_variable_address(const Dwarf_Die* variable_die, |
| Dwarf_Addr& address) const |
| { |
| bool is_tls_address = false; |
| if (!die_location_address(const_cast<Dwarf_Die*>(variable_die), |
| address, is_tls_address)) |
| return false; |
| if (!is_tls_address) |
| address = maybe_adjust_var_sym_address(address); |
| return true; |
| } |
| |
| /// Getter of the exported decls builder object. |
| /// |
| /// @return the exported decls builder. |
| corpus::exported_decls_builder* |
| exported_decls_builder() |
| {return corpus()->get_exported_decls_builder().get();} |
| |
| /// Getter of the "load_all_types" flag. This flag tells if all the |
| /// types (including those not reachable by public declarations) are |
| /// to be read and represented in the final ABI corpus. |
| /// |
| /// @return the load_all_types flag. |
| bool |
| load_all_types() const |
| {return options().load_all_types;} |
| |
| /// Setter of the "load_all_types" flag. This flag tells if all the |
| /// types (including those not reachable by public declarations) are |
| /// to be read and represented in the final ABI corpus. |
| /// |
| /// @param f the new load_all_types flag. |
| void |
| load_all_types(bool f) |
| {options().load_all_types = f;} |
| |
| bool |
| load_in_linux_kernel_mode() const |
| {return options().load_in_linux_kernel_mode;} |
| |
| void |
| load_in_linux_kernel_mode(bool f) |
| {options().load_in_linux_kernel_mode = f;} |
| |
| /// Getter of the 'load-undefined-interface' property. |
| /// |
| /// That property tells the reader if it should load the interfaces |
| /// that are undefined in the binary. An undefined interface is a |
| /// variable or function which has a symbol that is not defined in |
| /// the binary. |
| /// |
| /// @return true iff the front-end has to load the undefined |
| /// interfaces. |
| bool |
| load_undefined_interfaces() const |
| {return options().load_undefined_interfaces;} |
| |
| /// Test if it's allowed to assume that the DWARF debug info has |
| /// been factorized (for instance, with the DWZ tool) so that if two |
| /// type DIEs originating from the .gnu_debugaltlink section have |
| /// different offsets, they represent different types. |
| /// |
| /// @return true iff we can assume that the DWARF debug info has |
| /// been factorized. |
| bool |
| leverage_dwarf_factorization() const |
| { |
| if (!leverage_dwarf_factorization_.has_value()) |
| { |
| if (options().leverage_dwarf_factorization |
| && elf_helpers::find_section_by_name(elf_handle(), |
| ".gnu_debugaltlink")) |
| leverage_dwarf_factorization_ = true; |
| else |
| leverage_dwarf_factorization_ = false; |
| } |
| ABG_ASSERT(leverage_dwarf_factorization_.has_value()); |
| |
| return *leverage_dwarf_factorization_; |
| } |
| /// Getter of the "show_stats" flag. |
| /// |
| /// This flag tells if we should emit statistics about various |
| /// internal stuff. |
| /// |
| /// @return the value of the flag. |
| bool |
| show_stats() const |
| {return options().show_stats;} |
| |
| /// Setter of the "show_stats" flag. |
| /// |
| /// This flag tells if we should emit statistics about various |
| /// internal stuff. |
| /// |
| /// @param f the value of the flag. |
| void |
| show_stats(bool f) |
| {options().show_stats = f;} |
| |
| /// Getter of the "do_log" flag. |
| /// |
| /// This flag tells if we should log about various internal |
| /// details. |
| /// |
| /// return the "do_log" flag. |
| bool |
| do_log() const |
| {return options().do_log;} |
| |
| /// Setter of the "do_log" flag. |
| /// |
| /// This flag tells if we should log about various internal details. |
| /// |
| /// @param f the new value of the flag. |
| void |
| do_log(bool f) |
| {options().do_log = f;} |
| |
| /// Walk the DIEs under a given die and for each child, populate the |
| /// die -> parent map to record the child -> parent relationship |
| /// that |
| /// exists between the child and the given die. |
| /// |
| /// The function also builds the vector of places where units are |
| /// imported. |
| /// |
| /// This is done recursively as for each child DIE, this function |
| /// walks its children as well. |
| /// |
| /// @param die the DIE whose children to walk recursively. |
| /// |
| /// @param source where the DIE @p die comes from. |
| /// |
| /// @param imported_units a vector containing all the offsets of the |
| /// points where unit have been imported, under @p die. |
| void |
| build_die_parent_relations_under(Dwarf_Die* die, |
| die_source source, |
| imported_unit_points_type & imported_units) |
| { |
| if (!die) |
| return; |
| |
| offset_offset_map_type& parent_of = die_parent_map(source); |
| |
| Dwarf_Die child; |
| if (dwarf_child(die, &child) != 0) |
| return; |
| |
| do |
| { |
| parent_of[dwarf_dieoffset(&child)] = dwarf_dieoffset(die); |
| if (dwarf_tag(&child) == DW_TAG_imported_unit) |
| { |
| Dwarf_Die imported_unit; |
| if (die_die_attribute(&child, DW_AT_import, imported_unit) |
| // If the imported_unit has a sub-tree, let's record |
| // this point at which the sub-tree is imported into |
| // the current debug info. |
| // |
| // Otherwise, if the imported_unit has no sub-tree, |
| // there is no point in recording where a non-existent |
| // sub-tree is being imported. |
| // |
| // Note that the imported_unit_points_type type below |
| // expects the imported_unit to have a sub-tree. |
| && die_has_children(&imported_unit)) |
| { |
| die_source imported_unit_die_source = NO_DEBUG_INFO_DIE_SOURCE; |
| ABG_ASSERT(get_die_source(imported_unit, imported_unit_die_source)); |
| imported_units.push_back |
| (imported_unit_point(dwarf_dieoffset(&child), |
| imported_unit, |
| imported_unit_die_source)); |
| } |
| } |
| build_die_parent_relations_under(&child, source, imported_units); |
| } |
| while (dwarf_siblingof(&child, &child) == 0); |
| |
| } |
| |
| /// Determine if we do have to build a DIE -> parent map, depending |
| /// on a given language. |
| /// |
| /// Some languages like C++, Ada etc, do have the concept of |
| /// namespace and yet, the DIE data structure doesn't provide us |
| /// with a way to get the parent namespace of a given DIE. So for |
| /// those languages, we need to build a DIE -> parent map so that we |
| /// can get the namespace DIE (or more generally the scope DIE) of a given |
| /// DIE as we need it. |
| /// |
| /// But then some more basic languages like C or assembly don't have |
| /// that need. |
| /// |
| /// This function, depending on the language, tells us if we need to |
| /// build the DIE -> parent map or not. |
| /// |
| /// @param lang the language to consider. |
| /// |
| /// @return true iff we need to build the DIE -> parent map for this |
| /// language. |
| bool |
| do_we_build_die_parent_maps(translation_unit::language lang) |
| { |
| if (is_c_language(lang)) |
| return false; |
| |
| switch (lang) |
| { |
| case translation_unit::LANG_UNKNOWN: |
| #ifdef HAVE_DW_LANG_Mips_Assembler_enumerator |
| case translation_unit::LANG_Mips_Assembler: |
| #endif |
| return false; |
| default: |
| break; |
| } |
| return true; |
| } |
| |
| /// Walk all the DIEs accessible in the debug info (and in the |
| /// alternate debug info as well) and build maps representing the |
| /// relationship DIE -> parent. That is, make it so that we can get |
| /// the parent for a given DIE. |
| /// |
| /// Note that the goal of this map is to be able to get the parent |
| /// of a given DIE. This is to mainly to handle namespaces. For instance, |
| /// when we get a DIE of a type, and we want to build an internal |
| /// representation for it, we need to get its fully qualified name. |
| /// For that, we need to know what is the parent DIE of that type |
| /// DIE, so that we can know what the namespace of that type is. |
| /// |
| /// Note that as the C language doesn't have namespaces (all types |
| /// are defined in the same global namespace), this function doesn't |
| /// build the DIE -> parent map if the current translation unit |
| /// comes from C. This saves time on big C ELF files with a lot of |
| /// DIEs. |
| void |
| build_die_parent_maps() |
| { |
| bool we_do_have_to_build_die_parent_map = false; |
| uint8_t address_size = 0; |
| size_t header_size = 0; |
| // Get the DIE of the current translation unit, look at it to get |
| // its language. If that language is in C, then all types are in |
| // the global namespace so we don't need to build the DIE -> |
| // parent map. So we dont build it in that case. |
| for (Dwarf_Off offset = 0, next_offset = 0; |
| (dwarf_next_unit(const_cast<Dwarf*>(dwarf_debug_info()), |
| offset, &next_offset, &header_size, |
| NULL, NULL, &address_size, NULL, NULL, NULL) == 0); |
| offset = next_offset) |
| { |
| Dwarf_Off die_offset = offset + header_size; |
| Dwarf_Die cu; |
| if (!dwarf_offdie(const_cast<Dwarf*>(dwarf_debug_info()), |
| die_offset, &cu)) |
| continue; |
| |
| uint64_t l = 0; |
| die_unsigned_constant_attribute(&cu, DW_AT_language, l); |
| translation_unit::language lang = dwarf_language_to_tu_language(l); |
| if (do_we_build_die_parent_maps(lang)) |
| we_do_have_to_build_die_parent_map = true; |
| } |
| |
| if (!we_do_have_to_build_die_parent_map) |
| return; |
| |
| // Build the DIE -> parent relation for DIEs coming from the |
| // .debug_info section in the alternate debug info file. |
| die_source source = ALT_DEBUG_INFO_DIE_SOURCE; |
| for (Dwarf_Off offset = 0, next_offset = 0; |
| (dwarf_next_unit(const_cast<Dwarf*>(alternate_dwarf_debug_info()), |
| offset, &next_offset, &header_size, |
| NULL, NULL, &address_size, NULL, NULL, NULL) == 0); |
| offset = next_offset) |
| { |
| Dwarf_Off die_offset = offset + header_size; |
| Dwarf_Die cu; |
| if (!dwarf_offdie(const_cast<Dwarf*>(alternate_dwarf_debug_info()), |
| die_offset, &cu)) |
| continue; |
| cur_tu_die(&cu); |
| |
| imported_unit_points_type& imported_units = |
| tu_die_imported_unit_points_map(source)[die_offset] = |
| imported_unit_points_type(); |
| build_die_parent_relations_under(&cu, source, imported_units); |
| } |
| |
| // Build the DIE -> parent relation for DIEs coming from the |
| // .debug_info section of the main debug info file. |
| source = PRIMARY_DEBUG_INFO_DIE_SOURCE; |
| address_size = 0; |
| header_size = 0; |
| for (Dwarf_Off offset = 0, next_offset = 0; |
| (dwarf_next_unit(const_cast<Dwarf*>(dwarf_debug_info()), |
| offset, &next_offset, &header_size, |
| NULL, NULL, &address_size, NULL, NULL, NULL) == 0); |
| offset = next_offset) |
| { |
| Dwarf_Off die_offset = offset + header_size; |
| Dwarf_Die cu; |
| if (!dwarf_offdie(const_cast<Dwarf*>(dwarf_debug_info()), |
| die_offset, &cu)) |
| continue; |
| cur_tu_die(&cu); |
| imported_unit_points_type& imported_units = |
| tu_die_imported_unit_points_map(source)[die_offset] = |
| imported_unit_points_type(); |
| build_die_parent_relations_under(&cu, source, imported_units); |
| } |
| |
| // Build the DIE -> parent relation for DIEs coming from the |
| // .debug_types section. |
| source = TYPE_UNIT_DIE_SOURCE; |
| address_size = 0; |
| header_size = 0; |
| uint64_t type_signature = 0; |
| Dwarf_Off type_offset; |
| for (Dwarf_Off offset = 0, next_offset = 0; |
| (dwarf_next_unit(const_cast<Dwarf*>(dwarf_debug_info()), |
| offset, &next_offset, &header_size, |
| NULL, NULL, &address_size, NULL, |
| &type_signature, &type_offset) == 0); |
| offset = next_offset) |
| { |
| Dwarf_Off die_offset = offset + header_size; |
| Dwarf_Die cu; |
| |
| if (!dwarf_offdie_types(const_cast<Dwarf*>(dwarf_debug_info()), |
| die_offset, &cu)) |
| continue; |
| cur_tu_die(&cu); |
| imported_unit_points_type& imported_units = |
| tu_die_imported_unit_points_map(source)[die_offset] = |
| imported_unit_points_type(); |
| build_die_parent_relations_under(&cu, source, imported_units); |
| } |
| } |
| };// end class reader. |
| |
| /// The type of the aggregates being compared during a DIE comparison. |
| /// |
| /// This encapsulates the stack of aggregates being compared at any |
| /// single point. |
| /// |
| /// This is useful to detect "comparison cycles" and thus avoid the |
| /// resulting infinite loops. |
| /// |
| /// This is also useful for implementing a very important optimization |
| /// that takes place during the canonicalization |
| struct offset_pairs_stack_type |
| { |
| // The DWARF DWARF reader that is useful for so many things. |
| const reader& rdr_; |
| // The set of types that are being compared. This is to speed up |
| // searches. |
| offset_pair_set_type set_; |
| // The stack of types that are being compared. The top of the |
| // stack is the back of the vector. |
| offset_pair_vector_type vect_; |
| // A map that associates a redundant type pair to the vector of |
| // types that depends on it. |
| offset_pair_vect_map_type redundant_types_; |
| // A map that associates a dependant type to the vector of redundant |
| // types it depends on. |
| offset_pair_vect_map_type dependant_types_; |
| |
| offset_pairs_stack_type(const reader& rdr) |
| : rdr_ (rdr) |
| {} |
| |
| /// Add a pair of types being compared to the stack of aggregates |
| /// being compared. |
| /// |
| /// @param p the pair of offsets of the type DIEs to consider. |
| void |
| add(const offset_pair_type& p) |
| { |
| set_.insert(p); |
| vect_.push_back(p); |
| } |
| |
| /// Erase a pair of types being compared from the stack of |
| /// aggregates being compared. |
| /// |
| /// @param p the pair of offsets of the type DIEs to consider. |
| /// |
| /// @return true iff @p was found and erased from the stack. |
| bool |
| erase(const offset_pair_type& p) |
| { |
| if (set_.erase(p)) |
| { |
| offset_pair_vector_type::iterator i; |
| |
| for (i = vect_.begin();i < vect_.end(); ++i) |
| if (*i == p) |
| break; |
| |
| if (i != vect_.end()) |
| vect_.erase(i); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Test if a pair of type DIEs is part of the stack of type DIEs |
| /// being compared. |
| /// |
| /// @param p the pair of offsets of the type DIEs to consider. |
| /// |
| /// @return true iff @p was found in the stack of types being |
| /// compared. |
| bool |
| contains(const offset_pair_type &p) const |
| { |
| if (set_.find(p) == set_.end()) |
| return false; |
| return true; |
| } |
| |
| /// Get the set of comparison pair that depends on a given |
| /// comparison pair. |
| /// |
| /// A comparison pair T{t1,t2} depends on a comparison pair P{p1,p2} |
| /// if p1 is a subtype of t1 and p2 is a subtype of t2. In other |
| /// words, the pair T appears in the comparison stack BEFORE the |
| /// pair P. |
| /// |
| /// So, this function returns the vector of comparison pairs that |
| /// appear in the comparison stack AFTER a given comparison pair. |
| /// |
| /// @param p the comparison pair to consider. |
| /// |
| /// @param pairs out parameter. This is filled with the comparison |
| /// pairs that depend on @p, iff the function returns true. |
| /// |
| /// @return true iff comparison pairs depending on @p have been |
| /// found and collected in @pairs. |
| bool |
| get_pairs_that_depend_on(const offset_pair_type& p, |
| offset_pair_vector_type& pairs) const |
| { |
| bool result = false; |
| if (!contains(p)) |
| return result; |
| |
| // First, get an iterator on the position of 'p'. |
| offset_pair_vector_type::const_iterator i; |
| for (i = vect_.begin(); i != vect_.end(); ++i) |
| if (*i == p) |
| break; |
| |
| if (i == vect_.end()) |
| return result; |
| |
| // Then, harvest all the comparison pairs that come after the |
| // position of 'p'. |
| for (++i; i != vect_.end(); ++i) |
| { |
| pairs.push_back(*i); |
| result = true; |
| } |
| |
| return result; |
| } |
| |
| /// Record the fact that a set of comparison pairs depends on a |
| /// given comparison pair. |
| /// |
| /// Set a map that associates each dependant comparison pair to the |
| /// pair it depends on. |
| /// |
| /// @param p the comparison pair that the set depends on. |
| /// |
| /// @param dependant_types the set of types that depends on @p. |
| void |
| record_dependant_types(const offset_pair_type& p, |
| const offset_pair_vector_type& dependant_types) |
| { |
| for (auto type_pair : dependant_types) |
| dependant_types_[type_pair].push_back(p); |
| } |
| |
| /// Record a comparison pair as being redundant. |
| /// |
| /// |
| /// @param p the comparison pair to record as redundant. |
| void |
| record_redundant_type_die_pair(const offset_pair_type& p) |
| { |
| offset_pair_vector_type dependant_types; |
| get_pairs_that_depend_on(p, dependant_types); |
| |
| // First, record the relationship "p -> [pairs that depend on p]". |
| auto it = redundant_types_.find(p); |
| if (it == redundant_types_.end()) |
| { |
| auto entry = std::make_pair(p, dependant_types); |
| redundant_types_.insert(entry); |
| } |
| else |
| it->second.insert(it->second.end(), |
| dependant_types.begin(), |
| dependant_types.end()); |
| |
| // For each dependant type pair, record the association: |
| // dependant_pair --> [vect of redundant types] |
| record_dependant_types(p, dependant_types); |
| } |
| |
| /// Test if a given pair has been detected as redundant. |
| /// |
| /// @param p the pair of DIEs to consider. |
| /// |
| /// @return iff @p is redundant. |
| bool |
| is_redundant(const offset_pair_type& p) |
| { |
| auto i = redundant_types_.find(p); |
| if (i != redundant_types_.end()) |
| return true; |
| return false; |
| } |
| |
| /// Test if a given pair is dependant on at least a redundant type. |
| /// |
| /// @param p the pair to consider. |
| /// |
| /// @return true iff @p depends on a redundant type. |
| bool |
| depends_on_redundant_types(const offset_pair_type& p) |
| { |
| auto i = dependant_types_.find(p); |
| if (i == dependant_types_.end()) |
| return false; |
| return true; |
| } |
| |
| /// Remove a redundant pair from the system. |
| /// |
| /// This needs updating the system to also remove the dependant |
| /// types that depend on the redundant pair (if they depend only on |
| /// that redundant pair). |
| /// |
| /// @param p the pair to consider. |
| /// |
| /// @param erase_canonical_die_offset if true then erase the cached |
| /// comparison results for the redundant pair and its dependant |
| /// types. |
| void |
| erase_redundant_type_pair_entry(const offset_pair_type& p, |
| bool erase_cached_results = false) |
| { |
| // First, update the dependant types that depend on the redundant |
| // type pair |
| auto redundant_type = redundant_types_.find(p); |
| if (redundant_type != redundant_types_.end()) |
| { |
| for (auto dependant_type : redundant_type->second) |
| { |
| // Each dependant_type depends on the redundant type 'p', |
| // among others. |
| auto dependant_types_it = dependant_types_.find(dependant_type); |
| ABG_ASSERT(dependant_types_it != dependant_types_.end()); |
| // Erase the redundant type 'p' from the redundant types |
| // that dependant_type depends on. |
| { |
| auto i = dependant_types_it->second.begin(); |
| for (; i!= dependant_types_it->second.end();++i) |
| if (*i == p) |
| break; |
| if (i != dependant_types_it->second.end()) |
| dependant_types_it->second.erase(i); |
| } |
| // If the dependant type itself doesn't depend on ANY |
| // redundant type anymore, then remove the depend type |
| // from the map of the dependant types. |
| if (dependant_types_it->second.empty()) |
| { |
| if (erase_cached_results) |
| rdr_.die_comparison_results_.erase(dependant_type); |
| dependant_types_.erase(dependant_types_it); |
| } |
| } |
| } |
| if (erase_cached_results) |
| rdr_.die_comparison_results_.erase(p); |
| redundant_types_.erase(p); |
| } |
| |
| /// If a comparison pair has been detected as redundant, stop |
| /// tracking it as well as its dependant pairs. That will |
| /// essentially make it impossible to reset/cancel the canonical |
| /// propagated types for those depdant pairs, but will also save |
| /// ressources. |
| /// |
| /// @param p the comparison pair to consider. |
| void |
| confirm_canonical_propagated_type(const offset_pair_type& p) |
| {erase_redundant_type_pair_entry(p, /*erase_cached_results=*/true);} |
| |
| /// Walk the types that depend on a comparison pair and cancel their |
| /// canonical-propagate-type, that means remove their canonical |
| /// types and mark them as not being canonically-propagated. Also, |
| /// erase their cached comparison results that was likely set to |
| /// COMPARISON_RESULT_UNKNOWN. |
| /// |
| /// @param p the pair to consider. |
| void |
| cancel_canonical_propagated_type(const offset_pair_type& p) |
| { |
| offset_pair_set_type dependant_types; |
| get_dependant_types(p, dependant_types, /*transitive_closure=*/true); |
| for (auto dependant_type : dependant_types) |
| { |
| // If this dependant type was canonical-type-propagated then |
| // erase that canonical type. |
| if (rdr_.propagated_types_.find(dependant_type) |
| != rdr_.propagated_types_.end()) |
| { |
| rdr_.erase_canonical_die_offset(dependant_type.first.offset_, |
| dependant_type.first.source_, |
| /*die_as_type=*/true); |
| rdr_.propagated_types_.erase(dependant_type); |
| rdr_.cancelled_propagation_count_++; |
| } |
| // Update the cached result. We know the comparison result |
| // must now be different. |
| auto comp_result_it = rdr_.die_comparison_results_.find(dependant_type); |
| if (comp_result_it != rdr_.die_comparison_results_.end()) |
| comp_result_it->second= COMPARISON_RESULT_DIFFERENT; |
| } |
| |
| // Update the cached result of the root type to cancel too. |
| auto comp_result_it = rdr_.die_comparison_results_.find(p); |
| if (comp_result_it != rdr_.die_comparison_results_.end()) |
| { |
| // At this point, the result of p is either |
| // COMPARISON_RESULT_UNKNOWN (if we cache comparison |
| // results of that kind) or COMPARISON_RESULT_DIFFERENT. |
| // Make sure it's the cached result is now |
| // COMPARISON_RESULT_DIFFERENT. |
| if (comp_result_it->second == COMPARISON_RESULT_UNKNOWN) |
| comp_result_it->second= COMPARISON_RESULT_DIFFERENT; |
| ABG_ASSERT(comp_result_it->second == COMPARISON_RESULT_DIFFERENT); |
| } |
| |
| if (rdr_.propagated_types_.find(p) != rdr_.propagated_types_.end()) |
| { |
| rdr_.erase_canonical_die_offset(p.first.offset_, |
| p.first.source_, |
| /*die_as_type=*/true); |
| rdr_.propagated_types_.erase(p); |
| rdr_.cancelled_propagation_count_++; |
| } |
| } |
| |
| /// Get the set of comparison pairs that depend on a given pair. |
| /// |
| /// @param p the pair to consider. |
| /// |
| /// @param result this is set to the pairs that depend on @p, iff |
| /// the function returned true. |
| /// |
| /// @param transitive_closure if set to true, the transitive closure |
| /// of the @result is set to it. |
| /// |
| /// @return true iff @result could be filled with the dependant |
| /// types. |
| bool |
| get_dependant_types(const offset_pair_type& p, |
| offset_pair_set_type& result, |
| bool transitive_closure = false) |
| { |
| auto i = redundant_types_.find(p); |
| if (i != redundant_types_.end()) |
| { |
| for (auto dependant_type : i->second) |
| if (result.find(dependant_type) == result.end()) |
| { |
| result.insert(dependant_type); |
| if (transitive_closure) |
| get_dependant_types(p, result, /*transitive_closure=*/true); |
| } |
| return true; |
| } |
| return false; |
| } |
| }; // end struct offset_pairs_stack_type |
| |
| static type_or_decl_base_sptr |
| build_ir_node_from_die(reader& rdr, |
| Dwarf_Die* die, |
| scope_decl* scope, |
| bool called_from_public_decl, |
| size_t where_offset, |
| bool is_declaration_only = true, |
| bool is_required_decl_spec = false); |
| |
| static type_or_decl_base_sptr |
| build_ir_node_from_die(reader& rdr, |
| Dwarf_Die* die, |
| bool called_from_public_decl, |
| size_t where_offset); |
| |
| static decl_base_sptr |
| build_ir_node_for_void_type(reader& rdr); |
| |
| static type_or_decl_base_sptr |
| build_ir_node_for_void_pointer_type(reader& rdr); |
| |
| static class_decl_sptr |
| add_or_update_class_type(reader& rdr, |
| Dwarf_Die* die, |
| scope_decl* scope, |
| bool is_struct, |
| class_decl_sptr klass, |
| bool called_from_public_decl, |
| size_t where_offset, |
| bool is_declaration_only); |
| |
| static union_decl_sptr |
| add_or_update_union_type(reader& rdr, |
| Dwarf_Die* die, |
| scope_decl* scope, |
| union_decl_sptr union_type, |
| bool called_from_public_decl, |
| size_t where_offset, |
| bool is_declaration_only); |
| |
| static decl_base_sptr |
| build_ir_node_for_void_type(reader& rdr); |
| |
| static decl_base_sptr |
| build_ir_node_for_variadic_parameter_type(reader &rdr); |
| |
| static function_decl_sptr |
| build_function_decl(reader& rdr, |
| Dwarf_Die* die, |
| size_t where_offset, |
| function_decl_sptr fn); |
| |
| static bool |
| function_is_suppressed(const reader& rdr, |
| const scope_decl* scope, |
| Dwarf_Die *function_die, |
| bool is_declaration_only); |
| |
| static function_decl_sptr |
| build_or_get_fn_decl_if_not_suppressed(reader& rdr, |
| scope_decl *scope, |
| Dwarf_Die *die, |
| size_t where_offset, |
| bool is_declaration_only, |
| function_decl_sptr f); |
| |
| static var_decl_sptr |
| build_var_decl(reader& rdr, |
| Dwarf_Die *die, |
| size_t where_offset, |
| var_decl_sptr result = var_decl_sptr()); |
| |
| static var_decl_sptr |
| build_or_get_var_decl_if_not_suppressed(reader& rdr, |
| scope_decl *scope, |
| Dwarf_Die *die, |
| size_t where_offset, |
| bool is_declaration_only, |
| var_decl_sptr res = var_decl_sptr(), |
| bool is_required_decl_spec = false); |
| static bool |
| variable_is_suppressed(const reader& rdr, |
| const scope_decl* scope, |
| Dwarf_Die *variable_die, |
| bool is_declaration_only, |
| bool is_required_decl_spec = false); |
| |
| static void |
| finish_member_function_reading(Dwarf_Die* die, |
| const function_decl_sptr& f, |
| const class_or_union_sptr klass, |
| reader& rdr); |
| |
| /// Test if a given DIE is anonymous |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die is anonymous. |
| static bool |
| die_is_anonymous(const Dwarf_Die* die) |
| { |
| Dwarf_Attribute attr; |
| if (!dwarf_attr_integrate(const_cast<Dwarf_Die*>(die), DW_AT_name, &attr)) |
| return true; |
| return false; |
| } |
| |
| /// Test if a DIE is an anonymous data member, aka, "unnamed field". |
| /// |
| /// Unnamed fields are specified at |
| /// https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die is an anonymous data member. |
| static bool |
| die_is_anonymous_data_member(const Dwarf_Die* die) |
| { |
| if (!die |
| || dwarf_tag(const_cast<Dwarf_Die*>(die)) != DW_TAG_member |
| || !die_name(die).empty()) |
| return false; |
| |
| Dwarf_Die type_die; |
| if (!die_die_attribute(die, DW_AT_type, type_die)) |
| return false; |
| |
| if (dwarf_tag(&type_die) != DW_TAG_structure_type |
| && dwarf_tag(&type_die) != DW_TAG_union_type) |
| return false; |
| |
| return true; |
| } |
| |
| /// Get the value of an attribute that is supposed to be a string, or |
| /// an empty string if the attribute could not be found. |
| /// |
| /// @param die the DIE to get the attribute value from. |
| /// |
| /// @param attr_name the attribute name. Must come from dwarf.h and |
| /// be an enumerator representing an attribute like, e.g, DW_AT_name. |
| /// |
| /// @return the string representing the value of the attribute, or an |
| /// empty string if no string attribute could be found. |
| static string |
| die_string_attribute(const Dwarf_Die* die, unsigned attr_name) |
| { |
| if (!die) |
| return ""; |
| |
| Dwarf_Attribute attr; |
| if (!dwarf_attr_integrate(const_cast<Dwarf_Die*>(die), attr_name, &attr)) |
| return ""; |
| |
| const char* str = dwarf_formstring(&attr); |
| return str ? str : ""; |
| } |
| |
| /// Get the value of an attribute that is supposed to be a string, or |
| /// an empty string if the attribute could not be found. |
| /// |
| /// @param die the DIE to get the attribute value from. |
| /// |
| /// @param attr_name the attribute name. Must come from dwarf.h and |
| /// be an enumerator representing an attribute like, e.g, DW_AT_name. |
| /// |
| /// @return the char* representing the value of the attribute, or an |
| /// empty string if no string attribute could be found. |
| static const char* |
| die_char_str_attribute(const Dwarf_Die* die, unsigned attr_name) |
| { |
| if (!die) |
| return nullptr; |
| |
| Dwarf_Attribute attr; |
| if (!dwarf_attr_integrate(const_cast<Dwarf_Die*>(die), attr_name, &attr)) |
| return nullptr; |
| |
| const char* str = dwarf_formstring(&attr); |
| return str; |
| } |
| |
| /// Get the value of an attribute that is supposed to be an unsigned |
| /// constant. |
| /// |
| /// @param die the DIE to read the information from. |
| /// |
| /// @param attr_name the DW_AT_* name of the attribute. Must come |
| /// from dwarf.h and be an enumerator representing an attribute like, |
| /// e.g, DW_AT_decl_line. |
| /// |
| ///@param cst the output parameter that is set to the value of the |
| /// attribute @p attr_name. This parameter is set iff the function |
| /// return true. |
| /// |
| /// @return true if there was an attribute of the name @p attr_name |
| /// and with a value that is a constant, false otherwise. |
| static bool |
| die_unsigned_constant_attribute(const Dwarf_Die* die, |
| unsigned attr_name, |
| uint64_t& cst) |
| { |
| if (!die) |
| return false; |
| |
| Dwarf_Attribute attr; |
| Dwarf_Word result = 0; |
| if (!dwarf_attr_integrate(const_cast<Dwarf_Die*>(die), attr_name, &attr) |
| || dwarf_formudata(&attr, &result)) |
| return false; |
| |
| cst = result; |
| return true; |
| } |
| |
| /// Read a signed constant value from a given attribute. |
| /// |
| /// The signed constant expected must be of constant form. |
| /// |
| /// @param die the DIE to get the attribute from. |
| /// |
| /// @param attr_name the attribute name. |
| /// |
| /// @param cst the resulting signed constant read. |
| /// |
| /// @return true iff a signed constant attribute of the name @p |
| /// attr_name was found on the DIE @p die. |
| static bool |
| die_signed_constant_attribute(const Dwarf_Die *die, |
| unsigned attr_name, |
| int64_t& cst) |
| { |
| if (!die) |
| return false; |
| |
| Dwarf_Attribute attr; |
| Dwarf_Sword result = 0; |
| if (!dwarf_attr_integrate(const_cast<Dwarf_Die*>(die), attr_name, &attr) |
| || dwarf_formsdata(&attr, &result)) |
| return false; |
| |
| cst = result; |
| return true; |
| } |
| |
| /// Read the value of a constant attribute that is either signed or |
| /// unsigned into a array_type_def::subrange_type::bound_value value. |
| /// |
| /// The bound_value instance will capture the actual signedness of the |
| /// read attribute. |
| /// |
| /// @param die the DIE from which to read the value of the attribute. |
| /// |
| /// @param attr_name the attribute name to consider. |
| /// |
| /// @param is_signed true if the attribute value has to read as |
| /// signed. |
| /// |
| /// @param value the resulting value read from attribute @p attr_name |
| /// on DIE @p die. |
| /// |
| /// @return true iff DIE @p die has an attribute named @p attr_name |
| /// with a constant value. |
| static bool |
| die_constant_attribute(const Dwarf_Die *die, |
| unsigned attr_name, |
| bool is_signed, |
| array_type_def::subrange_type::bound_value &value) |
| { |
| if (!is_signed) |
| { |
| uint64_t l = 0; |
| if (!die_unsigned_constant_attribute(die, attr_name, l)) |
| return false; |
| value.set_unsigned(l); |
| } |
| else |
| { |
| int64_t l = 0; |
| if (!die_signed_constant_attribute(die, attr_name, l)) |
| return false; |
| value.set_signed(l); |
| } |
| return true; |
| } |
| |
| /// Test if a given DWARF form is DW_FORM_strx{1,4}. |
| /// |
| /// Unfortunaly, the DW_FORM_strx{1,4} are enumerators of an untagged |
| /// enum in dwarf.h so we have to use an unsigned int for the form, |
| /// grrr. |
| /// |
| /// @param form the form to consider. |
| /// |
| /// @return true iff @p form is DW_FORM_strx{1,4}. |
| static bool |
| form_is_DW_FORM_strx(unsigned form) |
| { |
| if (form) |
| { |
| #if defined HAVE_DW_FORM_strx1 \ |
| && defined HAVE_DW_FORM_strx2 \ |
| && defined HAVE_DW_FORM_strx3 \ |
| && defined HAVE_DW_FORM_strx4 |
| if (form == DW_FORM_strx1 |
| || form == DW_FORM_strx2 |
| || form == DW_FORM_strx3 |
| ||form == DW_FORM_strx4) |
| return true; |
| #endif |
| } |
| return false; |
| } |
| |
| /// Test if a given DWARF form is DW_FORM_line_strp. |
| /// |
| /// Unfortunaly, the DW_FORM_line_strp is an enumerator of an untagged |
| /// enum in dwarf.h so we have to use an unsigned int for the form, |
| /// grrr. |
| /// |
| /// @param form the form to consider. |
| /// |
| /// @return true iff @p form is DW_FORM_line_strp. |
| static bool |
| form_is_DW_FORM_line_strp(unsigned form) |
| { |
| if (form) |
| { |
| #if defined HAVE_DW_FORM_line_strp |
| if (form == DW_FORM_line_strp) |
| return true; |
| #endif |
| } |
| return false; |
| } |
| |
| /// Get the value of a DIE attribute; that value is meant to be a |
| /// flag. |
| /// |
| /// @param die the DIE to get the attribute from. |
| /// |
| /// @param attr_name the DW_AT_* name of the attribute. Must come |
| /// from dwarf.h and be an enumerator representing an attribute like, |
| /// e.g, DW_AT_external. |
| /// |
| /// @param flag the output parameter to store the flag value into. |
| /// This is set iff the function returns true. |
| /// |
| /// @param recursively if true, the function looks through the |
| /// possible DW_AT_specification and DW_AT_abstract_origin attribute |
| /// all the way down to the initial DIE that is cloned and look on |
| /// that DIE to see if it has the @p attr_name attribute. |
| /// |
| /// @return true if the DIE has a flag attribute named @p attr_name, |
| /// false otherwise. |
| static bool |
| die_flag_attribute(const Dwarf_Die* die, |
| unsigned attr_name, |
| bool& flag, |
| bool recursively = true) |
| { |
| Dwarf_Attribute attr; |
| if (recursively |
| ? !dwarf_attr_integrate(const_cast<Dwarf_Die*>(die), attr_name, &attr) |
| : !dwarf_attr(const_cast<Dwarf_Die*>(die), attr_name, &attr)) |
| return false; |
| |
| bool f = false; |
| if (dwarf_formflag(&attr, &f)) |
| return false; |
| |
| flag = f; |
| return true; |
| } |
| |
| /// Get the mangled name from a given DIE. |
| /// |
| /// @param die the DIE to read the mangled name from. |
| /// |
| /// @return the mangled name if it's present in the DIE, or just an |
| /// empty string if it's not. |
| static string |
| die_linkage_name(const Dwarf_Die* die) |
| { |
| if (!die) |
| return ""; |
| |
| string linkage_name = die_string_attribute(die, DW_AT_linkage_name); |
| if (linkage_name.empty()) |
| linkage_name = die_string_attribute(die, DW_AT_MIPS_linkage_name); |
| return linkage_name; |
| } |
| |
| /// Get the file path that is the value of the DW_AT_decl_file |
| /// attribute on a given DIE, if the DIE is a decl DIE having that |
| /// attribute. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return a string containing the file path that is the logical |
| /// value of the DW_AT_decl_file attribute. If the DIE @p die |
| /// doesn't have a DW_AT_decl_file attribute, then the return value is |
| /// just an empty string. |
| static string |
| die_decl_file_attribute(const Dwarf_Die* die) |
| { |
| if (!die) |
| return ""; |
| |
| const char* str = dwarf_decl_file(const_cast<Dwarf_Die*>(die)); |
| |
| return str ? str : ""; |
| } |
| |
| /// Get the value of an attribute which value is supposed to be a |
| /// reference to a DIE. |
| /// |
| /// @param die the DIE to read the value from. |
| /// |
| /// @param attr_name the DW_AT_* attribute name to read. |
| /// |
| /// @param result the DIE resulting from reading the attribute value. |
| /// This is set iff the function returns true. |
| /// |
| /// @param recursively if true, the function looks through the |
| /// possible DW_AT_specification and DW_AT_abstract_origin attribute |
| /// all the way down to the initial DIE that is cloned and look on |
| /// that DIE to see if it has the @p attr_name attribute. |
| /// |
| /// @return true if the DIE @p die contains an attribute named @p |
| /// attr_name that is a DIE reference, false otherwise. |
| static bool |
| die_die_attribute(const Dwarf_Die* die, |
| unsigned attr_name, |
| Dwarf_Die& result, |
| bool recursively) |
| { |
| Dwarf_Attribute attr; |
| if (recursively |
| ? !dwarf_attr_integrate(const_cast<Dwarf_Die*>(die), attr_name, &attr) |
| : !dwarf_attr(const_cast<Dwarf_Die*>(die), attr_name, &attr)) |
| return false; |
| |
| return dwarf_formref_die(&attr, &result); |
| } |
| |
| /// Get the DIE that is the "origin" of the current one. |
| /// |
| /// Some DIEs have a DW_AT_abstract_origin or a DW_AT_specification |
| /// attribute. Those DIEs represent a concrete instance of an |
| /// abstract entity. The concrete instance can be a concrete instance |
| /// of an inline function, or the concrete implementation of an |
| /// abstract interface. On both cases, we call the abstract instance |
| /// from which the concrete instance derives the "origin". |
| /// |
| /// This function returns the ultimate origin DIE of a given DIE by |
| /// following the chain of its DW_AT_abstract_origin and |
| /// DW_AT_specification attributes. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param origin_die this is an output parameter that is set by this |
| /// function to the resulting origin DIE iff the function returns |
| /// true. |
| /// |
| /// @return true iff the function actually found an origin DIE and |
| /// set it to the @p origin_die parameter. |
| static bool |
| die_origin_die(const Dwarf_Die* die, Dwarf_Die& origin_die) |
| { |
| if (die_die_attribute(die, DW_AT_specification, origin_die, true) |
| || die_die_attribute(die, DW_AT_abstract_origin, origin_die, true)) |
| { |
| while (die_die_attribute(&origin_die, |
| DW_AT_specification, |
| origin_die, true) |
| || die_die_attribute(&origin_die, |
| DW_AT_abstract_origin, |
| origin_die, true)) |
| { |
| // Keep looking for the origin die ... |
| ; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /// Test if a subrange DIE indirectly references another subrange DIE |
| /// through a given attribute. |
| /// |
| /// A DW_TAG_subrange_type DIE can have its DW_AT_{lower,upper}_bound |
| /// attribute be a reference to either a data member or a variable |
| /// which type is itself a DW_TAG_subrange_type. This latter subrange |
| /// DIE is said to be "indirectly referenced" by the former subrange |
| /// DIE. In that case, the DW_AT_{lower,upper}_bound of the latter is |
| /// the value we want for the DW_AT_upper_bound of the former. |
| /// |
| /// This function tests if the former subrange DIE does indirectly |
| /// reference another subrange DIE through a given attribute (not |
| /// necessarily DW_AT_upper_bound). |
| /// |
| /// @param die the DIE to consider. Note that It must be a |
| /// DW_TAG_subrange_type. |
| /// |
| /// @param attr_name the name of the attribute to look through for the |
| /// indirectly referenced subrange DIE. |
| /// |
| /// @param referenced_subrange if the function returns true, then the |
| /// argument of this parameter is set to the indirectly referenced |
| /// DW_TAG_subrange_type DIE. |
| /// |
| /// @return true iff @p DIE indirectly references a subrange DIE |
| /// through the attribute @p attr_name. |
| static bool |
| subrange_die_indirectly_references_subrange_die(const Dwarf_Die *die, |
| unsigned attr_name, |
| Dwarf_Die& referenced_subrange) |
| { |
| bool result = false; |
| |
| if (dwarf_tag(const_cast<Dwarf_Die*>(die)) != DW_TAG_subrange_type) |
| return result; |
| |
| Dwarf_Die referenced_die; |
| if (die_die_attribute(die, attr_name, referenced_die)) |
| { |
| unsigned tag = dwarf_tag(&referenced_die); |
| if ( tag == DW_TAG_member || tag == DW_TAG_variable) |
| { |
| Dwarf_Die type_die; |
| if (die_die_attribute(&referenced_die, DW_AT_type, type_die)) |
| { |
| tag = dwarf_tag(&type_die); |
| if (tag == DW_TAG_subrange_type) |
| { |
| memcpy(&referenced_subrange, &type_die, sizeof(type_die)); |
| result = true; |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| /// Return the bound value of subrange die by looking at an indirectly |
| /// referenced subrange DIE. |
| /// |
| /// A DW_TAG_subrange_type DIE can have its DW_AT_{lower,upper}_bound |
| /// attribute be a reference to either a data member or a variable |
| /// which type is itself a DW_TAG_subrange_type. This latter subrange |
| /// DIE is said to be "indirectly referenced" by the former subrange |
| /// DIE. In that case, the DW_AT_{lower,upper}_bound of the latter is |
| /// the value we want for the DW_AT_{lower,upper}_bound of the former. |
| /// |
| /// This function gets the DW_AT_{lower,upper}_bound value of a |
| /// subrange type by looking at the DW_AT_{lower,upper}_bound value of |
| /// the indirectly referenced subrange type, if it exists. |
| /// |
| /// @param die the subrange DIE to consider. |
| /// |
| /// @param attr_name the name of the attribute to consider, typically, |
| /// DW_AT_{lower,upper}_bound. |
| /// |
| /// @param v the found value, iff this function returned true. |
| /// |
| /// @param is_signed, this is set to true if @p v is signed. This |
| /// parameter is set at all only if the function returns true. |
| /// |
| /// @return true iff the DW_AT_{lower,upper}_bound was found on the |
| /// indirectly referenced subrange type. |
| static bool |
| subrange_die_indirect_bound_value(const Dwarf_Die *die, |
| unsigned attr_name, |
| array_type_def::subrange_type::bound_value& v, |
| bool& is_signed) |
| { |
| bool result = false; |
| |
| if (dwarf_tag(const_cast<Dwarf_Die*>(die)) != DW_TAG_subrange_type) |
| return result; |
| |
| Dwarf_Die subrange_die; |
| if (subrange_die_indirectly_references_subrange_die(die, attr_name, |
| subrange_die)) |
| { |
| if (die_constant_attribute(&subrange_die, attr_name, is_signed, v)) |
| result = true; |
| } |
| return result; |
| } |
| |
| /// Read and return an addresss class attribute from a given DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param attr_name the name of the address class attribute to read |
| /// the value from. |
| /// |
| /// @param the resulting address. |
| /// |
| /// @return true iff the attribute could be read, was of the expected |
| /// address class and could thus be translated into the @p result. |
| static bool |
| die_address_attribute(Dwarf_Die* die, unsigned attr_name, Dwarf_Addr& result) |
| { |
| Dwarf_Attribute attr; |
| if (!dwarf_attr_integrate(die, attr_name, &attr)) |
| return false; |
| return dwarf_formaddr(&attr, &result) == 0; |
| } |
| |
| /// Returns the source location associated with a decl DIE. |
| /// |
| /// @param rdr the @ref reader to use. |
| /// |
| /// @param die the DIE the read the source location from. |
| /// |
| /// @return the location associated with @p die. |
| static location |
| die_location(const reader& rdr, const Dwarf_Die* die) |
| { |
| if (!die) |
| return location(); |
| |
| string file = die_decl_file_attribute(die); |
| uint64_t line = 0; |
| die_unsigned_constant_attribute(die, DW_AT_decl_line, line); |
| |
| if (!file.empty() && line != 0) |
| { |
| translation_unit_sptr tu = rdr.cur_transl_unit(); |
| location l = tu->get_loc_mgr().create_new_location(file, line, 1); |
| return l; |
| } |
| return location(); |
| } |
| |
| /// Return a copy of the name of a DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return a copy of the name of the DIE. |
| static string |
| die_name(const Dwarf_Die* die) |
| { |
| string name = die_string_attribute(die, DW_AT_name); |
| return name; |
| } |
| |
| /// Return the location, the name and the mangled name of a given DIE. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the DIE to read location and names from. |
| /// |
| /// @param loc the location output parameter to set. |
| /// |
| /// @param name the name output parameter to set. |
| /// |
| /// @param linkage_name the linkage_name output parameter to set. |
| static void |
| die_loc_and_name(const reader& rdr, |
| Dwarf_Die* die, |
| location& loc, |
| string& name, |
| string& linkage_name) |
| { |
| loc = die_location(rdr, die); |
| name = die_name(die); |
| linkage_name = die_linkage_name(die); |
| } |
| |
| /// Return the name and the mangled name of a given DIE. |
| /// |
| /// @param die the DIE to read location and names from. |
| /// |
| /// @param name the name output parameter to set. |
| /// |
| /// @param linkage_name the linkage_name output parameter to set. |
| static void |
| die_name_and_linkage_name(const Dwarf_Die* die, |
| string& name, |
| string& linkage_name) |
| { |
| name = die_name(die); |
| linkage_name = die_linkage_name(die); |
| } |
| |
| /// Get the size of a (type) DIE as the value for the parameter |
| /// DW_AT_byte_size or DW_AT_bit_size. |
| /// |
| /// @param die the DIE to read the information from. |
| /// |
| /// @param size the resulting size in bits. This is set iff the |
| /// function return true. |
| /// |
| /// @return true if the size attribute was found. |
| static bool |
| die_size_in_bits(const Dwarf_Die* die, uint64_t& size) |
| { |
| if (!die) |
| return false; |
| |
| uint64_t byte_size = 0, bit_size = 0; |
| |
| if (!die_unsigned_constant_attribute(die, DW_AT_byte_size, byte_size)) |
| { |
| if (!die_unsigned_constant_attribute(die, DW_AT_bit_size, bit_size)) |
| return false; |
| } |
| else |
| bit_size = byte_size * 8; |
| |
| size = bit_size; |
| |
| return true; |
| } |
| |
| /// Get the access specifier (from the DW_AT_accessibility attribute |
| /// value) of a given DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param access the resulting access. This is set iff the function |
| /// returns true. |
| /// |
| /// @return bool if the DIE contains the DW_AT_accessibility die. |
| static bool |
| die_access_specifier(Dwarf_Die * die, access_specifier& access) |
| { |
| if (!die) |
| return false; |
| |
| uint64_t a = 0; |
| if (!die_unsigned_constant_attribute(die, DW_AT_accessibility, a)) |
| return false; |
| |
| access_specifier result = private_access; |
| |
| switch (a) |
| { |
| case private_access: |
| result = private_access; |
| break; |
| |
| case protected_access: |
| result = protected_access; |
| break; |
| |
| case public_access: |
| result = public_access; |
| break; |
| |
| default: |
| break; |
| } |
| |
| access = result; |
| return true; |
| } |
| |
| /// Test whether a given DIE represents a decl that is public. That |
| /// is, one with the DW_AT_external attribute set. |
| /// |
| /// @param die the DIE to consider for testing. |
| /// |
| /// @return true if a DW_AT_external attribute is present and its |
| /// value is set to the true; return false otherwise. |
| static bool |
| die_is_public_decl(const Dwarf_Die* die) |
| { |
| if (!die) |
| return false; |
| bool is_public = false; |
| |
| // If this is a DW_TAG_subprogram DIE, look for the |
| // DW_AT_external attribute on it. Otherwise, if it's a non-anonymous namespace, |
| // then it's public. In all other cases, this should return false. |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag == DW_TAG_subprogram || tag == DW_TAG_variable) |
| die_flag_attribute(die, DW_AT_external, is_public); |
| else if (tag == DW_TAG_namespace) |
| { |
| string name = die_name(die); |
| is_public = !name.empty(); |
| } |
| |
| return is_public; |
| } |
| |
| /// Test if a DIE is effectively public. |
| /// |
| /// This is meant to return true when either the DIE is public or when |
| /// it's a variable DIE that is at (global) namespace level. |
| /// |
| /// @return true iff either the DIE is public or is a variable DIE |
| /// that is at (global) namespace level. |
| static bool |
| die_is_effectively_public_decl(const reader& rdr, |
| const Dwarf_Die* die) |
| { |
| if (die_is_public_decl(die)) |
| return true; |
| |
| unsigned tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag == DW_TAG_variable || tag == DW_TAG_member) |
| { |
| // The DIE is a variable. |
| Dwarf_Die parent_die; |
| size_t where_offset = 0; |
| if (!get_parent_die(rdr, die, parent_die, where_offset)) |
| return false; |
| |
| tag = dwarf_tag(&parent_die); |
| if (tag == DW_TAG_compile_unit |
| || tag == DW_TAG_partial_unit |
| || tag == DW_TAG_type_unit) |
| // The DIE is at global scope. |
| return true; |
| |
| if (tag == DW_TAG_namespace) |
| { |
| string name = die_name(&parent_die); |
| if (name.empty()) |
| // The DIE at unnamed namespace scope, so it's not public. |
| return false; |
| // The DIE is at namespace scope. |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// Test whether a given DIE represents a declaration-only DIE. |
| /// |
| /// That is, if the DIE has the DW_AT_declaration flag set. |
| /// |
| /// @param die the DIE to consider. |
| // |
| /// @return true if a DW_AT_declaration is present, false otherwise. |
| static bool |
| die_is_declaration_only(Dwarf_Die* die) |
| { |
| bool is_declaration = false; |
| die_flag_attribute(die, DW_AT_declaration, is_declaration, false); |
| if (is_declaration && !die_has_size_attribute(die)) |
| return true; |
| return false; |
| } |
| |
| /// Test if a DIE is for a function decl. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die represents a function decl. |
| static bool |
| die_is_function_decl(const Dwarf_Die *die) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag == DW_TAG_subprogram) |
| return true; |
| return false; |
| } |
| |
| /// Test if a DIE is for a variable decl. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die represents a variable decl. |
| static bool |
| die_is_variable_decl(const Dwarf_Die *die) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag == DW_TAG_variable) |
| return true; |
| return false; |
| } |
| |
| /// Test if a DIE has size attribute. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true if the DIE has a size attribute. |
| static bool |
| die_has_size_attribute(const Dwarf_Die *die) |
| { |
| uint64_t s; |
| if (die_size_in_bits(die, s)) |
| return true; |
| return false; |
| } |
| |
| /// Test that a DIE has no child DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die has no child DIE. |
| static bool |
| die_has_no_child(const Dwarf_Die *die) |
| { |
| if (!die) |
| return true; |
| |
| Dwarf_Die child; |
| if (dwarf_child(const_cast<Dwarf_Die*>(die), &child) == 0) |
| return false; |
| return true; |
| } |
| |
| /// Test whether a given DIE represents a declaration-only DIE. |
| /// |
| /// That is, if the DIE has the DW_AT_declaration flag set. |
| /// |
| /// @param die the DIE to consider. |
| // |
| /// @return true if a DW_AT_declaration is present, false otherwise. |
| static bool |
| die_is_declaration_only(const Dwarf_Die* die) |
| {return die_is_declaration_only(const_cast<Dwarf_Die*>(die));} |
| |
| /// Tests whether a given DIE is artificial. |
| /// |
| /// @param die the test to test for. |
| /// |
| /// @return true if the DIE is artificial, false otherwise. |
| static bool |
| die_is_artificial(Dwarf_Die* die) |
| { |
| bool is_artificial; |
| return die_flag_attribute(die, DW_AT_artificial, is_artificial); |
| } |
| |
| ///@return true if a tag represents a type, false otherwise. |
| /// |
| ///@param tag the tag to consider. |
| static bool |
| is_type_tag(unsigned tag) |
| { |
| bool result = false; |
| |
| switch (tag) |
| { |
| case DW_TAG_array_type: |
| case DW_TAG_class_type: |
| case DW_TAG_enumeration_type: |
| case DW_TAG_pointer_type: |
| case DW_TAG_reference_type: |
| case DW_TAG_string_type: |
| case DW_TAG_structure_type: |
| case DW_TAG_subroutine_type: |
| case DW_TAG_typedef: |
| case DW_TAG_union_type: |
| case DW_TAG_ptr_to_member_type: |
| case DW_TAG_set_type: |
| case DW_TAG_subrange_type: |
| case DW_TAG_base_type: |
| case DW_TAG_const_type: |
| case DW_TAG_file_type: |
| case DW_TAG_packed_type: |
| case DW_TAG_thrown_type: |
| case DW_TAG_volatile_type: |
| case DW_TAG_restrict_type: |
| case DW_TAG_interface_type: |
| case DW_TAG_unspecified_type: |
| case DW_TAG_shared_type: |
| case DW_TAG_rvalue_reference_type: |
| case DW_TAG_coarray_type: |
| case DW_TAG_atomic_type: |
| case DW_TAG_immutable_type: |
| result = true; |
| break; |
| |
| default: |
| result = false; |
| break; |
| } |
| |
| return result; |
| } |
| |
| /// Test if a given DIE is a type whose canonical type is to be |
| /// propagated during DIE canonicalization |
| /// |
| /// This is a sub-routine of compare_dies. |
| /// |
| /// @param tag the tag of the DIE to consider. |
| /// |
| /// @return true iff the DIE of tag @p tag is can see its canonical |
| /// type be propagated during the type comparison that happens during |
| /// DIE canonicalization. |
| static bool |
| is_canon_type_to_be_propagated_tag(unsigned tag) |
| { |
| bool result = false; |
| |
| switch (tag) |
| { |
| case DW_TAG_class_type: |
| case DW_TAG_structure_type: |
| case DW_TAG_union_type: |
| case DW_TAG_subroutine_type: |
| case DW_TAG_subprogram: |
| result = true; |
| break; |
| |
| default: |
| result = false; |
| break; |
| } |
| |
| return result; |
| } |
| |
| /// Test if a given kind of DIE ought to have its comparison result |
| /// cached by compare_dies, so that subsequent invocations of |
| /// compare_dies can be faster. |
| /// |
| /// @param tag the tag of the DIE to consider. |
| /// |
| /// @return true iff DIEs of the tag @p tag ought to have its |
| /// comparison results cached. |
| static bool |
| type_comparison_result_to_be_cached(unsigned tag) |
| { |
| bool r = false; |
| switch (tag) |
| { |
| case DW_TAG_class_type: |
| case DW_TAG_structure_type: |
| case DW_TAG_union_type: |
| case DW_TAG_subroutine_type: |
| case DW_TAG_subprogram: |
| r = true; |
| break; |
| |
| default: |
| r = false; |
| break; |
| } |
| return r; |
| } |
| |
| /// Cache the result of comparing to type DIEs. |
| /// |
| /// @param rdr the context to consider. |
| /// |
| /// @param tag the tag of the DIEs to consider. |
| /// |
| /// @param p the offsets of the pair of DIEs being compared. |
| /// |
| /// @param result the comparison result to be cached. |
| static bool |
| maybe_cache_type_comparison_result(const reader& rdr, |
| int tag, |
| const offset_pair_type& p, |
| comparison_result result) |
| { |
| if (!type_comparison_result_to_be_cached(tag) |
| || (result != COMPARISON_RESULT_EQUAL |
| && result != COMPARISON_RESULT_DIFFERENT)) |
| return false; |
| |
| rdr.die_comparison_results_[p] = result; |
| |
| return true; |
| |
| } |
| |
| /// Get the cached result of the comparison of a pair of DIEs. |
| /// |
| /// @param rdr the context to consider. |
| /// |
| /// @param tag the tag of the pair of DIEs to consider. |
| /// |
| /// @param p the offsets of the pair of DIEs to consider. |
| /// |
| /// @param result out parameter set to the cached result of the |
| /// comparison of @p p if it has been found. |
| /// |
| /// @return true iff a cached result for the comparisonof @p has been |
| /// found and set into @p result. |
| static bool |
| get_cached_type_comparison_result(const reader& rdr, |
| const offset_pair_type& p, |
| comparison_result& result) |
| { |
| auto i = rdr.die_comparison_results_.find(p); |
| if (i != rdr.die_comparison_results_.end()) |
| { |
| result = i->second; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Get the cached result of the comparison of a pair of DIEs, if the |
| /// kind of DIEs ought to have its comparison results cached. |
| /// |
| /// @param rdr the context to consider. |
| /// |
| /// @param tag the tag of the pair of DIEs to consider. |
| /// |
| /// @param p the offsets of the pair of DIEs to consider. |
| /// |
| /// @param result out parameter set to the cached result of the |
| /// comparison of @p p if it has been found. |
| /// |
| /// @return true iff a cached result for the comparisonof @p has been |
| /// found and set into @p result. |
| static bool |
| maybe_get_cached_type_comparison_result(const reader& rdr, |
| int tag, |
| const offset_pair_type& p, |
| comparison_result& result) |
| { |
| if (type_comparison_result_to_be_cached(tag)) |
| { |
| // Types of this kind might have their comparison result cached |
| // when they are not canonicalized. So let's see if we have a |
| // cached comparison result. |
| if (get_cached_type_comparison_result(rdr, p, result)) |
| return true; |
| } |
| return false; |
| } |
| |
| /// Test if a given DIE is to be canonicalized. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die is to be canonicalized. |
| static bool |
| is_type_die_to_be_canonicalized(const Dwarf_Die *die) |
| { |
| bool result = false; |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| |
| if (!is_type_tag(tag)) |
| return false; |
| |
| switch (tag) |
| { |
| case DW_TAG_class_type: |
| case DW_TAG_structure_type: |
| case DW_TAG_union_type: |
| result = !die_is_declaration_only(die); |
| break; |
| |
| case DW_TAG_subroutine_type: |
| case DW_TAG_subprogram: |
| case DW_TAG_array_type: |
| result = true; |
| |
| default: |
| break; |
| } |
| |
| return result; |
| } |
| |
| /// Test if a DIE tag represents a declaration. |
| /// |
| /// @param tag the DWARF tag to consider. |
| /// |
| /// @return true iff @p tag is for a declaration. |
| static bool |
| is_decl_tag(unsigned tag) |
| { |
| switch (tag) |
| { |
| case DW_TAG_formal_parameter: |
| case DW_TAG_imported_declaration: |
| case DW_TAG_member: |
| case DW_TAG_unspecified_parameters: |
| case DW_TAG_subprogram: |
| case DW_TAG_variable: |
| case DW_TAG_namespace: |
| case DW_TAG_GNU_template_template_param: |
| case DW_TAG_GNU_template_parameter_pack: |
| case DW_TAG_GNU_formal_parameter_pack: |
| return true; |
| } |
| return false; |
| } |
| |
| /// Test if a DIE represents a type DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true if @p die represents a type, false otherwise. |
| static bool |
| die_is_type(const Dwarf_Die* die) |
| { |
| if (!die) |
| return false; |
| return is_type_tag(dwarf_tag(const_cast<Dwarf_Die*>(die))); |
| } |
| |
| /// Test if a DIE represents a declaration. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true if @p die represents a decl, false otherwise. |
| static bool |
| die_is_decl(const Dwarf_Die* die) |
| { |
| if (!die) |
| return false; |
| return is_decl_tag(dwarf_tag(const_cast<Dwarf_Die*>(die))); |
| } |
| |
| /// Test if a DIE represents a namespace. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true if @p die represents a namespace, false otherwise. |
| static bool |
| die_is_namespace(const Dwarf_Die* die) |
| { |
| if (!die) |
| return false; |
| return (dwarf_tag(const_cast<Dwarf_Die*>(die)) == DW_TAG_namespace); |
| } |
| |
| /// Test if a DIE has tag DW_TAG_unspecified_type. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true if @p die has tag DW_TAG_unspecified_type. |
| static bool |
| die_is_unspecified(Dwarf_Die* die) |
| { |
| if (!die) |
| return false; |
| return (dwarf_tag(die) == DW_TAG_unspecified_type); |
| } |
| |
| /// Test if a DIE represents a void type. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true if @p die represents a void type, false otherwise. |
| static bool |
| die_is_void_type(Dwarf_Die* die) |
| { |
| if (!die || dwarf_tag(die) != DW_TAG_base_type) |
| return false; |
| |
| string name = die_name(die); |
| if (name == "void") |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a DIE represents a pointer type. |
| /// |
| /// @param die the die to consider. |
| /// |
| /// @return true iff @p die represents a pointer type. |
| static bool |
| die_is_pointer_type(const Dwarf_Die* die) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag == DW_TAG_pointer_type) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a DIE is for a pointer, reference or qualified type to |
| /// anonymous class or struct. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p is for a pointer, reference or qualified type |
| /// to anonymous class or struct. |
| static bool |
| pointer_or_qual_die_of_anonymous_class_type(const Dwarf_Die* die) |
| { |
| if (!die_is_pointer_array_or_reference_type(die) |
| && !die_is_qualified_type(die)) |
| return false; |
| |
| Dwarf_Die underlying_type_die; |
| if (!die_die_attribute(die, DW_AT_type, underlying_type_die)) |
| return false; |
| |
| if (!die_is_class_type(&underlying_type_die)) |
| return false; |
| |
| string name = die_name(&underlying_type_die); |
| |
| return name.empty(); |
| } |
| |
| /// Test if a DIE represents a reference type. |
| /// |
| /// @param die the die to consider. |
| /// |
| /// @return true iff @p die represents a reference type. |
| static bool |
| die_is_reference_type(const Dwarf_Die* die) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag == DW_TAG_reference_type || tag == DW_TAG_rvalue_reference_type) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a DIE represents an array type. |
| /// |
| /// @param die the die to consider. |
| /// |
| /// @return true iff @p die represents an array type. |
| static bool |
| die_is_array_type(const Dwarf_Die* die) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag == DW_TAG_array_type) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a DIE represents a pointer, reference or array type. |
| /// |
| /// @param die the die to consider. |
| /// |
| /// @return true iff @p die represents a pointer or reference type. |
| static bool |
| die_is_pointer_array_or_reference_type(const Dwarf_Die* die) |
| {return (die_is_pointer_type(die) |
| || die_is_reference_type(die) |
| || die_is_array_type(die));} |
| |
| /// Test if a DIE represents a pointer or a reference type. |
| /// |
| /// @param die the die to consider. |
| /// |
| /// @return true iff @p die represents a pointer or reference type. |
| static bool |
| die_is_pointer_or_reference_type(const Dwarf_Die* die) |
| {return (die_is_pointer_type(die) || die_is_reference_type(die));} |
| |
| /// Test if a DIE represents a pointer, a reference or a typedef type. |
| /// |
| /// @param die the die to consider. |
| /// |
| /// @return true iff @p die represents a pointer, a reference or a |
| /// typedef type. |
| static bool |
| die_is_pointer_reference_or_typedef_type(const Dwarf_Die* die) |
| {return (die_is_pointer_array_or_reference_type(die) |
| || dwarf_tag(const_cast<Dwarf_Die*>(die)) == DW_TAG_typedef);} |
| |
| /// Test if a DIE represents a class type. |
| /// |
| /// @param die the die to consider. |
| /// |
| /// @return true iff @p die represents a class type. |
| static bool |
| die_is_class_type(const Dwarf_Die* die) |
| { |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| |
| if (tag == DW_TAG_class_type || tag == DW_TAG_structure_type) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a DIE is for a qualified type. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die is for a qualified type. |
| static bool |
| die_is_qualified_type(const Dwarf_Die* die) |
| { |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag == DW_TAG_const_type |
| || tag == DW_TAG_volatile_type |
| || tag == DW_TAG_restrict_type) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a DIE is for a function type. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die is for a function type. |
| static bool |
| die_is_function_type(const Dwarf_Die *die) |
| { |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag == DW_TAG_subprogram || tag == DW_TAG_subroutine_type) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a DIE for a function pointer or member function has an |
| /// DW_AT_object_pointer attribute. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param object_pointer out parameter. It's set to the DIE for the |
| /// object pointer iff the function returns true. |
| /// |
| /// @return true iff the DIE @p die has an object pointer. In that |
| /// case, the parameter @p object_pointer is set to the DIE of that |
| /// object pointer. |
| static bool |
| die_has_object_pointer(const Dwarf_Die* die, Dwarf_Die& object_pointer) |
| { |
| if (!die) |
| return false; |
| |
| if (die_die_attribute(die, DW_AT_object_pointer, object_pointer)) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a DIE has children DIEs. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p DIE has at least one child node. |
| static bool |
| die_has_children(const Dwarf_Die* die) |
| { |
| if (!die) |
| return false; |
| |
| Dwarf_Die child; |
| if (dwarf_child(const_cast<Dwarf_Die*>(die), &child) == 0) |
| return true; |
| |
| return false; |
| } |
| |
| /// Get the DIE representing the first parameter of the function |
| /// denoted by a given DIE. |
| /// |
| /// @param die the function DIE to consider. Note that if this |
| /// parameter is neither a DW_TAG_subprogram nor a |
| /// DW_TAG_subroutine_type, then the current process is aborted. |
| /// |
| /// @param first_parm_die output parameter. This is set to the DIE of |
| /// the first parameter of the function denoted by @p die. This |
| /// output parameter is set iff the function returns true. |
| /// |
| /// @return true iff the first parameter of the function denoted by @p |
| /// die is returned in output parameter @p first_parm_die. |
| static bool |
| fn_die_first_parameter_die(const Dwarf_Die* die, Dwarf_Die& first_parm_die) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| ABG_ASSERT(tag == DW_TAG_subroutine_type || tag == DW_TAG_subprogram); |
| |
| Dwarf_Die child; |
| if (dwarf_child(const_cast<Dwarf_Die*>(die), &child) == 0) |
| { |
| int child_tag = dwarf_tag(&child); |
| if (child_tag == DW_TAG_formal_parameter) |
| { |
| memcpy(&first_parm_die, &child, sizeof(Dwarf_Die)); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// Test if a member function denoted by a given DIE has a parameter |
| /// which is a "this pointer". |
| /// |
| /// Please note that if the member function denotes a static member |
| /// function or if the DIE does not denote a member function to begin |
| /// with, then the function will return false because no "this |
| /// pointer" will be found. |
| /// |
| /// @param rdr the current DWARF reader in use. |
| /// |
| /// @param die the DIE of the member function this function should |
| /// inspect. |
| /// |
| /// @param where_offset where in the DIE stream we logically are. |
| /// |
| /// @param class_die output parameter. This is set iff a "this |
| /// pointer" was found as the first parameters of the member function |
| /// denoted by @p die, and thus the function returns true If set, this |
| /// then points to the DIE of the class containing the member function |
| /// denoted by @p die. |
| /// |
| /// @param object_pointer_die output parameter. This is set to the |
| /// DIE of the function parameter that carries the "this pointe". |
| /// This is set iff this function return true. |
| /// |
| /// @return true iff the first parameter of the member function |
| /// denoted by @p die points to a "this pointer". |
| static bool |
| member_fn_die_has_this_pointer(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset, |
| Dwarf_Die& class_die, |
| Dwarf_Die& object_pointer_die) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag != DW_TAG_subprogram && tag != DW_TAG_subroutine_type) |
| return false; |
| |
| if (tag == DW_TAG_subprogram |
| && !die_is_at_class_scope(rdr, die, where_offset, class_die)) |
| return false; |
| |
| Dwarf_Die first_parm_die; |
| Dwarf_Die parm_type_die; |
| if (die_has_object_pointer(die, object_pointer_die)) |
| { |
| // This can be either a member function with a |
| // DW_AT_object_pointer attribute or a DW_TAG_subroutine_type |
| // with a DW_AT_object_pointer. In the later case, we are |
| // looking at a member function type. |
| memcpy(&first_parm_die, &object_pointer_die, sizeof(Dwarf_Die)); |
| if (!die_die_attribute(&first_parm_die, DW_AT_type, parm_type_die)) |
| return false; |
| die_peel_qual_ptr(&parm_type_die, parm_type_die); |
| die_peel_typedef(&parm_type_die, parm_type_die); |
| } |
| else if (fn_die_first_parameter_die(die, first_parm_die)) |
| { |
| memcpy(&object_pointer_die, &first_parm_die, sizeof(Dwarf_Die)); |
| bool is_artificial = false; |
| if (die_flag_attribute(&first_parm_die, DW_AT_artificial, is_artificial)) |
| { |
| if (die_die_attribute(&first_parm_die, DW_AT_type, parm_type_die)) |
| { |
| tag = dwarf_tag(&parm_type_die); |
| if (tag == DW_TAG_pointer_type) |
| { |
| die_peel_qual_ptr(&parm_type_die, parm_type_die); |
| die_peel_typedef(&parm_type_die, parm_type_die); |
| } |
| else |
| return false; |
| } |
| else |
| return false; |
| } |
| else |
| return false; |
| } |
| else |
| return false; |
| |
| tag = dwarf_tag(&parm_type_die); |
| if (tag == DW_TAG_class_type || tag == DW_TAG_structure_type) |
| { |
| memcpy(&class_die, &parm_type_die, sizeof(Dwarf_Die)); |
| return true; |
| } |
| return false; |
| } |
| |
| /// When given the object pointer DIE of a function type or member |
| /// function DIE, this function returns the "this" pointer that points |
| /// to the associated class. |
| /// |
| /// @param die the DIE of the object pointer of the function or member |
| /// function to consider. |
| /// |
| /// @param this_pointer_die out parameter. This is set to the DIE of |
| /// the "this" pointer iff the function returns true. |
| /// |
| /// @return true iff the function found the "this" pointer from the |
| /// object pointer DIE @p die. In that case, the parameter @p |
| /// this_pointer_die is set to the DIE of that "this" pointer. |
| static bool |
| die_this_pointer_from_object_pointer(Dwarf_Die* die, |
| Dwarf_Die& this_pointer_die) |
| { |
| ABG_ASSERT(die); |
| ABG_ASSERT(dwarf_tag(die) == DW_TAG_formal_parameter); |
| |
| if (die_die_attribute(die, DW_AT_type, this_pointer_die)) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a given "this" pointer that points to a particular class |
| /// type is for a const class or not. If it's for a const class, then |
| /// it means the function type or the member function associated to |
| /// that "this" pointer is const. |
| /// |
| /// @param dye the DIE of the "this" pointer to consider. |
| /// |
| /// @return true iff @p die points to a const class type. |
| static bool |
| die_this_pointer_is_const(Dwarf_Die* dye) |
| { |
| ABG_ASSERT(dye); |
| |
| Dwarf_Die die; |
| memcpy(&die, dye, sizeof(Dwarf_Die)); |
| if (dwarf_tag(&die) == DW_TAG_const_type) |
| ABG_ASSERT(die_peel_qualified(&die, die)); |
| |
| if (dwarf_tag(&die) == DW_TAG_pointer_type) |
| { |
| Dwarf_Die pointed_to_type_die; |
| if (die_die_attribute(&die, DW_AT_type, pointed_to_type_die)) |
| if (dwarf_tag(&pointed_to_type_die) == DW_TAG_const_type) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Test if an object pointer (referred-to via a DW_AT_object_pointer |
| /// attribute) points to a const implicit class and so is for a const |
| /// method or or a const member function type. |
| /// |
| /// @param die the DIE of the object pointer to consider. |
| /// |
| /// @return true iff the object pointer represented by @p die is for a |
| /// a const method or const member function type. |
| static bool |
| die_object_pointer_is_for_const_method(Dwarf_Die* die) |
| { |
| ABG_ASSERT(die); |
| ABG_ASSERT(dwarf_tag(die) == DW_TAG_formal_parameter); |
| |
| Dwarf_Die this_pointer_die; |
| if (die_this_pointer_from_object_pointer(die, this_pointer_die)) |
| if (die_this_pointer_is_const(&this_pointer_die)) |
| return true; |
| |
| return false; |
| } |
| |
| /// Test if a DIE represents an entity that is at class scope. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where we are logically at in the DIE stream. |
| /// |
| /// @param class_scope_die out parameter. Set to the DIE of the |
| /// containing class iff @p die happens to be at class scope; that is, |
| /// iff the function returns true. |
| /// |
| /// @return true iff @p die is at class scope. In that case, @p |
| /// class_scope_die is set to the DIE of the class that contains @p |
| /// die. |
| static bool |
| die_is_at_class_scope(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset, |
| Dwarf_Die& class_scope_die) |
| { |
| if (!get_scope_die(rdr, die, where_offset, class_scope_die)) |
| return false; |
| |
| int tag = dwarf_tag(&class_scope_die); |
| |
| return (tag == DW_TAG_structure_type |
| || tag == DW_TAG_class_type |
| || tag == DW_TAG_union_type); |
| } |
| |
| /// Return the leaf object under a pointer, reference or qualified |
| /// type DIE. |
| /// |
| /// @param die the DIE of the type to consider. |
| /// |
| /// @param peeled_die out parameter. Set to the DIE of the leaf |
| /// object iff the function actually peeled anything. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| die_peel_qual_ptr(Dwarf_Die *die, Dwarf_Die& peeled_die) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(die); |
| |
| if (tag == DW_TAG_const_type |
| || tag == DW_TAG_volatile_type |
| || tag == DW_TAG_restrict_type |
| || tag == DW_TAG_pointer_type |
| || tag == DW_TAG_reference_type |
| || tag == DW_TAG_rvalue_reference_type) |
| { |
| if (!die_die_attribute(die, DW_AT_type, peeled_die)) |
| return false; |
| } |
| else |
| return false; |
| |
| memcpy(&peeled_die, die, sizeof(peeled_die)); |
| |
| while (tag == DW_TAG_const_type |
| || tag == DW_TAG_volatile_type |
| || tag == DW_TAG_restrict_type |
| || tag == DW_TAG_pointer_type |
| || tag == DW_TAG_reference_type |
| || tag == DW_TAG_rvalue_reference_type) |
| { |
| if (!die_die_attribute(&peeled_die, DW_AT_type, peeled_die)) |
| break; |
| tag = dwarf_tag(&peeled_die); |
| } |
| |
| return true; |
| } |
| |
| /// Return the leaf object under a qualified type DIE. |
| /// |
| /// @param die the DIE of the type to consider. |
| /// |
| /// @param peeled_die out parameter. Set to the DIE of the leaf |
| /// object iff the function actually peeled anything. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| die_peel_qualified(Dwarf_Die *die, Dwarf_Die& peeled_die) |
| { |
| if (!die) |
| return false; |
| |
| memcpy(&peeled_die, die, sizeof(peeled_die)); |
| |
| int tag = dwarf_tag(&peeled_die); |
| |
| bool result = false; |
| while (tag == DW_TAG_const_type |
| || tag == DW_TAG_volatile_type |
| || tag == DW_TAG_restrict_type) |
| { |
| if (!die_die_attribute(&peeled_die, DW_AT_type, peeled_die)) |
| break; |
| tag = dwarf_tag(&peeled_die); |
| result = true; |
| } |
| |
| return result; |
| } |
| |
| /// Return the leaf object under a typedef type DIE. |
| /// |
| /// @param die the DIE of the type to consider. |
| /// |
| /// @param peeled_die out parameter. Set to the DIE of the leaf |
| /// object iff the function actually peeled anything. |
| /// |
| /// @return true upon successful completion. |
| static bool |
| die_peel_typedef(Dwarf_Die *die, Dwarf_Die& peeled_die) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(die); |
| |
| memcpy(&peeled_die, die, sizeof(peeled_die)); |
| |
| if (tag == DW_TAG_typedef) |
| { |
| if (!die_die_attribute(die, DW_AT_type, peeled_die)) |
| return false; |
| } |
| else |
| return false; |
| |
| while (tag == DW_TAG_typedef) |
| { |
| if (!die_die_attribute(&peeled_die, DW_AT_type, peeled_die)) |
| break; |
| tag = dwarf_tag(&peeled_die); |
| } |
| |
| return true; |
| |
| } |
| |
| /// Return the leaf DIE under a pointer, a reference or a typedef DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param peeled_die the resulting peeled (or leaf) DIE. This is set |
| /// iff the function returned true. |
| /// |
| /// @return true iff the function could peel @p die. |
| static bool |
| die_peel_pointer_and_typedef(const Dwarf_Die *die, Dwarf_Die& peeled_die) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| |
| if (tag == DW_TAG_pointer_type |
| || tag == DW_TAG_reference_type |
| || tag == DW_TAG_rvalue_reference_type |
| || tag == DW_TAG_typedef) |
| { |
| if (!die_die_attribute(die, DW_AT_type, peeled_die)) |
| return false; |
| } |
| else |
| return false; |
| |
| while (tag == DW_TAG_pointer_type |
| || tag == DW_TAG_reference_type |
| || tag == DW_TAG_rvalue_reference_type |
| || tag == DW_TAG_typedef) |
| { |
| if (!die_die_attribute(&peeled_die, DW_AT_type, peeled_die)) |
| break; |
| tag = dwarf_tag(&peeled_die); |
| } |
| return true; |
| } |
| |
| /// Test if a DIE for a function type represents a method type. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where we logically are in the stream of DIEs. |
| /// |
| /// @param object_pointer_die out parameter. This is set by the |
| /// function to the DIE that refers to the formal function parameter |
| /// which holds the implicit "this" pointer of the method. That die |
| /// is called the object pointer DIE. This is set iff the member |
| /// function is a non-static member function and if the function |
| /// returns true. In other words, this is only set if the is_static |
| /// out parameter is set to false and the function returns true. |
| /// |
| /// @param class_die out parameter. This is set by the function to |
| /// the DIE that represents the class of the method type. This is set |
| /// iff the function returns true. |
| /// |
| /// @param is_static out parameter. This is set to true by the |
| /// function if @p die is a static method or a the type of a static |
| /// method. This is set iff the function returns true. |
| /// |
| /// @return true iff @p die is a DIE for a method type. |
| static bool |
| die_function_type_is_method_type(const reader& rdr, |
| const Dwarf_Die *die, |
| size_t where_offset, |
| Dwarf_Die& object_pointer_die, |
| Dwarf_Die& class_die, |
| bool& is_static) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| ABG_ASSERT(tag == DW_TAG_subroutine_type || tag == DW_TAG_subprogram); |
| |
| if (member_fn_die_has_this_pointer(rdr, die, where_offset, class_die, object_pointer_die)) |
| { |
| is_static = false; |
| return true; |
| } |
| else if (die_is_at_class_scope(rdr, die, where_offset, class_die)) |
| { |
| is_static = true; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| enum virtuality |
| { |
| VIRTUALITY_NOT_VIRTUAL, |
| VIRTUALITY_VIRTUAL, |
| VIRTUALITY_PURE_VIRTUAL |
| }; |
| |
| /// Get the virtual-ness of a given DIE, that is, the value of the |
| /// DW_AT_virtuality attribute. |
| /// |
| /// @param die the DIE to read from. |
| /// |
| /// @param virt the resulting virtuality attribute. This is set iff |
| /// the function returns true. |
| /// |
| /// @return true if the virtual-ness could be determined. |
| static bool |
| die_virtuality(const Dwarf_Die* die, virtuality& virt) |
| { |
| if (!die) |
| return false; |
| |
| uint64_t v = 0; |
| die_unsigned_constant_attribute(die, DW_AT_virtuality, v); |
| |
| if (v == DW_VIRTUALITY_virtual) |
| virt = VIRTUALITY_VIRTUAL; |
| else if (v == DW_VIRTUALITY_pure_virtual) |
| virt = VIRTUALITY_PURE_VIRTUAL; |
| else |
| virt = VIRTUALITY_NOT_VIRTUAL; |
| |
| return true; |
| } |
| |
| /// Test whether the DIE represent either a virtual base or function. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return bool if the DIE represents a virtual base or function, |
| /// false othersise. |
| static bool |
| die_is_virtual(const Dwarf_Die* die) |
| { |
| virtuality v; |
| if (!die_virtuality(die, v)) |
| return false; |
| |
| return v == VIRTUALITY_PURE_VIRTUAL || v == VIRTUALITY_VIRTUAL; |
| } |
| |
| /// Test if the DIE represents an entity that was declared inlined. |
| /// |
| /// @param die the DIE to test for. |
| /// |
| /// @return true if the DIE represents an entity that was declared |
| /// inlined. |
| static bool |
| die_is_declared_inline(Dwarf_Die* die) |
| { |
| uint64_t inline_value = 0; |
| if (!die_unsigned_constant_attribute(die, DW_AT_inline, inline_value)) |
| return false; |
| return (inline_value == DW_INL_declared_inlined |
| || inline_value == DW_INL_declared_not_inlined); |
| } |
| |
| /// Compare two DWARF strings using the most accurate (and slowest) |
| /// method possible. |
| /// |
| /// @param l the DIE that carries the first string to consider, as an |
| /// attribute value. |
| /// |
| /// @param attr_name the name of the attribute which value is the |
| /// string to compare. |
| /// |
| /// @return true iff the string carried by @p l equals the one carried |
| /// by @p r. |
| static bool |
| slowly_compare_strings(const Dwarf_Die *l, |
| const Dwarf_Die *r, |
| unsigned attr_name) |
| { |
| const char *l_str = die_char_str_attribute(l, attr_name), |
| *r_str = die_char_str_attribute(r, attr_name); |
| if (!l_str && !r_str) |
| return true; |
| return l_str && r_str && !strcmp(l_str, r_str); |
| } |
| |
| /// This function is a fast routine (optimization) to compare the |
| /// values of two string attributes of two DIEs. |
| /// |
| /// @param l the first DIE to consider. |
| /// |
| /// @param r the second DIE to consider. |
| /// |
| /// @param attr_name the name of the attribute to compare, on the two |
| /// DIEs above. |
| /// |
| /// @param result out parameter. This is set to the result of the |
| /// comparison. If the value of attribute @p attr_name on DIE @p l |
| /// equals the value of attribute @p attr_name on DIE @p r, then the |
| /// the argument of this parameter is set to true. Otherwise, it's |
| /// set to false. Note that the argument of this parameter is set iff |
| /// the function returned true. |
| /// |
| /// @return true iff the comparison could be performed. There are |
| /// cases in which the comparison cannot be performed. For instance, |
| /// if one of the DIEs does not have the attribute @p attr_name. In |
| /// any case, if this function returns true, then the parameter @p |
| /// result is set to the result of the comparison. |
| static bool |
| compare_dies_string_attribute_value(const Dwarf_Die *l, const Dwarf_Die *r, |
| unsigned attr_name, |
| bool &result) |
| { |
| Dwarf_Attribute l_attr, r_attr; |
| if (!dwarf_attr_integrate(const_cast<Dwarf_Die*>(l), attr_name, &l_attr) |
| || !dwarf_attr_integrate(const_cast<Dwarf_Die*>(r), attr_name, &r_attr)) |
| return false; |
| |
| ABG_ASSERT(l_attr.form == DW_FORM_strp |
| || l_attr.form == DW_FORM_string |
| || l_attr.form == DW_FORM_GNU_strp_alt |
| || form_is_DW_FORM_strx(l_attr.form) |
| || form_is_DW_FORM_line_strp(l_attr.form)); |
| |
| ABG_ASSERT(r_attr.form == DW_FORM_strp |
| || r_attr.form == DW_FORM_string |
| || r_attr.form == DW_FORM_GNU_strp_alt |
| || form_is_DW_FORM_strx(r_attr.form) |
| || form_is_DW_FORM_line_strp(r_attr.form)); |
| |
| if ((l_attr.form == DW_FORM_strp |
| && r_attr.form == DW_FORM_strp) |
| || (l_attr.form == DW_FORM_GNU_strp_alt |
| && r_attr.form == DW_FORM_GNU_strp_alt) |
| || (form_is_DW_FORM_strx(l_attr.form) |
| && form_is_DW_FORM_strx(r_attr.form)) |
| || (form_is_DW_FORM_line_strp(l_attr.form) |
| && form_is_DW_FORM_line_strp(r_attr.form))) |
| { |
| // So these string attributes are actually pointers into a |
| // string table. The string table is most likely de-duplicated |
| // so comparing the *values* of the pointers should be enough. |
| // |
| // This is the fast path. |
| if (l_attr.valp == r_attr.valp) |
| { |
| #if WITH_DEBUG_TYPE_CANONICALIZATION |
| ABG_ASSERT(slowly_compare_strings(l, r, attr_name)); |
| #endif |
| result = true; |
| return true; |
| } |
| } |
| |
| // If we reached this point it means we couldn't use the fast path |
| // because the string atttributes are strings that are "inline" in |
| // the debug info section. Let's just compare them the slow and |
| // obvious way. |
| result = slowly_compare_strings(l, r, attr_name); |
| return true; |
| } |
| |
| /// Compare the file path of the compilation units (aka CUs) |
| /// associated to two DIEs. |
| /// |
| /// If the DIEs are for pointers or typedefs, this function also |
| /// compares the file paths of the CUs of the leaf DIEs (underlying |
| /// DIEs of the pointer or the typedef). |
| /// |
| /// @param l the first type DIE to consider. |
| /// |
| /// @param r the second type DIE to consider. |
| /// |
| /// @return true iff the file paths of the DIEs of the two types are |
| /// equal. |
| static bool |
| compare_dies_cu_decl_file(const Dwarf_Die* l, const Dwarf_Die *r, bool &result) |
| { |
| Dwarf_Die l_cu, r_cu; |
| if (!dwarf_diecu(const_cast<Dwarf_Die*>(l), &l_cu, 0, 0) |
| ||!dwarf_diecu(const_cast<Dwarf_Die*>(r), &r_cu, 0, 0)) |
| return false; |
| |
| bool compared = |
| compare_dies_string_attribute_value(&l_cu, &r_cu, |
| DW_AT_name, |
| result); |
| if (compared && result) |
| { |
| Dwarf_Die peeled_l, peeled_r; |
| if (die_is_pointer_reference_or_typedef_type(l) |
| && die_is_pointer_reference_or_typedef_type(r) |
| && die_peel_pointer_and_typedef(l, peeled_l) |
| && die_peel_pointer_and_typedef(r, peeled_r)) |
| { |
| if (!dwarf_diecu(&peeled_l, &l_cu, 0, 0) |
| ||!dwarf_diecu(&peeled_r, &r_cu, 0, 0)) |
| return false; |
| compared = |
| compare_dies_string_attribute_value(&l_cu, &r_cu, |
| DW_AT_name, |
| result); |
| } |
| } |
| |
| return compared; |
| } |
| |
| // ----------------------------------- |
| // <location expression evaluation> |
| // ----------------------------------- |
| |
| /// Get the value of a given DIE attribute, knowing that it must be a |
| /// location expression. |
| /// |
| /// @param die the DIE to read the attribute from. |
| /// |
| /// @param attr_name the name of the attribute to read the value for. |
| /// |
| /// @param expr the pointer to allocate and fill with the resulting |
| /// array of operators + operands forming a dwarf expression. This is |
| /// set iff the function returns true. |
| /// |
| /// @param expr_len the length of the resulting dwarf expression. |
| /// This is set iff the function returns true. |
| /// |
| /// @return true if the attribute exists and has a non-empty dwarf expression |
| /// as value. In that case the expr and expr_len arguments are set to the |
| /// resulting dwarf expression. |
| static bool |
| die_location_expr(const Dwarf_Die* die, |
| unsigned attr_name, |
| Dwarf_Op** expr, |
| size_t* expr_len) |
| { |
| if (!die) |
| return false; |
| |
| Dwarf_Attribute attr; |
| if (!dwarf_attr_integrate(const_cast<Dwarf_Die*>(die), attr_name, &attr)) |
| return false; |
| |
| size_t len = 0; |
| bool result = (dwarf_getlocation(&attr, expr, &len) == 0); |
| |
| // Ignore location expressions where reading them succeeded but |
| // their length is 0. |
| result &= len > 0; |
| |
| if (result) |
| *expr_len = len; |
| |
| return result; |
| } |
| |
| /// If the current operation in the dwarf expression represents a push |
| /// of a constant value onto the dwarf expr virtual machine (aka |
| /// DEVM), perform the operation and update the DEVM. |
| /// |
| /// If the result of the operation is a constant, update the DEVM |
| /// accumulator with its value. Otherwise, the DEVM accumulator is |
| /// left with its previous value. |
| /// |
| /// @param ops the array of the dwarf expression operations to consider. |
| /// |
| /// @param ops_len the lengths of @p ops array above. |
| /// |
| /// @param index the index of the operation to interpret, in @p ops. |
| /// |
| /// @param next_index the index of the operation to interpret at the |
| /// next step, after this function completed and returned. This is |
| /// set an output parameter that is set iff the function returns true. |
| /// |
| /// @param ctxt the DEVM evaluation context. |
| /// |
| /// @return true if the current operation actually pushes a constant |
| /// value onto the DEVM stack, false otherwise. |
| static bool |
| op_pushes_constant_value(Dwarf_Op* ops, |
| size_t ops_len, |
| size_t index, |
| size_t& next_index, |
| dwarf_expr_eval_context& ctxt) |
| { |
| ABG_ASSERT(index < ops_len); |
| |
| Dwarf_Op& op = ops[index]; |
| int64_t value = 0; |
| |
| switch (op.atom) |
| { |
| case DW_OP_addr: |
| value = ops[index].number; |
| break; |
| |
| case DW_OP_const1u: |
| case DW_OP_const1s: |
| case DW_OP_const2u: |
| case DW_OP_const2s: |
| case DW_OP_const4u: |
| case DW_OP_const4s: |
| case DW_OP_const8u: |
| case DW_OP_const8s: |
| case DW_OP_constu: |
| case DW_OP_consts: |
| value = ops[index].number; |
| break; |
| |
| case DW_OP_lit0: |
| value = 0; |
| break; |
| case DW_OP_lit1: |
| value = 1; |
| break; |
| case DW_OP_lit2: |
| value = 2; |
| break; |
| case DW_OP_lit3: |
| value = 3; |
| break; |
| case DW_OP_lit4: |
| value = 4; |
| break; |
| case DW_OP_lit5: |
| value = 5; |
| break; |
| case DW_OP_lit6: |
| value = 6; |
| break; |
| case DW_OP_lit7: |
| value = 7; |
| break; |
| case DW_OP_lit8: |
| value = 8; |
| break; |
| case DW_OP_lit9: |
| value = 9; |
| break; |
| case DW_OP_lit10: |
| value = 10; |
| break; |
| case DW_OP_lit11: |
| value = 11; |
| break; |
| case DW_OP_lit12: |
| value = 12; |
| break; |
| case DW_OP_lit13: |
| value = 13; |
| break; |
| case DW_OP_lit14: |
| value = 14; |
| break; |
| case DW_OP_lit15: |
| value = 15; |
| break; |
| case DW_OP_lit16: |
| value = 16; |
| break; |
| case DW_OP_lit17: |
| value = 17; |
| break; |
| case DW_OP_lit18: |
| value = 18; |
| break; |
| case DW_OP_lit19: |
| value = 19; |
| break; |
| case DW_OP_lit20: |
| value = 20; |
| break; |
| case DW_OP_lit21: |
| value = 21; |
| break; |
| case DW_OP_lit22: |
| value = 22; |
| break; |
| case DW_OP_lit23: |
| value = 23; |
| break; |
| case DW_OP_lit24: |
| value = 24; |
| break; |
| case DW_OP_lit25: |
| value = 25; |
| break; |
| case DW_OP_lit26: |
| value = 26; |
| break; |
| case DW_OP_lit27: |
| value = 27; |
| break; |
| case DW_OP_lit28: |
| value = 28; |
| break; |
| case DW_OP_lit29: |
| value = 29; |
| break; |
| case DW_OP_lit30: |
| value = 30; |
| break; |
| case DW_OP_lit31: |
| value = 31; |
| break; |
| |
| default: |
| return false; |
| } |
| |
| expr_result r(value); |
| ctxt.push(r); |
| ctxt.accum = r; |
| next_index = index + 1; |
| |
| return true; |
| } |
| |
| /// If the current operation in the dwarf expression represents a push |
| /// of a non-constant value onto the dwarf expr virtual machine (aka |
| /// DEVM), perform the operation and update the DEVM. A non-constant |
| /// is namely a quantity for which we need inferior (a running program |
| /// image) state to know the exact value. |
| /// |
| /// Upon successful completion, as the result of the operation is a |
| /// non-constant the DEVM accumulator value is left to its state as of |
| /// before the invocation of this function. |
| /// |
| /// @param ops the array of the dwarf expression operations to consider. |
| /// |
| /// @param ops_len the lengths of @p ops array above. |
| /// |
| /// @param index the index of the operation to interpret, in @p ops. |
| /// |
| /// @param next_index the index of the operation to interpret at the |
| /// next step, after this function completed and returned. This is |
| /// set an output parameter that is set iff the function returns true. |
| /// |
| /// @param ctxt the DEVM evaluation context. |
| /// |
| /// @return true if the current operation actually pushes a |
| /// non-constant value onto the DEVM stack, false otherwise. |
| static bool |
| op_pushes_non_constant_value(Dwarf_Op* ops, |
| size_t ops_len, |
| size_t index, |
| size_t& next_index, |
| dwarf_expr_eval_context& ctxt) |
| { |
| ABG_ASSERT(index < ops_len); |
| Dwarf_Op& op = ops[index]; |
| |
| switch (op.atom) |
| { |
| case DW_OP_reg0: |
| case DW_OP_reg1: |
| case DW_OP_reg2: |
| case DW_OP_reg3: |
| case DW_OP_reg4: |
| case DW_OP_reg5: |
| case DW_OP_reg6: |
| case DW_OP_reg7: |
| case DW_OP_reg8: |
| case DW_OP_reg9: |
| case DW_OP_reg10: |
| case DW_OP_reg11: |
| case DW_OP_reg12: |
| case DW_OP_reg13: |
| case DW_OP_reg14: |
| case DW_OP_reg15: |
| case DW_OP_reg16: |
| case DW_OP_reg17: |
| case DW_OP_reg18: |
| case DW_OP_reg19: |
| case DW_OP_reg20: |
| case DW_OP_reg21: |
| case DW_OP_reg22: |
| case DW_OP_reg23: |
| case DW_OP_reg24: |
| case DW_OP_reg25: |
| case DW_OP_reg26: |
| case DW_OP_reg27: |
| case DW_OP_reg28: |
| case DW_OP_reg29: |
| case DW_OP_reg30: |
| case DW_OP_reg31: |
| next_index = index + 1; |
| break; |
| |
| case DW_OP_breg0: |
| case DW_OP_breg1: |
| case DW_OP_breg2: |
| case DW_OP_breg3: |
| case DW_OP_breg4: |
| case DW_OP_breg5: |
| case DW_OP_breg6: |
| case DW_OP_breg7: |
| case DW_OP_breg8: |
| case DW_OP_breg9: |
| case DW_OP_breg10: |
| case DW_OP_breg11: |
| case DW_OP_breg12: |
| case DW_OP_breg13: |
| case DW_OP_breg14: |
| case DW_OP_breg15: |
| case DW_OP_breg16: |
| case DW_OP_breg17: |
| case DW_OP_breg18: |
| case DW_OP_breg19: |
| case DW_OP_breg20: |
| case DW_OP_breg21: |
| case DW_OP_breg22: |
| case DW_OP_breg23: |
| case DW_OP_breg24: |
| case DW_OP_breg25: |
| case DW_OP_breg26: |
| case DW_OP_breg27: |
| case DW_OP_breg28: |
| case DW_OP_breg29: |
| case DW_OP_breg30: |
| case DW_OP_breg31: |
| next_index = index + 1; |
| break; |
| |
| case DW_OP_regx: |
| next_index = index + 2; |
| break; |
| |
| case DW_OP_fbreg: |
| next_index = index + 1; |
| break; |
| |
| case DW_OP_bregx: |
| next_index = index + 1; |
| break; |
| |
| case DW_OP_GNU_variable_value: |
| next_index = index + 1; |
| break; |
| |
| default: |
| return false; |
| } |
| |
| expr_result r(false); |
| ctxt.push(r); |
| |
| return true; |
| } |
| |
| /// If the current operation in the dwarf expression represents a |
| /// manipulation of the stack of the DWARF Expression Virtual Machine |
| /// (aka DEVM), this function performs the operation and updates the |
| /// state of the DEVM. If the result of the operation represents a |
| /// constant value, then the accumulator of the DEVM is set to that |
| /// result's value, Otherwise, the DEVM accumulator is left with its |
| /// previous value. |
| /// |
| /// @param expr the array of the dwarf expression operations to consider. |
| /// |
| /// @param expr_len the lengths of @p ops array above. |
| /// |
| /// @param index the index of the operation to interpret, in @p ops. |
| /// |
| /// @param next_index the index of the operation to interpret at the |
| /// next step, after this function completed and returned. This is |
| /// set an output parameter that is set iff the function returns true. |
| /// |
| /// @param ctxt the DEVM evaluation context. |
| /// |
| /// @return true if the current operation actually manipulates the |
| /// DEVM stack, false otherwise. |
| static bool |
| op_manipulates_stack(Dwarf_Op* expr, |
| size_t expr_len, |
| size_t index, |
| size_t& next_index, |
| dwarf_expr_eval_context& ctxt) |
| { |
| Dwarf_Op& op = expr[index]; |
| expr_result v; |
| |
| switch (op.atom) |
| { |
| case DW_OP_dup: |
| v = ctxt.stack.front(); |
| ctxt.push(v); |
| break; |
| |
| case DW_OP_drop: |
| v = ctxt.stack.front(); |
| ctxt.pop(); |
| break; |
| |
| case DW_OP_over: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| v = ctxt.stack[1]; |
| ctxt.push(v); |
| break; |
| |
| case DW_OP_pick: |
| ABG_ASSERT(index + 1 < expr_len); |
| v = op.number; |
| ctxt.push(v); |
| break; |
| |
| case DW_OP_swap: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| v = ctxt.stack[1]; |
| ctxt.stack.erase(ctxt.stack.begin() + 1); |
| ctxt.push(v); |
| break; |
| |
| case DW_OP_rot: |
| ABG_ASSERT(ctxt.stack.size() > 2); |
| v = ctxt.stack[2]; |
| ctxt.stack.erase(ctxt.stack.begin() + 2); |
| ctxt.push(v); |
| break; |
| |
| case DW_OP_deref: |
| case DW_OP_deref_size: |
| ABG_ASSERT(ctxt.stack.size() > 0); |
| ctxt.pop(); |
| v.is_const(false); |
| ctxt.push(v); |
| break; |
| |
| case DW_OP_xderef: |
| case DW_OP_xderef_size: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| ctxt.pop(); |
| ctxt.pop(); |
| v.is_const(false); |
| ctxt.push(v); |
| break; |
| |
| case DW_OP_push_object_address: |
| v.is_const(false); |
| ctxt.push(v); |
| break; |
| |
| case DW_OP_form_tls_address: |
| case DW_OP_GNU_push_tls_address: |
| ABG_ASSERT(ctxt.stack.size() > 0); |
| v = ctxt.pop(); |
| if (op.atom == DW_OP_form_tls_address) |
| v.is_const(false); |
| ctxt.push(v); |
| break; |
| |
| case DW_OP_call_frame_cfa: |
| v.is_const(false); |
| ctxt.push(v); |
| break; |
| |
| default: |
| return false; |
| } |
| |
| if (v.is_const()) |
| ctxt.accum = v; |
| |
| if (op.atom == DW_OP_form_tls_address |
| || op.atom == DW_OP_GNU_push_tls_address) |
| ctxt.set_tls_address(true); |
| else |
| ctxt.set_tls_address(false); |
| |
| next_index = index + 1; |
| |
| return true; |
| } |
| |
| /// If the current operation in the dwarf expression represents a push |
| /// of an arithmetic or logic operation onto the dwarf expr virtual |
| /// machine (aka DEVM), perform the operation and update the DEVM. |
| /// |
| /// If the result of the operation is a constant, update the DEVM |
| /// accumulator with its value. Otherwise, the DEVM accumulator is |
| /// left with its previous value. |
| /// |
| /// @param expr the array of the dwarf expression operations to consider. |
| /// |
| /// @param expr_len the lengths of @p expr array above. |
| /// |
| /// @param index the index of the operation to interpret, in @p expr. |
| /// |
| /// @param next_index the index of the operation to interpret at the |
| /// next step, after this function completed and returned. This is |
| /// set an output parameter that is set iff the function returns true. |
| /// |
| /// @param ctxt the DEVM evaluation context. |
| /// |
| /// @return true if the current operation actually represent an |
| /// arithmetic or logic operation. |
| static bool |
| op_is_arith_logic(Dwarf_Op* expr, |
| size_t expr_len, |
| size_t index, |
| size_t& next_index, |
| dwarf_expr_eval_context& ctxt) |
| { |
| ABG_ASSERT(index < expr_len); |
| |
| Dwarf_Op& op = expr[index]; |
| expr_result val1, val2; |
| bool result = false; |
| |
| switch (op.atom) |
| { |
| case DW_OP_abs: |
| ABG_ASSERT(ctxt.stack.size() > 0); |
| val1 = ctxt.pop(); |
| val1 = val1.abs(); |
| ctxt.push(val1); |
| result = true; |
| break; |
| |
| case DW_OP_and: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| ctxt.push(val1 & val2); |
| break; |
| |
| case DW_OP_div: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| if (!val1.is_const()) |
| val1 = 1; |
| ctxt.push(val2 / val1); |
| result = true; |
| break; |
| |
| case DW_OP_minus: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| ctxt.push(val2 - val1); |
| result = true; |
| break; |
| |
| case DW_OP_mod: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| ctxt.push(val2 % val1); |
| result = true; |
| break; |
| |
| case DW_OP_mul: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| ctxt.push(val2 * val1); |
| result = true; |
| break; |
| |
| case DW_OP_neg: |
| ABG_ASSERT(ctxt.stack.size() > 0); |
| val1 = ctxt.pop(); |
| ctxt.push(-val1); |
| result = true; |
| break; |
| |
| case DW_OP_not: |
| ABG_ASSERT(ctxt.stack.size() > 0); |
| val1 = ctxt.pop(); |
| ctxt.push(~val1); |
| result = true; |
| break; |
| |
| case DW_OP_or: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| ctxt.push(val1 | val2); |
| result = true; |
| break; |
| |
| case DW_OP_plus: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| ctxt.push(val2 + val1); |
| result = true; |
| break; |
| |
| case DW_OP_plus_uconst: |
| ABG_ASSERT(ctxt.stack.size() > 0); |
| val1 = ctxt.pop(); |
| val1 += op.number; |
| ctxt.push(val1); |
| result = true; |
| break; |
| |
| case DW_OP_shl: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| ctxt.push(val2 << val1); |
| result = true; |
| break; |
| |
| case DW_OP_shr: |
| case DW_OP_shra: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| ctxt.push(val2 >> val1); |
| result = true; |
| break; |
| |
| case DW_OP_xor: |
| ABG_ASSERT(ctxt.stack.size() > 1); |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| ctxt.push(val2 ^ val1); |
| result = true; |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (result == true) |
| { |
| if (ctxt.stack.front().is_const()) |
| ctxt.accum = ctxt.stack.front(); |
| |
| next_index = index + 1; |
| } |
| return result;; |
| } |
| |
| /// If the current operation in the dwarf expression represents a push |
| /// of a control flow operation onto the dwarf expr virtual machine |
| /// (aka DEVM), perform the operation and update the DEVM. |
| /// |
| /// If the result of the operation is a constant, update the DEVM |
| /// accumulator with its value. Otherwise, the DEVM accumulator is |
| /// left with its previous value. |
| /// |
| /// @param expr the array of the dwarf expression operations to consider. |
| /// |
| /// @param expr_len the lengths of @p expr array above. |
| /// |
| /// @param index the index of the operation to interpret, in @p expr. |
| /// |
| /// @param next_index the index of the operation to interpret at the |
| /// next step, after this function completed and returned. This is |
| /// set an output parameter that is set iff the function returns true. |
| /// |
| /// @param ctxt the DEVM evaluation context. |
| /// |
| /// @return true if the current operation actually represents a |
| /// control flow operation, false otherwise. |
| static bool |
| op_is_control_flow(Dwarf_Op* expr, |
| size_t expr_len, |
| size_t index, |
| size_t& next_index, |
| dwarf_expr_eval_context& ctxt) |
| { |
| ABG_ASSERT(index < expr_len); |
| |
| Dwarf_Op& op = expr[index]; |
| expr_result val1, val2; |
| |
| switch (op.atom) |
| { |
| case DW_OP_eq: |
| case DW_OP_ge: |
| case DW_OP_gt: |
| case DW_OP_le: |
| case DW_OP_lt: |
| case DW_OP_ne: |
| { |
| bool value = true; |
| val1 = ctxt.pop(); |
| val2 = ctxt.pop(); |
| if (op.atom == DW_OP_eq) |
| value = val2 == val1; |
| else if (op.atom == DW_OP_ge) |
| value = val2 >= val1; |
| else if (op.atom == DW_OP_gt) |
| value = val2 > val1; |
| else if (op.atom == DW_OP_le) |
| value = val2 <= val1; |
| else if (op.atom == DW_OP_lt) |
| value = val2 < val1; |
| else if (op.atom == DW_OP_ne) |
| value = val2 != val1; |
| |
| val1 = value ? 1 : 0; |
| ctxt.push(val1); |
| } |
| break; |
| |
| case DW_OP_skip: |
| if (op.number > 0) |
| index += op.number - 1; |
| break; |
| |
| case DW_OP_bra: |
| val1 = ctxt.pop(); |
| if (val1.const_value() != 0) |
| index += val1.const_value() - 1; |
| break; |
| |
| case DW_OP_call2: |
| case DW_OP_call4: |
| case DW_OP_call_ref: |
| case DW_OP_nop: |
| break; |
| |
| default: |
| return false; |
| } |
| |
| if (ctxt.stack.front().is_const()) |
| ctxt.accum = ctxt.stack.front(); |
| |
| next_index = index + 1; |
| return true; |
| } |
| |
| /// This function quickly evaluates a DWARF expression that is a |
| /// constant. |
| /// |
| /// This is a "fast path" function that quickly evaluates a DWARF |
| /// expression that is only made of a DW_OP_plus_uconst operator. |
| /// |
| /// This is a sub-routine of die_member_offset. |
| /// |
| /// @param expr the DWARF expression to evaluate. |
| /// |
| /// @param expr_len the length of the expression @p expr. |
| /// |
| /// @param value out parameter. This is set to the result of the |
| /// evaluation of @p expr, iff this function returns true. |
| /// |
| /// @return true iff the evaluation of @p expr went OK. |
| static bool |
| eval_quickly(Dwarf_Op* expr, |
| uint64_t expr_len, |
| int64_t& value) |
| { |
| if (expr_len == 1 && (expr[0].atom == DW_OP_plus_uconst)) |
| { |
| value = expr[0].number; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Evaluate the value of the last sub-expression that is a constant, |
| /// inside a given DWARF expression. |
| /// |
| /// @param expr the DWARF expression to consider. |
| /// |
| /// @param expr_len the length of the expression to consider. |
| /// |
| /// @param value the resulting value of the last constant |
| /// sub-expression of the DWARF expression. This is set iff the |
| /// function returns true. |
| /// |
| /// @param is_tls_address out parameter. This is set to true iff |
| /// the resulting value of the evaluation is a TLS (thread local |
| /// storage) address. |
| /// |
| /// @param eval_ctxt the evaluation context to (re)use. Note that |
| /// this function initializes this context before using it. |
| /// |
| /// @return true if the function could find a constant sub-expression |
| /// to evaluate, false otherwise. |
| static bool |
| eval_last_constant_dwarf_sub_expr(Dwarf_Op* expr, |
| size_t expr_len, |
| int64_t& value, |
| bool& is_tls_address, |
| dwarf_expr_eval_context &eval_ctxt) |
| { |
| // Reset the evaluation context before evaluating the constant sub |
| // expression contained in the DWARF expression 'expr'. |
| eval_ctxt.reset(); |
| |
| size_t index = 0, next_index = 0; |
| do |
| { |
| if (op_is_arith_logic(expr, expr_len, index, |
| next_index, eval_ctxt) |
| || op_pushes_constant_value(expr, expr_len, index, |
| next_index, eval_ctxt) |
| || op_manipulates_stack(expr, expr_len, index, |
| next_index, eval_ctxt) |
| || op_pushes_non_constant_value(expr, expr_len, index, |
| next_index, eval_ctxt) |
| || op_is_control_flow(expr, expr_len, index, |
| next_index, eval_ctxt)) |
| ; |
| else |
| next_index = index + 1; |
| |
| ABG_ASSERT(next_index > index); |
| index = next_index; |
| } while (index < expr_len); |
| |
| is_tls_address = eval_ctxt.set_tls_address(); |
| if (eval_ctxt.accum.is_const()) |
| { |
| value = eval_ctxt.accum; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Evaluate the value of the last sub-expression that is a constant, |
| /// inside a given DWARF expression. |
| /// |
| /// @param expr the DWARF expression to consider. |
| /// |
| /// @param expr_len the length of the expression to consider. |
| /// |
| /// @param value the resulting value of the last constant |
| /// sub-expression of the DWARF expression. This is set iff the |
| /// function returns true. |
| /// |
| /// @return true if the function could find a constant sub-expression |
| /// to evaluate, false otherwise. |
| static bool |
| eval_last_constant_dwarf_sub_expr(Dwarf_Op* expr, |
| size_t expr_len, |
| int64_t& value, |
| bool& is_tls_address) |
| { |
| dwarf_expr_eval_context eval_ctxt; |
| return eval_last_constant_dwarf_sub_expr(expr, expr_len, value, |
| is_tls_address, eval_ctxt); |
| } |
| |
| // ----------------------------------- |
| // </location expression evaluation> |
| // ----------------------------------- |
| |
| /// Convert a DW_AT_bit_offset attribute value into the same value as |
| /// DW_AT_data_bit_offset - 8 * DW_AT_data_member_location. |
| /// |
| /// On big endian machines, the value of the DW_AT_bit_offset |
| /// attribute + 8 * the value of the DW_AT_data_member_location |
| /// attribute is the same as the value of the DW_AT_data_bit_offset |
| /// attribute. |
| /// |
| /// On little endian machines however, the situation is different. |
| /// The DW_AT_bit_offset value for a bit field is the number of bits |
| /// to the left of the most significant bit of the bit field, within |
| /// the integer value at DW_AT_data_member_location. |
| /// |
| /// The DW_AT_data_bit_offset offset value is the number of bits to |
| /// the right of the least significant bit of the bit field, again |
| /// relative to the containing integer value. |
| /// |
| /// In other words, DW_AT_data_bit_offset is what everybody would |
| /// instinctively think of as being the "offset of the bit field". 8 * |
| /// DW_AT_data_member_location + DW_AT_bit_offset however is very |
| /// counter-intuitive on little endian machines. |
| /// |
| /// This function thus reads the value of a DW_AT_bit_offset property |
| /// of a DIE and converts it into what the DW_AT_data_bit_offset would |
| /// have been if it was present, ignoring the contribution of |
| /// DW_AT_data_member_location. |
| /// |
| /// Note that DW_AT_bit_offset has been made obsolete starting from |
| /// DWARF5 (for GCC; Clang still emits it). |
| /// |
| /// If you like coffee and it's not too late, now might be a good time |
| /// to have a coffee break. Otherwise if it's late at night, you |
| /// might want to consider an herbal tea break. Then come back to |
| /// read this. |
| /// |
| /// |
| /// In what follows, the bit fields are all contained within the first |
| /// whole int of the struct, so DW_AT_data_member_location is 0. |
| /// |
| /// Okay, to have a better idea of what DW_AT_bit_offset and |
| /// DW_AT_data_bit_offset represent, let's consider a struct 'S' which |
| /// have bit fields data members defined as: |
| /// |
| /// struct S |
| /// { |
| /// int j:5; |
| /// int k:6; |
| /// int m:5; |
| /// int n:8; |
| /// }; |
| /// |
| /// The below wonderful (at least!) ASCII art sketch describes the |
| /// layout of the bitfields of 'struct S' on a little endian machine. |
| /// You need to read the sketch from the bottom-up. |
| /// |
| /// So please scroll down to its bottom. Note how the 32 bits integer |
| /// word containing the bit fields is laid out with its least |
| /// significant bit starting on the right hand side, at index 0. |
| /// |
| /// Then slowly scroll up starting from there, and take the time to |
| /// read each line and see how the bit fields are laid out and what |
| /// DW_AT_bit_offset and DW_AT_data_bit_offset represent for each of |
| /// the bit fields. |
| /// |
| /// DW_AT_bit_offset(n) |
| /// < - - - - - - > |
| /// | | n | |
| /// ^ ^< - - - - >^ |
| /// DW_AT_data_bit_offset(n) |
| /// < - - - - - - - - - - - - - - - > |
| /// | | |
| /// ^ ^ |
| /// DW_AT_bit_offset(m) |
| /// <---------------------------------> |
| /// | | m | |
| /// ^ ^< - >^ |
| /// DW_AT_data_bit_offset(m) |
| /// < - - - - - - - - - - > |
| /// | | |
| /// ^ ^ |
| /// DW_AT_bit_offset(k) |
| /// <--------------------------------------------> |
| /// | | k | |
| /// ^ ^< - - >^ |
| /// DW_AT_data_bit_offset(k) |
| /// < - - - - > |
| /// | | |
| /// ^ ^ |
| /// DW_AT_bit_offset(j) |
| /// <--------------------------------------------------------> |
| /// | | |
| /// ^ ^ |
| /// n m k j |
| /// < - - - - - - > < - - - > < - - - - > < - - - > |
| /// |
| /// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
| /// ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ |
| /// 31 27 23 16 15 11 10 6 5 4 0 |
| /// |
| /// So, the different bit fields all fit in one 32 bits word, assuming |
| /// the bit fields are tightly packed. |
| /// |
| /// Let's look at what DW_AT_bit_offset of the 'j' bit field would be |
| /// on this little endian machine and let's see how it relates to |
| /// DW_AT_data_bit_offset of j. |
| /// |
| /// DW_AT_bit_offset(j) would be equal to the number of bits from the |
| /// left of the 32 bits word (i.e from bit number 31) to the most |
| /// significant bit of the j bit field (i.e, bit number 4). Thus: |
| /// |
| /// DW_AT_bit_offset(j) = |
| /// sizeof_in_bits(int) - size_in_bits_of(j) = 32 - 5 = 27. |
| /// |
| /// DW_AT_data_bit_offset(j) is the number of bits from the right of the |
| /// 32 bits word (i.e, bit number 0) to the lest significant bit of |
| /// the 'j' bit field (ie, bit number 0). Thus: |
| /// |
| /// DW_AT_data_bit_offset(j) = 0. |
| /// |
| /// More generally, we can notice that: |
| /// |
| /// sizeof_in_bits(int) = |
| /// DW_AT_bit_offset(j) + sizeof_in_bits(j) + DW_AT_data_bit_offset(j). |
| /// |
| /// It follows that: |
| /// |
| /// DW_AT_data_bit_offset(j) = |
| /// sizeof_in_bits(int) - sizeof_in_bits(j) - DW_AT_bit_offset(j); |
| /// |
| /// Thus: |
| /// |
| /// DW_AT_data_bit_offset(j) = 32 - 27 - 5 = 0; |
| /// |
| /// Note that DW_AT_data_bit_offset(j) is the offset of 'j' starting |
| /// from the right hand side of the word. It is what we would |
| /// intuitively think it is. DW_AT_bit_offset however is super |
| /// counter-intuitive, pfff. |
| /// |
| /// Anyway, this general equation holds true for all bit fields. |
| /// |
| /// Similarly, it follows that: |
| /// |
| /// DW_AT_bit_offset(k) = |
| /// sizeof_in_bits(int) - sizeof_in_bits(k) - DW_AT_data_bit_offset(k); |
| /// |
| /// Thus: |
| /// DW_AT_bit_offset(k) = 32 - 6 - 5 = 21. |
| /// |
| /// |
| /// Likewise: |
| /// |
| /// DW_AT_bit_offset(m) = |
| /// sizeof_in_bits(int) - sizeof_in_bits(m) - DW_AT_data_bit_offset(m); |
| /// |
| /// |
| /// Thus: |
| /// DW_AT_bit_offset(m) = 32 - 5 - (5 + 6) = 16. |
| /// |
| /// And: |
| /// |
| /// |
| /// Lastly: |
| /// |
| /// DW_AT_bit_offset(n) = |
| /// sizeof_in_bits(int) - sizeof_in_bits(n) - DW_AT_bit_offset(n); |
| /// |
| /// Thus: |
| /// DW_AT_bit_offset(n) = 32 - 8 - (5 + 6 + 5) = 8. |
| /// |
| /// Luckily, the body of the function is much smaller than this |
| /// comment. Enjoy! |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param is_big_endian this is true iff the machine we are looking at |
| /// is big endian. |
| /// |
| /// @param offset this is the output parameter into which the value of |
| /// the DW_AT_bit_offset is put, converted as if it was the value of |
| /// the DW_AT_data_bit_offset parameter, less the contribution of |
| /// DW_AT_data_member_location. This parameter is set iff the |
| /// function returns true. |
| /// |
| /// @return true if DW_AT_bit_offset was found on @p die. |
| static bool |
| read_and_convert_DW_at_bit_offset(const Dwarf_Die* die, |
| bool is_big_endian, |
| uint64_t &offset) |
| { |
| uint64_t off = 0; |
| if (!die_unsigned_constant_attribute(die, DW_AT_bit_offset, off)) |
| return false; |
| |
| if (is_big_endian) |
| { |
| offset = off; |
| return true; |
| } |
| |
| // Okay, we are looking at a little endian machine. We need to |
| // convert DW_AT_bit_offset into what DW_AT_data_bit_offset would |
| // have been. To understand this, you really need to read the |
| // preliminary comment of this function. |
| uint64_t containing_anonymous_object_size = 0; |
| ABG_ASSERT(die_unsigned_constant_attribute(die, DW_AT_byte_size, |
| containing_anonymous_object_size)); |
| containing_anonymous_object_size *= 8; |
| |
| uint64_t bitfield_size = 0; |
| ABG_ASSERT(die_unsigned_constant_attribute(die, DW_AT_bit_size, |
| bitfield_size)); |
| |
| // As noted in the the preliminary comment of this function if we |
| // want to get the DW_AT_data_bit_offset of a bit field 'k' from the |
| // its DW_AT_bit_offset value, the equation is: |
| // |
| // DW_AT_data_bit_offset(k) = |
| // sizeof_in_bits(containing_anonymous_object_size) |
| // - DW_AT_data_bit_offset(k) |
| // - sizeof_in_bits(k) |
| offset = containing_anonymous_object_size - off - bitfield_size; |
| |
| return true; |
| } |
| |
| /// Get the value of the DW_AT_data_member_location of the given DIE |
| /// attribute as an constant. |
| /// |
| /// @param die the DIE to read the attribute from. |
| /// |
| /// @param offset the attribute as a constant value. This is set iff |
| /// the function returns true. |
| /// |
| /// @return true if the attribute exists and has a constant value. In |
| /// that case the offset is set to the value. |
| static bool |
| die_constant_data_member_location(const Dwarf_Die *die, |
| int64_t& offset) |
| { |
| if (!die) |
| return false; |
| |
| Dwarf_Attribute attr; |
| if (!dwarf_attr(const_cast<Dwarf_Die*>(die), |
| DW_AT_data_member_location, |
| &attr)) |
| return false; |
| |
| Dwarf_Word val; |
| if (dwarf_formudata(&attr, &val) != 0) |
| return false; |
| |
| offset = val; |
| return true; |
| } |
| |
| /// Get the offset of a struct/class member as represented by the |
| /// value of the DW_AT_data_member_location attribute. |
| /// |
| /// There is a huge gotcha in here. The value of the |
| /// DW_AT_data_member_location is not necessarily a constant that one |
| /// would just read and be done with it. Rather, it can be a DWARF |
| /// expression that one has to interpret. In general, the offset can |
| /// be given by the DW_AT_data_bit_offset or by the |
| /// DW_AT_data_member_location attribute and optionally the |
| /// DW_AT_bit_offset attribute. The bit offset attributes are |
| /// always simple constants, but the DW_AT_data_member_location |
| /// attribute is a DWARF location expression. |
| /// |
| /// When it's the DW_AT_data_member_location that is present, |
| /// there are three cases to possibly take into account: |
| /// |
| /// 1/ The offset in the vtable where the offset of a virtual base |
| /// can be found, aka vptr offset. Given the address of a |
| /// given object O, the vptr offset for B is given by the |
| /// (DWARF) expression: |
| /// |
| /// address(O) + *(*address(0) - VIRTUAL_OFFSET) |
| /// |
| /// where VIRTUAL_OFFSET is a constant value; In this case, |
| /// this function returns the constant VIRTUAL_OFFSET, as this |
| /// is enough to detect changes in a given virtual base |
| /// relative to the other virtual bases. |
| /// |
| /// 2/ The offset of a regular data member. Given the address of |
| /// a struct object named O, the memory location for a |
| /// particular data member is given by the (DWARF) expression: |
| /// |
| /// address(O) + OFFSET |
| /// |
| /// where OFFSET is a constant. In this case, this function |
| /// returns the OFFSET constant. |
| /// |
| /// 3/ The offset of a virtual member function in the virtual |
| /// pointer. The DWARF expression is a constant that designates |
| /// the offset of the function in the vtable. In this case this |
| /// function returns that constant. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE to read the information from. |
| /// |
| /// @param offset the resulting constant offset, in bits. This |
| /// argument is set iff the function returns true. |
| static bool |
| die_member_offset(const reader& rdr, |
| const Dwarf_Die* die, |
| int64_t& offset) |
| { |
| Dwarf_Op* expr = NULL; |
| size_t expr_len = 0; |
| uint64_t bit_offset = 0; |
| |
| // First let's see if the DW_AT_data_bit_offset attribute is |
| // present. |
| if (die_unsigned_constant_attribute(die, DW_AT_data_bit_offset, bit_offset)) |
| { |
| offset = bit_offset; |
| return true; |
| } |
| |
| // First try to read DW_AT_data_member_location as a plain constant. |
| // We do this because the generic method using die_location_expr |
| // might hit a bug in elfutils libdw dwarf_location_expression only |
| // fixed in elfutils 0.184+. The bug only triggers if the attribute |
| // is expressed as a (DWARF 5) DW_FORM_implicit_constant. But we |
| // handle all constants here because that is more consistent (and |
| // slightly faster in the general case where the attribute isn't a |
| // full DWARF expression). |
| if (!die_constant_data_member_location(die, offset)) |
| { |
| // Otherwise, let's see if the DW_AT_data_member_location |
| // attribute and, optionally, the DW_AT_bit_offset attributes |
| // are present. |
| if (!die_location_expr(die, DW_AT_data_member_location, |
| &expr, &expr_len)) |
| return false; |
| |
| // The DW_AT_data_member_location attribute is present. Let's |
| // evaluate it and get its constant sub-expression and return |
| // that one. |
| if (!eval_quickly(expr, expr_len, offset)) |
| { |
| bool is_tls_address = false; |
| if (!eval_last_constant_dwarf_sub_expr(expr, expr_len, |
| offset, is_tls_address, |
| rdr.dwarf_expr_eval_ctxt())) |
| return false; |
| } |
| } |
| offset *= 8; |
| |
| // On little endian machines, we need to convert the |
| // DW_AT_bit_offset attribute into a relative offset to 8 * |
| // DW_AT_data_member_location equal to what DW_AT_data_bit_offset |
| // would be if it were used instead. |
| // |
| // In other words, before adding it to 8 * |
| // DW_AT_data_member_location, DW_AT_bit_offset needs to be |
| // converted into a human-understandable form that represents the |
| // offset of the bitfield data member it describes. For details |
| // about the conversion, please read the extensive comments of |
| // read_and_convert_DW_at_bit_offset. |
| bool is_big_endian = architecture_is_big_endian(rdr.elf_handle()); |
| if (read_and_convert_DW_at_bit_offset(die, is_big_endian, bit_offset)) |
| offset += bit_offset; |
| |
| return true; |
| } |
| |
| /// Read the value of the DW_AT_location attribute from a DIE, |
| /// evaluate the resulting DWARF expression and, if it's a constant |
| /// expression, return it. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param address the resulting constant address. This is set iff |
| /// the function returns true. |
| /// |
| /// @return true iff the whole sequence of action described above |
| /// could be completed normally. |
| static bool |
| die_location_address(Dwarf_Die* die, |
| Dwarf_Addr& address, |
| bool& is_tls_address) |
| { |
| Dwarf_Op* expr = NULL; |
| size_t expr_len = 0; |
| |
| is_tls_address = false; |
| |
| if (!die) |
| return false; |
| |
| Dwarf_Attribute attr; |
| if (!dwarf_attr_integrate(const_cast<Dwarf_Die*>(die), DW_AT_location, &attr)) |
| return false; |
| |
| if (dwarf_getlocation(&attr, &expr, &expr_len)) |
| return false; |
| // Ignore location expressions where reading them succeeded but |
| // their length is 0. |
| if (expr_len == 0) |
| return false; |
| |
| Dwarf_Attribute result; |
| if (!dwarf_getlocation_attr(&attr, expr, &result)) |
| // A location that has been interpreted as an address. |
| return !dwarf_formaddr(&result, &address); |
| |
| // Just get the address out of the number field. |
| address = expr->number; |
| return true; |
| } |
| |
| /// Return the index of a function in its virtual table. That is, |
| /// return the value of the DW_AT_vtable_elem_location attribute. |
| /// |
| /// @param die the DIE of the function to consider. |
| /// |
| /// @param vindex the resulting index. This is set iff the function |
| /// returns true. |
| /// |
| /// @return true if the DIE has a DW_AT_vtable_elem_location |
| /// attribute. |
| static bool |
| die_virtual_function_index(Dwarf_Die* die, |
| int64_t& vindex) |
| { |
| if (!die) |
| return false; |
| |
| Dwarf_Op* expr = NULL; |
| size_t expr_len = 0; |
| if (!die_location_expr(die, DW_AT_vtable_elem_location, |
| &expr, &expr_len)) |
| return false; |
| |
| int64_t i = 0; |
| bool is_tls_addr = false; |
| if (!eval_last_constant_dwarf_sub_expr(expr, expr_len, i, is_tls_addr)) |
| return false; |
| |
| vindex = i; |
| return true; |
| } |
| |
| /// Test if a given DIE represents an anonymous type. |
| /// |
| /// Anonymous types we are interested in are classes, unions and |
| /// enumerations. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @return true iff @p die represents an anonymous type. |
| bool |
| is_anonymous_type_die(Dwarf_Die *die) |
| { |
| int tag = dwarf_tag(die); |
| |
| if (tag == DW_TAG_class_type |
| || tag == DW_TAG_structure_type |
| || tag == DW_TAG_union_type |
| || tag == DW_TAG_enumeration_type) |
| return die_is_anonymous(die); |
| |
| return false; |
| } |
| |
| /// Return the base of the internal name to represent an anonymous |
| /// type. |
| /// |
| /// Typically, anonymous enums would be named |
| /// __anonymous_enum__<number>, anonymous struct or classes would be |
| /// named __anonymous_struct__<number> and anonymous unions would be |
| /// named __anonymous_union__<number>. The first part of these |
| /// anonymous names (i.e, __anonymous_{enum,struct,union}__ is called |
| /// the base name. This function returns that base name, depending on |
| /// the kind of type DIE we are looking at. |
| /// |
| /// @param die the type DIE to look at. This function expects a type |
| /// DIE with an empty DW_AT_name property value (anonymous). |
| /// |
| /// @return a string representing the base of the internal anonymous |
| /// name. |
| static string |
| get_internal_anonymous_die_prefix_name(const Dwarf_Die *die) |
| { |
| ABG_ASSERT(die_is_type(die)); |
| ABG_ASSERT(die_string_attribute(die, DW_AT_name) == ""); |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| string type_name; |
| if (tag == DW_TAG_class_type || tag == DW_TAG_structure_type) |
| type_name = tools_utils::get_anonymous_struct_internal_name_prefix(); |
| else if (tag == DW_TAG_union_type) |
| type_name = tools_utils::get_anonymous_union_internal_name_prefix(); |
| else if (tag == DW_TAG_enumeration_type) |
| type_name = tools_utils::get_anonymous_enum_internal_name_prefix(); |
| |
| return type_name; |
| } |
| |
| /// Build a full internal anonymous type name. |
| /// |
| /// @param base_name this is the base name as returned by the function |
| /// @ref get_internal_anonymous_die_prefix_name. |
| /// |
| /// @param anonymous_type_index this is the index of the anonymous |
| /// type in its scope. That is, if there are more than one anonymous |
| /// types of a given kind in a scope, this index is what tells them |
| /// appart, starting from 0. |
| /// |
| /// @return the built string, which is a concatenation of @p base_name |
| /// and @p anonymous_type_index. |
| static string |
| build_internal_anonymous_die_name(const string &base_name, |
| size_t anonymous_type_index) |
| { |
| string name = base_name; |
| if (anonymous_type_index && !base_name.empty()) |
| { |
| std::ostringstream o; |
| o << base_name << anonymous_type_index; |
| name = o.str(); |
| } |
| return name; |
| } |
| |
| |
| /// Build a full internal anonymous type name. |
| /// |
| /// @param die the DIE representing the anonymous type to consider. |
| /// |
| /// @param anonymous_type_index the index of the anonymous type |
| /// represented by @p DIE, in its scope. That is, if there are |
| /// several different anonymous types of the same kind as @p die, this |
| /// index is what tells them appart. |
| /// |
| /// @return the internal name of the anonymous type represented by @p |
| /// DIE. |
| static string |
| get_internal_anonymous_die_name(Dwarf_Die *die, |
| size_t anonymous_type_index) |
| { |
| string name = get_internal_anonymous_die_prefix_name(die); |
| name = build_internal_anonymous_die_name(name, anonymous_type_index); |
| return name; |
| } |
| |
| // ------------------------------------ |
| // <DIE pretty printer> |
| // ------------------------------------ |
| |
| /// Compute the qualified name of a DIE that represents a type. |
| /// |
| /// For instance, if the DIE tag is DW_TAG_subprogram then this |
| /// function computes the name of the function *type*. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where in the are logically are in the DIE |
| /// stream. |
| /// |
| /// @return a copy of the qualified name of the type. |
| static string |
| die_qualified_type_name(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset) |
| { |
| if (!die) |
| return ""; |
| |
| int tag = dwarf_tag (const_cast<Dwarf_Die*>(die)); |
| if (tag == DW_TAG_compile_unit |
| || tag == DW_TAG_partial_unit |
| || tag == DW_TAG_type_unit) |
| return ""; |
| |
| string name = die_name(die); |
| |
| Dwarf_Die scope_die; |
| if (!get_scope_die(rdr, die, where_offset, scope_die)) |
| return ""; |
| |
| string parent_name = die_qualified_name(rdr, &scope_die, where_offset); |
| bool colon_colon = die_is_type(die) || die_is_namespace(die); |
| string separator = colon_colon ? "::" : "."; |
| |
| string repr; |
| |
| switch (tag) |
| { |
| case DW_TAG_unspecified_type: |
| break; |
| |
| case DW_TAG_base_type: |
| { |
| abigail::ir::integral_type int_type; |
| if (parse_integral_type(name, int_type)) |
| repr = int_type; |
| else |
| repr = name; |
| } |
| break; |
| |
| case DW_TAG_typedef: |
| case DW_TAG_enumeration_type: |
| case DW_TAG_structure_type: |
| case DW_TAG_class_type: |
| case DW_TAG_union_type: |
| { |
| if (name.empty()) |
| // TODO: handle cases where there are more than one |
| // anonymous type of the same kind in the same scope. In |
| // that case, their name must be built with the function |
| // get_internal_anonymous_die_name or something of the same |
| // kind. |
| name = get_internal_anonymous_die_prefix_name(die); |
| |
| ABG_ASSERT(!name.empty()); |
| repr = parent_name.empty() ? name : parent_name + separator + name; |
| } |
| break; |
| |
| case DW_TAG_const_type: |
| case DW_TAG_volatile_type: |
| case DW_TAG_restrict_type: |
| { |
| Dwarf_Die underlying_type_die; |
| bool has_underlying_type_die = |
| die_die_attribute(die, DW_AT_type, underlying_type_die); |
| |
| if (has_underlying_type_die && die_is_unspecified(&underlying_type_die)) |
| break; |
| |
| if (tag == DW_TAG_const_type) |
| { |
| if (has_underlying_type_die |
| && die_is_reference_type(&underlying_type_die)) |
| // A reference is always const. So, to lower false |
| // positive reports in diff computations, we consider a |
| // const reference just as a reference. But we need to |
| // keep the qualified-ness of the type. So we introduce |
| // a 'no-op' qualifier here. Please remember that this |
| // has to be kept in sync with what is done in |
| // get_name_of_qualified_type. So if you change this |
| // here, you have to change that code there too. |
| repr = ""; |
| else if (!has_underlying_type_die |
| || die_is_void_type(&underlying_type_die)) |
| { |
| repr = "void"; |
| break; |
| } |
| else |
| repr = "const"; |
| } |
| else if (tag == DW_TAG_volatile_type) |
| repr = "volatile"; |
| else if (tag == DW_TAG_restrict_type) |
| repr = "restrict"; |
| else |
| ABG_ASSERT_NOT_REACHED; |
| |
| string underlying_type_repr; |
| if (has_underlying_type_die) |
| underlying_type_repr = |
| die_qualified_type_name(rdr, &underlying_type_die, where_offset); |
| else |
| underlying_type_repr = "void"; |
| |
| if (underlying_type_repr.empty()) |
| repr.clear(); |
| else |
| { |
| if (has_underlying_type_die) |
| { |
| Dwarf_Die peeled; |
| die_peel_qualified(&underlying_type_die, peeled); |
| if (die_is_pointer_or_reference_type(&peeled)) |
| repr = underlying_type_repr + " " + repr; |
| else |
| repr += " " + underlying_type_repr; |
| } |
| else |
| repr += " " + underlying_type_repr; |
| } |
| } |
| break; |
| |
| case DW_TAG_pointer_type: |
| case DW_TAG_reference_type: |
| case DW_TAG_rvalue_reference_type: |
| { |
| Dwarf_Die pointed_to_type_die; |
| if (!die_die_attribute(die, DW_AT_type, pointed_to_type_die)) |
| { |
| if (tag == DW_TAG_pointer_type) |
| repr = "void*"; |
| break; |
| } |
| |
| if (die_is_unspecified(&pointed_to_type_die)) |
| break; |
| |
| string pointed_type_repr = |
| die_qualified_type_name(rdr, &pointed_to_type_die, where_offset); |
| |
| repr = pointed_type_repr; |
| if (repr.empty()) |
| break; |
| |
| if (tag == DW_TAG_pointer_type) |
| repr += "*"; |
| else if (tag == DW_TAG_reference_type) |
| repr += "&"; |
| else if (tag == DW_TAG_rvalue_reference_type) |
| repr += "&&"; |
| else |
| ABG_ASSERT_NOT_REACHED; |
| } |
| break; |
| |
| case DW_TAG_subrange_type: |
| { |
| // In Ada, this one can be generated on its own, that is, not |
| // as a sub-type of an array. So we need to support it on its |
| // own. Note that when it's emitted as the sub-type of an |
| // array like in C and C++, this is handled differently, for |
| // now. But we try to make this usable by other languages |
| // that are not Ada, even if we modelled it after Ada. |
| |
| // So we build a subrange type for the sole purpose of using |
| // the ::as_string() method of that type. So we don't add |
| // that type to the current type tree being built. |
| array_type_def::subrange_sptr s = |
| build_subrange_type(const_cast<reader&>(rdr), |
| die, where_offset, |
| /*associate_die_to_type=*/false); |
| repr += s->as_string(); |
| break; |
| } |
| |
| case DW_TAG_array_type: |
| { |
| Dwarf_Die element_type_die; |
| if (!die_die_attribute(die, DW_AT_type, element_type_die)) |
| break; |
| string element_type_name = |
| die_qualified_type_name(rdr, &element_type_die, where_offset); |
| if (element_type_name.empty()) |
| break; |
| |
| array_type_def::subranges_type subranges; |
| build_subranges_from_array_type_die(const_cast<reader&>(rdr), |
| die, subranges, where_offset, |
| /*associate_type_to_die=*/false); |
| |
| repr = element_type_name; |
| repr += array_type_def::subrange_type::vector_as_string(subranges); |
| } |
| break; |
| |
| case DW_TAG_subroutine_type: |
| case DW_TAG_subprogram: |
| { |
| string return_type_name; |
| string class_name; |
| vector<string> parm_names; |
| bool is_const = false; |
| bool is_static = false; |
| |
| die_return_and_parm_names_from_fn_type_die(rdr, die, where_offset, |
| /*pretty_print=*/true, |
| return_type_name, class_name, |
| parm_names, is_const, |
| is_static); |
| if (return_type_name.empty()) |
| return_type_name = "void"; |
| |
| repr = return_type_name; |
| |
| if (!class_name.empty()) |
| { |
| // This is a method, so print the class name. |
| repr += " (" + class_name + "::*)"; |
| } |
| |
| // Now parameters. |
| repr += " ("; |
| for (vector<string>::const_iterator i = parm_names.begin(); |
| i != parm_names.end(); |
| ++i) |
| { |
| if (i != parm_names.begin()) |
| repr += ", "; |
| repr += *i; |
| } |
| repr += ")"; |
| |
| } |
| break; |
| |
| case DW_TAG_string_type: |
| case DW_TAG_ptr_to_member_type: |
| case DW_TAG_set_type: |
| case DW_TAG_file_type: |
| case DW_TAG_packed_type: |
| case DW_TAG_thrown_type: |
| case DW_TAG_interface_type: |
| case DW_TAG_shared_type: |
| break; |
| } |
| |
| return repr; |
| } |
| |
| /// Compute the qualified name of a decl represented by a given DIE. |
| /// |
| /// For instance, for a DIE of tag DW_TAG_subprogram this function |
| /// computes the signature of the function *declaration*. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where we are logically at in the DIE stream. |
| /// |
| /// @return a copy of the computed name. |
| static string |
| die_qualified_decl_name(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset) |
| { |
| if (!die || !die_is_decl(die)) |
| return ""; |
| |
| string name = die_name(die); |
| |
| Dwarf_Die scope_die; |
| if (!get_scope_die(rdr, die, where_offset, scope_die)) |
| return ""; |
| |
| string scope_name = die_qualified_name(rdr, &scope_die, where_offset); |
| string separator = "::"; |
| |
| string repr; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| switch (tag) |
| { |
| case DW_TAG_namespace: |
| case DW_TAG_member: |
| case DW_TAG_variable: |
| repr = scope_name.empty() ? name : scope_name + separator + name; |
| break; |
| case DW_TAG_subprogram: |
| repr = die_function_signature(rdr, die, where_offset); |
| break; |
| |
| case DW_TAG_unspecified_parameters: |
| repr = "..."; |
| break; |
| |
| case DW_TAG_formal_parameter: |
| case DW_TAG_imported_declaration: |
| case DW_TAG_GNU_template_template_param: |
| case DW_TAG_GNU_template_parameter_pack: |
| case DW_TAG_GNU_formal_parameter_pack: |
| break; |
| } |
| return repr; |
| } |
| |
| /// Compute the qualified name of the artifact represented by a given |
| /// DIE. |
| /// |
| /// If the DIE represents a type, then the function computes the name |
| /// of the type. Otherwise, if the DIE represents a decl then the |
| /// function computes the name of the decl. Note that a DIE of tag |
| /// DW_TAG_subprogram is going to be considered as a "type" -- just |
| /// like if it was a DW_TAG_subroutine_type. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset where we are logically at in the DIE stream. |
| /// |
| /// @return a copy of the computed name. |
| static string |
| die_qualified_name(const reader& rdr, const Dwarf_Die* die, size_t where) |
| { |
| if (die_is_type(die)) |
| return die_qualified_type_name(rdr, die, where); |
| else if (die_is_decl(die)) |
| return die_qualified_decl_name(rdr, die, where); |
| return ""; |
| } |
| |
| /// Test if the qualified name of a given type should be empty. |
| /// |
| /// The reason why the name of a DIE with a given tag would be empty |
| /// is that libabigail's internal representation doesn't yet support |
| /// that tag; or if the DIE's qualified name is built from names of |
| /// sub-types DIEs whose tags are not yet supported. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where where we are logically at, in the DIE stream. |
| /// |
| /// @param qualified_name the qualified name of the DIE. This is set |
| /// only iff the function returns false. |
| /// |
| /// @return true if the qualified name of the DIE is empty. |
| static bool |
| die_qualified_type_name_empty(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where, string &qualified_name) |
| { |
| if (!die) |
| return true; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| |
| string qname; |
| if (tag == DW_TAG_typedef |
| || tag == DW_TAG_pointer_type |
| || tag == DW_TAG_reference_type |
| || tag == DW_TAG_rvalue_reference_type |
| || tag == DW_TAG_array_type |
| || tag == DW_TAG_const_type |
| || tag == DW_TAG_volatile_type |
| || tag == DW_TAG_restrict_type) |
| { |
| Dwarf_Die underlying_type_die; |
| if (die_die_attribute(die, DW_AT_type, underlying_type_die)) |
| { |
| string name = |
| die_qualified_type_name(rdr, &underlying_type_die, where); |
| if (name.empty()) |
| return true; |
| } |
| } |
| else |
| { |
| string name = die_qualified_type_name(rdr, die, where); |
| if (name.empty()) |
| return true; |
| } |
| |
| qname = die_qualified_type_name(rdr, die, where); |
| if (qname.empty()) |
| return true; |
| |
| qualified_name = qname; |
| return false; |
| } |
| |
| /// Given the DIE that represents a function type, compute the names |
| /// of the following properties the function's type: |
| /// |
| /// - return type |
| /// - enclosing class (if the function is a member function) |
| /// - function parameter types |
| /// |
| /// When the function we are looking at is a member function, it also |
| /// tells if it's const. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param die the DIE of the function or function type we are looking |
| /// at. |
| /// |
| /// @param where_offset where we are logically at in the DIE stream. |
| /// |
| /// @param pretty_print if set to yes, the type names are going to be |
| /// pretty-printed names; otherwise, they are just qualified type |
| /// names. |
| /// |
| /// @param return_type_name out parameter. This contains the name of |
| /// the return type of the function. |
| /// |
| /// @param class_name out parameter. If the function is a member |
| /// function, this contains the name of the enclosing class. |
| /// |
| /// @param parm_names out parameter. This vector is set to the names |
| /// of the types of the parameters of the function. |
| /// |
| /// @param is_const out parameter. If the function is a member |
| /// function, this is set to true iff the member function is const. |
| /// |
| /// @param is_static out parameter. If the function is a static |
| /// member function, then this is set to true. |
| static void |
| die_return_and_parm_names_from_fn_type_die(const reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset, |
| bool pretty_print, |
| string &return_type_name, |
| string &class_name, |
| vector<string>& parm_names, |
| bool& is_const, |
| bool& is_static) |
| { |
| Dwarf_Die child; |
| Dwarf_Die ret_type_die; |
| if (!die_die_attribute(die, DW_AT_type, ret_type_die)) |
| return_type_name = "void"; |
| else |
| return_type_name = |
| pretty_print |
| ? rdr.get_die_pretty_representation(&ret_type_die, where_offset) |
| : rdr.get_die_qualified_type_name(&ret_type_die, where_offset); |
| |
| if (return_type_name.empty()) |
| return_type_name = "void"; |
| |
| Dwarf_Die object_pointer_die, class_die; |
| bool is_method_type = |
| die_function_type_is_method_type(rdr, die, where_offset, |
| object_pointer_die, |
| class_die, is_static); |
| |
| is_const = false; |
| if (is_method_type) |
| { |
| class_name = rdr.get_die_qualified_type_name(&class_die, where_offset); |
| |
| Dwarf_Die this_pointer_die; |
| Dwarf_Die pointed_to_die; |
| if (!is_static |
| && die_die_attribute(&object_pointer_die, DW_AT_type, |
| this_pointer_die)) |
| if (die_die_attribute(&this_pointer_die, DW_AT_type, pointed_to_die)) |
| if (dwarf_tag(&pointed_to_die) == DW_TAG_const_type) |
| is_const = true; |
| |
| string fn_name = die_name(die); |
| string non_qualified_class_name = die_name(&class_die); |
| bool is_ctor = fn_name == non_qualified_class_name; |
| bool is_dtor = !fn_name.empty() && fn_name[0] == '~'; |
| |
| if (is_ctor || is_dtor) |
| return_type_name.clear(); |
| } |
| |
| if (dwarf_child(const_cast<Dwarf_Die*>(die), &child) == 0) |
| do |
| { |
| int child_tag = dwarf_tag(&child); |
| if (child_tag == DW_TAG_formal_parameter) |
| { |
| Dwarf_Die parm_type_die; |
| if (!die_die_attribute(&child, DW_AT_type, parm_type_die)) |
| continue; |
| string qualified_name = |
| pretty_print |
| ? rdr.get_die_pretty_representation(&parm_type_die, where_offset) |
| : rdr.get_die_qualified_type_name(&parm_type_die, where_offset); |
| |
| if (qualified_name.empty()) |
| continue; |
| parm_names.push_back(qualified_name); |
| } |
| else if (child_tag == DW_TAG_unspecified_parameters) |
| { |
| // This is a variadic function parameter. |
| parm_names.push_back(rdr.env().get_variadic_parameter_type_name()); |
| // After a DW_TAG_unspecified_parameters tag, we shouldn't |
| // keep reading for parameters. The |
| // unspecified_parameters TAG should be the last parameter |
| // that we record. For instance, if there are multiple |
| // DW_TAG_unspecified_parameters DIEs then we should care |
| // only for the first one. |
| break; |
| } |
| } |
| while (dwarf_siblingof(&child, &child) == 0); |
| |
| if (class_name.empty()) |
| { |
| Dwarf_Die parent_die; |
| if (get_parent_die(rdr, die, parent_die, where_offset)) |
| { |
| if (die_is_class_type(&parent_die)) |
| class_name = |
| rdr.get_die_qualified_type_name(&parent_die, where_offset); |
| } |
| } |
| } |
| |
| /// This computes the signature of the a function declaration |
| /// represented by a DIE. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param fn_die the DIE of the function to consider. |
| /// |
| /// @param where_offset where we are logically at in the stream of |
| /// DIEs. |
| /// |
| /// @return a copy of the computed function signature string. |
| static string |
| die_function_signature(const reader& rdr, |
| const Dwarf_Die *fn_die, |
| size_t where_offset) |
| { |
| |
| translation_unit::language lang; |
| bool has_lang = false; |
| if ((has_lang = get_die_language(fn_die, lang))) |
| { |
| // In a binary originating from the C language, it's OK to use |
| // the linkage name of the function as a key for the map which |
| // is meant to reduce the number of DIE comparisons involved |
| // during DIE canonicalization computation. |
| if (is_c_language(lang)) |
| { |
| string fn_name = die_linkage_name(fn_die); |
| if (fn_name.empty()) |
| fn_name = die_name(fn_die); |
| return fn_name; |
| } |
| } |
| |
| // TODO: When we can structurally compare DIEs originating from C++ |
| // as well, we can use the linkage name of functions in C++ too, to |
| // reduce the number of comparisons involved during DIE |
| // canonicalization. |
| |
| string return_type_name; |
| Dwarf_Die ret_type_die; |
| if (die_die_attribute(fn_die, DW_AT_type, ret_type_die)) |
| return_type_name = rdr.get_die_qualified_type_name(&ret_type_die, |
| where_offset); |
| |
| if (return_type_name.empty()) |
| return_type_name = "void"; |
| |
| Dwarf_Die scope_die; |
| string scope_name; |
| if (get_scope_die(rdr, fn_die, where_offset, scope_die)) |
| scope_name = rdr.get_die_qualified_name(&scope_die, where_offset); |
| string fn_name = die_name(fn_die); |
| if (!scope_name.empty()) |
| fn_name = scope_name + "::" + fn_name; |
| |
| string class_name; |
| vector<string> parm_names; |
| bool is_const = false; |
| bool is_static = false; |
| |
| die_return_and_parm_names_from_fn_type_die(rdr, fn_die, where_offset, |
| /*pretty_print=*/false, |
| return_type_name, class_name, |
| parm_names, is_const, is_static); |
| |
| bool is_virtual = die_is_virtual(fn_die); |
| |
| string repr = class_name.empty() ? "function" : "method"; |
| if (is_virtual) |
| repr += " virtual"; |
| |
| if (!return_type_name.empty()) |
| repr += " " + return_type_name; |
| |
| repr += " " + fn_name; |
| |
| // Now parameters. |
| repr += "("; |
| bool some_parm_emitted = false; |
| for (vector<string>::const_iterator i = parm_names.begin(); |
| i != parm_names.end(); |
| ++i) |
| { |
| if (i != parm_names.begin()) |
| { |
| if (some_parm_emitted) |
| repr += ", "; |
| } |
| else |
| if (!is_static && !class_name.empty()) |
| // We are printing a non-static method name, skip the implicit "this" |
| // parameter type. |
| continue; |
| repr += *i; |
| some_parm_emitted = true; |
| } |
| repr += ")"; |
| |
| if (is_const) |
| { |
| ABG_ASSERT(!class_name.empty()); |
| repr += " const"; |
| } |
| |
| return repr; |
| } |
| |
| /// Return a pretty string representation of a type, for internal purposes. |
| /// |
| /// By internal purpose, we mean things like key-ing types for lookup |
| /// purposes and so on. |
| /// |
| /// Note that this function is also used to pretty print functions. |
| /// For functions, it prints the *type* of the function. |
| /// |
| /// @param rdr the context to use. |
| /// |
| /// @param the DIE of the type to pretty print. |
| /// |
| /// @param where_offset where we logically are placed when calling |
| /// this. It's useful to handle inclusion of DW_TAG_compile_unit |
| /// entries. |
| /// |
| /// @return the resulting pretty representation. |
| static string |
| die_pretty_print_type(reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset) |
| { |
| if (!die |
| || (!die_is_type(die) |
| && dwarf_tag(const_cast<Dwarf_Die*>(die)) != DW_TAG_subprogram)) |
| return ""; |
| |
| string repr; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| switch (tag) |
| { |
| case DW_TAG_string_type: |
| // For now, we won't try to go get the actual representation of |
| // the string because this would make things more complicated; |
| // for that we'd need to interpret some location expressions to |
| // get the length of the string. And for dynamically allocated |
| // strings, the result of the location expression evaluation |
| // might not even be a constant. So at the moment I consider |
| // this to be a lot of hassle for no great return. Until proven |
| // otherwise, of course. |
| repr = "string type"; |
| |
| case DW_TAG_unspecified_type: |
| case DW_TAG_ptr_to_member_type: |
| break; |
| |
| case DW_TAG_namespace: |
| repr = "namespace " + rdr.get_die_qualified_type_name(die, where_offset); |
| break; |
| |
| case DW_TAG_base_type: |
| repr = rdr.get_die_qualified_type_name(die, where_offset); |
| break; |
| |
| case DW_TAG_typedef: |
| { |
| string qualified_name; |
| if (!die_qualified_type_name_empty(rdr, die, |
| where_offset, |
| qualified_name)) |
| repr = "typedef " + qualified_name; |
| } |
| break; |
| |
| case DW_TAG_const_type: |
| case DW_TAG_volatile_type: |
| case DW_TAG_restrict_type: |
| case DW_TAG_pointer_type: |
| case DW_TAG_reference_type: |
| case DW_TAG_rvalue_reference_type: |
| repr = rdr.get_die_qualified_type_name(die, where_offset); |
| break; |
| |
| case DW_TAG_enumeration_type: |
| { |
| string qualified_name = |
| rdr.get_die_qualified_type_name(die, where_offset); |
| repr = "enum " + qualified_name; |
| } |
| break; |
| |
| case DW_TAG_structure_type: |
| case DW_TAG_class_type: |
| { |
| string qualified_name = |
| rdr.get_die_qualified_type_name(die, where_offset); |
| repr = "class " + qualified_name; |
| } |
| break; |
| |
| case DW_TAG_union_type: |
| { |
| string qualified_name = |
| rdr.get_die_qualified_type_name(die, where_offset); |
| repr = "union " + qualified_name; |
| } |
| break; |
| |
| case DW_TAG_array_type: |
| { |
| Dwarf_Die element_type_die; |
| if (!die_die_attribute(die, DW_AT_type, element_type_die)) |
| break; |
| string element_type_name = |
| rdr.get_die_qualified_type_name(&element_type_die, where_offset); |
| if (element_type_name.empty()) |
| break; |
| |
| array_type_def::subranges_type subranges; |
| build_subranges_from_array_type_die(rdr, die, subranges, where_offset, |
| /*associate_type_to_die=*/false); |
| |
| repr = element_type_name; |
| repr += array_type_def::subrange_type::vector_as_string(subranges); |
| } |
| break; |
| |
| case DW_TAG_subrange_type: |
| { |
| // So this can be generated by Ada, on its own; that is, not |
| // as a subtype of an array. In that case we need to handle |
| // it properly. |
| |
| // For now, we consider that the pretty printed name of the |
| // subrange type is its name. We might need something more |
| // advance, should the needs of the users get more |
| // complicated. |
| repr += die_qualified_type_name(rdr, die, where_offset); |
| } |
| break; |
| |
| case DW_TAG_subroutine_type: |
| case DW_TAG_subprogram: |
| { |
| string return_type_name; |
| string class_name; |
| vector<string> parm_names; |
| bool is_const = false; |
| bool is_static = false; |
| |
| die_return_and_parm_names_from_fn_type_die(rdr, die, where_offset, |
| /*pretty_print=*/true, |
| return_type_name, class_name, |
| parm_names, is_const, |
| is_static); |
| if (class_name.empty()) |
| repr = "function type"; |
| else |
| repr = "method type"; |
| repr += " " + rdr.get_die_qualified_type_name(die, where_offset); |
| } |
| break; |
| |
| case DW_TAG_set_type: |
| case DW_TAG_file_type: |
| case DW_TAG_packed_type: |
| case DW_TAG_thrown_type: |
| case DW_TAG_interface_type: |
| case DW_TAG_shared_type: |
| ABG_ASSERT_NOT_REACHED; |
| } |
| |
| return repr; |
| } |
| |
| /// Return a pretty string representation of a declaration, for |
| /// internal purposes. |
| /// |
| /// By internal purpose, we mean things like key-ing declarations for |
| /// lookup purposes and so on. |
| /// |
| /// Note that this function is also used to pretty print functions. |
| /// For functions, it prints the signature of the function. |
| /// |
| /// @param rdr the context to use. |
| /// |
| /// @param the DIE of the declaration to pretty print. |
| /// |
| /// @param where_offset where we logically are placed when calling |
| /// this. It's useful to handle inclusion of DW_TAG_compile_unit |
| /// entries. |
| /// |
| /// @return the resulting pretty representation. |
| static string |
| die_pretty_print_decl(reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset) |
| { |
| if (!die || !die_is_decl(die)) |
| return ""; |
| |
| string repr; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| switch (tag) |
| { |
| case DW_TAG_namespace: |
| repr = "namespace " + die_qualified_name(rdr, die, where_offset); |
| break; |
| |
| case DW_TAG_member: |
| case DW_TAG_variable: |
| { |
| string type_repr = "void"; |
| Dwarf_Die type_die; |
| if (die_die_attribute(die, DW_AT_type, type_die)) |
| type_repr = die_qualified_type_name(rdr, &type_die, where_offset); |
| repr = die_qualified_name(rdr, die, where_offset); |
| if (!repr.empty()) |
| repr = type_repr + " " + repr; |
| } |
| break; |
| |
| case DW_TAG_subprogram: |
| repr = die_function_signature(rdr, die, where_offset); |
| break; |
| |
| default: |
| break; |
| } |
| return repr; |
| } |
| |
| /// Compute the pretty printed representation of an artifact |
| /// represented by a DIE. |
| /// |
| /// If the DIE is a type, compute the its pretty representation as a |
| /// type; otherwise, if it's a declaration, compute its pretty |
| /// representation as a declaration. Note for For instance, that a |
| /// DW_TAG_subprogram DIE is going to be represented as a function |
| /// *type*. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param where_offset we in the DIE stream we are logically at. |
| /// |
| /// @return a copy of the pretty printed artifact. |
| static string |
| die_pretty_print(reader& rdr, const Dwarf_Die* die, size_t where_offset) |
| { |
| if (die_is_type(die)) |
| return die_pretty_print_type(rdr, die, where_offset); |
| else if (die_is_decl(die)) |
| return die_pretty_print_decl(rdr, die, where_offset); |
| return ""; |
| } |
| |
| // ----------------------------------- |
| // </die pretty printer> |
| // ----------------------------------- |
| |
| |
| // ---------------------------------- |
| // <die comparison engine> |
| // --------------------------------- |
| |
| /// Compares two decls DIEs |
| /// |
| /// This works only for DIEs emitted by the C language. |
| /// |
| /// This implementation doesn't yet support namespaces. |
| /// |
| /// This is a subroutine of compare_dies. |
| /// |
| /// @return true iff @p l equals @p r. |
| static bool |
| compare_as_decl_dies(const Dwarf_Die *l, const Dwarf_Die *r) |
| { |
| ABG_ASSERT(l && r); |
| |
| int l_tag = dwarf_tag(const_cast<Dwarf_Die*>(l)); |
| int r_tag = dwarf_tag(const_cast<Dwarf_Die*>(r)); |
| if (l_tag != r_tag) |
| return false; |
| |
| bool result = false; |
| |
| if (l_tag == DW_TAG_subprogram || l_tag == DW_TAG_variable) |
| { |
| // Fast path for functions and global variables. |
| if (compare_dies_string_attribute_value(l, r, DW_AT_linkage_name, |
| result) |
| || compare_dies_string_attribute_value(l, r, DW_AT_MIPS_linkage_name, |
| result)) |
| { |
| if (!result) |
| return false; |
| } |
| |
| if (compare_dies_string_attribute_value(l, r, DW_AT_name, |
| result)) |
| { |
| if (!result) |
| return false; |
| } |
| return true; |
| } |
| |
| // Fast path for types. |
| if (compare_dies_string_attribute_value(l, r, DW_AT_name, |
| result)) |
| return result; |
| return true; |
| } |
| |
| /// Test if at least one of two ODR-relevant DIEs is decl-only. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param l the first type DIE to consider. |
| /// |
| /// @param r the second type DIE to consider. |
| /// |
| /// @return true iff either @p l or @p r is decl-only and both are |
| /// ODR-relevant. |
| static bool |
| at_least_one_decl_only_among_odr_relevant_dies(const reader &rdr, |
| const Dwarf_Die *l, |
| const Dwarf_Die *r) |
| { |
| if (!(rdr.odr_is_relevant(l) && rdr.odr_is_relevant(r))) |
| return false; |
| |
| if ((die_is_declaration_only(l) && die_has_no_child(l)) |
| || (die_is_declaration_only(r) && die_has_no_child(r))) |
| return true; |
| return false; |
| } |
| |
| /// Compares two type DIEs |
| /// |
| /// This is a subroutine of compare_dies. |
| /// |
| /// Note that this function doesn't look at the name of the DIEs. |
| /// Naming is taken into account by the function compare_as_decl_dies. |
| /// |
| /// If the two DIEs are from a translation unit that is subject to the |
| /// ONE Definition Rule, then the function considers that if one DIE |
| /// is a declaration, then it's equivalent to the second. In that |
| /// case, the sizes of the two DIEs are not compared. This is so that |
| /// a declaration of a type compares equal to the definition of the |
| /// type. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param l the left operand of the comparison operator. |
| /// |
| /// @param r the right operand of the comparison operator. |
| /// |
| /// @return true iff @p l equals @p r. |
| static bool |
| compare_as_type_dies(const reader& rdr, |
| const Dwarf_Die *l, |
| const Dwarf_Die *r) |
| { |
| ABG_ASSERT(l && r); |
| ABG_ASSERT(die_is_type(l)); |
| ABG_ASSERT(die_is_type(r)); |
| |
| if (dwarf_tag(const_cast<Dwarf_Die*>(l)) == DW_TAG_string_type |
| && dwarf_tag(const_cast<Dwarf_Die*>(r)) == DW_TAG_string_type |
| && (dwarf_dieoffset(const_cast<Dwarf_Die*>(l)) |
| != dwarf_dieoffset(const_cast<Dwarf_Die*>(r)))) |
| // For now, we cannot compare DW_TAG_string_type because of its |
| // string_length attribute that is a location descriptor that is |
| // not necessarily a constant. So it's super hard to evaluate it |
| // in a libabigail context. So for now, we just say that all |
| // DW_TAG_string_type DIEs are different, by default. |
| return false; |
| |
| if (at_least_one_decl_only_among_odr_relevant_dies(rdr, l, r)) |
| // A declaration of a type compares equal to the definition of the |
| // type. |
| return true; |
| |
| uint64_t l_size = 0, r_size = 0; |
| die_size_in_bits(l, l_size); |
| die_size_in_bits(r, r_size); |
| |
| return l_size == r_size; |
| } |
| |
| /// Compare two DIEs as decls (looking as their names etc) and as |
| /// types (looking at their size etc). |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param l the first DIE to consider. |
| /// |
| /// @param r the second DIE to consider. |
| /// |
| /// @return TRUE iff @p l equals @p r as far as naming and size is |
| /// concerned. |
| static bool |
| compare_as_decl_and_type_dies(const reader &rdr, |
| const Dwarf_Die *l, |
| const Dwarf_Die *r) |
| { |
| if (!compare_as_decl_dies(l, r) |
| || !compare_as_type_dies(rdr, l, r)) |
| return false; |
| |
| return true; |
| } |
| |
| /// Test if two DIEs representing function declarations have the same |
| /// linkage name, and thus are considered equal if they are C or C++, |
| /// because the two DIEs represent functions in the same binary. |
| /// |
| /// If the DIEs don't have a linkage name, the function compares their |
| /// name. But in that case, the caller of the function must know that |
| /// in C++ for instance, that doesn't imply that the two functions are |
| /// equal. |
| /// |
| /// @param l the first function DIE to consider. |
| /// |
| /// @param r the second function DIE to consider. |
| /// |
| /// @return true iff the function represented by @p l have the same |
| /// linkage name as the function represented by @p r. |
| static bool |
| fn_die_equal_by_linkage_name(const Dwarf_Die *l, |
| const Dwarf_Die *r) |
| { |
| if (!!l != !!r) |
| return false; |
| |
| if (!l) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(l)); |
| ABG_ASSERT(tag == DW_TAG_subprogram); |
| tag = dwarf_tag(const_cast<Dwarf_Die*>(r)); |
| ABG_ASSERT(tag == DW_TAG_subprogram); |
| |
| string lname = die_name(l), rname = die_name(r); |
| string llinkage_name = die_linkage_name(l), |
| rlinkage_name = die_linkage_name(r); |
| |
| if (die_is_in_c_or_cplusplus(l) |
| && die_is_in_c_or_cplusplus(r)) |
| { |
| if (!llinkage_name.empty() && !rlinkage_name.empty()) |
| return llinkage_name == rlinkage_name; |
| else if (!!llinkage_name.empty() != !!rlinkage_name.empty()) |
| return false; |
| else |
| return lname == rname; |
| } |
| |
| return (!llinkage_name.empty() |
| && !rlinkage_name.empty() |
| && llinkage_name == rlinkage_name); |
| } |
| |
| /// Compare two DIEs in the context of DIE canonicalization. |
| /// |
| /// If DIE canonicalization is on, the function compares the DIEs |
| /// canonically and structurally. The two types of comparison should |
| /// be equal, of course. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param l_offset the offset of the first canonical DIE to compare. |
| /// |
| /// @param r_offset the offset of the second canonical DIE to compare. |
| /// |
| /// @param l_die_source the source of the DIE denoted by the offset @p |
| /// l_offset. |
| /// |
| /// @param r_die_source the source of the DIE denoted by the offset @p |
| /// r_offset. |
| /// |
| /// @param l_has_canonical_die_offset output parameter. Is set to |
| /// true if @p l_offset has a canonical DIE. |
| /// |
| /// @param r_has_canonical_die_offset output parameter. Is set to |
| /// true if @p r_offset has a canonical DIE. |
| /// |
| /// @param l_canonical_die_offset output parameter. If @p |
| /// l_has_canonical_die_offset is set to true, then this parameter is |
| /// set to the offset of the canonical DIE of the DIE designated by @p |
| /// l_offset. |
| static bool |
| try_canonical_die_comparison(const reader& rdr, |
| Dwarf_Off l_offset, Dwarf_Off r_offset, |
| die_source l_die_source, die_source r_die_source, |
| bool& l_has_canonical_die_offset, |
| bool& r_has_canonical_die_offset, |
| Dwarf_Off& l_canonical_die_offset, |
| Dwarf_Off& r_canonical_die_offset, |
| bool& result) |
| { |
| #ifdef WITH_DEBUG_TYPE_CANONICALIZATION |
| if (rdr.debug_die_canonicalization_is_on_ |
| && !rdr.use_canonical_die_comparison_) |
| return false; |
| #endif |
| |
| |
| l_has_canonical_die_offset = |
| (l_canonical_die_offset = |
| rdr.get_canonical_die_offset(l_offset, l_die_source, |
| /*die_as_type=*/true)); |
| |
| r_has_canonical_die_offset = |
| (r_canonical_die_offset = |
| rdr.get_canonical_die_offset(r_offset, r_die_source, |
| /*die_as_type=*/true)); |
| |
| if (l_has_canonical_die_offset && r_has_canonical_die_offset) |
| { |
| result = (l_canonical_die_offset == r_canonical_die_offset); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| #ifdef WITH_DEBUG_TYPE_CANONICALIZATION |
| /// This function is called whenever a DIE comparison fails. |
| /// |
| /// This function is intended for debugging purposes. The idea is for |
| /// hackers to set a breakpoint on this function so that they can |
| /// discover why exactly the comparison failed. They then can execute |
| /// the program from compare_dies_during_canonicalization, for |
| /// instance. |
| /// |
| /// @param @l the left-hand side of the DIE comparison. |
| /// |
| /// @param @r the right-hand side of the DIE comparison. |
| static void |
| notify_die_comparison_failed(const Dwarf_Die* /*l*/, const Dwarf_Die* /*r*/) |
| { |
| } |
| |
| #define NOTIFY_DIE_COMPARISON_FAILED(l, r) \ |
| notify_die_comparison_failed(l, r) |
| #else |
| #define NOTIFY_DIE_COMPARISON_FAILED(l, r) |
| #endif |
| |
| /// A macro used to return from DIE comparison routines. |
| /// |
| /// If the return value is false, the macro invokes the |
| /// notify_die_comparison_failed signalling function before returning. |
| /// That way, hackers willing to learn more about why the comparison |
| /// routine returned "false" can just set a breakpoint on |
| /// notify_die_comparison_failed and execute the program from |
| /// compare_dies_during_canonicalization, for instance. |
| /// |
| /// @param value the value to return from the DIE comparison routines. |
| #define ABG_RETURN(value) \ |
| do \ |
| { \ |
| if ((value) == COMPARISON_RESULT_DIFFERENT) \ |
| { \ |
| NOTIFY_DIE_COMPARISON_FAILED(l, r); \ |
| } \ |
| return return_comparison_result(l, r, dies_being_compared, \ |
| value, aggregates_being_compared, \ |
| update_canonical_dies_on_the_fly); \ |
| } \ |
| while(false) |
| |
| /// A macro used to return the "false" boolean from DIE comparison |
| /// routines. |
| /// |
| /// As the return value is false, the macro invokes the |
| /// notify_die_comparison_failed signalling function before returning. |
| /// |
| /// @param value the value to return from the DIE comparison routines. |
| #define ABG_RETURN_FALSE \ |
| do \ |
| { \ |
| NOTIFY_DIE_COMPARISON_FAILED(l, r); \ |
| return return_comparison_result(l, r, dies_being_compared, \ |
| COMPARISON_RESULT_DIFFERENT, \ |
| aggregates_being_compared, \ |
| update_canonical_dies_on_the_fly); \ |
| } while(false) |
| |
| /// A macro to set the 'result' variable to 'false'. |
| /// |
| /// The macro invokes the notify_die_comparison_failed function so |
| /// that the hacker can set a debugging breakpoint on |
| /// notify_die_comparison_failed to know where a DIE comparison failed |
| /// during compare_dies_during_canonicalization for instance. |
| /// |
| /// @param result the 'result' variable to set. |
| /// |
| /// @param l the first DIE of the comparison operation. |
| /// |
| /// @param r the second DIE of the comparison operation. |
| #define SET_RESULT_TO_FALSE(result, l , r) \ |
| do \ |
| { \ |
| result = COMPARISON_RESULT_DIFFERENT; \ |
| NOTIFY_DIE_COMPARISON_FAILED(l, r); \ |
| } while(false) |
| |
| /// A macro to set the 'result' variable to a given value. |
| /// |
| /// If the value equals to COMPARISON_RESULT_DIFFERENT, then the macro |
| /// invokes the notify_die_comparison_failed function so that the |
| /// hacker can set a debugging breakpoint on |
| /// notify_die_comparison_failed to know where a DIE comparison failed |
| /// during compare_dies_during_canonicalization for instance. |
| /// |
| /// @param result the 'result' variable to set. |
| /// |
| /// @param l the first DIE of the comparison operation. |
| /// |
| /// @param r the second DIE of the comparison operation. |
| #define SET_RESULT_TO(result, value, l , r) \ |
| do \ |
| { \ |
| result = (value); \ |
| if (result == COMPARISON_RESULT_DIFFERENT) \ |
| { \ |
| NOTIFY_DIE_COMPARISON_FAILED(l, r); \ |
| } \ |
| } while(false) |
| |
| #define RETURN_IF_COMPARISON_CYCLE_DETECTED \ |
| do \ |
| { \ |
| if (aggregates_being_compared.contains(dies_being_compared)) \ |
| { \ |
| result = COMPARISON_RESULT_CYCLE_DETECTED; \ |
| aggregates_being_compared.record_redundant_type_die_pair(dies_being_compared); \ |
| ABG_RETURN(result); \ |
| } \ |
| } \ |
| while(false) |
| |
| /// Get the next member sibling of a given class or union member DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param member out parameter. This is set to the next member |
| /// sibling, iff the function returns TRUE. |
| /// |
| /// @return TRUE iff the function set @p member to the next member |
| /// sibling DIE. |
| static bool |
| get_next_member_sibling_die(const Dwarf_Die *die, Dwarf_Die *member) |
| { |
| if (!die) |
| return false; |
| |
| bool found_member = false; |
| for (found_member = (dwarf_siblingof(const_cast<Dwarf_Die*>(die), |
| member) == 0); |
| found_member; |
| found_member = (dwarf_siblingof(member, member) == 0)) |
| { |
| int tag = dwarf_tag(member); |
| if (tag == DW_TAG_member || tag == DW_TAG_inheritance) |
| break; |
| } |
| |
| return found_member; |
| } |
| |
| /// Get the first child DIE of a class/struct/union DIE that is a |
| /// member DIE. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param child out parameter. This is set to the first child DIE of |
| /// @p iff this function returns TRUE. |
| /// |
| /// @return TRUE iff @p child is set to the first child DIE of @p die |
| /// that is a member DIE. |
| static bool |
| get_member_child_die(const Dwarf_Die *die, Dwarf_Die *child) |
| { |
| if (!die) |
| return false; |
| |
| int tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| ABG_ASSERT(tag == DW_TAG_structure_type |
| || tag == DW_TAG_union_type |
| || tag == DW_TAG_class_type); |
| |
| bool found_child = (dwarf_child(const_cast<Dwarf_Die*>(die), |
| child) == 0); |
| |
| if (!found_child) |
| return false; |
| |
| tag = dwarf_tag(child); |
| |
| if (!(tag == DW_TAG_member |
| || tag == DW_TAG_inheritance |
| || tag == DW_TAG_subprogram)) |
| found_child = get_next_member_sibling_die(child, child); |
| |
| return found_child; |
| } |
| |
| /// This is a sub-routine of return_comparison_result. |
| /// |
| /// Propagate the canonical type of a the right-hand-side DIE to the |
| /// lef-hand-side DIE. This is a optimization that is done when the |
| /// two DIEs compare equal. |
| /// |
| /// If the right-hand-side DIE is not canonicalized, the function |
| /// performs its canonicalization. |
| /// |
| /// This optimization is performed only if |
| /// is_canon_type_to_be_propagated_tag returns true. |
| /// |
| /// @param rdr the current context to consider. |
| /// |
| /// @param l the left-hand-side DIE of the comparison. It's going to |
| /// receive the canonical type of the other DIE. |
| /// |
| /// @param r the right-hand-side DIE of the comparison. Its canonical |
| /// type is propagated to @p l. |
| static void |
| maybe_propagate_canonical_type(const reader& rdr, |
| const Dwarf_Die* l, |
| const Dwarf_Die* r) |
| { |
| int l_tag = dwarf_tag(const_cast<Dwarf_Die*>(l)), |
| r_tag = dwarf_tag(const_cast<Dwarf_Die*>(r)); |
| |
| if (l_tag != r_tag) |
| return; |
| |
| if (is_canon_type_to_be_propagated_tag(l_tag)) |
| propagate_canonical_type(rdr, l, r); |
| } |
| |
| /// Propagate the canonical type of a the right-hand-side DIE to the |
| /// left-hand-side DIE. This is a optimization that is done when the |
| /// two DIEs compare equal. |
| /// |
| /// If the right-hand-side DIE is not canonicalized, the function |
| /// performs its canonicalization. |
| /// |
| /// @param rdr the current context to consider. |
| /// |
| /// @param l the left-hand-side DIE of the comparison. It's going to |
| /// receive the canonical type of the other DIE. |
| /// |
| /// @param r the right-hand-side DIE of the comparison. Its canonical |
| /// type is propagated to @p l. |
| static void |
| propagate_canonical_type(const reader& rdr, |
| const Dwarf_Die* l, |
| const Dwarf_Die* r) |
| { |
| ABG_ASSERT(l && r); |
| |
| // If 'l' has no canonical DIE and if 'r' has one, then propagage |
| // the canonical DIE of 'r' to 'l'. |
| // |
| // In case 'r' has no canonical DIE, then compute it, and then |
| // propagate that canonical DIE to 'r'. |
| const die_source l_source = rdr.get_die_source(l); |
| const die_source r_source = rdr.get_die_source(r); |
| |
| Dwarf_Off l_offset = dwarf_dieoffset(const_cast<Dwarf_Die*>(l)); |
| Dwarf_Off r_offset = dwarf_dieoffset(const_cast<Dwarf_Die*>(r)); |
| bool l_has_canonical_die_offset = false; |
| bool r_has_canonical_die_offset = false; |
| Dwarf_Off l_canonical_die_offset = 0; |
| Dwarf_Off r_canonical_die_offset = 0; |
| |
| l_has_canonical_die_offset = |
| (l_canonical_die_offset = |
| rdr.get_canonical_die_offset(l_offset, l_source, |
| /*die_as_type=*/true)); |
| |
| r_has_canonical_die_offset = |
| (r_canonical_die_offset = |
| rdr.get_canonical_die_offset(r_offset, r_source, |
| /*die_as_type=*/true)); |
| |
| |
| if (!l_has_canonical_die_offset |
| && r_has_canonical_die_offset |
| // A DIE can be equivalent only to another DIE of the same |
| // source. |
| && l_source == r_source) |
| { |
| ABG_ASSERT(r_canonical_die_offset); |
| rdr.set_canonical_die_offset(l, r_canonical_die_offset, |
| /*die_as_type=*/true); |
| offset_type l_off = {l_source, l_offset}, r_off = {r_source, r_offset}; |
| rdr.propagated_types_.insert(std::make_pair(l_off,r_off)); |
| rdr.canonical_propagated_count_++; |
| } |
| } |
| |
| /// This function does the book keeping of comparison pairs necessary |
| /// to handle |
| /// |
| /// * the detection of cycles during the comparison of aggregate |
| /// types, in conjuction with the macro |
| /// RETURN_IF_COMPARISON_CYCLE_DETECTED |
| /// |
| /// * the handling of the canonical type propagation optimisation |
| /// to speed-up type canonicalization. |
| /// |
| /// |
| /// Note that this function is essentially a sub-routine of |
| /// compare_dies. |
| /// |
| /// @param l the left-hand-side DIE being compared. |
| /// |
| /// @param r the right-hand-side DIE being compared. |
| /// |
| /// @param cur_dies the pair of die offsets of l and r. This is |
| /// redundant as it can been computed from @p l and @p r. However, |
| /// getting it as an argument is an optimization to avoid computing it |
| /// over and over again, given how often this function is invoked from |
| /// compare_dies. |
| /// |
| /// @param return the result of comparing @p l against @p r. |
| /// |
| /// @param comparison_stack the stack of pair of type DIEs being |
| /// compared. |
| /// |
| /// @param do_propagate_canonical_type if true then the function |
| /// performs canonical DIEs propagation, meaning that if @p l equals |
| /// @p r and if @p r has a canonical type, then the canonical type of |
| /// @p l is set to the canonical type of @p r. |
| static comparison_result |
| return_comparison_result(const Dwarf_Die* l, |
| const Dwarf_Die* r, |
| const offset_pair_type& cur_dies, |
| comparison_result result, |
| offset_pairs_stack_type& comparison_stack, |
| bool do_propagate_canonical_type = true) |
| { |
| int l_tag = dwarf_tag(const_cast<Dwarf_Die*>(l)); |
| |
| if (result == COMPARISON_RESULT_EQUAL) |
| { |
| // The result comparing the two types is "true", basically. So |
| // let's propagate the canonical type of r onto l, so that we |
| // don't need to compute the canonical type of r. |
| if (do_propagate_canonical_type) |
| { |
| // Propagate canonical type. |
| maybe_propagate_canonical_type(comparison_stack.rdr_, l, r); |
| |
| // TODO: do we need to confirm any tentative canonical |
| // propagation? |
| } |
| } |
| else if (result == COMPARISON_RESULT_CYCLE_DETECTED) |
| { |
| // So upon detection of the comparison cycle, compare_dies |
| // returned early with the comparison result |
| // COMPARISON_RESULT_CYCLE_DETECTED, signalling us that we must |
| // carry on with the comparison of all the OTHER sub-types of |
| // the redundant type. If they all compare equal, then it means |
| // the redundant type pair compared equal. Otherwise, it |
| // compared different. |
| //ABG_ASSERT(comparison_stack.contains(l_offset, r_offset)); |
| // Let's fall through to let the end of this function set the |
| // result to COMPARISON_RESULT_UNKNOWN; |
| } |
| else if (result == COMPARISON_RESULT_UNKNOWN) |
| { |
| // Here is an introductory comment describing what we are going |
| // to do in this case where the result of the comparison of the |
| // current pair of type is not "false", basically. |
| // |
| // This means that we don't yet know what the result of |
| // comparing these two types is, because one of the sub-types of |
| // the types being compared is "redundant", meaning it appears |
| // more than once in the comparison stack, so if we were to |
| // naively try to carry on with the comparison member-wise, we'd |
| // end up with an endless loop, a.k.a "comparison cycle". |
| // |
| // If the current type pair is redundant then: |
| // |
| // * This is a redundant type that has just been fully |
| // compared. In that case, all the types that depend on |
| // this redundant type and that have been tentatively |
| // canonical-type-propagated must see their canonical types |
| // "confirmed". This means that this type is going to be |
| // considered as not being redundant anymore, meaning all |
| // the types that depend on it must be updated as not being |
| // dependant on it anymore, and the type itsef must be |
| // removed from the map of redundant types. |
| // |
| // After the type's canonical-type-propagation is confirmed, |
| // the result of its comparison must also be changed into |
| // COMPARISON_RESULT_EQUAL. |
| // |
| // After that, If the current type depends on a redundant type, |
| // then propagate its canonical type AND track it as having its |
| // type being canonical-type-propagated. |
| // |
| // If the current type is not redundant however, then it must be |
| // dependant on a redundant type. If it's not dependant on a |
| // redundant type, then it must be of those types which |
| // comparisons are not tracked for cycle, probably because they |
| // are not aggregates. Otherwise, ABORT to understand why. I |
| // believe this should not happen. In any case, after that |
| // safety check is passed, we just need to return at this point. |
| |
| if (comparison_stack.is_redundant(cur_dies) |
| && comparison_stack.vect_.back() == cur_dies) |
| { |
| // We are in the case described above of a redundant type |
| // that has been fully compared. |
| maybe_propagate_canonical_type(comparison_stack.rdr_, l, r); |
| comparison_stack.confirm_canonical_propagated_type(cur_dies); |
| |
| result = COMPARISON_RESULT_EQUAL; |
| } |
| else if (is_canon_type_to_be_propagated_tag(l_tag) |
| && comparison_stack.vect_.back() == cur_dies) |
| { |
| // The current type is not redundant. So, as described in |
| // the introductory comment above, it must be dependant on a |
| // redundant type. |
| ABG_ASSERT(comparison_stack.depends_on_redundant_types(cur_dies)); |
| maybe_propagate_canonical_type(comparison_stack.rdr_, l, r); |
| // Then pass through. |
| } |
| } |
| else if (result == COMPARISON_RESULT_DIFFERENT) |
| { |
| // Here is an introductory comment describing what we are going |
| // to do in this case where the result of the comparison of the |
| // current pair of type is "false", basically. |
| // |
| // If the type pair {l,r} is redundant then cancel the |
| // canonical-type-propagation of all the dependant pairs that |
| // depends on this redundant {l, r}. This means walk the types |
| // that depends on {l, r} and cancel their |
| // canonical-propagate-type, that means remove their canonical |
| // types and mark them as not being canonically-propagated. |
| // Also, erase their cached comparison results that was likely |
| // set to COMPARISON_RESULT_UNKNOWN. |
| // |
| // Also, update the cached result for this pair, that was likely |
| // to be COMPARISON_RESULT_UNKNOWN. |
| if (comparison_stack.is_redundant(cur_dies) |
| && comparison_stack.vect_.back() == cur_dies) |
| comparison_stack.cancel_canonical_propagated_type(cur_dies); |
| } |
| else |
| { |
| // We should never reach here. |
| ABG_ASSERT_NOT_REACHED; |
| } |
| |
| if (result == COMPARISON_RESULT_CYCLE_DETECTED) |
| result = COMPARISON_RESULT_UNKNOWN; |
| else if (is_canon_type_to_be_propagated_tag(l_tag) |
| && !comparison_stack.vect_.empty() |
| && comparison_stack.vect_.back() == cur_dies) |
| //Finally pop the pair types being compared from comparison_stack |
| //iff {l,r} is on the top of the stack. If it's not, then it means |
| //we are looking at a type that was detected as a being redundant |
| //and thus hasn't been pushed to the stack yet gain. |
| comparison_stack.erase(cur_dies); |
| |
| maybe_cache_type_comparison_result(comparison_stack.rdr_, |
| l_tag, cur_dies, result); |
| |
| return result; |
| } |
| |
| /// Compare two DIEs emitted by a C compiler. |
| /// |
| /// @param rdr the DWARF reader used to load the DWARF information. |
| /// |
| /// @param l the left-hand-side argument of this comparison operator. |
| /// |
| /// @param r the righ-hand-side argument of this comparison operator. |
| /// |
| /// @param aggregates_being_compared this holds the names of the set |
| /// of aggregates being compared. It's used by the comparison |
| /// function to avoid recursing infinitely when faced with types |
| /// referencing themselves through pointers or references. By |
| /// default, just pass an empty instance of @ref istring_set_type to |
| /// it. |
| /// |
| /// @param update_canonical_dies_on_the_fly if true, when two |
| /// sub-types compare equal (during the comparison of @p l and @p r) |
| /// update their canonical type. That way, two types of the same name |
| /// are structurally compared to each other only once. So the |
| /// non-linear structural comparison of two types of the same name |
| /// only happen once. |
| /// |
| /// @return COMPARISON_RESULT_EQUAL iff @p l equals @p r. |
| static comparison_result |
| compare_dies(const reader& rdr, |
| const Dwarf_Die *l, const Dwarf_Die *r, |
| offset_pairs_stack_type& aggregates_being_compared, |
| bool update_canonical_dies_on_the_fly) |
| { |
| ABG_ASSERT(l); |
| ABG_ASSERT(r); |
| |
| const die_source l_die_source = rdr.get_die_source(l); |
| const die_source r_die_source = rdr.get_die_source(r); |
| |
| offset_type l_offset = |
| { |
| l_die_source, |
| dwarf_dieoffset(const_cast<Dwarf_Die*>(l)) |
| }; |
| |
| offset_type r_offset = |
| { |
| r_die_source, |
| dwarf_dieoffset(const_cast<Dwarf_Die*>(r)) |
| }; |
| |
| offset_pair_type dies_being_compared(l_offset, r_offset); |
| |
| int l_tag = dwarf_tag(const_cast<Dwarf_Die*>(l)), |
| r_tag = dwarf_tag(const_cast<Dwarf_Die*>(r)); |
| |
| if (l_tag != r_tag) |
| ABG_RETURN_FALSE; |
| |
| if (l_offset == r_offset) |
| return COMPARISON_RESULT_EQUAL; |
| |
| if (rdr.leverage_dwarf_factorization() |
| && (l_die_source == ALT_DEBUG_INFO_DIE_SOURCE |
| && r_die_source == ALT_DEBUG_INFO_DIE_SOURCE)) |
| if (l_offset != r_offset) |
| return COMPARISON_RESULT_DIFFERENT; |
| |
| comparison_result result = COMPARISON_RESULT_EQUAL; |
| if (maybe_get_cached_type_comparison_result(rdr, l_tag, |
| dies_being_compared, |
| result)) |
| return result; |
| |
| Dwarf_Off l_canonical_die_offset = 0, r_canonical_die_offset = 0; |
| bool l_has_canonical_die_offset = false, r_has_canonical_die_offset = false; |
| |
| // If 'l' and 'r' already have canonical DIEs, then just compare the |
| // offsets of their canonical DIEs. |
| if (is_type_die_to_be_canonicalized(l) && is_type_die_to_be_canonicalized(r)) |
| { |
| bool canonical_compare_result = false; |
| if (try_canonical_die_comparison(rdr, l_offset, r_offset, |
| l_die_source, r_die_source, |
| l_has_canonical_die_offset, |
| r_has_canonical_die_offset, |
| l_canonical_die_offset, |
| r_canonical_die_offset, |
| canonical_compare_result)) |
| { |
| comparison_result result; |
| SET_RESULT_TO(result, |
| (canonical_compare_result |
| ? COMPARISON_RESULT_EQUAL |
| : COMPARISON_RESULT_DIFFERENT), |
| l, r); |
| return result; |
| } |
| } |
| |
| |
| |
| switch (l_tag) |
| { |
| case DW_TAG_base_type: |
| case DW_TAG_string_type: |
| case DW_TAG_unspecified_type: |
| if (!compare_as_decl_and_type_dies(rdr, l, r)) |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| |
| case DW_TAG_typedef: |
| case DW_TAG_pointer_type: |
| case DW_TAG_reference_type: |
| case DW_TAG_rvalue_reference_type: |
| case DW_TAG_const_type: |
| case DW_TAG_volatile_type: |
| case DW_TAG_restrict_type: |
| { |
| if (!compare_as_type_dies(rdr, l, r)) |
| { |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| |
| bool from_the_same_tu = false; |
| if (!pointer_or_qual_die_of_anonymous_class_type(l) |
| && compare_dies_cu_decl_file(l, r, from_the_same_tu) |
| && from_the_same_tu) |
| { |
| // These two typedefs, pointer, reference, or qualified |
| // types have the same name and are defined in the same TU. |
| // They thus ought to be the same. |
| // |
| // Note that pointers, reference or qualified types to |
| // anonymous types are not taking into account here because |
| // those always need to be structurally compared. |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| } |
| |
| { |
| // No fancy optimization in this case. We need to |
| // structurally compare the two DIEs. |
| Dwarf_Die lu_type_die, ru_type_die; |
| bool lu_is_void, ru_is_void; |
| |
| lu_is_void = !die_die_attribute(l, DW_AT_type, lu_type_die); |
| ru_is_void = !die_die_attribute(r, DW_AT_type, ru_type_die); |
| |
| if (lu_is_void && ru_is_void) |
| result = COMPARISON_RESULT_EQUAL; |
| else if (lu_is_void != ru_is_void) |
| SET_RESULT_TO_FALSE(result, l, r); |
| else |
| result = compare_dies(rdr, &lu_type_die, &ru_type_die, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| } |
| break; |
| |
| case DW_TAG_enumeration_type: |
| if (!compare_as_decl_and_type_dies(rdr, l, r)) |
| SET_RESULT_TO_FALSE(result, l, r); |
| else |
| { |
| // Walk the enumerators. |
| Dwarf_Die l_enumtor, r_enumtor; |
| bool found_l_enumtor = true, found_r_enumtor = true; |
| |
| if (!at_least_one_decl_only_among_odr_relevant_dies(rdr, l, r)) |
| for (found_l_enumtor = dwarf_child(const_cast<Dwarf_Die*>(l), |
| &l_enumtor) == 0, |
| found_r_enumtor = dwarf_child(const_cast<Dwarf_Die*>(r), |
| &r_enumtor) == 0; |
| found_l_enumtor && found_r_enumtor; |
| found_l_enumtor = dwarf_siblingof(&l_enumtor, &l_enumtor) == 0, |
| found_r_enumtor = dwarf_siblingof(&r_enumtor, &r_enumtor) == 0) |
| { |
| int l_tag = dwarf_tag(&l_enumtor), r_tag = dwarf_tag(&r_enumtor); |
| if ( l_tag != r_tag) |
| { |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| |
| if (l_tag != DW_TAG_enumerator) |
| continue; |
| |
| uint64_t l_val = 0, r_val = 0; |
| die_unsigned_constant_attribute(&l_enumtor, |
| DW_AT_const_value, |
| l_val); |
| die_unsigned_constant_attribute(&r_enumtor, |
| DW_AT_const_value, |
| r_val); |
| if (l_val != r_val) |
| { |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| } |
| if (found_l_enumtor != found_r_enumtor ) |
| SET_RESULT_TO_FALSE(result, l, r); |
| } |
| break; |
| |
| case DW_TAG_structure_type: |
| case DW_TAG_union_type: |
| case DW_TAG_class_type: |
| { |
| RETURN_IF_COMPARISON_CYCLE_DETECTED; |
| |
| rdr.compare_count_++; |
| |
| if (!compare_as_decl_and_type_dies(rdr, l, r)) |
| SET_RESULT_TO_FALSE(result, l, r); |
| else if (rdr.options().assume_odr_for_cplusplus |
| && rdr.odr_is_relevant(l) |
| && rdr.odr_is_relevant(r) |
| && !die_is_anonymous(l) |
| && !die_is_anonymous(r)) |
| result = COMPARISON_RESULT_EQUAL; |
| else |
| { |
| aggregates_being_compared.add(dies_being_compared); |
| |
| Dwarf_Die l_member, r_member; |
| bool found_l_member = true, found_r_member = true; |
| |
| if (!at_least_one_decl_only_among_odr_relevant_dies(rdr, l, r)) |
| for (found_l_member = get_member_child_die(l, &l_member), |
| found_r_member = get_member_child_die(r, &r_member); |
| found_l_member && found_r_member; |
| found_l_member = get_next_member_sibling_die(&l_member, |
| &l_member), |
| found_r_member = get_next_member_sibling_die(&r_member, |
| &r_member)) |
| { |
| int l_tag = dwarf_tag(&l_member), |
| r_tag = dwarf_tag(&r_member); |
| |
| if (l_tag != r_tag) |
| { |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| |
| ABG_ASSERT(l_tag == DW_TAG_member |
| || l_tag == DW_TAG_variable |
| || l_tag == DW_TAG_inheritance |
| || l_tag == DW_TAG_subprogram); |
| |
| comparison_result local_result = |
| compare_dies(rdr, &l_member, &r_member, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| |
| if (local_result == COMPARISON_RESULT_UNKNOWN) |
| // Note that if the result of comparing any |
| // sub-type is COMPARISON_RESULT_EQUAL, just |
| // because we have at least one sub-type's |
| // comparison being COMPARISON_RESULT_UNKNOWN |
| // means that the comparison of this type will |
| // return COMPARISON_RESULT_UNKNOWN to show |
| // callers that this type (and all the types that |
| // depend on it) depends on a redundant type |
| result = local_result; |
| |
| if (local_result == COMPARISON_RESULT_DIFFERENT) |
| { |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| } |
| if (found_l_member != found_r_member) |
| { |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| } |
| } |
| break; |
| |
| case DW_TAG_array_type: |
| { |
| RETURN_IF_COMPARISON_CYCLE_DETECTED; |
| |
| aggregates_being_compared.add(dies_being_compared); |
| |
| rdr.compare_count_++; |
| |
| Dwarf_Die l_child, r_child; |
| bool found_l_child, found_r_child; |
| for (found_l_child = dwarf_child(const_cast<Dwarf_Die*>(l), |
| &l_child) == 0, |
| found_r_child = dwarf_child(const_cast<Dwarf_Die*>(r), |
| &r_child) == 0; |
| found_l_child && found_r_child; |
| found_l_child = dwarf_siblingof(&l_child, &l_child) == 0, |
| found_r_child = dwarf_siblingof(&r_child, &r_child) == 0) |
| { |
| int l_child_tag = dwarf_tag(&l_child), |
| r_child_tag = dwarf_tag(&r_child); |
| if (l_child_tag == DW_TAG_subrange_type |
| || r_child_tag == DW_TAG_subrange_type) |
| { |
| result = compare_dies(rdr, &l_child, &r_child, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| if (!result) |
| { |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| } |
| } |
| if (found_l_child != found_r_child) |
| SET_RESULT_TO_FALSE(result, l, r); |
| // Compare the types of the elements of the array. |
| Dwarf_Die ltype_die, rtype_die; |
| bool found_ltype = die_die_attribute(l, DW_AT_type, ltype_die); |
| bool found_rtype = die_die_attribute(r, DW_AT_type, rtype_die); |
| ABG_ASSERT(found_ltype && found_rtype); |
| |
| result = compare_dies(rdr, <ype_die, &rtype_die, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| if (!result) |
| ABG_RETURN_FALSE; |
| } |
| break; |
| |
| case DW_TAG_subrange_type: |
| { |
| uint64_t l_lower_bound = 0, r_lower_bound = 0, |
| l_upper_bound = 0, r_upper_bound = 0; |
| bool l_lower_bound_set = false, r_lower_bound_set = false, |
| l_upper_bound_set = false, r_upper_bound_set = false; |
| |
| l_lower_bound_set = |
| die_unsigned_constant_attribute(l, DW_AT_lower_bound, l_lower_bound); |
| r_lower_bound_set = |
| die_unsigned_constant_attribute(r, DW_AT_lower_bound, r_lower_bound); |
| |
| if (!die_unsigned_constant_attribute(l, DW_AT_upper_bound, |
| l_upper_bound)) |
| { |
| uint64_t l_count = 0; |
| if (die_unsigned_constant_attribute(l, DW_AT_count, l_count)) |
| { |
| l_upper_bound = l_lower_bound + l_count; |
| l_upper_bound_set = true; |
| if (l_upper_bound) |
| --l_upper_bound; |
| } |
| } |
| else |
| l_upper_bound_set = true; |
| |
| if (!die_unsigned_constant_attribute(r, DW_AT_upper_bound, |
| r_upper_bound)) |
| { |
| uint64_t r_count = 0; |
| if (die_unsigned_constant_attribute(l, DW_AT_count, r_count)) |
| { |
| r_upper_bound = r_lower_bound + r_count; |
| r_upper_bound_set = true; |
| if (r_upper_bound) |
| --r_upper_bound; |
| } |
| } |
| else |
| r_upper_bound_set = true; |
| |
| if ((l_lower_bound_set != r_lower_bound_set) |
| || (l_upper_bound_set != r_upper_bound_set) |
| || (l_lower_bound != r_lower_bound) |
| || (l_upper_bound != r_upper_bound)) |
| SET_RESULT_TO_FALSE(result, l, r); |
| } |
| break; |
| |
| case DW_TAG_subroutine_type: |
| case DW_TAG_subprogram: |
| { |
| RETURN_IF_COMPARISON_CYCLE_DETECTED; |
| |
| aggregates_being_compared.add(dies_being_compared); |
| |
| rdr.compare_count_++; |
| |
| if (l_tag == DW_TAG_subprogram |
| && !fn_die_equal_by_linkage_name(l, r)) |
| { |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| else if (l_tag == DW_TAG_subprogram |
| && die_is_in_c(l) && die_is_in_c(r)) |
| { |
| result = COMPARISON_RESULT_EQUAL; |
| break; |
| } |
| else if (!die_is_in_c(l) && !die_is_in_c(r)) |
| { |
| // In C, we cannot have two different functions with the |
| // same linkage name in a given binary. But here we are |
| // looking at DIEs that don't originate from C. So we |
| // need to compare return types and parameter types. |
| Dwarf_Die l_return_type, r_return_type; |
| bool l_return_type_is_void = !die_die_attribute(l, DW_AT_type, |
| l_return_type); |
| bool r_return_type_is_void = !die_die_attribute(r, DW_AT_type, |
| r_return_type); |
| if (l_return_type_is_void != r_return_type_is_void |
| || (!l_return_type_is_void |
| && !compare_dies(rdr, |
| &l_return_type, &r_return_type, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly))) |
| SET_RESULT_TO_FALSE(result, l, r); |
| else |
| { |
| Dwarf_Die l_child, r_child; |
| bool found_l_child, found_r_child; |
| for (found_l_child = dwarf_child(const_cast<Dwarf_Die*>(l), |
| &l_child) == 0, |
| found_r_child = dwarf_child(const_cast<Dwarf_Die*>(r), |
| &r_child) == 0; |
| found_l_child && found_r_child; |
| found_l_child = dwarf_siblingof(&l_child, |
| &l_child) == 0, |
| found_r_child = dwarf_siblingof(&r_child, |
| &r_child)==0) |
| { |
| int l_child_tag = dwarf_tag(&l_child); |
| int r_child_tag = dwarf_tag(&r_child); |
| comparison_result local_result = |
| COMPARISON_RESULT_EQUAL; |
| if (l_child_tag != r_child_tag) |
| local_result = COMPARISON_RESULT_DIFFERENT; |
| if (l_child_tag == DW_TAG_formal_parameter) |
| local_result = |
| compare_dies(rdr, &l_child, &r_child, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| if (local_result == COMPARISON_RESULT_DIFFERENT) |
| { |
| result = local_result; |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| if (local_result == COMPARISON_RESULT_UNKNOWN) |
| // Note that if the result of comparing any |
| // sub-type is COMPARISON_RESULT_EQUAL, just |
| // because we have at least one sub-type's |
| // comparison being COMPARISON_RESULT_UNKNOWN |
| // means that the comparison of this type will |
| // return COMPARISON_RESULT_UNKNOWN to show |
| // callers that this type (and all the types |
| // that depend on it) depends on a redundant |
| // type and so, can't be |
| // canonical-type-propagated. |
| result = local_result; |
| } |
| if (found_l_child != found_r_child) |
| { |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| } |
| } |
| } |
| } |
| break; |
| |
| case DW_TAG_formal_parameter: |
| { |
| Dwarf_Die l_type, r_type; |
| bool l_type_is_void = !die_die_attribute(l, DW_AT_type, l_type); |
| bool r_type_is_void = !die_die_attribute(r, DW_AT_type, r_type); |
| if (l_type_is_void != r_type_is_void) |
| SET_RESULT_TO_FALSE(result, l, r); |
| else if (!l_type_is_void) |
| { |
| comparison_result local_result = |
| compare_dies(rdr, &l_type, &r_type, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| SET_RESULT_TO(result, local_result, l, r); |
| } |
| } |
| break; |
| |
| case DW_TAG_variable: |
| case DW_TAG_member: |
| if (compare_as_decl_dies(l, r)) |
| { |
| // Compare the offsets of the data members |
| if (l_tag == DW_TAG_member) |
| { |
| int64_t l_offset_in_bits = 0, r_offset_in_bits = 0; |
| die_member_offset(rdr, l, l_offset_in_bits); |
| die_member_offset(rdr, r, r_offset_in_bits); |
| if (l_offset_in_bits != r_offset_in_bits) |
| SET_RESULT_TO_FALSE(result, l, r); |
| } |
| if (result) |
| { |
| // Compare the types of the data members or variables. |
| Dwarf_Die l_type, r_type; |
| ABG_ASSERT(die_die_attribute(l, DW_AT_type, l_type)); |
| ABG_ASSERT(die_die_attribute(r, DW_AT_type, r_type)); |
| comparison_result local_result = |
| compare_dies(rdr, &l_type, &r_type, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| SET_RESULT_TO(result, local_result, l, r); |
| } |
| } |
| else |
| SET_RESULT_TO_FALSE(result, l, r); |
| break; |
| |
| case DW_TAG_inheritance: |
| { |
| Dwarf_Die l_type, r_type; |
| ABG_ASSERT(die_die_attribute(l, DW_AT_type, l_type)); |
| ABG_ASSERT(die_die_attribute(r, DW_AT_type, r_type)); |
| result = compare_dies(rdr, &l_type, &r_type, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| if (!result) |
| ABG_RETURN(COMPARISON_RESULT_DIFFERENT); |
| |
| uint64_t l_a = 0, r_a = 0; |
| die_unsigned_constant_attribute(l, DW_AT_accessibility, l_a); |
| die_unsigned_constant_attribute(r, DW_AT_accessibility, r_a); |
| if (l_a != r_a) |
| ABG_RETURN(COMPARISON_RESULT_DIFFERENT); |
| |
| die_unsigned_constant_attribute(l, DW_AT_virtuality, l_a); |
| die_unsigned_constant_attribute(r, DW_AT_virtuality, r_a); |
| if (l_a != r_a) |
| ABG_RETURN(COMPARISON_RESULT_DIFFERENT); |
| |
| int64_t l_offset_in_bits = 0, r_offset_in_bits = 0; |
| die_member_offset(rdr, l, l_offset_in_bits); |
| die_member_offset(rdr, r, r_offset_in_bits); |
| if (l_offset_in_bits != r_offset_in_bits) |
| ABG_RETURN(COMPARISON_RESULT_DIFFERENT); |
| } |
| break; |
| |
| case DW_TAG_ptr_to_member_type: |
| { |
| bool comp_result = false; |
| if (compare_dies_string_attribute_value(l, r, DW_AT_name, comp_result)) |
| if (!comp_result) |
| ABG_RETURN(COMPARISON_RESULT_DIFFERENT); |
| |
| Dwarf_Die l_type, r_type; |
| ABG_ASSERT(die_die_attribute(l, DW_AT_type, l_type)); |
| ABG_ASSERT(die_die_attribute(r, DW_AT_type, r_type)); |
| result = compare_dies(rdr, &l_type, &r_type, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| if (!result) |
| ABG_RETURN(result); |
| |
| ABG_ASSERT(die_die_attribute(l, DW_AT_containing_type, l_type)); |
| ABG_ASSERT(die_die_attribute(r, DW_AT_containing_type, r_type)); |
| result = compare_dies(rdr, &l_type, &r_type, |
| aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| if (!result) |
| ABG_RETURN(result); |
| } |
| break; |
| |
| case DW_TAG_enumerator: |
| case DW_TAG_packed_type: |
| case DW_TAG_set_type: |
| case DW_TAG_file_type: |
| case DW_TAG_thrown_type: |
| case DW_TAG_interface_type: |
| case DW_TAG_shared_type: |
| case DW_TAG_compile_unit: |
| case DW_TAG_namespace: |
| case DW_TAG_module: |
| case DW_TAG_constant: |
| case DW_TAG_partial_unit: |
| case DW_TAG_imported_unit: |
| case DW_TAG_dwarf_procedure: |
| case DW_TAG_imported_declaration: |
| case DW_TAG_entry_point: |
| case DW_TAG_label: |
| case DW_TAG_lexical_block: |
| case DW_TAG_unspecified_parameters: |
| case DW_TAG_variant: |
| case DW_TAG_common_block: |
| case DW_TAG_common_inclusion: |
| case DW_TAG_inlined_subroutine: |
| case DW_TAG_with_stmt: |
| case DW_TAG_access_declaration: |
| case DW_TAG_catch_block: |
| case DW_TAG_friend: |
| case DW_TAG_namelist: |
| case DW_TAG_namelist_item: |
| case DW_TAG_template_type_parameter: |
| case DW_TAG_template_value_parameter: |
| case DW_TAG_try_block: |
| case DW_TAG_variant_part: |
| case DW_TAG_imported_module: |
| case DW_TAG_condition: |
| case DW_TAG_type_unit: |
| case DW_TAG_template_alias: |
| case DW_TAG_lo_user: |
| case DW_TAG_MIPS_loop: |
| case DW_TAG_format_label: |
| case DW_TAG_function_template: |
| case DW_TAG_class_template: |
| case DW_TAG_GNU_BINCL: |
| case DW_TAG_GNU_EINCL: |
| case DW_TAG_GNU_template_template_param: |
| case DW_TAG_GNU_template_parameter_pack: |
| case DW_TAG_GNU_formal_parameter_pack: |
| case DW_TAG_GNU_call_site: |
| case DW_TAG_GNU_call_site_parameter: |
| case DW_TAG_hi_user: |
| #ifdef WITH_DEBUG_TYPE_CANONICALIZATION |
| if (rdr.debug_die_canonicalization_is_on_) |
| ABG_ASSERT_NOT_REACHED; |
| #endif |
| ABG_ASSERT_NOT_REACHED; |
| break; |
| } |
| |
| ABG_RETURN(result); |
| } |
| |
| /// Compare two DIEs emitted by a C compiler. |
| /// |
| /// @param rdr the DWARF reader used to load the DWARF information. |
| /// |
| /// @param l the left-hand-side argument of this comparison operator. |
| /// |
| /// @param r the righ-hand-side argument of this comparison operator. |
| /// |
| /// @param update_canonical_dies_on_the_fly if yes, then this function |
| /// updates the canonical DIEs of sub-type DIEs of 'l' and 'r', while |
| /// comparing l and r. This helps in making so that sub-type DIEs of |
| /// 'l' and 'r' are compared structurally only once. This is how we |
| /// turn this exponential comparison problem into a problem that is a |
| /// closer to a linear one. |
| /// |
| /// @return COMPARISON_RESULT_EQUAL iff @p l equals @p r. |
| static comparison_result |
| compare_dies(const reader& rdr, |
| const Dwarf_Die *l, |
| const Dwarf_Die *r, |
| bool update_canonical_dies_on_the_fly) |
| { |
| offset_pairs_stack_type aggregates_being_compared(rdr); |
| return compare_dies(rdr, l, r, aggregates_being_compared, |
| update_canonical_dies_on_the_fly); |
| } |
| |
| /// Compare two DIEs for the purpose of canonicalization. |
| /// |
| /// This is a sub-routine of reader::get_canonical_die. |
| /// |
| /// When DIE canonicalization debugging is on, this function performs |
| /// both structural and canonical comparison. It expects that both |
| /// comparison yield the same result. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param l the left-hand-side comparison operand DIE. |
| /// |
| /// @param r the right-hand-side comparison operand DIE. |
| /// |
| /// @param update_canonical_dies_on_the_fly if true, then some |
| /// aggregate DIEs will see their canonical types propagated. |
| /// |
| /// @return true iff @p l equals @p r. |
| static bool |
| compare_dies_during_canonicalization(reader& rdr, |
| const Dwarf_Die *l, |
| const Dwarf_Die *r, |
| bool update_canonical_dies_on_the_fly) |
| { |
| #ifdef WITH_DEBUG_TYPE_CANONICALIZATION |
| if (rdr.debug_die_canonicalization_is_on_) |
| { |
| bool canonical_equality = false, structural_equality = false; |
| rdr.use_canonical_die_comparison_ = false; |
| structural_equality = compare_dies(rdr, l, r, |
| /*update_canonical_dies_on_the_fly=*/false); |
| rdr.use_canonical_die_comparison_ = true; |
| canonical_equality = compare_dies(rdr, l, r, |
| update_canonical_dies_on_the_fly); |
| if (canonical_equality != structural_equality) |
| { |
| std::cerr << "structural & canonical equality different for DIEs: " |
| << std::hex |
| << "l: " << dwarf_dieoffset(const_cast<Dwarf_Die*>(l)) |
| << ", r: " << dwarf_dieoffset(const_cast<Dwarf_Die*>(r)) |
| << std::dec |
| << ", repr: '" |
| << rdr.get_die_pretty_type_representation(l, 0) |
| << "'" |
| << std::endl; |
| ABG_ASSERT_NOT_REACHED; |
| } |
| return structural_equality; |
| } |
| #endif |
| return compare_dies(rdr, l, r, |
| update_canonical_dies_on_the_fly); |
| } |
| |
| // ---------------------------------- |
| // </die comparison engine> |
| // --------------------------------- |
| |
| /// Get the point where a DW_AT_import DIE is used to import a given |
| /// (unit) DIE, between two DIEs. |
| /// |
| /// @param rdr the dwarf reader to consider. |
| /// |
| /// @param partial_unit_offset the imported unit for which we want to |
| /// know the insertion point. This is usually a partial unit (with |
| /// tag DW_TAG_partial_unit) but it does not necessarily have to be |
| /// so. |
| /// |
| /// @param first_die_offset the offset of the DIE from which this |
| /// function starts looking for the import point of |
| /// @partial_unit_offset. Note that this offset is excluded from the |
| /// set of potential solutions. |
| /// |
| /// @param first_die_cu_offset the offset of the (compilation) unit |
| /// that @p first_die_cu_offset belongs to. |
| /// |
| /// @param source where the DIE of first_die_cu_offset unit comes |
| /// from. |
| /// |
| /// @param last_die_offset the offset of the last DIE of the up to |
| /// which this function looks for the import point of @p |
| /// partial_unit_offset. Note that this offset is excluded from the |
| /// set of potential solutions. |
| /// |
| /// @param imported_point_offset. The resulting |
| /// imported_point_offset. Note that if the imported DIE @p |
| /// partial_unit_offset is not found between @p first_die_offset and |
| /// @p last_die_offset, this parameter is left untouched by this |
| /// function. |
| /// |
| /// @return true iff an imported unit is found between @p |
| /// first_die_offset and @p last_die_offset. |
| static bool |
| find_import_unit_point_between_dies(const reader& rdr, |
| size_t partial_unit_offset, |
| Dwarf_Off first_die_offset, |
| Dwarf_Off first_die_cu_offset, |
| die_source source, |
| size_t last_die_offset, |
| size_t& imported_point_offset) |
| { |
| const tu_die_imported_unit_points_map_type& tu_die_imported_unit_points_map = |
| rdr.tu_die_imported_unit_points_map(source); |
| |
| tu_die_imported_unit_points_map_type::const_iterator iter = |
| tu_die_imported_unit_points_map.find(first_die_cu_offset); |
| |
| ABG_ASSERT(iter != tu_die_imported_unit_points_map.end()); |
| |
| const imported_unit_points_type& imported_unit_points = iter->second; |
| if (imported_unit_points.empty()) |
| return false; |
| |
| imported_unit_points_type::const_iterator b = imported_unit_points.begin(); |
| imported_unit_points_type::const_iterator e = imported_unit_points.end(); |
| |
| find_lower_bound_in_imported_unit_points(imported_unit_points, |
| first_die_offset, |
| b); |
| |
| if (last_die_offset != static_cast<size_t>(-1)) |
| find_lower_bound_in_imported_unit_points(imported_unit_points, |
| last_die_offset, |
| e); |
| |
| if (e != imported_unit_points.end()) |
| { |
| for (imported_unit_points_type::const_iterator i = e; i >= b; --i) |
| if (i->imported_unit_die_off == partial_unit_offset) |
| { |
| imported_point_offset = i->offset_of_import ; |
| return true; |
| } |
| |
| for (imported_unit_points_type::const_iterator i = e; i >= b; --i) |
| { |
| if (find_import_unit_point_between_dies(rdr, |
| partial_unit_offset, |
| i->imported_unit_child_off, |
| i->imported_unit_cu_off, |
| i->imported_unit_die_source, |
| /*(Dwarf_Off)*/-1, |
| imported_point_offset)) |
| return true; |
| } |
| } |
| else |
| { |
| for (imported_unit_points_type::const_iterator i = b; i != e; ++i) |
| if (i->imported_unit_die_off == partial_unit_offset) |
| { |
| imported_point_offset = i->offset_of_import ; |
| return true; |
| } |
| |
| for (imported_unit_points_type::const_iterator i = b; i != e; ++i) |
| { |
| if (find_import_unit_point_between_dies(rdr, |
| partial_unit_offset, |
| i->imported_unit_child_off, |
| i->imported_unit_cu_off, |
| i->imported_unit_die_source, |
| /*(Dwarf_Off)*/-1, |
| imported_point_offset)) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /// In the current translation unit, get the last point where a |
| /// DW_AT_import DIE is used to import a given (unit) DIE, before a |
| /// given DIE is found. That given DIE is called the limit DIE. |
| /// |
| /// Said otherwise, this function returns the last import point of a |
| /// unit, before a limit. |
| /// |
| /// @param rdr the dwarf reader to consider. |
| /// |
| /// @param partial_unit_offset the imported unit for which we want to |
| /// know the insertion point of. This is usually a partial unit (with |
| /// tag DW_TAG_partial_unit) but it does not necessarily have to be |
| /// so. |
| /// |
| /// @param where_offset the offset of the limit DIE. |
| /// |
| /// @param imported_point_offset. The resulting imported_point_offset. |
| /// Note that if the imported DIE @p partial_unit_offset is not found |
| /// before @p die_offset, this is set to the last @p |
| /// partial_unit_offset found under @p parent_die. |
| /// |
| /// @return true iff an imported unit is found before @p die_offset. |
| /// Note that if an imported unit is found after @p die_offset then @p |
| /// imported_point_offset is set and the function return false. |
| static bool |
| find_import_unit_point_before_die(const reader& rdr, |
| size_t partial_unit_offset, |
| size_t where_offset, |
| size_t& imported_point_offset) |
| { |
| size_t import_point_offset = 0; |
| Dwarf_Die first_die_of_tu; |
| |
| if (dwarf_child(const_cast<Dwarf_Die*>(rdr.cur_tu_die()), |
| &first_die_of_tu) != 0) |
| return false; |
| |
| Dwarf_Die cu_die_memory; |
| Dwarf_Die *cu_die; |
| |
| cu_die = dwarf_diecu(const_cast<Dwarf_Die*>(&first_die_of_tu), |
| &cu_die_memory, 0, 0); |
| |
| if (find_import_unit_point_between_dies(rdr, partial_unit_offset, |
| dwarf_dieoffset(&first_die_of_tu), |
| dwarf_dieoffset(cu_die), |
| /*source=*/PRIMARY_DEBUG_INFO_DIE_SOURCE, |
| where_offset, |
| import_point_offset)) |
| { |
| imported_point_offset = import_point_offset; |
| return true; |
| } |
| |
| if (import_point_offset) |
| { |
| imported_point_offset = import_point_offset; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Return the parent DIE for a given DIE. |
| /// |
| /// Note that the function build_die_parent_map() must have been |
| /// called before this one can work. This function either succeeds or |
| /// aborts the current process. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE for which we want the parent. |
| /// |
| /// @param parent_die the output parameter set to the parent die of |
| /// @p die. Its memory must be allocated and handled by the caller. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return true if the function could get a parent DIE, false |
| /// otherwise. |
| static bool |
| get_parent_die(const reader& rdr, |
| const Dwarf_Die* die, |
| Dwarf_Die& parent_die, |
| size_t where_offset) |
| { |
| ABG_ASSERT(rdr.dwarf_debug_info()); |
| |
| const die_source source = rdr.get_die_source(die); |
| |
| const offset_offset_map_type& m = rdr.die_parent_map(source); |
| offset_offset_map_type::const_iterator i = |
| m.find(dwarf_dieoffset(const_cast<Dwarf_Die*>(die))); |
| |
| if (i == m.end()) |
| return false; |
| |
| switch (source) |
| { |
| case PRIMARY_DEBUG_INFO_DIE_SOURCE: |
| ABG_ASSERT(dwarf_offdie(const_cast<Dwarf*>(rdr.dwarf_debug_info()), |
| i->second, &parent_die)); |
| break; |
| case ALT_DEBUG_INFO_DIE_SOURCE: |
| ABG_ASSERT(dwarf_offdie(const_cast<Dwarf*>(rdr.alternate_dwarf_debug_info()), |
| i->second, &parent_die)); |
| break; |
| case TYPE_UNIT_DIE_SOURCE: |
| ABG_ASSERT(dwarf_offdie_types(const_cast<Dwarf*>(rdr.dwarf_debug_info()), |
| i->second, &parent_die)); |
| break; |
| case NO_DEBUG_INFO_DIE_SOURCE: |
| case NUMBER_OF_DIE_SOURCES: |
| ABG_ASSERT_NOT_REACHED; |
| } |
| |
| if (dwarf_tag(&parent_die) == DW_TAG_partial_unit) |
| { |
| if (where_offset == 0) |
| { |
| parent_die = *rdr.cur_tu_die(); |
| return true; |
| } |
| size_t import_point_offset = 0; |
| bool found = |
| find_import_unit_point_before_die(rdr, |
| dwarf_dieoffset(&parent_die), |
| where_offset, |
| import_point_offset); |
| if (!found) |
| // It looks like parent_die (which comes from the alternate |
| // debug info file) hasn't been imported into this TU. So, |
| // Let's assume its logical parent is the DIE of the current |
| // TU. |
| parent_die = *rdr.cur_tu_die(); |
| else |
| { |
| ABG_ASSERT(import_point_offset); |
| Dwarf_Die import_point_die; |
| ABG_ASSERT(dwarf_offdie(const_cast<Dwarf*>(rdr.dwarf_debug_info()), |
| import_point_offset, |
| &import_point_die)); |
| return get_parent_die(rdr, &import_point_die, |
| parent_die, where_offset); |
| } |
| } |
| |
| return true; |
| } |
| |
| /// Get the DIE representing the scope of a given DIE. |
| /// |
| /// Please note that when the DIE we are looking at has a |
| /// DW_AT_specification or DW_AT_abstract_origin attribute, the scope |
| /// DIE is the parent DIE of the DIE referred to by that attribute. |
| /// In other words, this function returns the scope of the origin DIE |
| /// of the current DIE. |
| /// |
| /// So, the scope DIE can be different from the parent DIE of a given |
| /// DIE. |
| /// |
| /// Also note that if the current translation unit is from C, then |
| /// this returns the global scope. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param dye the DIE to consider. |
| /// |
| /// @param where_offset where we are logically at in the DIE stream. |
| /// |
| /// @param scope_die out parameter. This is set to the resulting |
| /// scope DIE iff the function returns true. |
| /// |
| /// @return true iff the scope was found and returned in the @p |
| /// scope_die parameter. |
| static bool |
| get_scope_die(const reader& rdr, |
| const Dwarf_Die* dye, |
| size_t where_offset, |
| Dwarf_Die& scope_die) |
| { |
| Dwarf_Die origin_die_mem; |
| Dwarf_Die *die = &origin_die_mem; |
| if (!die_origin_die(dye, origin_die_mem)) |
| memcpy(&origin_die_mem, dye, sizeof(origin_die_mem)); |
| |
| translation_unit::language die_lang = translation_unit::LANG_UNKNOWN; |
| get_die_language(die, die_lang); |
| if (is_c_language(die_lang) |
| || rdr.die_parent_map(rdr.get_die_source(die)).empty()) |
| { |
| ABG_ASSERT(dwarf_tag(const_cast<Dwarf_Die*>(die)) != DW_TAG_member); |
| return dwarf_diecu(const_cast<Dwarf_Die*>(die), &scope_die, 0, 0); |
| } |
| |
| if (!get_parent_die(rdr, die, scope_die, where_offset)) |
| return false; |
| |
| if (dwarf_tag(&scope_die) == DW_TAG_subprogram |
| || dwarf_tag(&scope_die) == DW_TAG_subroutine_type |
| || dwarf_tag(&scope_die) == DW_TAG_array_type) |
| return get_scope_die(rdr, &scope_die, where_offset, scope_die); |
| |
| return true; |
| } |
| |
| /// Return the abigail IR node representing the scope of a given DIE. |
| /// |
| /// Note that it is the logical scope that is returned. That is, if |
| /// the DIE has a DW_AT_specification or DW_AT_abstract_origin |
| /// attribute, it's the scope of the referred-to DIE (via these |
| /// attributes) that is returned. In other words, its the scope of |
| /// the origin DIE that is returned. |
| /// |
| /// Also note that if the current translation unit is from C, then |
| /// this returns the global scope. |
| /// |
| /// @param rdr the dwarf reader to use. |
| /// |
| /// @param dye the DIE to get the scope for. |
| /// |
| /// @param called_from_public_decl is true if this function has been |
| /// initially called within the context of a public decl. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return the resulting scope, or nil if could not be computed. |
| static scope_decl_sptr |
| get_scope_for_die(reader& rdr, |
| Dwarf_Die* dye, |
| bool called_for_public_decl, |
| size_t where_offset) |
| { |
| Dwarf_Die origin_die_mem; |
| Dwarf_Die *die = &origin_die_mem; |
| |
| if (!die_origin_die(dye, origin_die_mem)) |
| // There was no origin DIE found, so let's make the "die" pointer |
| // above point to the content of the input "dye". |
| memcpy(&origin_die_mem, dye, sizeof(origin_die_mem)); |
| |
| const die_source source_of_die = rdr.get_die_source(die); |
| |
| translation_unit::language die_lang = translation_unit::LANG_UNKNOWN; |
| get_die_language(die, die_lang); |
| if (is_c_language(die_lang) |
| || rdr.die_parent_map(source_of_die).empty()) |
| { |
| // In units for the C languages all decls belong to the global |
| // namespace. This is generally the case if Libabigail |
| // determined that no DIE -> parent map was needed. |
| ABG_ASSERT(dwarf_tag(die) != DW_TAG_member); |
| return rdr.global_scope(); |
| } |
| |
| Dwarf_Die parent_die; |
| |
| if (!get_parent_die(rdr, die, parent_die, where_offset)) |
| return rdr.nil_scope(); |
| |
| if (dwarf_tag(&parent_die) == DW_TAG_compile_unit |
| || dwarf_tag(&parent_die) == DW_TAG_partial_unit |
| || dwarf_tag(&parent_die) == DW_TAG_type_unit) |
| { |
| if (dwarf_tag(&parent_die) == DW_TAG_partial_unit |
| || dwarf_tag(&parent_die) == DW_TAG_type_unit) |
| { |
| ABG_ASSERT(source_of_die == ALT_DEBUG_INFO_DIE_SOURCE |
| || source_of_die == TYPE_UNIT_DIE_SOURCE); |
| return rdr.cur_transl_unit()->get_global_scope(); |
| } |
| |
| // For top level DIEs like DW_TAG_compile_unit, we just want to |
| // return the global scope for the corresponding translation |
| // unit. This must have been set by |
| // build_translation_unit_and_add_to_ir if we already started to |
| // build the translation unit of parent_die. Otherwise, just |
| // return the global scope of the current translation unit. |
| die_tu_map_type::const_iterator i = |
| rdr.die_tu_map().find(dwarf_dieoffset(&parent_die)); |
| if (i != rdr.die_tu_map().end()) |
| return i->second->get_global_scope(); |
| return rdr.cur_transl_unit()->get_global_scope(); |
| } |
| |
| scope_decl_sptr s; |
| type_or_decl_base_sptr d; |
| if (dwarf_tag(&parent_die) == DW_TAG_subprogram |
| || dwarf_tag(&parent_die) == DW_TAG_array_type |
| || dwarf_tag(&parent_die) == DW_TAG_lexical_block) |
| // this is an entity defined in a scope that is a function. |
| // Normally, I would say that this should be dropped. But I have |
| // seen a case where a typedef DIE needed by a function parameter |
| // was defined right before the parameter, under the scope of the |
| // function. Yeah, weird. So if I drop the typedef DIE, I'd drop |
| // the function parm too. So for that case, let's say that the |
| // scope is the scope of the function itself. Note that this is |
| // an error of the DWARF emitter. We should never see this DIE in |
| // this context. |
| { |
| scope_decl_sptr s = get_scope_for_die(rdr, &parent_die, |
| called_for_public_decl, |
| where_offset); |
| if (is_anonymous_type_die(die)) |
| // For anonymous type that have nothing to do in a function or |
| // array type context, let's put it in the containing |
| // namespace. That is, do not let it be in a containing class |
| // or union where it has nothing to do. |
| while (is_class_or_union_type(s)) |
| { |
| if (!get_parent_die(rdr, &parent_die, parent_die, where_offset)) |
| return rdr.nil_scope(); |
| s = get_scope_for_die(rdr, &parent_die, |
| called_for_public_decl, |
| where_offset); |
| } |
| return s; |
| } |
| else |
| d = build_ir_node_from_die(rdr, &parent_die, |
| called_for_public_decl, |
| where_offset); |
| s = dynamic_pointer_cast<scope_decl>(d); |
| if (!s) |
| // this is an entity defined in someting that is not a scope. |
| // Let's drop it. |
| return rdr.nil_scope(); |
| |
| class_decl_sptr cl = dynamic_pointer_cast<class_decl>(d); |
| if (cl && cl->get_is_declaration_only()) |
| { |
| scope_decl_sptr scop = |
| dynamic_pointer_cast<scope_decl>(cl->get_definition_of_declaration()); |
| if (scop) |
| s = scop; |
| else |
| s = cl; |
| } |
| return s; |
| } |
| |
| /// Convert a DWARF constant representing the value of the |
| /// DW_AT_language property into the translation_unit::language |
| /// enumerator. |
| /// |
| /// @param l the DWARF constant to convert. |
| /// |
| /// @return the resulting translation_unit::language enumerator. |
| static translation_unit::language |
| dwarf_language_to_tu_language(size_t l) |
| { |
| switch (l) |
| { |
| case DW_LANG_C89: |
| return translation_unit::LANG_C89; |
| case DW_LANG_C: |
| return translation_unit::LANG_C; |
| case DW_LANG_Ada83: |
| return translation_unit::LANG_Ada83; |
| case DW_LANG_C_plus_plus: |
| return translation_unit::LANG_C_plus_plus; |
| case DW_LANG_Cobol74: |
| return translation_unit::LANG_Cobol74; |
| case DW_LANG_Cobol85: |
| return translation_unit::LANG_Cobol85; |
| case DW_LANG_Fortran77: |
| return translation_unit::LANG_Fortran77; |
| case DW_LANG_Fortran90: |
| return translation_unit::LANG_Fortran90; |
| case DW_LANG_Pascal83: |
| return translation_unit::LANG_Pascal83; |
| case DW_LANG_Modula2: |
| return translation_unit::LANG_Modula2; |
| case DW_LANG_Java: |
| return translation_unit::LANG_Java; |
| case DW_LANG_C99: |
| return translation_unit::LANG_C99; |
| case DW_LANG_Ada95: |
| return translation_unit::LANG_Ada95; |
| case DW_LANG_Fortran95: |
| return translation_unit::LANG_Fortran95; |
| case DW_LANG_PLI: |
| return translation_unit::LANG_PLI; |
| case DW_LANG_ObjC: |
| return translation_unit::LANG_ObjC; |
| case DW_LANG_ObjC_plus_plus: |
| return translation_unit::LANG_ObjC_plus_plus; |
| |
| #ifdef HAVE_DW_LANG_Rust_enumerator |
| case DW_LANG_Rust: |
| return translation_unit::LANG_Rust; |
| #endif |
| |
| #ifdef HAVE_DW_LANG_UPC_enumerator |
| case DW_LANG_UPC: |
| return translation_unit::LANG_UPC; |
| #endif |
| |
| #ifdef HAVE_DW_LANG_D_enumerator |
| case DW_LANG_D: |
| return translation_unit::LANG_D; |
| #endif |
| |
| #ifdef HAVE_DW_LANG_Python_enumerator |
| case DW_LANG_Python: |
| return translation_unit::LANG_Python; |
| #endif |
| |
| #ifdef HAVE_DW_LANG_Go_enumerator |
| case DW_LANG_Go: |
| return translation_unit::LANG_Go; |
| #endif |
| |
| #ifdef HAVE_DW_LANG_C11_enumerator |
| case DW_LANG_C11: |
| return translation_unit::LANG_C11; |
| #endif |
| |
| #ifdef HAVE_DW_LANG_C_plus_plus_03_enumerator |
| case DW_LANG_C_plus_plus_03: |
| return translation_unit::LANG_C_plus_plus_03; |
| #endif |
| |
| #ifdef HAVE_DW_LANG_C_plus_plus_11_enumerator |
| case DW_LANG_C_plus_plus_11: |
| return translation_unit::LANG_C_plus_plus_11; |
| #endif |
| |
| #ifdef HAVE_DW_LANG_C_plus_plus_14_enumerator |
| case DW_LANG_C_plus_plus_14: |
| return translation_unit::LANG_C_plus_plus_14; |
| #endif |
| |
| #ifdef HAVE_DW_LANG_Mips_Assembler_enumerator |
| case DW_LANG_Mips_Assembler: |
| return translation_unit::LANG_Mips_Assembler; |
| #endif |
| |
| default: |
| return translation_unit::LANG_UNKNOWN; |
| } |
| } |
| |
| /// Get the default array lower bound value as defined by the DWARF |
| /// specification, version 4, depending on the language of the |
| /// translation unit. |
| /// |
| /// @param l the language of the translation unit. |
| /// |
| /// @return the default array lower bound value. |
| static uint64_t |
| get_default_array_lower_bound(translation_unit::language l) |
| { |
| int value = 0; |
| switch (l) |
| { |
| case translation_unit::LANG_UNKNOWN: |
| value = 0; |
| break; |
| case translation_unit::LANG_Cobol74: |
| case translation_unit::LANG_Cobol85: |
| value = 1; |
| break; |
| case translation_unit::LANG_C89: |
| case translation_unit::LANG_C99: |
| case translation_unit::LANG_C11: |
| case translation_unit::LANG_C: |
| case translation_unit::LANG_C_plus_plus_03: |
| case translation_unit::LANG_C_plus_plus_11: |
| case translation_unit::LANG_C_plus_plus_14: |
| case translation_unit::LANG_C_plus_plus: |
| case translation_unit::LANG_ObjC: |
| case translation_unit::LANG_ObjC_plus_plus: |
| case translation_unit::LANG_Rust: |
| value = 0; |
| break; |
| case translation_unit::LANG_Fortran77: |
| case translation_unit::LANG_Fortran90: |
| case translation_unit::LANG_Fortran95: |
| case translation_unit::LANG_Ada83: |
| case translation_unit::LANG_Ada95: |
| case translation_unit::LANG_Pascal83: |
| case translation_unit::LANG_Modula2: |
| value = 1; |
| break; |
| case translation_unit::LANG_Java: |
| value = 0; |
| break; |
| case translation_unit::LANG_PLI: |
| value = 1; |
| break; |
| case translation_unit::LANG_UPC: |
| case translation_unit::LANG_D: |
| case translation_unit::LANG_Python: |
| case translation_unit::LANG_Go: |
| case translation_unit::LANG_Mips_Assembler: |
| value = 0; |
| break; |
| } |
| |
| return value; |
| } |
| |
| /// For a given offset, find the lower bound of a sorted vector of |
| /// imported unit point offset. |
| /// |
| /// The lower bound is the smallest point (the point with the smallest |
| /// offset) which is the greater than a given offset. |
| /// |
| /// @param imported_unit_points_type the sorted vector of imported |
| /// unit points. |
| /// |
| /// @param val the offset to consider when looking for the lower |
| /// bound. |
| /// |
| /// @param r an iterator to the lower bound found. This parameter is |
| /// set iff the function returns true. |
| /// |
| /// @return true iff the lower bound has been found. |
| static bool |
| find_lower_bound_in_imported_unit_points(const imported_unit_points_type& p, |
| Dwarf_Off val, |
| imported_unit_points_type::const_iterator& r) |
| { |
| imported_unit_point v(val); |
| imported_unit_points_type::const_iterator result = |
| std::lower_bound(p.begin(), p.end(), v); |
| |
| bool is_ok = result != p.end(); |
| |
| if (is_ok) |
| r = result; |
| |
| return is_ok; |
| } |
| |
| /// Given a DW_TAG_compile_unit, build and return the corresponding |
| /// abigail::translation_unit ir node. Note that this function |
| /// recursively reads the children dies of the current DIE and |
| /// populates the resulting translation unit. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the DW_TAG_compile_unit DIE to consider. |
| /// |
| /// @param address_size the size of the addresses expressed in this |
| /// translation unit in general. |
| /// |
| /// @return a pointer to the resulting translation_unit. |
| static translation_unit_sptr |
| build_translation_unit_and_add_to_ir(reader& rdr, |
| Dwarf_Die* die, |
| char address_size) |
| { |
| translation_unit_sptr result; |
| |
| if (!die) |
| return result; |
| ABG_ASSERT(dwarf_tag(die) == DW_TAG_compile_unit); |
| |
| // Clear the part of the context that is dependent on the translation |
| // unit we are reading. |
| rdr.clear_per_translation_unit_data(); |
| |
| rdr.cur_tu_die(die); |
| |
| string path = die_string_attribute(die, DW_AT_name); |
| if (path == "<artificial>") |
| { |
| // This is a file artificially generated by the compiler, so its |
| // name is '<artificial>'. As we want all different translation |
| // units to have unique path names, let's suffix this path name |
| // with its die offset. |
| std::ostringstream o; |
| o << path << "-" << std::hex << dwarf_dieoffset(die); |
| path = o.str(); |
| } |
| string compilation_dir = die_string_attribute(die, DW_AT_comp_dir); |
| |
| // See if the same translation unit exits already in the current |
| // corpus. Sometimes, the same translation unit can be present |
| // several times in the same debug info. The content of the |
| // different instances of the translation unit are different. So to |
| // represent that, we are going to re-use the same translation |
| // unit. That is, it's going to be the union of all the translation |
| // units of the same path. |
| { |
| const string& abs_path = |
| compilation_dir.empty() ? path : compilation_dir + "/" + path; |
| result = rdr.corpus()->find_translation_unit(abs_path); |
| } |
| |
| if (!result) |
| { |
| result.reset(new translation_unit(rdr.env(), |
| path, |
| address_size)); |
| result->set_compilation_dir_path(compilation_dir); |
| rdr.corpus()->add(result); |
| uint64_t l = 0; |
| die_unsigned_constant_attribute(die, DW_AT_language, l); |
| result->set_language(dwarf_language_to_tu_language(l)); |
| } |
| |
| rdr.cur_transl_unit(result); |
| rdr.die_tu_map()[dwarf_dieoffset(die)] = result; |
| |
| Dwarf_Die child; |
| if (dwarf_child(die, &child) != 0) |
| return result; |
| |
| result->set_is_constructed(false); |
| int tag = dwarf_tag(&child); |
| do |
| if (rdr.load_undefined_interfaces() |
| && (rdr.is_decl_die_with_undefined_symbol(&child) |
| || tag == DW_TAG_class_type // Top-level classes might |
| // have undefined interfaces |
| // that need to be |
| // represented, so let's |
| // analyze them as well. |
| || ((tag == DW_TAG_union_type || tag == DW_TAG_structure_type) |
| && die_is_in_cplus_plus(&child)))) |
| { |
| // Analyze undefined functions & variables for the purpose of |
| // analyzing compatibility matters. |
| build_ir_node_from_die(rdr, &child, |
| // Pretend the DIE is publicly defined |
| // so that types that are reachable |
| // from it get analyzed as well. |
| /*die_is_public=*/true, |
| dwarf_dieoffset(&child)); |
| } |
| else if (!rdr.env().analyze_exported_interfaces_only() |
| || rdr.is_decl_die_with_exported_symbol(&child)) |
| { |
| // Analyze all the DIEs we encounter unless we are asked to only |
| // analyze exported interfaces and the types reachables from them. |
| build_ir_node_from_die(rdr, &child, |
| die_is_public_decl(&child), |
| dwarf_dieoffset(&child)); |
| } |
| while (dwarf_siblingof(&child, &child) == 0); |
| |
| if (!rdr.var_decls_to_re_add_to_tree().empty()) |
| for (list<var_decl_sptr>::const_iterator v = |
| rdr.var_decls_to_re_add_to_tree().begin(); |
| v != rdr.var_decls_to_re_add_to_tree().end(); |
| ++v) |
| { |
| if (is_member_decl(*v)) |
| continue; |
| |
| ABG_ASSERT((*v)->get_scope()); |
| string demangled_name = |
| demangle_cplus_mangled_name((*v)->get_linkage_name()); |
| if (!demangled_name.empty()) |
| { |
| std::list<string> fqn_comps; |
| fqn_to_components(demangled_name, fqn_comps); |
| string mem_name = fqn_comps.back(); |
| fqn_comps.pop_back(); |
| class_decl_sptr class_type; |
| string ty_name; |
| if (!fqn_comps.empty()) |
| { |
| ty_name = components_to_type_name(fqn_comps); |
| class_type = |
| lookup_class_type(ty_name, *rdr.cur_transl_unit()); |
| } |
| if (class_type) |
| { |
| // So we are seeing a member variable for which there |
| // is a global variable definition DIE not having a |
| // reference attribute pointing back to the member |
| // variable declaration DIE. Thus remove the global |
| // variable definition from its current non-class |
| // scope ... |
| decl_base_sptr d; |
| if ((d = lookup_var_decl_in_scope(mem_name, class_type))) |
| // This is the data member with the same name in cl. |
| // We just need to flag it as static. |
| ; |
| else |
| { |
| // In this case there is no data member with the |
| // same name in cl already. Let's add it there then |
| // ... |
| remove_decl_from_scope(*v); |
| d = add_decl_to_scope(*v, class_type); |
| } |
| |
| ABG_ASSERT(dynamic_pointer_cast<var_decl>(d)); |
| // Let's flag the data member as static. |
| set_member_is_static(d, true); |
| } |
| } |
| } |
| rdr.var_decls_to_re_add_to_tree().clear(); |
| |
| result->set_is_constructed(true); |
| |
| return result; |
| } |
| |
| /// Build a abigail::namespace_decl out of a DW_TAG_namespace or |
| /// DW_TAG_module (for fortran) DIE. |
| /// |
| /// Note that this function connects the DW_TAG_namespace to the IR |
| /// being currently created, reads the children of the DIE and |
| /// connects them to the IR as well. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the DIE to read from. Must be either DW_TAG_namespace |
| /// or DW_TAG_module. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return the resulting @ref abigail::namespace_decl or NULL if it |
| /// couldn't be created. |
| static namespace_decl_sptr |
| build_namespace_decl_and_add_to_ir(reader& rdr, |
| Dwarf_Die* die, |
| size_t where_offset) |
| { |
| namespace_decl_sptr result; |
| |
| if (!die) |
| return result; |
| |
| unsigned tag = dwarf_tag(die); |
| if (tag != DW_TAG_namespace && tag != DW_TAG_module) |
| return result; |
| |
| scope_decl_sptr scope = get_scope_for_die(rdr, die, |
| /*called_for_public_decl=*/false, |
| where_offset); |
| |
| string name, linkage_name; |
| location loc; |
| die_loc_and_name(rdr, die, loc, name, linkage_name); |
| |
| result.reset(new namespace_decl(rdr.env(), name, loc)); |
| add_decl_to_scope(result, scope.get()); |
| rdr.associate_die_to_decl(die, result, where_offset); |
| |
| Dwarf_Die child; |
| if (dwarf_child(die, &child) != 0) |
| return result; |
| |
| rdr.scope_stack().push(result.get()); |
| do |
| build_ir_node_from_die(rdr, &child, |
| // If this namespace DIE is private |
| // (anonymous) then all its content is |
| // considered private. Otherwise, its |
| // public decls are considered public. |
| /*called_from_public_decl=*/ |
| die_is_public_decl(die) && die_is_public_decl(&child), |
| where_offset); |
| while (dwarf_siblingof(&child, &child) == 0); |
| rdr.scope_stack().pop(); |
| |
| return result; |
| } |
| |
| /// Build a @ref type_decl out of a DW_TAG_base_type DIE. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the DW_TAG_base_type to consider. |
| /// |
| /// @param where_offset where we are logically at in the DIE stream. |
| /// |
| /// @return the resulting decl_base_sptr. |
| static type_decl_sptr |
| build_type_decl(reader& rdr, Dwarf_Die* die, size_t where_offset) |
| { |
| type_decl_sptr result; |
| |
| if (!die) |
| return result; |
| ABG_ASSERT(dwarf_tag(die) == DW_TAG_base_type); |
| |
| uint64_t byte_size = 0, bit_size = 0; |
| if (!die_unsigned_constant_attribute(die, DW_AT_byte_size, byte_size)) |
| if (!die_unsigned_constant_attribute(die, DW_AT_bit_size, bit_size)) |
| return result; |
| |
| if (bit_size == 0 && byte_size != 0) |
| // Update the bit size. |
| bit_size = byte_size * 8; |
| |
| string type_name, linkage_name; |
| location loc; |
| die_loc_and_name(rdr, die, loc, type_name, linkage_name); |
| |
| if (byte_size == 0) |
| { |
| // The size of the type is zero, that must mean that we are |
| // looking at the definition of the void type. |
| if (type_name == "void") |
| result = is_type_decl(build_ir_node_for_void_type(rdr)); |
| else |
| // A type of size zero that is not void? Hmmh, I am not sure |
| // what that means. Return nil for now. |
| return result; |
| } |
| |
| if (corpus_sptr corp = rdr.should_reuse_type_from_corpus_group()) |
| { |
| string normalized_type_name = type_name; |
| integral_type int_type; |
| if (parse_integral_type(type_name, int_type)) |
| normalized_type_name = int_type.to_string(); |
| result = lookup_basic_type(normalized_type_name, *corp); |
| } |
| |
| if (!result) |
| if (corpus_sptr corp = rdr.corpus()) |
| result = lookup_basic_type(type_name, *corp); |
| if (!result) |
| result.reset(new type_decl(rdr.env(), type_name, bit_size, |
| /*alignment=*/0, loc, linkage_name)); |
| rdr.associate_die_to_type(die, result, where_offset); |
| return result; |
| } |
| |
| /// Construct the type that is to be used as the underlying type of an |
| /// enum. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param enum_name the name of the enum that this type is going to |
| /// be the underlying type of. |
| /// |
| /// @param enum_size the size of the enum. |
| /// |
| /// @param is_anonymous whether the underlying type is anonymous or |
| /// not. By default, this should be set to true as before c++11 (and |
| /// in C), it's almost the case. |
| static type_decl_sptr |
| build_enum_underlying_type(reader& rdr, |
| string enum_name, |
| uint64_t enum_size, |
| bool is_anonymous = true) |
| { |
| string underlying_type_name = |
| build_internal_underlying_enum_type_name(enum_name, is_anonymous, |
| enum_size); |
| |
| type_decl_sptr result(new type_decl(rdr.env(), underlying_type_name, |
| enum_size, enum_size, location())); |
| result->set_is_anonymous(is_anonymous); |
| result->set_is_artificial(true); |
| translation_unit_sptr tu = rdr.cur_transl_unit(); |
| decl_base_sptr d = add_decl_to_scope(result, tu->get_global_scope().get()); |
| result = dynamic_pointer_cast<type_decl>(d); |
| ABG_ASSERT(result); |
| canonicalize(result); |
| return result; |
| } |
| |
| /// Build an enum_type_decl from a DW_TAG_enumeration_type DIE. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the DIE to read from. |
| /// |
| /// @param scope the scope of the final enum. Note that this function |
| /// does *NOT* add the built type to this scope. The scope is just so |
| /// that the function knows how to name anonymous enums. |
| /// |
| /// @param is_declaration_only is true if the DIE denoted by @p die is |
| /// a declaration-only DIE. |
| /// |
| /// @return the built enum_type_decl or NULL if it could not be built. |
| static enum_type_decl_sptr |
| build_enum_type(reader& rdr, |
| Dwarf_Die* die, |
| scope_decl* scope, |
| size_t where_offset, |
| bool is_declaration_only) |
| { |
| enum_type_decl_sptr result; |
| if (!die) |
| return result; |
| |
| unsigned tag = dwarf_tag(die); |
| if (tag != DW_TAG_enumeration_type) |
| return result; |
| |
| string name, linkage_name; |
| location loc; |
| die_loc_and_name(rdr, die, loc, name, linkage_name); |
| |
| bool is_anonymous = false; |
| // If the enum is anonymous, let's give it a name. |
| if (name.empty()) |
| { |
| name = get_internal_anonymous_die_prefix_name(die); |
| ABG_ASSERT(!name.empty()); |
| // But we remember that the type is anonymous. |
| is_anonymous = true; |
| |
| if (size_t s = scope->get_num_anonymous_member_enums()) |
| name = build_internal_anonymous_die_name(name, s); |
| } |
| |
| bool use_odr = rdr.odr_is_relevant(die); |
| // If the type has location, then associate it to its |
| // representation. This way, all occurences of types with the same |
| // representation (name) and location can be later detected as being |
| // for the same type. |
| |
| if (!is_anonymous) |
| { |
| if (use_odr) |
| { |
| if (enum_type_decl_sptr pre_existing_enum = |
| is_enum_type(rdr.lookup_artifact_from_die(die))) |
| result = pre_existing_enum; |
| } |
| else if (corpus_sptr corp = rdr.should_reuse_type_from_corpus_group()) |
| { |
| if (loc) |
| result = lookup_enum_type_per_location(loc.expand(), *corp); |
| } |
| else if (loc) |
| { |
| if (enum_type_decl_sptr pre_existing_enum = |
| is_enum_type(rdr.lookup_artifact_from_die(die))) |
| if (pre_existing_enum->get_location() == loc) |
| result = pre_existing_enum; |
| } |
| |
| if (result) |
| { |
| rdr.associate_die_to_type(die, result, where_offset); |
| return result; |
| } |
| } |
| // TODO: for anonymous enums, maybe have a map of loc -> enums so that |
| // we can look them up? |
| |
| uint64_t size = 0; |
| if (die_unsigned_constant_attribute(die, DW_AT_byte_size, size)) |
| size *= 8; |
| bool is_artificial = die_is_artificial(die); |
| |
| // for now we consider that underlying types of enums are all anonymous |
| bool enum_underlying_type_is_anonymous= true; |
| |
| enum_type_decl::enumerators enms; |
| Dwarf_Die child; |
| if (dwarf_child(die, &child) == 0) |
| { |
| do |
| { |
| if (dwarf_tag(&child) != DW_TAG_enumerator) |
| continue; |
| |
| string n, m; |
| location l; |
| die_loc_and_name(rdr, &child, l, n, m); |
| uint64_t val = 0; |
| die_unsigned_constant_attribute(&child, DW_AT_const_value, val); |
| enms.push_back(enum_type_decl::enumerator(n, val)); |
| } |
| while (dwarf_siblingof(&child, &child) == 0); |
| } |
| |
| // DWARF up to version 4 (at least) doesn't seem to carry the |
| // underlying type, so let's create an artificial one here, which |
| // sole purpose is to be passed to the constructor of the |
| // enum_type_decl type. |
| type_decl_sptr t = |
| build_enum_underlying_type(rdr, name, size, |
| enum_underlying_type_is_anonymous); |
| t->set_is_declaration_only(is_declaration_only); |
| |
| result.reset(new enum_type_decl(name, loc, t, enms, linkage_name)); |
| result->set_is_anonymous(is_anonymous); |
| result->set_is_declaration_only(is_declaration_only); |
| result->set_is_artificial(is_artificial); |
| rdr.associate_die_to_type(die, result, where_offset); |
| |
| rdr.maybe_schedule_declaration_only_enum_for_resolution(result); |
| |
| return result; |
| } |
| |
| /// Once a function_decl has been built and added to a class as a |
| /// member function, this function updates the information of the |
| /// function_decl concerning the properties of its relationship with |
| /// the member class. That is, it updates properties like |
| /// virtualness, access, constness, cdtorness, etc ... |
| /// |
| /// @param die the DIE of the function_decl that has been just built. |
| /// |
| /// @param f the function_decl that has just been built from @p die. |
| /// |
| /// @param klass the @ref class_or_union that @p f belongs to. |
| /// |
| /// @param rdr the context used to read the ELF/DWARF information. |
| static void |
| finish_member_function_reading(Dwarf_Die* die, |
| const function_decl_sptr& f, |
| const class_or_union_sptr klass, |
| reader& rdr) |
| { |
| ABG_ASSERT(klass); |
| |
| method_decl_sptr m = is_method_decl(f); |
| ABG_ASSERT(m); |
| |
| method_type_sptr method_t = is_method_type(m->get_type()); |
| ABG_ASSERT(method_t); |
| |
| size_t is_inline = die_is_declared_inline(die); |
| bool is_ctor = (f->get_name() == klass->get_name()); |
| bool is_dtor = (!f->get_name().empty() |
| && static_cast<string>(f->get_name())[0] == '~'); |
| bool is_virtual = die_is_virtual(die); |
| int64_t vindex = -1; |
| if (is_virtual) |
| die_virtual_function_index(die, vindex); |
| access_specifier access = public_access; |
| if (class_decl_sptr c = is_class_type(klass)) |
| if (!c->is_struct()) |
| access = private_access; |
| die_access_specifier(die, access); |
| |
| m->is_declared_inline(is_inline); |
| set_member_access_specifier(m, access); |
| if (vindex != -1) |
| set_member_function_vtable_offset(m, vindex); |
| if (is_virtual) |
| set_member_function_is_virtual(m, is_virtual); |
| bool is_static = method_t->get_is_for_static_method(); |
| set_member_is_static(m, is_static); |
| set_member_function_is_ctor(m, is_ctor); |
| set_member_function_is_dtor(m, is_dtor); |
| set_member_function_is_const(m, method_t->get_is_const()); |
| |
| ABG_ASSERT(is_member_function(m)); |
| |
| if (is_virtual && !f->get_linkage_name().empty() && !f->get_symbol()) |
| { |
| // This is a virtual member function which has a linkage name |
| // but has no underlying symbol set. |
| // |
| // The underlying elf symbol to set to this function can show up |
| // later in the DWARF input or it can be that, because of some |
| // compiler optimization, the relation between this function and |
| // its underlying elf symbol is simply not emitted in the DWARF. |
| // |
| // Let's thus schedule this function for a later fixup pass |
| // (performed by |
| // reader::fixup_functions_with_no_symbols()) that will |
| // set its underlying symbol. |
| // |
| // Note that if the underying symbol is encountered later in the |
| // DWARF input, then the part of build_function_decl() that |
| // updates the function to set its underlying symbol will |
| // de-schedule this function wrt fixup pass. |
| Dwarf_Off die_offset = dwarf_dieoffset(die); |
| die_function_decl_map_type &fns_with_no_symbol = |
| rdr.die_function_decl_with_no_symbol_map(); |
| die_function_decl_map_type::const_iterator i = |
| fns_with_no_symbol.find(die_offset); |
| if (i == fns_with_no_symbol.end()) |
| fns_with_no_symbol[die_offset] = f; |
| } |
| |
| } |
| |
| /// If a function DIE has attributes which have not yet been read and |
| /// added to the internal representation that represents that function |
| /// then read those extra attributes and update the internal |
| /// representation. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the function DIE to consider. |
| /// |
| /// @param where_offset where we logical are, currently, in the stream |
| /// of DIEs. If you don't know what this is, you can just set it to zero. |
| /// |
| /// @param existing_fn the representation of the function to update. |
| /// |
| /// @return the updated function representation. |
| static function_decl_sptr |
| maybe_finish_function_decl_reading(reader& rdr, |
| Dwarf_Die* die, |
| size_t where_offset, |
| const function_decl_sptr& existing_fn) |
| { |
| function_decl_sptr result = build_function_decl(rdr, die, |
| where_offset, |
| existing_fn); |
| |
| return result; |
| } |
| |
| /// Lookup a class or a typedef with a given qualified name in the |
| /// corpus that a given scope belongs to. |
| /// |
| /// @param scope the scope to consider. |
| /// |
| /// @param type_name the qualified name of the type to look for. |
| /// |
| /// @return the typedef or class type found. |
| static type_base_sptr |
| lookup_class_or_typedef_from_corpus(scope_decl* scope, const string& type_name) |
| { |
| string qname = build_qualified_name(scope, type_name); |
| corpus* corp = scope->get_corpus(); |
| type_base_sptr result = lookup_class_or_typedef_type(qname, *corp); |
| return result; |
| } |
| |
| /// Lookup a class of typedef type from the current corpus being |
| /// constructed. |
| /// |
| /// The type being looked for has the same name as a given DIE. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the DIE which has the same name as the type we are |
| /// looking for. |
| /// |
| /// @param called_for_public_decl whether this function is being |
| /// called from a a publicly defined declaration. |
| /// |
| /// @param where_offset where we are logically at in the DIE stream. |
| /// |
| /// @return the type found. |
| static type_base_sptr |
| lookup_class_or_typedef_from_corpus(reader& rdr, |
| Dwarf_Die* die, |
| bool called_for_public_decl, |
| size_t where_offset) |
| { |
| if (!die) |
| return class_decl_sptr(); |
| |
| string class_name = die_string_attribute(die, DW_AT_name); |
| if (class_name.empty()) |
| return class_decl_sptr(); |
| |
| scope_decl_sptr scope = get_scope_for_die(rdr, die, |
| called_for_public_decl, |
| where_offset); |
| if (scope) |
| return lookup_class_or_typedef_from_corpus(scope.get(), class_name); |
| |
| return type_base_sptr(); |
| } |
| |
| /// Lookup a class, typedef or enum type with a given qualified name |
| /// in the corpus that a given scope belongs to. |
| /// |
| /// @param scope the scope to consider. |
| /// |
| /// @param type_name the qualified name of the type to look for. |
| /// |
| /// @return the typedef, enum or class type found. |
| static type_base_sptr |
| lookup_class_typedef_or_enum_type_from_corpus(scope_decl* scope, |
| const string& type_name) |
| { |
| string qname = build_qualified_name(scope, type_name); |
| corpus* corp = scope->get_corpus(); |
| type_base_sptr result = lookup_class_typedef_or_enum_type(qname, *corp); |
| return result; |
| } |
| |
| /// Lookup a class, typedef or enum type in a given scope, in the |
| /// corpus that scope belongs to. |
| /// |
| /// @param die the DIE of the class, typedef or enum to lookup. |
| /// |
| /// @param anonymous_member_type_idx if @p DIE represents an anonymous |
| /// type, this is the index of that anonymous type in its scope, in |
| /// case there are several anonymous types of the same kind in that |
| /// scope. |
| /// |
| /// @param scope the scope in which to look the type for. |
| /// |
| /// @return the typedef, enum or class type found. |
| static type_base_sptr |
| lookup_class_typedef_or_enum_type_from_corpus(Dwarf_Die* die, |
| size_t anonymous_member_type_idx, |
| scope_decl* scope) |
| { |
| if (!die) |
| return class_decl_sptr(); |
| |
| string type_name = die_string_attribute(die, DW_AT_name); |
| if (is_anonymous_type_die(die)) |
| type_name = |
| get_internal_anonymous_die_name(die, anonymous_member_type_idx); |
| |
| if (type_name.empty()) |
| return class_decl_sptr(); |
| |
| return lookup_class_typedef_or_enum_type_from_corpus(scope, type_name); |
| } |
| |
| |
| /// Test if a DIE represents a function that is a member of a given |
| /// class type. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param function_die the DIE of the function to consider. |
| /// |
| /// @param class_type the class type to consider. |
| /// |
| /// @param where_offset where we are logically at in the DIE stream. |
| /// |
| /// @return the method declaration corresponding to the member |
| /// function of @p class_type, iff @p function_die is for a member |
| /// function of @p class_type. |
| static method_decl_sptr |
| is_function_for_die_a_member_of_class(reader& rdr, |
| Dwarf_Die* function_die, |
| const class_or_union_sptr& class_type) |
| { |
| type_or_decl_base_sptr artifact = rdr.lookup_artifact_from_die(function_die); |
| |
| if (!artifact) |
| return method_decl_sptr(); |
| |
| method_decl_sptr method = is_method_decl(artifact); |
| method_type_sptr method_type; |
| |
| if (method) |
| method_type = method->get_type(); |
| else |
| method_type = is_method_type(artifact); |
| ABG_ASSERT(method_type); |
| |
| class_or_union_sptr method_class = method_type->get_class_type(); |
| ABG_ASSERT(method_class); |
| |
| string method_class_name = method_class->get_qualified_name(), |
| class_type_name = class_type->get_qualified_name(); |
| |
| if (method_class_name == class_type_name) |
| { |
| //ABG_ASSERT(class_type.get() == method_class.get()); |
| return method; |
| } |
| |
| return method_decl_sptr(); |
| } |
| |
| /// If a given function DIE represents an existing member function of |
| /// a given class, then update that member function with new |
| /// properties present in the DIE. Otherwise, if the DIE represents a |
| /// new member function that is not already present in the class then |
| /// add that new member function to the class. |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param function_die the DIE of the potential member function to |
| /// consider. |
| /// |
| /// @param class_type the class type to consider. |
| /// |
| /// @param called_from_public_decl is true iff this function was |
| /// called from a publicly defined and exported declaration. |
| /// |
| /// @param where_offset where we are logically at in the DIE stream. |
| /// |
| /// @return the method decl representing the member function. |
| static method_decl_sptr |
| add_or_update_member_function(reader& rdr, |
| Dwarf_Die* function_die, |
| const class_or_union_sptr& class_type, |
| bool called_from_public_decl, |
| size_t where_offset) |
| { |
| method_decl_sptr method = |
| is_function_for_die_a_member_of_class(rdr, function_die, class_type); |
| |
| if (!method) |
| method = is_method_decl(build_ir_node_from_die(rdr, function_die, |
| class_type.get(), |
| called_from_public_decl, |
| where_offset)); |
| if (!method) |
| return method_decl_sptr(); |
| |
| finish_member_function_reading(function_die, |
| is_function_decl(method), |
| class_type, rdr); |
| return method; |
| } |
| |
| /// Build a an IR node for class type from a DW_TAG_structure_type or |
| /// DW_TAG_class_type DIE and add that node to the ABI corpus being |
| /// currently built. |
| /// |
| /// If the represents class type that already exists, then update the |
| /// existing class type with the new properties found in the DIE. |
| /// |
| /// It meanst that this function can also update an existing |
| /// class_decl node with data members, member functions and other |
| /// properties coming from the DIE. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE to read information from. Must be either a |
| /// DW_TAG_structure_type or a DW_TAG_class_type. |
| /// |
| /// @param scope a pointer to the scope_decl* under which this class |
| /// is to be added to. |
| /// |
| /// @param is_struct whether the class was declared as a struct. |
| /// |
| /// @param klass if non-null, this is a klass to append the members |
| /// to. Otherwise, this function just builds the class from scratch. |
| /// |
| /// @param called_from_public_decl set to true if this class is being |
| /// called from a "Public declaration like vars or public symbols". |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @param is_declaration_only is true if the DIE denoted by @p die is |
| /// a declaration-only DIE. |
| /// |
| /// @return the resulting class_type. |
| static class_decl_sptr |
| add_or_update_class_type(reader& rdr, |
| Dwarf_Die* die, |
| scope_decl* scope, |
| bool is_struct, |
| class_decl_sptr klass, |
| bool called_from_public_decl, |
| size_t where_offset, |
| bool is_declaration_only) |
| { |
| class_decl_sptr result; |
| if (!die) |
| return result; |
| |
| const die_source source = rdr.get_die_source(die); |
| |
| unsigned tag = dwarf_tag(die); |
| |
| if (tag != DW_TAG_class_type && tag != DW_TAG_structure_type) |
| return result; |
| |
| { |
| die_class_or_union_map_type::const_iterator i = |
| rdr.die_wip_classes_map(source).find(dwarf_dieoffset(die)); |
| if (i != rdr.die_wip_classes_map(source).end()) |
| { |
| class_decl_sptr class_type = is_class_type(i->second); |
| ABG_ASSERT(class_type); |
| return class_type; |
| } |
| } |
| |
| string name, linkage_name; |
| location loc; |
| die_loc_and_name(rdr, die, loc, name, linkage_name); |
| |
| bool is_anonymous = false; |
| if (name.empty()) |
| { |
| // So we are looking at an anonymous struct. Let's |
| // give it a name. |
| name = get_internal_anonymous_die_prefix_name(die); |
| ABG_ASSERT(!name.empty()); |
| // But we remember that the type is anonymous. |
| is_anonymous = true; |
| |
| if (size_t s = scope->get_num_anonymous_member_classes()) |
| name = build_internal_anonymous_die_name(name, s); |
| } |
| |
| if (!is_anonymous) |
| { |
| if (corpus_sptr corp = rdr.should_reuse_type_from_corpus_group()) |
| { |
| if (loc) |
| // TODO: if there is only one class defined in the corpus |
| // for this location, then re-use it. But if there are |
| // more than one, then do not re-use it, for now. |
| result = lookup_class_type_per_location(loc.expand(), *corp); |
| else |
| // TODO: if there is just one class for that name defined, |
| // then re-use it. Otherwise, don't. |
| result = lookup_class_type(name, *corp); |
| if (result |
| // If we are seeing a declaration of a definition we |
| // already had, or if we are seing a type with the same |
| // declaration-only-ness that we had before, then keep |
| // the one we already had. |
| && (result->get_is_declaration_only() == is_declaration_only |
| || (!result->get_is_declaration_only() |
| && is_declaration_only))) |
| { |
| rdr.associate_die_to_type(die, result, where_offset); |
| return result; |
| } |
| else |
| // We might be seeing the definition of a declaration we |
| // already had. In that case, keep the definition and |
| // drop the declaration. |
| result.reset(); |
| } |
| } |
| |
| // If we've already seen the same class as 'die', then let's re-use |
| // that one, unless it's an anonymous class. We can't really safely |
| // re-use anonymous classes as they have no name, by construction. |
| // What we can do, rather, is to reuse the typedef that name them, |
| // when they do have a naming typedef. |
| if (!is_anonymous) |
| if (class_decl_sptr pre_existing_class = |
| is_class_type(rdr.lookup_type_artifact_from_die(die))) |
| klass = pre_existing_class; |
| |
| uint64_t size = 0; |
| die_size_in_bits(die, size); |
| bool is_artificial = die_is_artificial(die); |
| |
| Dwarf_Die child; |
| bool has_child = (dwarf_child(die, &child) == 0); |
| |
| decl_base_sptr res; |
| if (klass) |
| { |
| res = result = klass; |
| if (has_child && klass->get_is_declaration_only() |
| && klass->get_definition_of_declaration()) |
| res = result = is_class_type(klass->get_definition_of_declaration()); |
| if (loc) |
| result->set_location(loc); |
| } |
| else |
| { |
| result.reset(new class_decl(rdr.env(), name, size, |
| /*alignment=*/0, is_struct, loc, |
| decl_base::VISIBILITY_DEFAULT, |
| is_anonymous)); |
| |
| result->set_is_declaration_only(is_declaration_only); |
| |
| res = add_decl_to_scope(result, scope); |
| result = dynamic_pointer_cast<class_decl>(res); |
| ABG_ASSERT(result); |
| } |
| |
| if (!klass || klass->get_is_declaration_only()) |
| if (size != result->get_size_in_bits()) |
| result->set_size_in_bits(size); |
| |
| if (klass) |
| // We are amending a class that was built before. So let's check |
| // if we need to amend its "declaration-only-ness" status. |
| if (!!result->get_size_in_bits() == result->get_is_declaration_only()) |
| // The size of the class doesn't match its |
| // 'declaration-only-ness". We might have a non-zero sized |
| // class which is declaration-only, or a zero sized class that |
| // is not declaration-only. Let's set the declaration-only-ness |
| // according to what we are instructed to. |
| // |
| // Note however that there are binaries out there emitted by |
| // compilers (Clang, in C++) emit declarations-only classes that |
| // have non-zero size. So we must honor these too. That is why |
| // we are not forcing the declaration-only-ness to false when a |
| // class has non-zero size. An example of such binary is |
| // tests/data/test-diff-filter/test41-PR21486-abg-writer.llvm.o. |
| result->set_is_declaration_only(is_declaration_only); |
| |
| // If a non-decl-only class has children node and is advertized as |
| // having a non-zero size let's trust that. |
| if (!result->get_is_declaration_only() && has_child) |
| if (result->get_size_in_bits() == 0 && size != 0) |
| result->set_size_in_bits(size); |
| |
| result->set_is_artificial(is_artificial); |
| |
| rdr.associate_die_to_type(die, result, where_offset); |
| |
| rdr.maybe_schedule_declaration_only_class_for_resolution(result); |
| |
| if (!has_child) |
| // TODO: set the access specifier for the declaration-only class |
| // here. |
| return result; |
| |
| rdr.die_wip_classes_map(source)[dwarf_dieoffset(die)] = result; |
| |
| bool is_incomplete_type = false; |
| if (is_declaration_only && size == 0 && has_child) |
| // this is an incomplete DWARF type as defined by [5.7.1] |
| // |
| // An incomplete structure, union or class type is represented by |
| // a structure, union or class entry that does not have a byte |
| // size attribute and that has a DW_AT_declaration attribute. |
| // |
| // Let's consider that it's thus a decl-only class, likely |
| // referred to by a pointer. If we later encounter a definition |
| // for this decl-only class type, then this decl-only class will |
| // be resolved to it by the code in |
| // reader::resolve_declaration_only_classes. |
| is_incomplete_type = true; |
| |
| scope_decl_sptr scop = |
| dynamic_pointer_cast<scope_decl>(res); |
| ABG_ASSERT(scop); |
| rdr.scope_stack().push(scop.get()); |
| |
| if (has_child && !is_incomplete_type) |
| { |
| int anonymous_member_class_index = -1; |
| int anonymous_member_union_index = -1; |
| int anonymous_member_enum_index = -1; |
| |
| do |
| { |
| tag = dwarf_tag(&child); |
| |
| // Handle base classes. |
| if (tag == DW_TAG_inheritance) |
| { |
| result->set_is_declaration_only(false); |
| |
| Dwarf_Die type_die; |
| if (!die_die_attribute(&child, DW_AT_type, type_die)) |
| continue; |
| |
| type_base_sptr base_type; |
| if (!(base_type = |
| lookup_class_or_typedef_from_corpus(rdr, &type_die, |
| called_from_public_decl, |
| where_offset))) |
| { |
| base_type = |
| is_type(build_ir_node_from_die(rdr, &type_die, |
| called_from_public_decl, |
| where_offset)); |
| } |
| // Sometimes base_type can be a typedef. Let's make |
| // sure that typedef is compatible with a class type. |
| class_decl_sptr b = is_compatible_with_class_type(base_type); |
| if (!b) |
| continue; |
| |
| access_specifier access = |
| is_struct |
| ? public_access |
| : private_access; |
| |
| die_access_specifier(&child, access); |
| |
| bool is_virt= die_is_virtual(&child); |
| int64_t offset = 0; |
| bool is_offset_present = |
| die_member_offset(rdr, &child, offset); |
| |
| class_decl::base_spec_sptr base(new class_decl::base_spec |
| (b, access, |
| is_offset_present ? offset : -1, |
| is_virt)); |
| if (b->get_is_declaration_only() |
| // Only non-anonymous decl-only classes are |
| // scheduled for resolution to their definition. |
| // Anonymous classes that are decl-only are likely |
| // only artificially created by |
| // get_opaque_version_of_type, from anonymous fully |
| // defined classes. Those are never defined. |
| && !b->get_qualified_name().empty()) |
| ABG_ASSERT(rdr.is_decl_only_class_scheduled_for_resolution(b)); |
| if (result->find_base_class(b->get_qualified_name())) |
| continue; |
| result->add_base_specifier(base); |
| } |
| // Handle data members. |
| else if (tag == DW_TAG_member |
| || tag == DW_TAG_variable) |
| { |
| Dwarf_Die type_die; |
| if (!die_die_attribute(&child, DW_AT_type, type_die)) |
| continue; |
| |
| string n, m; |
| location loc; |
| die_loc_and_name(rdr, &child, loc, n, m); |
| /// For now, we skip the hidden vtable pointer. |
| /// Currently, we're looking for a member starting with |
| /// "_vptr[^0-9a-zA-Z_]", which is what Clang and GCC |
| /// use as a name for the hidden vtable pointer. |
| if (n.substr(0, 5) == "_vptr" |
| && n.size() > 5 |
| && !std::isalnum(n.at(5)) |
| && n.at(5) != '_') |
| continue; |
| |
| // If the variable is already a member of this class, |
| // move on. If it's an anonymous data member, we need |
| // to handle it differently. We'll do that later below. |
| if (!n.empty() && lookup_var_decl_in_scope(n, result)) |
| continue; |
| |
| int64_t offset_in_bits = 0; |
| bool is_laid_out = die_member_offset(rdr, &child, |
| offset_in_bits); |
| // For now, is_static == !is_laid_out. When we have |
| // templates, we'll try to be more specific. For now, |
| // this approximation should do OK. |
| bool is_static = !is_laid_out; |
| |
| if (is_static && variable_is_suppressed(rdr, |
| result.get(), |
| &child, |
| is_declaration_only)) |
| continue; |
| |
| decl_base_sptr ty = is_decl(build_ir_node_from_die(rdr, &type_die, |
| called_from_public_decl, |
| where_offset)); |
| type_base_sptr t = is_type(ty); |
| if (!t) |
| continue; |
| |
| if (n.empty() && !die_is_anonymous_data_member(&child)) |
| { |
| // We must be in a case where the data member has an |
| // empty name because the DWARF emitter has a bug. |
| // Let's generate an artificial name for that data |
| // member. |
| n = rdr.build_name_for_buggy_anonymous_data_member(&child); |
| ABG_ASSERT(!n.empty()); |
| } |
| |
| // The call to build_ir_node_from_die above could have |
| // triggered the adding of a data member named 'n' into |
| // result. So let's check again if the variable is |
| // already a member of this class. Here again, if it's |
| // an anonymous data member, we need to handle it |
| // differently. We'll do that later below. |
| if (!n.empty() && lookup_var_decl_in_scope(n, result)) |
| continue; |
| |
| if (!is_static) |
| // We have a non-static data member. So this class |
| // cannot be a declaration-only class anymore, even if |
| // some DWARF emitters might consider it otherwise. |
| result->set_is_declaration_only(false); |
| access_specifier access = |
| is_struct |
| ? public_access |
| : private_access; |
| |
| die_access_specifier(&child, access); |
| |
| var_decl_sptr dm(new var_decl(n, t, loc, m)); |
| if (n.empty() |
| && anonymous_data_member_exists_in_class(*dm, *result)) |
| // dm is an anonymous data member that was already |
| // present in the current class so let's not add it. |
| continue; |
| result->add_data_member(dm, access, is_laid_out, |
| is_static, offset_in_bits); |
| ABG_ASSERT(has_scope(dm)); |
| rdr.associate_die_to_decl(&child, dm, where_offset, |
| /*associate_by_repr=*/false); |
| } |
| // Handle member functions; |
| else if (tag == DW_TAG_subprogram) |
| { |
| decl_base_sptr r = |
| add_or_update_member_function(rdr, &child, result, |
| called_from_public_decl, |
| where_offset); |
| if (function_decl_sptr f = is_function_decl(r)) |
| rdr.associate_die_to_decl(&child, f, where_offset, |
| /*associate_by_repr=*/true); |
| } |
| // Handle member types |
| else if (die_is_type(&child)) |
| { |
| // Track the anonymous type index in the current |
| // scope. Look for what this means by reading the |
| // comment of the function |
| // build_internal_anonymous_die_name. |
| int anonymous_member_type_index = 0; |
| if (is_anonymous_type_die(&child)) |
| { |
| // Update the anonymous type index. |
| if (die_is_class_type(&child)) |
| anonymous_member_type_index = |
| ++anonymous_member_class_index; |
| else if (dwarf_tag(&child) == DW_TAG_union_type) |
| anonymous_member_type_index = |
| ++anonymous_member_union_index; |
| else if (dwarf_tag(&child) == DW_TAG_enumeration_type) |
| anonymous_member_type_index = |
| ++anonymous_member_enum_index; |
| } |
| // if the type is not already a member of this class, |
| // then add it to the class. |
| if ((is_anonymous_type_die(&child) |
| && !lookup_class_typedef_or_enum_type_from_corpus |
| (&child, anonymous_member_type_index, result.get())) |
| || !result->find_member_type(die_name(&child))) |
| build_ir_node_from_die(rdr, &child, result.get(), |
| called_from_public_decl, |
| where_offset); |
| } |
| } while (dwarf_siblingof(&child, &child) == 0); |
| } |
| |
| rdr.scope_stack().pop(); |
| |
| { |
| die_class_or_union_map_type::const_iterator i = |
| rdr.die_wip_classes_map(source).find(dwarf_dieoffset(die)); |
| if (i != rdr.die_wip_classes_map(source).end()) |
| { |
| if (is_member_type(i->second)) |
| set_member_access_specifier(res, |
| get_member_access_specifier(i->second)); |
| rdr.die_wip_classes_map(source).erase(i); |
| } |
| } |
| |
| rdr.maybe_schedule_declaration_only_class_for_resolution(result); |
| return result; |
| } |
| |
| /// Build an @ref union_decl from a DW_TAG_union_type DIE. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the DIE to read from. |
| /// |
| /// @param scope the scope the resulting @ref union_decl belongs to. |
| /// |
| /// @param union_type if this parameter is non-nil, then this function |
| /// updates the @ref union_decl that it points to, rather than |
| /// creating a new @ref union_decl. |
| /// |
| /// @param called_from_public_decl is true if this function has been |
| /// initially called within the context of a public decl. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @param is_declaration_only is true if the DIE denoted by @p die is |
| /// a declaration-only DIE. |
| /// |
| /// @return the resulting @ref union_decl type. |
| static union_decl_sptr |
| add_or_update_union_type(reader& rdr, |
| Dwarf_Die* die, |
| scope_decl* scope, |
| union_decl_sptr union_type, |
| bool called_from_public_decl, |
| size_t where_offset, |
| bool is_declaration_only) |
| { |
| union_decl_sptr result; |
| if (!die) |
| return result; |
| |
| unsigned tag = dwarf_tag(die); |
| |
| if (tag != DW_TAG_union_type) |
| return result; |
| |
| const die_source source = rdr.get_die_source(die); |
| { |
| die_class_or_union_map_type::const_iterator i = |
| rdr.die_wip_classes_map(source).find(dwarf_dieoffset(die)); |
| if (i != rdr.die_wip_classes_map(source).end()) |
| { |
| union_decl_sptr u = is_union_type(i->second); |
| ABG_ASSERT(u); |
| return u; |
| } |
| } |
| |
| string name, linkage_name; |
| location loc; |
| die_loc_and_name(rdr, die, loc, name, linkage_name); |
| |
| bool is_anonymous = false; |
| if (name.empty()) |
| { |
| // So we are looking at an anonymous union. Let's give it a |
| // name. |
| name = get_internal_anonymous_die_prefix_name(die); |
| ABG_ASSERT(!name.empty()); |
| // But we remember that the type is anonymous. |
| is_anonymous = true; |
| |
| if (size_t s = scope->get_num_anonymous_member_unions()) |
| name = build_internal_anonymous_die_name(name, s); |
| } |
| |
| // If the type has location, then associate it to its |
| // representation. This way, all occurences of types with the same |
| // representation (name) and location can be later detected as being |
| // for the same type. |
| |
| if (!is_anonymous) |
| { |
| if (corpus_sptr corp = rdr.should_reuse_type_from_corpus_group()) |
| { |
| if (loc) |
| result = lookup_union_type_per_location(loc.expand(), *corp); |
| else |
| result = lookup_union_type(name, *corp); |
| |
| if (result) |
| { |
| rdr.associate_die_to_type(die, result, where_offset); |
| return result; |
| } |
| } |
| } |
| |
| // if we've already seen a union with the same union as 'die' then |
| // let's re-use that one. We can't really safely re-use anonymous |
| // unions as they have no name, by construction. What we can do, |
| // rather, is to reuse the typedef that name them, when they do have |
| // a naming typedef. |
| if (!is_anonymous) |
| if (union_decl_sptr pre_existing_union = |
| is_union_type(rdr.lookup_artifact_from_die(die))) |
| union_type = pre_existing_union; |
| |
| uint64_t size = 0; |
| die_size_in_bits(die, size); |
| bool is_artificial = die_is_artificial(die); |
| |
| if (union_type) |
| { |
| result = union_type; |
| result->set_location(loc); |
| } |
| else |
| { |
| result.reset(new union_decl(rdr.env(), name, size, loc, |
| decl_base::VISIBILITY_DEFAULT, |
| is_anonymous)); |
| if (is_declaration_only) |
| result->set_is_declaration_only(true); |
| result = is_union_type(add_decl_to_scope(result, scope)); |
| ABG_ASSERT(result); |
| } |
| |
| if (size) |
| { |
| result->set_size_in_bits(size); |
| result->set_is_declaration_only(false); |
| } |
| |
| result->set_is_artificial(is_artificial); |
| |
| rdr.associate_die_to_type(die, result, where_offset); |
| |
| rdr.maybe_schedule_declaration_only_class_for_resolution(result); |
| |
| Dwarf_Die child; |
| bool has_child = (dwarf_child(die, &child) == 0); |
| if (!has_child) |
| return result; |
| |
| rdr.die_wip_classes_map(source)[dwarf_dieoffset(die)] = result; |
| |
| scope_decl_sptr scop = |
| dynamic_pointer_cast<scope_decl>(result); |
| ABG_ASSERT(scop); |
| rdr.scope_stack().push(scop.get()); |
| |
| if (has_child) |
| { |
| do |
| { |
| tag = dwarf_tag(&child); |
| // Handle data members. |
| if (tag == DW_TAG_member || tag == DW_TAG_variable) |
| { |
| Dwarf_Die type_die; |
| if (!die_die_attribute(&child, DW_AT_type, type_die)) |
| continue; |
| |
| string n, m; |
| location loc; |
| die_loc_and_name(rdr, &child, loc, n, m); |
| |
| // Because we can be updating an existing union, let's |
| // make sure we don't already have a member of the same |
| // name. Anonymous member are handled a bit later below |
| // so let's not consider them here. |
| if (!n.empty() && lookup_var_decl_in_scope(n, result)) |
| continue; |
| |
| ssize_t offset_in_bits = 0; |
| decl_base_sptr ty = |
| is_decl(build_ir_node_from_die(rdr, &type_die, |
| called_from_public_decl, |
| where_offset)); |
| type_base_sptr t = is_type(ty); |
| if (!t) |
| continue; |
| |
| // We have a non-static data member. So this union |
| // cannot be a declaration-only union anymore, even if |
| // some DWARF emitters might consider it otherwise. |
| result->set_is_declaration_only(false); |
| access_specifier access = public_access; |
| |
| die_access_specifier(&child, access); |
| |
| var_decl_sptr dm(new var_decl(n, t, loc, m)); |
| // If dm is an anonymous data member, let's make sure |
| // the current union doesn't already have it as a data |
| // member. |
| if (n.empty() && result->find_data_member(dm)) |
| continue; |
| |
| if (!n.empty() && lookup_var_decl_in_scope(n, result)) |
| continue; |
| |
| result->add_data_member(dm, access, /*is_laid_out=*/true, |
| /*is_static=*/false, |
| offset_in_bits); |
| ABG_ASSERT(has_scope(dm)); |
| rdr.associate_die_to_decl(&child, dm, where_offset, |
| /*associate_by_repr=*/false); |
| } |
| // Handle member functions; |
| else if (tag == DW_TAG_subprogram) |
| { |
| decl_base_sptr r = |
| is_decl(build_ir_node_from_die(rdr, &child, |
| result.get(), |
| called_from_public_decl, |
| where_offset)); |
| if (!r) |
| continue; |
| |
| function_decl_sptr f = dynamic_pointer_cast<function_decl>(r); |
| ABG_ASSERT(f); |
| |
| finish_member_function_reading(&child, f, result, rdr); |
| |
| rdr.associate_die_to_decl(&child, f, where_offset, |
| /*associate_by_repr=*/false); |
| } |
| // Handle member types |
| else if (die_is_type(&child)) |
| decl_base_sptr td = |
| is_decl(build_ir_node_from_die(rdr, &child, result.get(), |
| called_from_public_decl, |
| where_offset)); |
| } while (dwarf_siblingof(&child, &child) == 0); |
| } |
| |
| rdr.scope_stack().pop(); |
| |
| { |
| die_class_or_union_map_type::const_iterator i = |
| rdr.die_wip_classes_map(source).find(dwarf_dieoffset(die)); |
| if (i != rdr.die_wip_classes_map(source).end()) |
| { |
| if (is_member_type(i->second)) |
| set_member_access_specifier(result, |
| get_member_access_specifier(i->second)); |
| rdr.die_wip_classes_map(source).erase(i); |
| } |
| } |
| |
| return result; |
| } |
| |
| /// build a qualified type from a DW_TAG_const_type, |
| /// DW_TAG_volatile_type or DW_TAG_restrict_type DIE. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the input DIE to read from. |
| /// |
| /// @param called_from_public_decl true if this function was called |
| /// from a context where either a public function or a public variable |
| /// is being built. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return the resulting qualified_type_def. |
| static type_base_sptr |
| build_qualified_type(reader& rdr, |
| Dwarf_Die* die, |
| bool called_from_public_decl, |
| size_t where_offset) |
| { |
| type_base_sptr result; |
| if (!die) |
| return result; |
| |
| unsigned tag = dwarf_tag(die); |
| |
| if (tag != DW_TAG_const_type |
| && tag != DW_TAG_volatile_type |
| && tag != DW_TAG_restrict_type) |
| return result; |
| |
| Dwarf_Die underlying_type_die; |
| decl_base_sptr utype_decl; |
| if (!die_die_attribute(die, DW_AT_type, underlying_type_die)) |
| // So, if no DW_AT_type is present, then this means (if we are |
| // looking at a debug info emitted by GCC) that we are looking |
| // at a qualified void type. |
| utype_decl = build_ir_node_for_void_type(rdr); |
| |
| if (!utype_decl) |
| utype_decl = is_decl(build_ir_node_from_die(rdr, &underlying_type_die, |
| called_from_public_decl, |
| where_offset)); |
| if (!utype_decl) |
| return result; |
| |
| // The call to build_ir_node_from_die() could have triggered the |
| // creation of the type for this DIE. In that case, just return it. |
| if (type_base_sptr t = rdr.lookup_type_from_die(die)) |
| { |
| result = t; |
| rdr.associate_die_to_type(die, result, where_offset); |
| return result; |
| } |
| |
| type_base_sptr utype = is_type(utype_decl); |
| ABG_ASSERT(utype); |
| |
| qualified_type_def::CV qual = qualified_type_def::CV_NONE; |
| if (tag == DW_TAG_const_type) |
| qual |= qualified_type_def::CV_CONST; |
| else if (tag == DW_TAG_volatile_type) |
| qual |= qualified_type_def::CV_VOLATILE; |
| else if (tag == DW_TAG_restrict_type) |
| qual |= qualified_type_def::CV_RESTRICT; |
| else |
| ABG_ASSERT_NOT_REACHED; |
| |
| if (!result) |
| result.reset(new qualified_type_def(utype, qual, location())); |
| |
| rdr.associate_die_to_type(die, result, where_offset); |
| |
| return result; |
| } |
| |
| /// Walk a tree of typedef of qualified arrays and schedule all type |
| /// nodes for canonicalization. |
| /// |
| /// This is to be used after an array tree has been cloned. In that |
| /// case, the newly cloned type nodes have to be scheduled for |
| /// canonicalization. |
| /// |
| /// This is a subroutine of maybe_strip_qualification. |
| /// |
| /// @param t the type node to be scheduled for canonicalization. |
| /// |
| /// @param rdr the DWARF reader to use. |
| static void |
| schedule_array_tree_for_late_canonicalization(const type_base_sptr& t, |
| reader &rdr) |
| { |
| if (typedef_decl_sptr type = is_typedef(t)) |
| { |
| schedule_array_tree_for_late_canonicalization(type->get_underlying_type(), |
| rdr); |
| rdr.schedule_type_for_late_canonicalization(t); |
| } |
| else if (qualified_type_def_sptr type = is_qualified_type(t)) |
| { |
| schedule_array_tree_for_late_canonicalization(type->get_underlying_type(), |
| rdr); |
| rdr.schedule_type_for_late_canonicalization(t); |
| } |
| else if (array_type_def_sptr type = is_array_type(t)) |
| { |
| for (vector<array_type_def::subrange_sptr>::const_iterator i = |
| type->get_subranges().begin(); |
| i != type->get_subranges().end(); |
| ++i) |
| { |
| if (!(*i)->get_scope()) |
| add_decl_to_scope(*i, rdr.cur_transl_unit()->get_global_scope()); |
| rdr.schedule_type_for_late_canonicalization(*i); |
| |
| } |
| schedule_array_tree_for_late_canonicalization(type->get_element_type(), |
| rdr); |
| rdr.schedule_type_for_late_canonicalization(type); |
| } |
| } |
| |
| /// Strip qualification from a qualified type, when it makes sense. |
| /// |
| /// DWARF constructs "const reference". This is redundant because a |
| /// reference is always const. The issue is these redundant types then |
| /// leak into the IR and make for bad diagnostics. |
| /// |
| /// This function thus strips the const qualifier from the type in |
| /// that case. It might contain code to strip other cases like this |
| /// in the future. |
| /// |
| /// @param t the type to strip const qualification from. |
| /// |
| /// @param rdr the @ref reader to use. |
| /// |
| /// @return the stripped type or just return @p t. |
| static decl_base_sptr |
| maybe_strip_qualification(const qualified_type_def_sptr t, |
| reader &rdr) |
| { |
| if (!t) |
| return t; |
| |
| decl_base_sptr result = t; |
| type_base_sptr u = t->get_underlying_type(); |
| |
| strip_redundant_quals_from_underyling_types(t); |
| result = strip_useless_const_qualification(t); |
| if (result.get() != t.get()) |
| return result; |
| |
| if (is_array_type(u) || is_typedef_of_array(u)) |
| { |
| array_type_def_sptr array; |
| scope_decl * scope = 0; |
| if ((array = is_array_type(u))) |
| { |
| scope = array->get_scope(); |
| ABG_ASSERT(scope); |
| array = is_array_type(clone_array_tree(array)); |
| schedule_array_tree_for_late_canonicalization(array, rdr); |
| add_decl_to_scope(array, scope); |
| t->set_underlying_type(array); |
| u = t->get_underlying_type(); |
| } |
| else if (is_typedef_of_array(u)) |
| { |
| scope = is_decl(u)->get_scope(); |
| ABG_ASSERT(scope); |
| typedef_decl_sptr typdef = |
| is_typedef(clone_array_tree(is_typedef(u))); |
| schedule_array_tree_for_late_canonicalization(typdef, rdr); |
| ABG_ASSERT(typdef); |
| add_decl_to_scope(typdef, scope); |
| t->set_underlying_type(typdef); |
| u = t->get_underlying_type(); |
| array = is_typedef_of_array(u); |
| } |
| else |
| ABG_ASSERT_NOT_REACHED; |
| |
| ABG_ASSERT(array); |
| // We should not be editing types that are already canonicalized. |
| ABG_ASSERT(!array->get_canonical_type()); |
| type_base_sptr element_type = array->get_element_type(); |
| |
| if (qualified_type_def_sptr qualified = is_qualified_type(element_type)) |
| { |
| // We should not be editing types that are already canonicalized. |
| ABG_ASSERT(!qualified->get_canonical_type()); |
| qualified_type_def::CV quals = qualified->get_cv_quals(); |
| quals |= t->get_cv_quals(); |
| qualified->set_cv_quals(quals); |
| strip_redundant_quals_from_underyling_types(qualified); |
| result = is_decl(u); |
| } |
| else |
| { |
| qualified_type_def_sptr qual_type |
| (new qualified_type_def(element_type, |
| t->get_cv_quals(), |
| t->get_location())); |
| strip_redundant_quals_from_underyling_types(qual_type); |
| add_decl_to_scope(qual_type, is_decl(element_type)->get_scope()); |
| array->set_element_type(qual_type); |
| rdr.schedule_type_for_late_canonicalization(is_type(qual_type)); |
| result = is_decl(u); |
| } |
| } |
| |
| return result; |
| } |
| |
| /// Build a pointer type from a DW_TAG_pointer_type DIE. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE to read information from. |
| /// |
| /// @param called_from_public_decl true if this function was called |
| /// from a context where either a public function or a public variable |
| /// is being built. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return the resulting pointer to pointer_type_def. |
| static pointer_type_def_sptr |
| build_pointer_type_def(reader& rdr, |
| Dwarf_Die* die, |
| bool called_from_public_decl, |
| size_t where_offset) |
| { |
| pointer_type_def_sptr result; |
| |
| if (!die) |
| return result; |
| |
| unsigned tag = dwarf_tag(die); |
| if (tag != DW_TAG_pointer_type) |
| return result; |
| |
| type_or_decl_base_sptr utype_decl; |
| Dwarf_Die underlying_type_die; |
| bool has_underlying_type_die = false; |
| if (!die_die_attribute(die, DW_AT_type, underlying_type_die)) |
| // If the DW_AT_type attribute is missing, that means we are |
| // looking at a pointer to "void". |
| utype_decl = build_ir_node_for_void_type(rdr); |
| else |
| has_underlying_type_die = true; |
| |
| if (!utype_decl && has_underlying_type_die) |
| utype_decl = build_ir_node_from_die(rdr, &underlying_type_die, |
| called_from_public_decl, |
| where_offset); |
| if (!utype_decl) |
| return result; |
| |
| // The call to build_ir_node_from_die() could have triggered the |
| // creation of the type for this DIE. In that case, just return it. |
| if (type_base_sptr t = rdr.lookup_type_from_die(die)) |
| { |
| result = is_pointer_type(t); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| type_base_sptr utype = is_type(utype_decl); |
| ABG_ASSERT(utype); |
| |
| // if the DIE for the pointer type doesn't have a byte_size |
| // attribute then we assume the size of the pointer is the address |
| // size of the current translation unit. |
| uint64_t size = rdr.cur_transl_unit()->get_address_size(); |
| if (die_unsigned_constant_attribute(die, DW_AT_byte_size, size)) |
| // The size as expressed by DW_AT_byte_size is in byte, so let's |
| // convert it to bits. |
| size *= 8; |
| |
| // And the size of the pointer must be the same as the address size |
| // of the current translation unit. |
| ABG_ASSERT((size_t) rdr.cur_transl_unit()->get_address_size() == size); |
| |
| result.reset(new pointer_type_def(utype, size, /*alignment=*/0, location())); |
| ABG_ASSERT(result->get_pointed_to_type()); |
| |
| if (is_void_pointer_type(result)) |
| result = is_pointer_type(build_ir_node_for_void_pointer_type(rdr)); |
| |
| rdr.associate_die_to_type(die, result, where_offset); |
| return result; |
| } |
| |
| /// Build a reference type from either a DW_TAG_reference_type or |
| /// DW_TAG_rvalue_reference_type DIE. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE to read from. |
| /// |
| /// @param called_from_public_decl true if this function was called |
| /// from a context where either a public function or a public variable |
| /// is being built. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return a pointer to the resulting reference_type_def. |
| static reference_type_def_sptr |
| build_reference_type(reader& rdr, |
| Dwarf_Die* die, |
| bool called_from_public_decl, |
| size_t where_offset) |
| { |
| reference_type_def_sptr result; |
| |
| if (!die) |
| return result; |
| |
| unsigned tag = dwarf_tag(die); |
| if (tag != DW_TAG_reference_type |
| && tag != DW_TAG_rvalue_reference_type) |
| return result; |
| |
| Dwarf_Die underlying_type_die; |
| if (!die_die_attribute(die, DW_AT_type, underlying_type_die)) |
| return result; |
| |
| type_or_decl_base_sptr utype_decl = |
| build_ir_node_from_die(rdr, &underlying_type_die, |
| called_from_public_decl, |
| where_offset); |
| if (!utype_decl) |
| return result; |
| |
| // The call to build_ir_node_from_die() could have triggered the |
| // creation of the type for this DIE. In that case, just return it. |
| if (type_base_sptr t = rdr.lookup_type_from_die(die)) |
| { |
| result = is_reference_type(t); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| type_base_sptr utype = is_type(utype_decl); |
| ABG_ASSERT(utype); |
| |
| // if the DIE for the reference type doesn't have a byte_size |
| // attribute then we assume the size of the reference is the address |
| // size of the current translation unit. |
| uint64_t size = rdr.cur_transl_unit()->get_address_size(); |
| if (die_unsigned_constant_attribute(die, DW_AT_byte_size, size)) |
| size *= 8; |
| |
| // And the size of the pointer must be the same as the address size |
| // of the current translation unit. |
| ABG_ASSERT((size_t) rdr.cur_transl_unit()->get_address_size() == size); |
| |
| bool is_lvalue = tag == DW_TAG_reference_type; |
| |
| result.reset(new reference_type_def(utype, is_lvalue, size, |
| /*alignment=*/0, |
| location())); |
| if (corpus_sptr corp = rdr.corpus()) |
| if (reference_type_def_sptr t = lookup_reference_type(*result, *corp)) |
| result = t; |
| rdr.associate_die_to_type(die, result, where_offset); |
| return result; |
| } |
| |
| /// Build an instance of @ref ptr_to_mbr_type from a DIE of tag |
| /// DW_TAG_ptr_to_member_type. |
| /// |
| /// @param the DWARF reader touse. |
| /// |
| /// @param the DIE to consider. It must carry the tag |
| /// DW_TAG_ptr_to_member_type. |
| /// |
| /// @param called_from_public_decl true if this function was called |
| /// from a context where either a public function or a public variable |
| /// is being built. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return a pointer to the resulting @ref ptr_to_mbr_type. |
| static ptr_to_mbr_type_sptr |
| build_ptr_to_mbr_type(reader& rdr, |
| Dwarf_Die* die, |
| bool called_from_public_decl, |
| size_t where_offset) |
| { |
| ptr_to_mbr_type_sptr result; |
| |
| if (!die) |
| return result; |
| |
| unsigned tag = dwarf_tag(die); |
| if (tag != DW_TAG_ptr_to_member_type) |
| return result; |
| |
| Dwarf_Die data_member_type_die, containing_type_die; |
| |
| if (!die_die_attribute(die, DW_AT_type, data_member_type_die) |
| || !die_die_attribute(die, DW_AT_containing_type, containing_type_die)) |
| return result; |
| |
| type_or_decl_base_sptr data_member_type = |
| build_ir_node_from_die(rdr, &data_member_type_die, |
| called_from_public_decl, where_offset); |
| if (!data_member_type) |
| return result; |
| |
| type_or_decl_base_sptr containing_type = |
| build_ir_node_from_die(rdr, &containing_type_die, |
| called_from_public_decl, where_offset); |
| if (!containing_type) |
| return result; |
| |
| if (!is_typedef_of_maybe_qualified_class_or_union_type |
| (is_type(containing_type))) |
| return result; |
| |
| if (type_base_sptr t = rdr.lookup_type_from_die(die)) |
| { |
| result = is_ptr_to_mbr_type(t); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| uint64_t size_in_bits = rdr.cur_transl_unit()->get_address_size(); |
| |
| result.reset(new ptr_to_mbr_type(data_member_type->get_environment(), |
| is_type(data_member_type), |
| is_type(containing_type), |
| size_in_bits, |
| /*alignment=*/0, |
| location())); |
| |
| rdr.associate_die_to_type(die, result, where_offset); |
| return result; |
| } |
| |
| /// Build a subroutine type from a DW_TAG_subroutine_type DIE. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE to read from. |
| /// |
| /// @param is_method points to a class or union declaration iff we're |
| /// building the type for a method. This is the enclosing class or |
| /// union of the method. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positioned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return a pointer to the resulting function_type_sptr. |
| static function_type_sptr |
| build_function_type(reader& rdr, |
| Dwarf_Die* die, |
| class_or_union_sptr is_method, |
| size_t where_offset) |
| { |
| function_type_sptr result; |
| |
| if (!die) |
| return result; |
| |
| ABG_ASSERT(dwarf_tag(die) == DW_TAG_subroutine_type |
| || dwarf_tag(die) == DW_TAG_subprogram); |
| |
| const die_source source = rdr.get_die_source(die); |
| |
| { |
| size_t off = dwarf_dieoffset(die); |
| auto i = rdr.die_wip_function_types_map(source).find(off); |
| if (i != rdr.die_wip_function_types_map(source).end()) |
| { |
| function_type_sptr fn_type = is_function_type(i->second); |
| ABG_ASSERT(fn_type); |
| return fn_type; |
| } |
| } |
| |
| decl_base_sptr type_decl; |
| |
| translation_unit_sptr tu = rdr.cur_transl_unit(); |
| ABG_ASSERT(tu); |
| |
| /// If, inside the current translation unit, we've already seen a |
| /// function type with the same text representation, then reuse that |
| /// one instead. |
| if (type_base_sptr t = rdr.lookup_fn_type_from_die_repr_per_tu(die)) |
| { |
| result = is_function_type(t); |
| ABG_ASSERT(result); |
| rdr.associate_die_to_type(die, result, where_offset); |
| return result; |
| } |
| |
| bool odr_is_relevant = rdr.odr_is_relevant(die); |
| if (odr_is_relevant) |
| { |
| // So we can rely on the One Definition Rule to say that if |
| // several different function types have the same name (or |
| // rather, representation) across the entire binary, then they |
| // ought to designate the same function type. So let's ensure |
| // that if we've already seen a function type with the same |
| // representation as the function type 'die', then it's the same |
| // type as the one denoted by 'die'. |
| if (function_type_sptr fn_type = |
| is_function_type(rdr.lookup_type_artifact_from_die(die))) |
| { |
| rdr.associate_die_to_type(die, fn_type, where_offset); |
| return fn_type; |
| } |
| } |
| |
| // Let's look at the DIE to detect if it's the DIE for a method |
| // (type). If it is, we can deduce the name of its enclosing class |
| // and if it's a static or const. |
| bool is_const = false; |
| bool is_static = false; |
| Dwarf_Die object_pointer_die; |
| Dwarf_Die class_type_die; |
| bool has_this_parm_die = |
| die_function_type_is_method_type(rdr, die, where_offset, |
| object_pointer_die, |
| class_type_die, |
| is_static); |
| if (has_this_parm_die) |
| { |
| // The function (type) has a "this" parameter DIE. It means it's |
| // a member function DIE. |
| if (!is_static) |
| if (die_object_pointer_is_for_const_method(&object_pointer_die)) |
| is_const = true; |
| |
| if (!is_method) |
| { |
| // We were initially called as if the function represented |
| // by DIE was *NOT* a member function. But now we know it's |
| // a member function. Let's take that into account. |
| class_or_union_sptr klass_type = |
| is_class_or_union_type(build_ir_node_from_die(rdr, &class_type_die, |
| /*called_from_pub_decl=*/true, |
| where_offset)); |
| ABG_ASSERT(klass_type); |
| is_method = klass_type; |
| } |
| } |
| |
| // Let's create the type early and record it as being for the DIE |
| // 'die'. This way, when building the sub-type triggers the |
| // creation of a type matching the same 'die', then we'll reuse this |
| // one. |
| |
| result.reset(is_method |
| ? new method_type(is_method, is_const, |
| tu->get_address_size(), |
| /*alignment=*/0) |
| : new function_type(rdr.env(), tu->get_address_size(), |
| /*alignment=*/0)); |
| rdr.associate_die_to_type(die, result, where_offset); |
| rdr.die_wip_function_types_map(source)[dwarf_dieoffset(die)] = result; |
| |
| type_base_sptr return_type; |
| Dwarf_Die ret_type_die; |
| if (die_die_attribute(die, DW_AT_type, ret_type_die)) |
| return_type = |
| is_type(build_ir_node_from_die(rdr, &ret_type_die, |
| /*called_from_public_decl=*/true, |
| where_offset)); |
| if (!return_type) |
| return_type = is_type(build_ir_node_for_void_type(rdr)); |
| result->set_return_type(return_type); |
| |
| Dwarf_Die child; |
| function_decl::parameters function_parms; |
| |
| if (dwarf_child(die, &child) == 0) |
| do |
| { |
| int child_tag = dwarf_tag(&child); |
| if (child_tag == DW_TAG_formal_parameter) |
| { |
| // This is a "normal" function parameter. |
| string name, linkage_name; |
| location loc; |
| die_loc_and_name(rdr, &child, loc, name, linkage_name); |
| if (!tools_utils::string_is_ascii_identifier(name)) |
| // Sometimes, bogus compiler emit names that are |
| // non-ascii garbage. Let's just ditch that for now. |
| name.clear(); |
| bool is_artificial = die_is_artificial(&child); |
| type_base_sptr parm_type; |
| Dwarf_Die parm_type_die; |
| if (die_die_attribute(&child, DW_AT_type, parm_type_die)) |
| parm_type = |
| is_type(build_ir_node_from_die(rdr, &parm_type_die, |
| /*called_from_public_decl=*/true, |
| where_offset)); |
| if (!parm_type) |
| continue; |
| if (is_method |
| && is_const_qualified_type(parm_type) |
| && function_parms.empty()) |
| // We are looking at the first (implicit) parameter of a |
| // method. This is basically the "this pointer". For |
| // concrete instances of abstract methods, GCC sometimes |
| // represents that pointer as a const pointer, whereas |
| // in the abstract interface representing that method |
| // the this-pointer is represented as a non-qualified |
| // pointer. Let's trim the const qualifier away. That |
| // will minize the chance to have spurious |
| // const-qualifier changes on implicit parameters when |
| // comparing methods that otherwise have no meaningful |
| // ABI changes. |
| parm_type = |
| peel_const_qualified_type(is_qualified_type(parm_type)); |
| |
| function_decl::parameter_sptr p |
| (new function_decl::parameter(parm_type, name, loc, |
| /*variadic_marker=*/false, |
| is_artificial)); |
| function_parms.push_back(p); |
| } |
| else if (child_tag == DW_TAG_unspecified_parameters) |
| { |
| // This is a variadic function parameter. |
| bool is_artificial = die_is_artificial(&child); |
| |
| type_base_sptr parm_type = |
| is_type(build_ir_node_for_variadic_parameter_type(rdr)); |
| function_decl::parameter_sptr p |
| (new function_decl::parameter(parm_type, |
| /*name=*/"", |
| location(), |
| /*variadic_marker=*/true, |
| is_artificial)); |
| function_parms.push_back(p); |
| // After a DW_TAG_unspecified_parameters tag, we shouldn't |
| // keep reading for parameters. The |
| // unspecified_parameters TAG should be the last parameter |
| // that we record. For instance, if there are multiple |
| // DW_TAG_unspecified_parameters DIEs then we should care |
| // only for the first one. |
| break; |
| } |
| } |
| while (dwarf_siblingof(&child, &child) == 0); |
| |
| result->set_parameters(function_parms); |
| |
| tu->bind_function_type_life_time(result); |
| |
| result->set_is_artificial(true); |
| |
| rdr.associate_die_repr_to_fn_type_per_tu(die, result); |
| |
| { |
| die_function_type_map_type::const_iterator i = |
| rdr.die_wip_function_types_map(source). |
| find(dwarf_dieoffset(die)); |
| if (i != rdr.die_wip_function_types_map(source).end()) |
| rdr.die_wip_function_types_map(source).erase(i); |
| } |
| |
| maybe_canonicalize_type(result, rdr); |
| return result; |
| } |
| |
| /// Build a subrange type from a DW_TAG_subrange_type. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE to read from. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at in the DIE tree. This is useful when @p die is |
| /// e,g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @param associate_die_to_type if this is true then the resulting |
| /// type is associated to the @p die, so that next time when the |
| /// system looks up the type associated to it, the current resulting |
| /// type is returned. If false, then no association is done and the |
| /// resulting type can be destroyed right after. This can be useful |
| /// when the sole purpose of building the @ref |
| /// array_type_def::subrange_type is to use some of its method like, |
| /// e.g, its name pretty printing methods. |
| /// |
| /// @return the newly built instance of @ref |
| /// array_type_def::subrange_type, or nil if no type could be built. |
| static array_type_def::subrange_sptr |
| build_subrange_type(reader& rdr, |
| const Dwarf_Die* die, |
| size_t where_offset, |
| bool associate_type_to_die) |
| { |
| array_type_def::subrange_sptr result; |
| |
| if (!die) |
| return result; |
| |
| unsigned tag = dwarf_tag(const_cast<Dwarf_Die*>(die)); |
| if (tag != DW_TAG_subrange_type) |
| return result; |
| |
| string name = die_name(die); |
| |
| // load the underlying type. |
| Dwarf_Die underlying_type_die; |
| type_base_sptr underlying_type; |
| /* Unless there is an underlying type which says differently. */ |
| bool is_signed = false; |
| if (die_die_attribute(die, DW_AT_type, underlying_type_die)) |
| underlying_type = |
| is_type(build_ir_node_from_die(rdr, |
| &underlying_type_die, |
| /*called_from_public_decl=*/true, |
| where_offset)); |
| |
| if (underlying_type) |
| { |
| uint64_t ate; |
| if (die_unsigned_constant_attribute (&underlying_type_die, |
| DW_AT_encoding, |
| ate)) |
| is_signed = (ate == DW_ATE_signed || ate == DW_ATE_signed_char); |
| } |
| |
| translation_unit::language language = rdr.cur_transl_unit()->get_language(); |
| array_type_def::subrange_type::bound_value lower_bound = |
| get_default_array_lower_bound(language); |
| array_type_def::subrange_type::bound_value upper_bound; |
| uint64_t count = 0; |
| bool is_non_finite = false; |
| bool non_zero_count_present = false; |
| |
| // The DWARF 4 specifications says, in [5.11 Subrange |
| // Type Entries]: |
| // |
| // The subrange entry may have the attributes |
| // DW_AT_lower_bound and DW_AT_upper_bound to |
| // specify, respectively, the lower and upper bound |
| // values of the subrange. |
| // |
| // So let's look for DW_AT_lower_bound first. |
| die_constant_attribute(die, DW_AT_lower_bound, is_signed, lower_bound); |
| |
| bool found_upper_bound = die_constant_attribute(die, DW_AT_upper_bound, |
| is_signed, upper_bound); |
| if (!found_upper_bound) |
| found_upper_bound = subrange_die_indirect_bound_value(die, |
| DW_AT_upper_bound, |
| upper_bound, |
| is_signed); |
| // Then, DW_AT_upper_bound. |
| if (!found_upper_bound) |
| { |
| // The DWARF 4 spec says, in [5.11 Subrange Type |
| // Entries]: |
| // |
| // The DW_AT_upper_bound attribute may be replaced |
| // by a DW_AT_count attribute, whose value |
| // describes the number of elements in the |
| // subrange rather than the value of the last |
| // element." |
| // |
| // So, as DW_AT_upper_bound is not present in this |
| // case, let's see if there is a DW_AT_count. |
| if (die_unsigned_constant_attribute(die, DW_AT_count, count)) |
| { |
| if (count) |
| // DW_AT_count can be present and be set to zero. This is |
| // for instance the case to model this gcc extension to |
| // represent flexible arrays: |
| // https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html. |
| // For instance: int flex_array[0]; |
| non_zero_count_present = true; |
| |
| // When the count is present and non-zero, we can deduce the |
| // upper_bound from the lower_bound and the number of |
| // elements of the array: |
| int64_t u = lower_bound.get_signed_value() + count; |
| if (u) |
| upper_bound = u - 1; |
| } |
| |
| if (!non_zero_count_present) |
| // No upper_bound nor count was present on the DIE, this means |
| // the array is considered to have an infinite (or rather not |
| // known) size. |
| is_non_finite = true; |
| } |
| |
| if (UINT64_MAX == upper_bound.get_unsigned_value()) |
| // If the upper_bound size is the max of the integer value |
| // then it most certainly means unknown size. |
| is_non_finite = true; |
| |
| result.reset |
| (new array_type_def::subrange_type(rdr.env(), |
| name, |
| lower_bound, |
| upper_bound, |
| location())); |
| result->is_non_finite(is_non_finite); |
| |
| if (underlying_type) |
| result->set_underlying_type(underlying_type); |
| |
| // Let's ensure the resulting subrange looks metabolically healhty. |
| ABG_ASSERT(result->is_non_finite() |
| || (result->get_length() == |
| (uint64_t) (result->get_upper_bound() |
| - result->get_lower_bound() + 1))); |
| |
| if (associate_type_to_die) |
| rdr.associate_die_to_type(die, result, where_offset); |
| |
| return result; |
| } |
| |
| /// Build the sub-ranges of an array type. |
| /// |
| /// This is a sub-routine of build_array_type(). |
| /// |
| /// @param rdr the context to read from. |
| /// |
| /// @param die the DIE of tag DW_TAG_array_type which contains |
| /// children DIEs that represent the sub-ranges. |
| /// |
| /// @param subranges out parameter. This is set to the sub-ranges |
| /// that are built from @p die. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positioned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| static void |
| build_subranges_from_array_type_die(reader& rdr, |
| const Dwarf_Die* die, |
| array_type_def::subranges_type& subranges, |
| size_t where_offset, |
| bool associate_type_to_die) |
| { |
| Dwarf_Die child; |
| |
| if (dwarf_child(const_cast<Dwarf_Die*>(die), &child) == 0) |
| { |
| do |
| { |
| int child_tag = dwarf_tag(&child); |
| if (child_tag == DW_TAG_subrange_type) |
| { |
| array_type_def::subrange_sptr s; |
| if (associate_type_to_die) |
| { |
| // We are being called to create the type, add it to |
| // the current type graph and associate it to the |
| // DIE it's been created from. |
| type_or_decl_base_sptr t = |
| build_ir_node_from_die(rdr, &child, |
| /*called_from_public_decl=*/true, |
| where_offset); |
| s = is_subrange_type(t); |
| } |
| else |
| // We are being called to create the type but *NOT* |
| // add it to the current tyupe tree, *NOR* associate |
| // it to the DIE it's been created from. |
| s = build_subrange_type(rdr, &child, |
| where_offset, |
| /*associate_type_to_die=*/false); |
| if (s) |
| subranges.push_back(s); |
| } |
| } |
| while (dwarf_siblingof(&child, &child) == 0); |
| } |
| } |
| |
| /// Build an array type from a DW_TAG_array_type DIE. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE to read from. |
| /// |
| /// @param called_from_public_decl true if this function was called |
| /// from a context where either a public function or a public variable |
| /// is being built. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positioned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return a pointer to the resulting array_type_def. |
| static array_type_def_sptr |
| build_array_type(reader& rdr, |
| Dwarf_Die* die, |
| bool called_from_public_decl, |
| size_t where_offset) |
| { |
| array_type_def_sptr result; |
| |
| if (!die) |
| return result; |
| |
| unsigned tag = dwarf_tag(die); |
| if (tag != DW_TAG_array_type) |
| return result; |
| |
| decl_base_sptr type_decl; |
| Dwarf_Die type_die; |
| |
| if (die_die_attribute(die, DW_AT_type, type_die)) |
| type_decl = is_decl(build_ir_node_from_die(rdr, &type_die, |
| called_from_public_decl, |
| where_offset)); |
| if (!type_decl) |
| return result; |
| |
| // The call to build_ir_node_from_die() could have triggered the |
| // creation of the type for this DIE. In that case, just return it. |
| if (type_base_sptr t = rdr.lookup_type_from_die(die)) |
| { |
| result = is_array_type(t); |
| ABG_ASSERT(result); |
| return result; |
| } |
| |
| type_base_sptr type = is_type(type_decl); |
| ABG_ASSERT(type); |
| |
| array_type_def::subranges_type subranges; |
| |
| build_subranges_from_array_type_die(rdr, die, subranges, where_offset); |
| |
| result.reset(new array_type_def(type, subranges, location())); |
| |
| return result; |
| } |
| |
| /// Create a typedef_decl from a DW_TAG_typedef DIE. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param die the DIE to read from. |
| /// |
| /// @param called_from_public_decl true if this function was called |
| /// from a context where either a public function or a public variable |
| /// is being built. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return the newly created typedef_decl. |
| static typedef_decl_sptr |
| build_typedef_type(reader& rdr, |
| Dwarf_Die* die, |
| bool called_from_public_decl, |
| size_t where_offset) |
| { |
| typedef_decl_sptr result; |
| |
| if (!die) |
| return result; |
| |
| unsigned tag = dwarf_tag(die); |
| if (tag != DW_TAG_typedef) |
| return result; |
| |
| string name, linkage_name; |
| location loc; |
| die_loc_and_name(rdr, die, loc, name, linkage_name); |
| |
| if (corpus_sptr corp = rdr.should_reuse_type_from_corpus_group()) |
| if (loc) |
| result = lookup_typedef_type_per_location(loc.expand(), *corp); |
| |
| if (!result) |
| { |
| type_base_sptr utype; |
| Dwarf_Die underlying_type_die; |
| if (!die_die_attribute(die, DW_AT_type, underlying_type_die)) |
| // A typedef DIE with no underlying type means a typedef to |
| // void type. |
| utype = rdr.env().get_void_type(); |
| |
| if (!utype) |
| utype = |
| is_type(build_ir_node_from_die(rdr, |
| &underlying_type_die, |
| called_from_public_decl, |
| where_offset)); |
| if (!utype) |
| return result; |
| |
| ABG_ASSERT(utype); |
| result.reset(new typedef_decl(name, utype, loc, linkage_name)); |
| |
| if ((is_class_or_union_type(utype) || is_enum_type(utype)) |
| && is_anonymous_type(utype)) |
| { |
| // This is a naming typedef for an enum or a class. Let's |
| // mark the underlying decl as such. |
| decl_base_sptr decl = is_decl(utype); |
| ABG_ASSERT(decl); |
| decl->set_naming_typedef(result); |
| if (is_class_or_union_type(utype)) |
| rdr.maybe_schedule_declaration_only_class_for_resolution |
| (is_class_or_union_type(utype)); |
| else if (is_enum_type(utype)) |
| rdr.maybe_schedule_declaration_only_enum_for_resolution |
| (is_enum_type(utype)); |
| } |
| } |
| |
| rdr.associate_die_to_type(die, result, where_offset); |
| |
| return result; |
| } |
| |
| /// Build a @ref var_decl out of a DW_TAG_variable DIE if the variable |
| /// denoted by the DIE is not suppressed by a suppression |
| /// specification associated to the current DWARF reader. |
| /// |
| /// Note that if a member variable declaration with the same name as |
| /// the name of the DIE we are looking at exists, this function returns |
| /// that existing variable declaration. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the DIE representing the variable we are looking at. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @param is_declaration_only if true, it means the variable DIE has |
| /// the is_declaration_only only attribute. |
| /// |
| /// @param result if this is set to an existing var_decl, this means |
| /// that the function will append the new properties it sees on @p die |
| /// to that exising var_decl. Otherwise, if this parameter is NULL, a |
| /// new var_decl is going to be allocated and returned. |
| /// |
| /// @param is_required_decl_spec this is true iff the variable to |
| /// build is referred to as being the specification of another |
| /// variable. |
| /// |
| /// @return a pointer to the newly created var_decl. If the var_decl |
| /// could not be built, this function returns NULL. |
| static var_decl_sptr |
| build_or_get_var_decl_if_not_suppressed(reader& rdr, |
| scope_decl *scope, |
| Dwarf_Die *die, |
| size_t where_offset, |
| bool is_declaration_only, |
| var_decl_sptr result, |
| bool is_required_decl_spec) |
| { |
| var_decl_sptr var; |
| if (variable_is_suppressed(rdr, scope, die, |
| is_declaration_only, |
| is_required_decl_spec)) |
| return var; |
| |
| if (class_decl* class_type = is_class_type(scope)) |
| { |
| string var_name = die_name(die); |
| if (!var_name.empty()) |
| if ((var = class_type->find_data_member(var_name))) |
| return var; |
| } |
| var = build_var_decl(rdr, die, where_offset, result); |
| return var; |
| } |
| |
| /// Build a @ref var_decl out of a DW_TAG_variable DIE. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param die the DIE representing the variable we are looking at. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @param result if this is set to an existing var_decl, this means |
| /// that the function will append the new properties it sees on @p die |
| /// to that exising var_decl. Otherwise, if this parameter is NULL, a |
| /// new var_decl is going to be allocated and returned. |
| /// |
| /// @return a pointer to the newly created var_decl. If the var_decl |
| /// could not be built, this function returns NULL. |
| static var_decl_sptr |
| build_var_decl(reader& rdr, |
| Dwarf_Die *die, |
| size_t where_offset, |
| var_decl_sptr result) |
| { |
| if (!die) |
| return result; |
| |
| int tag = dwarf_tag(die); |
| ABG_ASSERT(tag == DW_TAG_variable || tag == DW_TAG_member); |
| |
| if (!die_is_public_decl(die)) |
| return result; |
| |
| type_base_sptr type; |
| Dwarf_Die type_die; |
| if (die_die_attribute(die, DW_AT_type, type_die)) |
| { |
| decl_base_sptr ty = |
| is_decl(build_ir_node_from_die(rdr, &type_die, |
| /*called_from_public_decl=*/true, |
| where_offset)); |
| if (!ty) |
| return result; |
| type = is_type(ty); |
| ABG_ASSERT(type); |
| } |
| |
| if (!type && !result) |
| return result; |
| |
| string name, linkage_name; |
| location loc; |
| die_loc_and_name(rdr, die, loc, name, linkage_name); |
| |
| if (!result) |
| result.reset(new var_decl(name, type, loc, linkage_name)); |
| else |
| { |
| // We were called to append properties that might have been |
| // missing from the first version of the variable. And usually |
| // that missing property is the mangled name or the type. |
| if (!linkage_name.empty()) |
| result->set_linkage_name(linkage_name); |
| |
| if (type) |
| result->set_type(type); |
| } |
| |
| // Check if a variable symbol with this name is exported by the elf |
| // binary. If it is, then set the symbol of the variable, if it's |
| // not set already. |
| if (!result->get_symbol()) |
| { |
| elf_symbol_sptr var_sym; |
| Dwarf_Addr var_addr; |
| |
| if (rdr.get_variable_address(die, var_addr)) |
| { |
| rdr.symtab()-> |
| update_main_symbol(var_addr, |
| result->get_linkage_name().empty() |
| ? result->get_name() |
| : result->get_linkage_name()); |
| var_sym = rdr.variable_symbol_is_exported(var_addr); |
| } |
| |
| if (var_sym) |
| { |
| result->set_symbol(var_sym); |
| // If the linkage name is not set or is wrong, set it to |
| // the name of the underlying symbol. |
| string linkage_name = result->get_linkage_name(); |
| if (linkage_name.empty() |
| || !var_sym->get_alias_from_name(linkage_name)) |
| result->set_linkage_name(var_sym->get_name()); |
| result->set_is_in_public_symbol_table(true); |
| } |
| |
| if (!var_sym && rdr.is_decl_die_with_undefined_symbol(die)) |
| { |
| // We are looking at a global variable which symbol is |
| // undefined. Let's set its symbol. |
| string n = result->get_linkage_name(); |
| if (n.empty()) |
| n = result->get_name(); |
| var_sym = rdr.symtab()->lookup_undefined_variable_symbol(n); |
| if (var_sym) |
| { |
| result->set_symbol(var_sym); |
| result->set_is_in_public_symbol_table(false); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /// Test if a given function denoted by its DIE and its scope is |
| /// suppressed by any of the suppression specifications associated to |
| /// a given context of ELF/DWARF reading. |
| /// |
| /// Note that a non-member function which symbol is not exported is |
| /// also suppressed. |
| /// |
| /// @param rdr the ELF/DWARF reading content of interest. |
| /// |
| /// @param scope of the scope of the function. |
| /// |
| /// @param function_die the DIE representing the function. |
| /// |
| /// @param is_declaration_only is true if the DIE denoted by @p die is |
| /// a declaration-only DIE. |
| /// |
| /// @return true iff @p function_die is suppressed by at least one |
| /// suppression specification attached to the @p rdr. |
| static bool |
| function_is_suppressed(const reader& rdr, |
| const scope_decl* scope, |
| Dwarf_Die *function_die, |
| bool is_declaration_only) |
| { |
| if (function_die == 0 |
| || dwarf_tag(function_die) != DW_TAG_subprogram) |
| return false; |
| |
| string fname = die_string_attribute(function_die, DW_AT_name); |
| string flinkage_name = die_linkage_name(function_die); |
| if (flinkage_name.empty() && die_is_in_c(function_die)) |
| flinkage_name = fname; |
| string qualified_name = build_qualified_name(scope, fname); |
| |
| // A non-member non-static function which symbol is not exported is |
| // suppressed. |
| // |
| // Note that if the non-member non-static function has an undefined |
| // symbol, by default, it's not suppressed. Unless we are asked to |
| // drop undefined symbols too. |
| if (!is_class_type(scope) |
| && (!is_declaration_only || rdr.drop_undefined_syms())) |
| { |
| Dwarf_Addr fn_addr; |
| if (!rdr.get_function_address(function_die, fn_addr)) |
| return true; |
| |
| elf_symbol_sptr symbol = |
| rdr.function_symbol_is_exported(fn_addr); |
| if (!symbol) |
| return true; |
| if (!symbol->is_suppressed()) |
| return false; |
| |
| // Since there is only one symbol in DWARF associated with an elf_symbol, |
| // we can assume this is the main symbol then. Otherwise the main hinting |
| // did not work as expected. |
| ABG_ASSERT(symbol->is_main_symbol()); |
| if (symbol->has_aliases()) |
| for (elf_symbol_sptr a = symbol->get_next_alias(); |
| !a->is_main_symbol(); a = a->get_next_alias()) |
| if (!a->is_suppressed()) |
| return false; |
| } |
| |
| return suppr::is_function_suppressed(rdr, qualified_name, flinkage_name, |
| /*require_drop_property=*/true); |
| } |
| |
| /// Build a @ref function_decl out of a DW_TAG_subprogram DIE if the |
| /// function denoted by the DIE is not suppressed by a suppression |
| /// specification associated to the current DWARF reader. |
| /// |
| /// Note that if a member function declaration with the same signature |
| /// (pretty representation) as one of the DIE we are looking at |
| /// exists, this function returns that existing function declaration. |
| /// Similarly, if there is already a constructed member function with |
| /// the same linkage name as the one on the DIE, this function returns |
| /// that member function. |
| /// |
| /// Also note that the function_decl IR returned by this function must |
| /// be passed to finish_member_function_reading because several |
| /// properties from the DIE are actually read by that function, and |
| /// the corresponding properties on the function_decl IR are updated |
| /// accordingly. This is done to support "updating" a function_decl |
| /// IR with properties scathered across several DIEs. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @param scope the scope of the function we are looking at. |
| /// |
| /// @param fn_die the DIE representing the function we are looking at. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @param is_declaration_only is true if the DIE denoted by @p fn_die |
| /// is a declaration-only DIE. |
| /// |
| /// @param result if this is set to an existing function_decl, this |
| /// means that the function will append the new properties it sees on |
| /// @p fn_die to that exising function_decl. Otherwise, if this |
| /// parameter is NULL, a new function_decl is going to be allocated |
| /// and returned. |
| /// |
| /// @return a pointer to the newly created var_decl. If the var_decl |
| /// could not be built, this function returns NULL. |
| static function_decl_sptr |
| build_or_get_fn_decl_if_not_suppressed(reader& rdr, |
| scope_decl *scope, |
| Dwarf_Die *fn_die, |
| size_t where_offset, |
| bool is_declaration_only, |
| function_decl_sptr result) |
| { |
| function_decl_sptr fn; |
| if (function_is_suppressed(rdr, scope, fn_die, is_declaration_only)) |
| return fn; |
| |
| string name = die_name(fn_die); |
| string linkage_name = die_linkage_name(fn_die); |
| bool is_dtor = !name.empty() && name[0]== '~'; |
| bool is_virtual = false; |
| if (is_dtor) |
| { |
| Dwarf_Attribute attr; |
| if (dwarf_attr_integrate(const_cast<Dwarf_Die*>(fn_die), |
| DW_AT_vtable_elem_location, |
| &attr)) |
| is_virtual = true; |
| } |
| |
| |
| // If we've already built an IR for a function with the same |
| // signature (from another DIE), reuse it, unless that function is a |
| // virtual C++ destructor. Several virtual C++ destructors with the |
| // same signature can be implemented by several different ELF |
| // symbols. So re-using C++ destructors like that can lead to us |
| // missing some destructors. |
| if (!result && (!(is_dtor && is_virtual))) |
| if ((fn = is_function_decl(rdr.lookup_artifact_from_die(fn_die)))) |
| { |
| fn = maybe_finish_function_decl_reading(rdr, fn_die, where_offset, fn); |
| rdr.associate_die_to_decl(fn_die, fn, /*do_associate_by_repr=*/true); |
| rdr.associate_die_to_type(fn_die, fn->get_type(), where_offset); |
| return fn; |
| } |
| |
| // If a member function with the same linkage name as the one |
| // carried by the DIE already exists, then return it. |
| if (class_decl* klass = is_class_type(scope)) |
| { |
| string linkage_name = die_linkage_name(fn_die); |
| fn = klass->find_member_function_sptr(linkage_name); |
| if (fn) |
| // We found a member function that has the same signature. |
| // Let's mark it for update. |
| result = fn; |
| } |
| |
| if (!fn || !fn->get_symbol()) |
| // We haven't yet been able to construct a function IR, or, we |
| // have one 'partial' function IR that doesn't have any associated |
| // symbol yet. Note that in the later case, a function IR without |
| // any associated symbol will be dropped on the floor by |
| // potential_member_fn_should_be_dropped. So let's build or a new |
| // function IR or complete the existing partial IR. |
| fn = build_function_decl(rdr, fn_die, where_offset, result); |
| |
| return fn; |
| } |
| |
| /// Test if a given variable denoted by its DIE and its scope is |
| /// suppressed by any of the suppression specifications associated to |
| /// a given context of ELF/DWARF reading. |
| /// |
| /// @param rdr the ELF/DWARF reading content of interest. |
| /// |
| /// @param scope of the scope of the variable. |
| /// |
| /// @param variable_die the DIE representing the variable. |
| /// |
| /// @param is_declaration_only true if the variable is supposed to be |
| /// decl-only. |
| /// |
| /// @param is_required_decl_spec if true, means that the @p |
| /// variable_die being considered is for a variable decl that is a |
| /// specification for a concrete variable being built. |
| /// |
| /// @return true iff @p variable_die is suppressed by at least one |
| /// suppression specification attached to the @p rdr. |
| static bool |
| variable_is_suppressed(const reader& rdr, |
| const scope_decl* scope, |
| Dwarf_Die *variable_die, |
| bool is_declaration_only, |
| bool is_required_decl_spec) |
| { |
| if (variable_die == 0 |
| || (dwarf_tag(variable_die) != DW_TAG_variable |
| && dwarf_tag(variable_die) != DW_TAG_member)) |
| return false; |
| |
| string name = die_string_attribute(variable_die, DW_AT_name); |
| string linkage_name = die_linkage_name(variable_die); |
| if (linkage_name.empty() && die_is_in_c(variable_die)) |
| linkage_name = name; |
| string qualified_name = build_qualified_name(scope, name); |
| |
| // If a non member variable that is a declaration (has no defined |
| // and exported symbol) and is not the specification of another |
| // concrete variable, then it's suppressed. This is a size |
| // optimization; it removes useless declaration-only variables from |
| // the IR. |
| if (!is_class_type(scope) |
| && !is_required_decl_spec |
| // If we are asked to load undefined interfaces, then we don't |
| // suppress declaration-only variables as they might have |
| // undefined elf-symbols. |
| && (!is_declaration_only || !rdr.load_undefined_interfaces())) |
| { |
| Dwarf_Addr var_addr = 0; |
| if (!rdr.get_variable_address(variable_die, var_addr)) |
| return true; |
| |
| elf_symbol_sptr symbol = |
| rdr.variable_symbol_is_exported(var_addr); |
| if (!symbol) |
| return true; |
| if (!symbol->is_suppressed()) |
| return false; |
| |
| // Since there is only one symbol in DWARF associated with an elf_symbol, |
| // we can assume this is the main symbol then. Otherwise the main hinting |
| // did not work as expected. |
| ABG_ASSERT(symbol->is_main_symbol()); |
| if (symbol->has_aliases()) |
| for (elf_symbol_sptr a = symbol->get_next_alias(); |
| !a->is_main_symbol(); a = a->get_next_alias()) |
| if (!a->is_suppressed()) |
| return false; |
| } |
| |
| return suppr::is_variable_suppressed(rdr, |
| qualified_name, |
| linkage_name, |
| /*require_drop_property=*/true); |
| } |
| |
| /// Test if a type (designated by a given DIE) in a given scope is |
| /// suppressed by the suppression specifications that are associated |
| /// to a given DWARF reader. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param scope of the scope of the type DIE to consider. |
| /// |
| /// @param type_die the DIE that designates the type to consider. |
| /// |
| /// @param type_is_opaque out parameter. If this function returns |
| /// true (the type @p type_die is suppressed) and if the type was |
| /// suppressed because it's opaque then this parameter is set to |
| /// true. |
| /// |
| /// @return true iff the type designated by the DIE @p type_die, in |
| /// the scope @p scope is suppressed by at the suppression |
| /// specifications associated to the current DWARF reader. |
| static bool |
| type_is_suppressed(const reader& rdr, |
| const scope_decl* scope, |
| Dwarf_Die *type_die, |
| bool &type_is_opaque) |
| { |
| if (type_die == 0 |
| || (dwarf_tag(type_die) != DW_TAG_enumeration_type |
| && dwarf_tag(type_die) != DW_TAG_class_type |
| && dwarf_tag(type_die) != DW_TAG_structure_type |
| && dwarf_tag(type_die) != DW_TAG_union_type)) |
| return false; |
| |
| string type_name, linkage_name; |
| location type_location; |
| die_loc_and_name(rdr, type_die, type_location, type_name, linkage_name); |
| string qualified_name = build_qualified_name(scope, type_name); |
| |
| return suppr::is_type_suppressed(rdr, |
| qualified_name, |
| type_location, |
| type_is_opaque, |
| /*require_drop_property=*/true); |
| } |
| |
| /// Test if a type (designated by a given DIE) in a given scope is |
| /// suppressed by the suppression specifications that are associated |
| /// to a given DWARF reader. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @param scope of the scope of the type DIE to consider. |
| /// |
| /// @param type_die the DIE that designates the type to consider. |
| /// |
| /// @return true iff the type designated by the DIE @p type_die, in |
| /// the scope @p scope is suppressed by at the suppression |
| /// specifications associated to the current DWARF reader. |
| static bool |
| type_is_suppressed(const reader& rdr, |
| const scope_decl* scope, |
| Dwarf_Die *type_die) |
| { |
| bool type_is_opaque = false; |
| return type_is_suppressed(rdr, scope, type_die, type_is_opaque); |
| } |
| |
| /// Get the opaque version of a type that was suppressed because it's |
| /// a private type. |
| /// |
| /// The opaque version version of the type is just a declared-only |
| /// version of the type (class, union or enum type) denoted by @p |
| /// type_die. |
| /// |
| /// @param rdr the DWARF reader in use. |
| /// |
| /// @param scope the scope of the type die we are looking at. |
| /// |
| /// @param type_die the type DIE we are looking at. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return the opaque version of the type denoted by @p type_die or |
| /// nil if no opaque version was found. |
| static type_or_decl_base_sptr |
| get_opaque_version_of_type(reader &rdr, |
| scope_decl *scope, |
| Dwarf_Die *type_die, |
| size_t where_offset) |
| { |
| type_or_decl_base_sptr result; |
| |
| if (type_die == 0) |
| return result; |
| |
| unsigned tag = dwarf_tag(type_die); |
| if (tag != DW_TAG_class_type |
| && tag != DW_TAG_structure_type |
| && tag != DW_TAG_union_type |
| && tag != DW_TAG_enumeration_type) |
| return result; |
| |
| string type_name, linkage_name; |
| location type_location; |
| die_loc_and_name(rdr, type_die, type_location, type_name, linkage_name); |
| |
| string qualified_name = build_qualified_name(scope, type_name); |
| |
| // |
| // TODO: also handle declaration-only unions. To do that, we mostly |
| // need to adapt add_or_update_union_type to make it schedule |
| // declaration-only unions for resolution too. |
| // |
| if (tag == DW_TAG_structure_type || tag == DW_TAG_class_type) |
| { |
| string_classes_or_unions_map::const_iterator i = |
| rdr.declaration_only_classes().find(qualified_name); |
| if (i != rdr.declaration_only_classes().end()) |
| result = i->second.back(); |
| |
| if (!result) |
| { |
| // So we didn't find any pre-existing forward-declared-only |
| // class for the class definition that we could return as an |
| // opaque type. So let's build one. |
| // |
| // TODO: we need to be able to do this for unions too! |
| class_decl_sptr klass(new class_decl(rdr.env(), type_name, |
| /*alignment=*/0, /*size=*/0, |
| tag == DW_TAG_structure_type, |
| type_location, |
| decl_base::VISIBILITY_DEFAULT)); |
| klass->set_is_declaration_only(true); |
| klass->set_is_artificial(die_is_artificial(type_die)); |
| add_decl_to_scope(klass, scope); |
| rdr.associate_die_to_type(type_die, klass, where_offset); |
| rdr.maybe_schedule_declaration_only_class_for_resolution(klass); |
| result = klass; |
| } |
| } |
| |
| if (tag == DW_TAG_enumeration_type) |
| { |
| string_enums_map::const_iterator i = |
| rdr.declaration_only_enums().find(qualified_name); |
| if (i != rdr.declaration_only_enums().end()) |
| result = i->second.back(); |
| |
| if (!result) |
| { |
| uint64_t size = 0; |
| if (die_unsigned_constant_attribute(type_die, DW_AT_byte_size, size)) |
| size *= 8; |
| type_decl_sptr underlying_type = |
| build_enum_underlying_type(rdr, type_name, size, |
| /*anonymous=*/true); |
| enum_type_decl::enumerators enumeratorz; |
| enum_type_decl_sptr enum_type (new enum_type_decl(type_name, |
| type_location, |
| underlying_type, |
| enumeratorz, |
| linkage_name)); |
| enum_type->set_is_artificial(die_is_artificial(type_die)); |
| add_decl_to_scope(enum_type, scope); |
| result = enum_type; |
| } |
| } |
| |
| return result; |
| } |
| |
| /// Create a function symbol with a given name. |
| /// |
| /// @param sym_name the name of the symbol to create. |
| /// |
| /// @param env the environment to create the symbol in. |
| /// |
| /// @return the newly created symbol. |
| elf_symbol_sptr |
| create_default_fn_sym(const string& sym_name, const environment& env) |
| { |
| elf_symbol::version ver; |
| elf_symbol_sptr result = |
| elf_symbol::create(env, |
| /*symbol index=*/ 0, |
| /*symbol size=*/ 0, |
| sym_name, |
| /*symbol type=*/ elf_symbol::FUNC_TYPE, |
| /*symbol binding=*/ elf_symbol::GLOBAL_BINDING, |
| /*symbol is defined=*/ true, |
| /*symbol is common=*/ false, |
| /*symbol version=*/ ver, |
| /*symbol visibility=*/elf_symbol::DEFAULT_VISIBILITY); |
| return result; |
| } |
| |
| /// Build a @ref function_decl our of a DW_TAG_subprogram DIE. |
| /// |
| /// @param rdr the DWARF reader to use |
| /// |
| /// @param die the DW_TAG_subprogram DIE to read from. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @param called_for_public_decl this is set to true if the function |
| /// was called for a public (function) decl. |
| static function_decl_sptr |
| build_function_decl(reader& rdr, |
| Dwarf_Die* die, |
| size_t where_offset, |
| function_decl_sptr fn) |
| { |
| function_decl_sptr result = fn; |
| if (!die) |
| return result; |
| int tag = dwarf_tag(die); |
| ABG_ASSERT(tag == DW_TAG_subprogram || tag == DW_TAG_inlined_subroutine); |
| |
| if (!die_is_public_decl(die)) |
| return result; |
| |
| translation_unit_sptr tu = rdr.cur_transl_unit(); |
| ABG_ASSERT(tu); |
| |
| string fname, flinkage_name; |
| location floc; |
| die_loc_and_name(rdr, die, floc, fname, flinkage_name); |
| |
| size_t is_inline = die_is_declared_inline(die); |
| class_or_union_sptr is_method = |
| is_class_or_union_type(get_scope_for_die(rdr, die, true, where_offset)); |
| |
| if (result) |
| { |
| // Add the properties that might have been missing from the |
| // first declaration of the function. For now, it usually is |
| // the mangled name that goes missing in the first declarations. |
| // |
| // Also note that if 'fn' has just been cloned, the current |
| // linkage name (of the current DIE) might be different from the |
| // linkage name of 'fn'. In that case, update the linkage name |
| // of 'fn' too. |
| if (!flinkage_name.empty() |
| && result->get_linkage_name() != flinkage_name) |
| result->set_linkage_name(flinkage_name); |
| if (floc) |
| if (!result->get_location()) |
| result->set_location(floc); |
| result->is_declared_inline(is_inline); |
| } |
| else |
| { |
| function_type_sptr fn_type(build_function_type(rdr, die, is_method, |
| where_offset)); |
| if (!fn_type) |
| return result; |
| |
| maybe_canonicalize_type(fn_type, rdr); |
| |
| result.reset(is_method |
| ? new method_decl(fname, fn_type, |
| is_inline, floc, |
| flinkage_name) |
| : new function_decl(fname, fn_type, |
| is_inline, floc, |
| flinkage_name)); |
| } |
| |
| // Set the symbol of the function. If the linkage name is not set |
| // or is wrong, set it to the name of the underlying symbol. |
| if (!result->get_symbol()) |
| { |
| elf_symbol_sptr fn_sym; |
| Dwarf_Addr fn_addr; |
| if (rdr.get_function_address(die, fn_addr)) |
| { |
| rdr.symtab()-> |
| update_main_symbol(fn_addr, |
| result->get_linkage_name().empty() |
| ? result->get_name() |
| : result->get_linkage_name()); |
| fn_sym = rdr.function_symbol_is_exported(fn_addr); |
| } |
| |
| if (fn_sym && !rdr.symbol_already_belongs_to_a_function(fn_sym)) |
| { |
| result->set_symbol(fn_sym); |
| string linkage_name = result->get_linkage_name(); |
| if (linkage_name.empty()) |
| result->set_linkage_name(fn_sym->get_name()); |
| result->set_is_in_public_symbol_table(true); |
| } |
| |
| if (!fn_sym && rdr.is_decl_die_with_undefined_symbol(die)) |
| { |
| // We are looking at a function which symbol is undefined. |
| // let's set its symbol. |
| string n = result->get_linkage_name(); |
| if (n.empty()) |
| n = result->get_name(); |
| fn_sym = rdr.symtab()->lookup_undefined_function_symbol(n); |
| if (fn_sym) |
| { |
| result->set_symbol(fn_sym); |
| result->set_is_in_public_symbol_table(false); |
| } |
| } |
| } |
| |
| rdr.associate_die_to_type(die, result->get_type(), where_offset); |
| |
| size_t die_offset = dwarf_dieoffset(die); |
| |
| if (fn |
| && is_member_function(fn) |
| && get_member_function_is_virtual(fn) |
| && !result->get_linkage_name().empty()) |
| // This function is a virtual member function which has its |
| // linkage name *and* and has its underlying symbol correctly set. |
| // It thus doesn't need any fixup related to elf symbol. So |
| // remove it from the set of virtual member functions with linkage |
| // names and no elf symbol that need to be fixed up. |
| rdr.die_function_decl_with_no_symbol_map().erase(die_offset); |
| return result; |
| } |
| |
| /// Canonicalize a type if it's suitable for early canonicalizing, or, |
| /// if it's not, schedule it for late canonicalization, after the |
| /// debug info of the current translation unit has been fully read. |
| /// |
| /// A (composite) type is deemed suitable for early canonicalizing iff |
| /// all of its sub-types are canonicalized themselve. Non composite |
| /// types are always deemed suitable for early canonicalization. |
| /// |
| /// Note that this function knows how to deal with anonymous classes, |
| /// structs and enums, unlike the overload below: |
| /// |
| /// @param t the type DIE to consider for canonicalization. |
| /// |
| /// @param rdr the @ref reader to use. |
| static void |
| maybe_canonicalize_type(const type_base_sptr& t, |
| reader& rdr) |
| { |
| if (!t) |
| return; |
| |
| type_base_sptr peeled_type = peel_typedef_pointer_or_reference_type(t); |
| if (is_class_type(peeled_type) |
| || is_union_type(peeled_type) |
| || is_function_type(peeled_type) |
| || is_array_type(peeled_type) |
| || is_qualified_type(peeled_type) |
| || is_enum_type(peeled_type) |
| ||(is_decl(peeled_type) && is_decl(peeled_type)->get_is_anonymous())) |
| // We delay canonicalization of classes/unions or typedef, |
| // pointers, references and array to classes/unions. This is |
| // because the (underlying) class might not be finished yet and we |
| // might not be able to able detect it here (thinking about |
| // classes that are work-in-progress, or classes that might be |
| // later amended by some DWARF construct). So we err on the safe |
| // side. We also delay canonicalization for array and qualified |
| // types because they can be edited (in particular by |
| // maybe_strip_qualification) after they are initially built. |
| rdr.schedule_type_for_late_canonicalization(t); |
| else if (type_has_non_canonicalized_subtype(t)) |
| rdr.schedule_type_for_late_canonicalization(t); |
| else |
| canonicalize(t); |
| } |
| |
| /// If a given decl is a member type declaration, set its access |
| /// specifier from the DIE that represents it. |
| /// |
| /// @param member_type_declaration the member type declaration to |
| /// consider. |
| static void |
| maybe_set_member_type_access_specifier(decl_base_sptr member_type_declaration, |
| Dwarf_Die* die) |
| { |
| if (is_type(member_type_declaration) |
| && is_member_decl(member_type_declaration)) |
| { |
| class_or_union* scope = |
| is_class_or_union_type(member_type_declaration->get_scope()); |
| ABG_ASSERT(scope); |
| |
| access_specifier access = public_access; |
| if (class_decl* cl = is_class_type(scope)) |
| if (!cl->is_struct()) |
| access = private_access; |
| |
| die_access_specifier(die, access); |
| set_member_access_specifier(member_type_declaration, access); |
| } |
| } |
| |
| /// This function tests if a given function which might be intented to |
| /// be added to a class scope (to become a member function) should be |
| /// dropped on the floor instead and not be added to the class. |
| /// |
| /// This is a subroutine of build_ir_node_from_die. |
| /// |
| /// @param fn the function to consider. |
| /// |
| /// @param fn_die the DWARF die of @p fn. |
| /// |
| /// @param scope the scope in which @p fn is to be added. |
| /// |
| /// @return true iff @p fn should be dropped on the floor. |
| static bool |
| potential_member_fn_should_be_dropped(const function_decl_sptr& fn, |
| const Dwarf_Die *fn_die) |
| { |
| if (!fn || fn->get_scope()) |
| return false; |
| |
| if (// A function that is not virtual ... |
| !die_is_virtual(fn_die) |
| // .. and yet has no defined ELF symbol associated ... |
| && !fn->get_symbol()) |
| // Should not be added to its class scope. |
| // |
| // Why would it? It's not part of the ABI anyway, as it doesn't |
| // have any ELF symbol associated and is not a virtual member |
| // function. It just constitutes bloat in the IR and might even |
| // induce spurious change reports down the road. |
| return true; |
| |
| return false; |
| } |
| |
| /// Build an IR node from a given DIE and add the node to the current |
| /// IR being build and held in the DWARF reader. Doing that is called |
| /// "emitting an IR node for the DIE". |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param scope the scope under which the resulting IR node has to be |
| /// added. |
| /// |
| /// @param called_from_public_decl set to yes if this function is |
| /// called from the functions used to build a public decl (functions |
| /// and variables). In that case, this function accepts building IR |
| /// nodes representing types. Otherwise, this function only creates |
| /// IR nodes representing public decls (functions and variables). |
| /// This is done to avoid emitting IR nodes for types that are not |
| /// referenced by public functions or variables. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @param is_required_decl_spec if true, it means the ir node to |
| /// build is for a decl that is a specification for another decl that |
| /// is concrete. If you don't know what this is, set it to false. |
| /// |
| /// @param is_declaration_only is true if the DIE denoted by @p die is |
| /// a declaration-only DIE. |
| /// |
| /// @return the resulting IR node. |
| static type_or_decl_base_sptr |
| build_ir_node_from_die(reader& rdr, |
| Dwarf_Die* die, |
| scope_decl* scope, |
| bool called_from_public_decl, |
| size_t where_offset, |
| bool is_declaration_only, |
| bool is_required_decl_spec) |
| { |
| type_or_decl_base_sptr result; |
| |
| if (!die || !scope) |
| return result; |
| |
| int tag = dwarf_tag(die); |
| |
| if (!called_from_public_decl) |
| { |
| if (rdr.load_all_types() && die_is_type(die)) |
| /* We were instructed to load debug info for all types, |
| included those that are not reachable from a public |
| declaration. So load the debug info for this type. */; |
| else if (tag != DW_TAG_subprogram |
| && tag != DW_TAG_variable |
| && tag != DW_TAG_member |
| && tag != DW_TAG_namespace) |
| return result; |
| } |
| |
| const die_source source_of_die = rdr.get_die_source(die); |
| |
| if ((result = rdr.lookup_decl_from_die_offset(dwarf_dieoffset(die), |
| source_of_die))) |
| { |
| if (rdr.load_all_types()) |
| if (called_from_public_decl) |
| if (type_base_sptr t = is_type(result)) |
| if (corpus *abi_corpus = scope->get_corpus()) |
| abi_corpus->record_type_as_reachable_from_public_interfaces(*t); |
| |
| return result; |
| } |
| |
| // This is *the* bit of code that ensures we have the right notion |
| // of "declared" at any point in a DIE chain formed from |
| // DW_AT_abstract_origin and DW_AT_specification links. There should |
| // be no other callers of die_is_declaration_only. |
| is_declaration_only = is_declaration_only && die_is_declaration_only(die); |
| |
| switch (tag) |
| { |
| // Type DIEs we support. |
| case DW_TAG_base_type: |
| if (type_decl_sptr t = build_type_decl(rdr, die, where_offset)) |
| { |
| result = |
| add_decl_to_scope(t, rdr.cur_transl_unit()->get_global_scope()); |
| canonicalize(t); |
| } |
| break; |
| |
| case DW_TAG_typedef: |
| { |
| typedef_decl_sptr t = build_typedef_type(rdr, die, |
| called_from_public_decl, |
| where_offset); |
| |
| result = add_decl_to_scope(t, scope); |
| if (result) |
| { |
| maybe_set_member_type_access_specifier(is_decl(result), die); |
| maybe_canonicalize_type(t, rdr); |
| } |
| } |
| break; |
| |
| case DW_TAG_pointer_type: |
| { |
| pointer_type_def_sptr p = |
| build_pointer_type_def(rdr, die, |
| called_from_public_decl, |
| where_offset); |
| if (p) |
| { |
| result = |
| add_decl_to_scope(p, rdr.cur_transl_unit()->get_global_scope()); |
| ABG_ASSERT(result->get_translation_unit()); |
| maybe_canonicalize_type(p, rdr); |
| } |
| } |
| break; |
| |
| case DW_TAG_reference_type: |
| case DW_TAG_rvalue_reference_type: |
| { |
| reference_type_def_sptr r = |
| build_reference_type(rdr, die, |
| called_from_public_decl, |
| where_offset); |
| if (r) |
| { |
| result = |
| add_decl_to_scope(r, rdr.cur_transl_unit()->get_global_scope()); |
| |
| rdr.associate_die_to_type(die, r, where_offset); |
| maybe_canonicalize_type(r, rdr); |
| } |
| } |
| break; |
| |
| case DW_TAG_ptr_to_member_type: |
| { |
| ptr_to_mbr_type_sptr p = |
| build_ptr_to_mbr_type(rdr, die, called_from_public_decl, |
| where_offset); |
| if (p) |
| { |
| result = |
| add_decl_to_scope(p, rdr.cur_transl_unit()->get_global_scope()); |
| maybe_canonicalize_type(p, rdr); |
| } |
| } |
| break; |
| |
| case DW_TAG_const_type: |
| case DW_TAG_volatile_type: |
| case DW_TAG_restrict_type: |
| { |
| type_base_sptr q = |
| build_qualified_type(rdr, die, |
| called_from_public_decl, |
| where_offset); |
| if (q) |
| { |
| // Strip some potentially redundant type qualifiers from |
| // the qualified type we just built. |
| decl_base_sptr d = maybe_strip_qualification(is_qualified_type(q), |
| rdr); |
| if (!d) |
| d = get_type_declaration(q); |
| ABG_ASSERT(d); |
| type_base_sptr ty = is_type(d); |
| // Associate the die to type ty again because 'ty'might be |
| // different from 'q', because 'ty' is 'q' possibly |
| // stripped from some redundant type qualifier. |
| rdr.associate_die_to_type(die, ty, where_offset); |
| result = |
| add_decl_to_scope(d, rdr.cur_transl_unit()->get_global_scope()); |
| maybe_canonicalize_type(is_type(result), rdr); |
| } |
| } |
| break; |
| |
| case DW_TAG_enumeration_type: |
| { |
| bool type_is_opaque = false; |
| bool type_suppressed = |
| type_is_suppressed(rdr, scope, die, type_is_opaque); |
| if (type_suppressed && type_is_opaque) |
| { |
| // The type is suppressed because it's private. If other |
| // non-suppressed and declaration-only instances of this |
| // type exist in the current corpus, then it means those |
| // non-suppressed instances are opaque versions of the |
| // suppressed private type. Lets return one of these opaque |
| // types then. |
| result = get_opaque_version_of_type(rdr, scope, die, where_offset); |
| maybe_canonicalize_type(is_type(result), rdr); |
| } |
| else if (!type_suppressed) |
| { |
| enum_type_decl_sptr e = build_enum_type(rdr, die, scope, |
| where_offset, |
| is_declaration_only); |
| result = add_decl_to_scope(e, scope); |
| if (result) |
| { |
| maybe_set_member_type_access_specifier(is_decl(result), die); |
| maybe_canonicalize_type(is_type(result), rdr); |
| } |
| } |
| } |
| break; |
| |
| case DW_TAG_class_type: |
| case DW_TAG_structure_type: |
| { |
| bool type_is_opaque = false; |
| bool type_suppressed= |
| type_is_suppressed(rdr, scope, die, type_is_opaque); |
| |
| if (type_suppressed && type_is_opaque) |
| { |
| // The type is suppressed because it's private. If other |
| // non-suppressed and declaration-only instances of this |
| // type exist in the current corpus, then it means those |
| // non-suppressed instances are opaque versions of the |
| // suppressed private type. Lets return one of these opaque |
| // types then. |
| result = get_opaque_version_of_type(rdr, scope, die, where_offset); |
| maybe_canonicalize_type(is_type(result), rdr); |
| } |
| else if (!type_suppressed) |
| { |
| Dwarf_Die spec_die; |
| scope_decl_sptr scop; |
| class_decl_sptr klass; |
| if (die_die_attribute(die, DW_AT_specification, spec_die)) |
| { |
| scope_decl_sptr skope = |
| get_scope_for_die(rdr, &spec_die, |
| called_from_public_decl, |
| where_offset); |
| ABG_ASSERT(skope); |
| decl_base_sptr cl = |
| is_decl(build_ir_node_from_die(rdr, &spec_die, |
| skope.get(), |
| called_from_public_decl, |
| where_offset, |
| is_declaration_only, |
| /*is_required_decl_spec=*/false)); |
| ABG_ASSERT(cl); |
| klass = dynamic_pointer_cast<class_decl>(cl); |
| ABG_ASSERT(klass); |
| |
| klass = |
| add_or_update_class_type(rdr, die, |
| skope.get(), |
| tag == DW_TAG_structure_type, |
| klass, |
| called_from_public_decl, |
| where_offset, |
| is_declaration_only); |
| } |
| else |
| klass = |
| add_or_update_class_type(rdr, die, scope, |
| tag == DW_TAG_structure_type, |
| class_decl_sptr(), |
| called_from_public_decl, |
| where_offset, |
| is_declaration_only); |
| result = klass; |
| if (klass) |
| { |
| maybe_set_member_type_access_specifier(klass, die); |
| maybe_canonicalize_type(klass, rdr); |
| } |
| } |
| } |
| break; |
| case DW_TAG_union_type: |
| if (!type_is_suppressed(rdr, scope, die)) |
| { |
| union_decl_sptr union_type = |
| add_or_update_union_type(rdr, die, scope, |
| union_decl_sptr(), |
| called_from_public_decl, |
| where_offset, |
| is_declaration_only); |
| if (union_type) |
| { |
| maybe_set_member_type_access_specifier(union_type, die); |
| maybe_canonicalize_type(union_type, rdr); |
| } |
| result = union_type; |
| } |
| break; |
| case DW_TAG_string_type: |
| break; |
| case DW_TAG_subroutine_type: |
| { |
| function_type_sptr f = build_function_type(rdr, die, |
| class_decl_sptr(), |
| where_offset); |
| if (f) |
| { |
| result = f; |
| result->set_is_artificial(false); |
| maybe_canonicalize_type(f, rdr); |
| } |
| } |
| break; |
| case DW_TAG_array_type: |
| { |
| array_type_def_sptr a = build_array_type(rdr, |
| die, |
| called_from_public_decl, |
| where_offset); |
| if (a) |
| { |
| result = |
| add_decl_to_scope(a, rdr.cur_transl_unit()->get_global_scope()); |
| rdr.associate_die_to_type(die, a, where_offset); |
| maybe_canonicalize_type(a, rdr); |
| } |
| break; |
| } |
| case DW_TAG_subrange_type: |
| { |
| // If we got here, this means the subrange type is a "free |
| // form" defined in the global namespace of the current |
| // translation unit, like what is found in Ada. |
| array_type_def::subrange_sptr s = |
| build_subrange_type(rdr, die, where_offset); |
| if (s) |
| { |
| result = |
| add_decl_to_scope(s, rdr.cur_transl_unit()->get_global_scope()); |
| rdr.associate_die_to_type(die, s, where_offset); |
| maybe_canonicalize_type(s, rdr); |
| } |
| } |
| break; |
| case DW_TAG_packed_type: |
| break; |
| case DW_TAG_set_type: |
| break; |
| case DW_TAG_file_type: |
| break; |
| case DW_TAG_thrown_type: |
| break; |
| case DW_TAG_interface_type: |
| break; |
| case DW_TAG_unspecified_type: |
| break; |
| case DW_TAG_shared_type: |
| break; |
| |
| case DW_TAG_compile_unit: |
| // We shouldn't reach this point b/c this should be handled by |
| // build_translation_unit. |
| ABG_ASSERT_NOT_REACHED; |
| |
| case DW_TAG_namespace: |
| case DW_TAG_module: |
| result = build_namespace_decl_and_add_to_ir(rdr, die, where_offset); |
| break; |
| |
| case DW_TAG_variable: |
| case DW_TAG_member: |
| { |
| Dwarf_Die spec_die; |
| bool var_is_cloned = false; |
| |
| if (tag == DW_TAG_member) |
| ABG_ASSERT(!die_is_in_c(die)); |
| |
| if (die_die_attribute(die, DW_AT_specification, spec_die, false) |
| || (var_is_cloned = die_die_attribute(die, DW_AT_abstract_origin, |
| spec_die, false))) |
| { |
| scope_decl_sptr spec_scope = |
| get_scope_for_die(rdr, &spec_die, |
| /*called_from_public_decl=*/ |
| die_is_effectively_public_decl(rdr, die), |
| where_offset); |
| if (spec_scope) |
| { |
| decl_base_sptr d = |
| is_decl(build_ir_node_from_die(rdr, &spec_die, |
| spec_scope.get(), |
| called_from_public_decl, |
| where_offset, |
| is_declaration_only, |
| /*is_required_decl_spec=*/true)); |
| if (d) |
| { |
| var_decl_sptr m = |
| dynamic_pointer_cast<var_decl>(d); |
| if (var_is_cloned) |
| m = m->clone(); |
| m = build_var_decl(rdr, die, where_offset, m); |
| if (is_data_member(m)) |
| { |
| set_member_is_static(m, true); |
| rdr.associate_die_to_decl(die, m, where_offset, |
| /*associate_by_repr=*/false); |
| } |
| else |
| { |
| ABG_ASSERT(has_scope(m)); |
| rdr.var_decls_to_re_add_to_tree().push_back(m); |
| } |
| ABG_ASSERT(m->get_scope()); |
| rdr.add_var_to_exported_or_undefined_decls(m.get()); |
| result = m; |
| } |
| } |
| } |
| else if (var_decl_sptr v = |
| build_or_get_var_decl_if_not_suppressed(rdr, scope, die, |
| where_offset, |
| is_declaration_only, |
| /*result=*/var_decl_sptr(), |
| is_required_decl_spec)) |
| { |
| result = add_decl_to_scope(v, scope); |
| ABG_ASSERT(is_decl(result)->get_scope()); |
| v = dynamic_pointer_cast<var_decl>(result); |
| ABG_ASSERT(v); |
| ABG_ASSERT(v->get_scope()); |
| rdr.var_decls_to_re_add_to_tree().push_back(v); |
| rdr.add_var_to_exported_or_undefined_decls(v.get()); |
| } |
| } |
| break; |
| |
| case DW_TAG_subprogram: |
| case DW_TAG_inlined_subroutine: |
| { |
| if (die_is_artificial(die)) |
| break; |
| |
| Dwarf_Die abstract_origin_die; |
| bool has_abstract_origin = die_die_attribute(die, DW_AT_abstract_origin, |
| abstract_origin_die, |
| /*recursive=*/true); |
| |
| |
| scope_decl_sptr s = get_scope_for_die(rdr, die, called_from_public_decl, |
| where_offset); |
| scope_decl* interface_scope = scope ? scope : s.get(); |
| |
| class_decl* class_scope = is_class_type(interface_scope); |
| string linkage_name = die_linkage_name(die); |
| string spec_linkage_name; |
| function_decl_sptr existing_fn; |
| |
| if (class_scope) |
| { |
| // The scope of the function DIE we are looking at is a |
| // class. So we are looking at a member function. |
| if (!linkage_name.empty()) |
| { |
| if ((existing_fn = |
| class_scope->find_member_function_sptr(linkage_name))) |
| { |
| // A function with the same linkage name has |
| // already been created. Let's see if we are a |
| // clone of it or not. |
| spec_linkage_name = existing_fn->get_linkage_name(); |
| if (has_abstract_origin |
| && !spec_linkage_name.empty() |
| && linkage_name != spec_linkage_name) |
| { |
| // The current DIE has 'existing_fn' as |
| // abstract orign, and has a linkage name that |
| // is different from from the linkage name of |
| // 'existing_fn'. That means, the current DIE |
| // represents a clone of 'existing_fn'. |
| existing_fn = existing_fn->clone(); |
| } |
| } |
| } |
| } |
| |
| rdr.scope_stack().push(interface_scope); |
| |
| // Either we create a branch new IR for the current function |
| // DIE we are looking at, or we complete an existing IR node |
| // with the new completementary information carried by this |
| // DIE for that IR node. |
| result = |
| build_or_get_fn_decl_if_not_suppressed(rdr, interface_scope, |
| die, where_offset, |
| is_declaration_only, |
| existing_fn); |
| |
| if (result && !existing_fn) |
| { |
| // We built a brand new IR for the function DIE. Now |
| // there should be enough information on that IR to know |
| // if we should drop it on the floor or keep it ... |
| if (potential_member_fn_should_be_dropped(is_function_decl(result), die) |
| && !is_required_decl_spec) |
| { |
| // So apparently we should drop that function IR on |
| // the floor. Let's do so. |
| result.reset(); |
| break; |
| } |
| // OK so we came to the conclusion that we need to keep |
| // the function. So let's add it to its scope. |
| result = add_decl_to_scope(is_decl(result), interface_scope); |
| } |
| |
| function_decl_sptr fn = is_function_decl(result); |
| if (fn && is_member_function(fn)) |
| { |
| class_decl_sptr klass(static_cast<class_decl*>(interface_scope), |
| sptr_utils::noop_deleter()); |
| ABG_ASSERT(klass); |
| finish_member_function_reading(die, fn, klass, rdr); |
| } |
| |
| if (fn) |
| { |
| if (!is_member_function(fn) |
| || !get_member_function_is_virtual(fn)) |
| // Virtual member functions are added to the set of |
| // functions exported by the current ABI corpus *after* |
| // the canonicalization of their parent type. So let's |
| // not do it here. |
| rdr.add_fn_to_exported_or_undefined_decls(fn.get()); |
| rdr.associate_die_to_decl(die, fn, where_offset, |
| /*associate_by_repr=*/false); |
| maybe_canonicalize_type(fn->get_type(), rdr); |
| } |
| |
| rdr.scope_stack().pop(); |
| } |
| break; |
| |
| case DW_TAG_formal_parameter: |
| // We should not read this case as it should have been dealt |
| // with by build_function_decl above. |
| ABG_ASSERT_NOT_REACHED; |
| |
| case DW_TAG_constant: |
| break; |
| case DW_TAG_enumerator: |
| break; |
| |
| case DW_TAG_partial_unit: |
| case DW_TAG_imported_unit: |
| // For now, the DIEs under these are read lazily when they are |
| // referenced by a public decl DIE that is under a |
| // DW_TAG_compile_unit, so we shouldn't get here. |
| ABG_ASSERT_NOT_REACHED; |
| |
| // Other declaration we don't really intend to support yet. |
| case DW_TAG_dwarf_procedure: |
| case DW_TAG_imported_declaration: |
| case DW_TAG_entry_point: |
| case DW_TAG_label: |
| case DW_TAG_lexical_block: |
| case DW_TAG_unspecified_parameters: |
| case DW_TAG_variant: |
| case DW_TAG_common_block: |
| case DW_TAG_common_inclusion: |
| case DW_TAG_inheritance: |
| case DW_TAG_with_stmt: |
| case DW_TAG_access_declaration: |
| case DW_TAG_catch_block: |
| case DW_TAG_friend: |
| case DW_TAG_namelist: |
| case DW_TAG_namelist_item: |
| case DW_TAG_template_type_parameter: |
| case DW_TAG_template_value_parameter: |
| case DW_TAG_try_block: |
| case DW_TAG_variant_part: |
| case DW_TAG_imported_module: |
| case DW_TAG_condition: |
| case DW_TAG_type_unit: |
| case DW_TAG_template_alias: |
| case DW_TAG_lo_user: |
| case DW_TAG_MIPS_loop: |
| case DW_TAG_format_label: |
| case DW_TAG_function_template: |
| case DW_TAG_class_template: |
| case DW_TAG_GNU_BINCL: |
| case DW_TAG_GNU_EINCL: |
| case DW_TAG_GNU_template_template_param: |
| case DW_TAG_GNU_template_parameter_pack: |
| case DW_TAG_GNU_formal_parameter_pack: |
| case DW_TAG_GNU_call_site: |
| case DW_TAG_GNU_call_site_parameter: |
| case DW_TAG_hi_user: |
| default: |
| break; |
| } |
| |
| if (result && tag != DW_TAG_subroutine_type) |
| rdr.associate_die_to_decl(die, is_decl(result), where_offset, |
| /*associate_by_repr=*/false); |
| |
| if (result) |
| if (rdr.load_all_types()) |
| if (called_from_public_decl) |
| if (type_base_sptr t = is_type(result)) |
| if (corpus *abi_corpus = scope->get_corpus()) |
| abi_corpus->record_type_as_reachable_from_public_interfaces(*t); |
| |
| return result; |
| } |
| |
| /// Build the IR node for a void type. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @return the void type node. |
| static decl_base_sptr |
| build_ir_node_for_void_type(reader& rdr) |
| { |
| const environment& env = rdr.env(); |
| |
| type_base_sptr t = env.get_void_type(); |
| add_decl_to_scope(is_decl(t), rdr.cur_transl_unit()->get_global_scope()); |
| decl_base_sptr type_declaration = get_type_declaration(t); |
| canonicalize(t); |
| return type_declaration; |
| } |
| |
| /// Build the IR node for a "pointer to void type". |
| /// |
| /// That IR node is shared across the ABI corpus. |
| /// |
| /// Note that this function just gets that IR node from the |
| /// environment and, if it's not added to any scope yet, adds it to |
| /// the global scope associated to the current translation unit. |
| /// |
| /// @param rdr the DWARF reader to consider. |
| /// |
| /// @return the IR node. |
| static type_or_decl_base_sptr |
| build_ir_node_for_void_pointer_type(reader& rdr) |
| { |
| const environment& env = rdr.env(); |
| |
| type_base_sptr t = env.get_void_pointer_type(); |
| add_decl_to_scope(is_decl(t), rdr.cur_transl_unit()->get_global_scope()); |
| decl_base_sptr type_declaration = get_type_declaration(t); |
| canonicalize(t); |
| return type_declaration; |
| } |
| |
| /// Build the IR node for a variadic parameter type. |
| /// |
| /// @param rdr the DWARF reader to use. |
| /// |
| /// @return the variadic parameter type. |
| static decl_base_sptr |
| build_ir_node_for_variadic_parameter_type(reader &rdr) |
| { |
| |
| const environment& env = rdr.env(); |
| |
| type_base_sptr t = env.get_variadic_parameter_type(); |
| add_decl_to_scope(is_decl(t), rdr.cur_transl_unit()->get_global_scope()); |
| decl_base_sptr type_declaration = get_type_declaration(t); |
| canonicalize(t); |
| return type_declaration; |
| } |
| |
| /// Build an IR node from a given DIE and add the node to the current |
| /// IR being build and held in the DWARF reader. Doing that is called |
| /// "emitting an IR node for the DIE". |
| /// |
| /// @param rdr the DWARF reader. |
| /// |
| /// @param die the DIE to consider. |
| /// |
| /// @param called_from_public_decl set to yes if this function is |
| /// called from the functions used to build a public decl (functions |
| /// and variables). In that case, this function accepts building IR |
| /// nodes representing types. Otherwise, this function only creates |
| /// IR nodes representing public decls (functions and variables). |
| /// This is done to avoid emitting IR nodes for types that are not |
| /// referenced by public functions or variables. |
| /// |
| /// @param where_offset the offset of the DIE where we are "logically" |
| /// positionned at, in the DIE tree. This is useful when @p die is |
| /// e.g, DW_TAG_partial_unit that can be included in several places in |
| /// the DIE tree. |
| /// |
| /// @return the resulting IR node. |
| static type_or_decl_base_sptr |
| build_ir_node_from_die(reader& rdr, |
| Dwarf_Die* die, |
| bool called_from_public_decl, |
| size_t where_offset) |
| { |
| if (!die) |
| return decl_base_sptr(); |
| |
| // Normaly, a decl that is meant to be external has a DW_AT_external |
| // set. But then some compilers fail to always emit that flag. For |
| // instance, for static data members, some compilers won't emit the |
| // DW_AT_external. In that case, we assume that if the variable is |
| // at global or named namespace scope, then we can assume it's |
| // external. If the variable doesn't have any ELF symbol associated |
| // to it, it'll be dropped on the floor anyway. Those variable |
| // decls are considered as being "effectively public". |
| bool consider_as_called_from_public_decl = |
| called_from_public_decl || die_is_effectively_public_decl(rdr, die); |
| scope_decl_sptr scope = get_scope_for_die(rdr, die, |
| consider_as_called_from_public_decl, |
| where_offset); |
| return build_ir_node_from_die(rdr, die, scope.get(), |
| called_from_public_decl, |
| where_offset, true); |
| } |
| |
| /// Create a dwarf::reader. |
| /// |
| /// @param elf_path the path to the elf file the reader is to be used |
| /// for. |
| /// |
| /// @param debug_info_root_paths a vector to the paths to the |
| /// directories under which the debug info is to be found for @p |
| /// elf_path. Pass an empty vector if the debug info is not in a |
| /// split file. |
| /// |
| /// @param environment the environment used by the current context. |
| /// This environment contains resources needed by the DWARF reader and by |
| /// the types and declarations that are to be created later. Note |
| /// that ABI artifacts that are to be compared all need to be created |
| /// within the same environment. |
| /// |
| /// Please also note that the life time of this environment object |
| /// must be greater than the life time of the resulting @ref |
| /// reader the context uses resources that are allocated in the |
| /// environment. |
| /// |
| /// @param load_all_types if set to false only the types that are |
| /// reachable from publicly exported declarations (of functions and |
| /// variables) are read. If set to true then all types found in the |
| /// debug information are loaded. |
| /// |
| /// @param linux_kernel_mode if set to true, then consider the special |
| /// linux kernel symbol tables when determining if a symbol is |
| /// exported or not. |
| /// |
| /// @return a smart pointer to the resulting dwarf::reader. |
| elf_based_reader_sptr |
| create_reader(const std::string& elf_path, |
| const vector<char**>& debug_info_root_paths, |
| environment& environment, |
| bool load_all_types, |
| bool linux_kernel_mode) |
| { |
| |
| reader_sptr r = reader::create(elf_path, |
| debug_info_root_paths, |
| environment, |
| load_all_types, |
| linux_kernel_mode); |
| return static_pointer_cast<elf_based_reader>(r); |
| } |
| |
| /// Re-initialize a reader so that it can re-used to read |
| /// another binary. |
| /// |
| /// @param rdr the context to re-initialize. |
| /// |
| /// @param elf_path the path to the elf file the context is to be used |
| /// for. |
| /// |
| /// @param debug_info_root_path a pointer to the path to the root |
| /// directory under which the debug info is to be found for @p |
| /// elf_path. Leave this to NULL if the debug info is not in a split |
| /// file. |
| /// |
| /// @param environment the environment used by the current context. |
| /// This environment contains resources needed by the DWARF reader and by |
| /// the types and declarations that are to be created later. Note |
| /// that ABI artifacts that are to be compared all need to be created |
| /// within the same environment. |
| /// |
| /// Please also note that the life time of this environment object |
| /// must be greater than the life time of the resulting @ref |
| /// reader the context uses resources that are allocated in the |
| /// environment. |
| /// |
| /// @param load_all_types if set to false only the types that are |
| /// reachable from publicly exported declarations (of functions and |
| /// variables) are read. If set to true then all types found in the |
| /// debug information are loaded. |
| /// |
| /// @param linux_kernel_mode if set to true, then consider the special |
| /// linux kernel symbol tables when determining if a symbol is |
| /// exported or not. |
| /// |
| /// @return a smart pointer to the resulting dwarf::reader. |
| void |
| reset_reader(elf_based_reader& rdr, |
| const std::string& elf_path, |
| const vector<char**>&debug_info_root_path, |
| bool read_all_types, |
| bool linux_kernel_mode) |
| { |
| reader& r = dynamic_cast<reader&>(rdr); |
| r.initialize(elf_path, debug_info_root_path, |
| read_all_types, linux_kernel_mode); |
| } |
| |
| /// Read all @ref abigail::translation_unit possible from the debug info |
| /// accessible from an elf file, stuff them into a libabigail ABI |
| /// Corpus and return it. |
| /// |
| /// @param elf_path the path to the elf file. |
| /// |
| /// @param debug_info_root_paths a vector of pointers to root paths |
| /// under which to look for the debug info of the elf files that are |
| /// later handled by the Dwfl. This for cases where the debug info is |
| /// split into a different file from the binary we want to inspect. |
| /// On Red Hat compatible systems, this root path is usually |
| /// /usr/lib/debug by default. If this argument is set to NULL, then |
| /// "./debug" and /usr/lib/debug will be searched for sub-directories |
| /// containing the debug info file. |
| /// |
| /// @param environment the environment used by the current context. |
| /// This environment contains resources needed by the DWARF reader and by |
| /// the types and declarations that are to be created later. Note |
| /// that ABI artifacts that are to be compared all need to be created |
| /// within the same environment. Also, the lifetime of the |
| /// environment must be greater than the lifetime of the resulting |
| /// corpus because the corpus uses resources that are allocated in the |
| /// environment. |
| /// |
| /// @param load_all_types if set to false only the types that are |
| /// reachable from publicly exported declarations (of functions and |
| /// variables) are read. If set to true then all types found in the |
| /// debug information are loaded. |
| /// |
| /// @param resulting_corp a pointer to the resulting abigail::corpus. |
| /// |
| /// @return the resulting status. |
| corpus_sptr |
| read_corpus_from_elf(const std::string& elf_path, |
| const vector<char**>& debug_info_root_paths, |
| environment& environment, |
| bool load_all_types, |
| fe_iface::status& status) |
| { |
| elf_based_reader_sptr rdr = |
| dwarf::reader::create(elf_path, debug_info_root_paths, |
| environment, load_all_types, |
| /*linux_kernel_mode=*/false); |
| |
| return rdr->read_corpus(status); |
| } |
| |
| /// Look into the symbol tables of a given elf file and see if we find |
| /// a given symbol. |
| /// |
| /// @param env the environment we are operating from. |
| /// |
| /// @param elf_path the path to the elf file to consider. |
| /// |
| /// @param symbol_name the name of the symbol to look for. |
| /// |
| /// @param demangle if true, try to demangle the symbol name found in |
| /// the symbol table. |
| /// |
| /// @param syms the vector of symbols found with the name @p symbol_name. |
| /// |
| /// @return true iff the symbol was found among the publicly exported |
| /// symbols of the ELF file. |
| bool |
| lookup_symbol_from_elf(const environment& env, |
| const string& elf_path, |
| const string& symbol_name, |
| bool demangle, |
| vector<elf_symbol_sptr>& syms) |
| |
| { |
| if (elf_version(EV_CURRENT) == EV_NONE) |
| return false; |
| |
| int fd = open(elf_path.c_str(), O_RDONLY); |
| if (fd < 0) |
| return false; |
| |
| struct stat s; |
| if (fstat(fd, &s)) |
| return false; |
| |
| Elf* elf = elf_begin(fd, ELF_C_READ, 0); |
| if (elf == 0) |
| return false; |
| |
| bool value = lookup_symbol_from_elf(env, elf, symbol_name, |
| demangle, syms); |
| elf_end(elf); |
| close(fd); |
| |
| return value; |
| } |
| |
| /// Look into the symbol tables of an elf file to see if a public |
| /// function of a given name is found. |
| /// |
| /// @param env the environment we are operating from. |
| /// |
| /// @param elf_path the path to the elf file to consider. |
| /// |
| /// @param symbol_name the name of the function to look for. |
| /// |
| /// @param syms the vector of public function symbols found with the |
| /// name @p symname. |
| /// |
| /// @return true iff a function with symbol name @p symbol_name is |
| /// found. |
| bool |
| lookup_public_function_symbol_from_elf(environment& env, |
| const string& path, |
| const string& symname, |
| vector<elf_symbol_sptr>& syms) |
| { |
| if (elf_version(EV_CURRENT) == EV_NONE) |
| return false; |
| |
| int fd = open(path.c_str(), O_RDONLY); |
| if (fd < 0) |
| return false; |
| |
| struct stat s; |
| if (fstat(fd, &s)) |
| return false; |
| |
| Elf* elf = elf_begin(fd, ELF_C_READ, 0); |
| if (elf == 0) |
| return false; |
| |
| bool value = lookup_public_function_symbol_from_elf(env, elf, symname, syms); |
| elf_end(elf); |
| close(fd); |
| |
| return value; |
| } |
| |
| }// end namespace dwarf |
| |
| }// end namespace abigail |