Initial support of the serialization of the KMI of a Linux Kernel Tree
authorDodji Seketeli <dodji@redhat.com>
Thu, 6 Apr 2017 10:00:51 +0000 (12:00 +0200)
committerDodji Seketeli <dodji@redhat.com>
Mon, 3 Jul 2017 15:45:41 +0000 (17:45 +0200)
We have the kmidiff program that takes two Linux Kernel trees
containing the vmlinux binary and its modules and compare their Kernel
Module Interface, aka KMI.

We need to be able to serialize (save in a file) a representation of
that KMI.  We need to load that KMI back, and compare two serialized
KMIs.

This patch implements the serialization of the KMI of a Linux Kernel
tree.  It actually serializes an instance of abigail::ir::corpus_group
that is a collection of instances of abigail::ir::corpus.  The KMI of
each individual binary (vmlinux or kernel module) is represented by
one abigail::ir::corpus.  All the corpora share the same definitions
of types and decls, whenever that makes sense.

The patch thus factorizes the routines used to walk a Linux kernel out
of the kmidiff program.  These routines are then re-used in the abidw
program to make it walk a Linux kernel tree (when the --linux-tree
option is provided), load the vmlinux and module binaries as an
instance of abigail::corpus_group and serialize it out into an output
stream.

* include/abg-tools-utils.h (check_dir)
(get_binary_paths_from_kernel_dist)
(build_corpus_group_from_kernel_dist_under): Declare new
functions.  The last two functions are being moved from
tools/kmidiff.cc so that they can be re-used.
* include/abg-writer.h (write_corpus): Declare one overload that
takes a write_context parameter.
(write_corpus_group): Declare three overloads of this new function.
* src/abg-tools-utils.cc (check_dir): Define new function.
(load_generate_apply_suppressions, is_vmlinux, is_kernel_module)
(find_vmlinux_and_module_paths)
(get_binary_paths_from_kernel_dist)
(build_corpus_group_from_kernel_dist_under): Define new functions.
* src/abg-writer.cc (write_context::set_annotate): Define new
member function.
(write_corpus): Add an overload that takes a write_context.  Adapt
the existing overload to make it use this new one.
(write_corpus_group): Define this new function and two additional
overloads for it.
* tools/kmidiff.cc (set_suppressions, is_vmlinux)
(is_kernel_module, find_vmlinux_and_module_paths)
(get_binary_paths_from_kernel_dist)
(build_corpus_group_from_kernel_dist_under): Remove.
(main): Adjust the call to
build_corpus_group_from_kernel_dist_under as its arguments are now
adapted since it's been factorized out into abg-tools-utils.h.
* tools/abidw.cc (options::corpus_group_for_linux): Define new
data member.
(options::options): Adjust.
(display_usage): Add help strings for the new --linux-tree option.
(load_corpus_and_write_abixml): Factorize this function out of the
main function.
(load_kernel_corpus_group_and_write_abixml): Define new function.
(main): Use the factorized load_corpus_and_write_abixml and the
new load_corpus_and_write_abixml functions.
* tests/test-read-write.cc: Adjust.
* doc/manuals/abidw.rst: Add documentation for the new
--linux-tree option.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
doc/manuals/abidw.rst
include/abg-tools-utils.h
include/abg-writer.h
src/abg-tools-utils.cc
src/abg-writer.cc
tests/test-read-write.cc
tools/abidw.cc
tools/kmidiff.cc

index fd9299e12bd2b002bae3292ca0b6984d582f18c6..e2a7685003f457cee5d879d7c87ed7d43211f7a7 100644 (file)
@@ -8,6 +8,16 @@ also includes a representation of the globally defined ELF symbols of
 the file.  The input shared library must contain associated debug
 information in `DWARF`_ format.
 
+When given the ``--linux-tree`` option, this program can also handle a
+Linux kernel tree.  That is, a directory tree that contains both the
+vmlinux binary and Linux kernel modules.  It analyses those Linux
+kernel binaries and emits an XML representation of the interface
+between the kernel and its module, to standard output.  In this case,
+we don't call it an ABI, but a KMI (Kernel Module Interface).  The
+emitted KMI includes all the globally defined functions and variables,
+along with a complete representation of their types.  The input
+binaries must contain associated debug information in `DWARF`_ format.
+
 Invocation
 ==========
 
@@ -85,6 +95,28 @@ Options
     provided -- then the entire KMI, that is, all publicly defined and
     exported functions and global variables by the Linux Kernel
     binaries is emitted.
+    
+  * ``--linux-tree | --lt``
+
+    Make ``abidw`` to consider the input path as a path to a directory
+    containing the vmlinux binary as several kernel modules binaries.
+    In that case, this program emits the representation of the Kernel
+    Module Interface (KMI) on the standard output.
+
+    Below is an example of usage of ``abidw`` on a Linux Kernel tree.
+
+    First, checkout a Linux kernel source tree and build it.  Then
+    install the kernel modules in a directory somewhere.  Copy the
+    vmlinux binary into that directory too.  And then serialize the
+    KMI of that kernel to disk, using ``abidw``: ::
+
+       $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
+       $ cd linux && git checkout v4.5
+       $ make allyesconfig all
+       $ mkdir build-output
+       $ make INSTALL_MOD_PATH=./build-output modules_install 
+       $ cp vmlinux build-output/modules/4.5.0
+       $ abidw --linux-tree build-output/modules/4.5.0 > build-output/linux-4.5.0.kmi
 
   * ``--headers-dir | --hd`` <headers-directory-path-1>
 
index 343ca396b6e3a391a7ce5968bc300b04e2d6e3cb..28289398a89d0e9b61e5b3a7842a8f0f782b64b0 100644 (file)
@@ -55,6 +55,7 @@ bool ensure_dir_path_created(const string&);
 bool ensure_parent_dir_created(const string&);
 ostream& emit_prefix(const string& prog_name, ostream& out);
 bool check_file(const string& path, ostream& out, const string& prog_name = "");
+bool check_dir(const string& path, ostream& out, const string& prog_name="");
 bool string_ends_with(const string&, const string&);
 bool string_begins_with(const string&, const string&);
 bool string_is_ascii(const string&);
@@ -67,6 +68,17 @@ bool
 gen_suppr_spec_from_kernel_abi_whitelist(const string& abi_whitelist_path,
                                         suppr::suppressions_type& s);
 
+bool
+get_binary_paths_from_kernel_dist(const string&        dist_root,
+                                 string&               vmlinux_path,
+                                 vector<string>&       module_paths,
+                                 string&               debug_info_root_path);
+
+bool
+get_binary_paths_from_kernel_dist(const string&        dist_root,
+                                 string&               vmlinux_path,
+                                 vector<string>&       module_paths);
+
 string
 get_default_system_suppression_file_path();
 
@@ -235,6 +247,14 @@ file_is_kernel_debuginfo_package(string& file_path,
 std::tr1::shared_ptr<char>
 make_path_absolute(const char*p);
 
+corpus_group_sptr
+build_corpus_group_from_kernel_dist_under(const string&                root,
+                                         vector<string>&               suppr_paths,
+                                         vector<string>&               kabi_wl_paths,
+                                         suppr::suppressions_type&     supprs,
+                                         bool                          verbose,
+                                         environment_sptr&             env);
+
 extern const char* PRIVATE_TYPES_SUPPR_SPEC_NAME;
 }// end namespace tools_utils
 
index b393e285f16ff7f1238eaffa1bb1d00452e75b1e..e86175f75b83e09acb569d82eee2212352317540 100644 (file)
@@ -66,6 +66,13 @@ bool
 write_corpus_to_archive(const corpus_sptr corp,
                        const bool annotate = false);
 
+bool
+write_corpus(const corpus_sptr corpus,
+            unsigned           indent,
+            write_context&     ctxt,
+            std::ostream&      out,
+            const bool annotate = false);
+
 bool
 write_corpus(const corpus_sptr corpus,
             unsigned           indent,
@@ -78,6 +85,25 @@ write_corpus(const corpus_sptr       corpus,
             const string&      path,
             const bool annotate = false);
 
+bool
+write_corpus_group(const corpus_group_sptr&    group,
+                  unsigned                     indent,
+                  write_context&               ctxt,
+                  std::ostream&                out,
+                  const bool                   annotate = false);
+
+bool
+write_corpus_group(const corpus_group_sptr&    group,
+                  unsigned                     indent,
+                  std::ostream&                out,
+                  const bool                   annotate = false);
+
+bool
+write_corpus_group(const corpus_group_sptr&    group,
+                  unsigned                     indent,
+                  const string&                path,
+                  const bool                   annotate = false);
+
 }// end namespace xml_writer
 }// end namespace abigail
 
index 3ec74cd7c4ed67ff9db407cb5945faa784471e91..dcb97540fc7becd3b84d62b3d4fcbe44a9ef18d2 100644 (file)
@@ -35,6 +35,7 @@
 #include <iostream>
 #include <sstream>
 
+#include "abg-dwarf-reader.h"
 #include "abg-internal.h"
 // <headers defining libabigail's API go under here>
 ABG_BEGIN_EXPORT_DECLARATIONS
@@ -444,6 +445,34 @@ check_file(const string& path, ostream& out, const string& prog_name)
   return true;
 }
 
+/// Check if a given path exists, is readable and is a directory.
+///
+/// @param path the path to consider.
+///
+/// @param out the out stream to report errors to.
+///
+/// @param prog_name the program name on behalf of which to report the
+/// error, if any.
+///
+/// @return true iff @p path exists and is for a directory.
+bool
+check_dir(const string& path, ostream& out, const string& prog_name)
+{
+  if (!file_exists(path))
+    {
+      emit_prefix(prog_name, out) << "path " << path << " does not exist\n";
+      return false;
+    }
+
+  if (!is_dir(path))
+    {
+      emit_prefix(prog_name, out) << path << " is not a directory\n";
+      return false;
+    }
+
+  return true;
+}
+
 /// Test if a given string ends with a particular suffix.
 ///
 /// @param str the string to consider.
@@ -1305,6 +1334,349 @@ load_default_user_suppressions(suppr::suppressions_type& supprs)
   read_suppressions(default_user_suppr_path, supprs);
 }
 
+/// If we were given suppression specification files or kabi whitelist
+/// files, this function parses those, come up with suppression
+/// specifications as a result, and set them to the read context.
+///
+/// @param read_ctxt the read context to consider.
+///
+/// @param suppr_paths paths to suppression specification files that
+/// we were given.  If empty, it means we were not given any
+/// suppression specification path.
+///
+/// @param kabi_whitelist_paths paths to kabi whitelist files that we
+/// were given.  If empty, it means we were not given any kabi
+/// whitelist.
+///
+/// @param supprs the suppressions specifications resulting from
+/// parsing the suppression specification files at @p suppr_paths and
+/// the kabi whitelist at @p kabi_whitelist_paths.
+///
+/// @param opts the options to consider.
+static void
+load_generate_apply_suppressions(dwarf_reader::read_context &read_ctxt,
+                                vector<string>& suppr_paths,
+                                vector<string>& kabi_whitelist_paths,
+                                suppressions_type& supprs)
+{
+  if (supprs.empty())
+    {
+      for (vector<string>::const_iterator i = suppr_paths.begin();
+          i != suppr_paths.end();
+          ++i)
+       read_suppressions(*i, supprs);
+
+      for (vector<string>::const_iterator i =
+            kabi_whitelist_paths.begin();
+          i != kabi_whitelist_paths.end();
+          ++i)
+       gen_suppr_spec_from_kernel_abi_whitelist(*i, supprs);
+    }
+
+  abigail::dwarf_reader::add_read_context_suppressions(read_ctxt, supprs);
+}
+
+/// Test if an FTSENT pointer (resulting from fts_read) represents the
+/// vmlinux binary.
+///
+/// @param entry the FTSENT to consider.
+///
+/// @return true iff @p entry is for a vmlinux binary.
+static bool
+is_vmlinux(const FTSENT *entry)
+{
+  if (entry == NULL
+      || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
+      || entry->fts_info == FTS_ERR
+      || entry->fts_info == FTS_NS)
+    return false;
+
+  string fname = entry->fts_name;
+
+  if (fname == "vmlinux")
+    {
+      string dirname;
+      dir_name(entry->fts_path, dirname);
+      if (string_ends_with(dirname, "compressed"))
+       return false;
+
+      return true;
+    }
+
+  return false;
+}
+
+/// Test if an FTSENT pointer (resulting from fts_read) represents a a
+/// linux kernel module binary.
+///
+/// @param entry the FTSENT to consider.
+///
+/// @return true iff @p entry is for a linux kernel module binary.
+static bool
+is_kernel_module(const FTSENT *entry)
+{
+    if (entry == NULL
+      || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
+      || entry->fts_info == FTS_ERR
+      || entry->fts_info == FTS_NS)
+    return false;
+
+  string fname = entry->fts_name;
+  if (string_ends_with(fname, ".ko")
+      || string_ends_with(fname, ".ko.xz")
+      || string_ends_with(fname, ".ko.gz"))
+    return true;
+
+  return false;
+}
+
+/// Find a vmlinux and its kernel modules in a given directory tree.
+///
+/// @param from the directory tree to start looking from.
+///
+/// @param vmlinux_path output parameter.  This is set to the path
+/// where the vmlinux binary is found.  This is set iff the returns
+/// true.
+///
+/// @param module_paths output parameter.  This is set to the paths of
+/// the linux kernel module binaries.
+///
+/// @return true iff at least the vmlinux binary was found.
+static bool
+find_vmlinux_and_module_paths(const string&    from,
+                             string            &vmlinux_path,
+                             vector<string>    &module_paths)
+{
+  char* path[] = {const_cast<char*>(from.c_str()), 0};
+
+  FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
+  if (!file_hierarchy)
+    return false;
+
+  bool found_vmlinux = false;
+  FTSENT *entry;
+  while ((entry = fts_read(file_hierarchy)))
+    {
+      // Skip descendents of symbolic links.
+      if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
+       {
+         fts_set(file_hierarchy, entry, FTS_SKIP);
+         continue;
+       }
+
+      if (!found_vmlinux && is_vmlinux(entry))
+       {
+         vmlinux_path = entry->fts_path;
+         found_vmlinux = true;
+       }
+      else if (is_kernel_module(entry))
+       module_paths.push_back(entry->fts_path);
+    }
+
+  fts_close(file_hierarchy);
+
+  return found_vmlinux;
+}
+
+/// Get the paths of the vmlinux and kernel module binaries under
+/// given directory.
+///
+/// @param dist_root the directory under which to look for.
+///
+/// @param vmlinux_path output parameter.  The path of the vmlinux
+/// binary that was found.
+///
+/// @param module_paths output parameter.  The paths of the kernel
+/// module binaries that were found.
+///
+/// @param debug_info_root_path output parameter. If a debug info
+/// sub-directory is found under @p dist_root, it's set to this
+/// parameter.
+///
+/// @return true if at least the path to the vmlinux binary was found.
+bool
+get_binary_paths_from_kernel_dist(const string&        dist_root,
+                                 string&               vmlinux_path,
+                                 vector<string>&       module_paths,
+                                 string&               debug_info_root_path)
+{
+  if (!dir_exists(dist_root))
+    return false;
+
+  // For now, we assume either an Enterprise Linux or a Fedora kernel
+  // distribution directory.
+  //
+  // We also take into account split debug info package for these.  In
+  // this case, the content split debug info package is installed
+  // under the 'dist_root' directory as well, and its content is
+  // accessible from <dist_root>/usr/lib/debug directory.
+
+  string kernel_modules_root;
+  string debug_info_root;
+  if (dir_exists(dist_root + "/lib/modules"))
+    {
+      dist_root + "/lib/modules";
+      debug_info_root = dist_root + "/usr/lib/debug";
+    }
+
+  if (dir_is_empty(debug_info_root))
+    debug_info_root.clear();
+
+  bool found = false;
+  string from = !debug_info_root.empty() ? debug_info_root : dist_root;
+  if (find_vmlinux_and_module_paths(from, vmlinux_path, module_paths))
+    found = true;
+
+  debug_info_root_path = debug_info_root;
+
+  return found;
+}
+
+/// Get the paths of the vmlinux and kernel module binaries under
+/// given directory.
+///
+/// @param dist_root the directory under which to look for.
+///
+/// @param vmlinux_path output parameter.  The path of the vmlinux
+/// binary that was found.
+///
+/// @param module_paths output parameter.  The paths of the kernel
+/// module binaries that were found.
+///
+/// @return true if at least the path to the vmlinux binary was found.
+bool
+get_binary_paths_from_kernel_dist(const string&        dist_root,
+                                 string&               vmlinux_path,
+                                 vector<string>&       module_paths)
+{
+  string debug_info_root_path;
+  return get_binary_paths_from_kernel_dist(dist_root,
+                                          vmlinux_path,
+                                          module_paths,
+                                          debug_info_root_path);
+}
+
+/// Walk a given directory and build an instance of @ref corpus_group
+/// from the vmlinux kernel binary and the linux kernel modules found
+/// under that directory and under its sub-directories, recursively.
+///
+/// The main corpus of the @ref corpus_group is made of the vmlinux
+/// binary.  The other corpora are made of the linux kernel binaries.
+///
+/// @param root the path of the directory under which vmlinux and its
+/// kernel modules are to be found.
+///
+/// @param opts the options to use during the search.
+///
+/// @param env the environment to create the corpus_group in.
+corpus_group_sptr
+build_corpus_group_from_kernel_dist_under(const string&        root,
+                                         vector<string>&       suppr_paths,
+                                         vector<string>&       kabi_wl_paths,
+                                         suppressions_type&    supprs,
+                                         bool                  verbose,
+                                         environment_sptr&     env)
+{
+  corpus_group_sptr result;
+  string vmlinux;
+  vector<string> modules;
+  string debug_info_root_path;
+
+  if (verbose)
+    std::cout << "Analysing kernel dist root '"
+             << root << "' ... " << std::flush;
+
+  bool got_binary_paths =
+    get_binary_paths_from_kernel_dist(root, vmlinux, modules,
+                                     debug_info_root_path);
+
+  if (verbose)
+    std::cout << "DONE\n";
+
+  if (got_binary_paths)
+    {
+      shared_ptr<char> di_root =
+       make_path_absolute(debug_info_root_path.c_str());
+      char *di_root_ptr = di_root.get();
+      abigail::dwarf_reader::status status = abigail::dwarf_reader::STATUS_OK;
+      corpus_group_sptr group;
+      if (!vmlinux.empty())
+       {
+         dwarf_reader::read_context_sptr ctxt =
+           dwarf_reader::create_read_context(vmlinux, &di_root_ptr, env.get(),
+                                             /*read_all_types=*/false,
+                                             /*linux_kernel_mode=*/true);
+
+         load_generate_apply_suppressions(*ctxt, suppr_paths,
+                                          kabi_wl_paths, supprs);
+
+         // If we have been given a whitelist of functions and
+         // variable symbols to look at, then we can avoid loading
+         // and analyzing the ELF symbol table.
+         bool do_ignore_symbol_table = !kabi_wl_paths.empty();
+         set_ignore_symbol_table(*ctxt, do_ignore_symbol_table);
+
+         group.reset(new corpus_group(env.get(), root));
+
+         set_read_context_corpus_group(*ctxt, group);
+
+         if (verbose)
+           std::cout << "reading kernel binary '"
+                     << vmlinux << "' ... " << std::flush;
+
+         // Read the vmlinux corpus and add it to the group.
+         read_and_add_corpus_to_group_from_elf(*ctxt, *group, status);
+
+           if (verbose)
+             std::cout << " DONE\n";
+       }
+
+      if (!group->is_empty())
+       {
+         // Now add the corpora of the modules to the corpus group.
+         int total_nb_modules = modules.size();
+         int cur_module_index = 1;
+         for (vector<string>::const_iterator m = modules.begin();
+              m != modules.end();
+              ++m, ++cur_module_index)
+           {
+             if (verbose)
+               std::cout << "reading module '"
+                         << *m << "' ("
+                         << cur_module_index
+                         << "/" << total_nb_modules
+                         << ") ... " << std::flush;
+
+             dwarf_reader::read_context_sptr module_ctxt =
+               dwarf_reader::create_read_context(*m, &di_root_ptr, env.get(),
+                                                 /*read_all_types=*/false,
+                                                 /*linux_kernel_mode=*/true);
+
+             // If we have been given a whitelist of functions and
+             // variable symbols to look at, then we can avoid loading
+             // and analyzing the ELF symbol table.
+             bool do_ignore_symbol_table = !kabi_wl_paths.empty();
+
+             set_ignore_symbol_table(*module_ctxt, do_ignore_symbol_table);
+
+             load_generate_apply_suppressions(*module_ctxt, suppr_paths,
+                                              kabi_wl_paths, supprs);
+
+             set_read_context_corpus_group(*module_ctxt, group);
+
+             read_and_add_corpus_to_group_from_elf(*module_ctxt,
+                                                   *group, status);
+             if (verbose)
+               std::cout << " DONE\n";
+           }
+
+         result = group;
+       }
+    }
+
+  return result;
+}
+
 }//end namespace tools_utils
 
 using abigail::ir::function_decl;
index b700d2e34056df7256354659997141741b3f3881..0d884e2c7381d739384af6d2b39d5ac01b5fa3eb 100644 (file)
@@ -212,6 +212,13 @@ public:
   get_annotate()
   {return m_annotate;}
 
+  /// Setter of the annotation option.
+  ///
+  /// @param f the new value of the flag.
+  void
+  set_annotate(bool f)
+  {m_annotate = f;}
+
   /// Getter of the @ref id_manager.
   ///
   /// @return the @ref id_manager used by the current instance of @ref
@@ -3906,6 +3913,8 @@ write_corpus_to_archive(const corpus_sptr corp, const bool annotate)
 ///
 /// @param indent the number of white space indentation to use.
 ///
+/// @param ctxt the write context to use.
+///
 /// @param out the output stream to serialize the ABI corpus to.
 ///
 /// @param annotate whether ABIXML output should be annotated.
@@ -3914,14 +3923,13 @@ write_corpus_to_archive(const corpus_sptr corp, const bool annotate)
 bool
 write_corpus(const corpus_sptr corpus,
             unsigned           indent,
+            write_context&     ctxt,
             std::ostream&      out,
             const bool annotate)
 {
   if (!corpus)
     return false;
 
-  write_context ctxt(corpus->get_environment(), out, annotate);
-
   do_indent_to_level(ctxt, indent, 0);
   out << "<abi-corpus";
   if (!corpus->get_path().empty())
@@ -3941,7 +3949,11 @@ write_corpus(const corpus_sptr   corpus,
 
   out << ">\n";
 
-  // Write the list of needed corpora
+  // Write the list of needed corpora.
+
+  bool saved_annotate = ctxt.get_annotate();
+  ctxt.set_annotate(annotate);
+
   if (!corpus->get_needed().empty())
     {
       do_indent_to_level(ctxt, indent, 1);
@@ -3993,9 +4005,119 @@ write_corpus(const corpus_sptr  corpus,
   do_indent_to_level(ctxt, indent, 0);
   out << "</abi-corpus>\n";
 
+  ctxt.set_annotate(saved_annotate);
+
+  return true;
+}
+
+/// Serialize an ABI corpus to a single native xml document.  The root
+/// note of the resulting XML document is 'abi-corpus'.
+///
+/// @param corpus the corpus to serialize.
+///
+/// @param indent the number of white space indentation to use.
+///
+/// @param out the output stream to serialize the ABI corpus to.
+///
+/// @param annotate whether ABIXML output should be annotated.
+///
+/// @return true upon successful completion, false otherwise.
+bool
+write_corpus(const corpus_sptr corpus,
+            unsigned           indent,
+            std::ostream&      out,
+            const bool annotate)
+{
+  if (!corpus)
+    return false;
+
+  write_context ctxt(corpus->get_environment(), out, annotate);
+
+  return write_corpus(corpus, indent, ctxt, out, annotate);
+}
+
+/// Serialize an ABI corpus group to a single native xml document.
+/// The root note of the resulting XML document is 'abi-corpus-group'.
+///
+/// @param group the corpus group to serialize.
+///
+/// @param indent the number of white space indentation to use.
+///
+/// @param ctxt the write context to use.
+///
+/// @param out the output stream to serialize the ABI corpus to.
+///
+/// @param annotate whether ABIXML output should be annotated.
+///
+/// @return true upon successful completion, false otherwise.
+bool
+write_corpus_group(const corpus_group_sptr&    group,
+                  unsigned                     indent,
+                  write_context&               ctxt,
+                  std::ostream&                out,
+                  const bool                   annotate)
+
+{
+  if (!group)
+    return false;
+
+  do_indent_to_level(ctxt, indent, 0);
+  out << "<abi-corpus-group";
+
+  if (!group->get_path().empty())
+    out << " path='" << xml::escape_xml_string(group->get_path()) << "'";
+
+  if (!group->get_architecture_name().empty())
+    out << " architecture='" << group->get_architecture_name()<< "'";
+
+  if (group->is_empty())
+    {
+      out << "/>\n";
+      return true;
+    }
+
+  out << ">\n";
+
+  // Write the list of corpora
+  for (corpus_group::corpora_type::const_iterator c =
+        group->get_corpora().begin();
+       c != group->get_corpora().end();
+       ++c)
+    write_corpus(*c, get_indent_to_level(ctxt, indent, 1), ctxt, out, annotate);
+
+  do_indent_to_level(ctxt, indent, 0);
+  out << "</abi-corpus-group>\n";
+
   return true;
 }
 
+/// Serialize an ABI corpus group to a single native xml document.
+/// The root note of the resulting XML document is 'abi-corpus-group'.
+///
+/// @param group the corpus group to serialize.
+///
+/// @param indent the number of white space indentation to use.
+///
+/// @param out the output stream to serialize the ABI corpus to.
+///
+/// @param annotate whether ABIXML output should be annotated.
+///
+/// @return true upon successful completion, false otherwise.
+bool
+write_corpus_group(const corpus_group_sptr&    group,
+                  unsigned                     indent,
+                  std::ostream&                out,
+                  const bool                   annotate)
+
+{
+  if (!group)
+    return false;
+
+  write_context ctxt(group->get_environment(), out, annotate);
+
+  return write_corpus_group(group, indent, ctxt, out, annotate);
+}
+
 /// Serialize an ABI corpus to a single native xml document.  The root
 /// note of the resulting XML document is 'abi-corpus'.
 ///
@@ -4003,7 +4125,7 @@ write_corpus(const corpus_sptr    corpus,
 ///
 /// @param indent the number of white space indentation to use.
 ///
-/// @param out the output file to serialize the ABI corpus to.
+/// @param path the output file to serialize the ABI corpus to.
 ///
 /// @param annotate whether ABIXML output should be annotated.
 ///
index 8a312af5b68fcb6b53adbb597142904d695a4d93..2061d00fe077d84524afc38f5a97b9e03a89312a 100644 (file)
@@ -53,7 +53,6 @@ using abigail::corpus_sptr;
 using abigail::xml_reader::read_translation_unit_from_file;
 using abigail::xml_reader::read_corpus_from_native_xml_file;
 using abigail::xml_writer::write_translation_unit;
-using abigail::xml_writer::write_corpus;
 
 using abigail::workers::queue;
 using abigail::workers::task;
index 12119d12cc8e6af345a77f6923568d06aa218460..ce6b9ae946783a2e7651b7de203ce1176125f793 100644 (file)
@@ -55,6 +55,7 @@ using abigail::tools_utils::emit_prefix;
 using abigail::tools_utils::temp_file;
 using abigail::tools_utils::temp_file_sptr;
 using abigail::tools_utils::check_file;
+using abigail::tools_utils::build_corpus_group_from_kernel_dist_under;
 using abigail::ir::environment_sptr;
 using abigail::ir::environment;
 using abigail::corpus;
@@ -93,6 +94,7 @@ struct options
   bool                 write_corpus_path;
   bool                 load_all_types;
   bool                 linux_kernel_mode;
+  bool                 corpus_group_for_linux;
   bool                 show_stats;
   bool                 noout;
   bool                 show_locs;
@@ -108,6 +110,7 @@ struct options
       write_corpus_path(true),
       load_all_types(),
       linux_kernel_mode(true),
+      corpus_group_for_linux(false),
       show_stats(),
       noout(),
       show_locs(true),
@@ -142,6 +145,8 @@ display_usage(const string& prog_name, ostream& out)
        "a Linux Kernel binary\n"
     << "  --kmi-whitelist|-w  path to a linux kernel "
     "abi whitelist\n"
+    << "  --linux-tree|--lt  emit the ABI for the union of a"
+    "vmlinux and its modules\n"
     << "  --abidiff  compare the loaded ABI against itself\n"
     << "  --annotate  annotate the ABI artifacts emitted in the output\n"
     << "  --stats  show statistics about various internal stuff\n"
@@ -216,6 +221,9 @@ parse_command_line(int argc, char* argv[], options& opts)
          opts.kabi_whitelist_paths.push_back(argv[j]);
          ++i;
        }
+      else if (!strcmp(argv[i], "--linux-tree")
+              || !strcmp(argv[i], "--lt"))
+       opts.corpus_group_for_linux = true;
       else if (!strcmp(argv[i], "--noout"))
        opts.noout = true;
       else if (!strcmp(argv[i], "--no-architecture"))
@@ -342,102 +350,37 @@ set_suppressions(read_context& read_ctxt, options& opts)
   add_read_context_suppressions(read_ctxt, opts.kabi_whitelist_supprs);
 }
 
-int
-main(int argc, char* argv[])
+/// Load an ABI @ref corpus (the internal representation of the ABI of
+/// a binary) and write it out as an abixml.
+///
+/// @param argv the arguments the program was called with.
+///
+/// @param env the environment the ABI artifacts are being created in.
+///
+/// @param context the context of the ELF reading.
+///
+/// @param opts the options of the program.
+///
+/// @return the exit code: 0 if everything went fine, non-zero
+/// otherwise.
+static int
+load_corpus_and_write_abixml(char* argv[],
+                            environment_sptr& env,
+                            read_context_sptr& context,
+                            const options& opts)
 {
-  options opts;
-
-  if (!parse_command_line(argc, argv, opts)
-      || (opts.in_file_path.empty()
-         && !opts.display_version))
-    {
-      if (!opts.wrong_option.empty())
-       emit_prefix(argv[0], cerr)
-         << "unrecognized option: " << opts.wrong_option << "\n";
-      display_usage(argv[0], cerr);
-      return 1;
-    }
-
-  if (opts.display_version)
-    {
-      string major, minor, revision;
-      abigail::abigail_get_library_version(major, minor, revision);
-      cout << major << "." << minor << "." << revision << "\n";
-      return 0;
-    }
+  int exit_code = 0;
 
-  assert(!opts.in_file_path.empty());
-  if (!abigail::tools_utils::check_file(opts.in_file_path, cerr, argv[0]))
-    return 1;
-
-  if (!maybe_check_suppression_files(opts))
-    return 1;
-
-  abigail::tools_utils::file_type type =
-    abigail::tools_utils::guess_file_type(opts.in_file_path);
-  if (type != abigail::tools_utils::FILE_TYPE_ELF
-      && type != abigail::tools_utils::FILE_TYPE_AR)
-    {
-      emit_prefix(argv[0], cerr)
-       << opts.in_file_path << " is not an ELF file\n";
-      return 1;
-    }
-
-  char* p = opts.di_root_path.get();
-  environment_sptr env(new environment);
+  read_context& ctxt = *context;
   corpus_sptr corp;
-  read_context_sptr c = create_read_context(opts.in_file_path,
-                                           &p, env.get(),
-                                           opts.load_all_types,
-                                           opts.linux_kernel_mode);
-  read_context& ctxt = *c;
-
-  set_show_stats(ctxt, opts.show_stats);
-  set_suppressions(ctxt, opts);
-  abigail::dwarf_reader::set_do_log(ctxt, opts.do_log);
-  if (!opts.kabi_whitelist_supprs.empty())
-    set_ignore_symbol_table(ctxt, true);
-
-  if (opts.check_alt_debug_info_path)
-    {
-      bool has_alt_di = false;
-      string alt_di_path;
-      abigail::dwarf_reader::status status =
-       abigail::dwarf_reader::has_alt_debug_info(ctxt,
-                                                 has_alt_di,
-                                                 alt_di_path);
-      if (status & abigail::dwarf_reader::STATUS_OK)
-       {
-         if (alt_di_path.empty())
-           ;
-         else
-           {
-             cout << "found the alternate debug info file";
-             if (opts.show_base_name_alt_debug_info_path)
-               {
-                 tools_utils::base_name(alt_di_path, alt_di_path);
-                 cout << " '" << alt_di_path << "'";
-               }
-             cout << "\n";
-           }
-         return 0;
-       }
-      else
-       {
-         emit_prefix(argv[0], cerr)
-           << "could not find alternate debug info file\n";
-         return 1;
-       }
-    }
-
   dwarf_reader::status s = dwarf_reader::STATUS_UNKNOWN;
   corp = read_corpus_from_elf(ctxt, s);
-  c.reset();
+  context.reset();
   if (!corp)
     {
       if (s == dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
        {
-         if (p == 0)
+         if (opts.di_root_path.get() == 0)
            {
              emit_prefix(argv[0], cerr)
                << "Could not read debug info from "
@@ -453,7 +396,8 @@ main(int argc, char* argv[])
            {
              emit_prefix(argv[0], cerr)
                << "Could not read debug info for '" << opts.in_file_path
-               << "' from debug info root directory '" << p
+               << "' from debug info root directory '"
+               << opts.di_root_path.get()
                << "'\n";
            }
        }
@@ -520,8 +464,170 @@ main(int argc, char* argv[])
          return 0;
        }
       else
-       abigail::xml_writer::write_corpus(corp, 0, cout, opts.annotate);
+       exit_code = !abigail::xml_writer::write_corpus(corp, 0, cout,
+                                                      opts.annotate);
+    }
+
+  return exit_code;
+}
+
+/// Load a corpus group representing the union of a Linux Kernel
+/// vmlinux binary and its modules, and emit an abixml representation
+/// for it.
+///
+/// @param argv the arguments this program was called with.
+///
+/// @param env the environment the ABI artifacts are created in.
+///
+/// @param opts the options this program was created with.
+///
+/// @return the exit code.  Zero if everything went well, non-zero
+/// otherwise.
+static int
+load_kernel_corpus_group_and_write_abixml(char* argv[],
+                                         environment_sptr& env,
+                                         options& opts)
+{
+  if (!(tools_utils::is_dir(opts.in_file_path) && opts.corpus_group_for_linux))
+    return 0;
+
+  int exit_code = 0;
+
+  suppressions_type supprs;
+  corpus_group_sptr group =
+    build_corpus_group_from_kernel_dist_under(opts.in_file_path,
+                                             opts.suppression_paths,
+                                             opts.kabi_whitelist_paths,
+                                             supprs, opts.do_log, env);
+
+  if (!group)
+    return exit_code;
+
+  if (!opts.write_architecture)
+    group->set_architecture_name("");
+  if (!opts.write_corpus_path)
+    group->set_path("");
+
+  if (!opts.out_file_path.empty())
+    {
+      ofstream of(opts.out_file_path.c_str(), std::ios_base::trunc);
+      if (!of.is_open())
+       {
+         emit_prefix(argv[0], cerr)
+           << "could not open output file '"
+           << opts.out_file_path << "'\n";
+         return 1;
+       }
+      exit_code = !xml_writer::write_corpus_group(group, 0, of, opts.annotate);
+    }
+  else
+    exit_code = !xml_writer::write_corpus_group(group, 0, cout, opts.annotate);
+
+  return exit_code;
+}
+
+int
+main(int argc, char* argv[])
+{
+  options opts;
+
+  if (!parse_command_line(argc, argv, opts)
+      || (opts.in_file_path.empty()
+         && !opts.display_version))
+    {
+      if (!opts.wrong_option.empty())
+       emit_prefix(argv[0], cerr)
+         << "unrecognized option: " << opts.wrong_option << "\n";
+      display_usage(argv[0], cerr);
+      return 1;
     }
 
-  return 0;
+  if (opts.display_version)
+    {
+      string major, minor, revision;
+      abigail::abigail_get_library_version(major, minor, revision);
+      cout << major << "." << minor << "." << revision << "\n";
+      return 0;
+    }
+
+  assert(!opts.in_file_path.empty());
+  if (opts.corpus_group_for_linux)
+    {
+      if (!abigail::tools_utils::check_dir(opts.in_file_path, cerr, argv[0]))
+       return 1;
+    }
+  else
+    {
+      if (!abigail::tools_utils::check_file(opts.in_file_path, cerr, argv[0]))
+       return 1;
+    }
+
+  if (!maybe_check_suppression_files(opts))
+    return 1;
+
+  abigail::tools_utils::file_type type =
+    abigail::tools_utils::guess_file_type(opts.in_file_path);
+  if (type != abigail::tools_utils::FILE_TYPE_ELF
+      && type != abigail::tools_utils::FILE_TYPE_AR
+      && type != abigail::tools_utils::FILE_TYPE_DIR)
+    {
+      emit_prefix(argv[0], cerr)
+       << "files of the kind of "<< opts.in_file_path << " are not handled\n";
+      return 1;
+    }
+
+  char* p = opts.di_root_path.get();
+  environment_sptr env(new environment);
+  int exit_code = 0;
+
+  if (tools_utils::is_regular_file(opts.in_file_path))
+    {
+      read_context_sptr c = create_read_context(opts.in_file_path,
+                                               &p, env.get(),
+                                               opts.load_all_types,
+                                               opts.linux_kernel_mode);
+      read_context& ctxt = *c;
+      set_show_stats(ctxt, opts.show_stats);
+      set_suppressions(ctxt, opts);
+      abigail::dwarf_reader::set_do_log(ctxt, opts.do_log);
+      if (!opts.kabi_whitelist_supprs.empty())
+       set_ignore_symbol_table(ctxt, true);
+
+      if (opts.check_alt_debug_info_path)
+       {
+         bool has_alt_di = false;
+         string alt_di_path;
+         abigail::dwarf_reader::status status =
+           abigail::dwarf_reader::has_alt_debug_info(ctxt,
+                                                     has_alt_di,
+                                                     alt_di_path);
+         if (status & abigail::dwarf_reader::STATUS_OK)
+           {
+             if (alt_di_path.empty())
+               ;
+             else
+               {
+                 cout << "found the alternate debug info file";
+                 if (opts.show_base_name_alt_debug_info_path)
+                   {
+                     tools_utils::base_name(alt_di_path, alt_di_path);
+                     cout << " '" << alt_di_path << "'";
+                   }
+                 cout << "\n";
+               }
+             return 0;
+           }
+         else
+           {
+             emit_prefix(argv[0], cerr)
+               << "could not find alternate debug info file\n";
+             return 1;
+           }
+       }
+      exit_code = load_corpus_and_write_abixml(argv, env, c, opts);
+    }
+  else
+    exit_code = load_kernel_corpus_group_and_write_abixml(argv, env, opts);
+
+  return exit_code;
 }
index fb4afaa35ae1f76952e807c37258c6129894b2a5..2940636cb52c5bdb44c61bb8891e1fecb8e1655e 100644 (file)
@@ -200,34 +200,6 @@ maybe_check_suppression_files(const options& opts)
   return true;
 }
 
-/// If the user specified suppression specification files, this
-/// function parses those, set them to the options of the program and
-/// set the read context with the suppression specification.
-///
-/// @param read_ctxt the read context to consider.
-///
-/// @param opts the options to consider.
-static void
-set_suppressions(read_context &read_ctxt, options& opts)
-{
-  if (opts.read_time_supprs.empty())
-    {
-      for (vector<string>::const_iterator i = opts.suppression_paths.begin();
-          i != opts.suppression_paths.end();
-          ++i)
-       read_suppressions(*i, opts.read_time_supprs);
-
-      for (vector<string>::const_iterator i =
-            opts.kabi_whitelist_paths.begin();
-          i != opts.kabi_whitelist_paths.end();
-          ++i)
-       gen_suppr_spec_from_kernel_abi_whitelist(*i, opts.read_time_supprs);
-    }
-
-  abigail::dwarf_reader::add_read_context_suppressions(read_ctxt,
-                                                      opts.read_time_supprs);
-}
-
 /// Setup the diff context from the program's options.
 ///
 /// @param ctxt the diff context to consider.
@@ -262,301 +234,6 @@ set_diff_context(diff_context_sptr ctxt, const options& opts)
     ctxt->add_suppressions(opts.diff_time_supprs);
 }
 
-/// Test if an FTSENT pointer (resulting from fts_read) represents the
-/// vmlinux binary.
-///
-/// @param entry the FTSENT to consider.
-///
-/// @return true iff @p entry is for a vmlinux binary.
-static bool
-is_vmlinux(const FTSENT *entry)
-{
-  if (entry == NULL
-      || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
-      || entry->fts_info == FTS_ERR
-      || entry->fts_info == FTS_NS)
-    return false;
-
-  string fname = entry->fts_name;
-
-  if (fname == "vmlinux")
-    {
-      string dirname;
-      dir_name(entry->fts_path, dirname);
-      if (string_ends_with(dirname, "compressed"))
-       return false;
-
-      return true;
-    }
-
-  return false;
-}
-
-/// Test if an FTSENT pointer (resulting from fts_read) represents a a
-/// linux kernel module binary.
-///
-/// @param entry the FTSENT to consider.
-///
-/// @return true iff @p entry is for a linux kernel module binary.
-static bool
-is_kernel_module(const FTSENT *entry)
-{
-    if (entry == NULL
-      || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
-      || entry->fts_info == FTS_ERR
-      || entry->fts_info == FTS_NS)
-    return false;
-
-  string fname = entry->fts_name;
-  if (string_ends_with(fname, ".ko")
-      || string_ends_with(fname, ".ko.xz")
-      || string_ends_with(fname, ".ko.gz"))
-    return true;
-
-  return false;
-}
-
-/// Find a vmlinux and its kernel modules in a given directory tree.
-///
-/// @param from the directory tree to start looking from.
-///
-/// @param vmlinux_path output parameter.  This is set to the path
-/// where the vmlinux binary is found.  This is set iff the returns
-/// true.
-///
-/// @param module_paths output parameter.  This is set to the paths of
-/// the linux kernel module binaries.
-///
-/// @return true iff at least the vmlinux binary was found.
-static bool
-find_vmlinux_and_module_paths(const string&    from,
-                             string            &vmlinux_path,
-                             vector<string>    &module_paths)
-{
-  char* path[] = {const_cast<char*>(from.c_str()), 0};
-
-  FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
-  if (!file_hierarchy)
-    return false;
-
-  bool found_vmlinux = false;
-  FTSENT *entry;
-  while ((entry = fts_read(file_hierarchy)))
-    {
-      // Skip descendents of symbolic links.
-      if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
-       {
-         fts_set(file_hierarchy, entry, FTS_SKIP);
-         continue;
-       }
-
-      if (!found_vmlinux && is_vmlinux(entry))
-       {
-         vmlinux_path = entry->fts_path;
-         found_vmlinux = true;
-       }
-      else if (is_kernel_module(entry))
-       module_paths.push_back(entry->fts_path);
-    }
-
-  fts_close(file_hierarchy);
-
-  return found_vmlinux;
-}
-
-/// Get the paths of the vmlinux and kernel module binaries under
-/// given directory.
-///
-/// @param dist_root the directory under which to look for.
-///
-/// @param vmlinux_path output parameter.  The path of the vmlinux
-/// binary that was found.
-///
-/// @param module_paths output parameter.  The paths of the kernel
-/// module binaries that were found.
-///
-/// @param debug_info_root_path output parameter. If a debug info
-/// sub-directory is found under @p dist_root, it's set to this
-/// parameter.
-///
-/// @return true if at least the path to the vmlinux binary was found.
-static bool
-get_binary_paths_from_kernel_dist(const string&        dist_root,
-                                 string&               vmlinux_path,
-                                 vector<string>&       module_paths,
-                                 string&               debug_info_root_path)
-{
-  if (!dir_exists(dist_root))
-    return false;
-
-  // For now, we assume either an Enterprise Linux or a Fedora kernel
-  // distribution directory.
-  //
-  // We also take into account split debug info package for these.  In
-  // this case, the content split debug info package is installed
-  // under the 'dist_root' directory as well, and its content is
-  // accessible from <dist_root>/usr/lib/debug directory.
-
-  string kernel_modules_root;
-  string debug_info_root;
-  if (dir_exists(dist_root + "/lib/modules"))
-    {
-      dist_root + "/lib/modules";
-      debug_info_root = dist_root + "/usr/lib/debug";
-    }
-
-  if (dir_is_empty(debug_info_root))
-    debug_info_root.clear();
-
-  bool found = false;
-  string from = !debug_info_root.empty() ? debug_info_root : dist_root;
-  if (find_vmlinux_and_module_paths(from, vmlinux_path, module_paths))
-    found = true;
-
-  debug_info_root_path = debug_info_root;
-
-  return found;
-}
-
-/// Get the paths of the vmlinux and kernel module binaries under
-/// given directory.
-///
-/// @param dist_root the directory under which to look for.
-///
-/// @param vmlinux_path output parameter.  The path of the vmlinux
-/// binary that was found.
-///
-/// @param module_paths output parameter.  The paths of the kernel
-/// module binaries that were found.
-///
-/// @return true if at least the path to the vmlinux binary was found.
-static bool
-get_binary_paths_from_kernel_dist(const string&        dist_root,
-                                 string&               vmlinux_path,
-                                 vector<string>&       module_paths)
-{
-  string debug_info_root_path;
-  return get_binary_paths_from_kernel_dist(dist_root,
-                                          vmlinux_path,
-                                          module_paths,
-                                          debug_info_root_path);
-}
-
-/// Walk a given directory and build an instance of @ref corpus_group
-/// from the vmlinux kernel binary and the linux kernel modules found
-/// under that directory and under its sub-directories, recursively.
-///
-/// The main corpus of the @ref corpus_group is made of the vmlinux
-/// binary.  The other corpora are made of the linux kernel binaries.
-///
-/// @param root the path of the directory under which vmlinux and its
-/// kernel modules are to be found.
-///
-/// @param opts the options to use during the search.
-///
-/// @param env the environment to create the corpus_group in.
-static corpus_group_sptr
-build_corpus_group_from_kernel_dist_under(const string&        root,
-                                         options&              opts,
-                                         environment_sptr&     env)
-{
-  corpus_group_sptr result;
-  string vmlinux;
-  vector<string> modules;
-  string debug_info_root_path;
-
-  if (opts.verbose)
-    cout << "Analysing kernel dist root '" << root << "' ... " << std::flush;
-
-  bool got_binary_paths =
-    get_binary_paths_from_kernel_dist(root, vmlinux, modules,
-                                     debug_info_root_path);
-
-  if (opts.verbose)
-    cout << "DONE\n";
-
-  if (got_binary_paths)
-    {
-      shared_ptr<char> di_root =
-       make_path_absolute(debug_info_root_path.c_str());
-      char *di_root_ptr = di_root.get();
-      abigail::dwarf_reader::status status = abigail::dwarf_reader::STATUS_OK;
-      corpus_group_sptr group;
-      if (!vmlinux.empty())
-       {
-         read_context_sptr ctxt =
-           create_read_context(vmlinux, &di_root_ptr, env.get(),
-                               /*read_all_types=*/false,
-                               /*linux_kernel_mode=*/true);
-
-         set_suppressions(*ctxt, opts);
-
-         // If we have been given a whitelist of functions and
-         // variable symbols to look at, then we can avoid loading
-         // and analyzing the ELF symbol table.
-         bool do_ignore_symbol_table = !opts.read_time_supprs.empty();
-         set_ignore_symbol_table(*ctxt, do_ignore_symbol_table);
-
-         group.reset(new corpus_group(env.get(), root));
-
-         set_read_context_corpus_group(*ctxt, group);
-
-         if (opts.verbose)
-           cout << "reading kernel binary '"
-                << vmlinux << "' ... " << std::flush;
-
-         // Read the vmlinux corpus and add it to the group.
-         read_and_add_corpus_to_group_from_elf(*ctxt, *group, status);
-
-           if (opts.verbose)
-             cout << " DONE\n";
-       }
-
-      if (!group->is_empty())
-       {
-         // Now add the corpora of the modules to the corpus group.
-         int total_nb_modules = modules.size();
-         int cur_module_index = 1;
-         for (vector<string>::const_iterator m = modules.begin();
-              m != modules.end();
-              ++m, ++cur_module_index)
-           {
-             if (opts.verbose)
-               cout << "reading module '"
-                    << *m << "' ("
-                    << cur_module_index
-                    << "/" << total_nb_modules
-                    << ") ... " << std::flush;
-
-             read_context_sptr module_ctxt =
-               create_read_context(*m, &di_root_ptr, env.get(),
-                                   /*read_all_types=*/false,
-                                   /*linux_kernel_mode=*/true);
-
-             // If we have been given a whitelist of functions and
-             // variable symbols to look at, then we can avoid loading
-             // and analyzing the ELF symbol table.
-             bool do_ignore_symbol_table = !opts.read_time_supprs.empty();
-
-             set_ignore_symbol_table(*module_ctxt, do_ignore_symbol_table);
-
-             set_suppressions(*module_ctxt, opts);
-
-             set_read_context_corpus_group(*module_ctxt, group);
-
-             read_and_add_corpus_to_group_from_elf(*module_ctxt,
-                                                   *group, status);
-             if (opts.verbose)
-               cout << " DONE\n";
-           }
-
-         result = group;
-       }
-    }
-
-  return result;
-}
-
 /// Print information about the kernel (and modules) binaries found
 /// under a given directory.
 ///
@@ -637,7 +314,11 @@ main(int argc, char* argv[])
     {
       group1 =
        build_corpus_group_from_kernel_dist_under(opts.kernel_dist_root1,
-                                                 opts, env);
+                                                 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);
     }
 
@@ -645,7 +326,11 @@ main(int argc, char* argv[])
     {
       group2 =
        build_corpus_group_from_kernel_dist_under(opts.kernel_dist_root2,
-                                                 opts, env);
+                                                 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);
     }