From 08e760221bf3473761d1ed330be09c0b7cb01a1c Mon Sep 17 00:00:00 2001 From: Dodji Seketeli Date: Thu, 10 Nov 2016 13:49:08 +0100 Subject: [PATCH] Support Linux Kernel ABI whitelist files 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 --- include/abg-suppression.h | 5 +-- include/abg-tools-utils.h | 4 ++ src/abg-suppression-priv.h | 5 +++ src/abg-suppression.cc | 9 +++++ src/abg-tools-utils.cc | 79 ++++++++++++++++++++++++++++++++++++++ tools/abidiff.cc | 34 ++++++++++++++-- 6 files changed, 130 insertions(+), 6 deletions(-) diff --git a/include/abg-suppression.h b/include/abg-suppression.h index 6c354799..57c681f6 100644 --- a/include/abg-suppression.h +++ b/include/abg-suppression.h @@ -401,9 +401,6 @@ class function_suppression : public suppression_base struct priv; typedef shared_ptr 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, diff --git a/include/abg-tools-utils.h b/include/abg-tools-utils.h index 39caae6c..b649d7f1 100644 --- a/include/abg-tools-utils.h +++ b/include/abg-tools-utils.h @@ -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(); diff --git a/src/abg-suppression-priv.h b/src/abg-suppression-priv.h index 34379c1e..90702231 100644 --- a/src/abg-suppression-priv.h +++ b/src/abg-suppression-priv.h @@ -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, diff --git a/src/abg-suppression.cc b/src/abg-suppression.cc index 69304c9c..6e069183 100644 --- a/src/abg-suppression.cc +++ b/src/abg-suppression.cc @@ -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 diff --git a/src/abg-tools-utils.cc b/src/abg-tools-utils.cc index d68fe06c..0b2457a6 100644 --- a/src/abg-tools-utils.cc +++ b/src/abg-tools-utils.cc @@ -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. diff --git a/tools/abidiff.cc b/tools/abidiff.cc index 28230acf..2ebe1130 100644 --- a/tools/abidiff.cc +++ b/tools/abidiff.cc @@ -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 suppression_paths; + vector kernel_abi_whitelist_paths; vector drop_fn_regex_patterns; vector drop_var_regex_patterns; vector 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::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::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