Support Linux Kernel ABI whitelist files
authorDodji Seketeli <dodji@redhat.com>
Thu, 10 Nov 2016 12:49:08 +0000 (13:49 +0100)
committerDodji Seketeli <dodji@redhat.com>
Fri, 6 Jan 2017 11:41:57 +0000 (12:41 +0100)
Enterprise Linux (at least) kernels come with ABI whitelist files that list the
names of the set of functions to be considered when looking at the ABI
exposed by the kernel to its modules.

This patch adds a new --linux-kernel-abi-whitelist option to abidiff
so that it drops changes that relate to functions that are *NOT*
defined in the whitelist.

The patch reads the whitelist file and generates one or several
instances of function_suppression that are applied when the two
binaries are loaded.

* include/abg-suppression.h
(function_suppression::function_suppression): Make the declaration
of the default constructor public.
* src/abg-suppression-priv.h (function_suppression::priv::priv):
Declare a default constructor.
* src/abg-suppression.cc
(function_suppression::function_suppression): Define default
constructor.
* include/abg-tools-utils.h
(gen_suppr_spec_from_kernel_abi_whitelist): Declare new function.
* src/abg-tools-utils.cc
(gen_suppr_spec_from_kernel_abi_whitelist): Define new function.
* tools/abidiff.cc (options::kernel_abi_whitelist_paths):
(display_usage): Display a help string for the new
--linux-kernel-abi-whitelist option.
(parse_command_line): Parse the --linux-kernel-abi-whitelist from
the command line.
(maybe_check_suppression_files): Check the presence of the kernel
abi whitelist files.
(set_suppressions): Generate suppression specifications from the
whitelist files.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
include/abg-suppression.h
include/abg-tools-utils.h
src/abg-suppression-priv.h
src/abg-suppression.cc
src/abg-tools-utils.cc
tools/abidiff.cc

index 6c354799ff688fdb264f897ae5f5f0c60f9f407d..57c681f6eb19ca4b04b5384e7446f011a32926fc 100644 (file)
@@ -401,9 +401,6 @@ class function_suppression : public suppression_base
   struct priv;
   typedef shared_ptr<priv> priv_sptr;
 
-  // Forbid this.
-  function_suppression();
-
 public:
 
   priv_sptr priv_;
@@ -435,6 +432,8 @@ public:
                       | DELETED_FUNCTION_CHANGE_KIND)
   };
 
+  function_suppression();
+
   function_suppression(const string&           label,
                       const string&            name,
                       const string&            name_regex,
index 39caae6c049a00049f0aa1da8fa173e57aae0345..b649d7f15954ca68d3054faf7b2f9d90b140a641 100644 (file)
@@ -60,6 +60,10 @@ bool string_is_ascii_identifier(const string&);
 suppr::type_suppression_sptr
 gen_suppr_spec_from_headers(const string& hdrs_root_dir);
 
+bool
+gen_suppr_spec_from_kernel_abi_whitelist(const string& abi_whitelist_path,
+                                        suppr::suppressions_type& s);
+
 string
 get_default_system_suppression_file_path();
 
index 34379c1ea4540b4a3b7b5d13b1b1701537a71831..907022310e79029851adc68141fbc32e7fb4a512 100644 (file)
@@ -277,6 +277,11 @@ struct function_suppression::priv
   mutable sptr_utils::regex_t_sptr     symbol_version_regex_;
   bool                                 allow_other_aliases_;
 
+  priv():
+    change_kind_(ALL_CHANGE_KIND),
+    allow_other_aliases_(true)
+  {}
+
   priv(const string&                   name,
        const string&                   name_regex_str,
        const string&                   return_type_name,
index 69304c9ce7c0edd16291e5a0c4a8ec21355fdd41..6e069183a4a5a15bba6221ededfa4832a1731ee0 100644 (file)
@@ -1816,6 +1816,15 @@ function_suppression::parameter_spec::set_parameter_type_name_regex_str
 (const string& type_name_regex_str)
 {priv_->type_name_regex_str_ = type_name_regex_str;}
 
+/// Default constructor for the @ref function_suppression type.
+///
+/// It defines no suppression for now.  Suppressions have to be
+/// specified by using the various accessors of the @ref
+/// function_suppression type.
+function_suppression::function_suppression()
+  :  suppression_base(/*label=*/""), priv_(new priv)
+{}
+
 /// Constructor for the @ref function_suppression type.
 ///
 /// @param label an informative text string that the evalution code
index d68fe06c9e5359602d3c5ed8700b69190dac1396..0b2457a64fec4c3786552ea6bf719dccab32ab97 100644 (file)
@@ -50,6 +50,7 @@ namespace abigail
 {
 
 using namespace abigail::suppr;
+using namespace abigail::ini;
 
 /// @brief Namespace for a set of utility function used by tools based
 /// on libabigail.
@@ -888,6 +889,84 @@ gen_suppr_spec_from_headers(const string& headers_root_dir)
   return result;
 }
 
+/// Generate a suppression specification from kernel abi whitelist
+/// files.
+///
+/// A kernel ABI whitelist file is an INI file that usually has only
+/// one section.  The name of the section is a string that ends up
+/// with the sub-string "whitelist".  For instance
+/// RHEL7_x86_64_whitelist.
+///
+/// Then the content of the section is a set of function names, one
+/// name per line.  Each function names is the name of a function
+/// whose changes are to be keept.
+///
+/// This function reads the white list and generate
+/// function_suppression_sptr (or several, if there are more than one
+/// section) that is added to a vector of suppressions.
+///
+/// @param abi_whitelist_path the path to the Kernel ABI whitelist.
+///
+/// @param supprs the resulting vector of suppressions to which the
+/// new function suppressions resulting from reading the whitelist are
+/// added.  This vector is updated iff the function returns true.
+///
+/// @return true iff the abi whitelist file was read and function
+/// suppressions could be generated as a result.
+bool
+gen_suppr_spec_from_kernel_abi_whitelist(const string& abi_whitelist_path,
+                                        suppressions_type& supprs)
+{
+  config whitelist;
+  if (!read_config(abi_whitelist_path, whitelist))
+    return false;
+
+  bool created_a_suppr = false;
+
+  const config::sections_type &whitelist_sections = whitelist.get_sections();
+  for (config::sections_type::const_iterator s =
+        whitelist_sections.begin();
+       s != whitelist_sections.end();
+       ++s)
+    {
+      string section_name = (*s)->get_name();
+      if (!string_ends_with(section_name, "whitelist"))
+       continue;
+
+      function_suppression_sptr suppr;
+      string function_names_regexp;
+      for (config::properties_type::const_iterator p =
+            (*s)->get_properties().begin();
+          p != (*s)->get_properties().end();
+          ++p)
+       {
+         if (simple_property_sptr prop = is_simple_property(*p))
+           if (prop->has_empty_value())
+             {
+               const string &function_name = prop->get_name();
+               if (!function_name.empty())
+                 {
+                   if (!function_names_regexp.empty())
+                     function_names_regexp += "|";
+                   function_names_regexp += "^" + function_name + "$";
+                 }
+             }
+       }
+
+      if (!function_names_regexp.empty())
+       {
+         suppr.reset(new function_suppression);
+         suppr->set_label(section_name);
+         suppr->set_name_not_regex_str(function_names_regexp);
+         suppr->set_drops_artifact_from_ir(true);
+         supprs.push_back(suppr);
+         created_a_suppr = true;
+       }
+    }
+
+  return created_a_suppr;
+}
+
 /// Get the path to the default system suppression file.
 ///
 /// @return a copy of the default system suppression file.
index 28230acfb35dec89738d3f45f46cd86dd14ae4af..2ebe11300a6cf1df6296270ec60d2b8fca066609 100644 (file)
@@ -56,6 +56,7 @@ using abigail::tools_utils::emit_prefix;
 using abigail::tools_utils::check_file;
 using abigail::tools_utils::guess_file_type;
 using abigail::tools_utils::gen_suppr_spec_from_headers;
+using abigail::tools_utils::gen_suppr_spec_from_kernel_abi_whitelist;
 using abigail::tools_utils::load_default_system_suppressions;
 using abigail::tools_utils::load_default_user_suppressions;
 using abigail::tools_utils::abidiff_status;
@@ -69,6 +70,7 @@ struct options
   string               file1;
   string               file2;
   vector<string>       suppression_paths;
+  vector<string>       kernel_abi_whitelist_paths;
   vector<string>       drop_fn_regex_patterns;
   vector<string>       drop_var_regex_patterns;
   vector<string>       keep_fn_regex_patterns;
@@ -151,6 +153,8 @@ display_usage(const string& prog_name, ostream& out)
     "internal representation\n"
     << " --no-linux-kernel-mode  don't consider the input binaries as "
        "linux kernel binaries\n"
+    << " --linux-kernel-abi-whitelist|--lkaw  path to a "
+       "linux kernel abi whitelist\n"
     << " --stat  only display the diff stats\n"
     << " --symtabs  only display the symbol tables of the corpora\n"
     << " --no-default-suppression  don't load any "
@@ -280,8 +284,19 @@ parse_command_line(int argc, char* argv[], options& opts)
          opts.headers_dir2 = argv[j];
          ++i;
        }
-      else if (!strcmp(argv[i], "--no-linux-kernel-mode"))
-       opts.linux_kernel_mode = false;
+      else if (!strcmp(argv[i], "--linux-kernel-abi-whitelist")
+              || !strcmp(argv[i], "--lkaw"))
+       {
+         int j = i + 1;
+         if (j >= argc)
+           {
+             opts.missing_operand = true;
+             opts.wrong_option = argv[i];
+             return true;
+           }
+         opts.kernel_abi_whitelist_paths.push_back(argv[j]);
+         ++i;
+       }
       else if (!strcmp(argv[i], "--stat"))
        opts.show_stats_only = true;
       else if (!strcmp(argv[i], "--symtabs"))
@@ -533,6 +548,13 @@ maybe_check_suppression_files(const options& opts)
     if (!check_file(*i, cerr, "abidiff"))
       return false;
 
+  for (vector<string>::const_iterator i =
+        opts.kernel_abi_whitelist_paths.begin();
+       i != opts.kernel_abi_whitelist_paths.end();
+       ++i)
+    if (!check_file(*i, cerr, "abidiff"))
+      return false;
+
   return true;
 }
 
@@ -673,7 +695,13 @@ set_suppressions(ReadContextType& read_ctxt, const options& opts)
        }
     }
 
-    add_read_context_suppressions(read_ctxt, supprs);
+  for (vector<string>::const_iterator i =
+        opts.kernel_abi_whitelist_paths.begin();
+       i != opts.kernel_abi_whitelist_paths.end();
+       ++i)
+    gen_suppr_spec_from_kernel_abi_whitelist(*i, supprs);
+
+  add_read_context_suppressions(read_ctxt, supprs);
 }
 
 /// Set the regex patterns describing the functions to drop from the