Initial implementation of the abicompat tool
Given an application A that links to a shared library L of version V
denoted L(V) and a subsequent version of that library denoted L(V+P),
the 'abicompat' tool tells the user if L(V+P) is still ABI compatible
with L(V+P). And if it is not, abicompat gives a reports that shows
the differences between L(V) and L(V+P) that makes L(V+P)
ABI-incompatible with A.
The source code of this tool is in the tools/abicompat.cc source
file. To support this new tool, this commit changes the comparison
engine to optionally avoid showing added symbols that were not
referenced by any debug info. It changes the ABI corpus type to allow
the specification of a list of variables and functions symbols to keep
(and drop all other functions and variables which have other symbols
on the floor even before starting to compare the two libraries).
This is how the abicompat tool itself works. It basically compares
L(V) and L(V+P) but it only looks at their exported functions and
variables which symbols are undefined in application A. If the list
of exported and defined variables and functions of L(V) whose symbols
are undefined in A equals that of L(V+P) (including the sub-types of
these variables and functions) A is still compatible with L(V+P).
Otherwise, they might not be compatible depending on the kind of
differences that are found.
* include/abg-comparison.h
(diff_context::show_added_symbols_unreferenced_by_debug_info):
Declare new accessors.
(corpus_diff::{deleted_variables,
deleted_unrefed_function_symbols,
deleted_unrefed_variable_symbols,
apply_filters_and_suppressions_before_reporting}): Declare new
methods.
(corpus_diff::diff_stats): Declare this new type. Actually this
was previously corpus_diff::priv::diff_stats, which was a hidden
internal type.. We are moving it here, in the external API so
that client code can have more information about changes
statistics. Change all the previously publicly accessible data
members into accessor functions.
* src/abg-comparison.cc (class corpus_diff::diff_stats::priv): New
type.
(diff_context::priv::show_added_syms_unreferenced_by_di_): New
data member.
(diff_context::priv::priv): Adjust.
(diff_context::show_added_symbols_unreferenced_by_debug_info):
Define this new method.
(corpus_diff::priv::emit_diff_stats): Do not show the diff stat
if the only changes is added function or variables symbols and if
we were instructed to not show added symbols.
(corpus_diff::priv::{diff_stats_, filters_and_suppr_applied_}):
New data members.
(corpus_diff::priv::priv): Initialize the
filters_and_suppr_applied_ data member.
(corpus_diff::priv::diff_stats): Move this type to
corpus_diff::diff_stats.
(corpus_diff::priv::{apply_filters_and_compute_diff_stats,
emit_diff_stats}): Adjust.
(corpus_diff::apply_filters_and_suppressions_before_reporting):
Define new member function.
(corpus_diff::report): Use the new
apply_filters_and_suppressions_before_reporting() function, rather
than applying the filters and suppressions by ourselves. Also
adjust to the use the accessors of the new corpus_diff::diff_stats
type.
(corpus_diff::{deleted_variables,
deleted_unrefed_function_symbols,
deleted_unrefed_variable_symbols}): Define new accessors.
(corpus_diff::diff_stats::{diff_stats, num_func_removed,
num_func_added, num_func_changed, num_func_filtered_out,
net_num_func_changed, num_vars_removed, num_vars_added,
num_vars_changed, num_vars_filtered_out, net_num_vars_changed,
num_func_sym_removed, num_func_syms_added, num_var_syms_removed,
num_var_syms_added}): Define new member functions.
* include/abg-corpus.h (corpus::{get_sym_ids_of_fns_to_keep,
get_sym_ids_of_vars_to_keep}): Declare new methods.
* src/abg-corpus.cc (corpus::priv::{sym_id_fns_to_keep,
sym_id_vars_to_keep}): Added data members.
(symtab_build_visitor_type::{unrefed_fun_symbols,
unrefed_var_symbols, sym_id_fns_to_keep, sym_id_vars_to_keep}):
Added new data members.
(symtab_build_visitor_type::symtab_build_visitor_type): Take two
additional parameters for the function and variable symbol ids to
keep.
(symtab_build_visitor_type::add_fn_to_wip_fns): Take the function
symbols to keep in account when building the exported symbol
table.
(symtab_build_visitor_type::add_var_to_wip_vars): Likewise, take
the variable symbols to keep in account when building the exported
symbol table.
(corpus::priv::build_public_decl_table): Adjust the initialization
of the visitor that walks the ABI artifacts to build the exported
symbol table to know take a list of function/variable symbols to
keep.
(corpus::priv::build_unreferenced_symbols_tables): Ensure that the
public table of functions/variables is built before doing the work
of this function. Also, if a list of variable/function symbols to
keep is given, drop all symbols that are not in that list on the
floor.
(corpus::{get_sym_ids_of_fns_to_keep,
get_sym_ids_of_vars_to_keep}): Define new accessors.
* tools/abicompat.cc: New abicompat tool.
* doc/manuals/abicompat.rst: New documentation source for
abicompat.
* doc/manuals/libabigail-tools.rst: Add an entry for the abicompat
doc.
* tests/test-abicompat.cc: New test harness for the 'abicompat'
tool.
* tests/Makefile.am: Build the runtestabicompat test harness and
add it to the list of tests harnesses that are run by make check.
* tests/data/test-abicompat/libtest0-fn-changed-libapp-v0.so: New
test input.
* tests/data/test-abicompat/libtest0-fn-changed-libapp-v1.so: Likewise.
* tests/data/test-abicompat/test0-fn-changed-app: Likewise.
* tests/data/test-abicompat/test0-fn-changed-0.suppr: Likewise
* tests/data/test-abicompat/test0-fn-changed-report-0.txt: Likewise.
* tests/data/test-abicompat/test0-fn-changed-report-1.txt: Likewise.
* tests/data/test-abicompat/test0-fn-changed-app.cc: Likewise.
* tests/data/test-abicompat/test0-fn-changed-libapp.h: Likewise.
* tests/data/test-abicompat/test0-fn-changed-libapp-v0.cc: Likewise.
* tests/data/test-abicompat/test0-fn-changed-libapp-v1.cc: Likewise.
* tests/data/test-abicompat/libtest1-fn-removed-v0.so: Likewise.
* tests/data/test-abicompat/libtest1-fn-removed-v1.so: Likewise.
* tests/data/test-abicompat/test1-fn-removed-app: Likewise.
* tests/data/test-abicompat/test1-fn-removed-app.cc: Likewise.
* tests/data/test-abicompat/test1-fn-removed-report-0.txt: Likewise.
* tests/data/test-abicompat/test1-fn-removed-v0.cc: Likewise.
* tests/data/test-abicompat/test1-fn-removed-v1.cc: Likewise.
* tests/data/test-abicompat/libtest2-var-removed-v0.so: Likewise.
* tests/data/test-abicompat/libtest2-var-removed-v1.so: Likewise.
* tests/data/test-abicompat/test2-var-removed-app: Likewise.
* tests/data/test-abicompat/test2-var-removed-app.cc: Likewise.
* tests/data/test-abicompat/test2-var-removed-report-0.txt: Likewise.
* tests/data/test-abicompat/test2-var-removed-v0.cc: Likewise.
* tests/data/test-abicompat/test2-var-removed-v1.cc: Likewise.
* tests/data/test-abicompat/libtest3-fn-removed-v0.so: Likewise.
* tests/data/test-abicompat/libtest3-fn-removed-v1.so: Likewise.
* tests/data/test-abicompat/test3-fn-removed-app: Likewise.
* tests/data/test-abicompat/test3-fn-removed-app.cc: Likewise.
* tests/data/test-abicompat/test3-fn-removed-report-0.txt: Likewise.
* tests/data/test-abicompat/test3-fn-removed-v0.cc: Likewise.
* tests/data/test-abicompat/test3-fn-removed-v1.cc: Likewise.
* tests/data/test-abicompat/test3-fn-removed-version-script-0 Likewise.:
* tests/data/test-abicompat/test3-fn-removed-version-script-1: Likewise.
* tests/data/Makefile.am: Add the new test inputs above to the
source distribution.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
2014-11-30 17:18:55 +00:00
|
|
|
// -*- Mode: C++ -*-
|
|
|
|
//
|
|
|
|
// Copyright (C) 2014 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 the A is still ABI compatible with
|
|
|
|
/// L in version V+P.
|
|
|
|
|
|
|
|
#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 list_undefined_symbols_only;
|
|
|
|
bool show_base_names;
|
|
|
|
|
|
|
|
options()
|
|
|
|
:display_help(),
|
|
|
|
list_undefined_symbols_only(),
|
|
|
|
show_base_names()
|
|
|
|
{}
|
|
|
|
}; // end struct options
|
|
|
|
|
|
|
|
static void
|
|
|
|
display_usage(const string& prog_name, ostream& out)
|
|
|
|
{
|
|
|
|
out << "usage: " << prog_name
|
|
|
|
<< " [options] [application-path] [path-lib-version-1 path-lib-version-2]"
|
|
|
|
<< "\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"
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
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::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::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::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], "--help")
|
|
|
|
|| !strcmp(argv[i], "-h"))
|
|
|
|
{
|
|
|
|
opts.display_help = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
opts.unknow_option = argv[i];
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!opts.list_undefined_symbols_only)
|
|
|
|
{
|
|
|
|
if (opts.app_path.empty()
|
|
|
|
|| opts.lib1_path.empty()
|
|
|
|
|| opts.lib2_path.empty())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 false;
|
|
|
|
}
|
|
|
|
|
|
|
|
cerr << "wrong invocation\n"
|
|
|
|
<< "try the --help option for more information\n";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.display_help)
|
|
|
|
{
|
|
|
|
display_usage(argv[0], cout);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(!opts.app_path.empty());
|
|
|
|
if (!abigail::tools::check_file(opts.app_path, cerr))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
abigail::tools::file_type type =
|
|
|
|
abigail::tools::guess_file_type(opts.app_path);
|
|
|
|
if (type != abigail::tools::FILE_TYPE_ELF)
|
|
|
|
{
|
|
|
|
cerr << opts.app_path << " is not an ELF file\n";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
using abigail::tools::check_file;
|
|
|
|
using abigail::tools::dirname;
|
|
|
|
using abigail::tools::base_name;
|
|
|
|
using abigail::corpus;
|
|
|
|
using abigail::corpus_sptr;
|
|
|
|
using abigail::ir::elf_symbols;
|
|
|
|
using abigail::ir::demangle_cplus_mangled_name;
|
|
|
|
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::corpus_diff;
|
|
|
|
using abigail::comparison::corpus_diff_sptr;
|
|
|
|
using abigail::comparison::compute_diff;
|
|
|
|
using abigail::comparison::suppression_sptr;
|
|
|
|
using abigail::comparison::suppressions_type;
|
|
|
|
using abigail::comparison::read_suppressions;
|
|
|
|
|
|
|
|
// Read the application ELF file.
|
|
|
|
corpus_sptr app_corpus;
|
|
|
|
char * app_di_root = opts.app_di_root_path.get();
|
|
|
|
status status = read_corpus_from_elf(opts.app_path,
|
|
|
|
&app_di_root,
|
|
|
|
app_corpus);
|
|
|
|
|
|
|
|
if (status & abigail::dwarf_reader::STATUS_NO_SYMBOLS_FOUND)
|
|
|
|
{
|
|
|
|
cerr << "could not read symbols from " << opts.app_path << "\n";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (!(status & abigail::dwarf_reader::STATUS_OK))
|
|
|
|
{
|
|
|
|
cerr << "could not read file " << opts.app_path << "\n";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the first version of the library.
|
|
|
|
assert(!opts.lib1_path.empty());
|
|
|
|
if (!abigail::tools::check_file(opts.lib1_path, cerr))
|
|
|
|
return 1;
|
|
|
|
type = abigail::tools::guess_file_type(opts.lib1_path);
|
|
|
|
if (type != abigail::tools::FILE_TYPE_ELF)
|
|
|
|
{
|
|
|
|
cerr << opts.lib1_path << " is not an ELF file\n";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
corpus_sptr lib1_corpus;
|
|
|
|
char * lib1_di_root = opts.lib1_di_root_path.get();
|
|
|
|
status = read_corpus_from_elf(opts.lib1_path,
|
|
|
|
&lib1_di_root,
|
|
|
|
lib1_corpus);
|
|
|
|
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 1;
|
|
|
|
}
|
|
|
|
if (!(status & abigail::dwarf_reader::STATUS_OK))
|
|
|
|
{
|
|
|
|
cerr << "could not read file " << opts.lib1_path << "\n";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the second version of the library.
|
|
|
|
assert(!opts.lib2_path.empty());
|
|
|
|
corpus_sptr lib2_corpus;
|
|
|
|
char * lib2_di_root = opts.lib2_di_root_path.get();
|
|
|
|
status = read_corpus_from_elf(opts.lib2_path,
|
|
|
|
&lib2_di_root,
|
|
|
|
lib2_corpus);
|
|
|
|
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 1;
|
|
|
|
}
|
|
|
|
if (!(status & abigail::dwarf_reader::STATUS_OK))
|
|
|
|
{
|
|
|
|
cerr << "could not read file " << opts.lib2_path << "\n";
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(lib1_corpus);
|
|
|
|
assert(lib2_corpus);
|
|
|
|
assert(app_corpus);
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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->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();
|
|
|
|
|
2014-12-08 12:36:01 +00:00
|
|
|
if (changes->soname_changed()
|
|
|
|
|| s.num_func_removed() != 0
|
Initial implementation of the abicompat tool
Given an application A that links to a shared library L of version V
denoted L(V) and a subsequent version of that library denoted L(V+P),
the 'abicompat' tool tells the user if L(V+P) is still ABI compatible
with L(V+P). And if it is not, abicompat gives a reports that shows
the differences between L(V) and L(V+P) that makes L(V+P)
ABI-incompatible with A.
The source code of this tool is in the tools/abicompat.cc source
file. To support this new tool, this commit changes the comparison
engine to optionally avoid showing added symbols that were not
referenced by any debug info. It changes the ABI corpus type to allow
the specification of a list of variables and functions symbols to keep
(and drop all other functions and variables which have other symbols
on the floor even before starting to compare the two libraries).
This is how the abicompat tool itself works. It basically compares
L(V) and L(V+P) but it only looks at their exported functions and
variables which symbols are undefined in application A. If the list
of exported and defined variables and functions of L(V) whose symbols
are undefined in A equals that of L(V+P) (including the sub-types of
these variables and functions) A is still compatible with L(V+P).
Otherwise, they might not be compatible depending on the kind of
differences that are found.
* include/abg-comparison.h
(diff_context::show_added_symbols_unreferenced_by_debug_info):
Declare new accessors.
(corpus_diff::{deleted_variables,
deleted_unrefed_function_symbols,
deleted_unrefed_variable_symbols,
apply_filters_and_suppressions_before_reporting}): Declare new
methods.
(corpus_diff::diff_stats): Declare this new type. Actually this
was previously corpus_diff::priv::diff_stats, which was a hidden
internal type.. We are moving it here, in the external API so
that client code can have more information about changes
statistics. Change all the previously publicly accessible data
members into accessor functions.
* src/abg-comparison.cc (class corpus_diff::diff_stats::priv): New
type.
(diff_context::priv::show_added_syms_unreferenced_by_di_): New
data member.
(diff_context::priv::priv): Adjust.
(diff_context::show_added_symbols_unreferenced_by_debug_info):
Define this new method.
(corpus_diff::priv::emit_diff_stats): Do not show the diff stat
if the only changes is added function or variables symbols and if
we were instructed to not show added symbols.
(corpus_diff::priv::{diff_stats_, filters_and_suppr_applied_}):
New data members.
(corpus_diff::priv::priv): Initialize the
filters_and_suppr_applied_ data member.
(corpus_diff::priv::diff_stats): Move this type to
corpus_diff::diff_stats.
(corpus_diff::priv::{apply_filters_and_compute_diff_stats,
emit_diff_stats}): Adjust.
(corpus_diff::apply_filters_and_suppressions_before_reporting):
Define new member function.
(corpus_diff::report): Use the new
apply_filters_and_suppressions_before_reporting() function, rather
than applying the filters and suppressions by ourselves. Also
adjust to the use the accessors of the new corpus_diff::diff_stats
type.
(corpus_diff::{deleted_variables,
deleted_unrefed_function_symbols,
deleted_unrefed_variable_symbols}): Define new accessors.
(corpus_diff::diff_stats::{diff_stats, num_func_removed,
num_func_added, num_func_changed, num_func_filtered_out,
net_num_func_changed, num_vars_removed, num_vars_added,
num_vars_changed, num_vars_filtered_out, net_num_vars_changed,
num_func_sym_removed, num_func_syms_added, num_var_syms_removed,
num_var_syms_added}): Define new member functions.
* include/abg-corpus.h (corpus::{get_sym_ids_of_fns_to_keep,
get_sym_ids_of_vars_to_keep}): Declare new methods.
* src/abg-corpus.cc (corpus::priv::{sym_id_fns_to_keep,
sym_id_vars_to_keep}): Added data members.
(symtab_build_visitor_type::{unrefed_fun_symbols,
unrefed_var_symbols, sym_id_fns_to_keep, sym_id_vars_to_keep}):
Added new data members.
(symtab_build_visitor_type::symtab_build_visitor_type): Take two
additional parameters for the function and variable symbol ids to
keep.
(symtab_build_visitor_type::add_fn_to_wip_fns): Take the function
symbols to keep in account when building the exported symbol
table.
(symtab_build_visitor_type::add_var_to_wip_vars): Likewise, take
the variable symbols to keep in account when building the exported
symbol table.
(corpus::priv::build_public_decl_table): Adjust the initialization
of the visitor that walks the ABI artifacts to build the exported
symbol table to know take a list of function/variable symbols to
keep.
(corpus::priv::build_unreferenced_symbols_tables): Ensure that the
public table of functions/variables is built before doing the work
of this function. Also, if a list of variable/function symbols to
keep is given, drop all symbols that are not in that list on the
floor.
(corpus::{get_sym_ids_of_fns_to_keep,
get_sym_ids_of_vars_to_keep}): Define new accessors.
* tools/abicompat.cc: New abicompat tool.
* doc/manuals/abicompat.rst: New documentation source for
abicompat.
* doc/manuals/libabigail-tools.rst: Add an entry for the abicompat
doc.
* tests/test-abicompat.cc: New test harness for the 'abicompat'
tool.
* tests/Makefile.am: Build the runtestabicompat test harness and
add it to the list of tests harnesses that are run by make check.
* tests/data/test-abicompat/libtest0-fn-changed-libapp-v0.so: New
test input.
* tests/data/test-abicompat/libtest0-fn-changed-libapp-v1.so: Likewise.
* tests/data/test-abicompat/test0-fn-changed-app: Likewise.
* tests/data/test-abicompat/test0-fn-changed-0.suppr: Likewise
* tests/data/test-abicompat/test0-fn-changed-report-0.txt: Likewise.
* tests/data/test-abicompat/test0-fn-changed-report-1.txt: Likewise.
* tests/data/test-abicompat/test0-fn-changed-app.cc: Likewise.
* tests/data/test-abicompat/test0-fn-changed-libapp.h: Likewise.
* tests/data/test-abicompat/test0-fn-changed-libapp-v0.cc: Likewise.
* tests/data/test-abicompat/test0-fn-changed-libapp-v1.cc: Likewise.
* tests/data/test-abicompat/libtest1-fn-removed-v0.so: Likewise.
* tests/data/test-abicompat/libtest1-fn-removed-v1.so: Likewise.
* tests/data/test-abicompat/test1-fn-removed-app: Likewise.
* tests/data/test-abicompat/test1-fn-removed-app.cc: Likewise.
* tests/data/test-abicompat/test1-fn-removed-report-0.txt: Likewise.
* tests/data/test-abicompat/test1-fn-removed-v0.cc: Likewise.
* tests/data/test-abicompat/test1-fn-removed-v1.cc: Likewise.
* tests/data/test-abicompat/libtest2-var-removed-v0.so: Likewise.
* tests/data/test-abicompat/libtest2-var-removed-v1.so: Likewise.
* tests/data/test-abicompat/test2-var-removed-app: Likewise.
* tests/data/test-abicompat/test2-var-removed-app.cc: Likewise.
* tests/data/test-abicompat/test2-var-removed-report-0.txt: Likewise.
* tests/data/test-abicompat/test2-var-removed-v0.cc: Likewise.
* tests/data/test-abicompat/test2-var-removed-v1.cc: Likewise.
* tests/data/test-abicompat/libtest3-fn-removed-v0.so: Likewise.
* tests/data/test-abicompat/libtest3-fn-removed-v1.so: Likewise.
* tests/data/test-abicompat/test3-fn-removed-app: Likewise.
* tests/data/test-abicompat/test3-fn-removed-app.cc: Likewise.
* tests/data/test-abicompat/test3-fn-removed-report-0.txt: Likewise.
* tests/data/test-abicompat/test3-fn-removed-v0.cc: Likewise.
* tests/data/test-abicompat/test3-fn-removed-v1.cc: Likewise.
* tests/data/test-abicompat/test3-fn-removed-version-script-0 Likewise.:
* tests/data/test-abicompat/test3-fn-removed-version-script-1: Likewise.
* tests/data/Makefile.am: Add the new test inputs above to the
source distribution.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
2014-11-30 17:18:55 +00:00
|
|
|
|| 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);
|
|
|
|
}
|
|
|
|
|
2014-12-08 12:36:01 +00:00
|
|
|
bool abi_break_for_sure = changes->soname_changed()
|
|
|
|
|| s.num_vars_removed()
|
Initial implementation of the abicompat tool
Given an application A that links to a shared library L of version V
denoted L(V) and a subsequent version of that library denoted L(V+P),
the 'abicompat' tool tells the user if L(V+P) is still ABI compatible
with L(V+P). And if it is not, abicompat gives a reports that shows
the differences between L(V) and L(V+P) that makes L(V+P)
ABI-incompatible with A.
The source code of this tool is in the tools/abicompat.cc source
file. To support this new tool, this commit changes the comparison
engine to optionally avoid showing added symbols that were not
referenced by any debug info. It changes the ABI corpus type to allow
the specification of a list of variables and functions symbols to keep
(and drop all other functions and variables which have other symbols
on the floor even before starting to compare the two libraries).
This is how the abicompat tool itself works. It basically compares
L(V) and L(V+P) but it only looks at their exported functions and
variables which symbols are undefined in application A. If the list
of exported and defined variables and functions of L(V) whose symbols
are undefined in A equals that of L(V+P) (including the sub-types of
these variables and functions) A is still compatible with L(V+P).
Otherwise, they might not be compatible depending on the kind of
differences that are found.
* include/abg-comparison.h
(diff_context::show_added_symbols_unreferenced_by_debug_info):
Declare new accessors.
(corpus_diff::{deleted_variables,
deleted_unrefed_function_symbols,
deleted_unrefed_variable_symbols,
apply_filters_and_suppressions_before_reporting}): Declare new
methods.
(corpus_diff::diff_stats): Declare this new type. Actually this
was previously corpus_diff::priv::diff_stats, which was a hidden
internal type.. We are moving it here, in the external API so
that client code can have more information about changes
statistics. Change all the previously publicly accessible data
members into accessor functions.
* src/abg-comparison.cc (class corpus_diff::diff_stats::priv): New
type.
(diff_context::priv::show_added_syms_unreferenced_by_di_): New
data member.
(diff_context::priv::priv): Adjust.
(diff_context::show_added_symbols_unreferenced_by_debug_info):
Define this new method.
(corpus_diff::priv::emit_diff_stats): Do not show the diff stat
if the only changes is added function or variables symbols and if
we were instructed to not show added symbols.
(corpus_diff::priv::{diff_stats_, filters_and_suppr_applied_}):
New data members.
(corpus_diff::priv::priv): Initialize the
filters_and_suppr_applied_ data member.
(corpus_diff::priv::diff_stats): Move this type to
corpus_diff::diff_stats.
(corpus_diff::priv::{apply_filters_and_compute_diff_stats,
emit_diff_stats}): Adjust.
(corpus_diff::apply_filters_and_suppressions_before_reporting):
Define new member function.
(corpus_diff::report): Use the new
apply_filters_and_suppressions_before_reporting() function, rather
than applying the filters and suppressions by ourselves. Also
adjust to the use the accessors of the new corpus_diff::diff_stats
type.
(corpus_diff::{deleted_variables,
deleted_unrefed_function_symbols,
deleted_unrefed_variable_symbols}): Define new accessors.
(corpus_diff::diff_stats::{diff_stats, num_func_removed,
num_func_added, num_func_changed, num_func_filtered_out,
net_num_func_changed, num_vars_removed, num_vars_added,
num_vars_changed, num_vars_filtered_out, net_num_vars_changed,
num_func_sym_removed, num_func_syms_added, num_var_syms_removed,
num_var_syms_added}): Define new member functions.
* include/abg-corpus.h (corpus::{get_sym_ids_of_fns_to_keep,
get_sym_ids_of_vars_to_keep}): Declare new methods.
* src/abg-corpus.cc (corpus::priv::{sym_id_fns_to_keep,
sym_id_vars_to_keep}): Added data members.
(symtab_build_visitor_type::{unrefed_fun_symbols,
unrefed_var_symbols, sym_id_fns_to_keep, sym_id_vars_to_keep}):
Added new data members.
(symtab_build_visitor_type::symtab_build_visitor_type): Take two
additional parameters for the function and variable symbol ids to
keep.
(symtab_build_visitor_type::add_fn_to_wip_fns): Take the function
symbols to keep in account when building the exported symbol
table.
(symtab_build_visitor_type::add_var_to_wip_vars): Likewise, take
the variable symbols to keep in account when building the exported
symbol table.
(corpus::priv::build_public_decl_table): Adjust the initialization
of the visitor that walks the ABI artifacts to build the exported
symbol table to know take a list of function/variable symbols to
keep.
(corpus::priv::build_unreferenced_symbols_tables): Ensure that the
public table of functions/variables is built before doing the work
of this function. Also, if a list of variable/function symbols to
keep is given, drop all symbols that are not in that list on the
floor.
(corpus::{get_sym_ids_of_fns_to_keep,
get_sym_ids_of_vars_to_keep}): Define new accessors.
* tools/abicompat.cc: New abicompat tool.
* doc/manuals/abicompat.rst: New documentation source for
abicompat.
* doc/manuals/libabigail-tools.rst: Add an entry for the abicompat
doc.
* tests/test-abicompat.cc: New test harness for the 'abicompat'
tool.
* tests/Makefile.am: Build the runtestabicompat test harness and
add it to the list of tests harnesses that are run by make check.
* tests/data/test-abicompat/libtest0-fn-changed-libapp-v0.so: New
test input.
* tests/data/test-abicompat/libtest0-fn-changed-libapp-v1.so: Likewise.
* tests/data/test-abicompat/test0-fn-changed-app: Likewise.
* tests/data/test-abicompat/test0-fn-changed-0.suppr: Likewise
* tests/data/test-abicompat/test0-fn-changed-report-0.txt: Likewise.
* tests/data/test-abicompat/test0-fn-changed-report-1.txt: Likewise.
* tests/data/test-abicompat/test0-fn-changed-app.cc: Likewise.
* tests/data/test-abicompat/test0-fn-changed-libapp.h: Likewise.
* tests/data/test-abicompat/test0-fn-changed-libapp-v0.cc: Likewise.
* tests/data/test-abicompat/test0-fn-changed-libapp-v1.cc: Likewise.
* tests/data/test-abicompat/libtest1-fn-removed-v0.so: Likewise.
* tests/data/test-abicompat/libtest1-fn-removed-v1.so: Likewise.
* tests/data/test-abicompat/test1-fn-removed-app: Likewise.
* tests/data/test-abicompat/test1-fn-removed-app.cc: Likewise.
* tests/data/test-abicompat/test1-fn-removed-report-0.txt: Likewise.
* tests/data/test-abicompat/test1-fn-removed-v0.cc: Likewise.
* tests/data/test-abicompat/test1-fn-removed-v1.cc: Likewise.
* tests/data/test-abicompat/libtest2-var-removed-v0.so: Likewise.
* tests/data/test-abicompat/libtest2-var-removed-v1.so: Likewise.
* tests/data/test-abicompat/test2-var-removed-app: Likewise.
* tests/data/test-abicompat/test2-var-removed-app.cc: Likewise.
* tests/data/test-abicompat/test2-var-removed-report-0.txt: Likewise.
* tests/data/test-abicompat/test2-var-removed-v0.cc: Likewise.
* tests/data/test-abicompat/test2-var-removed-v1.cc: Likewise.
* tests/data/test-abicompat/libtest3-fn-removed-v0.so: Likewise.
* tests/data/test-abicompat/libtest3-fn-removed-v1.so: Likewise.
* tests/data/test-abicompat/test3-fn-removed-app: Likewise.
* tests/data/test-abicompat/test3-fn-removed-app.cc: Likewise.
* tests/data/test-abicompat/test3-fn-removed-report-0.txt: Likewise.
* tests/data/test-abicompat/test3-fn-removed-v0.cc: Likewise.
* tests/data/test-abicompat/test3-fn-removed-v1.cc: Likewise.
* tests/data/test-abicompat/test3-fn-removed-version-script-0 Likewise.:
* tests/data/test-abicompat/test3-fn-removed-version-script-1: Likewise.
* tests/data/Makefile.am: Add the new test inputs above to the
source distribution.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
2014-11-30 17:18:55 +00:00
|
|
|
|| s.num_func_removed()
|
|
|
|
|| s.num_var_syms_removed()
|
|
|
|
|| s.num_func_syms_removed();
|
|
|
|
|
|
|
|
cout << "ELF file '" << app_path << "'";
|
|
|
|
if (abi_break_for_sure)
|
|
|
|
cout << " is not ";
|
|
|
|
else
|
|
|
|
cout << " might not be ";
|
|
|
|
|
|
|
|
cout << "ABI compatible with '" << lib2_path
|
|
|
|
<< "' due to differences with '" << lib1_path
|
|
|
|
<< "' below:\n";
|
|
|
|
changes->report(cout);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|