// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -*- Mode: C++ -*- // // Copyright (C) 2016-2020 Red Hat, Inc. // // Author: Dodji Seketeli /// @file /// /// This contains the implementation of the suppression engine of /// libabigail. #include #include "abg-internal.h" #include #include // ABG_BEGIN_EXPORT_DECLARATIONS #include "abg-ini.h" #include "abg-comp-filter.h" #include "abg-suppression.h" #include "abg-tools-utils.h" ABG_END_EXPORT_DECLARATIONS // #include "abg-suppression-priv.h" namespace abigail { namespace suppr { using std::dynamic_pointer_cast; using regex::regex_t_sptr; // // section parsing /// Check if a section has at least one of the given properties. /// /// @param names pointer to the start of an array of names. /// /// @param count number of names in the array. /// /// @return whether at least of one the properties was found. bool check_sufficient_props(const char *const * names, size_t count, const ini::config::section& section) { for (const char *const * name = names; name < names + count; ++name) if (section.find_property(*name)) return true; // TODO: Possibly give reason for failure in a message here. return false; } // // /// Constructor for @ref suppression_base /// /// @param a label for the suppression. This represents just a /// comment. suppression_base::suppression_base(const string& label) : priv_(new priv(label)) {} /// Constructor for @ref suppression_base /// /// @param a label for the suppression. This represents just a /// comment. /// /// @param file_name_regex_str the regular expression that denotes the /// file name to match. /// /// @param file_name_not_regex_str the regular expression that denotes /// the file name to *NOT* match. suppression_base::suppression_base(const string& label, const string& file_name_regex_str, const string& file_name_not_regex_str) : priv_(new priv(label, file_name_regex_str, file_name_not_regex_str)) { } /// Tests if the current suppression specification is to avoid adding /// the matched ABI artifact to the internal representation or not. /// /// @return true iff the current suppression specification is to avoid /// adding the matched ABI artifact to the internal representation. bool suppression_base::get_drops_artifact_from_ir() const {return priv_->drops_artifact_;} /// Set the flag that says whether the current suppression /// specification is to avoid adding the matched ABI artifact to the /// internal representation or not. /// /// @param f the flag to set to true iff the current suppression /// specification is to avoid adding the matched ABI artifact to the /// internal representation. void suppression_base::set_drops_artifact_from_ir(bool f) {priv_->drops_artifact_ = f;} /// Test is the suppression specification is artificial. /// /// Artificial means that the suppression was automatically generated /// by libabigail, rather than being constructed from a suppression /// file provided by the user. /// /// @return TRUE iff the suppression specification is artificial. bool suppression_base::get_is_artificial() const {return priv_->is_artificial_;} /// Set a flag saying if the suppression specification is artificial /// or not. /// /// Artificial means that the suppression was automatically generated /// by libabigail, rather than being constructed from a suppression /// file provided by the user. void suppression_base::set_is_artificial(bool f) {priv_->is_artificial_ = f;} /// Getter for the label associated to this suppression specification. /// /// @return the label. const string suppression_base::get_label() const {return priv_->label_;} /// Setter for the label associated to this suppression specification. /// /// @param label the new label. void suppression_base::set_label(const string& label) {priv_->label_ = label;} /// Setter for the "file_name_regex" property of the current instance /// of @ref suppression_base. /// /// The "file_name_regex" property is a regular expression string that /// designates the file name that contains the ABI artifact this /// suppression should apply to. /// /// @param regexp the new regular expression string. void suppression_base::set_file_name_regex_str(const string& regexp) {priv_->file_name_regex_str_ = regexp;} /// Getter for the "file_name_regex" property of the current instance /// of @ref suppression_base. /// /// The "file_name_regex" property is a regular expression string that /// designates the file name that contains the ABI artifacts this /// suppression should apply to. /// /// @return the regular expression string. const string& suppression_base::get_file_name_regex_str() const {return priv_->file_name_regex_str_;} /// Setter for the "file_name_not_regex" property of the current /// instance of @ref suppression_base. /// /// The current suppression specification should apply to ABI /// artifacts of a file which name does *NOT* match the regular /// expression string designated by the "file_name_not_regex" /// property. /// /// @param regexp the new regular expression string. void suppression_base::set_file_name_not_regex_str(const string& regexp) {priv_->file_name_not_regex_str_ = regexp;} /// Getter for the "file_name_not_regex" property of the current /// instance of @ref suppression_base. /// /// The current suppression specification should apply to ABI /// artifacts of a file which name does *NOT* match the regular /// expression string designated by the "file_name_not_regex" /// property. /// /// @return the regular expression string. const string& suppression_base::get_file_name_not_regex_str() const {return priv_->file_name_not_regex_str_;} /// Test if the current suppression has a property related to file /// name. /// /// @return true iff the current suppression has either a /// file_name_regex or a file_name_not_regex property. bool suppression_base::has_file_name_related_property() const { return (!(get_file_name_regex_str().empty() && get_file_name_not_regex_str().empty())); } /// Setter of the "soname_regex_str property of the current instance /// of @ref suppression_base. /// /// The "soname_regex_str" is a regular expression string that /// designates the soname of the shared library that contains the ABI /// artifacts this suppression should apply to. /// /// @param regexp the new regular expression string. void suppression_base::set_soname_regex_str(const string& regexp) {priv_->soname_regex_str_ = regexp;} /// Getter of the "soname_regex_str property of the current instance /// of @ref suppression_base. /// /// The "soname_regex_str" is a regular expression string that /// designates the soname of the shared library that contains the ABI /// artifacts this suppression should apply to. /// /// @return the regular expression string. const string& suppression_base::get_soname_regex_str() const {return priv_->soname_regex_str_;} /// Setter of the "soname_not_regex_str property of the current /// instance of @ref suppression_base. /// /// The current suppression specification should apply to ABI /// artifacts of a shared library which SONAME does *NOT* match the /// regular expression string designated by the "soname_not_regex" /// property. /// /// @param regexp the new regular expression string. void suppression_base::set_soname_not_regex_str(const string& regexp) {priv_->soname_not_regex_str_ = regexp;} /// Getter of the "soname_not_regex_str property of the current /// instance of @ref suppression_base. /// /// The current suppression specification should apply to ABI /// artifacts of a shared library which SONAME does *NOT* match the /// regular expression string designated by the "soname_not_regex" /// property. /// /// @return the regular expression string. const string& suppression_base::get_soname_not_regex_str() const {return priv_->soname_not_regex_str_;} /// Test if the current suppression has a property related to SONAMEs. /// /// @return true iff the current suppression has either a soname_regex /// or a soname_not_regex property. bool suppression_base::has_soname_related_property() const { return (!(get_soname_regex_str().empty() && get_soname_not_regex_str().empty())); } /// Check if the SONAMEs of the two binaries being compared match the /// content of the properties "soname_regexp" and "soname_not_regexp" /// of the current suppression specification. /// /// @param suppr the suppression specification /// /// @param ctxt the context of the comparison. /// /// @return false if the regular expression contained in the property /// soname_regexp or in the property "soname_not_regexp" does *NOT* /// match at least one of the SONAMEs of the two binaries being /// compared. Return true otherwise. static bool sonames_of_binaries_match(const suppression_base& suppr, const diff_context& ctxt) { // Check if the sonames of the binaries match string first_soname = ctxt.get_corpus_diff()->first_corpus()->get_soname(), second_soname = ctxt.get_corpus_diff()->second_corpus()->get_soname(); if (!suppr.has_soname_related_property()) return false; if (!suppr.priv_->matches_soname(first_soname) && !suppr.priv_->matches_soname(second_soname)) return false; return true; } /// Check if the names of the two binaries being compared match the /// content of the properties "file_name_regexp" and /// "file_name_not_regexp". /// /// @param suppr the current suppression specification. /// /// @param ctxt the context of the comparison. /// /// @return false if the regular expression contained in the property /// file_name_regexp or in the property "file_name_not_regexp" does /// *NOT* match at least one of the names of the two binaries being /// compared. Return true otherwise. static bool names_of_binaries_match(const suppression_base& suppr, const diff_context &ctxt) { // Check if the file names of the binaries match string first_binary_path = ctxt.get_corpus_diff()->first_corpus()->get_path(), second_binary_path = ctxt.get_corpus_diff()->second_corpus()->get_path(); if (!suppr.has_file_name_related_property()) return false; if (!suppr.priv_->matches_binary_name(first_binary_path) && !suppr.priv_->matches_binary_name(second_binary_path)) return false; return true; } suppression_base::~suppression_base() {} static type_suppression_sptr read_type_suppression(const ini::config::section& section); static function_suppression_sptr read_function_suppression(const ini::config::section& section); static variable_suppression_sptr read_variable_suppression(const ini::config::section& section); static file_suppression_sptr read_file_suppression(const ini::config::section& section); /// Read a vector of suppression specifications from the sections of /// an ini::config. /// /// Note that this function needs to be updated each time a new kind /// of suppression specification is added. /// /// @param config the config to read from. /// /// @param suppressions out parameter. The vector of suppressions to /// append the newly read suppressions to. static void read_suppressions(const ini::config& config, suppressions_type& suppressions) { suppression_sptr s; for (ini::config::sections_type::const_iterator i = config.get_sections().begin(); i != config.get_sections().end(); ++i) if ((s = read_type_suppression(**i)) || (s = read_function_suppression(**i)) || (s = read_variable_suppression(**i)) || (s = read_file_suppression(**i))) suppressions.push_back(s); } /// Read suppressions specifications from an input stream. /// /// @param input the input stream to read from. /// /// @param suppressions the vector of suppressions to append the newly /// read suppressions to. void read_suppressions(std::istream& input, suppressions_type& suppressions) { if (ini::config_sptr config = ini::read_config(input)) read_suppressions(*config, suppressions); } /// Read suppressions specifications from an input file on disk. /// /// @param input the path to the input file to read from. /// /// @param suppressions the vector of suppressions to append the newly /// read suppressions to. void read_suppressions(const string& file_path, suppressions_type& suppressions) { if (ini::config_sptr config = ini::read_config(file_path)) read_suppressions(*config, suppressions); } // // /// Constructor for @ref type_suppression. /// /// @param label the label of the suppression. This is just a free /// form comment explaining what the suppression is about. /// /// @param type_name_regexp the regular expression describing the /// types about which diff reports should be suppressed. If it's an /// empty string, the parameter is ignored. /// /// @param type_name the name of the type about which diff reports /// should be suppressed. If it's an empty string, the parameter is /// ignored. /// /// Note that parameter @p type_name_regexp and @p type_name_regexp /// should not necessarily be populated. It usually is either one or /// the other that the user wants. type_suppression::type_suppression(const string& label, const string& type_name_regexp, const string& type_name) : suppression_base(label), priv_(new priv(type_name_regexp, type_name, /*consider_type_kind=*/false, /*type_kind=*/CLASS_TYPE_KIND, /*consider_reach_kind=*/false, /*reach_kind=*/DIRECT_REACH_KIND)) {} type_suppression::~type_suppression() {} /// Setter for the "type_name_regex" property of the type suppression /// specification. /// /// This sets a regular expression that specifies the family of types /// about which diff reports should be suppressed. /// /// @param name_regex_str the new regular expression to set. void type_suppression::set_type_name_regex_str(const string& name_regex_str) {priv_->type_name_regex_str_ = name_regex_str;} /// Getter for the "type_name_regex" property of the type suppression /// specification. /// /// This returns a regular expression string that specifies the family /// of types about which diff reports should be suppressed. /// /// @return the regular expression string. const string& type_suppression::get_type_name_regex_str() const {return priv_->type_name_regex_str_;} /// Setter for the "type_name_not_regex_str" property of the type /// suppression specification. /// /// This returns a regular expression string that specifies the family /// of types that should be kept after suppression. /// /// @param r the new regexp string. void type_suppression::set_type_name_not_regex_str(const string& r) {priv_->set_type_name_not_regex_str(r);} /// Getter for the "type_name_not_regex_str" property of the type /// suppression specification. /// /// This returns a regular expression string that specifies the family /// of types that should be kept after suppression. /// /// @return the new regexp string. const string& type_suppression::get_type_name_not_regex_str() const {return priv_->get_type_name_not_regex_str();} /// Setter for the name of the type about which diff reports should be /// suppressed. /// /// @param name the new type name. void type_suppression::set_type_name(const string& name) {priv_->type_name_ = name;} /// Getter for the name of the type about which diff reports should be /// suppressed. /// /// @param return the type name. const string& type_suppression::get_type_name() const {return priv_->type_name_;} /// Getter of the property that says whether to consider the kind of /// type this suppression is about. /// /// @return the boolean value of the property. bool type_suppression::get_consider_type_kind() const {return priv_->consider_type_kind_;} /// Setter of the property that says whether to consider the kind of /// type this suppression is about. /// /// @param f the new boolean value of the property. void type_suppression::set_consider_type_kind(bool f) {priv_->consider_type_kind_ = f;} /// Setter of the kind of type this suppression is about. /// /// Note that this will be considered during evaluation of the /// suppression only if type_suppression::get_consider_type_kind() /// returns true. /// /// @param k the new kind of type this suppression is about. void type_suppression::set_type_kind(type_kind k) {priv_->type_kind_ = k;} /// Getter of the kind of type this suppression is about. /// /// Note that this will be considered during evaluation of the /// suppression only if type_suppression::get_consider_type_kind() /// returns true. /// /// @return the kind of type this suppression is about. type_suppression::type_kind type_suppression::get_type_kind() const {return priv_->type_kind_;} /// Test if the current type suppression specification /// suggests to consider how the matching diff node is reached. /// /// @return true if the current type suppression specification /// suggests to consider how the matching diff node is reached. bool type_suppression::get_consider_reach_kind() const {return priv_->consider_reach_kind_;} /// Set a flag saying if the current type suppression specification /// suggests to consider how the matching diff node is reached. /// /// @param f the new value of the flag. It's true iff the current /// type suppression specification suggests to consider how the /// matching diff node is reached. void type_suppression::set_consider_reach_kind(bool f) {priv_->consider_reach_kind_ = f;} /// Getter of the way the diff node matching the current suppression /// specification is to be reached. /// /// @return the way the diff node matching the current suppression /// specification is to be reached. type_suppression::reach_kind type_suppression::get_reach_kind() const {return priv_->reach_kind_;} /// Setter of the way the diff node matching the current suppression /// specification is to be reached. /// /// @param p the way the diff node matching the current suppression /// specification is to be reached. void type_suppression::set_reach_kind(reach_kind k) {priv_->reach_kind_ = k;} /// Setter for the vector of data member insertion ranges that /// specifies where a data member is inserted as far as this /// suppression specification is concerned. /// /// @param r the new insertion range vector. void type_suppression::set_data_member_insertion_ranges(const insertion_ranges& r) {priv_->insertion_ranges_ = r;} /// Getter for the vector of data member insertion range that /// specifiers where a data member is inserted as far as this /// suppression specification is concerned. /// /// @return the vector of insertion ranges. const type_suppression::insertion_ranges& type_suppression::get_data_member_insertion_ranges() const {return priv_->insertion_ranges_;} /// Getter for the vector of data member insertion range that /// specifiers where a data member is inserted as far as this /// suppression specification is concerned. /// /// @return the vector of insertion ranges. type_suppression::insertion_ranges& type_suppression::get_data_member_insertion_ranges() {return priv_->insertion_ranges_;} /// Getter for the array of source location paths of types that should /// *NOT* be suppressed. /// /// @return the set of source locations of types that should *NOT* be /// supressed. const unordered_set& type_suppression::get_source_locations_to_keep() const {return priv_->source_locations_to_keep_;} /// Getter for the array of source location paths of types that should /// *NOT* be suppressed. /// /// @return the array of source locations of types that should *NOT* /// be supressed. unordered_set& type_suppression::get_source_locations_to_keep() {return priv_->source_locations_to_keep_;} /// Setter for the array of source location paths of types that should /// *NOT* be suppressed. /// /// @param l the new array. void type_suppression::set_source_locations_to_keep (const unordered_set& l) {priv_->source_locations_to_keep_ = l;} /// Getter of the regular expression string that designates the source /// location paths of types that should not be suppressed. /// /// @return the regular expression string. const string& type_suppression::get_source_location_to_keep_regex_str() const {return priv_->source_location_to_keep_regex_str_;} /// Setter of the regular expression string that designates the source /// location paths of types that should not be suppressed. /// /// @param r the new regular expression. void type_suppression::set_source_location_to_keep_regex_str(const string& r) {priv_->source_location_to_keep_regex_str_ = r;} /// Getter of the vector of the changed enumerators that are supposed /// to be suppressed. Note that this will be "valid" only if the type /// suppression has the 'type_kind = enum' property. /// /// @return the vector of the changed enumerators that are supposed to /// be suppressed. const vector& type_suppression::get_changed_enumerator_names() const {return priv_->changed_enumerator_names_;} /// Setter of the vector of changed enumerators that are supposed to /// be suppressed. Note that this will be "valid" only if the type /// suppression has the 'type_kind = enum' property. /// /// @param n the vector of the changed enumerators that are supposed /// to be suppressed. void type_suppression::set_changed_enumerator_names(const vector& n) {priv_->changed_enumerator_names_ = n;} /// Evaluate this suppression specification on a given diff node and /// say if the diff node should be suppressed or not. /// /// @param diff the diff node to evaluate this suppression /// specification against. /// /// @return true if @p diff should be suppressed. bool type_suppression::suppresses_diff(const diff* diff) const { const type_diff_base* d = is_type_diff(diff); if (!d) { // So the diff we are looking at is not a type diff. However, // there are cases where a type suppression can suppress changes // on functions. // Typically, if a virtual member function's virtual index (its // index in the vtable of a class) changes and if the current // type suppression is meant to suppress change reports about // the enclosing class of the virtual member function, then this // type suppression should suppress reports about that function // change. const function_decl_diff* d = is_function_decl_diff(diff); if (d) { // Let's see if 'd' carries a virtual member function // change. if (comparison::filtering::has_virtual_mem_fn_change(d)) { function_decl_sptr f = d->first_function_decl(); class_decl_sptr fc = is_class_type(is_method_type(f->get_type())->get_class_type()); ABG_ASSERT(fc); if (suppresses_type(fc, diff->context())) return true; } } return false; } // If the suppression should consider the way the diff node has been // reached, then do it now. if (get_consider_reach_kind()) { if (get_reach_kind() == POINTER_REACH_KIND) { if (const pointer_diff* ptr_diff = is_pointer_diff(diff)) { d = is_type_diff(ptr_diff->underlying_type_diff().get()); if (!d) // This might be of, e.g, distinct_diff type. return false; d = is_type_diff(peel_qualified_diff(d)); } else return false; } else if (get_reach_kind() == REFERENCE_REACH_KIND) { if (const reference_diff* ref_diff = is_reference_diff(diff)) { d = is_type_diff(ref_diff->underlying_type_diff().get()); if (!d) // This might be of, e.g, distinct_diff type. return false; d = is_type_diff(peel_qualified_diff(d)); } else return false; } else if (get_reach_kind() == REFERENCE_OR_POINTER_REACH_KIND) { if (const pointer_diff* ptr_diff = is_pointer_diff(diff)) { d = is_type_diff(ptr_diff->underlying_type_diff().get()); ABG_ASSERT(d); d = is_type_diff(peel_qualified_diff(d)); } else if (const reference_diff* ref_diff = is_reference_diff(diff)) { d = is_type_diff(ref_diff->underlying_type_diff().get()); ABG_ASSERT(d); d = is_type_diff(peel_qualified_diff(d)); } else return false; } } type_base_sptr ft, st; ft = is_type(d->first_subject()); st = is_type(d->second_subject()); ABG_ASSERT(ft && st); if (!suppresses_type(ft, d->context()) && !suppresses_type(st, d->context())) { // A private type suppression specification considers that a // type can be private and yet some typedefs of that type can be // public -- depending on, e.g, if the typedef is defined in a // public header or not. So if we are in the context of a // private type suppression let's *NOT* peel typedefs away. if (!is_private_type_suppr_spec(*this)) { ft = peel_typedef_type(ft); st = peel_typedef_type(st); } if (!suppresses_type(ft, d->context()) && !suppresses_type(st, d->context())) return false; d = is_type_diff(get_typedef_diff_underlying_type_diff(d)); } // Now let's consider class diffs in the context of a suppr spec // that contains properties like "has_data_member_inserted_*". const class_diff* klass_diff = dynamic_cast(d); if (klass_diff) { // We are looking at a class diff ... if (!get_data_member_insertion_ranges().empty()) { // ... and the suppr spec contains a // "has_data_member_inserted_*" clause ... if (klass_diff->deleted_data_members().empty() && (klass_diff->first_class_decl()->get_size_in_bits() <= klass_diff->second_class_decl()->get_size_in_bits())) { // That "has_data_member_inserted_*" clause doesn't hold // if the class has deleted data members or shrunk. const class_decl_sptr& first_type_decl = klass_diff->first_class_decl(); for (string_decl_base_sptr_map::const_iterator m = klass_diff->inserted_data_members().begin(); m != klass_diff->inserted_data_members().end(); ++m) { decl_base_sptr member = m->second; size_t dm_offset = get_data_member_offset(member); bool matched = false; for (insertion_ranges::const_iterator i = get_data_member_insertion_ranges().begin(); i != get_data_member_insertion_ranges().end(); ++i) { type_suppression::insertion_range_sptr range = *i; uint64_t range_begin_val = 0, range_end_val = 0; if (!type_suppression::insertion_range::eval_boundary (range->begin(), first_type_decl, range_begin_val)) break; if (!type_suppression::insertion_range::eval_boundary (range->end(), first_type_decl, range_end_val)) break; uint64_t range_begin = range_begin_val; uint64_t range_end = range_end_val; if (insertion_range::boundary_value_is_end(range_begin) && insertion_range::boundary_value_is_end(range_end)) { // This idiom represents the predicate // "has_data_member_inserted_at = end" if (dm_offset > get_data_member_offset(get_last_data_member (first_type_decl))) { // So the data member was added after // last data member of the klass. That // matches the suppr spec // "has_data_member_inserted_at = end". matched = true; continue; } } if (range_begin > range_end) // Wrong suppr spec. Ignore it. continue; if (dm_offset < range_begin || dm_offset > range_end) // The offset of the added data member doesn't // match the insertion range specified. So // the diff object won't be suppressed. continue; // If we reached this point, then all the // insertion range constraints have been // satisfied. So matched = true; } if (!matched) return false; } } else return false; } } const enum_diff* enum_dif = dynamic_cast(d); if (// We are looking at an enum diff node which ... enum_dif //... carries no deleted enumerator ... " && enum_dif->deleted_enumerators().empty() // ... carries no size change ... && (enum_dif->first_enum()->get_size_in_bits() == enum_dif->second_enum()->get_size_in_bits()) // ... and yet carries some changed enumerators! && !enum_dif->changed_enumerators().empty()) { // Make sure that all changed enumerators are listed in the // vector of enumerator names returned by the // get_changed_enumerator_names() member function. bool matched = true; for (string_changed_enumerator_map::const_iterator i = enum_dif->changed_enumerators().begin(); i != enum_dif->changed_enumerators().end(); ++i) { matched &= true; if (std::find(get_changed_enumerator_names().begin(), get_changed_enumerator_names().end(), i->first) == get_changed_enumerator_names().end()) { matched &= false; break; } } if (!matched) return false; } return true; } /// Test if the current instance of @ref type_suppression suppresses a /// change reports about a given type. /// /// @param type the type to consider. /// /// @param ctxt the context of comparison we are involved with. /// /// @return true iff the suppression specification suppresses type @p /// type. bool type_suppression::suppresses_type(const type_base_sptr& type, const diff_context_sptr& ctxt) const { if (ctxt) { // Check if the names of the binaries match the suppression if (!names_of_binaries_match(*this, *ctxt)) if (has_file_name_related_property()) return false; // Check if the sonames of the binaries match the suppression if (!sonames_of_binaries_match(*this, *ctxt)) if (has_soname_related_property()) return false; } return suppresses_type(type); } /// Test if an instance of @ref type_suppression matches a given type. /// /// This function does not take the name of the type into account /// while testing if the type matches the type_suppression. /// /// @param s the suppression to evaluate. /// /// @param type the type to consider. /// /// @return true iff the suppression specification matches type @p /// type without taking its name into account. static bool suppression_matches_type_no_name(const type_suppression& s, const type_base_sptr &type) { // If the suppression should consider type kind then, well, check // for that. if (s.get_consider_type_kind()) { type_suppression::type_kind tk = s.get_type_kind(); bool matches = true; switch (tk) { case type_suppression::UNKNOWN_TYPE_KIND: case type_suppression::CLASS_TYPE_KIND: if (!is_class_type(type)) matches = false; break; case type_suppression::STRUCT_TYPE_KIND: { class_decl_sptr klass = is_class_type(type); if (!klass || !klass->is_struct()) matches = false; } break; case type_suppression::UNION_TYPE_KIND: if (!is_union_type(type)) matches = false; break; case type_suppression::ENUM_TYPE_KIND: if (!is_enum_type(type)) matches = false; break; case type_suppression::ARRAY_TYPE_KIND: if (!is_array_type(type)) matches = false; break; case type_suppression::TYPEDEF_TYPE_KIND: if (!is_typedef(type)) matches = false; break; case type_suppression::BUILTIN_TYPE_KIND: if (!is_type_decl(type)) matches = false; break; } if (!matches) return false; } // Check if there is a source location related match. if (!suppression_matches_type_location(s, type)) return false; return true; } /// Test if a type suppression specification matches a type name. /// /// @param s the type suppression to consider. /// /// @param type_name the type name to consider. /// /// @return true iff the type designated by its name @p type_name is /// matched by the type suppression specification @p s. bool suppression_matches_type_name(const type_suppression& s, const string& type_name) { if (!s.get_type_name().empty() || s.priv_->get_type_name_regex() || s.priv_->get_type_name_not_regex()) { // Check if there is an exact type name match. if (!s.get_type_name().empty()) { if (s.get_type_name() != type_name) return false; } else { // Now check if there is a regular expression match. // // If the qualified name of the considered type doesn't match // the regular expression of the type name, then this // suppression doesn't apply. if (const regex_t_sptr& type_name_regex = s.priv_->get_type_name_regex()) { if (!regex::match(type_name_regex, type_name)) return false; } if (const regex_t_sptr type_name_not_regex = s.priv_->get_type_name_not_regex()) { if (regex::match(type_name_not_regex, type_name)) return false; } } } return true; } /// Test if a type suppression matches a type in a particular scope. /// /// @param s the type suppression to consider. /// /// @param type_scope the scope of the type to consider. /// /// @param type the type to consider. /// /// @return true iff the supression @p s matches type @p type in scope /// @p type_scope. bool suppression_matches_type_name(const suppr::type_suppression& s, const scope_decl* type_scope, const type_base_sptr& type) { string type_name = build_qualified_name(type_scope, type); return suppression_matches_type_name(s, type_name); } /// Test if a type suppression matches a source location. /// /// @param s the type suppression to consider. /// /// @param loc the location to consider. /// /// @return true iff the suppression @p s matches location @p loc. bool suppression_matches_type_location(const type_suppression& s, const location& loc) { if (loc) { // Check if there is a source location related match. string loc_path, loc_path_base; unsigned loc_line = 0, loc_column = 0; loc.expand(loc_path, loc_line, loc_column); if (regex_t_sptr regexp = s.priv_->get_source_location_to_keep_regex()) if (regex::match(regexp, loc_path)) return false; tools_utils::base_name(loc_path, loc_path_base); if (s.get_source_locations_to_keep().find(loc_path_base) != s.get_source_locations_to_keep().end()) return false; if (s.get_source_locations_to_keep().find(loc_path) != s.get_source_locations_to_keep().end()) return false; } else { if (!s.get_source_locations_to_keep().empty() || s.priv_->get_source_location_to_keep_regex()) // The user provided a "source_location_not_regexp" or // a "source_location_not_in" property that was not // triggered. This means the current type suppression // doesn't suppress the type given. return false; } return true; } /// Test if a type suppression matches a type. /// /// @param s the type suppression to consider. /// /// @param type the type to consider. /// /// @return true iff the suppression @p s matches type @p type. bool suppression_matches_type_location(const type_suppression& s, const type_base_sptr& type) { location loc = get_location(type); if (loc) return suppression_matches_type_location(s, loc); else { // The type had no source location. // // In the case where this type suppression was automatically // generated to suppress types not defined in public // headers, then this might mean that the type is not // defined in the public headers. Otherwise, why does it // not have a source location? if (s.get_is_artificial()) { if (class_decl_sptr cl = is_class_type(type)) { if (cl->get_is_declaration_only()) // We tried hard above to get the definition of // the declaration. If we reach this place, it // means the class has no definition at this point. ABG_ASSERT(!cl->get_definition_of_declaration()); if (s.get_label() == get_private_types_suppr_spec_label()) // So this looks like what really amounts to an // opaque type. So it's not defined in the public // headers. So we want to filter it out. return true; } } if (!s.get_source_locations_to_keep().empty() || s.priv_->get_source_location_to_keep_regex()) // The user provided a "source_location_not_regexp" or // a "source_location_not_in" property that was not // triggered. This means the current type suppression // doesn't suppress the type given. return false; } return true; } /// Test if a type suppression matches a type name and location. /// /// @param s the type suppression to consider. /// /// @param type_name the name of the type to consider. /// /// @param type_location the location of the type to consider. /// /// @return true iff suppression @p s matches a type named @p /// type_name with a location @p type_location. bool suppression_matches_type_name_or_location(const type_suppression& s, const string& type_name, const location& type_location) { if (!suppression_matches_type_name(s, type_name)) return false; if (!suppression_matches_type_location(s, type_location)) return false; return true; } /// Test if the current instance of @ref type_suppression matches a /// given type. /// /// @param type the type to consider. /// /// @return true iff the suppression specification suppresses type @p /// type. bool type_suppression::suppresses_type(const type_base_sptr& type) const { if (!suppression_matches_type_no_name(*this, type)) return false; if (!suppression_matches_type_name(*this, get_name(type))) return false; return true; } /// Test if the current instance of @ref type_suppression matches a /// given type in a given scope. /// /// @param type the type to consider. /// /// @param type_scope the scope of type @p type. /// /// @return true iff the suppression specification suppresses type @p /// type from scope @p type_scope. bool type_suppression::suppresses_type(const type_base_sptr& type, const scope_decl* type_scope) const { if (!suppression_matches_type_no_name(*this, type)) return false; if (!suppression_matches_type_name(*this, type_scope, type)) return false; return true; } /// The private data of type_suppression::insertion_range struct type_suppression::insertion_range::priv { boundary_sptr begin_; boundary_sptr end_; priv() {} priv(boundary_sptr begin, boundary_sptr end) : begin_(begin), end_(end) {} }; // end struct type_suppression::insertion_range::priv /// Default Constructor of @ref type_suppression::insertion_range. type_suppression::insertion_range::insertion_range() : priv_(new priv) {} /// Constructor of @ref type_suppression::insertion_range. /// /// @param begin the start of the range. A range boundary that is an /// instance of @ref interger_boundary with a negative value means the /// maximum possible value. /// /// @param end the end of the range. A range boundary that is an /// instance of @ref interger_boundary with a negative value means the /// maximum possible value. type_suppression::insertion_range::insertion_range(boundary_sptr begin, boundary_sptr end) : priv_(new priv(begin, end)) {} /// Getter for the beginning of the range. /// /// @return the beginning of the range. A range boundary that is an /// instance of @ref interger_boundary with a negative value means the /// maximum possible value. type_suppression::insertion_range::boundary_sptr type_suppression::insertion_range::begin() const {return priv_->begin_;} /// Getter for the end of the range. /// /// @return the end of the range. A range boundary that is an /// instance of @ref interger_boundary with a negative value means the /// maximum possible value. type_suppression::insertion_range::boundary_sptr type_suppression::insertion_range::end() const {return priv_->end_;} /// Create an integer boundary. /// /// The return value of this function is to be used as a boundary for /// an instance of @ref type_suppression::insertion_range. That /// boundary evaluates to an integer value. /// /// @param value the value of the integer boundary. /// /// @return the resulting integer boundary. type_suppression::insertion_range::integer_boundary_sptr type_suppression::insertion_range::create_integer_boundary(int value) {return integer_boundary_sptr(new integer_boundary(value));} /// Create a function call expression boundary. /// /// The return value of this function is to be used as a boundary for /// an instance of @ref type_suppression::insertion_range. The value /// of that boundary is actually a function call expression that /// itself evalutates to an integer value, in the context of a @ref /// class_decl. /// /// @param expr the function call expression to create the boundary from. /// /// @return the resulting function call expression boundary. type_suppression::insertion_range::fn_call_expr_boundary_sptr type_suppression::insertion_range::create_fn_call_expr_boundary(ini::function_call_expr_sptr expr) {return fn_call_expr_boundary_sptr(new fn_call_expr_boundary(expr));} /// Create a function call expression boundary. /// /// The return value of this function is to be used as a boundary for /// an instance of @ref type_suppression::insertion_range. The value /// of that boundary is actually a function call expression that /// itself evalutates to an integer value, in the context of a @ref /// class_decl. /// /// @param s a string representing the expression the function call /// expression to create the boundary from. /// /// @return the resulting function call expression boundary. type_suppression::insertion_range::fn_call_expr_boundary_sptr type_suppression::insertion_range::create_fn_call_expr_boundary(const string& s) { fn_call_expr_boundary_sptr result, nil; ini::function_call_expr_sptr expr; if (ini::read_function_call_expr(s, expr) && expr) result.reset(new fn_call_expr_boundary(expr)); return result; } /// Evaluate an insertion range boundary to get a resulting integer /// value. /// /// @param boundary the boundary to evaluate. /// /// @param context the context of evualuation. It's a @ref class_decl /// to take into account during the evaluation, if there is a need for /// it. /// /// @return true iff the evaluation was successful and @p value /// contains the resulting value. bool type_suppression::insertion_range::eval_boundary(boundary_sptr boundary, class_decl_sptr context, uint64_t& value) { if (integer_boundary_sptr b = is_integer_boundary(boundary)) { value = b->as_integer(); return true; } else if (fn_call_expr_boundary_sptr b = is_fn_call_expr_boundary(boundary)) { ini::function_call_expr_sptr fn_call = b->as_function_call_expr(); if ((fn_call->get_name() == "offset_of" || fn_call->get_name() == "offset_after") && fn_call->get_arguments().size() == 1) { string member_name = fn_call->get_arguments()[0]; for (class_decl::data_members::const_iterator it = context->get_data_members().begin(); it != context->get_data_members().end(); ++it) { if (!get_data_member_is_laid_out(**it)) continue; if ((*it)->get_name() == member_name) { if (fn_call->get_name() == "offset_of") value = get_data_member_offset(*it); else if (fn_call->get_name() == "offset_after") { if (!get_next_data_member_offset(context, *it, value)) { value = get_data_member_offset(*it) + (*it)->get_type()->get_size_in_bits(); } } else // We should not reach this point. abort(); return true; } } } } return false; } /// Test if a given value supposed to be inside an insertion range /// represents the end of the range. /// /// @param value the value to test for. /// /// @return true iff @p value represents the end of the insertion /// range. bool type_suppression::insertion_range::boundary_value_is_end(uint64_t value) { return value == std::numeric_limits::max(); } /// Tests if a given instance of @ref /// type_suppression::insertion_range::boundary is actually an integer boundary. /// /// @param b the boundary to test. /// /// @return a pointer to the instance of /// type_suppression::insertion_range::integer_boundary if @p b is /// actually an integer boundary. Otherwise, return a null pointer. type_suppression::insertion_range::integer_boundary_sptr is_integer_boundary(type_suppression::insertion_range::boundary_sptr b) {return dynamic_pointer_cast(b);} /// Tests if a given instance of @ref /// type_suppression::insertion_range::boundary is actually an function call expression boundary. /// /// @param b the boundary to test. /// /// @return a pointer to the instance of /// type_suppression::insertion_range::fn_call_expr_boundary if @p b /// is actually an function call expression boundary. Otherwise, /// return a null pointer. type_suppression::insertion_range::fn_call_expr_boundary_sptr is_fn_call_expr_boundary(type_suppression::insertion_range::boundary_sptr b) {return dynamic_pointer_cast(b);} /// The private data type of @ref /// type_suppression::insertion_range::boundary. struct type_suppression::insertion_range::boundary::priv { priv() {} }; // end struct type_suppression::insertion_range::boundary::priv /// Default constructor of @ref /// type_suppression::insertion_range::boundary type_suppression::insertion_range::boundary::boundary() : priv_(new priv()) {} /// Destructor of @ref type_suppression::insertion_range::boundary. type_suppression::insertion_range::boundary::~boundary() {} /// Private data type for @ref /// type_suppression::insertion_range::integer_boundary. struct type_suppression::insertion_range::integer_boundary::priv { uint64_t value_; priv() : value_() {} priv(uint64_t value) : value_(value) {} }; // end type_suppression::insertion_range::integer_boundary::priv /// Converting constructor of /// type_suppression::insertion_range::integer_boundary. /// /// @param value the integer value of the newly created integer boundary. type_suppression::insertion_range::integer_boundary::integer_boundary(uint64_t value) : priv_(new priv(value)) {} /// Return the integer value of the current instance of @ref /// type_suppression::insertion_range::integer_boundary. /// /// @return the integer value of the current boundary. uint64_t type_suppression::insertion_range::integer_boundary::as_integer() const {return priv_->value_;} /// Converts the current boundary into an integer value. /// /// @return the integer value of the current boundary. type_suppression::insertion_range::integer_boundary::operator uint64_t() const {return as_integer();} /// Destructor of @ref type_suppression::insertion_range::integer_boundary. type_suppression::insertion_range::integer_boundary::~integer_boundary() {} /// Private data type of type @ref /// type_suppression::insertion_range::fn_call_expr_boundary. struct type_suppression::insertion_range::fn_call_expr_boundary::priv { ini::function_call_expr_sptr expr_; priv() {} priv(ini::function_call_expr_sptr expr) : expr_(expr) {} }; // end struct type_suppression::insertion_range::fn_call_expr_boundary::priv /// Converting constructor for @ref /// type_suppression::insertion_range::fn_call_expr_boundary. /// /// @param expr the function call expression to build this boundary /// from. type_suppression::insertion_range::fn_call_expr_boundary:: fn_call_expr_boundary(ini::function_call_expr_sptr expr) : priv_(new priv(expr)) {} /// Returns the function call expression value of the current boundary. /// /// @return the function call expression value of the current boundary; ini::function_call_expr_sptr type_suppression::insertion_range::fn_call_expr_boundary::as_function_call_expr() const {return priv_->expr_;} /// Converts the current boundary to its function call expression value. /// /// @return the function call expression value of the current boundary. type_suppression::insertion_range::fn_call_expr_boundary::operator ini::function_call_expr_sptr () const {return as_function_call_expr();} /// Destructor of @ref /// type_suppression::insertion_range::fn_call_expr_boundary. type_suppression::insertion_range::fn_call_expr_boundary::~fn_call_expr_boundary() {} /// Test if an instance of @ref suppression is an instance of @ref /// type_suppression. /// /// @param suppr the instance of @ref suppression to test for. /// /// @return if @p suppr is an instance of @ref type_suppression, then /// return the sub-object of the @p suppr of type @ref /// type_suppression, otherwise return a nil pointer. type_suppression_sptr is_type_suppression(suppression_sptr suppr) {return dynamic_pointer_cast(suppr);} // /// Parse the value of the "type_kind" property in the "suppress_type" /// section. /// /// @param input the input string representing the value of the /// "type_kind" property. /// /// @return the @ref type_kind enumerator parsed. static type_suppression::type_kind read_type_kind_string(const string& input) { if (input == "class") return type_suppression::CLASS_TYPE_KIND; else if (input == "struct") return type_suppression::STRUCT_TYPE_KIND; else if (input == "union") return type_suppression::UNION_TYPE_KIND; else if (input == "enum") return type_suppression::ENUM_TYPE_KIND; else if (input == "array") return type_suppression::ARRAY_TYPE_KIND; else if (input == "typedef") return type_suppression::TYPEDEF_TYPE_KIND; else if (input == "builtin") return type_suppression::BUILTIN_TYPE_KIND; else return type_suppression::UNKNOWN_TYPE_KIND; } /// Parse the value of the "accessed_through" property in the /// "suppress_type" section. /// /// @param input the input string representing the value of the /// "accessed_through" property. /// /// @return the @ref type_suppression::reach_kind enumerator parsed. static type_suppression::reach_kind read_suppression_reach_kind(const string& input) { if (input == "direct") return type_suppression::DIRECT_REACH_KIND; else if (input == "pointer") return type_suppression::POINTER_REACH_KIND; else if (input == "reference") return type_suppression::REFERENCE_REACH_KIND; else if (input == "reference-or-pointer") return type_suppression::REFERENCE_OR_POINTER_REACH_KIND; else return type_suppression::DIRECT_REACH_KIND; } /// Read a type suppression from an instance of ini::config::section /// and build a @ref type_suppression as a result. /// /// @param section the section of the ini config to read. /// /// @return the resulting @ref type_suppression upon successful /// parsing, or nil. static type_suppression_sptr read_type_suppression(const ini::config::section& section) { type_suppression_sptr result; if (section.get_name() != "suppress_type") return result; static const char *const sufficient_props[] = { "file_name_regexp", "file_name_not_regexp", "soname_regexp", "soname_not_regexp", "name", "name_regexp", "name_not_regexp", "type_kind", "source_location_not_in", "source_location_not_regexp", }; if (!check_sufficient_props(sufficient_props, sizeof(sufficient_props)/sizeof(char*), section)) return result; ini::simple_property_sptr drop_artifact = is_simple_property(section.find_property("drop_artifact")); if (!drop_artifact) drop_artifact = is_simple_property(section.find_property("drop")); string drop_artifact_str = drop_artifact ? drop_artifact->get_value()->as_string() : ""; ini::simple_property_sptr label = is_simple_property(section.find_property("label")); string label_str = label ? label->get_value()->as_string() : ""; ini::simple_property_sptr file_name_regex_prop = is_simple_property(section.find_property("file_name_regexp")); string file_name_regex_str = file_name_regex_prop ? file_name_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr file_name_not_regex_prop = is_simple_property(section.find_property("file_name_not_regexp")); string file_name_not_regex_str = file_name_not_regex_prop ? file_name_not_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr soname_regex_prop = is_simple_property(section.find_property("soname_regexp")); string soname_regex_str = soname_regex_prop ? soname_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr soname_not_regex_prop = is_simple_property(section.find_property("soname_not_regexp")); string soname_not_regex_str = soname_not_regex_prop ? soname_not_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr name_regex_prop = is_simple_property(section.find_property("name_regexp")); string name_regex_str = name_regex_prop ? name_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr name_not_regex_prop = is_simple_property(section.find_property("name_not_regexp")); string name_not_regex_str = name_not_regex_prop ? name_not_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr name_prop = is_simple_property(section.find_property("name")); string name_str = name_prop ? name_prop->get_value()->as_string() : ""; ini::property_sptr srcloc_not_in_prop = section.find_property("source_location_not_in"); unordered_set srcloc_not_in; if (srcloc_not_in_prop) { if (ini::simple_property_sptr p = is_simple_property(srcloc_not_in_prop)) srcloc_not_in.insert(p->get_value()->as_string()); else { ini::list_property_sptr list_property = is_list_property(srcloc_not_in_prop); if (list_property) { vector::const_iterator i; for (i = list_property->get_value()->get_content().begin(); i != list_property->get_value()->get_content().end(); ++i) srcloc_not_in.insert(*i); } } } ini::simple_property_sptr srcloc_not_regexp_prop = is_simple_property(section.find_property("source_location_not_regexp")); string srcloc_not_regexp_str; if (srcloc_not_regexp_prop) srcloc_not_regexp_str = srcloc_not_regexp_prop->get_value()->as_string(); bool consider_type_kind = false; type_suppression::type_kind type_kind = type_suppression::UNKNOWN_TYPE_KIND; if (ini::simple_property_sptr type_kind_prop = is_simple_property(section.find_property("type_kind"))) { consider_type_kind = true; type_kind = read_type_kind_string(type_kind_prop->get_value()->as_string()); } bool consider_reach_kind = false; type_suppression::reach_kind reach_kind = type_suppression::DIRECT_REACH_KIND; if (ini::simple_property_sptr reach_kind_prop = is_simple_property(section.find_property("accessed_through"))) { consider_reach_kind = true; reach_kind = read_suppression_reach_kind(reach_kind_prop->get_value()->as_string()); } // Support has_data_member_inserted_at vector insert_ranges; bool consider_data_member_insertion = false; if (ini::simple_property_sptr prop = is_simple_property(section.find_property("has_data_member_inserted_at"))) { // So this property has the form: // has_data_member_inserted_at = string ins_point = prop->get_value()->as_string(); type_suppression::insertion_range::boundary_sptr begin, end; if (ins_point == "end") begin = type_suppression::insertion_range::create_integer_boundary(-1); else if (isdigit(ins_point[0])) begin = type_suppression::insertion_range::create_integer_boundary (atoi(ins_point.c_str())); else if (type_suppression::insertion_range::fn_call_expr_boundary_sptr expr = type_suppression::insertion_range::create_fn_call_expr_boundary(ini::read_function_call_expr(ins_point))) begin = expr; else return result; end = type_suppression::insertion_range::create_integer_boundary(-1); type_suppression::insertion_range_sptr insert_range (new type_suppression::insertion_range(begin, end)); insert_ranges.push_back(insert_range); consider_data_member_insertion = true; } // Support has_data_member_inserted_between if (ini::tuple_property_sptr prop = is_tuple_property(section.find_property ("has_data_member_inserted_between"))) { // ensures that this has the form: // has_data_member_inserted_between = {0 , end}; // and not (for instance): // has_data_member_inserted_between = {{0 , end}, {1, foo}} // // This means that the tuple_property_value contains just one // value, which is a list_property that itself contains 2 // values. type_suppression::insertion_range::boundary_sptr begin, end; ini::tuple_property_value_sptr v = prop->get_value(); if (v && v->get_value_items().size() == 1 && is_list_property_value(v->get_value_items()[0]) && is_list_property_value(v->get_value_items()[0])->get_content().size() == 2) { ini::list_property_value_sptr val = is_list_property_value(v->get_value_items()[0]); ABG_ASSERT(val); string str = val->get_content()[0]; if (str == "end") begin = type_suppression::insertion_range::create_integer_boundary(-1); else if (isdigit(str[0])) begin = type_suppression::insertion_range::create_integer_boundary (atoi(str.c_str())); else if (type_suppression::insertion_range::fn_call_expr_boundary_sptr expr = type_suppression::insertion_range::create_fn_call_expr_boundary(ini::read_function_call_expr(str))) begin = expr; else return result; str = val->get_content()[1]; if (str == "end") end = type_suppression::insertion_range::create_integer_boundary(-1); else if (isdigit(str[0])) end = type_suppression::insertion_range::create_integer_boundary (atoi(str.c_str())); else if (type_suppression::insertion_range::fn_call_expr_boundary_sptr expr = type_suppression::insertion_range::create_fn_call_expr_boundary(ini::read_function_call_expr(str))) end = expr; else return result; type_suppression::insertion_range_sptr insert_range (new type_suppression::insertion_range(begin, end)); insert_ranges.push_back(insert_range); consider_data_member_insertion = true; } else // the 'has_data_member_inserted_between' property has a wrong // value type, so let's discard the endire [suppress_type] // section. return result; } // Support has_data_members_inserted_between // The syntax looks like: // // has_data_members_inserted_between = {{8, 24}, {32, 64}, {128, end}} // // So we expect a tuple property, with potentially several pairs (as // part of the value); each pair designating a range. Note that // each pair (range) is a list property value. if (ini::tuple_property_sptr prop = is_tuple_property(section.find_property ("has_data_members_inserted_between"))) { bool is_well_formed = true; for (vector::const_iterator i = prop->get_value()->get_value_items().begin(); is_well_formed && i != prop->get_value()->get_value_items().end(); ++i) { ini::tuple_property_value_sptr tuple_value = is_tuple_property_value(*i); if (!tuple_value || tuple_value->get_value_items().size() != 1 || !is_list_property_value(tuple_value->get_value_items()[0])) { is_well_formed = false; break; } ini::list_property_value_sptr list_value = is_list_property_value(tuple_value->get_value_items()[0]); if (list_value->get_content().size() != 2) { is_well_formed = false; break; } type_suppression::insertion_range::boundary_sptr begin, end; string str = list_value->get_content()[0]; if (str == "end") begin = type_suppression::insertion_range::create_integer_boundary(-1); else if (isdigit(str[0])) begin = type_suppression::insertion_range::create_integer_boundary (atoi(str.c_str())); else if (type_suppression::insertion_range::fn_call_expr_boundary_sptr expr = type_suppression::insertion_range::create_fn_call_expr_boundary(ini::read_function_call_expr(str))) begin = expr; else return result; str = list_value->get_content()[1]; if (str == "end") end = type_suppression::insertion_range::create_integer_boundary(-1); else if (isdigit(str[0])) end = type_suppression::insertion_range::create_integer_boundary (atoi(str.c_str())); else if (type_suppression::insertion_range::fn_call_expr_boundary_sptr expr = type_suppression::insertion_range::create_fn_call_expr_boundary(ini::read_function_call_expr(str))) end = expr; else return result; type_suppression::insertion_range_sptr insert_range (new type_suppression::insertion_range(begin, end)); insert_ranges.push_back(insert_range); consider_data_member_insertion = true; } if (!is_well_formed) return result; } /// Support 'changed_enumerators = foo, bar, baz' /// /// Note that this constraint is valid only if we have: /// 'type_kind = enum'. /// /// If the current type is an enum and if it carries changed /// enumerators listed in the changed_enumerators property value /// then it should be suppressed. ini::property_sptr changed_enumerators_prop = section.find_property("changed_enumerators"); vector changed_enumerator_names; if (changed_enumerators_prop) { if (ini::list_property_sptr p = is_list_property(changed_enumerators_prop)) changed_enumerator_names = p->get_value()->get_content(); else if (ini::simple_property_sptr p = is_simple_property(changed_enumerators_prop)) changed_enumerator_names.push_back(p->get_value()->as_string()); } result.reset(new type_suppression(label_str, name_regex_str, name_str)); if (consider_type_kind) { result->set_consider_type_kind(true); result->set_type_kind(type_kind); } if (consider_reach_kind) { result->set_consider_reach_kind(true); result->set_reach_kind(reach_kind); } if (consider_data_member_insertion) result->set_data_member_insertion_ranges(insert_ranges); if (!name_not_regex_str.empty()) result->set_type_name_not_regex_str(name_not_regex_str); if (!file_name_regex_str.empty()) result->set_file_name_regex_str(file_name_regex_str); if (!file_name_not_regex_str.empty()) result->set_file_name_not_regex_str(file_name_not_regex_str); if (!soname_regex_str.empty()) result->set_soname_regex_str(soname_regex_str); if (!soname_not_regex_str.empty()) result->set_soname_not_regex_str(soname_not_regex_str); if (!srcloc_not_in.empty()) result->set_source_locations_to_keep(srcloc_not_in); if (!srcloc_not_regexp_str.empty()) result->set_source_location_to_keep_regex_str(srcloc_not_regexp_str); if ((drop_artifact_str == "yes" || drop_artifact_str == "true") && ((!name_regex_str.empty() || !name_str.empty() || !srcloc_not_regexp_str.empty() || !srcloc_not_in.empty()))) result->set_drops_artifact_from_ir(true); if (result->get_type_kind() == type_suppression::ENUM_TYPE_KIND && !changed_enumerator_names.empty()) result->set_changed_enumerator_names(changed_enumerator_names); return result; } // /// Constructor for the @ref the function_suppression::parameter_spec /// type. /// /// @param i the index of the parameter designated by this specification. /// /// @param tn the type name of the parameter designated by this specification. /// /// @param tn_regex a regular expression that defines a set of type /// names for the parameter designated by this specification. Note /// that at evaluation time, this regular expression is taken in /// account only if the parameter @p tn is empty. function_suppression::parameter_spec::parameter_spec(size_t i, const string& tn, const string& tn_regex) : priv_(new priv(i, tn, tn_regex)) {} /// Getter for the index of the parameter designated by this /// specification. /// /// @return the index of the parameter designated by this /// specification. size_t function_suppression::parameter_spec::get_index() const {return priv_->index_;} /// Setter for the index of the parameter designated by this /// specification. /// /// @param i the new index to set. void function_suppression::parameter_spec::set_index(size_t i) {priv_->index_ = i;} /// Getter for the type name of the parameter designated by this specification. /// /// @return the type name of the parameter. const string& function_suppression::parameter_spec::get_parameter_type_name() const {return priv_->type_name_;} /// Setter for the type name of the parameter designated by this /// specification. /// /// @param tn new parameter type name to set. void function_suppression::parameter_spec::set_parameter_type_name(const string& tn) {priv_->type_name_ = tn;} /// Getter for the regular expression that defines a set of type names /// for the parameter designated by this specification. /// /// Note that at evaluation time, this regular expression is taken in /// account only if the name of the parameter as returned by /// function_suppression::parameter_spec::get_parameter_type_name() is /// empty. /// /// @return the regular expression or the parameter type name. const string& function_suppression::parameter_spec::get_parameter_type_name_regex_str() const {return priv_->type_name_regex_str_;} /// Setter for the regular expression that defines a set of type names /// for the parameter designated by this specification. /// /// Note that at evaluation time, this regular expression is taken in /// account only if the name of the parameter as returned by /// function_suppression::parameter_spec::get_parameter_type_name() is /// empty. /// /// @param type_name_regex_str the new type name regular expression to /// set. void 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 /// might use to designate this function suppression specification in /// error messages. This parameter might be empty, in which case it's /// ignored at evaluation time. /// /// @param the name of the function the user wants the current /// specification to designate. This parameter might be empty, in /// which case it's ignored at evaluation time. /// /// @param nr if @p name is empty this parameter is a regular /// expression for a family of names of functions the user wants the /// current specification to designate. If @p name is not empty, this /// parameter is ignored at specification evaluation time. This /// parameter might be empty, in which case it's ignored at evaluation /// time. /// /// @param ret_tn the name of the return type of the function the user /// wants this specification to designate. This parameter might be /// empty, in which case it's ignored at evaluation time. /// /// @param ret_tr if @p ret_tn is empty, then this is a regular /// expression for a family of return type names for functions the /// user wants the current specification to designate. If @p ret_tn /// is not empty, then this parameter is ignored at specification /// evaluation time. This parameter might be empty, in which case /// it's ignored at evaluation time. /// /// @param ps a vector of parameter specifications to specify /// properties of the parameters of the functions the user wants this /// specification to designate. This parameter might be empty, in /// which case it's ignored at evaluation time. /// /// @param sym_n the name of symbol of the function the user wants /// this specification to designate. This parameter might be empty, /// in which case it's ignored at evaluation time. /// /// @param sym_nr if the parameter @p sym_n is empty, then this /// parameter is a regular expression for a family of names of symbols /// of functions the user wants this specification to designate. If /// the parameter @p sym_n is not empty, then this parameter is /// ignored at specification evaluation time. This parameter might be /// empty, in which case it's ignored at evaluation time. /// /// @param sym_v the name of the version of the symbol of the function /// the user wants this specification to designate. This parameter /// might be empty, in which case it's ignored at evaluation time. /// /// @param sym_vr if the parameter @p sym_v is empty, then this /// parameter is a regular expression for a family of versions of /// symbols of functions the user wants the current specification to /// designate. If the parameter @p sym_v is non empty, then this /// parameter is ignored. This parameter might be empty, in which /// case it's ignored at evaluation time. function_suppression::function_suppression(const string& label, const string& name, const string& nr, const string& ret_tn, const string& ret_tr, parameter_specs_type& ps, const string& sym_n, const string& sym_nr, const string& sym_v, const string& sym_vr) : suppression_base(label), priv_(new priv(name, nr, ret_tn, ret_tr, ps, sym_n, sym_nr, sym_v, sym_vr)) {} function_suppression::~function_suppression() {} /// Parses a string containing the content of the "change-kind" /// property and returns the an instance of @ref /// function_suppression::change_kind as a result. /// /// @param s the string to parse. /// /// @return the resulting @ref function_suppression::change_kind. function_suppression::change_kind function_suppression::parse_change_kind(const string& s) { if (s == "function-subtype-change") return FUNCTION_SUBTYPE_CHANGE_KIND; else if (s == "added-function") return ADDED_FUNCTION_CHANGE_KIND; else if (s == "deleted-function") return DELETED_FUNCTION_CHANGE_KIND; else if (s == "all") return ALL_CHANGE_KIND; else return UNDEFINED_CHANGE_KIND; } /// Getter of the "change-kind" property. /// /// @param returnthe "change-kind" property. function_suppression::change_kind function_suppression::get_change_kind() const {return priv_->change_kind_;} /// Setter of the "change-kind" property. /// /// @param k the new value of the change_kind property. void function_suppression::set_change_kind(change_kind k) {priv_->change_kind_ = k;} /// Getter for the name of the function the user wants the current /// specification to designate. This might be empty, in which case /// it's ignored at evaluation time. /// /// @return the name of the function. const string& function_suppression::get_name() const {return priv_->name_;} /// Setter for the name of the function the user wants the current /// specification to designate. This might be empty, in which case /// it's ignored at evaluation time. /// /// @param n the new function name to set. void function_suppression::set_name(const string& n) {priv_->name_ = n;} /// Getter for a regular expression for a family of names of functions /// the user wants the current specification to designate. /// /// @return the regular expression for the possible names of the /// function(s). const string& function_suppression::get_name_regex_str() const {return priv_->name_regex_str_;} /// Setter for a regular expression for a family of names of functions /// the user wants the current specification to designate. /// /// @param r the new the regular expression for the possible names of /// the function(s). void function_suppression::set_name_regex_str(const string& r) {priv_->name_regex_str_ = r;} /// Getter for a regular expression of a family of names of functions /// the user wants the current specification to designate the negation /// of. /// /// @return the regular expression for the possible names of the /// function(s). const string& function_suppression::get_name_not_regex_str() const {return priv_->name_not_regex_str_;} /// Setter for a regular expression for a family of names of functions /// the user wants the current specification to designate the negation /// of. /// /// @param r the new the regular expression for the possible names of /// the function(s). void function_suppression::set_name_not_regex_str(const string& r) {priv_->name_not_regex_str_ = r;} /// Getter for the name of the return type of the function the user /// wants this specification to designate. This property might be /// empty, in which case it's ignored at evaluation time. /// /// @return the name of the return type of the function. const string& function_suppression::get_return_type_name() const {return priv_->return_type_name_;} /// Setter for the name of the return type of the function the user /// wants this specification to designate. This property might be /// empty, in which case it's ignored at evaluation time. /// /// @param tr the new name of the return type of the function to set. void function_suppression::set_return_type_name(const string& tr) {priv_->return_type_name_ = tr;} /// Getter for a regular expression for a family of return type names /// for functions the user wants the current specification to /// designate. /// /// If the name of the return type of the function as returned by /// function_suppression::get_return_type_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 for the possible names of the /// return types of the function(s). const string& function_suppression::get_return_type_regex_str() const {return priv_->return_type_regex_str_;} /// Setter for a regular expression for a family of return type names /// for functions the user wants the current specification to /// designate. /// /// If the name of the return type of the function as returned by /// function_suppression::get_return_type_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 r the new regular expression for the possible names of the /// return types of the function(s) to set. void function_suppression::set_return_type_regex_str(const string& r) {priv_->return_type_regex_str_ = r;} /// Getter for a vector of parameter specifications to specify /// properties of the parameters of the functions the user wants this /// specification to designate. /// /// This property might be empty, in which case it's ignored at /// evaluation time. /// /// @return the specifications of the parameters of the function(s). const function_suppression::parameter_specs_type& function_suppression::get_parameter_specs() const {return priv_->parm_specs_;} /// Setter for a vector of parameter specifications to specify /// properties of the parameters of the functions the user wants this /// specification to designate. /// /// This property might be empty, in which case it's ignored at /// evaluation time. /// /// @param p the new specifications of the parameters of the /// function(s) to set. void function_suppression::set_parameter_specs(parameter_specs_type& p) {priv_->parm_specs_ = p;} /// Append a specification of a parameter of the function specification. /// /// @param p the parameter specification to add. void function_suppression::append_parameter_specs(const parameter_spec_sptr p) {priv_->parm_specs_.push_back(p);} /// Getter for the name of symbol of the function the user wants this /// specification to designate. /// /// This property might be empty, in which case it's ignored at /// evaluation time. /// /// @return name of the symbol of the function. const string& function_suppression::get_symbol_name() const {return priv_->symbol_name_;} /// Setter for the name of symbol of the function the user wants this /// specification to designate. /// /// This property might be empty, in which case it's ignored at /// evaluation time. /// /// @return name of the symbol of the function. void function_suppression::set_symbol_name(const string& n) {priv_->symbol_name_ = n;} /// Getter for a regular expression for a family of names of symbols /// of functions the user wants this specification to designate. /// /// 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 for a family of names of symbols of /// functions to designate. const string& function_suppression::get_symbol_name_regex_str() const {return priv_->symbol_name_regex_str_;} /// Setter for a regular expression for a family of names of symbols /// of functions the user wants this specification to designate. /// /// 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 r the new regular expression for a family of names of /// symbols of functions to set. 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. /// /// This property might be empty, in which case it's ignored at /// evaluation time. /// /// @return the symbol version of the function. const string& function_suppression::get_symbol_version() const {return priv_->symbol_version_;} /// Setter for the name of the version of the symbol of the function /// the user wants this specification to designate. /// /// This property might be empty, in which case it's ignored at /// evaluation time. /// /// @param v the new symbol version of the function. void function_suppression::set_symbol_version(const string& v) {priv_->symbol_version_ = v;} /// Getter for a regular expression for a family of versions of /// symbols of functions the user wants the current specification to /// designate. /// /// If the symbol version as returned by /// function_suppression::get_symbol_version() is non empty, then this /// property is ignored. This property might be empty, in which case /// it's ignored at evaluation time. /// /// @return the regular expression for the versions of symbols of /// functions to designate. const string& function_suppression::get_symbol_version_regex_str() const {return priv_->symbol_version_regex_str_;} /// Setter for a regular expression for a family of versions of /// symbols of functions the user wants the current specification to /// designate. /// /// If the symbol version as returned by /// function_suppression::get_symbol_version() is non empty, then this /// property is ignored. This property might be empty, in which case /// it's ignored at evaluation time. /// /// @param the new regular expression for the versions of symbols of /// functions to designate. void function_suppression::set_symbol_version_regex_str(const string& r) {priv_->symbol_version_regex_str_ = r;} /// Getter for the "allow_other_aliases" property of the function /// suppression specification. /// /// @return the value of the "allow_other_aliases" property. bool function_suppression::get_allow_other_aliases() const {return priv_->allow_other_aliases_;} /// Setter for the "allow_other_aliases" property of the function /// suppression specification. /// /// @param f the new value of the property. void function_suppression::set_allow_other_aliases(bool f) {priv_->allow_other_aliases_ = f;} /// Evaluate this suppression specification on a given diff node and /// say if the diff node should be suppressed or not. /// /// @param diff the diff node to evaluate this suppression /// specification against. /// /// @return true if @p diff should be suppressed. bool function_suppression::suppresses_diff(const diff* diff) const { const function_decl_diff* d = is_function_decl_diff(diff); if (!d) return false; function_decl_sptr ff = is_function_decl(d->first_function_decl()), sf = is_function_decl(d->second_function_decl()); ABG_ASSERT(ff && sf); return (suppresses_function(ff, FUNCTION_SUBTYPE_CHANGE_KIND, diff->context()) || suppresses_function(sf, FUNCTION_SUBTYPE_CHANGE_KIND, diff->context())); } /// Evaluate the current function suppression specification on a given /// @ref function_decl and say if a report about a change involving this /// @ref function_decl should be suppressed or not. /// /// @param fn the @ref function_decl to evaluate this suppression /// specification against. /// /// @param k the kind of function change @p fn is supposed to have. /// /// @param ctxt the context of the current diff. /// /// @return true iff a report about a change involving the function @p /// fn should be suppressed. bool function_suppression::suppresses_function(const function_decl* fn, change_kind k, const diff_context_sptr ctxt) const { if (!(get_change_kind() & k)) return false; // Check if the name and soname of the binaries match the current // suppr spec if (ctxt) { // Check if the name of the binaries match the current suppr spec if (!names_of_binaries_match(*this, *ctxt)) if (has_file_name_related_property()) return false; // Check if the soname of the binaries match the current suppr spec if (!sonames_of_binaries_match(*this, *ctxt)) if (has_soname_related_property()) return false; } string fname = fn->get_qualified_name(); // Check if the "name" property matches. if (!get_name().empty()) { if (get_name() != fn->get_qualified_name()) return false; if (get_allow_other_aliases() && fn->get_symbol() && fn->get_symbol()->get_alias_from_name(fname)) { // So we are in a case of a languages in which the symbol // name is the same as the function name and we want to // allow the removal of change reports on an aliased // function only if the suppression condition matches the // names of all aliases. string symbol_name; elf_symbol_sptr sym = fn->get_symbol(); ABG_ASSERT(sym); symbol_name = sym->get_name(); if (sym->has_aliases() && sym->get_alias_from_name(fname)) { for (elf_symbol_sptr a = sym->get_next_alias(); a && !a->is_main_symbol(); a = a->get_next_alias()) if (a->get_name() != symbol_name) // There is an alias which name is different from // the function (symbol) name given in the // suppression condition. return false; } } } // check if the "name_regexp" property matches. const regex_t_sptr name_regex = priv_->get_name_regex(); if (name_regex) { if (!regex::match(name_regex, fname)) return false; if (get_allow_other_aliases() && fn->get_symbol() && fn->get_symbol()->get_alias_from_name(fname)) { // So we are in a case of a languages in which the symbol // name is the same as the function name and we want to // allow the removal of change reports on an aliased // function only if the suppression condition matches *all* // the aliases. string symbol_name; elf_symbol_sptr sym = fn->get_symbol(); ABG_ASSERT(sym); symbol_name = sym->get_name(); if (sym->has_aliases()) { for (elf_symbol_sptr a = sym->get_next_alias(); a && !a->is_main_symbol(); a = a->get_next_alias()) if (!regex::match(name_regex, a->get_name())) return false; } } } // check if the "name_not_regexp" property matches. const regex_t_sptr name_not_regex = priv_->get_name_not_regex(); if (name_not_regex) { if (regex::match(name_not_regex, fname)) return false; if (get_allow_other_aliases() && fn->get_symbol() && fn->get_symbol()->get_alias_from_name(fname)) { // So we are in a case of a languages in which the symbol // name is the same as the function name and we want to // allow the removal of change reports on an aliased // function only if the suppression condition matches *all* // the aliases. string symbol_name; elf_symbol_sptr sym = fn->get_symbol(); ABG_ASSERT(sym); symbol_name = sym->get_name(); if (sym->has_aliases()) { for (elf_symbol_sptr a = sym->get_next_alias(); a && !a->is_main_symbol(); a = a->get_next_alias()) if (regex::match(name_regex, a->get_name())) return false; } } } // Check if the "return_type_name" or "return_type_regexp" // properties matches. string fn_return_type_name = fn->get_type()->get_return_type() ? static_cast ((get_type_declaration(fn->get_type()->get_return_type()) ->get_qualified_name())) : ""; if (!get_return_type_name().empty()) { if (fn_return_type_name != get_return_type_name()) return false; } else { const regex_t_sptr return_type_regex = priv_->get_return_type_regex(); if (return_type_regex && !regex::match(return_type_regex, fn_return_type_name)) return false; } // 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) { fn_sym_name = sym->get_name(); fn_sym_version = sym->get_version().str(); } if (sym && !get_symbol_name().empty()) { if (fn_sym_name != get_symbol_name()) return false; if (sym && get_allow_other_aliases()) { // In this case, we want to allow the suppression of change // reports about an aliased symbol only if the suppression // condition matches the name of all aliases. if (sym->has_aliases()) { for (elf_symbol_sptr a = sym->get_next_alias(); a && !a->is_main_symbol(); a = a->get_next_alias()) if (a->get_name() != fn_sym_name) return false; } } } else if (sym) { const regex_t_sptr symbol_name_regex = priv_->get_symbol_name_regex(); if (symbol_name_regex && !regex::match(symbol_name_regex, fn_sym_name)) return false; const regex_t_sptr symbol_name_not_regex = priv_->get_symbol_name_not_regex(); if (symbol_name_not_regex && regex::match(symbol_name_not_regex, fn_sym_name)) return false; if (get_allow_other_aliases()) { // In this case, we want to allow the suppression of change // reports about an aliased symbol only if the suppression // condition matches the name of all aliases. if (sym->has_aliases()) { for (elf_symbol_sptr a = sym->get_next_alias(); a && !a->is_main_symbol(); a = a->get_next_alias()) { if (symbol_name_regex && !regex::match(symbol_name_regex, a->get_name())) return false; if (symbol_name_not_regex && regex::match(symbol_name_not_regex, a->get_name())) return false; } } } } // Check if the "symbol_version" and "symbol_version_regexp" // properties match. if (sym && !get_symbol_version().empty()) { if (fn_sym_version != get_symbol_version()) return false; } else if (sym) { const regex_t_sptr symbol_version_regex = priv_->get_symbol_version_regex(); if (symbol_version_regex && !regex::match(symbol_version_regex, fn_sym_version)) return false; } // Check the 'parameter' property. if (!get_parameter_specs().empty()) { function_type_sptr fn_type = fn->get_type(); type_base_sptr parm_type; for (parameter_specs_type::const_iterator p = get_parameter_specs().begin(); p != get_parameter_specs().end(); ++p) { size_t index = (*p)->get_index(); function_decl::parameter_sptr fn_parm = fn_type->get_parm_at_index_from_first_non_implicit_parm(index); if (!fn_parm) return false; string fn_parm_type_qualified_name; if (fn_parm) { parm_type = fn_parm->get_type(); fn_parm_type_qualified_name = get_type_declaration(parm_type)->get_qualified_name(); } const string& tn = (*p)->get_parameter_type_name(); if (!tn.empty()) { if (tn != fn_parm_type_qualified_name) return false; } else { const regex_t_sptr parm_type_name_regex = (*p)->priv_->get_type_name_regex(); if (parm_type_name_regex) { if (!regex::match(parm_type_name_regex, fn_parm_type_qualified_name)) return false; } } } } return true; } /// Evaluate the current function suppression specification on a given /// @ref function_decl and say if a report about a change involving this /// @ref function_decl should be suppressed or not. /// /// @param fn the @ref function_decl to evaluate this suppression /// specification against. /// /// @param k the kind of function change @p fn is supposed to have. /// /// @param ctxt the context of the current diff. /// /// @return true iff a report about a change involving the function @p /// fn should be suppressed. bool function_suppression::suppresses_function(const function_decl_sptr fn, change_kind k, const diff_context_sptr ctxt) const {return suppresses_function(fn.get(), k, ctxt);} /// Evaluate the current function suppression specification on a given /// @ref elf_symbol and say if a report about a change involving this /// @ref elf_symbol should be suppressed or not. /// /// @param sym the @ref elf_symbol to evaluate this suppression /// specification against. /// /// @param k the kind of function change @p sym is supposed to have. /// /// @param ctxt the context of the current diff. /// /// @return true iff a report about a change involving the symbol @p /// sym should be suppressed. bool function_suppression::suppresses_function_symbol(const elf_symbol* sym, change_kind k, const diff_context_sptr ctxt) { if (!sym) return false; if (!(get_change_kind() & k)) return false; if (!sym->is_function()) return false; ABG_ASSERT(k & function_suppression::ADDED_FUNCTION_CHANGE_KIND || k & function_suppression::DELETED_FUNCTION_CHANGE_KIND); // Check if the name and soname of the binaries match the current // suppr spect if (ctxt) { // Check if the name of the binaries match the current // suppr spect if (!names_of_binaries_match(*this, *ctxt)) if (has_file_name_related_property()) return false; // Check if the soname of the binaries match the current // suppr spect if (!sonames_of_binaries_match(*this, *ctxt)) if (has_soname_related_property()) return false; } string sym_name = sym->get_name(), sym_version = sym->get_version().str(); bool no_symbol_name = false, no_symbol_version = false; // Consider the symbol name. if (!get_symbol_name().empty()) { if (sym_name != get_symbol_name()) return false; } else if (!get_symbol_name_regex_str().empty()) { const regex_t_sptr symbol_name_regex = priv_->get_symbol_name_regex(); if (symbol_name_regex && !regex::match(symbol_name_regex, sym_name)) return false; } else no_symbol_name = true; // Consider the symbol version if (!get_symbol_version().empty()) { if (sym_version != get_symbol_version()) return false; } else if (!get_symbol_version_regex_str().empty()) { const regex_t_sptr symbol_version_regex = priv_->get_symbol_version_regex(); if (symbol_version_regex && !regex::match(symbol_version_regex, sym_version)) return false; } else no_symbol_version = true; if (no_symbol_name && no_symbol_version) return false; return true; } /// Evaluate the current function suppression specification on a given /// @ref elf_symbol and say if a report about a change involving this /// @ref elf_symbol should be suppressed or not. /// /// @param sym the @ref elf_symbol to evaluate this suppression /// specification against. /// /// @param k the kind of function change @p sym is supposed to have. /// /// @param ctxt the context of the current diff. /// /// @return true iff a report about a change involving the symbol @p /// sym should be suppressed. bool function_suppression::suppresses_function_symbol(const elf_symbol_sptr sym, change_kind k, const diff_context_sptr ctxt) {return suppresses_function_symbol(sym.get(), k, ctxt);} /// Test if an instance of @ref suppression is an instance of @ref /// function_suppression. /// /// @param suppr the instance of @ref suppression to test for. /// /// @return if @p suppr is an instance of @ref function_suppression, then /// return the sub-object of the @p suppr of type @ref /// function_suppression, otherwise return a nil pointer. function_suppression_sptr is_function_suppression(const suppression_sptr suppr) {return dynamic_pointer_cast(suppr);} /// The bitwise 'and' operator for the enum @ref /// function_suppression::change_kind. /// /// @param l the first operand of the 'and' operator. /// /// @param r the second operand of the 'and' operator. /// /// @return the result of 'and' operation on @p l and @p r. function_suppression::change_kind operator&(function_suppression::change_kind l, function_suppression::change_kind r) { return static_cast (static_cast(l) & static_cast(r)); } /// The bitwise 'or' operator for the enum @ref /// function_suppression::change_kind. /// /// @param l the first operand of the 'or' operator. /// /// @param r the second operand of the 'or' operator. /// /// @return the result of 'or' operation on @p l and @p r. function_suppression::change_kind operator|(function_suppression::change_kind l, function_suppression::change_kind r) { return static_cast (static_cast(l) | static_cast(r)); } /// Test whether if a given function suppression matches a function /// designated by a regular expression that describes its name. /// /// @param s the suppression specification to evaluate to see if it /// matches a given function name. /// /// @param fn_name the name of the function of interest. Note that /// this name must be *non* qualified. /// /// @return true iff the suppression specification @p s matches the /// function whose name is @p fn_name. bool suppression_matches_function_name(const suppr::function_suppression& s, const string& fn_name) { if (regex_t_sptr regexp = s.priv_->get_name_regex()) { if (!regex::match(regexp, fn_name)) return false; } else if (regex_t_sptr regexp = s.priv_->get_name_not_regex()) { if (regex::match(regexp, fn_name)) return false; } else if (s.priv_->name_.empty()) return false; else // if (!s.priv_->name_.empty()) { if (s.priv_->name_ != fn_name) return false; } return true; } /// Test whether if a given function suppression matches a function /// designated by a regular expression that describes its linkage /// name (symbol name). /// /// @param s the suppression specification to evaluate to see if it /// matches a given function linkage name /// /// @param fn_linkage_name the linkage name of the function of interest. /// /// @return true iff the suppression specification @p s matches the /// function whose linkage name is @p fn_linkage_name. bool suppression_matches_function_sym_name(const suppr::function_suppression& s, const string& fn_linkage_name) { if (regex_t_sptr regexp = s.priv_->get_symbol_name_regex()) { if (!regex::match(regexp, fn_linkage_name)) return false; } else if (regex_t_sptr regexp = s.priv_->get_symbol_name_not_regex()) { if (regex::match(regexp, fn_linkage_name)) return false; } else if (s.priv_->symbol_name_.empty()) return false; else // if (!s.priv_->symbol_name_.empty()) { if (s.priv_->symbol_name_ != fn_linkage_name) return false; } return true; } /// Test if a variable suppression matches a variable denoted by its name. /// /// @param s the variable suppression to consider. /// /// @param var_name the name of the variable to consider. /// /// @return true if the variable is matches by the suppression /// specification. bool suppression_matches_variable_name(const suppr::variable_suppression& s, const string& var_name) { if (regex_t_sptr regexp = s.priv_->get_name_regex()) { if (!regex::match(regexp, var_name)) return false; } else if (regex_t_sptr regexp = s.priv_->get_name_not_regex()) { if (regex::match(regexp, var_name)) return false; } else if (s.priv_->name_.empty()) return false; else // if (!s.priv_->name_.empty()) { if (s.priv_->name_ != var_name) return false; } return true; } /// Test if a variable suppression matches a variable denoted by its /// symbol name. /// /// @param s the variable suppression to consider. /// /// @param var_linkage_name the name of the variable to consider. /// /// @return true if the variable is matches by the suppression /// specification. bool suppression_matches_variable_sym_name(const suppr::variable_suppression& s, const string& var_linkage_name) { if (regex_t_sptr regexp = s.priv_->get_symbol_name_regex()) { if (!regex::match(regexp, var_linkage_name)) return false; } else if (regex_t_sptr regexp = s.priv_->get_symbol_name_not_regex()) { if (regex::match(regexp, var_linkage_name)) return false; } else if (s.priv_->symbol_name_.empty()) return false; else // if (!s.priv_->symbol_name_.empty()) { if (s.priv_->symbol_name_ != var_linkage_name) return false; } return true; } /// Test if a type suppression matches a type designated by its fully /// qualified name. /// /// @param s the type suppression to consider. /// /// @param type_name the name of the type to consider. /// /// @return true iff the suppression s matches the type denoted by /// name @p type_name. bool suppression_matches_type(const suppr::type_suppression& s, const string& type_name) { if (regex_t_sptr regexp = s.priv_->get_type_name_regex()) { if (!regex::match(regexp, type_name)) return false; } else if (!s.get_type_name().empty()) { if (s.get_type_name() != type_name) return false; } else return false; return true; } /// Parse a string containing a parameter spec, build an instance of /// function_suppression::parameter_spec from it and return a pointer /// to that object. /// /// @return a shared pointer pointer to the newly built instance of /// function_suppression::parameter_spec. If the parameter /// specification could not be parsed, return a nil object. static function_suppression::parameter_spec_sptr read_parameter_spec_from_string(const string& str) { string::size_type cur = 0; function_suppression::parameter_spec_sptr result; // skip leading white spaces. for (; cur < str.size(); ++cur) if (!isspace(str[cur])) break; // look for the parameter index string index_str; if (str[cur] == '\'') { ++cur; for (; cur < str.size(); ++cur) if (!isdigit(str[cur])) break; else index_str += str[cur]; } // skip white spaces. for (; cur < str.size(); ++cur) if (!isspace(str[cur])) break; bool is_regex = false; if (str[cur] == '/') { is_regex = true; ++cur; } // look for the type name (regex) string type_name; for (; cur < str.size(); ++cur) if (!isspace(str[cur])) { if (is_regex && str[cur] == '/') break; type_name += str[cur]; } if (is_regex && str[cur] == '/') ++cur; if (!index_str.empty() || !type_name.empty()) { std::string type_name_regex; if (is_regex) { type_name_regex = type_name; type_name.clear(); } function_suppression::parameter_spec* p = new function_suppression::parameter_spec(atoi(index_str.c_str()), type_name, type_name_regex); result.reset(p); } return result; } /// Parse function suppression specification, build a resulting @ref /// function_suppression type and return a shared pointer to that /// object. /// /// @return a shared pointer to the newly built @ref /// function_suppression. If the function suppression specification /// could not be parsed then a nil shared pointer is returned. static function_suppression_sptr read_function_suppression(const ini::config::section& section) { function_suppression_sptr result; if (section.get_name() != "suppress_function") return result; static const char *const sufficient_props[] = { "label", "file_name_regexp", "file_name_not_regexp", "soname_regexp", "soname_not_regexp", "name", "name_regexp", "name_not_regexp", "parameter", "return_type_name", "return_type_regexp", "symbol_name", "symbol_name_regexp", "symbol_name_not_regexp", "symbol_version", "symbol_version_regexp", }; if (!check_sufficient_props(sufficient_props, sizeof(sufficient_props)/sizeof(char*), section)) return result; ini::simple_property_sptr drop_artifact = is_simple_property(section.find_property("drop_artifact")); if (!drop_artifact) drop_artifact = is_simple_property(section.find_property("drop")); string drop_artifact_str = drop_artifact ? drop_artifact->get_value()->as_string() : ""; ini::simple_property_sptr change_kind_prop = is_simple_property(section.find_property("change_kind")); string change_kind_str = change_kind_prop ? change_kind_prop->get_value()->as_string() : ""; ini::simple_property_sptr label_prop = is_simple_property(section.find_property("label")); string label_str = label_prop ? label_prop->get_value()->as_string() : ""; ini::simple_property_sptr file_name_regex_prop = is_simple_property(section.find_property("file_name_regexp")); string file_name_regex_str = file_name_regex_prop ? file_name_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr file_name_not_regex_prop = is_simple_property(section.find_property("file_name_not_regexp")); string file_name_not_regex_str = file_name_not_regex_prop ? file_name_not_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr soname_regex_prop = is_simple_property(section.find_property("soname_regexp")); string soname_regex_str = soname_regex_prop ? soname_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr soname_not_regex_prop = is_simple_property(section.find_property("soname_not_regexp")); string soname_not_regex_str = soname_not_regex_prop ? soname_not_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr name_prop = is_simple_property(section.find_property("name")); string name = name_prop ? name_prop->get_value()->as_string() : ""; ini::simple_property_sptr name_regex_prop = is_simple_property(section.find_property("name_regexp")); string name_regex_str = name_regex_prop ? name_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr name_not_regex_prop = is_simple_property(section.find_property("name_not_regexp")); string name_not_regex_str = name_not_regex_prop ? name_not_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr return_type_name_prop = is_simple_property(section.find_property("return_type_name")); string return_type_name = return_type_name_prop ? return_type_name_prop->get_value()->as_string() : ""; ini::simple_property_sptr return_type_regex_prop = is_simple_property(section.find_property("return_type_regexp")); string return_type_regex_str = return_type_regex_prop ? return_type_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr sym_name_prop = is_simple_property(section.find_property("symbol_name")); string sym_name = sym_name_prop ? sym_name_prop->get_value()->as_string() : ""; ini::simple_property_sptr sym_name_regex_prop = is_simple_property(section.find_property("symbol_name_regexp")); string sym_name_regex_str = sym_name_regex_prop ? 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 ? sym_ver_prop->get_value()->as_string() : ""; ini::simple_property_sptr sym_ver_regex_prop = is_simple_property(section.find_property("symbol_version_regexp")); string sym_ver_regex_str = sym_ver_regex_prop ? sym_ver_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr allow_other_aliases_prop = is_simple_property(section.find_property("allow_other_aliases")); string allow_other_aliases = allow_other_aliases_prop ? allow_other_aliases_prop->get_value()->as_string() : ""; function_suppression::parameter_spec_sptr parm; function_suppression::parameter_specs_type parms; for (ini::config::properties_type::const_iterator p = section.get_properties().begin(); p != section.get_properties().end(); ++p) if ((*p)->get_name() == "parameter") { ini::simple_property_sptr prop = is_simple_property(*p); ABG_ASSERT(prop); if ((parm = read_parameter_spec_from_string (prop->get_value()->as_string()))) parms.push_back(parm); } result.reset(new function_suppression(label_str, name, name_regex_str, return_type_name, return_type_regex_str, parms, sym_name, sym_name_regex_str, sym_version, sym_ver_regex_str)); if ((drop_artifact_str == "yes" || drop_artifact_str == "true") && (!name.empty() || !name_regex_str.empty() || !name_not_regex_str.empty() || !sym_name.empty() || !sym_name_regex_str.empty() || !sym_name_not_regex_str.empty())) result->set_drops_artifact_from_ir(true); if (!change_kind_str.empty()) result->set_change_kind (function_suppression::parse_change_kind(change_kind_str)); if (!allow_other_aliases.empty()) result->set_allow_other_aliases(allow_other_aliases == "yes" || allow_other_aliases == "true"); 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); if (!file_name_not_regex_str.empty()) result->set_file_name_not_regex_str(file_name_not_regex_str); if (!soname_regex_str.empty()) result->set_soname_regex_str(soname_regex_str); if (!soname_not_regex_str.empty()) result->set_soname_not_regex_str(soname_not_regex_str); return result; } // // /// Constructor for the @ref variable_suppression type. /// /// @param label an informative text string that the evalution code /// might use to designate this variable suppression specification in /// error messages. This parameter might be empty, in which case it's /// ignored at evaluation time. /// /// @param name the name of the variable the user wants the current /// specification to designate. This parameter might be empty, in /// which case it's ignored at evaluation time. /// /// @param name_regex_str if @p name is empty, this parameter is a /// regular expression for a family of names of variables the user /// wants the current specification to designate. If @p name is not /// empty, then this parameter is ignored at evaluation time. This /// parameter might be empty, in which case it's ignored at evaluation /// time. /// /// @param symbol_name the name of the symbol of the variable the user /// wants the current specification to designate. This parameter /// might be empty, in which case it's ignored at evaluation time. /// /// @param symbol_name_str if @p symbol_name is empty, this parameter /// is a regular expression for a family of names of symbols of /// variables the user wants the current specification to designate. /// If @p symbol_name is not empty, then this parameter is ignored at /// evaluation time. This parameter might be empty, in which case /// it's ignored at evaluation time. /// /// @param symbol_version the version of the symbol of the variable /// the user wants the current specification to designate. This /// parameter might be empty, in which case it's ignored at evaluation /// time. /// /// @param symbol_version_regex if @p symbol_version is empty, then /// this parameter is a regular expression for a family of versions of /// symbol for the variables the user wants the current specification /// to designate. If @p symbol_version is not empty, then this /// parameter is ignored at evaluation time. This parameter might be /// empty, in which case it's ignored at evaluation time. /// /// @param type_name the name of the type of the variable the user /// wants the current specification to designate. This parameter /// might be empty, in which case it's ignored at evaluation time. /// /// @param type_name_regex_str if @p type_name is empty, then this /// parameter is a regular expression for a family of type names of /// variables the user wants the current specification to designate. /// If @p type_name is not empty, then this parameter is ignored at /// evluation time. This parameter might be empty, in which case it's /// ignored at evaluation time. variable_suppression::variable_suppression(const string& label, const string& name, const string& name_regex_str, const string& symbol_name, const string& symbol_name_regex_str, const string& symbol_version, const string& symbol_version_regex, const string& type_name, const string& type_name_regex_str) : suppression_base(label), priv_(new priv(name, name_regex_str, symbol_name, symbol_name_regex_str, symbol_version, symbol_version_regex, type_name, type_name_regex_str)) {} /// Virtual destructor for the @erf variable_suppression type. /// variable_suppression type. variable_suppression::~variable_suppression() {} /// Parses a string containing the content of the "change-kind" /// property and returns the an instance of @ref /// variable_suppression::change_kind as a result. /// /// @param s the string to parse. /// /// @return the resulting @ref variable_suppression::change_kind. variable_suppression::change_kind variable_suppression::parse_change_kind(const string& s) { if (s == "variable-subtype-change") return VARIABLE_SUBTYPE_CHANGE_KIND; else if (s == "added-variable") return ADDED_VARIABLE_CHANGE_KIND; else if (s == "deleted-variable") return DELETED_VARIABLE_CHANGE_KIND; else if (s == "all") return ALL_CHANGE_KIND; else return UNDEFINED_CHANGE_KIND; } /// Getter of the "change_king" property. /// /// @return the value of the "change_kind" property. variable_suppression::change_kind variable_suppression::get_change_kind() const {return priv_->change_kind_;} /// Setter of the "change_kind" property. /// /// @param k the new value of of the change_kind. void variable_suppression::set_change_kind(change_kind k) {priv_->change_kind_ = k;} /// Getter for the name 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. /// /// @return the name of the variable. const string& variable_suppression::get_name() const {return priv_->name_;} /// Setter for the name 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. /// /// @param n the new name of the variable to set. void variable_suppression::set_name(const string& n) {priv_->name_ = n;} /// Getter for the regular expression for a family of names of /// variables the user wants the current specification to designate. /// If the variable name as returned by /// variable_suppression::get_name() is not empty, then this property /// is ignored at evaluation time. This property might be empty, in /// which case it's ignored at evaluation time. /// /// @return the regular expression for the variable name. const string& variable_suppression::get_name_regex_str() const {return priv_->name_regex_str_;} /// Setter for the regular expression for a family of names of /// variables the user wants the current specification to designate. /// If the variable name as returned by /// variable_suppression::get_name() is not empty, then this property /// is ignored at evaluation time. This property might be empty, in /// which case it's ignored at evaluation time. /// /// @param r the new regular expression for the variable name. void variable_suppression::set_name_regex_str(const string& r) {priv_->name_regex_str_ = r;} /// Getter for the "name_not_regexp" property of the specification. /// /// @return the value of the "name_not_regexp" property. const string& variable_suppression::get_name_not_regex_str() const {return priv_->name_not_regex_str_;} /// Setter for the "name_not_regexp" property of the specification. /// /// @param r the new value of the "name_not_regexp" property. void variable_suppression::set_name_not_regex_str(const string& r) {priv_->name_not_regex_str_ = r;} /// Getter for the name of the symbol of the variable the user wants /// the current specification to designate. /// /// This property might be empty, in which case it is ignored at /// evaluation time. /// /// @return the name of the symbol of the variable. const string& variable_suppression::get_symbol_name() const {return priv_->symbol_name_;} /// Setter for the name of the symbol of the variable the user wants /// the current specification to designate. /// /// This property might be empty, in which case it is ignored at /// evaluation time. /// /// @param n the new name of the symbol of the variable. void variable_suppression::set_symbol_name(const string& n) {priv_->symbol_name_ = n;} /// Getter of the regular expression for a family of symbol names of /// the variables this specification is about to designate. /// /// This property might be empty, in which case it's ignored at /// evaluation time. Otherwise, it is taken in account iff the /// property returned by variable_suppression::get_symbol_name() is /// empty. /// /// @return the regular expression for a symbol name of the variable. const string& variable_suppression::get_symbol_name_regex_str() const {return priv_->symbol_name_regex_str_;} /// Setter of the regular expression for a family of symbol names of /// the variables this specification is about to designate. /// /// This property might be empty, in which case it's ignored at /// evaluation time. Otherwise, it is taken in account iff the /// property returned by variable_suppression::get_symbol_name() is /// empty. /// /// @param r the regular expression for a symbol name of the variable. 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. /// /// @return the symbol version of the variable. const string& variable_suppression::get_symbol_version() const {return priv_->symbol_version_;} /// Setter 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. /// /// @return the new symbol version of the variable. void variable_suppression::set_symbol_version(const string& v) {priv_->symbol_version_ = v;} /// Getter of the regular expression for a family of versions of /// symbol for the variables the user wants the current specification /// to designate. If @p symbol_version is not empty, then this /// property is ignored at evaluation time. This property might be /// empty, in which case it's ignored at evaluation time. /// /// @return the regular expression of the symbol version of the /// variable. const string& variable_suppression::get_symbol_version_regex_str() const {return priv_->symbol_version_regex_str_;} /// Setter of the regular expression for a family of versions of /// symbol for the variables the user wants the current specification /// to designate. If @p symbol_version is not empty, then this /// property is ignored at evaluation time. This property might be /// empty, in which case it's ignored at evaluation time. /// /// @param v the new regular expression of the symbol version of the /// variable. void variable_suppression::set_symbol_version_regex_str(const string& r) {priv_->symbol_version_regex_str_ = r;} /// Getter for the name of the type 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. /// /// @return the name of the variable type. const string& variable_suppression::get_type_name() const {return priv_->type_name_;} /// Setter for the name of the type 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. /// /// @param n the new name of the variable type. void variable_suppression::set_type_name(const string& n) {priv_->type_name_ = n;} /// Getter for the regular expression for a family of type names of /// variables the user wants the current specification to designate. /// /// If the type name as returned by /// variable_suppression::get_type_name() is not empty, then this /// property is ignored at evaluation time. This property might be /// empty, in which case it's ignored at evaluation time. /// /// @return the regular expression of the variable type name. const string& variable_suppression::get_type_name_regex_str() const {return priv_->type_name_regex_str_;} /// Setter for the regular expression for a family of type names of /// variables the user wants the current specification to designate. /// /// If the type name as returned by /// variable_suppression::get_type_name() is not empty, then this /// property is ignored at evaluation time. This property might be /// empty, in which case it's ignored at evaluation time. /// /// @param r the regular expression of the variable type name. void variable_suppression::set_type_name_regex_str(const string& r) {priv_->type_name_regex_str_ = r;} /// Evaluate this suppression specification on a given diff node and /// say if the diff node should be suppressed or not. /// /// @param diff the diff node to evaluate this suppression /// specification against. /// /// @return true if @p diff should be suppressed. bool variable_suppression::suppresses_diff(const diff* diff) const { const var_diff* d = is_var_diff(diff); if (!d) return false; var_decl_sptr fv = is_var_decl(is_decl(d->first_subject())), sv = is_var_decl(is_decl(d->second_subject())); ABG_ASSERT(fv && sv); return (suppresses_variable(fv, VARIABLE_SUBTYPE_CHANGE_KIND, diff->context()) || suppresses_variable(sv, VARIABLE_SUBTYPE_CHANGE_KIND, diff->context())); } /// Evaluate the current variable suppression specification on a given /// @ref var_decl and say if a report about a change involving this /// @ref var_decl should be suppressed or not. /// /// @param var the @ref var_decl to evaluate this suppression /// specification against. /// /// @param k the kind of variable change @p var is supposed to have. /// /// @param ctxt the context of the current diff. /// /// @return true iff a report about a change involving the variable @p /// var should be suppressed. bool variable_suppression::suppresses_variable(const var_decl* var, change_kind k, const diff_context_sptr ctxt) const { if (!(get_change_kind() & k)) return false; // Check if the name and soname of the binaries match if (ctxt) { // Check if the name of the binaries match the current // suppr spec if (!names_of_binaries_match(*this, *ctxt)) if (has_file_name_related_property()) return false; // Check if the soname of the binaries match the current suppr // spec if (!sonames_of_binaries_match(*this, *ctxt)) if (has_soname_related_property()) return false; } string var_name = var->get_qualified_name(); // Check for "name" property match. if (!get_name().empty()) { if (get_name() != var_name) return false; } else { // If the "name" property is empty, then consider checking for the // "name_regex" and "name_not_regex" properties match if (get_name().empty()) { const regex_t_sptr name_regex = priv_->get_name_regex(); if (name_regex && !regex::match(name_regex, var_name)) return false; const regex_t_sptr name_not_regex = priv_->get_name_not_regex(); if (name_not_regex && regex::match(name_not_regex, var_name)) return false; } } // 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()) { if (get_symbol_name() != var_sym_name) return false; } else { const regex_t_sptr sym_name_regex = priv_->get_symbol_name_regex(); if (sym_name_regex && !regex::match(sym_name_regex, var_sym_name)) return false; const regex_t_sptr sym_name_not_regex = priv_->get_symbol_name_not_regex(); if (sym_name_not_regex && regex::match(sym_name_not_regex, var_sym_name)) return false; } // Check for symbol_version and symbol_version_regexp property match string var_sym_version = var->get_symbol() ? var->get_symbol()->get_version().str() : ""; if (!get_symbol_version().empty()) { if (get_symbol_version() != var_sym_version) return false; } else { const regex_t_sptr symbol_version_regex = priv_->get_symbol_version_regex(); if (symbol_version_regex && !regex::match(symbol_version_regex, var_sym_version)) return false; } // Check for the "type_name" and type_name_regex properties match. string var_type_name = get_type_declaration(var->get_type())->get_qualified_name(); if (!get_type_name().empty()) { if (get_type_name() != var_type_name) return false; } else { if (get_type_name().empty()) { const regex_t_sptr type_name_regex = priv_->get_type_name_regex(); if (type_name_regex && !regex::match(type_name_regex, var_type_name)) return false; } } return true; } /// Evaluate the current variable suppression specification on a given /// @ref var_decl and say if a report about a change involving this /// @ref var_decl should be suppressed or not. /// /// @param var the @ref var_decl to evaluate this suppression /// specification against. /// /// @param k the kind of variable change @p var is supposed to have. /// /// @param ctxt the context of the current diff. /// /// @return true iff a report about a change involving the variable @p /// var should be suppressed. bool variable_suppression::suppresses_variable(const var_decl_sptr var, change_kind k, const diff_context_sptr ctxt) const {return suppresses_variable(var.get(), k, ctxt);} /// Evaluate the current variable suppression specification on a given /// @ref elf_symbol and say if a report about a change involving this /// @ref elf_symbol should be suppressed or not. /// /// @param sym the @ref elf_symbol to evaluate this suppression /// specification against. /// /// @param k the kind of variable change @p sym is supposed to have. /// /// @param ctxt the context of the current diff. /// /// @return true iff a report about a change involving the symbol @p /// sym should be suppressed. bool variable_suppression::suppresses_variable_symbol(const elf_symbol* sym, change_kind k, const diff_context_sptr ctxt) const { if (!sym) return false; if (!(get_change_kind() & k)) return false; if (!sym->is_variable()) return false; ABG_ASSERT(k & ADDED_VARIABLE_CHANGE_KIND || k & DELETED_VARIABLE_CHANGE_KIND); // Check if the name and soname of the binaries match the current // suppr spec. if (ctxt) { // Check if the name of the binaries match the current suppr // spec if (!names_of_binaries_match(*this, *ctxt)) if (has_file_name_related_property()) return false; // Check if the soname of the binaries match the current suppr spec if (!sonames_of_binaries_match(*this, *ctxt)) if (has_soname_related_property()) return false; } string sym_name = sym->get_name(), sym_version = sym->get_version().str(); bool no_symbol_name = false, no_symbol_version = false; // Consider the symbol name if (!get_name().empty()) { if (get_name() != sym_name) return false; } else if (!get_symbol_name().empty()) { if (get_symbol_name() != sym_name) return false; } else if (!get_symbol_name_regex_str().empty()) { const regex_t_sptr sym_name_regex = priv_->get_symbol_name_regex(); if (sym_name_regex && !regex::match(sym_name_regex, sym_name)) return false; } else no_symbol_name = true; // Consider the symbol version. if (!get_symbol_version().empty()) { if (get_symbol_version() != sym_version) return false; } else if (!get_symbol_version_regex_str().empty()) { const regex_t_sptr symbol_version_regex = priv_->get_symbol_version_regex(); if (symbol_version_regex && !regex::match(symbol_version_regex, sym_version)) return false; } else no_symbol_version = true; if (no_symbol_name && no_symbol_version) return false; return true; } /// Evaluate the current variable suppression specification on a given /// @ref elf_symbol and say if a report about a change involving this /// @ref elf_symbol should be suppressed or not. /// /// @param sym the @ref elf_symbol to evaluate this suppression /// specification against. /// /// @param k the kind of variable change @p sym is supposed to have. /// /// @param ctxt the context of the current diff. /// /// @return true iff a report about a change involving the symbol @p /// sym should be suppressed. bool variable_suppression::suppresses_variable_symbol(const elf_symbol_sptr sym, change_kind k, const diff_context_sptr ctxt) const {return suppresses_variable_symbol(sym.get(), k, ctxt);} /// Test if an instance of @ref suppression is an instance of @ref /// variable_suppression. /// /// @param suppr the instance of @ref suppression to test for. /// /// @return if @p suppr is an instance of @ref variable_suppression, then /// return the sub-object of the @p suppr of type @ref /// variable_suppression, otherwise return a nil pointer. variable_suppression_sptr is_variable_suppression(const suppression_sptr s) {return dynamic_pointer_cast(s);} /// The bitwise 'and' operator for the enum @ref /// variable_suppression::change_kind. /// /// @param l the first operand of the 'and' operator. /// /// @param r the second operand of the 'and' operator. /// /// @return the result of 'and' operation on @p l and @p r. variable_suppression::change_kind operator&(variable_suppression::change_kind l, variable_suppression::change_kind r) { return static_cast (static_cast(l) & static_cast(r)); } /// The bitwise 'or' operator for the enum @ref /// variable_suppression::change_kind. /// /// @param l the first operand of the 'or' operator. /// /// @param r the second operand of the 'or' operator. /// /// @return the result of 'or' operation on @p l and @p r. variable_suppression::change_kind operator|(variable_suppression::change_kind l, variable_suppression::change_kind r) { return static_cast (static_cast(l) | static_cast(r)); } /// Parse variable suppression specification, build a resulting @ref /// variable_suppression type and return a shared pointer to that /// object. /// /// @return a shared pointer to the newly built @ref /// variable_suppression. If the variable suppression specification /// could not be parsed then a nil shared pointer is returned. static variable_suppression_sptr read_variable_suppression(const ini::config::section& section) { variable_suppression_sptr result; if (section.get_name() != "suppress_variable") return result; static const char *const sufficient_props[] = { "label", "file_name_regexp", "file_name_not_regexp", "soname_regexp", "soname_not_regexp", "name", "name_regexp", "name_not_regexp", "symbol_name", "symbol_name_regexp", "symbol_name_not_regexp", "symbol_version", "symbol_version_regexp", "type_name", "type_name_regexp", }; if (!check_sufficient_props(sufficient_props, sizeof(sufficient_props)/sizeof(char*), section)) return result; ini::simple_property_sptr drop_artifact = is_simple_property(section.find_property("drop_artifact")); if (!drop_artifact) drop_artifact = is_simple_property(section.find_property("drop")); string drop_artifact_str = drop_artifact ? drop_artifact->get_value()->as_string() : ""; ini::simple_property_sptr change_kind_prop = is_simple_property(section.find_property("change_kind")); string change_kind_str = change_kind_prop ? change_kind_prop->get_value()->as_string() : ""; ini::simple_property_sptr label_prop = is_simple_property(section.find_property("label")); string label_str = (label_prop ? label_prop->get_value()->as_string() : ""); ini::simple_property_sptr file_name_regex_prop = is_simple_property(section.find_property("file_name_regexp")); string file_name_regex_str = file_name_regex_prop ? file_name_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr file_name_not_regex_prop = is_simple_property(section.find_property("file_name_not_regexp")); string file_name_not_regex_str = file_name_not_regex_prop ? file_name_not_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr soname_regex_prop = is_simple_property(section.find_property("soname_regexp")); string soname_regex_str = soname_regex_prop ? soname_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr soname_not_regex_prop = is_simple_property(section.find_property("soname_not_regexp")); string soname_not_regex_str = soname_not_regex_prop ? soname_not_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr name_prop = is_simple_property(section.find_property("name")); string name_str = (name_prop ? name_prop->get_value()->as_string() : ""); ini::simple_property_sptr name_regex_prop = is_simple_property(section.find_property("name_regexp")); string name_regex_str = (name_regex_prop ? name_regex_prop->get_value()->as_string() : ""); ini::simple_property_sptr name_not_regex_prop = is_simple_property(section.find_property("name_not_regexp")); string name_not_regex_str = name_not_regex_prop ? name_not_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr sym_name_prop = is_simple_property(section.find_property("symbol_name")); string symbol_name = (sym_name_prop ? sym_name_prop->get_value()->as_string() : ""); ini::simple_property_sptr sym_name_regex_prop = is_simple_property(section.find_property("symbol_name_regexp")); string symbol_name_regex_str = sym_name_regex_prop ? 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 ? sym_version_prop->get_value()->as_string() : ""; ini::simple_property_sptr sym_version_regex_prop = is_simple_property(section.find_property("symbol_version_regexp")); string symbol_version_regex_str = sym_version_regex_prop ? sym_version_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr type_name_prop = is_simple_property(section.find_property("type_name")); string type_name_str = type_name_prop ? type_name_prop->get_value()->as_string() : ""; ini::simple_property_sptr type_name_regex_prop = is_simple_property(section.find_property("type_name_regexp")); string type_name_regex_str = type_name_regex_prop ? type_name_regex_prop->get_value()->as_string() : ""; result.reset(new variable_suppression(label_str, name_str, name_regex_str, symbol_name, symbol_name_regex_str, symbol_version, symbol_version_regex_str, type_name_str, type_name_regex_str)); if ((drop_artifact_str == "yes" || drop_artifact_str == "true") && (!name_str.empty() || !name_regex_str.empty() || !name_not_regex_str.empty() || !symbol_name.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 (!change_kind_str.empty()) result->set_change_kind (variable_suppression::parse_change_kind(change_kind_str)); if (!file_name_regex_str.empty()) result->set_file_name_regex_str(file_name_regex_str); if (!file_name_not_regex_str.empty()) result->set_file_name_not_regex_str(file_name_not_regex_str); if (!soname_regex_str.empty()) result->set_soname_regex_str(soname_regex_str); if (!soname_not_regex_str.empty()) result->set_soname_not_regex_str(soname_not_regex_str); return result; } // // /// Constructor for the the @ref file_suppression type. /// /// @param label the label of the suppression directive. /// /// @param fname_regex_str the regular expression string that /// designates the file name that instances of @ref file_suppression /// should match. /// /// @param fname_not_regex_str the regular expression string that /// designates the file name that instances of @ref file_suppression /// shoult *NOT* match. In other words, this file_suppression should /// be activated if its file name does not match the regular /// expression @p fname_not_regex_str. file_suppression::file_suppression(const string& label, const string& fname_regex_str, const string& fname_not_regex_str) : suppression_base(label, fname_regex_str, fname_not_regex_str) {} /// Test if instances of this @ref file_suppression suppresses a /// certain instance of @ref diff. /// /// This function always returns false because, obviously, a /// file_suppression is meants to prevents Abigail tools from loading /// some files. It is not meant to act on instance of @ref diff. /// @return false. bool file_suppression::suppresses_diff(const diff*) const {return false;} /// Test if a instances of this @ref file_suppression suppresses a /// given file. /// /// @param file_path the file path to test against. /// /// @return true iff this file_suppression matches the file path @p /// file_path. bool file_suppression::suppresses_file(const string& file_path) { if (file_path.empty()) return false; string fname; tools_utils::base_name(file_path, fname); bool has_regexp = false; if (regex_t_sptr regexp = suppression_base::priv_->get_file_name_regex()) { has_regexp = true; if (!regex::match(regexp, fname)) return false; } if (regex_t_sptr regexp = suppression_base::priv_->get_file_name_not_regex()) { has_regexp = true; if (regex::match(regexp, fname)) return false; } if (!has_regexp) return false; return true; } /// Destructor of @ref file_suppression. file_suppression::~file_suppression() { } /// Read a file suppression from an instance of ini::config::section /// and build a @ref type_suppression as a result. /// /// @param section the section (from an ini file) to read the file /// suppression from. /// /// @return file_suppression_sptr. static file_suppression_sptr read_file_suppression(const ini::config::section& section) { file_suppression_sptr result; if (section.get_name() != "suppress_file") return result; static const char *const sufficient_props[] = { "file_name_regexp", "file_name_not_regexp", "soname_regexp", "soname_not_regexp", }; if (!check_sufficient_props(sufficient_props, sizeof(sufficient_props)/sizeof(char*), section)) return result; ini::simple_property_sptr label_prop = is_simple_property(section.find_property("label")); string label_str = (label_prop ? label_prop->get_value()->as_string() : ""); ini::simple_property_sptr file_name_regex_prop = is_simple_property(section.find_property("file_name_regexp")); string file_name_regex_str = file_name_regex_prop ? file_name_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr file_name_not_regex_prop = is_simple_property(section.find_property("file_name_not_regexp")); string file_name_not_regex_str = file_name_not_regex_prop ? file_name_not_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr soname_regex_prop = is_simple_property(section.find_property("soname_regexp")); string soname_regex_str = soname_regex_prop ? soname_regex_prop->get_value()->as_string() : ""; ini::simple_property_sptr soname_not_regex_prop = is_simple_property(section.find_property("soname_not_regexp")); string soname_not_regex_str = soname_not_regex_prop ? soname_not_regex_prop->get_value()->as_string() : ""; result.reset(new file_suppression(label_str, file_name_regex_str, file_name_not_regex_str)); if (!soname_regex_str.empty()) { result->set_soname_regex_str(soname_regex_str); result->set_drops_artifact_from_ir(true); } if (!soname_not_regex_str.empty()) { result->set_soname_not_regex_str(soname_not_regex_str); result->set_drops_artifact_from_ir(true); } return result; } /// Test if a given suppression specification is a file suppression /// specification. /// /// @param s the instance of @ref suppression_base to test. /// /// @return the instance of @ref file_suppression that @p s points to, /// iff s is an instance of @ref file_suppression. Otherwise, returns /// nil. file_suppression_sptr is_file_suppression(const suppression_sptr s) {return dynamic_pointer_cast(s);} /// Test if a given file path is "suppressed" by at least one file /// suppression specification among a vector of suppression /// specifications. /// /// @param file_path the file path to test. /// /// @param sprs the vector of suppressions to use to test if one of /// them at lease matches the file path @p file_path. /// /// @return a pointer to the first instance of @ref file_suppression /// that matches @p file_path, or nil if no file suppression matches. file_suppression_sptr file_is_suppressed(const string& file_path, const suppressions_type& sprs) { for (suppressions_type::const_iterator i = sprs.begin(); i != sprs.end(); ++i) if (file_suppression_sptr s = is_file_suppression(*i)) if (s->suppresses_file(file_path)) return s; return file_suppression_sptr(); } /// Test if a given SONAME is matched by a given suppression /// specification. /// /// @param soname the SONAME to consider. /// /// @param suppr the suppression specification to consider. /// /// @return true iff a given SONAME is matched by a given suppression /// specification. bool suppression_matches_soname(const string& soname, const suppression_base& suppr) { return suppr.priv_->matches_soname(soname); } /// Test if a given SONAME or file name is matched by a given /// suppression specification. /// /// @param soname the SONAME to consider. /// /// @param filename the file name to consider. /// /// @param suppr the suppression specification to consider. /// /// @return true iff either @p soname or @p filename is matched by the /// suppression specification @p suppr. bool suppression_matches_soname_or_filename(const string& soname, const string& filename, const suppression_base& suppr) { return (suppression_matches_soname(soname, suppr) || suppr.priv_->matches_binary_name(filename)); } /// @return the name of the artificial private type suppression /// specification that is auto-generated by libabigail to suppress /// change reports about types that are not defined in public headers. const char* get_private_types_suppr_spec_label() { static const char *PRIVATE_TYPES_SUPPR_SPEC_NAME = "Artificial private types suppression specification"; return PRIVATE_TYPES_SUPPR_SPEC_NAME; } /// Test if a type suppression specification represents a private type /// suppression automatically generated by libabigail from the user /// telling us where public headers are. /// /// @param s the suppression specification we are looking at. /// /// @return true iff @p s is a private type suppr spec. bool is_private_type_suppr_spec(const type_suppression& s) {return s.get_label() == get_private_types_suppr_spec_label();} /// Test if a type suppression specification represents a private type /// suppression automatically generated by libabigail from the user /// telling us where public headers are. /// /// @param s the suppression specification we are looking at. /// /// @return true iff @p s is a private type suppr spec. bool is_private_type_suppr_spec(const suppression_sptr& s) { type_suppression_sptr type_suppr = is_type_suppression(s); return (type_suppr && type_suppr->get_label() == get_private_types_suppr_spec_label()); } // }// end namespace suppr } // end namespace abigail