diff mbox series

abipkgdiff: Add a new --self-check option

Message ID 86v9dvqc1l.fsf@redhat.com
State New
Headers show
Series abipkgdiff: Add a new --self-check option | expand

Commit Message

Dodji Seketeli Nov. 24, 2020, 10:33 a.m. UTC
Hello,

After the report of
https://sourceware.org/bugzilla/show_bug.cgi?id=26769 it became
apparent that we want to be able compare a binary against its ABIXML
representation, including for cases where the binaries are embedded in
RPM packages.

This patches thus introduces the --self-check option to abipkgdiff so
that it can be invoked like this:

    $ abipkgdiff --self-check --d1 libstdc++-debuginfo-10.2.1-8.fc34.x86_64.rpm --d1 gcc-debuginfo-10.2.1-8.fc34.x86_64.rpm libstdc++-10.2.1-8.fc34.x86_64.rpm

    ==== SELF CHECK SUCCEEDED for 'libstdc++.so.6.0.28'====
    $

With this option, libabigail compares each binary in the RPM against
its own ABIXML representation.

This should hopefully help to write regression tests which have as
sole inputs the links to download the RPMs.  It's also useful to ease
the process of reproducing the issue raised.

This option can now be used, for instance, by the libabigail-selfcheck
program over at https://pagure.io/libabigail-selfcheck.

	* tools/abipkgdiff.cc (options::self_check): Define new data
	member.
	(options::options): Initialize it.
	(display_usage): Add help string for the --self-check option.
	(parse_command): Parse the new --self-check option.
	(extract_deb): Add missing newline.
	(compare): Remove useless white space.
	(compare_to_self, self_compare_prepared_userspace_package)
	(self_compare_prepared_package, compare_to_self): Add new static
	functions.
	(class self_compare_task): Add new class.
	(prepare_package): Add a new overload that takes just one
	parameter.
	(elf_size_is_greater): Don't crash if the args are empty.
	(main): If the --self-check option is given, make sure we have
	just one package in argument.  Use the new compare_to_self
	function to handle the --self-check option.
	* doc/manuals/abipkgdiff.rst: Add documentation for the new
	--self-check option.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 doc/manuals/abipkgdiff.rst |  17 ++
 tools/abipkgdiff.cc        | 504 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 505 insertions(+), 16 deletions(-)
diff mbox series

Patch

diff --git a/doc/manuals/abipkgdiff.rst b/doc/manuals/abipkgdiff.rst
index 2d8e42d..bc5acb9 100644
--- a/doc/manuals/abipkgdiff.rst
+++ b/doc/manuals/abipkgdiff.rst
@@ -451,6 +451,23 @@  Options
 
     Emit verbose progress messages.
 
+
+  * ``self-check``
+
+    This is used to test the underlying Libabigail library.  When in
+    used, the command expects only on input package, along with its
+    associated debug info packages.  The command then compares each
+    binary inside the package against its own ABIXML
+    representation. The result of the comparison should yield the
+    empty set if Libabigail behaves correctly.  Otherwise, it means
+    there is an issue that ought to be fixed.  This option is used by
+    people interested in Libabigail development for regression testing
+    purposes.  Here is an example of the use of this option: ::
+
+      $ abipkgdiff --self-check --d1 mesa-libGLU-debuginfo-9.0.1-3.fc33.x86_64.rpm  mesa-libGLU-9.0.1-3.fc33.x86_64.rpm
+       ==== SELF CHECK SUCCEEDED for 'libGLU.so.1.3.1' ====
+      $
+
 .. _abipkgdiff_return_value_label:
 
 Return value
diff --git a/tools/abipkgdiff.cc b/tools/abipkgdiff.cc
index 445e240..344fe28 100644
--- a/tools/abipkgdiff.cc
+++ b/tools/abipkgdiff.cc
@@ -87,6 +87,7 @@ 
 #endif
 
 #include <iostream>
+#include <fstream>
 #include <string>
 #include <cstring>
 #include <cstdlib>
@@ -101,11 +102,14 @@ 
 #include "abg-comparison.h"
 #include "abg-suppression.h"
 #include "abg-dwarf-reader.h"
+#include "abg-reader.h"
+#include "abg-writer.h"
 
 using std::cout;
 using std::cerr;
 using std::string;
 using std::ostream;
+using std::ofstream;
 using std::vector;
 using std::map;
 using abg_compat::unordered_set;
@@ -158,6 +162,13 @@  using abigail::dwarf_reader::create_read_context;
 using abigail::dwarf_reader::get_soname_of_elf_file;
 using abigail::dwarf_reader::get_type_of_elf_file;
 using abigail::dwarf_reader::read_corpus_from_elf;
+using abigail::xml_reader::read_corpus_from_native_xml_file;
+using abigail::xml_writer::HASH_TYPE_ID_STYLE;
+using abigail::xml_writer::create_write_context;
+using abigail::xml_writer::type_id_style_kind;
+using abigail::xml_writer::write_context_sptr;
+using abigail::xml_writer::write_corpus;
+
 
 /// The options passed to the current program.
 class options
@@ -203,6 +214,7 @@  public:
   bool		show_added_binaries;
   bool		fail_if_no_debug_info;
   bool		show_identical_binaries;
+  bool		self_check;
   vector<string> kabi_whitelist_packages;
   vector<string> suppression_paths;
   vector<string> kabi_whitelist_paths;
@@ -216,8 +228,8 @@  public:
       nonexistent_file(),
       abignore(true),
       parallel(true),
-      verbose(false),
-      drop_private_types(false),
+      verbose(),
+      drop_private_types(),
       show_relative_offset_changes(true),
       no_default_suppression(),
       keep_tmp_files(),
@@ -237,7 +249,8 @@  public:
       show_symbols_not_referenced_by_debug_info(true),
       show_added_binaries(true),
       fail_if_no_debug_info(),
-      show_identical_binaries()
+      show_identical_binaries(),
+      self_check()
   {
     // set num_workers to the default number of threads of the
     // underlying maching.  This is the default value for the number
@@ -854,6 +867,8 @@  display_usage(const string& prog_name, ostream& out)
     << " --fail-no-dbg                  fail if no debug info was found\n"
     << " --show-identical-binaries      show the names of identical binaries\n"
     << " --verbose                      emit verbose progress messages\n"
+    << " --self-check                   perform a sanity check by comparing "
+    "binaries inside the input package against their ABIXML representation\n"
     << " --help|-h                      display this help message\n"
     << " --version|-v                   display program version information"
     " and exit\n";
@@ -927,7 +942,7 @@  extract_deb(const string& package_path,
       << package_path
       << " to "
       << extracted_package_dir_path
-      << " ...";
+      << " ...\n";
 
   string cmd = "mkdir -p " + extracted_package_dir_path + " && dpkg -x " +
     package_path + " " + extracted_package_dir_path;
@@ -1272,7 +1287,7 @@  compare(const elf_file& elf1,
 
   if (opts.verbose)
     emit_prefix("abipkgdiff", cerr)
-      << "  Reading file "
+      << "Reading file "
       << elf1.path
       << " ...\n";
 
@@ -1352,13 +1367,13 @@  compare(const elf_file& elf1,
 
   if (opts.verbose)
     emit_prefix("abipkgdiff", cerr)
-      << " DONE reading file "
+      << "DONE reading file "
       << elf1.path
       << "\n";
 
   if (opts.verbose)
     emit_prefix("abipkgdiff", cerr)
-      << "  Reading file "
+      << "Reading file "
       << elf2.path
       << " ...\n";
 
@@ -1466,6 +1481,190 @@  compare(const elf_file& elf1,
   return s;
 }
 
+/// Compare an ELF file to its ABIXML representation.
+///
+/// @param elf the ELF file to compare.
+///
+/// @param debug_dir the debug directory of the ELF file.
+///
+/// @param opts the options passed the user.
+///
+/// @param env the environment to use for the comparison.
+///
+/// @param diff the diff object resulting from the comparison of @p
+/// elf against its ABIXML representation.
+///
+/// @param ctxt the resulting diff context used for the comparison
+/// that yielded @p diff.
+///
+/// @param detailed_error_status the detailed error satus returned by
+/// this function.
+///
+/// @return the status of the self comparison.
+static abidiff_status
+compare_to_self(const elf_file& elf,
+		const string&	debug_dir,
+		const options&	opts,
+		abigail::ir::environment_sptr	&env,
+		corpus_diff_sptr	&diff,
+		diff_context_sptr	&ctxt,
+		abigail::dwarf_reader::status *detailed_error_status = 0)
+{
+  char *di_dir = (char*) debug_dir.c_str();
+
+  vector<char**> di_dirs;
+  di_dirs.push_back(&di_dir);
+
+  abigail::dwarf_reader::status c_status = abigail::dwarf_reader::STATUS_OK;
+
+  if (opts.verbose)
+    emit_prefix("abipkgdiff", cerr)
+      << "Comparing the ABI of file '"
+      << elf.path
+      << "' against itself ...\n";
+
+  if (opts.verbose)
+    emit_prefix("abipkgdiff", cerr)
+      << "Reading file "
+      << elf.path
+      << " ...\n";
+
+  corpus_sptr corp;
+  {
+    read_context_sptr c =
+      create_read_context(elf.path, di_dirs, env.get(),
+			  /*load_all_types=*/opts.show_all_types);
+
+    corp = read_corpus_from_elf(*c, c_status);
+
+    if (!(c_status & abigail::dwarf_reader::STATUS_OK))
+      {
+	if (opts.verbose)
+	  emit_prefix("abipkgdiff", cerr)
+	    << "Could not read file '"
+	    << elf.path
+	    << "' propertly\n";
+
+	if (detailed_error_status)
+	  *detailed_error_status = c_status;
+
+	return abigail::tools_utils::ABIDIFF_ERROR;
+      }
+
+    if (opts.verbose)
+      emit_prefix("abipkgdiff", cerr)
+	<< "Read file '"
+	<< elf.path
+	<< "' OK\n";
+
+
+    ABG_ASSERT(corp);
+  }
+
+  corpus_sptr reread_corp;
+  string abi_file_path;
+  {
+    abi_file_path = elf.path + ".abi";
+    ofstream of(abi_file_path.c_str(), std::ios_base::trunc);
+
+
+    {
+      const abigail::xml_writer::write_context_sptr c =
+	abigail::xml_writer::create_write_context(env.get(), of);
+
+      if (opts.verbose)
+	emit_prefix("abipkgdiff", cerr)
+	  << "Writting ABIXML file '"
+	  << abi_file_path
+	  << "' ...\n";
+
+      if (!write_corpus(*c, corp, 0))
+	{
+	  if (opts.verbose)
+	    emit_prefix("abipkgdiff", cerr)
+	      << "Could not write the ABIXML file to '"
+	      << abi_file_path << "'\n";
+
+	  return abigail::tools_utils::ABIDIFF_ERROR;
+	}
+
+      of.flush();
+      of.close();
+
+      if (opts.verbose)
+	emit_prefix("abipkgdiff", cerr)
+	  << "Wrote ABIXML file '"
+	  << abi_file_path
+	  << "' OK\n";
+    }
+
+    {
+      abigail::xml_reader::read_context_sptr c =
+	abigail::xml_reader::create_native_xml_read_context(abi_file_path,
+							    env.get());
+      if (!c)
+	{
+	  if (opts.verbose)
+	    emit_prefix("abipkgdiff", cerr)
+	      << "Could not create read context for ABIXML file '"
+	      << abi_file_path << "'\n";
+
+	  return abigail::tools_utils::ABIDIFF_ERROR;
+	}
+
+      if (opts.verbose)
+	emit_prefix("abipkgdiff", cerr)
+	  << "Reading ABIXML file '"
+	  << abi_file_path
+	  << "' ...\n";
+
+      reread_corp = read_corpus_from_input(*c);
+      if (!reread_corp)
+	{
+	  if (opts.verbose)
+	    emit_prefix("abipkgdiff", cerr)
+	      << "Could not read temporary ABIXML file '"
+	      << abi_file_path << "'\n";
+
+	  return abigail::tools_utils::ABIDIFF_ERROR;
+	}
+
+      if (opts.verbose)
+	emit_prefix("abipkgdiff", cerr)
+	  << "Read file '"
+	  << abi_file_path
+	  << "' OK\n";
+    }
+  }
+
+  ctxt.reset(new diff_context);
+  set_diff_context_from_opts(ctxt, opts);
+
+  if (opts.verbose)
+    emit_prefix("abipkgdiff", cerr)
+      << "Comparing the ABIs of: \n"
+      << "   '" << corp->get_path() << "' against \n"
+      << "   '" << abi_file_path << "'...\n";
+
+  diff = compute_diff(corp, reread_corp, ctxt);
+  if (opts.verbose)
+    emit_prefix("abipkgdfiff", cerr)
+      << "... Comparing the ABIs: DONE\n";
+
+  abidiff_status s = abigail::tools_utils::ABIDIFF_OK;
+  if (diff->has_net_changes())
+    s |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
+  if (diff->has_incompatible_changes())
+    s |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
+
+  if (opts.verbose)
+    emit_prefix("abipkgdfiff", cerr)
+      << (s == abigail::tools_utils::ABIDIFF_OK)
+      ? string("Comparison against self SUCCEEDED\n")
+      : string("Comparison against self FAILED\n");
+  return s;
+}
+
 /// If devel packages were associated to the main package we are
 /// looking at, use the names of the header files (extracted from the
 /// package) to generate suppression specification to filter out types
@@ -1834,6 +2033,86 @@  public:
 /// Convenience typedef for a shared_ptr of @ref compare_task.
 typedef shared_ptr<compare_task> compare_task_sptr;
 
+/// The worker task which job is to compare an ELF binary to its ABI
+/// representation.
+class self_compare_task : public compare_task
+{
+public:
+
+  compare_args_sptr args;
+  abidiff_status status;
+  ostringstream out;
+  string pretty_output;
+
+  self_compare_task()
+    : status(abigail::tools_utils::ABIDIFF_OK)
+  {}
+
+  self_compare_task(const compare_args_sptr& a)
+    : args(a),
+      status(abigail::tools_utils::ABIDIFF_OK)
+  {}
+
+  /// The job performed by the task.
+  ///
+  /// This compares an ELF file to its ABIXML representation and
+  /// expects the result to be the empty set.
+  virtual void
+  perform()
+  {
+    abigail::ir::environment_sptr env(new abigail::ir::environment);
+    diff_context_sptr ctxt;
+    corpus_diff_sptr diff;
+
+    abigail::dwarf_reader::status detailed_status =
+      abigail::dwarf_reader::STATUS_UNKNOWN;
+
+    status |= compare_to_self(args->elf1, args->debug_dir1,
+			      args->opts, env, diff, ctxt,
+			      &detailed_status);
+
+    string name = args->elf1.name;
+    if (status == abigail::tools_utils::ABIDIFF_OK)
+      pretty_output += "==== SELF CHECK SUCCEEDED for '"+ name + "' ====\n";
+    else if ((status & abigail::tools_utils::ABIDIFF_ABI_CHANGE)
+	     ||( diff && diff->has_net_changes()))
+      {
+	// There is an ABI change, tell the user about it.
+	diff->report(out, /*prefix=*/"  ");
+
+	pretty_output +=
+	  string("======== comparing'") + name +
+	  "' to itself wrongly yielded result: ===========\n"
+	  + out.str()
+	  + "===SELF CHECK FAILED for '"+ name + "'\n";
+      }
+
+    // If an error happened while comparing the two binaries, tell the
+    // user about it.
+    if (status & abigail::tools_utils::ABIDIFF_ERROR)
+      {
+	string diagnostic =
+	  abigail::dwarf_reader::status_to_diagnostic_string(detailed_status);
+
+	if (!diagnostic.empty())
+	  {
+	    string name = args->elf1.name;
+
+	    pretty_output +=
+	      "==== Error happened during self check of " + name + ": ====\n";
+
+	    pretty_output += diagnostic;
+
+	    pretty_output +=
+	      "==== SELF CHECK FAILED for " + name + " ====\n";
+	  }
+      }
+  }
+}; // end class self_compare
+
+/// Convenience typedef for a shared_ptr of @ref compare_task.
+typedef shared_ptr<self_compare_task> self_compare_task_sptr;
+
 /// This function is a sub-routine of create_maps_of_package_content.
 ///
 /// It's called during the walking of the directory tree containing
@@ -2220,6 +2499,21 @@  prepare_packages(package_sptr &first_package,
   return first_pkg_prepare->is_ok && second_pkg_prepare->is_ok;
 }
 
+/// Prepare one package for the sake of comparing it to its ABIXML
+/// representation.
+///
+/// The preparation entails unpacking the content of the package into
+/// a temporary directory and mapping its content.
+///
+/// @param pkg the package to prepare.
+///
+/// @param opts the options provided by the user.
+///
+/// @return true iff the preparation succeeded.
+static bool
+prepare_package(package_sptr& pkg, options &opts)
+{return extract_package_and_map_its_content(pkg, opts);}
+
 /// Compare the added sizes of an ELF pair (specified by a comparison
 /// task that compares two ELF files) against the added sizes of a
 /// second ELF pair.
@@ -2244,8 +2538,8 @@  elf_size_is_greater(const task_sptr &task1,
   compare_task_sptr t1 = dynamic_pointer_cast<compare_task>(task1);
   compare_task_sptr t2 = dynamic_pointer_cast<compare_task>(task2);
 
-  off_t s1 = t1->args->elf1.size + t1->args->elf2.size;
-  off_t s2 = t2->args->elf1.size + t2->args->elf2.size;
+  off_t s1 = t1->args ? t1->args->elf1.size + t1->args->elf2.size : 0;
+  off_t s2 = t2->args ? t2->args->elf1.size + t2->args->elf2.size: 0;
 
   return s1 > s2;
 }
@@ -2483,6 +2777,112 @@  compare_prepared_userspace_packages(package& first_package,
   return status;
 }
 
+/// In the context of the unpacked content of a given package, compare
+/// the binaries inside the package against their ABIXML
+/// representation.  This should yield the empty set.
+///
+/// @param pkg (unpacked) package
+///
+/// @param diff the representation of the changes between the binaries
+/// and their ABIXML.  This should obviously be the empty set.
+///
+/// @param diff a textual representation of the diff.
+///
+/// @param opts the options provided by the user.
+static abidiff_status
+self_compare_prepared_userspace_package(package&	pkg,
+					abi_diff&	diff,
+					options&	opts)
+{
+  abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
+  abigail::workers::queue::tasks_type self_compare_tasks;
+  string pkg_name = pkg.base_name();
+
+  // Setting debug-info path of libraries
+  string debug_dir, relative_debug_path = "/usr/lib/debug/";
+  if (!pkg.debug_info_packages().empty())
+    debug_dir =
+      pkg.debug_info_packages().front()->extracted_dir_path() +
+      relative_debug_path;
+
+  suppressions_type supprs;
+  for (map<string, elf_file_sptr>::iterator it =
+	 pkg.path_elf_file_sptr_map().begin();
+       it != pkg.path_elf_file_sptr_map().end();
+       ++it)
+    {
+      if (it != pkg.path_elf_file_sptr_map().end()
+	  && (it->second->type == abigail::dwarf_reader::ELF_TYPE_DSO
+	      || it->second->type == abigail::dwarf_reader::ELF_TYPE_EXEC
+              || it->second->type == abigail::dwarf_reader::ELF_TYPE_PI_EXEC
+	      || it->second->type == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE))
+	{
+	  if (it->second->type != abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)
+	    {
+	      compare_args_sptr args
+		(new compare_args(*it->second,
+				  debug_dir,
+				  supprs,
+				  *it->second,
+				  debug_dir,
+				  supprs,
+				  opts));
+	      self_compare_task_sptr t(new self_compare_task(args));
+	      self_compare_tasks.push_back(t);
+	    }
+	}
+    }
+
+  if (self_compare_tasks.empty())
+    {
+      maybe_erase_temp_dirs(pkg, pkg, opts);
+      return abigail::tools_utils::ABIDIFF_OK;
+    }
+
+  // Larger elfs are processed first, since it's usually safe to assume
+  // their debug-info is larger as well, but the results are still
+  // in a map ordered by looked up in elf.name order.
+  std::sort(self_compare_tasks.begin(),
+	    self_compare_tasks.end(),
+	    elf_size_is_greater);
+
+  // There's no reason to spawn more workers than there are ELF pairs
+  // to be compared.
+  size_t num_workers = (opts.parallel
+			? std::min(opts.num_workers, self_compare_tasks.size())
+			: 1);
+  assert(num_workers >= 1);
+
+  comparison_done_notify notifier(diff);
+  abigail::workers::queue comparison_queue(num_workers, notifier);
+
+  // Compare all the binaries, in parallel and then wait for the
+  // comparisons to complete.
+  comparison_queue.schedule_tasks(self_compare_tasks);
+  comparison_queue.wait_for_workers_to_complete();
+
+  // Get the set of comparison tasks that were perform and sort them.
+  queue::tasks_type& done_tasks = comparison_queue.get_completed_tasks();
+  std::sort(done_tasks.begin(), done_tasks.end(), elf_size_is_greater);
+
+  // Print the reports of the comparison to standard output.
+  for (queue::tasks_type::const_iterator i = done_tasks.begin();
+       i != done_tasks.end();
+       ++i)
+    {
+      self_compare_task_sptr t = dynamic_pointer_cast<self_compare_task>(*i);
+      if (t)
+	cout << t->pretty_output;
+    }
+
+  // Erase temporary directory tree we might have left behind.
+  maybe_erase_temp_dirs(pkg, pkg, opts);
+
+  status = notifier.status;
+
+  return status;
+}
+
 /// Compare the ABI of two prepared packages that contain linux kernel
 /// binaries.
 ///
@@ -2626,6 +3026,29 @@  compare_prepared_package(package& first_package, package& second_package,
   return status;
 }
 
+/// Compare binaries in a package against their ABIXML
+/// representations.
+///
+/// @param pkg the package to consider.
+///
+/// @param diff the textual representation of the resulting
+/// comparison.
+///
+/// @param opts the options provided by the user
+///
+/// @return the status of the comparison.
+static abidiff_status
+self_compare_prepared_package(package& pkg,
+			      abi_diff& diff,
+			      options& opts)
+{
+  abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
+
+  status = self_compare_prepared_userspace_package(pkg, diff, opts);
+
+  return status;
+}
+
 /// Compare the ABI of two packages
 ///
 /// @param first_package the first package to consider.
@@ -2658,6 +3081,24 @@  compare(package_sptr& first_package, package_sptr& second_package,
   return compare_prepared_package(*first_package, *second_package, diff, opts);
 }
 
+/// Compare binaries in a package against their ABIXML
+/// representations.
+///
+/// @param pkg the package to consider.
+///
+/// @param opts the options provided by the user
+///
+/// @return the status of the comparison.
+static abidiff_status
+compare_to_self(package_sptr& pkg, options& opts)
+{
+  if (!prepare_package(pkg, opts))
+    return abigail::tools_utils::ABIDIFF_ERROR;
+
+  abi_diff diff;
+  return self_compare_prepared_package(*pkg, diff, opts);
+}
+
 /// Compare the ABI of two packages.
 ///
 /// @param first_package the first package to consider.
@@ -2830,6 +3271,8 @@  parse_command_line(int argc, char* argv[], options& opts)
 	opts.parallel = false;
       else if (!strcmp(argv[i], "--show-identical-binaries"))
 	opts.show_identical_binaries = true;
+      else if (!strcmp(argv[i], "--self-check"))
+	opts.self_check = true;
       else if (!strcmp(argv[i], "--suppressions")
 	       || !strcmp(argv[i], "--suppr"))
 	{
@@ -2976,10 +3419,31 @@  main(int argc, char* argv[])
     return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
 	    | abigail::tools_utils::ABIDIFF_ERROR);
 
-  if (opts.package1.empty() || opts.package2.empty())
+  bool need_just_one_input_package = opts.self_check;
+
+  if (need_just_one_input_package)
+    {
+      bool bail_out = false;
+      if (!opts.package2.empty())
+	{
+	  // We don't need the second package, we'll ignore it later
+	  // down below.
+	  ;
+	}
+      if (opts.package1.empty())
+	{
+	  // We need at least one package to work with!
+	  emit_prefix("abipkgdiff", cerr)
+	    << "missing input package\n";
+	  if (bail_out)
+	    return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
+		    | abigail::tools_utils::ABIDIFF_ERROR);
+	}
+    }
+  else if(opts.package1.empty() || opts.package2.empty())
     {
       emit_prefix("abipkgdiff", cerr)
-	<< "Please enter two packages to compare" << "\n";
+	<< "please enter two packages to compare" << "\n";
       return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
 	      | abigail::tools_utils::ABIDIFF_ERROR);
     }
@@ -3036,7 +3500,8 @@  main(int argc, char* argv[])
   switch (first_package->type())
     {
     case abigail::tools_utils::FILE_TYPE_RPM:
-      if (second_package->type() != abigail::tools_utils::FILE_TYPE_RPM)
+      if (!second_package->path().empty()
+	  && second_package->type() != abigail::tools_utils::FILE_TYPE_RPM)
 	{
 	  base_name(opts.package2, package_name);
 	  emit_prefix("abipkgdiff", cerr)
@@ -3063,7 +3528,8 @@  main(int argc, char* argv[])
 	    }
 
 	  if (first_package->debug_info_packages().empty()
-	      || second_package->debug_info_packages().empty())
+	      || (!second_package->path().empty()
+		  && second_package->debug_info_packages().empty()))
 	    {
 	      emit_prefix("abipkgdiff", cerr)
 		<< "a Linux Kernel package must be accompanied with its "
@@ -3086,7 +3552,8 @@  main(int argc, char* argv[])
       break;
 
     case abigail::tools_utils::FILE_TYPE_DEB:
-      if (second_package->type() != abigail::tools_utils::FILE_TYPE_DEB)
+      if (!second_package->path().empty()
+	  && second_package->type() != abigail::tools_utils::FILE_TYPE_DEB)
 	{
 	  base_name(opts.package2, package_name);
 	  emit_prefix("abipkgdiff", cerr)
@@ -3097,7 +3564,8 @@  main(int argc, char* argv[])
       break;
 
     case abigail::tools_utils::FILE_TYPE_DIR:
-      if (second_package->type() != abigail::tools_utils::FILE_TYPE_DIR)
+      if (!second_package->path().empty()
+	  && second_package->type() != abigail::tools_utils::FILE_TYPE_DIR)
 	{
 	  base_name(opts.package2, package_name);
 	  emit_prefix("abipkgdiff", cerr)
@@ -3108,7 +3576,8 @@  main(int argc, char* argv[])
       break;
 
     case abigail::tools_utils::FILE_TYPE_TAR:
-      if (second_package->type() != abigail::tools_utils::FILE_TYPE_TAR)
+      if (!second_package->path().empty()
+	  && second_package->type() != abigail::tools_utils::FILE_TYPE_TAR)
 	{
 	  base_name(opts.package2, package_name);
 	  emit_prefix("abipkgdiff", cerr)
@@ -3126,5 +3595,8 @@  main(int argc, char* argv[])
 	      | abigail::tools_utils::ABIDIFF_ERROR);
     }
 
+  if (opts.self_check)
+    return compare_to_self(first_package, opts);
+
   return compare(first_package, second_package, opts);
 }