Support symbol_name_not_regexp in [suppress_{function, variable}]
authorDodji Seketeli <dodji@redhat.com>
Fri, 18 Oct 2019 08:13:41 +0000 (10:13 +0200)
committerDodji Seketeli <dodji@redhat.com>
Fri, 8 Nov 2019 10:01:54 +0000 (11:01 +0100)
In the suppress_function and suppress_variable directives of the
suppression specification language, we lack the
'symbol_name_not_regexp' properties, that would allow users to specify
which (function/variable) symbols to *keep* as opposed to specifying
which symbols to suppress.

This patch adds that feature.  That will later allow us to make the
linux kernel symbol white lists[1] functionality use this feature;
that is, upon analysing the content of a kernel symbol whitelist which
lists a symbol named "foo", Libabigail would automatically generate a
suppression specification which contains, e.g a 'suppress_function"
directive that has this new 'symbol_name_not_regexp' property which
value is set to "foo".

Note that the patch makes sure that feature is supported when
analyzing both abixml and DWARF formats.

[1]: You can learn about what a Linux Kernel symbols white list is by
reading about it at
https://sourceware.org/libabigail/manual/kmidiff.html#environment.

* doc/manuals/libabigail-concepts.rst: Document the new
symbol_name_not_regexp properties for the
suppress_{function,variable} directives.
* include/abg-suppression.h
({function,variable}_suppression::{g,s}et_symbol_name_not_regex_str):
Declare new member functions.
* src/abg-dwarf-reader.cc
(read_context::is_elf_symbol_suppressed): Define new member functions.
(read_context::{load_symbol_maps_from_symtab_section,
populate_symbol_map_from_ksymtab,
populate_symbol_map_from_ksymtab_reloc}): Drop suppressed symbols
when reading symbol tables.
({function,variable}_is_suppressed): Consider that in C, the
linkage name is _by default_ the same as the function/variable
name. Remove local variable.
* include/abg-ir.h (elf_symbol_is_{function,variable}): Add ...
* src/abg-ir.cc (elf_symbol_is_{function,variable}): ... new
functions.
* src/abg-reader.cc (build_elf_symbol): Take an additional boolean
to detect and drop suppressed symbols.
(build_elf_symbol_db): Adjust the call to build_elf_symbol to make
it detect and drop suppressed symbols.
(read_corpus_from_input): Be mindful that the set of symbols for a
given corpus can be empty because of suppression specifications.
* src/abg-suppression-priv.h
({function,variable}_suppression::priv::symbol_name_not_regex[_str_]):
Add new data members.
(function,variable}_suppression::priv::get_symbol_name_not_regex):
Add new member functions.
({function,variable}_is_suppressed): Guard against empty name.
(is_elf_symbol_suppressed): Define new function template.
* src/abg-suppression.cc
({function,variable}_suppression::{g,s}et_symbol_name_not_regex_str):
Define new member functions.
({function,variable}_suppression::suppresses_function)
(suppression_matches_{function,variable}_sym_name)
(read_{function,variable}_suppression): Support the new
"symbol_name_not_regex" property.
* tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-1.txt:
New test reference report.
* tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-2.txt:
Likewise.
* tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v{0,1}.c:
Sources of the new test input.
* tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v{0,1}.o:
New test input binaries.
* tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v{0,1}.o.abi:
New test input abixml files.
* tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp.suppr.txt:
Next test suppression specification.
* tests/data/Makefile.am: Add the new test material above to
source distribution.
* tests/test-diff-suppr.cc (in_out_specs): Add the input tests
above to the test harness.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
19 files changed:
doc/manuals/libabigail-concepts.rst
include/abg-ir.h
include/abg-suppression.h
src/abg-dwarf-reader.cc
src/abg-ir.cc
src/abg-reader.cc
src/abg-suppression-priv.h
src/abg-suppression.cc
tests/data/Makefile.am
tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-1.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-2.txt [new file with mode: 0644]
tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.c [new file with mode: 0644]
tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o [new file with mode: 0644]
tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o.abi [new file with mode: 0644]
tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.c [new file with mode: 0644]
tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o [new file with mode: 0644]
tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o.abi [new file with mode: 0644]
tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp.suppr.txt [new file with mode: 0644]
tests/test-diff-suppr.cc

index a1835d190bd60882c62f9c7c677fad2fc1443c43..6565d30b8c07b6fa0378d2c3ef8dfb9c4ce758ac 100644 (file)
@@ -601,7 +601,8 @@ one of the following properties must be provided:
   ``label``, ``file_name_regexp``, ``file_name_not_regexp``,
   ``soname_regexp``, ``soname_not_regexp``, ``name``, ``name_regexp``,
   ``name_not_regexp``, ``parameter``, ``return_type_name``,
-   ``symbol_name``, ``symbol_name_regexp``, ``symbol_version``,
+  ``symbol_name``, ``symbol_name_regexp``,
+  ``symbol_name_not_regexp``,``symbol_version``,
   ``symbol_version_regexp``.
 
 If none of the following properties are provided, then the
@@ -830,6 +831,15 @@ The potential properties of this sections are:
  the aliases of the function for the directive to actually suppress
  the diff reports for said function.
 
+* ``symbol_name_not_regexp``
+
+ Usage:
+
+   ``symbol_name_not_regexp`` ``=`` <:ref:`regular-expression <suppr_regexp_label>`>
+
+ Suppresses change reports involving functions whose symbol name does
+ not match the regular expression specified as value of this property.
+
 * ``symbol_version``
 
  Usage:
@@ -879,8 +889,8 @@ one of the following properties must be provided:
 
   ``label``, ``file_name_regexp``, ``file_name_not_regexp``,
   ``soname_regexp``, ``soname_not_regexp``, ``name``, ``name_regexp``,
-  ``symbol_name``, ``symbol_name_regexp``, ``symbol_version``,
-  ``symbol_version_regexp``.
+  ``symbol_name``, ``symbol_name_regexp``, ``symbol_name_not_regexp``,
+  ``symbol_version``, ``symbol_version_regexp``.
 
 If none of the following properties are provided, then the
 ``[suppres_variable]`` directive is simply ignored.
@@ -988,6 +998,15 @@ The potential properties of this sections are:
  matches the regular expression specified as value of this property.
 
 
+* ``symbol_name_not_regexp``
+
+ Usage:
+
+   ``symbol_name_not_regexp`` ``=`` <:ref:`regular-expression <suppr_regexp_label>`>
+
+ Suppresses change reports involving variables whose symbol name does
+ not match the regular expression specified as value of this property.
+
 * ``symbol_version``
 
  Usage:
index 0d344f79b100ea5f00dbe1ed4f7eeb1ec5654396..43455714d560c9bae19e22211cbd5378d7784e1a 100644 (file)
@@ -992,6 +992,12 @@ string_to_elf_symbol_binding(const string&, elf_symbol::binding&);
 bool
 string_to_elf_symbol_visibility(const string&, elf_symbol::visibility&);
 
+bool
+elf_symbol_is_function(elf_symbol::type);
+
+bool
+elf_symbol_is_variable(elf_symbol::type);
+
 bool
 operator==(const elf_symbol_sptr& lhs, const elf_symbol_sptr& rhs);
 
index 47dac5a909031978a5c61f1f6a93845a17295245..02f3fc4c28479395abb3ec0c006f760d6cd770ad 100644 (file)
@@ -518,6 +518,12 @@ public:
   void
   set_symbol_name_regex_str(const string&);
 
+  const string&
+  get_symbol_name_not_regex_str() const;
+
+  void
+  set_symbol_name_not_regex_str(const string&);
+
   const string&
   get_symbol_version() const;
 
@@ -707,6 +713,12 @@ public:
   void
   set_symbol_name_regex_str(const string&);
 
+  const string&
+  get_symbol_name_not_regex_str() const;
+
+  void
+  set_symbol_name_not_regex_str(const string&);
+
   const string&
   get_symbol_version() const;
 
index f2ebede11582c40dafa1c8f97e268b8802b3a426..1ae0ed70354e1eaf5084e0415679b54ca79a3b5d 100644 (file)
@@ -7385,6 +7385,10 @@ public:
            ABG_ASSERT(symbol);
            ABG_ASSERT(symbol->is_function());
 
+           // If the symbol was suppressed by a suppression
+           // specification then drop it on the floor.
+           if (is_elf_symbol_suppressed(symbol))
+             continue;
 
            if (load_fun_map && symbol->is_public())
              {
@@ -7850,6 +7854,21 @@ public:
     return nb_ksymtab_gpl_entries_;
   }
 
+  /// Test if a given ELF symbol was suppressed by a suppression
+  /// specification.
+  ///
+  /// @param symbol the ELF symbol to consider.
+  ///
+  /// @return true iff @p symbol is suppressed.
+  bool
+  is_elf_symbol_suppressed(const elf_symbol_sptr& symbol) const
+  {
+    return (symbol
+           && suppr::is_elf_symbol_suppressed(*this,
+                                              symbol->get_name(),
+                                              symbol->get_type()));
+  }
+
   /// Populate the symbol map by reading exported symbols from the
   /// ksymtab directly.
   ///
@@ -7939,6 +7958,11 @@ public:
              continue;
          }
 
+       // If the symbol was suppressed by a suppression
+       // specification then drop it on the floor.
+       if (is_elf_symbol_suppressed(symbol))
+         continue;
+
        address_set_sptr set;
        if (symbol->is_function())
          {
@@ -8031,6 +8055,11 @@ public:
            continue;
          }
 
+       // If the symbol was suppressed by a suppression
+       // specification then drop it on the floor.
+       if (is_elf_symbol_suppressed(symbol))
+         continue;
+
        // If we are looking at an ET_REL (relocatable) binary, then
        // the symbol value of native_symbol is relative to the
        // section that symbol is defined in.  We need to translate it
@@ -16477,13 +16506,14 @@ function_is_suppressed(const read_context& ctxt,
 
   string fname = die_string_attribute(function_die, DW_AT_name);
   string flinkage_name = die_linkage_name(function_die);
+  if (flinkage_name.empty() && ctxt.die_is_in_c(function_die))
+    flinkage_name = fname;
   string qualified_name = build_qualified_name(scope, fname);
 
   // A non-member function which symbol is not exported is suppressed.
   if (!is_class_type(scope) && !die_is_declaration_only(function_die))
     {
       Dwarf_Addr fn_addr;
-      elf_symbol_sptr fn_sym;
       if (!ctxt.get_function_address(function_die, fn_addr))
        return true;
       if (!get_ignore_symbol_table(ctxt))
@@ -16583,6 +16613,8 @@ variable_is_suppressed(const read_context& ctxt,
 
   string name = die_string_attribute(variable_die, DW_AT_name);
   string linkage_name = die_linkage_name(variable_die);
+  if (linkage_name.empty() && ctxt.die_is_in_c(variable_die))
+    linkage_name = name;
   string qualified_name = build_qualified_name(scope, name);
 
   // If a non member variable that is a declaration (has no exported
@@ -16597,7 +16629,6 @@ variable_is_suppressed(const read_context& ctxt,
   if (!is_class_type(scope) && !is_required_decl_spec)
     {
       Dwarf_Addr var_addr = 0;
-      elf_symbol_sptr var_sym;
       if (!ctxt.get_variable_address(variable_die, var_addr))
        return true;
       if (!get_ignore_symbol_table(ctxt))
index 87b20e48665c98c8bceed14c06af236afc65c56d..ebe2e3fadb06532aabef393733d76931fe082fd3 100644 (file)
@@ -2314,6 +2314,26 @@ string_to_elf_symbol_visibility(const string& s, elf_symbol::visibility& v)
   return true;
 }
 
+/// Test if the type of an ELF symbol denotes a function symbol.
+///
+/// @param t the type of the ELF symbol.
+///
+/// @return true iff elf symbol type @p t denotes a function symbol
+/// type.
+bool
+elf_symbol_is_function(elf_symbol::type t)
+{return t == elf_symbol::FUNC_TYPE;}
+
+/// Test if the type of an ELF symbol denotes a function symbol.
+///
+/// @param t the type of the ELF symbol.
+///
+/// @return true iff elf symbol type @p t denotes a function symbol
+/// type.
+bool
+elf_symbol_is_variable(elf_symbol::type t)
+{return t == elf_symbol::OBJECT_TYPE;}
+
 // <elf_symbol::version stuff>
 
 struct elf_symbol::version::priv
index 2ff6dfce45fefcd4c29146abb108c89612a474c7..ce1f3ea7467c935e6eb3c64fcfd89d1de6bfd208 100644 (file)
@@ -1093,7 +1093,7 @@ build_namespace_decl(read_context&, const xmlNodePtr, bool);
 // below.
 
 static elf_symbol_sptr
-build_elf_symbol(read_context&, const xmlNodePtr);
+build_elf_symbol(read_context&, const xmlNodePtr, bool);
 
 static elf_symbol_sptr
 build_elf_symbol_from_reference(read_context&, const xmlNodePtr,
@@ -1882,7 +1882,9 @@ read_corpus_from_input(read_context& ctxt)
   bool is_ok = read_symbol_db_from_input(ctxt, fn_sym_db, var_sym_db);
   if (is_ok)
     {
-      ABG_ASSERT(fn_sym_db || var_sym_db);
+      // Note that it's possible that both fn_sym_db and var_sym_db
+      // are nil, due to potential suppression specifications.  That's
+      // fine.
       if (fn_sym_db)
        {
          corp.set_fun_symbol_map(fn_sym_db);
@@ -2592,9 +2594,13 @@ build_namespace_decl(read_context&       ctxt,
 ///
 /// @param node the XML node to read.
 ///
+/// @param drop_if_suppressed if the elf symbol was suppressed by a
+/// suppression specification then do not build it.
+///
 /// @return the @ref elf_symbol built, or nil if it couldn't be built.
 static elf_symbol_sptr
-build_elf_symbol(read_context& ctxt, const xmlNodePtr node)
+build_elf_symbol(read_context& ctxt, const xmlNodePtr node,
+                bool drop_if_suppressed)
 {
   elf_symbol_sptr nil;
 
@@ -2657,6 +2663,9 @@ build_elf_symbol(read_context& ctxt, const xmlNodePtr node)
 
   elf_symbol::version version(version_string, is_default_version);
 
+  if (drop_if_suppressed && suppr::is_elf_symbol_suppressed(ctxt, name, type))
+    return elf_symbol_sptr();
+
   const environment* env = ctxt.get_environment();
   elf_symbol_sptr e = elf_symbol::create(env, /*index=*/0,
                                         size, name, type, binding,
@@ -2758,7 +2767,7 @@ build_elf_symbol_db(read_context& ctxt,
   elf_symbol_sptr sym;
   for (xmlNodePtr n = node->children; n; n = n->next)
     {
-      if ((sym = build_elf_symbol(ctxt, n)))
+      if ((sym = build_elf_symbol(ctxt, n, /*drop_if_suppress=*/true)))
        {
          id_sym_map[sym->get_id_string()] = sym;
          xml_node_ptr_elf_symbol_map[n] = sym;
index 07e17180f2cefdb019d9c334ae9714cd55a6a9f5..449814c95ae8056f00e501449a71886b6d3d1be9 100644 (file)
@@ -272,6 +272,8 @@ struct function_suppression::priv
   string                               symbol_name_;
   string                               symbol_name_regex_str_;
   mutable sptr_utils::regex_t_sptr     symbol_name_regex_;
+  string                               symbol_name_not_regex_str_;
+  mutable sptr_utils::regex_t_sptr     symbol_name_not_regex_;
   string                               symbol_version_;
   string                               symbol_version_regex_str_;
   mutable sptr_utils::regex_t_sptr     symbol_version_regex_;
@@ -397,6 +399,29 @@ struct function_suppression::priv
     return symbol_name_regex_;
   }
 
+  /// Getter for a pointer to a regular expression object built from
+  /// the regular expression string
+  /// function_suppression::priv::symbol_name_not_regex_str_.
+  ///
+  /// If that string is empty, then an empty regular expression object
+  /// pointer is returned.
+  ///
+  /// @return a pointer to the regular expression object of
+  /// function_suppression::priv::symbol_name_not_regex_str_.
+  const sptr_utils::regex_t_sptr
+  get_symbol_name_not_regex() const
+  {
+    if (!symbol_name_not_regex_ && !symbol_name_not_regex_str_.empty())
+      {
+       sptr_utils::regex_t_sptr r = sptr_utils::build_sptr<regex_t>();
+       if (regcomp(r.get(),
+                   symbol_name_not_regex_str_.c_str(),
+                   REG_EXTENDED) == 0)
+         symbol_name_not_regex_ = r;
+      }
+    return symbol_name_not_regex_;
+  }
+
   /// Getter for a pointer to a regular expression object built from
   /// the regular expression string
   /// function_suppression::priv::symbol_version_regex_str_.
@@ -474,10 +499,12 @@ function_is_suppressed(const ReadContextType&     ctxt,
       {
        if (require_drop_property && !(*i)->get_drops_artifact_from_ir())
          continue;
-       if (ctxt.suppression_matches_function_name(*suppr, fn_name))
+       if (!fn_name.empty()
+           && ctxt.suppression_matches_function_name(*suppr, fn_name))
          return true;
-       if (ctxt.suppression_matches_function_sym_name(*suppr,
-                                                      fn_linkage_name))
+       if (!fn_linkage_name.empty()
+           && ctxt.suppression_matches_function_sym_name(*suppr,
+                                                         fn_linkage_name))
          return true;
       }
   return false;
@@ -500,6 +527,8 @@ struct variable_suppression::priv
   string                               symbol_name_;
   string                               symbol_name_regex_str_;
   mutable sptr_utils::regex_t_sptr     symbol_name_regex_;
+  string                               symbol_name_not_regex_str_;
+  mutable sptr_utils::regex_t_sptr     symbol_name_not_regex_;
   string                               symbol_version_;
   string                               symbol_version_regex_str_;
   mutable sptr_utils::regex_t_sptr     symbol_version_regex_;
@@ -595,6 +624,28 @@ struct variable_suppression::priv
     return symbol_name_regex_;
   }
 
+  /// Getter for a pointer to a regular expression object built from
+  /// the regular expression string
+  /// variable_suppression::priv::symbol_name_not_regex_str_.
+  ///
+  /// If that string is empty, then an empty regular expression object
+  /// pointer is returned.
+  ///
+  /// @return a pointer to the regular expression object of
+  /// variable_suppression::priv::symbol_name_not_regex_str_.
+  const sptr_utils::regex_t_sptr
+  get_symbol_name_not_regex() const
+  {
+    if (!symbol_name_not_regex_ && !symbol_name_not_regex_str_.empty())
+      {
+       sptr_utils::regex_t_sptr r = sptr_utils::build_sptr<regex_t>();
+       if (regcomp(r.get(), symbol_name_not_regex_str_.c_str(),
+                   REG_EXTENDED == 0) == 0)
+         symbol_name_not_regex_ = r;
+      }
+    return symbol_name_not_regex_;
+  }
+
   /// Getter for a pointer to a regular expression object built from
   /// the regular expression string
   /// variable_suppression::priv::symbol_version_regex_str_.
@@ -657,10 +708,12 @@ variable_is_suppressed(const ReadContextType&     ctxt,
       {
        if (require_drop_property && !(*i)->get_drops_artifact_from_ir())
          continue;
-       if (ctxt.suppression_matches_variable_name(*suppr, var_name))
+       if (!var_name.empty()
+           && ctxt.suppression_matches_variable_name(*suppr, var_name))
          return true;
-       if (ctxt.suppression_matches_variable_sym_name(*suppr,
-                                                      var_linkage_name))
+       if (!var_linkage_name.empty()
+           && ctxt.suppression_matches_variable_sym_name(*suppr,
+                                                         var_linkage_name))
          return true;
       }
   return false;
@@ -915,6 +968,33 @@ type_is_suppressed(const ReadContextType&  ctxt,
   return false;
 }
 
+/// Test if a given ELF symbol is suppressed by a suppression
+/// specification.
+///
+/// @param ctxt the read context to use.
+///
+/// @param sym_name the name of the symbol to consider.
+///
+/// @param sym_type the type of the symbol to consider.
+///
+/// @return true iff the elf symbol denoted by @p sym_name and @p
+/// sym_type is suppressed.
+template<typename ReadContextType>
+bool
+is_elf_symbol_suppressed(const ReadContextType& ctxt,
+                        const string& sym_name,
+                        elf_symbol::type sym_type)
+{
+  if (elf_symbol_is_function(sym_type))
+    return suppr::function_is_suppressed(ctxt, /*fn_name=*/"",
+                                        /*symbol_name=*/sym_name);
+  else if (elf_symbol_is_variable(sym_type))
+    return suppr::variable_is_suppressed(ctxt, /*var_name=*/"",
+                                        /*symbol_name=*/sym_name);
+
+  return false;
+}
+
 // </type_suppression stuff>
 
 }// end namespace suppr
index 1c10c8e67e64b82983bd258e4923d1e8121e4aaa..05d385812fc1483e8c9d5c4bf215390c0037123c 100644 (file)
@@ -2265,6 +2265,45 @@ void
 function_suppression::set_symbol_name_regex_str(const string& r)
 {priv_->symbol_name_regex_str_ = r;}
 
+/// Getter for a regular expression for a family of names of symbols
+/// of functions the user wants this specification to designate.
+///
+/// If a symbol name is matched by this regular expression, then the
+/// suppression specification will *NOT* suppress the symbol.
+///
+/// If the symbol name as returned by
+/// function_suppression::get_symbol_name() is not empty, then this
+/// property is ignored at specification evaluation time.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @return the regular expression string for a family of names of
+/// symbols that is to be *NOT* suppressed by this suppression specification.
+const string&
+function_suppression::get_symbol_name_not_regex_str() const
+{return priv_->symbol_name_not_regex_str_;}
+
+/// Setter for a regular expression for a family of names of symbols
+/// of functions the user wants this specification to designate.
+///
+/// If a symbol name is matched by this regular expression, then the
+/// suppression specification will *NOT* suppress the symbol.
+///
+/// If the symbol name as returned by
+/// function_suppression::get_symbol_name() is not empty, then this
+/// property is ignored at specification evaluation time.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @param the new regular expression string for a family of names of
+/// symbols that is to be *NOT* suppressed by this suppression
+/// specification.
+void
+function_suppression::set_symbol_name_not_regex_str(const string& r)
+{priv_->symbol_name_not_regex_str_ = r;}
+
 /// Getter for the name of the version of the symbol of the function
 /// the user wants this specification to designate.
 ///
@@ -2522,8 +2561,8 @@ function_suppression::suppresses_function(const function_decl* fn,
        return false;
     }
 
-  // Check if the "symbol_name" and "symbol_name_regexp" properties
-  // match.
+  // Check if the "symbol_name", "symbol_name_regexp", and
+  // "symbol_name_not_regexp" properties match.
   string fn_sym_name, fn_sym_version;
   elf_symbol_sptr sym = fn->get_symbol();
   if (sym)
@@ -2562,6 +2601,14 @@ function_suppression::suppresses_function(const function_decl* fn,
                      0, NULL, 0) != 0))
        return false;
 
+      const sptr_utils::regex_t_sptr symbol_name_not_regex =
+       priv_->get_symbol_name_not_regex();
+      if (symbol_name_not_regex
+         && (regexec(symbol_name_not_regex.get(),
+                     fn_sym_name.c_str(),
+                     0, NULL, 0) == 0))
+       return false;
+
       if (get_allow_other_aliases())
        {
          // In this case, we want to allow the suppression of change
@@ -2572,11 +2619,19 @@ function_suppression::suppresses_function(const function_decl* fn,
              for (elf_symbol_sptr a = sym->get_next_alias();
                   a && !a->is_main_symbol();
                   a = a->get_next_alias())
-               if (symbol_name_regex
-                   && (regexec(symbol_name_regex.get(),
-                               a->get_name().c_str(),
-                               0, NULL, 0) != 0))
-                 return false;
+               {
+                 if (symbol_name_regex
+                     && (regexec(symbol_name_regex.get(),
+                                 a->get_name().c_str(),
+                                 0, NULL, 0) != 0))
+                   return false;
+
+                 if (symbol_name_not_regex
+                     && (regexec(symbol_name_not_regex.get(),
+                                 a->get_name().c_str(),
+                                 0, NULL, 0) == 0))
+                   return false;
+               }
            }
        }
     }
@@ -2875,6 +2930,11 @@ suppression_matches_function_sym_name(const suppr::function_suppression& s,
       if (regexec(regexp.get(), fn_linkage_name.c_str(), 0, NULL, 0) != 0)
        return false;
     }
+  else if (sptr_utils::regex_t_sptr regexp = s.priv_->get_symbol_name_not_regex())
+    {
+      if (regexec(regexp.get(), fn_linkage_name.c_str(), 0, NULL, 0) == 0)
+       return false;
+    }
   else if (s.priv_->symbol_name_.empty())
     return false;
   else // if (!s.priv_->symbol_name_.empty())
@@ -2937,6 +2997,12 @@ suppression_matches_variable_sym_name(const suppr::variable_suppression& s,
       if (regexec(regexp.get(), var_linkage_name.c_str(), 0, NULL, 0) != 0)
        return false;
     }
+  else if (sptr_utils::regex_t_sptr regexp =
+          s.priv_->get_symbol_name_not_regex())
+    {
+      if (regexec(regexp.get(), var_linkage_name.c_str(), 0, NULL, 0) == 0)
+       return false;
+    }
   else if (s.priv_->symbol_name_.empty())
     return false;
   else // if (!s.priv_->symbol_name_.empty())
@@ -3149,6 +3215,12 @@ read_function_suppression(const ini::config::section& section)
     ? sym_name_regex_prop->get_value()->as_string()
     : "";
 
+  ini::simple_property_sptr sym_name_not_regex_prop =
+    is_simple_property(section.find_property("symbol_name_not_regexp"));
+  string sym_name_not_regex_str = sym_name_not_regex_prop
+    ? sym_name_not_regex_prop->get_value()->as_string()
+    : "";
+
   ini::simple_property_sptr sym_ver_prop =
     is_simple_property(section.find_property("symbol_version"));
   string sym_version = sym_ver_prop
@@ -3195,6 +3267,7 @@ read_function_suppression(const ini::config::section& section)
       || !return_type_regex_str.empty()
       || !sym_name.empty()
       || !sym_name_regex_str.empty()
+      || !sym_name_not_regex_str.empty()
       || !sym_version.empty()
       || !sym_ver_regex_str.empty()
       || !parms.empty())
@@ -3213,7 +3286,8 @@ read_function_suppression(const ini::config::section& section)
          || !name_regex_str.empty()
          || !name_not_regex_str.empty()
          || !sym_name.empty()
-         || !sym_name_regex_str.empty()))
+         || !sym_name_regex_str.empty()
+         || !sym_name_not_regex_str.empty()))
     result->set_drops_artifact_from_ir(true);
 
   if (result && !change_kind_str.empty())
@@ -3227,6 +3301,9 @@ read_function_suppression(const ini::config::section& section)
   if (!name_not_regex_str.empty())
     result->set_name_not_regex_str(name_not_regex_str);
 
+  if (!sym_name_not_regex_str.empty())
+    result->set_symbol_name_not_regex_str(sym_name_not_regex_str);
+
   if (!file_name_regex_str.empty())
     result->set_file_name_regex_str(file_name_regex_str);
 
@@ -3458,6 +3535,45 @@ void
 variable_suppression::set_symbol_name_regex_str(const string& r)
 {priv_->symbol_name_regex_str_ = r;}
 
+/// Getter for a regular expression for a family of names of symbols
+/// of variables the user wants this specification to designate.
+///
+/// If a symbol name is matched by this regular expression, then the
+/// suppression specification will *NOT* suppress the symbol.
+///
+/// If the symbol name as returned by
+/// variable_suppression::get_symbol_name() is not empty, then this
+/// property is ignored at specification evaluation time.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @return the regular expression string for a family of names of
+/// symbols that is to be *NOT* suppressed by this suppression specification.
+const string&
+variable_suppression::get_symbol_name_not_regex_str() const
+{return priv_->symbol_name_not_regex_str_;}
+
+/// Setter for a regular expression for a family of names of symbols
+/// of variables the user wants this specification to designate.
+///
+/// If a symbol name is matched by this regular expression, then the
+/// suppression specification will *NOT* suppress the symbol.
+///
+/// If the symbol name as returned by
+/// variable_suppression::get_symbol_name() is not empty, then this
+/// property is ignored at specification evaluation time.
+///
+/// This property might be empty, in which case it's ignored at
+/// evaluation time.
+///
+/// @param the new regular expression string for a family of names of
+/// symbols that is to be *NOT* suppressed by this suppression
+/// specification.
+void
+variable_suppression::set_symbol_name_not_regex_str(const string& r)
+{priv_->symbol_name_not_regex_str_ = r;}
+
 /// Getter for the version of the symbol of the variable the user
 /// wants the current specification to designate.  This property might
 /// be empty, in which case it's ignored at evaluation time.
@@ -3637,7 +3753,8 @@ variable_suppression::suppresses_variable(const var_decl* var,
        }
     }
 
-  // Check for the symbol_name property match.
+  // Check for the symbol_name, symbol_name_regex and
+  // symbol_name_not_regex property match.
   string var_sym_name = var->get_symbol() ? var->get_symbol()->get_name() : "";
   if (!get_symbol_name().empty())
     {
@@ -3652,6 +3769,13 @@ variable_suppression::suppresses_variable(const var_decl* var,
          && (regexec(sym_name_regex.get(), var_sym_name.c_str(),
                      0, NULL, 0) != 0))
        return false;
+
+      const sptr_utils::regex_t_sptr sym_name_not_regex =
+       priv_->get_symbol_name_not_regex();
+      if (sym_name_not_regex
+         && (regexec(sym_name_not_regex.get(), var_sym_name.c_str(),
+                     0, NULL, 0) == 0))
+       return false;
     }
 
   // Check for symbol_version and symbol_version_regexp property match
@@ -3965,6 +4089,12 @@ read_variable_suppression(const ini::config::section& section)
     ? sym_name_regex_prop->get_value()->as_string()
     : "";
 
+  ini::simple_property_sptr sym_name_not_regex_prop =
+    is_simple_property(section.find_property("symbol_name_not_regexp"));
+  string symbol_name_not_regex_str = sym_name_not_regex_prop
+    ? sym_name_not_regex_prop->get_value()->as_string()
+    : "";
+
   ini::simple_property_sptr sym_version_prop =
     is_simple_property(section.find_property("symbol_version"));
   string symbol_version = sym_version_prop
@@ -3999,6 +4129,7 @@ read_variable_suppression(const ini::config::section& section)
       && soname_not_regex_str.empty()
       && symbol_name.empty()
       && symbol_name_regex_str.empty()
+      && symbol_name_not_regex_str.empty()
       && symbol_version.empty()
       && symbol_version_regex_str.empty()
       && type_name_str.empty()
@@ -4015,12 +4146,16 @@ read_variable_suppression(const ini::config::section& section)
          || !name_regex_str.empty()
          || !name_not_regex_str.empty()
          || !symbol_name.empty()
-         || !symbol_name_regex_str.empty()))
+         || !symbol_name_regex_str.empty()
+         || !symbol_name_not_regex_str.empty()))
     result->set_drops_artifact_from_ir(true);
 
   if (!name_not_regex_str.empty())
     result->set_name_not_regex_str(name_not_regex_str);
 
+  if (!symbol_name_not_regex_str.empty())
+    result->set_symbol_name_not_regex_str(symbol_name_not_regex_str);
+
   if (result && !change_kind_str.empty())
     result->set_change_kind
       (variable_suppression::parse_change_kind(change_kind_str));
index 54b3d6e300759189463023ee19926048710023e7..657e4707b914ee33240cc4476dab9b4b9f6efaef 100644 (file)
@@ -1224,6 +1224,15 @@ test-diff-suppr/test43-suppr-direct-fn-subtype-v0.o \
 test-diff-suppr/test43-suppr-direct-fn-subtype-v1.cc \
 test-diff-suppr/test43-suppr-direct-fn-subtype-v1.o \
 test-diff-suppr/test43-suppr-direct-fn-subtype-report-1.txt \
+test-diff-suppr/test44-suppr-sym-name-not-regexp-report-1.txt \
+test-diff-suppr/test44-suppr-sym-name-not-regexp-report-2.txt \
+test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.c \
+test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o \
+test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o.abi \
+test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.c \
+test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o \
+test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o.abi \
+test-diff-suppr/test44-suppr-sym-name-not-regexp.suppr.txt \
 \
 test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1 \
 test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1.abi \
diff --git a/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-1.txt b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-1.txt
new file mode 100644 (file)
index 0000000..b1ba80e
--- /dev/null
@@ -0,0 +1,11 @@
+Functions changes summary: 1 Removed, 0 Changed, 0 Added function
+Variables changes summary: 1 Removed, 0 Changed, 0 Added variable
+
+1 Removed function:
+
+  'function void test2()'    {test2}
+
+1 Removed variable:
+
+  'char test2_variable'    {test2_variable}
+
diff --git a/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-2.txt b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-2.txt
new file mode 100644 (file)
index 0000000..73451c5
--- /dev/null
@@ -0,0 +1,3 @@
+Functions changes summary: 0 Removed (1 filtered out), 0 Changed, 0 Added function
+Variables changes summary: 0 Removed (1 filtered out), 0 Changed, 0 Added variable
+
diff --git a/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.c b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.c
new file mode 100644 (file)
index 0000000..a2043f1
--- /dev/null
@@ -0,0 +1,4 @@
+int test1_variable = 0;
+char test2_variable = 0;
+void test1(){};
+void test2(){};
diff --git a/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o
new file mode 100644 (file)
index 0000000..e25819c
Binary files /dev/null and b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o differ
diff --git a/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o.abi b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o.abi
new file mode 100644 (file)
index 0000000..b54177a
--- /dev/null
@@ -0,0 +1,23 @@
+<abi-corpus path='test44-suppr-sym-name-not-regexp-v0.o' architecture='elf-amd-x86_64'>
+  <elf-function-symbols>
+    <elf-symbol name='test1' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='test2' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-function-symbols>
+  <elf-variable-symbols>
+    <elf-symbol name='test1_variable' size='4' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+    <elf-symbol name='test2_variable' size='1' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-variable-symbols>
+  <abi-instr version='1.0' address-size='64' path='test44-suppr-sym-name-not-regexp-v0.c' comp-dir-path='/home/dodji/git/libabigail/PR25095/tests/data/test-diff-suppr' language='LANG_C99'>
+    <type-decl name='char' size-in-bits='8' id='type-id-1'/>
+    <type-decl name='int' size-in-bits='32' id='type-id-2'/>
+    <type-decl name='void' id='type-id-3'/>
+    <var-decl name='test1_variable' type-id='type-id-2' mangled-name='test1_variable' visibility='default' filepath='/home/dodji/git/libabigail/PR25095/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.c' line='1' column='1' elf-symbol-id='test1_variable'/>
+    <var-decl name='test2_variable' type-id='type-id-1' mangled-name='test2_variable' visibility='default' filepath='/home/dodji/git/libabigail/PR25095/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.c' line='2' column='1' elf-symbol-id='test2_variable'/>
+    <function-decl name='test2' mangled-name='test2' filepath='/home/dodji/git/libabigail/PR25095/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.c' line='4' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='test2'>
+      <return type-id='type-id-3'/>
+    </function-decl>
+    <function-decl name='test1' mangled-name='test1' filepath='/home/dodji/git/libabigail/PR25095/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.c' line='3' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='test1'>
+      <return type-id='type-id-3'/>
+    </function-decl>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.c b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.c
new file mode 100644 (file)
index 0000000..c85da95
--- /dev/null
@@ -0,0 +1,2 @@
+int test1_variable = 0;
+void test1(){};
diff --git a/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o
new file mode 100644 (file)
index 0000000..b6c2676
Binary files /dev/null and b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o differ
diff --git a/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o.abi b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o.abi
new file mode 100644 (file)
index 0000000..2d456da
--- /dev/null
@@ -0,0 +1,16 @@
+<abi-corpus path='test44-suppr-sym-name-not-regexp-v1.o' architecture='elf-amd-x86_64'>
+  <elf-function-symbols>
+    <elf-symbol name='test1' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-function-symbols>
+  <elf-variable-symbols>
+    <elf-symbol name='test1_variable' size='4' type='object-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+  </elf-variable-symbols>
+  <abi-instr version='1.0' address-size='64' path='test44-suppr-sym-name-not-regexp-v1.c' comp-dir-path='/home/dodji/git/libabigail/PR25095/tests/data/test-diff-suppr' language='LANG_C99'>
+    <type-decl name='int' size-in-bits='32' id='type-id-1'/>
+    <type-decl name='void' id='type-id-2'/>
+    <var-decl name='test1_variable' type-id='type-id-1' mangled-name='test1_variable' visibility='default' filepath='/home/dodji/git/libabigail/PR25095/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.c' line='1' column='1' elf-symbol-id='test1_variable'/>
+    <function-decl name='test1' mangled-name='test1' filepath='/home/dodji/git/libabigail/PR25095/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.c' line='2' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='test1'>
+      <return type-id='type-id-2'/>
+    </function-decl>
+  </abi-instr>
+</abi-corpus>
diff --git a/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp.suppr.txt b/tests/data/test-diff-suppr/test44-suppr-sym-name-not-regexp.suppr.txt
new file mode 100644 (file)
index 0000000..343de25
--- /dev/null
@@ -0,0 +1,5 @@
+[suppress_function]
+  symbol_name_not_regexp = test1
+
+[suppress_variable]
+ symbol_name_not_regexp = test1_variable
index 55f8f8502fbcf01b54a315f2db86c38f741badcc..9fe67db8a543c04638e5a5e8b3f7e0934a24037a 100644 (file)
@@ -1778,6 +1778,46 @@ InOutSpec in_out_specs[] =
     "data/test-diff-suppr/test43-suppr-direct-fn-subtype-report-1.txt",
     "output/test-diff-suppr/test43-suppr-direct-fn-subtype-report-1.txt"
   },
+  {
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o",
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-1.txt",
+    "output/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-1.txt"
+  },
+  {
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o",
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o",
+    "",
+    "",
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp.suppr.txt",
+    "--no-default-suppression",
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-2.txt",
+    "output/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-2.txt"
+  },
+  {
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o.abi",
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o.abi",
+    "",
+    "",
+    "",
+    "--no-default-suppression",
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-1.txt",
+    "output/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-1.txt"
+  },
+  {
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v0.o.abi",
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-v1.o.abi",
+    "",
+    "",
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp.suppr.txt",
+    "--no-default-suppression",
+    "data/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-2.txt",
+    "output/test-diff-suppr/test44-suppr-sym-name-not-regexp-report-2.txt"
+  },
   // This should be the last entry
   {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
 };