libabigail/tools/kmidiff.cc
Dodji Seketeli c80f79271a Re-license the project to Apache v2 With LLVM Exception
Thanks to the previous work done, changing the license is just a
matter of changing the SPDX identifer from "LGPL-3.0-or-later" to
"Apache-2.0 WITH LLVM-exception".  Note that for the abigail.m4,
tests/test-dot.cc and tests/test-svg.cc the change was from
"GPL-3.0-or-later WITH GCC-exception-3.1" to "Apache-2.0 WITH
LLVM-exception".  include/abg-cxx-compat.h was changed from
"LGPL-2.0-or-later" to "Apache-2.0 WITH LLVM-exception".  Source code
of programs (as opposed to source code of the library) where generally
licensed under GPL-3.0-or-later; they are also now licensed
"Apache-2.0 WITH LLVM-exception".

This is what this patch does.

	* abigail.m4: Change the SPDX identifier from "GPL-3.0-or-later
	WITH GCC-exception-3.1" to "Apache-2.0 WITH LLVM-exception"
	* include/abg-cxx-compat.h: Change the SPDX identifier from
	"LGPL-2.0-or-later" to "Apache-2.0 WITH LLVM-exception".
	* .clang-format: Change the SPDX identifier from
	  "LGPL-3.0-or-later" to "Apache-2.0 WITH LLVM-exception".
	* Makefile.am: Likewise.
	* bash-completion/Makefile.am: Likewise.
	* bash-completion/abicompat: Likewise.
	* bash-completion/abidiff: Likewise.
	* bash-completion/abidw: Likewise.
	* bash-completion/abilint: Likewise.
	* bash-completion/abinilint: Likewise.
	* bash-completion/abipkgdiff: Likewise.
	* bash-completion/abisym: Likewise.
	* bash-completion/fedabipkgdiff: Likewise.
	* configure.ac: Likewise.
	* default.abignore: Likewise.
	* doc/Makefile.am: Likewise.
	* doc/api/libabigail.doxy: Likewise.
	* doc/manuals/Makefile.am: Likewise.
	* doc/website/libabigail-website.doxy: Likewise.
	* include/Makefile.am: Likewise.
	* include/abg-comp-filter.h: Likewise.
	* include/abg-comparison.h: Likewise.
	* include/abg-config.h: Likewise.
	* include/abg-corpus.h: Likewise.
	* include/abg-diff-utils.h: Likewise.
	* include/abg-dwarf-reader.h: Likewise.
	* include/abg-fwd.h: Likewise.
	* include/abg-hash.h: Likewise.
	* include/abg-ini.h: Likewise.
	* include/abg-interned-str.h: Likewise.
	* include/abg-ir.h: Likewise.
	* include/abg-libxml-utils.h: Likewise.
	* include/abg-libzip-utils.h: Likewise.
	* include/abg-reader.h: Likewise.
	* include/abg-regex.h: Likewise.
	* include/abg-reporter.h: Likewise.
	* include/abg-sptr-utils.h: Likewise.
	* include/abg-suppression.h: Likewise.
	* include/abg-tools-utils.h: Likewise.
	* include/abg-traverse.h: Likewise.
	* include/abg-version.h.in: Likewise.
	* include/abg-viz-common.h: Likewise.
	* include/abg-viz-dot.h: Likewise.
	* include/abg-viz-svg.h: Likewise.
	* include/abg-workers.h: Likewise.
	* include/abg-writer.h: Likewise.
	* scripts/dot_to_png.sh: Likewise.
	* scripts/dot_to_svg.sh: Likewise.
	* scripts/make-verbose.sh: Likewise.
	* scripts/svg_to_plain_svg.sh: Likewise.
	* scripts/svg_to_png_and_pdf.sh: Likewise.
	* src/Makefile.am: Likewise.
	* src/abg-comp-filter.cc: Likewise.
	* src/abg-comparison-priv.h: Likewise.
	* src/abg-comparison.cc: Likewise.
	* src/abg-config.cc: Likewise.
	* src/abg-corpus-priv.h: Likewise.
	* src/abg-corpus.cc: Likewise.
	* src/abg-default-reporter.cc: Likewise.
	* src/abg-diff-utils.cc: Likewise.
	* src/abg-dwarf-reader.cc: Likewise.
	* src/abg-elf-helpers.cc: Likewise.
	* src/abg-elf-helpers.h: Likewise.
	* src/abg-hash.cc: Likewise.
	* src/abg-ini.cc: Likewise.
	* src/abg-internal.h: Likewise.
	* src/abg-ir-priv.h: Likewise.
	* src/abg-ir.cc: Likewise.
	* src/abg-leaf-reporter.cc: Likewise.
	* src/abg-libxml-utils.cc: Likewise.
	* src/abg-libzip-utils.cc: Likewise.
	* src/abg-reader.cc: Likewise.
	* src/abg-regex.cc: Likewise.
	* src/abg-reporter-priv.cc: Likewise.
	* src/abg-reporter-priv.h: Likewise.
	* src/abg-suppression-priv.h: Likewise.
	* src/abg-suppression.cc: Likewise.
	* src/abg-tools-utils.cc: Likewise.
	* src/abg-traverse.cc: Likewise.
	* src/abg-viz-common.cc: Likewise.
	* src/abg-viz-dot.cc: Likewise.
	* src/abg-viz-svg.cc: Likewise.
	* src/abg-workers.cc: Likewise.
	* src/abg-writer.cc: Likewise.
	* tests/Makefile.am: Likewise.
	* tests/data/Makefile.am: Likewise.
	* tests/lib/catch.cc: Likewise.
	* tests/mockfedabipkgdiff.in: Likewise.
	* tests/print-diff-tree.cc: Likewise.
	* tests/runtestcanonicalizetypes.sh.in: Likewise.
	* tests/runtestdefaultsupprs.py.in: Likewise.
	* tests/runtestdefaultsupprspy3.sh.in: Likewise.
	* tests/runtestfedabipkgdiff.py.in: Likewise.
	* tests/runtestfedabipkgdiffpy3.sh.in: Likewise.
	* tests/test-abicompat.cc: Likewise.
	* tests/test-abidiff-exit.cc: Likewise.
	* tests/test-abidiff.cc: Likewise.
	* tests/test-alt-dwarf-file.cc: Likewise.
	* tests/test-annotate.cc: Likewise.
	* tests/test-core-diff.cc: Likewise.
	* tests/test-cxx-compat.cc: Likewise.
	* tests/test-diff-dwarf-abixml.cc: Likewise.
	* tests/test-diff-dwarf.cc: Likewise.
	* tests/test-diff-filter.cc: Likewise.
	* tests/test-diff-pkg.cc: Likewise.
	* tests/test-diff-suppr.cc: Likewise.
	* tests/test-diff2.cc: Likewise.
	* tests/test-dot.cc: Change the SPDX identifier from
	"GPL-3.0-or-later WITH GCC-exception-3.1" to "Apache-2.0 WITH
	LLVM-exception"
	* tests/test-elf-helpers.cc: Change the SPDX identifier from
	"LGPL-3.0-or-later" to "Apache-2.0 WITH LLVM-exception"
	* tests/test-ini.cc: Likewise.
	* tests/test-ir-walker.cc: Likewise.
	* tests/test-kmi-whitelist.cc: Likewise.
	* tests/test-lookup-syms.cc: Likewise.
	* tests/test-read-dwarf.cc: Likewise.
	* tests/test-read-write.cc: Likewise.
	* tests/test-svg.cc: Change the SPDX identifier from
	"GPL-3.0-or-later WITH GCC-exception-3.1" to "Apache-2.0 WITH
	LLVM-exception".
	* tests/test-symtab.cc: Change the SPDX identifier from
	"LGPL-3.0-or-later" to "Apache-2.0 WITH LLVM-exception"
	* tests/test-tools-utils.cc: Likewise.
	* tests/test-types-stability.cc: Likewise.
	* tests/test-utils.cc: Likewise.
	* tests/test-utils.h: Likewise.
	* tests/test-write-read-archive.cc: Likewise.
	* tests/update-test-output.py: Likewise.
	* tools/Makefile.am: Likewise.
	* tools/abiar.cc: Likewise.
	* tools/abicompat.cc: Likewise.
	* tools/abidiff.cc: Likewise.
	* tools/abidw.cc: Likewise.
	* tools/abilint.cc: Likewise.
	* tools/abipkgdiff.cc: Likewise.
	* tools/abisym.cc: Likewise.
	* tools/binilint.cc: Likewise.
	* tools/fedabipkgdiff: Likewise.
	* tools/kmidiff.cc: Likewise.
	* update-copyright.sh: Likewise.

Signed-off-by: Benjamin De Kosnik <bkoz@gnu.org>
Signed-off-by: Ben Woodard <woodard@redhat.com>
Signed-off-by: Chenxiong Qi <cqi@redhat.com>
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Signed-off-by: Giuliano Procida <gprocida@google.com>
Signed-off-by: Jan Engelhardt <jengelh@inai.de>
Signed-off-by: Jessica Yu <jeyu@kernel.org>
Signed-off-by: Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Mark Wielaard <mark@klomp.org>
Signed-off-by: Matthias Klose <doko@ubuntu.com>
Signed-off-by: Matthias Maennich <maennich@google.com>
Signed-off-by: Ondrej Oprala <ondrej.oprala@gmail.com>
Signed-off-by: Roland McGrath <roland@hack.frob.com>
Signed-off-by: Sinny Kumari <ksinny@gmail.com>
Signed-off-by: Slava Barinov <v.barinov@samsung.com>
2020-12-02 11:49:13 +01:00

473 lines
13 KiB
C++

// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2017-2020 Red Hat, Inc.
//
// Author: Dodji Seketeli
/// @file
///
/// The source code of the Kernel Module Interface Diff tool.
#include <sys/types.h>
#include <dirent.h>
#include <fts.h>
#include <cstring>
#include <string>
#include <vector>
#include <iostream>
#include "abg-config.h"
#include "abg-tools-utils.h"
#include "abg-corpus.h"
#include "abg-dwarf-reader.h"
#include "abg-reader.h"
#include "abg-comparison.h"
using std::string;
using std::vector;
using std::ostream;
using std::cout;
using std::cerr;
using namespace abigail::tools_utils;
using namespace abigail::dwarf_reader;
using namespace abigail::ir;
using abigail::comparison::diff_context_sptr;
using abigail::comparison::diff_context;
using abigail::comparison::translation_unit_diff_sptr;
using abigail::comparison::corpus_diff;
using abigail::comparison::corpus_diff_sptr;
using abigail::comparison::compute_diff;
using abigail::comparison::get_default_harmless_categories_bitmap;
using abigail::comparison::get_default_harmful_categories_bitmap;
using abigail::suppr::suppression_sptr;
using abigail::suppr::suppressions_type;
using abigail::suppr::read_suppressions;
using abigail::tools_utils::guess_file_type;
using abigail::tools_utils::file_type;
using abigail::xml_reader::read_corpus_group_from_native_xml_file;
/// The options of this program.
struct options
{
bool display_usage;
bool display_version;
bool verbose;
bool missing_operand;
bool leaf_changes_only;
bool show_hexadecimal_values;
bool show_offsets_sizes_in_bits;
bool show_impacted_interfaces;
string wrong_option;
string kernel_dist_root1;
string kernel_dist_root2;
string vmlinux1;
string vmlinux2;
vector<string> kabi_whitelist_paths;
vector<string> suppression_paths;
suppressions_type read_time_supprs;
suppressions_type diff_time_supprs;
shared_ptr<char> di_root_path1;
shared_ptr<char> di_root_path2;
options()
: display_usage(),
display_version(),
verbose(),
missing_operand(),
leaf_changes_only(true),
show_hexadecimal_values(true),
show_offsets_sizes_in_bits(false),
show_impacted_interfaces(false)
{}
}; // end struct options.
/// Display the usage of the program.
///
/// @param prog_name the name of this program.
///
/// @param out the output stream the usage stream is sent to.
static void
display_usage(const string& prog_name, ostream& out)
{
emit_prefix(prog_name, out)
<< "usage: " << prog_name << " [options] kernel-modules-dir1 kernel-modules-dir2\n"
<< " where options can be:\n"
<< " --help|-h display this message\n"
<< " --version|-v display program version information and exit\n"
<< " --verbose display verbose messages\n"
<< " --debug-info-dir1|--d1 <path> the root for the debug info of "
"the first kernel\n"
<< " --debug-info-dir2|--d2 <path> the root for the debug info of "
"the second kernel\n"
<< " --vmlinux1|--l1 <path> the path to the first vmlinux\n"
<< " --vmlinux2|--l2 <path> the path to the second vmlinux\n"
<< " --suppressions|--suppr <path> specify a suppression file\n"
<< " --kmi-whitelist|-w <path> path to a kernel module interface "
"whitelist\n"
<< " --impacted-interfaces|-i show interfaces impacted by ABI changes\n"
<< " --full-impact|-f show the full impact of changes on top-most "
"interfaces\n"
<< " --show-bytes show size and offsets in bytes\n"
<< " --show-bits show size and offsets in bits\n"
<< " --show-hex show size and offset in hexadecimal\n"
<< " --show-dec show size and offset in decimal\n";
}
/// Parse the command line of the program.
///
/// @param argc the number of arguments on the command line, including
/// the program name.
///
/// @param argv the arguments on the command line, including the
/// program name.
///
/// @param opts the options resulting from the command line parsing.
///
/// @return true iff the command line parsing went fine.
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.kernel_dist_root1.empty())
opts.kernel_dist_root1 = argv[i];
else if (opts.kernel_dist_root2.empty())
opts.kernel_dist_root2 = argv[i];
else
return false;
}
else if (!strcmp(argv[i], "--verbose"))
opts.verbose = true;
else if (!strcmp(argv[i], "--version")
|| !strcmp(argv[i], "-v"))
{
opts.display_version = true;
return true;
}
else if (!strcmp(argv[i], "--help")
|| !strcmp(argv[i], "-h"))
{
opts.display_usage = true;
return true;
}
else if (!strcmp(argv[i], "--debug-info-dir1")
|| !strcmp(argv[i], "--d1"))
{
int j = i + 1;
if (j >= argc)
{
opts.missing_operand = true;
opts.wrong_option = argv[i];
return true;
}
// elfutils wants the root path to the debug info to be
// absolute.
opts.di_root_path1 =
abigail::tools_utils::make_path_absolute(argv[j]);
++i;
}
else if (!strcmp(argv[i], "--debug-info-dir2")
|| !strcmp(argv[i], "--d2"))
{
int j = i + 1;
if (j >= argc)
{
opts.missing_operand = true;
opts.wrong_option = argv[i];
return true;
}
// elfutils wants the root path to the debug info to be
// absolute.
opts.di_root_path2 =
abigail::tools_utils::make_path_absolute(argv[j]);
++i;
}
else if (!strcmp(argv[i], "--vmlinux1")
|| !strcmp(argv[i], "--l1"))
{
int j = i + 1;
if (j >= argc)
{
opts.missing_operand = true;
opts.wrong_option = argv[i];
return false;
}
opts.vmlinux1 = argv[j];
++i;
}
else if (!strcmp(argv[i], "--vmlinux2")
|| !strcmp(argv[i], "--l2"))
{
int j = i + 1;
if (j >= argc)
{
opts.missing_operand = true;
opts.wrong_option = argv[i];
return false;
}
opts.vmlinux2 = argv[j];
++i;
}
else if (!strcmp(argv[i], "--kmi-whitelist")
|| !strcmp(argv[i], "-w"))
{
int j = i + 1;
if (j >= argc)
{
opts.missing_operand = true;
opts.wrong_option = argv[i];
return false;
}
opts.kabi_whitelist_paths.push_back(argv[j]);
++i;
}
else if (!strcmp(argv[i], "--suppressions")
|| !strcmp(argv[i], "--suppr"))
{
int j = i + 1;
if (j >= argc)
{
opts.missing_operand = true;
opts.wrong_option = argv[i];
return false;
}
opts.suppression_paths.push_back(argv[j]);
++i;
}
else if (!strcmp(argv[i], "--impacted-interfaces")
|| !strcmp(argv[i], "-i"))
opts.show_impacted_interfaces = true;
else if (!strcmp(argv[i], "--full-impact")
|| !strcmp(argv[i], "-f"))
opts.leaf_changes_only = false;
else if (!strcmp(argv[i], "--show-bytes"))
opts.show_offsets_sizes_in_bits = false;
else if (!strcmp(argv[i], "--show-bits"))
opts.show_offsets_sizes_in_bits = true;
else if (!strcmp(argv[i], "--show-hex"))
opts.show_hexadecimal_values = true;
else if (!strcmp(argv[i], "--show-dec"))
opts.show_hexadecimal_values = false;
else
{
opts.wrong_option = argv[i];
return false;
}
}
return true;
}
/// Check that the suppression specification files supplied are
/// present. If not, emit an error on stderr.
///
/// @param opts the options instance to use.
///
/// @return true if all suppression specification files are present,
/// false otherwise.
static bool
maybe_check_suppression_files(const options& opts)
{
for (vector<string>::const_iterator i = opts.suppression_paths.begin();
i != opts.suppression_paths.end();
++i)
if (!check_file(*i, cerr, "abidiff"))
return false;
for (vector<string>::const_iterator i =
opts.kabi_whitelist_paths.begin();
i != opts.kabi_whitelist_paths.end();
++i)
if (!check_file(*i, cerr, "abidiff"))
return false;
return true;
}
/// Setup the diff context from the program's options.
///
/// @param ctxt the diff context to consider.
///
/// @param opts the options to set the context.
static void
set_diff_context(diff_context_sptr ctxt, const options& opts)
{
ctxt->default_output_stream(&cout);
ctxt->error_output_stream(&cerr);
ctxt->show_relative_offset_changes(true);
ctxt->show_redundant_changes(false);
ctxt->show_locs(true);
ctxt->show_linkage_names(false);
ctxt->show_symbols_unreferenced_by_debug_info
(true);
ctxt->show_leaf_changes_only(opts.leaf_changes_only);
ctxt->show_impacted_interfaces(opts.show_impacted_interfaces);
ctxt->show_hex_values(opts.show_hexadecimal_values);
ctxt->show_offsets_sizes_in_bits(opts.show_offsets_sizes_in_bits);
ctxt->switch_categories_off(get_default_harmless_categories_bitmap());
if (!opts.diff_time_supprs.empty())
ctxt->add_suppressions(opts.diff_time_supprs);
}
/// Print information about the kernel (and modules) binaries found
/// under a given directory.
///
/// Note that this function actually look for the modules iff the
/// --verbose option was provided.
///
/// @param root the directory to consider.
///
/// @param opts the options to use during the search.
static void
print_kernel_dist_binary_paths_under(const string& root, const options &opts)
{
string vmlinux;
vector<string> modules;
if (opts.verbose)
if (get_binary_paths_from_kernel_dist(root, /*debug_info_root_path*/"",
vmlinux, modules))
{
cout << "Found kernel binaries under: '" << root << "'\n";
if (!vmlinux.empty())
cout << "[linux kernel binary]\n"
<< " '" << vmlinux << "'\n";
if (!modules.empty())
{
cout << "[linux kernel module binaries]\n";
for (vector<string>::const_iterator p = modules.begin();
p != modules.end();
++p)
cout << " '" << *p << "' \n";
}
cout << "\n";
}
}
int
main(int argc, char* argv[])
{
options opts;
if (!parse_command_line(argc, argv, opts))
{
emit_prefix(argv[0], cerr)
<< "unrecognized option: "
<< opts.wrong_option << "\n"
<< "try the --help option for more information\n";
return 1;
}
if (opts.missing_operand)
{
emit_prefix(argv[0], cerr)
<< "missing operand to option: " << opts.wrong_option <<"\n"
<< "try the --help option for more information\n";
return 1;
}
if (!maybe_check_suppression_files(opts))
return 1;
if (opts.display_usage)
{
display_usage(argv[0], cout);
return 1;
}
if (opts.display_version)
{
emit_prefix(argv[0], cout)
<< abigail::tools_utils::get_library_version_string()
<< "\n";
return 0;
}
environment_sptr env(new environment);
corpus_group_sptr group1, group2;
string debug_info_root_dir;
if (!opts.kernel_dist_root1.empty())
{
file_type ftype = guess_file_type(opts.kernel_dist_root1);
if (ftype == FILE_TYPE_DIR)
{
debug_info_root_dir = opts.di_root_path1.get()
? opts.di_root_path1.get()
: "";
group1 =
build_corpus_group_from_kernel_dist_under(opts.kernel_dist_root1,
debug_info_root_dir,
opts.vmlinux1,
opts.suppression_paths,
opts.kabi_whitelist_paths,
opts.read_time_supprs,
opts.verbose,
env);
print_kernel_dist_binary_paths_under(opts.kernel_dist_root1, opts);
}
else if (ftype == FILE_TYPE_XML_CORPUS_GROUP)
group1 =
read_corpus_group_from_native_xml_file(opts.kernel_dist_root1,
env.get());
}
if (!opts.kernel_dist_root2.empty())
{
file_type ftype = guess_file_type(opts.kernel_dist_root2);
if (ftype == FILE_TYPE_DIR)
{
debug_info_root_dir = opts.di_root_path2.get()
? opts.di_root_path2.get()
: "";
group2 =
build_corpus_group_from_kernel_dist_under(opts.kernel_dist_root2,
debug_info_root_dir,
opts.vmlinux2,
opts.suppression_paths,
opts.kabi_whitelist_paths,
opts.read_time_supprs,
opts.verbose,
env);
print_kernel_dist_binary_paths_under(opts.kernel_dist_root2, opts);
}
else if (ftype == FILE_TYPE_XML_CORPUS_GROUP)
group2 =
read_corpus_group_from_native_xml_file(opts.kernel_dist_root2,
env.get());
}
abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
if (group1 && group2)
{
diff_context_sptr diff_ctxt(new diff_context);
set_diff_context(diff_ctxt, opts);
corpus_diff_sptr diff= compute_diff(group1, group2, diff_ctxt);
if (diff->has_net_changes())
status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
if (diff->has_incompatible_changes())
status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
if (diff->has_changes())
diff->report(cout);
}
else
status = abigail::tools_utils::ABIDIFF_ERROR;
return status;
}