blob: 43e5c85d8a146bdd559d1247de2768d1ec7f0e26 [file] [log] [blame]
// 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, &ltype_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