libabigail/tools/abicompat.cc
Dodji Seketeli b2e5366d3f Introduce the concept of environment
There are resources needed by the type system and other artifacts of
libabigail.  Today, when the life time of those resources need to be
greater than all of artifacts of Abigail, then said resources are made
global.

But then global resources are not great, if anything because they
complicate the future use of the library in concurrent computing
setups.

As I was in the need to add one resource to be used by the type
system, I decided to sit down and first overhaul how these long lived
resources needed to be handled.

And here comes the concept of "environment".  An environment is a
place where one can put resources that need to live longer than all
the other artifacts of the Abigail system.  And so, the code that
creates Abigail artifacts needs and environment of for said artifacts
to use.  In other words, artifacts now use an environment.

This has interesting and strong implications.  We can only compare two
artifacts if they use the same environment.  This is quite a strong
requirement.

But then when this requirement is fulfilled, comparing two types
amounts to just comparing two pointer values; hash values for types
can also be cached.  Now *that* is great for speed of comparison, is
it not?

This patch introduce the concept environment (which is basically a new
abigail::ir::environment type), removes the global variables and uses
the environment instead.  Each ABI artifact (either type or decl) now
has a ::get_environment() member function to get its environment.

This patch also disables the caching of hash values because the
caching must happen only *after* all types have been canonicalized.
We were not respecting that requirement until now, and that introduces
wrong hash values.  A subsequent patch is going to re-introduce hash
value caching again, once the infrastructure is in place to set a flag
in the environment (hah!) once type canonicalization is done, and then
later read that flag when some client code requests a hash value, to
know if we should look in the hash value cache or not.

The patch obviously changes the output of numerous regression tests
(if anything b/c it disables hash value caching) so 'make check'
yields regressions.  But then, it's only the subsequent patch that
updates the tests.

	* include/abg-ir.h: Adjust note about memory management.
	(class environment): Declare new class.
	(translation_unit::translation_unit): Take an environment in
	parameter.
	(translation_unit::{g,s}et_environment): Declare new member
	functions.
	(type_or_decl_base::{g,s}et_environment): Likewise.
	(type_or_decl_base::{get_cached_hash_value,
	set_cached_hash_value}): Change the name of
	decl_base::peek_hash_value() and decl_base::set_hash() here into
	these and move them here.
	(type_or_decl_base::hashing_started): Move
	decl_base::hashing_started() here.
	({g,s}et_environment_for_artifact): Declare new functions.
	(class decl_base): Move member functions hashing_started(),
	peek_hash_value() and set_hash() on to the type_or_decl_base base
	class.
	(scope_decl::scope_decl): Initialize the virtual member
	type_or_decl_base().
	(type_decl::{get_void_type_decl,
	get_variadic_parameter_type_decl}): Remove these static member
	functions.  They are now non-static member functions of the new
	environment type.
	* src/abg-ir.cc (class environment_setter): New internal class.
	(get_canonical_types_map): Remove.  This now becomes a member
	function of the environment type.
	(class usage_watchdog): Remove.
	(usage_watchdog_{s,w}ptr): Remove these typedefs.
	(get_usage_watchdog_wptr, ref_usage_watchdog)
	(maybe_cleanup_type_system_data): Remove these functions.
	(translation_unit::priv::usage_watchdog_): Remove data member.
	(translation_unit::priv::env_): New data member.
	(translation_unit::priv::priv): Take an environment and initialize
	the new env_ data member.  Do not initialize the removed
	usage_watchdog_.
	(translation_unit::translation_unit): Take an environment
	parameter.
	(translation_unit::get_global_scope): Set the environment of a new
	global scope.
	(translation_unit::{g,s}et_environment): New accessors.
	(translation_unit::bind_function_type_life_time): Set the
	environment of the function type.
	(struct environment::priv): New class.
	(environment::{environment, ~environment, get_canonical_types_map,
	get_variadic_parameter_type_decl, canonicalization_is_done}): New
	member functions.
	(struct type_or_decl_base::priv): New class.
	(type_or_decl_base::{type_or_decl_base, hashing_started,
	get_cached_hash_value, set_cached_hash_value, set_environment,
	get_environment, traverse}): New member functions.
	({s,g}get_environment_for_artifact): New functions.
	(decl_base::priv::{hash_, hashing_started}): Remove.
	(decl_base::priv::priv): Adjust.
	(decl_base::decl_base): In the copy constructor, initialize the
	virtual base type_or_decl_base.  Do not initialize hash_ and
	hashing_started data member that got removed.
	(decl_base::{hashing_started, peek_hash_value, set_hash}): Remove
	member functions.
	(strip_typedef): Set the environment of the new type which has its
	typedefs stripped off.  Adjust the call to type_or_void().
	(scope_decl::{add, insert}_member_decl): Set the environment of
	the new member decl to the environment of its scope.
	(synthesize_type_from_translation_unit)
	(synthesize_function_type_from_translation_unit): Set the
	environment for the newly synthesized type. Adjust calls to
	type_or_void().
	(type_or_void): Take an environment in parameter.  Get the void
	type from the environment.
	(get_canonical_types_map): Remove.
	(type_base::get_canonical_type_for): Get the canonical types map
	from the environment, not from a global variable.
	(type_decl::{get_void_type_decl,
	get_variadic_parameter_type_decl}): Remove.
	(pointer_type_def::pointer_type_def): Adjust call to type_or_void.
	(reference_type_def::reference_type_def): Likewise.
	(function_decl::parameter::get_pretty_representation): Get the
	variadic parameter type decl from the environment.
	(class_decl::priv::classes_being_compared_): Remove static data
	member.
	(class_decl::priv::{mark_as_being_compared,
	unmark_as_being_compared, comparison_started): Use the "classes
	being compared" map from the environment.
	(class_decl::base_spec::get_hash): Adjust.
	(keep_type_alive): Get the alive types array from the environment)
	not from a global variable anymore.
	(get_next_string): Put the counter in thread-local storage.
	* src/abg-hash.cc (scope_decl:#️⃣:operator())
	(function_decl:#️⃣:operator()): Do not handle caching (here).
	* include/abg-corpus.h (corpus::{g,s}et_environment): Declare new
	accessors.
	* src/abg-corpus.cc (corpus::priv::env): New data member.
	(corpus::priv::priv): Initialize it.
	(corpus::corpus):  Take an environment in parameter.
	(corpus::{g,s}et_environment): Define new member functions
	(corpus::add): Set the environment of the newly added translation
	unit, if it's not set already set.  In any case, assert that the
	translation unit must use the same environment as the corpus.
	* include/abg-dwarf-reader.h (create_read_context)
	(read_corpus_from_elf): Take an environment parameter.
	({s,g}et_debug_info_root_path, {s,g}et_environment): Declare new
	functions.
	* src/abg-dwarf-reader.cc (read_context::{env_,
	offline_callbacks_}): New data members.
	(read_context::read_context): Initialize them.
	(read_context::clear_per_translation_unit_data): Do not touch the
	void type declaration, it doesn't belong to the translation unit.
	(read_context::{env, offline_callbacks}): New accessors.
	(read_context::{create_default_dwfl}): New member function.
	(read_context::dwfl_handle): Add a setter overload.
	({s,g}et_debug_info_root_path): Define new accessors.
	(create_default_dwfl, create_dwfl_sptr, create_default_dwfl_sptr):
	Remove these.
	(build_translation_unit_and_add_to_ir): Adjust to pass the
	environment to the newly created translation unit.
	(build_function_decl): Adjust to pass the environment to the
	created function and parameter types.  Get variadic parameter type
	node from the current environment, not from a global variable.
	And do not try to canonicalize function types here.
	(read_debug_info_into_corpus): Set the environment of the newly
	created corpus.
	(build_ir_node_for_void_type): Get the void type node from the
	current environment, rather than from a global variable.
	(create_read_context): Take the environment in parameter.
	Create the default dwarf front end library handle using the new
	member function of the read context.  Set the current environment
	used by the reader.
	(read_corpus_from_elf): Take an environment in
	parameter. Overhaul.  This is now simpler.
	(has_alt_debug_info): Adjust the call to create_read_context() to
	make it pass an empty environment.
	* include/abg-fwd.h (class environment): Forward declare.
	* include/abg-reader.h (read_translation_unit_from_file)
	(read_translation_unit_from_buffer)
	(read_translation_unit_from_istream)
	(read_corpus_from_native_xml): Take an environment in parameter.
	* src/abg-reader.cc (read_context::m_env): New data member.
	(read_context::read_context): Initialize it.
	(read_context::{get_environment, set_environment}): New data
	member.
	(read_translation_unit): Set environment of the new translation
	unit.
	(read_corpus_from_input): Set the environment of the new corpus.
	(read_translation_unit_from_file)
	(read_translation_unit_from_buffer)
	(read_translation_unit_from_istream, read_corpus_from_native_xml):
	Take an environment in parameter.
	(build_function_parameter): Get variadic parameter type from the environment.
	* src/abg-comparison.cc (compute_diff): Add asserts in all the
	overloads to ensure that the artifact being compared come from the
	same environment.
	* tests/print-diff-tree.cc (main): Create an env for the ABI
	artifacts to use.
	* tests/test-abidiff.cc (main): Likewise.
	* tests/test-diff-dwarf.cc (main): Likewise.
	* tests/test-ir-walker.cc (main): Likewise.
	* tests/test-read-dwarf.cc (main): Likewise.
	* tests/test-read-write.cc (main): Likewise.
	* tools/abicompat.cc (main): Likewise.
	* tools/abidiff.cc (main): Likewise.
	* tools/abidw.cc (main): Likewise.
	* tools/abilint.cc (main): Likewise.
	* tools/abipkgdiff.cc (main): Likewise.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
2015-09-07 23:35:29 +02:00

715 lines
22 KiB
C++

// -*- Mode: C++ -*-
//
// Copyright (C) 2014-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 a program A, one library L in version V which A
/// links against, and the same library L in a different version, V+P.
/// The program then checks that A is still ABI compatible with L in
/// version V+P.
///
/// The program also comes with a "weak mode" in which just the
/// application and the library in version V+P need to be provided by
/// the user. In that case, 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. If they match exactly, then the types of functions
/// and variables that the application expects from the library are
/// honoured by the library. Otherwise, the library might provide
/// functions and variables that mean something different from what
/// the application expects and that might signal an ABI
/// incompatibility between what the application expects and what the
/// library provides.
#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-comparison.h"
using std::string;
using std::cerr;
using std::cout;
using std::ostream;
using std::ofstream;
using std::vector;
using std::tr1::shared_ptr;
struct options
{
string unknow_option;
string app_path;
string lib1_path;
string lib2_path;
shared_ptr<char> app_di_root_path;
shared_ptr<char> lib1_di_root_path;
shared_ptr<char> lib2_di_root_path;
vector<string> suppression_paths;
bool display_help;
bool weak_mode;
bool list_undefined_symbols_only;
bool show_base_names;
bool show_redundant;
options()
:display_help(),
weak_mode(),
list_undefined_symbols_only(),
show_base_names(),
show_redundant(true)
{}
}; // end struct options
static void
display_usage(const string& prog_name, ostream& out)
{
out << "usage: " << prog_name
<< " [options] [application-path] [lib-v1-path] [lib-v2-path]"
<< "\n"
<< " where options can be: \n"
<< " --help|-h display this help message\n"
<< " --list-undefined-symbols|-u display the list of "
"undefined symbols of the application\n"
<< " --show-base-names|b in the report, only show the base names "
" of the files; not the full paths\n"
<< " --app-debug-info-dir <path-to-app-debug-info> set the path "
"to the debug information directory for the application\n"
<< " --lib-debug-info-dir1 <path-to-lib-debug-info1> set the path "
"to the debug information directory for the first library\n"
<< " --lib-debug-info-dir2 <path-to-lib-debug-info2> set the path "
"to the debug information directory for the second library\n"
<< "--suppressions <path> specify a suppression file\n"
<< "--no-redundant do not display redundant changes\n"
<< "--redundant display redundant changes (this is the default)\n"
<< "--weak-mode check compatibility between the application and "
"just one version of the library."
;
}
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.app_path.empty())
opts.app_path = argv[i];
else if (opts.lib1_path.empty())
opts.lib1_path = argv[i];
else if (opts.lib2_path.empty())
opts.lib2_path = argv[i];
else
return false;
}
else if (!strcmp(argv[i], "--list-undefined-symbols")
|| !strcmp(argv[i], "-u"))
opts.list_undefined_symbols_only = true;
else if (!strcmp(argv[i], "--show-base-names")
|| !strcmp(argv[i], "-b"))
opts.show_base_names = true;
else if (!strcmp(argv[i], "--app-debug-info-dir"))
{
if (argc <= i + 1
|| argv[i + 1][0] == '-')
return false;
// elfutils wants the root path to the debug info to be
// absolute.
opts.app_di_root_path =
abigail::tools_utils::make_path_absolute(argv[i + 1]);
++i;
}
else if (!strcmp(argv[i], "--lib-debug-info-dir1"))
{
if (argc <= i + 1
|| argv[i + 1][0] == '-')
return false;
// elfutils wants the root path to the debug info to be
// absolute.
opts.lib1_di_root_path =
abigail::tools_utils::make_path_absolute(argv[i + 1]);
++i;
}
else if (!strcmp(argv[i], "--lib-debug-info-dir2"))
{
if (argc <= i + 1
|| argv[i + 1][0] == '-')
return false;
// elfutils wants the root path to the debug info to be
// absolute.
opts.lib2_di_root_path =
abigail::tools_utils::make_path_absolute(argv[i + 1]);
++i;
}
else if (!strcmp(argv[i], "--suppressions"))
{
int j = i + 1;
if (j >= argc)
return false;
opts.suppression_paths.push_back(argv[j]);
++i;
}
else if (!strcmp(argv[i], "--redundant"))
opts.show_redundant = true;
else if (!strcmp(argv[i], "--no-redundant"))
opts.show_redundant = false;
else if (!strcmp(argv[i], "--help")
|| !strcmp(argv[i], "-h"))
{
opts.display_help = true;
return true;
}
else if (!strcmp(argv[i], "--weak-mode"))
opts.weak_mode = true;
else
{
opts.unknow_option = argv[i];
return false;
}
}
if (!opts.list_undefined_symbols_only)
{
if (opts.app_path.empty()
|| opts.lib1_path.empty())
return false;
if (!opts.weak_mode && opts.lib2_path.empty())
opts.weak_mode = true;
}
return true;
}
using abigail::tools_utils::check_file;
using abigail::tools_utils::base_name;
using abigail::tools_utils::abidiff_status;
using abigail::ir::environment;
using abigail::ir::environment_sptr;
using abigail::corpus;
using abigail::corpus_sptr;
using abigail::ir::elf_symbols;
using abigail::ir::demangle_cplus_mangled_name;
using abigail::ir::type_base_sptr;
using abigail::ir::function_type_sptr;
using abigail::ir::function_decl;
using abigail::ir::var_decl;
using abigail::dwarf_reader::status;
using abigail::dwarf_reader::read_corpus_from_elf;
using abigail::comparison::diff_context_sptr;
using abigail::comparison::diff_context;
using abigail::comparison::diff_sptr;
using abigail::comparison::corpus_diff;
using abigail::comparison::corpus_diff_sptr;
using abigail::comparison::function_type_diff_sptr;
using abigail::comparison::compute_diff;
using abigail::comparison::suppression_sptr;
using abigail::comparison::suppressions_type;
using abigail::comparison::read_suppressions;
/// Perform a compatibility check of an application corpus linked
/// against a first version of library corpus, with a second version
/// of the same library.
///
/// @param opts the options the tool got invoked with.
///
/// @param app_corpus the application corpus to consider.
///
/// @param lib1_corpus the library corpus that got linked with the
/// application which corpus is @p app_corpus.
///
/// @param lib2_corpus the second version of the library corpus @p
/// lib1_corpus. This function checks that the functions and
/// variables that @p app_corpus expects from lib1_corpus are still
/// present in @p lib2_corpus and that their types mean the same
/// thing.
///
/// @return a status bitfield.
static abidiff_status
perform_compat_check_in_normal_mode(options& opts,
corpus_sptr app_corpus,
corpus_sptr lib1_corpus,
corpus_sptr lib2_corpus)
{
assert(lib1_corpus);
assert(lib2_corpus);
assert(app_corpus);
abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
// compare lib1 and lib2 only by looking at the functions and
// variables which symbols are those undefined in the app.
for (elf_symbols::const_iterator i =
app_corpus->get_sorted_undefined_fun_symbols().begin();
i != app_corpus->get_sorted_undefined_fun_symbols().end();
++i)
{
string id = (*i)->get_id_string();
lib1_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
lib2_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
}
for (elf_symbols::const_iterator i =
app_corpus->get_sorted_undefined_var_symbols().begin();
i != app_corpus->get_sorted_undefined_var_symbols().end();
++i)
{
string id = (*i)->get_id_string();
lib1_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
lib2_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
}
if (!app_corpus->get_sorted_undefined_var_symbols().empty()
|| !app_corpus->get_sorted_undefined_fun_symbols().empty())
{
lib1_corpus->maybe_drop_some_exported_decls();
lib2_corpus->maybe_drop_some_exported_decls();
}
diff_context_sptr ctxt(new diff_context());
ctxt->show_added_fns(false);
ctxt->show_added_vars(false);
ctxt->show_added_symbols_unreferenced_by_debug_info(false);
ctxt->show_linkage_names(true);
ctxt->show_redundant_changes(opts.show_redundant);
ctxt->switch_categories_off
(abigail::comparison::ACCESS_CHANGE_CATEGORY
| abigail::comparison::COMPATIBLE_TYPE_CHANGE_CATEGORY
| abigail::comparison::HARMLESS_DECL_NAME_CHANGE_CATEGORY
| abigail::comparison::NON_VIRT_MEM_FUN_CHANGE_CATEGORY
| abigail::comparison::STATIC_DATA_MEMBER_CHANGE_CATEGORY
| abigail::comparison::HARMLESS_ENUM_CHANGE_CATEGORY
| abigail::comparison::HARMLESS_SYMBOL_ALIAS_CHANGE_CATEORY);
// load the suppression specifications
// before starting to diff the libraries.
suppressions_type supprs;
for (vector<string>::const_iterator i = opts.suppression_paths.begin();
i != opts.suppression_paths.end();
++i)
if (check_file(*i, cerr))
read_suppressions(*i, supprs);
if (!supprs.empty())
ctxt->add_suppressions(supprs);
// Now really do the diffing.
corpus_diff_sptr changes = compute_diff(lib1_corpus, lib2_corpus, ctxt);
const corpus_diff::diff_stats& s =
changes->apply_filters_and_suppressions_before_reporting();
if (changes->soname_changed()
|| s.num_func_removed() != 0
|| s.num_vars_removed() != 0
|| s.num_func_syms_removed() != 0
|| s.num_var_syms_removed() != 0
|| s.net_num_func_changed() != 0
|| s.net_num_vars_changed() != 0)
{
string app_path = opts.app_path,
lib1_path = opts.lib1_path,
lib2_path = opts.lib2_path;
if (opts.show_base_names)
{
base_name(opts.app_path, app_path);
base_name(opts.lib1_path, lib1_path);
base_name(opts.lib2_path, lib2_path);
}
status |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
bool abi_broke_for_sure = changes->soname_changed()
|| s.num_vars_removed()
|| s.num_func_removed()
|| s.num_var_syms_removed()
|| s.num_func_syms_removed();
cout << "ELF file '" << app_path << "'";
if (abi_broke_for_sure)
{
cout << " is not ";
status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
}
else
cout << " might not be ";
cout << "ABI compatible with '" << lib2_path
<< "' due to differences with '" << lib1_path
<< "' below:\n";
changes->report(cout);
}
return status;
}
/// An description of a change of the type of a function. It contains
/// the declaration of the function we are interested in, as well as
/// the differences found in the type of that function.
struct fn_change
{
function_decl* decl;
function_type_diff_sptr diff;
fn_change()
: decl()
{}
fn_change(function_decl* decl,
function_type_diff_sptr difference)
: decl(decl),
diff(difference)
{}
}; // end struct fn_change
/// An description of a change of the type of a variable. It contains
/// the declaration of the variable we are interested in, as well as
/// the differences found in the type of that variable.
struct var_change
{
var_decl* decl;
diff_sptr diff;
var_change()
: decl()
{}
var_change(var_decl* var,
diff_sptr difference)
: decl(var),
diff(difference)
{}
}; // end struct var_change
/// Perform a compatibility check of an application corpus and a
/// library corpus.
///
/// @param opts the options the tool got invoked with.
///
/// @param app_corpus the application corpus to consider.
///
/// @param lib_corpus the library corpus to consider. The types of
/// the variables and functions exported by this library and consumed
/// by the application are compared with the types expected by the
/// application @p app_corpus. This function checks that the types
/// mean the same thing; otherwise it emits on standard output type
/// layout differences found.
///
/// @return a status bitfield.
static abidiff_status
perform_compat_check_in_weak_mode(options& opts,
corpus_sptr app_corpus,
corpus_sptr lib_corpus)
{
assert(lib_corpus);
assert(app_corpus);
abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
for (elf_symbols::const_iterator i =
app_corpus->get_sorted_undefined_fun_symbols().begin();
i != app_corpus->get_sorted_undefined_fun_symbols().end();
++i)
{
string id = (*i)->get_id_string();
lib_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
}
for (elf_symbols::const_iterator i =
app_corpus->get_sorted_undefined_var_symbols().begin();
i != app_corpus->get_sorted_undefined_var_symbols().end();
++i)
{
string id = (*i)->get_id_string();
lib_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
}
if (!app_corpus->get_sorted_undefined_var_symbols().empty()
|| !app_corpus->get_sorted_undefined_fun_symbols().empty())
lib_corpus->maybe_drop_some_exported_decls();
diff_context_sptr ctxt(new diff_context());
ctxt->show_added_fns(false);
ctxt->show_added_vars(false);
ctxt->show_added_symbols_unreferenced_by_debug_info(false);
ctxt->show_linkage_names(true);
ctxt->show_redundant_changes(opts.show_redundant);
ctxt->switch_categories_off
(abigail::comparison::ACCESS_CHANGE_CATEGORY
| abigail::comparison::COMPATIBLE_TYPE_CHANGE_CATEGORY
| abigail::comparison::HARMLESS_DECL_NAME_CHANGE_CATEGORY
| abigail::comparison::NON_VIRT_MEM_FUN_CHANGE_CATEGORY
| abigail::comparison::STATIC_DATA_MEMBER_CHANGE_CATEGORY
| abigail::comparison::HARMLESS_ENUM_CHANGE_CATEGORY
| abigail::comparison::HARMLESS_SYMBOL_ALIAS_CHANGE_CATEORY);
suppressions_type supprs;
for (vector<string>::const_iterator i = opts.suppression_paths.begin();
i != opts.suppression_paths.end();
++i)
if (check_file(*i, cerr))
read_suppressions(*i, supprs);
if (!supprs.empty())
ctxt->add_suppressions(supprs);
{
function_type_sptr lib_fn_type, app_fn_type;
function_type_diff_sptr fn_type_diff;
vector<fn_change> fn_changes;
for (corpus::functions::const_iterator i =
lib_corpus->get_functions().begin();
i != lib_corpus->get_functions().end();
++i)
{
lib_fn_type = (*i)->get_type();
assert(lib_fn_type);
app_fn_type = lookup_function_type_in_corpus(lib_fn_type, *app_corpus);
fn_type_diff = compute_diff(app_fn_type, lib_fn_type, ctxt);
if (fn_type_diff && fn_type_diff->to_be_reported())
fn_changes.push_back(fn_change(*i, fn_type_diff));
}
string lib1_path = opts.lib1_path, app_path = opts.app_path;
if (opts.show_base_names)
{
base_name(opts.lib1_path, lib1_path);
base_name(opts.app_path, app_path);
}
if (!fn_changes.empty())
{
cout << "functions defined in library "
<< "'" << lib1_path << "'\n"
<< "have sub-types that are different from what application "
<< "'" << app_path << "' "
<< "expects:\n\n";
for (vector<fn_change>::const_iterator i = fn_changes.begin();
i != fn_changes.end();
++i)
{
cout << " "
<< i->decl->get_pretty_representation()
<< ":\n";
i->diff->report(cout, " ");
cout << "\n";
}
}
if (!fn_changes.empty())
status |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
type_base_sptr lib_var_type, app_var_type;
diff_sptr type_diff;
vector<var_change> var_changes;
for (corpus::variables::const_iterator i =
lib_corpus->get_variables().begin();
i != lib_corpus->get_variables().end();
++i)
{
lib_var_type = (*i)->get_type();
assert(lib_var_type);
app_var_type = lookup_type_in_corpus(lib_var_type, *app_corpus);
type_diff = compute_diff(app_var_type, lib_var_type, ctxt);
if (type_diff && type_diff->to_be_reported())
var_changes.push_back(var_change(*i, type_diff));
}
if (!var_changes.empty())
{
cout << "variables defined in library "
<< "'" << lib1_path << "'\n"
<< "have sub-types that are different from what application "
<< "'" << app_path << "' "
<< "expects:\n\n";
for (vector<var_change>::const_iterator i = var_changes.begin();
i != var_changes.end();
++i)
{
cout << " "
<< i->decl->get_pretty_representation()
<< ":\n";
i->diff->report(cout, " ");
cout << "\n";
}
}
}
return status;
}
int
main(int argc, char* argv[])
{
options opts;
if (!parse_command_line(argc, argv, opts))
{
if (!opts.unknow_option.empty())
{
cerr << "unrecognized option: " << opts.unknow_option << "\n"
<< "try the --help option for more information\n";
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
}
cerr << "wrong invocation\n"
<< "try the --help option for more information\n";
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
}
if (opts.display_help)
{
display_usage(argv[0], cout);
return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
| abigail::tools_utils::ABIDIFF_ERROR);
}
assert(!opts.app_path.empty());
if (!abigail::tools_utils::check_file(opts.app_path, cerr))
return abigail::tools_utils::ABIDIFF_ERROR;
abigail::tools_utils::file_type type =
abigail::tools_utils::guess_file_type(opts.app_path);
if (type != abigail::tools_utils::FILE_TYPE_ELF)
{
cerr << opts.app_path << " is not an ELF file\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
// Read the application ELF file.
char * app_di_root = opts.app_di_root_path.get();
status status = abigail::dwarf_reader::STATUS_UNKNOWN;
environment_sptr env(new environment);
corpus_sptr app_corpus=
read_corpus_from_elf(opts.app_path,
&app_di_root, env.get(),
/*load_all_types=*/opts.weak_mode,
status);
if (status & abigail::dwarf_reader::STATUS_NO_SYMBOLS_FOUND)
{
cerr << "could not read symbols from " << opts.app_path << "\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
if (!(status & abigail::dwarf_reader::STATUS_OK))
{
cerr << "could not read file " << opts.app_path << "\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
if (opts.list_undefined_symbols_only)
{
for (elf_symbols::const_iterator i =
app_corpus->get_sorted_undefined_fun_symbols().begin();
i != app_corpus->get_sorted_undefined_fun_symbols().end();
++i)
{
string id = (*i)->get_id_string();
string sym_name = (*i)->get_name();
string demangled_name = demangle_cplus_mangled_name(sym_name);
if (demangled_name != sym_name)
cout << demangled_name << " {" << id << "}\n";
else
cout << id << "\n";
}
return abigail::tools_utils::ABIDIFF_OK;
}
// Read the first version of the library.
assert(!opts.lib1_path.empty());
if (!abigail::tools_utils::check_file(opts.lib1_path, cerr))
return abigail::tools_utils::ABIDIFF_ERROR;
type = abigail::tools_utils::guess_file_type(opts.lib1_path);
if (type != abigail::tools_utils::FILE_TYPE_ELF)
{
cerr << opts.lib1_path << " is not an ELF file\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
char * lib1_di_root = opts.lib1_di_root_path.get();
corpus_sptr lib1_corpus = read_corpus_from_elf(opts.lib1_path,
&lib1_di_root, env.get(),
/*load_all_types=*/false,
status);
if (status & abigail::dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
cerr << "could not read debug info for " << opts.lib1_path << "\n";
if (status & abigail::dwarf_reader::STATUS_NO_SYMBOLS_FOUND)
{
cerr << "could not read symbols from " << opts.lib1_path << "\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
if (!(status & abigail::dwarf_reader::STATUS_OK))
{
cerr << "could not read file " << opts.lib1_path << "\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
// Read the second version of the library.
corpus_sptr lib2_corpus;
if (!opts.weak_mode)
{
assert(!opts.lib2_path.empty());
char * lib2_di_root = opts.lib2_di_root_path.get();
lib2_corpus = read_corpus_from_elf(opts.lib2_path,
&lib2_di_root,
env.get(),
/*load_all_types=*/false,
status);
if (status & abigail::dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
cerr << "could not read debug info for " << opts.lib2_path << "\n";
if (status & abigail::dwarf_reader::STATUS_NO_SYMBOLS_FOUND)
{
cerr << "could not read symbols from " << opts.lib2_path << "\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
if (!(status & abigail::dwarf_reader::STATUS_OK))
{
cerr << "could not read file " << opts.lib2_path << "\n";
return abigail::tools_utils::ABIDIFF_ERROR;
}
}
abidiff_status s = abigail::tools_utils::ABIDIFF_OK;
if (opts.weak_mode)
s = perform_compat_check_in_weak_mode(opts,
app_corpus,
lib1_corpus);
else
s = perform_compat_check_in_normal_mode(opts,
app_corpus,
lib1_corpus,
lib2_corpus);
return s;
}