diff --git a/configure.ac b/configure.ac index ad021de5..03ca50aa 100644 --- a/configure.ac +++ b/configure.ac @@ -40,6 +40,19 @@ LT_INIT AC_LANG([C++]) AC_LANG_COMPILER_REQUIRE +dnl Check for dependency: libdw (elfutils) +DW_LIBS= +AC_CHECK_LIB(dw, dwfl_begin, [DW_LIBS=-ldw]) +AC_CHECK_HEADER(elfutils/libdwfl.h, + [], + [AC_MSG_ERROR([could not find elfutils/libdwfl.h installed])]) + +if test x$DW_LIBS = x; then + AC_MSG_ERROR([could not find elfutils dwarf library installed]) +fi + +AC_SUBST(DW_LIBS) + dnl Check for dependency: libxml LIBXML2_VERSION=2.6.22 PKG_CHECK_MODULES(XML, libxml-2.0 >= $LIBXML2_VERSION) @@ -59,7 +72,7 @@ AC_SUBST(LIBZIP_CFLAGS) DEPS_CPPFLAGS="$XML_CFLAGS $LIBZIP_CFLAGS" AC_SUBST(DEPS_CPPFLAGS) -DEPS_LIBS="$XML_LIBS $LIBZIP_LIBS" +DEPS_LIBS="$XML_LIBS $LIBZIP_LIBS $DW_LIBS" AC_SUBST(DEPS_LIBS) if test x$ABIGAIL_DEVEL != x; then diff --git a/include/Makefile.am b/include/Makefile.am index c8d590dc..76062b8d 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,6 +2,7 @@ headers = \ abg-ir.h \ abg-corpus.h \ abg-reader.h \ +abg-dwarf-reader.h \ abg-writer.h \ abg-comparison.h \ abg-diff-utils.h \ diff --git a/include/abg-dwarf-reader.h b/include/abg-dwarf-reader.h new file mode 100644 index 00000000..2cef414e --- /dev/null +++ b/include/abg-dwarf-reader.h @@ -0,0 +1,47 @@ +// -*- Mode: C++ -*- +// +// Copyright (C) 2013 Red Hat, Inc. +// +// This file is part of the GNU Application Binary Interface Generic +// Analysis and Instrumentation Library (libabigail). This library is +// free software; you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) any +// later version. + +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Lesser Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with this program; see the file COPYING-LGPLV3. If +// not, see . +// +// Author: Dodji Seketeli + +/// @file +/// +/// This file contains the declarations of the entry points to +/// de-serialize an instance of @ref corpus from a file in elf format, +/// containing dwarf information. + +#include "abg-corpus.h" + +#ifndef __ABG_DWARF_READER_H__ +#define __ABG_DWARF_READER_H__ + +namespace abigail +{ + +namespace dwarf_reader +{ + +corpus_sptr +read_corpus_from_elf(const std::string& elf_path); + +}// end namespace dwarf_reader + +}// end namespace abigail + +#endif //__ABG_DWARF_READER_H__ diff --git a/include/abg-fwd.h b/include/abg-fwd.h index d317e0bb..9b2b8ae8 100644 --- a/include/abg-fwd.h +++ b/include/abg-fwd.h @@ -31,6 +31,7 @@ #include #include #include // for std::rel_ops, at least. +#include #include "abg-hash.h" /// Toplevel namespace for libabigail. @@ -168,17 +169,35 @@ get_type_name(const shared_ptr); shared_ptr get_type_declaration(const shared_ptr); +void +dump(const shared_ptr, std::ostream&); + void dump(const shared_ptr); +void +dump(const shared_ptr, std::ostream&); + void dump(const shared_ptr); +void +dump(const shared_ptr, std::ostream&); + void dump(const shared_ptr); +void +dump(const translation_unit&, std::ostream&); + void dump(const translation_unit&); +void +dump(const shared_ptr, std::ostream&); + +void +dump(const shared_ptr); + } // end namespace abigail #endif // __ABG_IRFWD_H__ diff --git a/include/abg-ir.h b/include/abg-ir.h index f4079817..5c6a672b 100644 --- a/include/abg-ir.h +++ b/include/abg-ir.h @@ -104,21 +104,18 @@ typedef std::vector translation_units; /// into a translation unit. class translation_unit : public traversable_base { -public: + struct priv; + typedef shared_ptr priv_sptr; - /// Convenience typedef for a shared pointer on a @ref global_scope. - typedef shared_ptr global_scope_sptr; - -private: - std::string path_; - location_manager loc_mgr_; - mutable global_scope_sptr global_scope_; + priv_sptr priv_; // Forbidden translation_unit(); +public: + /// Convenience typedef for a shared pointer on a @ref global_scope. + typedef shared_ptr global_scope_sptr; public: - translation_unit(const std::string& path); virtual ~translation_unit(); @@ -141,6 +138,12 @@ public: bool is_empty() const; + shared_ptr + canonicalize_type(shared_ptr) const; + + shared_ptr + canonicalize_type(shared_ptr) const; + void traverse(ir_node_visitor& v); };//end class translation_unit diff --git a/src/Makefile.am b/src/Makefile.am index 6ac394ad..95f7e676 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,6 +9,7 @@ $(h)/abg-corpus.cc \ $(h)/abg-diff-utils.cc \ $(h)/abg-comparison.cc \ $(h)/abg-reader.cc \ +$(h)/abg-dwarf-reader.cc \ $(h)/abg-libxml-utils.cc \ $(h)/abg-libzip-utils.cc \ $(h)/abg-hash.cc \ diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc new file mode 100644 index 00000000..0243d7a7 --- /dev/null +++ b/src/abg-dwarf-reader.cc @@ -0,0 +1,742 @@ +// -*- Mode: C++ -*- +// +// Copyright (C) 2013 Red Hat, Inc. +// +// This file is part of the GNU Application Binary Interface Generic +// Analysis and Instrumentation Library (libabigail). This library is +// free software; you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) any +// later version. + +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Lesser Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with this program; see the file COPYING-LGPLV3. If +// not, see . +// +// Author: Dodji Seketeli + +/// @file +/// +/// This file contains the definitions of the entry points to +/// de-serialize an instance of @ref corpus from a file in elf format, +/// containing dwarf information. + +#include +#include +#include +#include +#include +#include +#include +#include "abg-dwarf-reader.h" + +using std::string; + +namespace abigail +{ + +namespace dwarf_reader +{ + +using std::tr1::dynamic_pointer_cast; +using std::tr1::unordered_map; +using std::stack; + +/// 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_sptr; + +/// A map hashing functor for Dwarf_Die*. +struct die_hash +{ + size_t + operator()(Dwarf_Die* die) const + {return std::hash()(die->addr);} +}; + +/// Convenience typedef for a map which key is a dwarf die, and which +/// value is the corresponding decl_base. +typedef unordered_map die_decl_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_stack_type; + +/// Convenience typedef for a map that contains the decls of the types +/// that have been built so far. This is used to canonicalize the +/// types we create so that only one copy of a given type remains in +/// the system per translation unit, when that makes sense. +typedef unordered_map type_decl_map; + +/// The context accumulated during the reading of dwarf debug info and +/// building of the resulting ABI Corpus as a result. +/// +/// This context is to be created by the top-most function that wants +/// to read debug info and build an ABI corpus from it. It's then +/// passed to all the routines that read specific dwarf bits as they +/// get some important data from it. +class read_context +{ + dwfl_sptr handle_; + const string elf_path_; + die_decl_map_type die_decl_map_; + corpus_sptr cur_corpus_; + translation_unit_sptr cur_tu_; + scope_stack_type scope_stack_; + + read_context(); + +public: + read_context(dwfl_sptr handle, + const string& elf_path) + : handle_(handle), + elf_path_(elf_path) + {} + + dwfl_sptr + dwfl_handle() + {return handle_;} + + const string& + elf_path() const + {return elf_path_;} + + const die_decl_map_type& + die_decl_map() const + {return die_decl_map_;} + + die_decl_map_type& + die_decl_map() + {return die_decl_map_;} + + const corpus_sptr + current_corpus() const + {return cur_corpus_;} + + corpus_sptr + current_corpus() + {return cur_corpus_;} + + void + current_corpus(corpus_sptr c) + { + if (c) + cur_corpus_ = c; + } + + void + reset_current_corpus() + {cur_corpus_.reset();} + + const translation_unit_sptr + current_translation_unit() const + {return cur_tu_;} + + translation_unit_sptr + current_translation_unit() + {return cur_tu_;} + + void + current_translation_unit(translation_unit_sptr tu) + { + if (tu) + cur_tu_ = tu; + } + + const scope_stack_type& + scope_stack() const + {return scope_stack_;} + + scope_stack_type& + scope_stack() + {return scope_stack_;} + + scope_decl_sptr + current_scope() + { + if (scope_stack().empty()) + { + if (current_translation_unit()) + scope_stack().push(current_translation_unit()->get_global_scope()); + } + return scope_stack().top(); + } +};// end class read_context. + +static decl_base_sptr +build_ir_node_from_die(read_context& ctxt, + Dwarf_Die* die); + +/// Constructor for a default Dwfl handle that knows how to load debug +/// info from a library or executable elf file. +/// +/// @return the constructed Dwfl handle. +static Dwfl* +create_default_dwfl() +{ + static const Dwfl_Callbacks offline_callbacks = + { + 0, + .find_debuginfo = dwfl_standard_find_debuginfo, + .section_address = dwfl_offline_section_address, + 0 + }; + + return dwfl_begin(&offline_callbacks); +} + + +/// Create a shared pointer for a pointer to Dwfl. +/// +/// @param dwlf the pointer to Dwfl to create the shared pointer for. +/// +/// @return the newly created shared pointer. +static dwfl_sptr +create_dwfl_sptr(Dwfl* dwfl) +{ + dwfl_sptr result(dwfl, dwfl_deleter()); + return result; +} + +/// Create a shared pointer to a default Dwfl handle. This uses the +/// create_default_dwfl() function. +/// +/// @return the created shared pointer. +static dwfl_sptr +create_default_dwfl_sptr() +{return create_dwfl_sptr(create_default_dwfl());} + +/// Load the debug info associated with an elf file that is at a given +/// path. To do so, we need a handle to the DWARF Front End Library +/// as it's that library that is going to do the heavy work. +/// +/// @param handle a smart pointer to the DWARF Front End Library +/// handle. This can be created by the function +/// create_default_dwfl_sptr(). +/// +/// @param elf_path the path to the elf file to load the debug info +/// from. +/// +/// @return true upon successful debug info loading, false otherwise. +static bool +load_debug_info_from_elf(dwfl_sptr handle, + const string& elf_path) +{ + if (!handle) + return false; + + Dwfl_Module* module = + dwfl_report_offline(handle.get(), + basename(const_cast(elf_path.c_str())), + elf_path.c_str(), + -1); + dwfl_report_end(handle.get(), 0, 0); + + Dwarf_Addr bias = 0; + if (dwfl_module_getdwarf(module, &bias) == 0) + 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 a the string representing the value of the attribute, or +/// an empty string if no string attribute could be found. +static string +die_string_attribute(Dwarf_Die* die, unsigned attr_name) +{ + if (!die) + return ""; + + Dwarf_Attribute attr; + if (!dwarf_attr(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 an unsigned +/// constant. +/// +/// @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. +static bool +die_unsigned_constant_attribute(Dwarf_Die* die, + unsigned attr_name, + size_t& cst) +{ + if (!die) + return false; + + Dwarf_Attribute attr; + Dwarf_Word result = 0; + if (!dwarf_attr(die, attr_name, &attr) + || dwarf_formudata(&attr, &result)) + return false; + + cst = result; + return true; +} + +#if 0 +static bool +die_signed_constant_attribute(Dwarf_Die* die, + unsigned attr_name, + ssize_t& cst) +{ + if (!die) + return false; + + Dwarf_Attribute attr; + Dwarf_Sword result = 0; + if (!dwarf_attr(die, attr_name, &attr) + || dwarf_formsdata(&attr, &result)) + return false; + + cst = result; + return true; +} +#endif + +/// 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_mangled_name(Dwarf_Die* die) +{ + if (!die) + return ""; + + string mangled_name = die_string_attribute(die, DW_AT_linkage_name); + if (mangled_name.empty()) + mangled_name = die_string_attribute(die, DW_AT_MIPS_linkage_name); + return mangled_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 @ref 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(Dwarf_Die* die) +{ + if (!die) + return ""; + + const char* str = dwarf_decl_file(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 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. +/// +/// @return true if the DIE @ref die contains an attribute named @ref +/// attr_name that is a DIE reference, false otherwise. +static bool +die_die_attribute(Dwarf_Die* die, unsigned attr_name, Dwarf_Die& result) +{ + Dwarf_Attribute attr; + if (!dwarf_attr(die, attr_name, &attr)) + return false; + return dwarf_formref_die(&attr, &result); +} + +/// Returns the source location associated with a decl DIE. +/// +/// @param ctxt the @ref read_context to use. +/// +/// @param die the DIE the read the source location from. +static location +die_location(read_context& ctxt, Dwarf_Die* die) +{ + if (!die) + return location(); + + string file = die_decl_file_attribute(die); + size_t line = 0; + die_unsigned_constant_attribute(die, DW_AT_decl_line, line); + + translation_unit_sptr tu = ctxt.current_translation_unit(); + location l = tu->get_loc_mgr().create_new_location(file, line, 1); + return l; +} + +/// Given a DW_TAG_compile_unit, build and return the corresponding +/// abigail::translation_unit ir node. +/// +/// @param ctxt the read_context to use. +/// +/// @param die the DW_TAG_compile_unit DIE to consider. +/// +/// @param recurse if set to yes, this function recursively reads the +/// children dies of @ref die and populate the resulting translation +/// unit. +/// +/// @return a pointer to the resulting translation_unit. +static translation_unit_sptr +build_translation_unit(read_context& ctxt, + Dwarf_Die* die, + bool recurse = false) +{ + translation_unit_sptr result; + + if (!die) + return result; + assert(dwarf_tag(die) == DW_TAG_compile_unit); + + string path = die_string_attribute(die, DW_AT_name); + result.reset(new translation_unit(path)); + + ctxt.current_corpus()->add(result); + ctxt.current_translation_unit(result); + + Dwarf_Die child; + if (!recurse + || (dwarf_child(die, &child) != 0)) + return result; + + do + build_ir_node_from_die(ctxt, &child); + while (dwarf_siblingof(&child, &child) == 0); + + return result; +} + +/// Build a @ref type_decl out of a DW_TAG_base_type DIE. +/// +/// @param die the DW_TAG_base_type to consider. +/// +/// @return the resulting decl_base_sptr. +static type_decl_sptr +build_type_decl(read_context& /*ctxt*/, + Dwarf_Die* die) +{ + type_decl_sptr result; + + if (!die) + return result; + assert(dwarf_tag(die) == DW_TAG_base_type); + + string type_name = die_string_attribute(die, DW_AT_name); + size_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 (byte_size == 0 && bit_size == 0) + return result; + + if (bit_size == 0) + bit_size = byte_size * 8; + + size_t alignment = bit_size < 8 ? 8 : bit_size; + string mangled_name = die_mangled_name(die); + + result.reset(new type_decl(type_name, bit_size, alignment, + location(), mangled_name)); + return result; +} + +/// Build a @ref var_decl out of a DW_TAG_variable DIE. +/// +/// @param ctxt the read context to use. +/// +/// @param die the DIE to read from to build the @ref var_decl. +/// +/// @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(read_context& ctxt, + Dwarf_Die *die) +{ + var_decl_sptr result; + + if (!die) + return result; + assert(dwarf_tag(die) == DW_TAG_variable); + + type_base_sptr type; + Dwarf_Die type_die; + if (die_die_attribute(die, DW_AT_type, type_die)) + { + decl_base_sptr ty = build_ir_node_from_die(ctxt, &type_die); + if (!ty) + return result; + type = is_type(ty); + assert(type); + } + + string name = die_string_attribute(die, DW_AT_name); + string mangled_name = die_mangled_name(die); + location loc = die_location(ctxt, die); + + result.reset(new var_decl(name, type, loc, mangled_name)); + + return result; +} + +/// Read all @ref translation_unit possible from the debug info +/// accessible through a DWARF Front End Library handle, and stuff +/// them into a libabigail ABI Corpus. +/// +/// @param ctxt the read context. +/// +/// @return a pointer to the resulting corpus, or NULL if the corpus +/// could not be constructed. +static corpus_sptr +build_corpus(read_context& ctxt) +{ + Dwarf_Die *cu = 0; + + Dwarf_Addr bias = 0; + + while ((cu = dwfl_nextcu(ctxt.dwfl_handle().get(), cu, &bias))) + { + if (!ctxt.current_corpus()) + { + corpus_sptr corp (new corpus(ctxt.elf_path())); + ctxt.current_corpus(corp); + } + + // 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(ctxt, cu, + /*recurse=*/true); + assert(ir_node); + } + return ctxt.current_corpus(); +} +/// Build an IR node from a given DIE and add the node to the current +/// IR being build and held in the read_context. +/// +/// @param ctxt the read context. +/// +/// @parm die the DIE to consider. +/// +/// @return the resulting IR node. +static decl_base_sptr +build_ir_node_from_die(read_context& ctxt, + Dwarf_Die* die) +{ + decl_base_sptr result; + + if (!die) + return result; + + die_decl_map_type::const_iterator it = ctxt.die_decl_map().find(die); + if (it != ctxt.die_decl_map().end()) + return it->second; + + int tag = dwarf_tag(die); + switch (tag) + { + // Type DIEs we intent to support someday, maybe. + case DW_TAG_base_type: + if((result = build_type_decl(ctxt, die))) + { + result = ctxt.current_translation_unit()->canonicalize_type(result); + assert(result); + + if (result->get_scope()) + // This base type is the same as a type that was already added + // to the IR tree. Do not add a new one. Just re-use the + // previous one. + ; + else + add_decl_to_scope(result, ctxt.current_scope()); + } + break; + case DW_TAG_typedef: + break; + case DW_TAG_pointer_type: + break; + case DW_TAG_reference_type: + break; + case DW_TAG_rvalue_reference_type: + break; + case DW_TAG_const_type: + break; + case DW_TAG_volatile_type: + break; + case DW_TAG_enumeration_type: + break; + case DW_TAG_class_type: + break; + case DW_TAG_string_type: + break; + case DW_TAG_structure_type: + break; + case DW_TAG_subroutine_type: + break; + case DW_TAG_union_type: + break; + case DW_TAG_array_type: + break; + case DW_TAG_packed_type: + break; + case DW_TAG_set_type: + break; + case DW_TAG_file_type: + break; + case DW_TAG_ptr_to_member_type: + break; + case DW_TAG_subrange_type: + break; + case DW_TAG_thrown_type: + break; + case DW_TAG_restrict_type: + break; + case DW_TAG_interface_type: + break; + case DW_TAG_unspecified_type: + break; + case DW_TAG_mutable_type: + break; + case DW_TAG_shared_type: + break; + + // Other declarations we intend to support someday, maybe. + + case DW_TAG_compile_unit: + // We shouldn't reach this point b/c this should be handled by + // build_translation_unit. + abort(); + break; + + case DW_TAG_namespace: + break; + case DW_TAG_variable: + if ((result = build_var_decl(ctxt, die))) + add_decl_to_scope(result, ctxt.current_scope()); + break; + case DW_TAG_subprogram: + break; + case DW_TAG_formal_parameter: + break; + case DW_TAG_constant: + break; + case DW_TAG_enumerator: + break; + + // Other declaration we don't really intend to support. + 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_member: + 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_inlined_subroutine: + case DW_TAG_module: + 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_partial_unit: + case DW_TAG_imported_unit: + 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) + ctxt.die_decl_map()[die] = result; + + return result; +} + +/// Read all @ref 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. +/// +/// @return a pointer to the resulting @ref corpus. +corpus_sptr +read_corpus_from_elf(const std::string& elf_path) +{ + // Create a DWARF Front End Library handle to be used by functions + // of that library. + dwfl_sptr handle = create_default_dwfl_sptr(); + + // Load debug info from the elf path. + if (!load_debug_info_from_elf(handle, elf_path)) + return corpus_sptr(); + + read_context ctxt(handle, elf_path); + + // Now, read an ABI corpus proper from the debug info we have + // through the dwfl handle. + corpus_sptr corp = build_corpus(ctxt); + + return corp; +} + +}// end namespace dwarf_reader + +}// end namespace abigail diff --git a/src/abg-ir.cc b/src/abg-ir.cc index c7b87c77..7e6102d6 100644 --- a/src/abg-ir.cc +++ b/src/abg-ir.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include "abg-ir.h" namespace abigail @@ -37,6 +38,7 @@ namespace abigail using std::string; using std::list; using std::vector; +using std::tr1::unordered_map; using std::tr1::dynamic_pointer_cast; using std::tr1::static_pointer_cast; @@ -141,13 +143,28 @@ location_manager::expand_location(const location location, column = l.column_; } +typedef unordered_map, + bool, + type_base::shared_ptr_hash, + type_shared_ptr_equal> type_ptr_map; + +/// Private type to hold private members of @ref translation_unit +struct translation_unit::priv +{ + std::string path_; + location_manager loc_mgr_; + mutable global_scope_sptr global_scope_; + type_ptr_map canonical_types_; +}; // end translation_unit::priv + +// + /// Constructor of translation_unit. /// /// @param path the location of the translation unit. translation_unit::translation_unit(const std::string& path) - : path_ (path) -{ -} + : priv_(new priv) +{priv_->path_ = path;} /// Getter of the the global scope of the translation unit. /// @@ -157,18 +174,17 @@ translation_unit::translation_unit(const std::string& path) const shared_ptr translation_unit::get_global_scope() const { - if (!global_scope_) - global_scope_.reset(new global_scope(const_cast(this))); - return global_scope_; + if (!priv_->global_scope_) + priv_->global_scope_.reset + (new global_scope(const_cast(this))); + return priv_->global_scope_; } /// @return the path of the compilation unit associated to the current /// instance of translation_unit. const std::string& translation_unit::get_path() const -{ - return path_; -} +{return priv_->path_;} /// Set the path associated to the current instance of /// translation_unit. @@ -176,9 +192,7 @@ translation_unit::get_path() const /// @param a_path the new path to set. void translation_unit::set_path(const string& a_path) -{ - path_ = a_path; -} +{priv_->path_ = a_path;} /// Getter of the location manager for the current translation unit. /// @@ -186,7 +200,7 @@ translation_unit::set_path(const string& a_path) /// translation unit. location_manager& translation_unit::get_loc_mgr() -{return loc_mgr_;} +{return priv_->loc_mgr_;} /// const Getter of the location manager. /// @@ -194,7 +208,7 @@ translation_unit::get_loc_mgr() /// translation unit. const location_manager& translation_unit::get_loc_mgr() const -{return loc_mgr_;} +{return priv_->loc_mgr_;} /// Tests whether if the current translation unit contains ABI /// artifacts or not. @@ -204,6 +218,59 @@ bool translation_unit::is_empty() const {return get_global_scope()->is_empty();} +/// If the current translation_unit "knows" about a type T' that is +/// equivalent to a given T, then this method returns T' when passed +/// T. Otherwise, the function stores T, so that next time it sees a +/// T'', it can say that it is equivalent to T and then return that T. +/// +/// In other words, this methods can be used to help enforce that if +/// we build two types that are equivalent at the same scope then we +/// can decide to just keep one. +/// +/// @param t the type to canonicalize. +/// +/// @return the canonical type for @ref t. +type_base_sptr +translation_unit::canonicalize_type(type_base_sptr t) const +{ + if (!t) + return t; + + type_ptr_map::iterator e = + priv_->canonical_types_.find(t); + + if (e == priv_->canonical_types_.end()) + { + priv_->canonical_types_[t] = true; + return t; + } + return e->first; +} + +/// Canonicalize the type of a given type declaration. +/// +/// To understand more about type canonicalization, please read the +/// API doc of the overload of this function that takes a @ref +/// type_base_sptr. +/// +/// @param t the type declaration to return a canonical type +/// declaration for. +/// +/// @return the declaration for the canonical type of the type for +/// @ref t. +decl_base_sptr +translation_unit::canonicalize_type(decl_base_sptr t) const +{ + type_base_sptr type = is_type(t); + + if (!type) + return t; + + type = canonicalize_type(type); + assert(type); + + return get_type_declaration(type); +} /// This implements the traversable_base::traverse pure virtual /// function. /// @@ -216,6 +283,8 @@ translation_unit::traverse(ir_node_visitor& v) translation_unit::~translation_unit() {} +// + // decl_base::decl_base(const std::string& name, location locus, diff --git a/src/abg-writer.cc b/src/abg-writer.cc index 950605ec..7d62d3c0 100644 --- a/src/abg-writer.cc +++ b/src/abg-writer.cc @@ -1780,45 +1780,102 @@ write_corpus_to_archive(const corpus_sptr corp) // +/// Serialize a pointer to decl_base to an output stream. +/// +/// @param d the pointer to decl_base to serialize. +/// +/// @param o the output stream to consider. +void +dump(const decl_base_sptr d, std::ostream& o) +{ + xml_writer::write_context ctxt(o); + write_decl(d, ctxt, /*indent=*/0); + cerr << "\n"; +} + /// Serialize a pointer to decl_base to stderr. /// /// @param d the pointer to decl_base to serialize. void dump(const decl_base_sptr d) -{ - xml_writer::write_context ctxt(cerr); - write_decl(d, ctxt, /*indent=*/0); - cerr << "\n"; -} +{dump(d, cerr);} + +/// Serialize a pointer to type_base to an output stream. +/// +/// @param t the pointer to type_base to serialize. +/// +/// @param o the output stream to serialize the @ref type_base to. +void +dump(const type_base_sptr t, std::ostream& o) +{dump(get_type_declaration(t), o);} /// Serialize a pointer to type_base to stderr. /// /// @param t the pointer to type_base to serialize. void dump(const type_base_sptr t) -{return dump(dynamic_pointer_cast(t));} +{dump(t, cerr);} + +/// Serialize a pointer to var_decl to an output stream. +/// +/// @param v the pointer to var_decl to serialize. +/// +/// @param o the output stream to serialize the @ref var_decl to. +void +dump(const var_decl_sptr v, std::ostream& o) +{ + xml_writer::write_context ctxt(o); + write_var_decl(v, ctxt, /*mangled_name*/true, /*indent=*/0); + cerr << "\n"; +} /// Serialize a pointer to var_decl to stderr. /// /// @param v the pointer to var_decl to serialize. void dump(const var_decl_sptr v) -{ - xml_writer::write_context ctxt(cerr); - write_var_decl(v, ctxt, /*mangled_name*/true, /*indent=*/0); - cerr << "\n"; -} +{dump(v, cerr);} -/// Serialize an instance of translation_unit to stderr. +/// Serialize a @ref translation_unit to an output stream. /// /// @param t the translation_unit to serialize. +/// +/// @param o the outpout stream to serialize the translation_unit to. void -dump(const translation_unit& t) +dump(const translation_unit& t, std::ostream& o) { - xml_writer::write_context ctxt(cerr); + xml_writer::write_context ctxt(o); write_translation_unit(t, ctxt, /*indent=*/0); cerr << "\n"; } +/// Serialize an instance of @ref translation_unit to stderr. +/// +/// @param t the translation_unit to serialize. +void +dump(const translation_unit& t) +{dump(t, cerr);} + +/// Serialize a pointer to @ref translation_unit to an output stream. +/// +/// @param t the @ref translation_unit_sptr to serialize. +/// +/// @param o the output stream to serialize the translation unit to. +void +dump(const translation_unit_sptr t, std::ostream& o) +{ + if (t) + dump(*t, o); +} + +/// Serialize a pointer to @ref translation_unit to stderr. +/// +/// @param t the translation_unit_sptr to serialize. +void +dump(const translation_unit_sptr t) +{ + if (t) + dump(*t); +} // } //end namespace abigail diff --git a/tools/Makefile.am b/tools/Makefile.am index 00bd21d3..b463b1ef 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -7,7 +7,7 @@ libtoolsutils_la_SOURCES= \ $(h)/abg-tools-utils.h \ $(h)/abg-tools-utils.cc -bin_PROGRAMS = biar bidiff bilint +bin_PROGRAMS = biar bidiff bilint bidw biar_SOURCES = $(h)/biar.cc biardir = $(bindir) biar_LDFLAGS = $(abs_top_builddir)/src/libabigail.la $(abs_top_builddir)/tools/libtoolsutils.la @@ -23,4 +23,9 @@ bilintdir = $(bindir) bilint_LDFLAGS = $(abs_top_builddir)/src/libabigail.la $(abs_top_builddir)/tools/libtoolsutils.la bilint_DEPENDENCIES = libtoolsutils.la +bidw_SOURCES = $(h)/bidw.cc +bidwdir = $(bindir) +bidw_LDFLAGS = $(abs_top_builddir)/src/libabigail.la $(abs_top_builddir)/tools/libtoolsutils.la +bidw_DEPENDENCIES = libtoolsutils.la + AM_CPPFLAGS=-I$(abs_top_srcdir)/include -I$(abs_top_srcdir)/tools diff --git a/tools/bidw.cc b/tools/bidw.cc new file mode 100644 index 00000000..50e43506 --- /dev/null +++ b/tools/bidw.cc @@ -0,0 +1,135 @@ +// -*- Mode: C++ -*- +// +// Copyright (C) 2013 Red Hat, Inc. +// +// This file is part of the GNU Application Binary Interface Generic +// Analysis and Instrumentation Library (libabigail). This library is +// free software; you can redistribute it and/or modify it under the +// terms of the GNU Lesser General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) any +// later version. + +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Lesser Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with this program; see the file COPYING-LGPLV3. If +// not, see . +// +// Author: Dodji Seketeli + +/// @file +/// +/// This program reads an elf file, try to load its debug info (in +/// DWARF format) and emit it back in a set of "text sections" in native +/// libabigail XML format. + +#include +#include +#include +#include +#include +#include +#include +#include "abg-tools-utils.h" +#include "abg-corpus.h" +#include "abg-dwarf-reader.h" + +using std::string; +using std::cerr; +using std::cout; +using std::ostream; +using std::ofstream; +using abigail::tools::check_file; + +struct options +{ + string in_file_path; + string out_file_path; +}; + +static void +display_usage(const string& prog_name, ostream& out) +{ + out << "usage: " << prog_name << "[options] []\n" + << " where options can be: \n" + << " --help display this message\n" + << " --out-file write the output to 'file-path'\n"; +} + +static bool +parse_command_line(int argc, char* argv[], options& opts) +{ + if (argc < 2) + return false; + + for (int i = 1; i < argc; ++i) + { + if (argv[i][0] != '-') + { + if (opts.in_file_path.empty()) + opts.in_file_path = argv[i]; + else + return false; + } + else if (!strcmp(argv[i], "--out-file")) + { + if (argc <= i + 1 + || argv[i + 1][0] == '-' + || !opts.out_file_path.empty()) + return false; + + opts.out_file_path = argv[i + 1]; + ++i; + } + else if (!strcmp(argv[i], "--help")) + return false; + else + return false; + } + + return true; +} + +int +main(int argc, char* argv[]) +{ + options opts; + + if (!parse_command_line(argc, argv, opts)) + { + display_usage(argv[0], cerr); + return 1; + } + + assert(!opts.in_file_path.empty()); + if (!check_file(opts.in_file_path, cerr)) + return 1; + + using abigail::corpus; + using abigail::corpus_sptr; + using abigail::translation_units; + using abigail::dwarf_reader::read_corpus_from_elf; + + corpus_sptr corp = read_corpus_from_elf(opts.in_file_path); + if (!corp) + { + cerr << "Could not read debug info from " << opts.in_file_path << "\n"; + return 1; + } + + cout << "for corpus " << corp->get_path() << ":\n"; + for (translation_units::const_iterator it = + corp->get_translation_units().begin(); + it != corp->get_translation_units().end(); + ++it) + { + cout << "translation unit: " << (*it)->get_path() << ":\n"; + dump(*it, cout); + cout << "\n"; + } + + return 0; +}