blob: 604c76e3f67169e645868274df2a296b03c2071e [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2020 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 "config.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-elf-helpers.h"
#include "abg-internal.h"
// <headers defining libabigail's API go under here>
ABG_BEGIN_EXPORT_DECLARATIONS
#include "abg-dwarf-reader.h"
#include "abg-sptr-utils.h"
#include "abg-symtab-reader.h"
#include "abg-tools-utils.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_reader
{
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 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
};
/// Prefix increment operator for @ref die_source.
///
/// @param source the die_source to increment.
/// @return the incremented source.
static die_source&
operator++(die_source& source)
{
source = static_cast<die_source>(source + 1);
return source;
}
/// A functor used by @ref dwfl_sptr.
struct dwfl_deleter
{
void
operator()(Dwfl* dwfl)
{dwfl_end(dwfl);}
};//end struct dwfl_deleter
/// A convenience typedef for a shared pointer to a Dwfl.
typedef shared_ptr<Dwfl> dwfl_sptr;
/// 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;
/// Convenience typedef for a map which key is an elf address and
/// which value is an elf_symbol_sptr.
typedef unordered_map<GElf_Addr, elf_symbol_sptr> addr_elf_symbol_sptr_map_type;
/// Convenience typedef for a set of ELF addresses.
typedef unordered_set<GElf_Addr> address_set_type;
typedef unordered_set<interned_string, hash_interned_string> istring_set_type;
/// Convenience typedef for a shared pointer to an @ref address_set_type.
typedef shared_ptr<address_set_type> address_set_sptr;
/// 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.
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 read_context& ctxt,
const Dwarf_Die* die,
Dwarf_Die& parent_die,
size_t where_offset);
static bool
get_scope_die(const read_context& ctxt,
const Dwarf_Die* die,
size_t where_offset,
Dwarf_Die& scope_die);
static bool
die_is_anonymous(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_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_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
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
die_is_at_class_scope(const read_context& ctxt,
const Dwarf_Die* die,
size_t where_offset,
Dwarf_Die& class_scope_die);
static bool
eval_last_constant_dwarf_sub_expr(Dwarf_Op* expr,
uint64_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
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 location
die_location(const read_context& ctxt, 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 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
build_internal_underlying_enum_type_name(const string &base_name,
bool is_anonymous,
uint64_t size);
static string
die_qualified_type_name(const read_context& ctxt,
const Dwarf_Die* die,
size_t where);
static string
die_qualified_decl_name(const read_context& ctxt,
const Dwarf_Die* die,
size_t where);
static string
die_qualified_name(const read_context& ctxt,
const Dwarf_Die* die,
size_t where);
static bool
die_qualified_type_name_empty(const read_context& ctxt,
const Dwarf_Die* die, size_t where,
string &qualified_name);
static void
die_return_and_parm_names_from_fn_type_die(const read_context& ctxt,
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 read_context& ctxt,
const Dwarf_Die *die,
size_t where_offset);
static bool
die_peel_qual_ptr(Dwarf_Die *die, Dwarf_Die& peeled_die);
static bool
die_function_type_is_method_type(const read_context& ctxt,
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(read_context& ctxt,
const Dwarf_Die* die,
size_t where_offset);
static string
die_pretty_print_decl(read_context& ctxt,
const Dwarf_Die* die,
size_t where_offset);
static string
die_pretty_print(read_context& ctxt,
const Dwarf_Die* die,
size_t where_offset);
static void
maybe_canonicalize_type(const Dwarf_Die* die,
read_context& ctxt);
static void
maybe_canonicalize_type(const type_base_sptr& t,
read_context& ctxt);
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(read_context& ctxt,
const Dwarf_Die* die,
size_t where_offset,
bool associate_type_to_die = true);
static void
build_subranges_from_array_type_die(read_context& ctxt,
const Dwarf_Die* die,
array_type_def::subranges_type& subranges,
size_t where_offset,
bool associate_type_to_die = true);
static bool
compare_dies(const read_context& ctxt,
const Dwarf_Die *l, const Dwarf_Die *r,
bool update_canonical_dies_on_the_fly);
/// Find the file name of the alternate debug info file.
///
/// @param elf_module the elf module to consider.
///
/// @param out parameter. Is set to the file name of the alternate
/// debug info file, iff this function returns true.
///
/// @return true iff the location of the alternate debug info file was
/// found.
static bool
find_alt_debug_info_link(Dwfl_Module *elf_module,
string &alt_file_name)
{
GElf_Addr bias = 0;
Dwarf *dwarf = dwfl_module_getdwarf(elf_module, &bias);
Elf *elf = dwarf_getelf(dwarf);
GElf_Ehdr ehmem, *elf_header;
elf_header = gelf_getehdr(elf, &ehmem);
Elf_Scn* section = 0;
while ((section = elf_nextscn(elf, section)) != 0)
{
GElf_Shdr header_mem, *header;
header = gelf_getshdr(section, &header_mem);
if (header->sh_type != SHT_PROGBITS)
continue;
const char *section_name = elf_strptr(elf,
elf_header->e_shstrndx,
header->sh_name);
char *alt_name = 0;
char *buildid = 0;
size_t buildid_len = 0;
if (section_name != 0
&& strcmp(section_name, ".gnu_debugaltlink") == 0)
{
Elf_Data *data = elf_getdata(section, 0);
if (data != 0 && data->d_size != 0)
{
alt_name = (char*) data->d_buf;
char *end_of_alt_name =
(char *) memchr(alt_name, '\0', data->d_size);
buildid_len = data->d_size - (end_of_alt_name - alt_name + 1);
if (buildid_len == 0)
return false;
buildid = end_of_alt_name + 1;
}
}
else
continue;
if (buildid == 0 || alt_name == 0)
return false;
alt_file_name = alt_name;
return true;
}
return false;
}
/// Find alternate debuginfo file of a given "link" under a set of
/// root directories.
///
/// The link is a string that is read by the function
/// find_alt_debug_info_link(). That link is a path that is relative
/// to a given debug info file, e.g, "../../../.dwz/something.debug".
/// It designates the alternate debug info file associated to a given
/// debug info file.
///
/// This function will thus try to find the .dwz/something.debug file
/// under some given root directories.
///
/// @param root_dirs the set of root directories to look from.
///
/// @param alt_file_name a relative path to the alternate debug info
/// file to look for.
///
/// @param alt_file_path the resulting absolute path to the alternate
/// debuginfo path denoted by @p alt_file_name and found under one of
/// the directories in @p root_dirs. This is set iff the function
/// returns true.
///
/// @return true iff the function found the alternate debuginfo file.
static bool
find_alt_debug_info_path(const vector<char**> root_dirs,
const string &alt_file_name,
string &alt_file_path)
{
if (alt_file_name.empty())
return false;
string altfile_name = tools_utils::trim_leading_string(alt_file_name, "../");
for (vector<char**>::const_iterator i = root_dirs.begin();
i != root_dirs.end();
++i)
if (tools_utils::find_file_under_dir(**i, altfile_name, alt_file_path))
return true;
return false;
}
/// Return the alternate debug info associated to a given main debug
/// info file.
///
/// @param elf_module the elf module to consider.
///
/// @param debug_root_dirs a set of root debuginfo directories under
/// which too look for the alternate debuginfo file.
///
/// @param alt_file_name output parameter. This is set to the file
/// path of the alternate debug info file associated to @p elf_module.
/// This is set iff the function returns a non-null result.
///
/// @param alt_fd the file descriptor used to access the alternate
/// debug info. If this parameter is set by the function, then the
/// caller needs to fclose it, otherwise the file descriptor is going
/// to be leaked. Note however that on recent versions of elfutils
/// where libdw.h contains the function dwarf_getalt(), this parameter
/// is set to 0, so it doesn't need to be fclosed.
///
/// Note that the alternate debug info file is a DWARF extension as of
/// DWARF 4 ans is decribed at
/// http://www.dwarfstd.org/ShowIssue.php?issue=120604.1.
///
/// @return the alternate debuginfo, or null. If @p alt_fd is
/// non-zero, then the caller of this function needs to call
/// dwarf_end() on the returned alternate debuginfo pointer,
/// otherwise, it's going to be leaked.
static Dwarf*
find_alt_debug_info(Dwfl_Module *elf_module,
const vector<char**> debug_root_dirs,
string& alt_file_name,
int& alt_fd)
{
if (elf_module == 0)
return 0;
Dwarf* result = 0;
find_alt_debug_info_link(elf_module, alt_file_name);
#ifdef LIBDW_HAS_DWARF_GETALT
// We are on recent versions of elfutils where the function
// dwarf_getalt exists, so let's use it.
Dwarf_Addr bias = 0;
Dwarf* dwarf = dwfl_module_getdwarf(elf_module, &bias);
result = dwarf_getalt(dwarf);
alt_fd = 0;
#else
// We are on an old version of elfutils where the function
// dwarf_getalt doesn't exist yet, so let's open code its
// functionality
char *alt_name = 0;
const char *file_name = 0;
void **user_data = 0;
Dwarf_Addr low_addr = 0;
char *alt_file = 0;
file_name = dwfl_module_info(elf_module, &user_data,
&low_addr, 0, 0, 0, 0, 0);
alt_fd = dwfl_standard_find_debuginfo(elf_module, user_data,
file_name, low_addr,
alt_name, file_name,
0, &alt_file);
result = dwarf_begin(alt_fd, DWARF_C_READ);
#endif
if (result == 0)
{
// So we didn't find the alternate debuginfo file from the
// information that is in the debuginfo file associated to
// elf_module. Maybe the alternate debuginfo file is located
// under one of the directories in debug_root_dirs. So let's
// look in there.
string alt_file_path;
if (!find_alt_debug_info_path(debug_root_dirs,
alt_file_name,
alt_file_path))
return result;
// If we reach this point it means we have found the path to the
// alternate debuginfo file and it's in alt_file_path. So let's
// open it and read it.
int fd = open(alt_file_path.c_str(), O_RDONLY);
if (fd == -1)
return result;
result = dwarf_begin(fd, DWARF_C_READ);
#ifdef LIBDW_HAS_DWARF_GETALT
Dwarf_Addr bias = 0;
Dwarf* dwarf = dwfl_module_getdwarf(elf_module, &bias);
dwarf_setalt(dwarf, result);
#endif
}
return result;
}
/// 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;
Elf_Scn *strings_section = find_ksymtab_strings_section(elf_handle);
size_t strings_ndx = strings_section
? elf_ndxscn(strings_section)
: 0;
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,
symbol.st_shndx == strings_ndx);
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;
Elf_Scn *strings_section = find_ksymtab_strings_section(elf_handle);
size_t strings_ndx = strings_section
? elf_ndxscn(strings_section)
: 0;
// 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,
symbol.st_shndx == strings_ndx);
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;
Elf_Scn *strings_section = find_ksymtab_strings_section(elf_handle);
size_t strings_ndx = strings_section
? elf_ndxscn(strings_section)
: 0;
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,
sym->st_shndx == strings_ndx);
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(const 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;
}
/// Get data tag information of an ELF file by looking up into its
/// dynamic segment
///
/// @param elf the elf handle to use for the query.
///
/// @param dt_tag data tag to look for in dynamic segment
/// @param dt_tag_data vector of found information for a given @p data_tag
///
/// @return true iff data tag @p data_tag was found
bool
lookup_data_tag_from_dynamic_segment(Elf* elf,
Elf64_Sxword data_tag,
vector<string>& dt_tag_data)
{
size_t num_prog_headers = 0;
bool found = false;
if (elf_getphdrnum(elf, &num_prog_headers) < 0)
return found;
// Cycle through each program header.
for (size_t i = 0; i < num_prog_headers; ++i)
{
GElf_Phdr phdr_mem;
GElf_Phdr *phdr = gelf_getphdr(elf, i, &phdr_mem);
if (phdr == NULL || phdr->p_type != PT_DYNAMIC)
continue;
// Poke at the dynamic segment like a section, so that we can
// get its section header information; also we'd like to read
// the data of the segment by using elf_getdata() but that
// function needs a Elf_Scn data structure to act on.
// Elfutils doesn't really have any particular function to
// access segment data, other than the functions used to
// access section data.
Elf_Scn *dynamic_section = gelf_offscn(elf, phdr->p_offset);
GElf_Shdr shdr_mem;
GElf_Shdr *dynamic_section_header = gelf_getshdr(dynamic_section,
&shdr_mem);
if (dynamic_section_header == NULL
|| dynamic_section_header->sh_type != SHT_DYNAMIC)
continue;
// Get data of the dynamic segment (seen as a section).
Elf_Data *data = elf_getdata(dynamic_section, NULL);
if (data == NULL)
continue;
// Get the index of the section headers string table.
size_t string_table_index = 0;
ABG_ASSERT (elf_getshdrstrndx(elf, &string_table_index) >= 0);
size_t dynamic_section_header_entry_size = gelf_fsize(elf,
ELF_T_DYN, 1,
EV_CURRENT);
GElf_Shdr link_mem;
GElf_Shdr *link =
gelf_getshdr(elf_getscn(elf,
dynamic_section_header->sh_link),
&link_mem);
ABG_ASSERT(link != NULL);
size_t num_dynamic_section_entries =
dynamic_section_header->sh_size / dynamic_section_header_entry_size;
// Now walk through all the DT_* data tags that are in the
// segment/section
for (size_t j = 0; j < num_dynamic_section_entries; ++j)
{
GElf_Dyn dynamic_section_mem;
GElf_Dyn *dynamic_section = gelf_getdyn(data,
j,
&dynamic_section_mem);
if (dynamic_section->d_tag == data_tag)
{
dt_tag_data.push_back(elf_strptr(elf,
dynamic_section_header->sh_link,
dynamic_section->d_un.d_val));
found = true;
}
}
}
return found;
}
/// Convert the type of ELF file into @ref elf_type.
///
/// @param elf the elf handle to use for the query.
///
/// @return the @ref elf_type for a given elf type.
static elf_type
elf_file_type(Elf* elf)
{
GElf_Ehdr ehdr_mem;
GElf_Ehdr *header = gelf_getehdr (elf, &ehdr_mem);
vector<string> dt_debug_data;
switch (header->e_type)
{
case ET_DYN:
if (lookup_data_tag_from_dynamic_segment(elf, DT_DEBUG, dt_debug_data))
return ELF_TYPE_PI_EXEC;
else
return ELF_TYPE_DSO;
case ET_EXEC:
return ELF_TYPE_EXEC;
case ET_REL:
return ELF_TYPE_RELOCATABLE;
default:
return ELF_TYPE_UNKNOWN;
}
}
// ---------------------------------------
// <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>
// ---------------------------------------
/// The context used to build ABI corpus from debug info in DWARF
/// format.
///
/// This context is to be created by create_read_context(). It's then
/// passed to all the routines that read specific dwarf bits as they
/// get some important data from it.
///
/// When a new data member is added to this context, it must be
/// initiliazed by the read_context::initiliaze() function. So please
/// do not forget.
class read_context
{
public:
struct options_type
{
environment* env;
bool load_in_linux_kernel_mode;
bool load_all_types;
bool show_stats;
bool do_log;
options_type()
: env(),
load_in_linux_kernel_mode(),
load_all_types(),
show_stats(),
do_log()
{}
};// read_context::options_type
/// 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 ctxt the read context 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 read_context& ctxt, const Dwarf_Die *die)
{
const die_source source = ctxt.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 ctxt the read context 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 read_context& ctxt, const Dwarf_Die *die) const
{
return const_cast<die_source_dependant_container_set*>(this)->
get_container(ctxt, 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
suppr::suppressions_type supprs_;
unsigned short dwarf_version_;
Dwfl_Callbacks offline_callbacks_;
// The set of directories under which to look for debug info.
vector<char**> debug_info_root_paths_;
dwfl_sptr handle_;
Dwarf* dwarf_;
// The alternate debug info. Alternate debug info sections are a
// DWARF extension as of DWARF4 and are described at
// http://www.dwarfstd.org/ShowIssue.php?issue=120604.1. Below are
// the file desctor used to access the alternate debug info
// sections, and the representation of the DWARF debug info. Both
// need to be freed after we are done using them, with fclose and
// dwarf_end.
int alt_fd_;
Dwarf* alt_dwarf_;
string alt_debug_info_path_;
// The address range of the offline elf file we are looking at.
Dwfl_Module* elf_module_;
mutable Elf* elf_handle_;
string elf_path_;
mutable Elf_Scn* symtab_section_;
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_;
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<Dwarf_Off> types_to_canonicalize_;
vector<Dwarf_Off> alt_types_to_canonicalize_;
vector<Dwarf_Off> type_unit_types_to_canonicalize_;
vector<type_base_sptr> extra_types_to_canonicalize_;
string_classes_map decl_only_classes_map_;
string_enums_map decl_only_enums_map_;
die_tu_map_type die_tu_map_;
corpus_group_sptr cur_corpus_group_;
corpus_sptr cur_corpus_;
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_;
vector<string> dt_needed_;
string dt_soname_;
string elf_architecture_;
corpus::exported_decls_builder* exported_decls_builder_;
options_type options_;
bool drop_undefined_syms_;
read_context();
private:
mutable symtab_reader::symtab_sptr symtab_;
public:
/// Constructor of read_context.
///
/// @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 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
/// read_context 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.
read_context(const string& elf_path,
const vector<char**>& debug_info_root_paths,
ir::environment* environment,
bool load_all_types,
bool linux_kernel_mode)
{
initialize(elf_path, debug_info_root_paths, environment,
load_all_types, linux_kernel_mode);
}
/// Initializer of read_context.
///
/// @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 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
/// read_context 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.
void
initialize(const string& elf_path,
const vector<char**>& debug_info_root_paths,
ir::environment* environment,
bool load_all_types,
bool linux_kernel_mode)
{
dwarf_version_ = 0;
dwarf_ = 0;
handle_.reset();
alt_fd_ = 0;
alt_dwarf_ = 0;
elf_module_ = 0;
elf_handle_ = 0;
elf_path_ = elf_path;
symtab_section_ = 0;
cur_tu_die_ = 0;
exported_decls_builder_ = 0;
clear_alt_debug_info_data();
supprs_.clear();
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();
alt_types_to_canonicalize_.clear();
type_unit_types_to_canonicalize_.clear();
extra_types_to_canonicalize_.clear();
decl_only_classes_map_.clear();
die_tu_map_.clear();
cur_corpus_group_.reset();
cur_corpus_.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();
dt_needed_.clear();
dt_soname_.clear();
elf_architecture_.clear();
symtab_.reset();
clear_per_translation_unit_data();
memset(&offline_callbacks_, 0, sizeof(offline_callbacks_));
create_default_dwfl(debug_info_root_paths);
options_.env = environment;
options_.load_in_linux_kernel_mode = linux_kernel_mode;
options_.load_all_types = load_all_types;
drop_undefined_syms_ = false;
load_in_linux_kernel_mode(linux_kernel_mode);
}
/// Clear the resources related to the alternate DWARF data.
void
clear_alt_debug_info_data()
{
if (alt_fd_)
{
close(alt_fd_);
alt_fd_ = 0;
if (alt_dwarf_)
{
dwarf_end(alt_dwarf_);
alt_dwarf_ = 0;
}
alt_debug_info_path_.clear();
}
}
/// Detructor of the @ref read_context type.
~read_context()
{
clear_alt_debug_info_data();
}
/// 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.
const ir::environment*
env() const
{return options_.env;}
/// Getter for the current environment.
///
/// @return the current environment.
ir::environment*
env()
{return options_.env;}
/// Setter for the current environment.
///
/// @param env the new current environment.
void
env(ir::environment* env)
{options_.env = 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 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)
{drop_undefined_syms_ = f;}
/// Getter of the suppression specifications to be used during
/// ELF/DWARF parsing.
///
/// @return the suppression specifications.
const suppr::suppressions_type&
get_suppressions() const
{return supprs_;}
/// Getter of the suppression specifications to be used during
/// ELF/DWARF parsing.
///
/// @return the suppression specifications.
suppr::suppressions_type&
get_suppressions()
{return supprs_;}
/// Getter for the callbacks of the Dwarf Front End library of
/// elfutils that is used by this reader to read dwarf.
///
/// @return the callbacks.
const Dwfl_Callbacks*
offline_callbacks() const
{return &offline_callbacks_;}
/// Getter for the callbacks of the Dwarf Front End library of
/// elfutils that is used by this reader to read dwarf.
/// @returnthe callbacks
Dwfl_Callbacks*
offline_callbacks()
{return &offline_callbacks_;}
/// Constructor for a default Dwfl handle that knows how to load debug
/// info from a library or executable elf file.
///
/// @param debug_info_root_paths a vector of pointers to the root
/// path under which to look for the debug info of the elf files
/// that are later handled by the Dwfl. This is 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 the empty set, then "./debug" and /usr/lib/debug will be
/// searched for sub-directories containing the debug info file.
/// Note that for now, elfutils wants this path to be absolute
/// otherwise things just don't work and the debug info is not
/// found.
///
/// @return the constructed Dwfl handle.
void
create_default_dwfl(const vector<char**>& debug_info_root_paths)
{
offline_callbacks()->find_debuginfo = dwfl_standard_find_debuginfo;
offline_callbacks()->section_address = dwfl_offline_section_address;
offline_callbacks()->debuginfo_path =
debug_info_root_paths.empty() ? 0 : debug_info_root_paths.front();
handle_.reset(dwfl_begin(offline_callbacks()),
dwfl_deleter());
debug_info_root_paths_ = debug_info_root_paths;
}
unsigned short
dwarf_version() const
{return dwarf_version_;}
void
dwarf_version(unsigned short v)
{dwarf_version_ = v;}
/// Getter for a smart pointer to a handle on the dwarf front end
/// library that we use to read dwarf.
///
/// @return the dwfl handle.
dwfl_sptr
dwfl_handle() const
{return handle_;}
/// Setter for a smart pointer to a handle on the dwarf front end
/// library that we use to read dwarf.
///
/// @param h the new dwfl handle.
void
dwfl_handle(dwfl_sptr& h)
{handle_ = h;}
Dwfl_Module*
elf_module() const
{return elf_module_;}
/// Return the ELF descriptor for the binary we are analizing.
///
/// @return a pointer to the Elf descriptor representing the binary
/// we are analizing.
Elf*
elf_handle() const
{
if (elf_handle_ == 0)
{
if (elf_module())
{
GElf_Addr bias = 0;
elf_handle_ = dwfl_module_getelf(elf_module(), &bias);
}
}
return elf_handle_;
}
/// Return the ELF descriptor used for DWARF access.
///
/// This can be the same as read_context::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(dwarf());}
/// 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();}
/// Add paths to the set of paths under which to look for split
/// debuginfo files.
///
/// @param debug_info_root_paths the paths to add.
void
add_debug_info_root_paths(const vector<char **>& debug_info_root_paths)
{
debug_info_root_paths_.insert(debug_info_root_paths_.end(),
debug_info_root_paths.begin(),
debug_info_root_paths.end());
}
/// Add a path to the set of paths under which to look for split
/// debuginfo files.
///
/// @param debug_info_root_path the path to add.
void
add_debug_info_root_path(char** debug_info_root_path)
{debug_info_root_paths_.push_back(debug_info_root_path);}
/// Find the alternate debuginfo file associated to a given elf file.
///
/// @param elf_module represents the elf file to consider.
///
/// @param alt_file_name the resulting path to the alternate
/// debuginfo file found. This is set iff the function returns a
/// non-nil value.
Dwarf*
find_alt_debug_info(Dwfl_Module *elf_module,
string& alt_file_name,
int& alt_fd)
{
Dwarf *result = 0;
result = dwarf_reader::find_alt_debug_info(elf_module,
debug_info_root_paths_,
alt_file_name, alt_fd);
return result;
}
/// Load the debug info associated with an elf file that is at a
/// given path.
///
/// @return a pointer to the DWARF debug info pointer upon
/// successful debug info loading, NULL otherwise.
Dwarf*
load_debug_info()
{
if (!dwfl_handle())
return 0;
if (dwarf_)
return dwarf_;
elf_module_ =
dwfl_report_offline(dwfl_handle().get(),
basename(const_cast<char*>(elf_path().c_str())),
elf_path().c_str(),
-1);
dwfl_report_end(dwfl_handle().get(), 0, 0);
Dwarf_Addr bias = 0;
dwarf_ = dwfl_module_getdwarf(elf_module_, &bias);
// Look for split debuginfo files under multiple possible
// debuginfo roots.
for (vector<char**>::const_iterator i = debug_info_root_paths_.begin();
dwarf_ == 0 && i != debug_info_root_paths_.end();
++i)
{
offline_callbacks()->debuginfo_path = *i;
dwarf_ = dwfl_module_getdwarf(elf_module_, &bias);
}
if (!alt_dwarf_)
alt_dwarf_ = find_alt_debug_info(elf_module_,
alt_debug_info_path_,
alt_fd_);
return dwarf_;
}
/// Return the main debug info we are looking at.
///
/// @return the main debug info.
Dwarf*
dwarf() const
{return dwarf_;}
/// Return the alternate debug info we are looking at.
///
/// 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 alternate debug info.
Dwarf*
alt_dwarf() const
{return alt_dwarf_;}
/// 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.
Dwarf*
dwarf_per_die_source(die_source source) const
{
Dwarf *result = 0;
switch(source)
{
case PRIMARY_DEBUG_INFO_DIE_SOURCE:
case TYPE_UNIT_DIE_SOURCE:
result = dwarf();
break;
case ALT_DEBUG_INFO_DIE_SOURCE:
result = alt_dwarf();
break;
case NO_DEBUG_INFO_DIE_SOURCE:
case NUMBER_OF_DIE_SOURCES:
ABG_ASSERT_NOT_REACHED;
}
return result;
}
/// Return the path to the alternate debug info as contained in the
/// .gnu_debugaltlink section of the main elf file.
///
/// 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 path to the alternate debug info file, or an empty
/// path if no alternate debug info file is associated.
const string&
alt_debug_info_path() const
{return alt_debug_info_path_;}
/// Return the path to the ELF path we are reading.
///
/// @return the elf path.
const string&
elf_path() const
{return elf_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<read_context*>(this)->canonical_type_die_offsets_.
get_container(*this, die)
: const_cast<read_context*>(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<read_context*>(this)->
type_die_repr_die_offsets_maps().get_container(source))
: (const_cast<read_context*>(this)->
decl_die_repr_die_offsets_maps().get_container(source));
Dwarf_Die die;
ABG_ASSERT(dwarf_offdie(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;
}
if (odr_is_relevant(&die))
{
// ODR is relevant for this DIE. In this case, all types with
// the same name are considered equivalent. So the array
// i->second shoud only have on element. If not, then
// the DIEs referenced in the array should all compare equal.
// Otherwise, this is an ODR violation. In any case, return
// the first element of the array.
// ABG_ASSERT(i->second.size() == 1);
canonical_die_offset = i->second.front();
get_die_from_offset(source, canonical_die_offset, &canonical_die);
set_canonical_die_offset(canonical_dies, die_offset, die_offset);
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 resuling
/// 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
{
const die_source source = get_die_source(die);
offset_offset_map_type &canonical_dies =
die_as_type
? const_cast<read_context*>(this)->canonical_type_die_offsets_.
get_container(source)
: const_cast<read_context*>(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<read_context*>(this)->
type_die_repr_die_offsets_maps().get_container(*this, die))
: (const_cast<read_context*>(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;
if (odr_is_relevant(die))
{
// ODR is relevant for this DIE. In this case, all types with
// the same name are considered equivalent. So the array
// i->second shoud only have on element. If not, then
// the DIEs referenced in the array should all compare equal.
// Otherwise, this is an ODR violation. In any case, return
// the first element of the array.
// ABG_ASSERT(i->second.size() == 1);
Dwarf_Off canonical_die_offset = i->second.front();
get_die_from_offset(source, canonical_die_offset, &canonical_die);
set_canonical_die_offset(canonical_dies,
die_offset,
canonical_die_offset);
return true;
}
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(*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<read_context*>(this)->canonical_type_die_offsets_.
get_container(source)
: const_cast<read_context*>(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;
}
// 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<read_context*>(this)->
type_die_repr_die_offsets_maps().get_container(*this, die))
: (const_cast<read_context*>(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;
}
if (odr_is_relevant(die))
{
// ODR is relevant for this DIE. In this case, all types with
// the same name are considered equivalent. So the array
// i->second shoud only have on element. If not, then
// the DIEs referenced in the array should all compare equal.
// Otherwise, this is an ODR violation. In any case, return
// the first element of the array.
// ABG_ASSERT(i->second.size() == 1);
Dwarf_Off die_offset = i->second.front();
get_die_from_offset(source, die_offset, &canonical_die);
set_canonical_die_offset(canonical_dies,
initial_die_offset,
die_offset<