mirror of
git://sourceware.org/git/libabigail.git
synced 2024-12-24 10:42:21 +00:00
a102a2f032
This patch implements the weak mode of abicompat. In this mode, just the application and the new version of the library are provided. The types of functions and variables of the library that are consumed by the application are compared to the types of the functions and variables expected by the application. The goal is to check if the types of the declarations consumed by the application and provided by the library are compatible with what the application expects. The abicompat first gets the set of symbols undefined in the application and exported by the library. It then builds the set of declarations exported by the library that have those symbols. We call these the set of declarations of the library that are consumed by the application. Note that the debug information for the application does not contain the declarations of the functions/variables whose symbols are undefined. So we can not just read them to compare them to declarations exported by the library. But the *types* of the variables and the *sub-types* of the functions whose symbols are undefined in the application are present in the debug information of the application. So in the weak mode, abicompat compare the *types* of the declarations consumed by the application as expected by the application (described by the debug information of the application) with the types of the declarations exported by the library. To do this a number of changes were necessary. The patch builds a representation of all the types found in the application's debug info. Before that, only the types that are reachable from exported declarations were represented. The abidw tool got a new --load-all-types to test this new ability of loading all types. The patch also adds support for looking a type, not by name, but by its internal representation. In the comparison engine, function_type_diff is introduced to represent changes between two function types. For this, a new class type_or_decl_base has been introduced in the IR. It's now the base class for both decl_base and type_base. And abigail::comparison::diff now takes two pointers of type_or_decl, not decl_base anymore. So function_type_diff can take two function_type now; not that a function_type has no declaration so it doesn't inherit decl_base. A bunch of changes got made just to adjust to this modification. A number of fixes were made too, to make this work, like adding missing comparison operators, removing asserts that too strong, etc.. The patch also adjust the test suite as well as the documentation. * include/abg-fwd.h (class type_or_decl_base): Forward declare this. (is_decl, is_type, is_function_type, get_name, get_type_name) (get_function_type_name, get_pretty_representation) (lookup_function_type_in_corpus, lookup_type_in_translation_unit) (lookup_function_type_in_translation_unit) (synthesize_function_type_from_translation_unit) (hash_type_or_decl): New function declarations. * src/abg-corpus.cc (lookup_type_in_corpus) (lookup_function_type_in_corpus): Define new functions. * include/abg-ir.h (translation_unit::lookup_function_type_in_translation_unit): Declare new friend function. (class type_or_decl_base): Declare this. (operator==(const type_or_decl_base&, const type_or_decl_base&)): Declare new operator. (operator==(const type_or_decl_base_sptr&, const type_or_decl_base_sptr&)): Likewise. (class {decl_base, type_base}): Make these class inherit type_or_decl_base. (decl_base::get_member_scopes): New const overload. (bool operator==(const function_decl::parameter_sptr&, const function_decl::parameter_sptr&)): New operator. (function_type::get_parameters): Remove the non-const overload. (function_type::get_pretty_representation): Declare new member function. (method_type::get_pretty_representation): Likewise. * src/abg-ir.cc (bool operator==(const type_or_decl_base&, const type_or_decl_base&)): Define new equality operator. (bool operator==(const type_or_decl_base_sptr&, const type_or_decl_base_sptr&)): Likewise. (strip_typedef): Do not expect canonicalized types anymore. Now the system accepts (and expects) canonicalized types in certain cases. For instance, non-complete types and aggregated types that contain non-complete sub-types. (get_name, get_function_type_name, get_type_name) (get_pretty_representation, is_decl, is_type, is_function_type) (lookup_function_type_in_translation_unit) (synthesize_function_type_from_translation_unit) (lookup_type_in_scope, lookup_type_in_translation_unit): Define new functions or new overloads. (bool operator==(const function_decl::parameter_sptr&, const function_decl::parameter_sptr& r)): Define new operator. (function_type::get_parameters): Remove non-const overload. (function_type::get_pretty_representation): Define new function. (function_type::traverse): Adjust. (method_type::get_pretty_representation): Likewise. (function_decl::get_pretty_representation): Avoid emitting the type of cdtors. (hash_type_or_decl): Define new function. * include/abg-dwarf-reader.h (create_read_context) (read_corpus_from_elf): Take a new 'read_all_types' flag. * src/abg-dwarf-reader.cc (read_context::load_all_types_): New flag. (read_context::read_context): Initialize it. (read_context::canonical_types_scheduled): If some types still have non-canonicalized sub-types, then do not canonicalize them. (read_context::load_all_types): New member functions. (build_function_decl): Do not represent void return type like empty type anymore, rather, represent it like a void type node. (build_ir_node_from_die): When asked, load all types including those that are not reachable from an exported declaration. (create_read_context, read_corpus_from_elf): Take a new 'load_all_types' flag and honour it. * src/abg-reader.cc (read_context::type_is_from_translation_unit): Support looking up function types in the current translation unit, now that we now how to lookup function types. * include/abg-comparison.h (diff_context::{has_diff_for, add_diff, set_canonical_diff_for, set_or_get_canonical_diff_for, get_canonical_diff_for}): Make these take instances of type_or_decl_base_sptr, instead of decl_base_sptr. (diff::diff): Likewise. (diff::{first_subject, second_subject}): Make these return type_or_decl_base_sptr instead of decl_base_sptr. (type_diff_base::type_diff_base): Make these take instances of type_or_decl_base_sptr instead of decl_base_sptr. (distinct_diff::distinct_diff): Likewise. (distinct_diff::{first, second}): Make these return type_or_decl_base_sptr instead of decl_base_sptr. (distinct_diff::entities_are_of_distinct_kinds): Make these take instances of type_or_decl_base_sptr instead of decl_base_sptr. (class function_type_diff): Create this new type. It's a factorization of the function_decl_diff type. * src/abg-comparison.cc (): * src/abg-comp-filter.cc ({harmless, harmful}_filter::visit): Adjust as diff::{first,second}_subject() now returns a type_or_decl_base_sptr, no more a decl_base_sptr. (decls_type, decls_diff_map_type): Remove these typedefs and replace it with ... (types_or_decls_type, types_or_decls_diff_map_type): ... these. (struct {decls_hash, decls_equals): Remove these type sand replace them with ... (struct {types_or_decls_hash, types_or_decls_equals}): ... these. ({type_suppression, variable_suppression}::suppresses_diff): Adjust. (diff_context::priv::decls_diff_map): Replace this with ... (diff_context::priv::types_or_decls_diff_map): ... this. (diff_context::{has_diff_for, add_diff, get_canonical_diff_for, set_canonical_diff_for, set_or_get_canonical_diff_for}): Take type_or_decl_base_sptr instead of decl_base_sptr. (diff::priv::{first, second}_subject): Make the type of these be type_or_decl_base_sptr, no more decl_base_sptr. (diff::priv::priv): Adjust for the subjects of the diff being of type type_or_decl_sptr now, no more decl_base_sptr. (diff_less_than_functor::operator()(const diff_sptr, const diff_sptr) const): Adjust. (diff::diff): djust for the subjects of the diff being of type type_or_decl_sptr now, no more decl_base_sptr. (diff::{first,second}_subject): Make the type of these be type_or_decl_base_sptr, no more decl_base_sptr. (report_size_and_alignment_changes): Likewise. (type_diff_base::type_diff_base): Make the type of this be type_or_decl_base_sptr instead of type_base_sptr. (distinct_diff::distinct_diff): Make this take instances of type_or_decl_base_sptr instead of decl_base_sptr. (distinct_diff::{first, second, entities_are_of_distinct_kinds}): Likewise. (distinct_diff::has_changes): Simplify logic. (distinct_diff::report): Adjust. (compute_diff_for_types): Add an additional case to support the new function_type. (report_size_and_alignment_changes): Make this take instances of type_or_decl_base_sptr instead of decl_base_sptr. (class_diff::priv::member_type_has_changed): Return an instance of type_or_decl_base_sptr rather than a decl_base_sptr. (class_diff::report): Adjust. (diff_comp::operator()(const diff&, diff&) const): Adjust. (enum function_decl_diff::priv::Flags): Remove. (function_decl_diff::priv::{first_fn_flags_, second_fn_flags_, fn_flags_changes_}): Remove. (function_decl_diff::priv::{fn_is_declared_inline_to_flag, fn_binding_to_flag}): Remove. (function_decl_diff::{deleted_parameter_at, inserted_parameter_at}): Remove. (function_decl_diff::ensure_lookup_tables_populated): Empty this. (function_decl_diff::chain_into_hierarchy): Adjust. (function_decl_diff::function_decl_diff): This now only takes the subjects. It's body is now empty. (function_decl_diff::{return_type_diff, subtype_changed_parms, removed_parms, added_parms, type_diff}): Remove these member functions. (function_decl_diff::type_diff): Define new member function. (function_decl_diff::report): Simplify logic by using the reporting of the child type diff node. (compute_diff): Likewise, in the overload for function_decl_sptr simplify logic by using the child type diff object. (function_type_diff::priv): Define new type. (function_type_diff::{function_type_diff, ensure_lookup_tables_populated, deleted_parameter_at, inserted_parameter_at, finish_diff_type, first_function_type, second_function_type, return_type_diff, subtype_changed_parms, removed_parms, added_parms, get_pretty_representation, has_changes, has_local_changes, report, chain_into_hierarchy}): Define new functions. (compute_diff): Define new overload for function_type_sptr. * tools/abicompat.cc (options::weak_mode): New data member. (options::options): Initialize it. (enum abicompat_status): New enum (abicompat_status operator|(abicompat_status, abicompat_status)) (abicompat_status& operator|=(abicompat_status &, abicompat_status)) (abicompat_status operator&(abicompat_status, abicompat_status)): New operators to manipulate the abicompat_status enum. (display_usage): Add help string for the new --weak-mode option. (parse_command_line): Add the new --weak-mode command line argument. If the tool is called with just the application and one library then assume that we are in the weak mode. (perform_compat_check_in_normal_mode): Define new function, factorized from what was in the main function. (perform_compat_check_in_weak_mode): Define new function. (struct {fn,var}_change): Define new types. (main): Use perform_compat_check_in_weak_mode() and perform_compat_check_in_normal_mode(). * tools/abidiff.cc (main): Adjust. * tools/abidw.cc: (options::load_all_types): Add new data member. (options::options): Initialize it. (display_usage): New help string for --load-all-types. (parse_command_line): Support the new --load-all-types option. (main): Adjust and honour the --load-all-types option. * tools/abilint.cc (main): Adjust. * doc/manuals/abicompat.rst: Update documentation for the new weak mode. Also provide stuff that was missing from the examples provided. * doc/manuals/abidw.rst: Update documentation for the new --load-all-types option. * tests/print-diff-tree.cc (main): Adjust. * tests/test-diff-dwarf.cc (main): Likewise. * tests/test-read-dwarf.cc (main): Likewise. * tests/data/test-abicompat/test0-fn-changed-app: Recompile this. * tests/data/test-abicompat/libtest5-fn-changed-libapp-v{0,1}.so: New new test input binaries * tests/data/test-abicompat/test5-fn-changed-app: Likewise. * tests/data/test-abicompat/test6-var-changed-app: Likewise. * tests/data/test-abicompat/libtest6-var-changed-libapp-v{0,1}.so: Likewise. * tests/data/test-abicompat/test5-fn-changed-report-0.txt: Reference output for one test above. * tests/data/test-abicompat/test6-var-changed-report-0.txt: Likewise. * tests/data/test-abicompat/test5-fn-changed-app.cc: Source file for a binary above. * tests/data/test-abicompat/test5-fn-changed-libapp-v{0,1}.{h,cc}: Likewise. * tests/data/test-abicompat/test6-var-changed-libapp-v{0,1}.{cc,h}: Likewise. * tests/data/test-abicompat/test6-var-changed-app.cc: Likewise. * tests/data/Makefile.am: Add the test related files above to the source distribution. * tests/test-abicompat.cc (in_out_spec): Add the new test input above to the list of inputs to feed to this test harness. (main): Support taking just the app and one library. * tests/data/test-read-dwarf/test{0, 1, 2.so, 3.so, 5.o, 8-qualified-this-pointer.so,}.abi: Adjust for void type being really emitted now, as opposed to just being an empty type. Signed-off-by: Dodji Seketeli <dodji@redhat.com>
255 lines
6.8 KiB
C++
255 lines
6.8 KiB
C++
// -*- Mode: C++ -*-
|
|
//
|
|
// Copyright (C) 2013-2015 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 <http://www.gnu.org/licenses/>.
|
|
//
|
|
// 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 <unistd.h>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <tr1/memory>
|
|
#include "abg-tools-utils.h"
|
|
#include "abg-corpus.h"
|
|
#include "abg-dwarf-reader.h"
|
|
#include "abg-writer.h"
|
|
|
|
using std::string;
|
|
using std::cerr;
|
|
using std::cout;
|
|
using std::ostream;
|
|
using std::ofstream;
|
|
using std::tr1::shared_ptr;
|
|
|
|
struct options
|
|
{
|
|
string in_file_path;
|
|
string out_file_path;
|
|
shared_ptr<char> di_root_path;
|
|
bool check_alt_debug_info_path;
|
|
bool show_base_name_alt_debug_info_path;
|
|
bool write_architecture;
|
|
bool load_all_types;
|
|
|
|
options()
|
|
: check_alt_debug_info_path(),
|
|
show_base_name_alt_debug_info_path(),
|
|
write_architecture(true),
|
|
load_all_types()
|
|
{}
|
|
};
|
|
|
|
static void
|
|
display_usage(const string& prog_name, ostream& out)
|
|
{
|
|
out << "usage: " << prog_name << " [options] [<path-to-elf-file>]\n"
|
|
<< " where options can be: \n"
|
|
<< " --help display this message\n"
|
|
<< " --debug-info-dir <dir-path> look for debug info under 'dir-path'\n"
|
|
<< " --out-file <file-path> write the output to 'file-path'\n"
|
|
<< " --no-architecture do not emit architecture info in the output\n"
|
|
<< " --check-alternate-debug-info <elf-path> check alternate debug info "
|
|
"of <elf-path>\n"
|
|
<< " --check-alternate-debug-info-base-name <elf-path> check alternate "
|
|
"debug info of <elf-path>, and show its base name\n"
|
|
<< " --load-all-types read all types including those not reachable from"
|
|
"exported declarations\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], "--debug-info-dir"))
|
|
{
|
|
if (argc <= i + 1
|
|
|| argv[i + 1][0] == '-'
|
|
|| !opts.out_file_path.empty())
|
|
return false;
|
|
// elfutils wants the root path to the debug info to be
|
|
// absolute.
|
|
opts.di_root_path =
|
|
abigail::tools_utils::make_path_absolute(argv[i + 1]);
|
|
++i;
|
|
}
|
|
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], "--no-architecture"))
|
|
opts.write_architecture = false;
|
|
else if (!strcmp(argv[i], "--check-alternate-debug-info")
|
|
|| !strcmp(argv[i], "--check-alternate-debug-info-base-name"))
|
|
{
|
|
if (argc <= i + 1
|
|
|| argv[i + 1][0] == '-'
|
|
|| !opts.in_file_path.empty())
|
|
return false;
|
|
if (!strcmp(argv[i], "--check-alternate-debug-info-base-name"))
|
|
opts.show_base_name_alt_debug_info_path = true;
|
|
opts.check_alt_debug_info_path = true;
|
|
opts.in_file_path = argv[i + 1];
|
|
++i;
|
|
}
|
|
else if (!strcmp(argv[i], "--load-all-types"))
|
|
opts.load_all_types = true;
|
|
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], cout);
|
|
return 1;
|
|
}
|
|
|
|
assert(!opts.in_file_path.empty());
|
|
if (!abigail::tools_utils::check_file(opts.in_file_path, cerr))
|
|
return 1;
|
|
|
|
abigail::tools_utils::file_type type =
|
|
abigail::tools_utils::guess_file_type(opts.in_file_path);
|
|
if (type != abigail::tools_utils::FILE_TYPE_ELF
|
|
&& type != abigail::tools_utils::FILE_TYPE_AR)
|
|
{
|
|
cerr << opts.in_file_path << " is not an ELF file\n";
|
|
return 1;
|
|
}
|
|
|
|
using abigail::corpus;
|
|
using abigail::corpus_sptr;
|
|
using abigail::translation_units;
|
|
using abigail::dwarf_reader::read_context;
|
|
using abigail::dwarf_reader::read_context_sptr;
|
|
using abigail::dwarf_reader::read_corpus_from_elf;
|
|
using abigail::dwarf_reader::create_read_context;
|
|
using namespace abigail;
|
|
|
|
char* p = opts.di_root_path.get();
|
|
corpus_sptr corp;
|
|
read_context_sptr c = create_read_context(opts.in_file_path, &p,
|
|
opts.load_all_types);
|
|
read_context& ctxt = *c;
|
|
|
|
if (opts.check_alt_debug_info_path)
|
|
{
|
|
bool has_alt_di = false;
|
|
string alt_di_path;
|
|
abigail::dwarf_reader::status status =
|
|
abigail::dwarf_reader::has_alt_debug_info(ctxt,
|
|
has_alt_di, alt_di_path);
|
|
if (status & abigail::dwarf_reader::STATUS_OK)
|
|
{
|
|
if (alt_di_path.empty())
|
|
;
|
|
else
|
|
{
|
|
if (opts.show_base_name_alt_debug_info_path)
|
|
tools_utils::base_name(alt_di_path, alt_di_path);
|
|
|
|
cout << "found the alternate debug info file '"
|
|
<< alt_di_path
|
|
<< "'\n";
|
|
}
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
cerr << "could not find alternate debug info file\n";
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
dwarf_reader::status s = read_corpus_from_elf(ctxt, corp);
|
|
if (!corp)
|
|
{
|
|
if (s == dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
|
|
{
|
|
if (p == 0)
|
|
{
|
|
cerr <<
|
|
"Could not read debug info from "
|
|
<< opts.in_file_path << "\n";
|
|
|
|
cerr << "You might want to supply the root directory where "
|
|
"to search debug info from, using the "
|
|
"--debug-info-dir option "
|
|
"(e.g --debug-info-dir /usr/lib/debug)\n";
|
|
}
|
|
else
|
|
{
|
|
cerr << "Could not read debug info for '" << opts.in_file_path
|
|
<< "' from debug info root directory '" << p
|
|
<< "'\n";
|
|
}
|
|
}
|
|
else if (s == dwarf_reader::STATUS_NO_SYMBOLS_FOUND)
|
|
cerr << "Could not read ELF symbol information from "
|
|
<< opts.in_file_path << "\n";
|
|
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if (!opts.write_architecture)
|
|
corp->set_architecture_name("");
|
|
abigail::xml_writer::write_corpus_to_native_xml(corp, 0, cout);
|
|
}
|
|
|
|
return 0;
|
|
}
|