123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677 |
- // -*- C++ -*-
- // Copyright (C) 2004-2022 Free Software Foundation, Inc.
- // This library is free software; you can redistribute it and/or
- // modify it under the terms of the GNU General Public License as
- // published by the Free Software Foundation; either version 3, or (at
- // your option) any later version.
- // This library is distributed in the hope that it will be useful, but
- // WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- // General Public License for more details.
- // You should have received a copy of the GNU General Public License
- // along with this library; see the file COPYING3. If not see
- // <http://www.gnu.org/licenses/>.
- // Benjamin Kosnik <bkoz@redhat.com>
- #include "testsuite_abi.h"
- #include <cstdlib>
- #include <sstream>
- #include <fstream>
- #include <iostream>
- #include <vector>
- #include <algorithm>
- using namespace std;
- void
- symbol::init(string& data)
- {
- const char delim = ':';
- const char version_delim = '@';
- const string::size_type npos = string::npos;
- string::size_type n = 0;
- // Set the type.
- if (data.find("FUNC") == 0)
- type = symbol::function;
- else if (data.find("OBJECT") == 0)
- type = symbol::object;
- else if (data.find("TLS") == 0)
- type = symbol::tls;
- n = data.find_first_of(delim);
- if (n != npos)
- data.erase(data.begin(), data.begin() + n + 1);
- // Iff object or TLS, get size info.
- if (type == symbol::object || type == symbol::tls)
- {
- n = data.find_first_of(delim);
- if (n != npos)
- {
- string objectsize(data.begin(), data.begin() + n);
- istringstream iss(objectsize);
- int x;
- iss >> x;
- if (!iss.fail())
- size = x;
- data.erase(data.begin(), data.begin() + n + 1);
- }
- }
- // Set the name and raw_name.
- raw_name = string(data.begin(), data.end());
- n = data.find_first_of(version_delim);
- if (n != npos)
- {
- // Found version string.
- name = string(data.begin(), data.begin() + n);
- n = data.find_last_of(version_delim);
- data.erase(data.begin(), data.begin() + n + 1);
- // Set version name.
- version_name = data;
- }
- else
- {
- // No versioning info.
- name = string(data.begin(), data.end());
- version_status = symbol::none;
- }
- // Set the demangled name.
- demangled_name = demangle(name);
- }
- void
- symbol::print() const
- {
- const char tab = '\t';
- cout << name << endl;
- if (demangled_name != name)
- cout << demangled_name << endl;
- string vers;
- switch (version_status)
- {
- case none:
- vers = "none";
- break;
- case compatible:
- vers = "compatible";
- break;
- case incompatible:
- vers = "incompatible";
- break;
- case unversioned:
- vers = "unversioned";
- break;
- default:
- vers = "<default>";
- }
- cout << "version status: " << vers << endl;
- if (version_name.size()
- && (version_status == compatible || version_status == incompatible))
- cout << version_name << endl;
- string type_string;
- switch (type)
- {
- case function:
- type_string = "function";
- break;
- case object:
- type_string = "object";
- break;
- case tls:
- type_string = "tls";
- break;
- case uncategorized:
- type_string = "uncategorized";
- break;
- default:
- type_string = "<default>";
- }
- cout << "type: " << type_string << endl;
- if (type == object || type == tls)
- cout << "type size: " << size << endl;
- string status_string;
- switch (status)
- {
- case added:
- status_string = "added";
- break;
- case subtracted:
- status_string = "subtracted";
- break;
- case undesignated:
- status_string = "undesignated";
- break;
- default:
- status_string = "<default>";
- }
- cout << "status: " << status_string << endl;
- cout << endl;
- }
- bool
- check_version(symbol& test, bool added)
- {
- // Construct list of compatible versions.
- typedef std::vector<std::string> compat_list;
- static compat_list known_versions;
- if (known_versions.empty())
- {
- // NB: First version here must be the default version for this
- // version of DT_SONAME.
- known_versions.push_back("GLIBCXX_3.4");
- known_versions.push_back("GLIBCXX_LDBL_3.4");
- known_versions.push_back("GLIBCXX_3.4.1");
- known_versions.push_back("GLIBCXX_3.4.2");
- known_versions.push_back("GLIBCXX_3.4.3");
- known_versions.push_back("GLIBCXX_3.4.4");
- known_versions.push_back("GLIBCXX_3.4.5");
- known_versions.push_back("GLIBCXX_3.4.6");
- known_versions.push_back("GLIBCXX_3.4.7");
- known_versions.push_back("GLIBCXX_LDBL_3.4.7");
- known_versions.push_back("GLIBCXX_3.4.8");
- known_versions.push_back("GLIBCXX_3.4.9");
- known_versions.push_back("GLIBCXX_3.4.10");
- known_versions.push_back("GLIBCXX_LDBL_3.4.10");
- known_versions.push_back("GLIBCXX_3.4.11");
- known_versions.push_back("GLIBCXX_3.4.12");
- known_versions.push_back("GLIBCXX_3.4.13");
- known_versions.push_back("GLIBCXX_3.4.14");
- known_versions.push_back("GLIBCXX_3.4.15");
- known_versions.push_back("GLIBCXX_3.4.16");
- known_versions.push_back("GLIBCXX_3.4.17");
- known_versions.push_back("GLIBCXX_3.4.18");
- known_versions.push_back("GLIBCXX_3.4.19");
- known_versions.push_back("GLIBCXX_3.4.20");
- known_versions.push_back("GLIBCXX_3.4.21");
- known_versions.push_back("GLIBCXX_LDBL_3.4.21");
- known_versions.push_back("GLIBCXX_3.4.22");
- known_versions.push_back("GLIBCXX_3.4.23");
- known_versions.push_back("GLIBCXX_3.4.24");
- known_versions.push_back("GLIBCXX_3.4.25");
- known_versions.push_back("GLIBCXX_3.4.26");
- known_versions.push_back("GLIBCXX_3.4.27");
- known_versions.push_back("GLIBCXX_3.4.28");
- known_versions.push_back("GLIBCXX_3.4.29");
- known_versions.push_back("GLIBCXX_3.4.30");
- known_versions.push_back("GLIBCXX_LDBL_3.4.29");
- known_versions.push_back("GLIBCXX_IEEE128_3.4.29");
- known_versions.push_back("GLIBCXX_IEEE128_3.4.30");
- known_versions.push_back("CXXABI_1.3");
- known_versions.push_back("CXXABI_LDBL_1.3");
- known_versions.push_back("CXXABI_1.3.1");
- known_versions.push_back("CXXABI_1.3.2");
- known_versions.push_back("CXXABI_1.3.3");
- known_versions.push_back("CXXABI_1.3.4");
- known_versions.push_back("CXXABI_1.3.5");
- known_versions.push_back("CXXABI_1.3.6");
- known_versions.push_back("CXXABI_1.3.7");
- known_versions.push_back("CXXABI_1.3.8");
- known_versions.push_back("CXXABI_1.3.9");
- known_versions.push_back("CXXABI_1.3.10");
- known_versions.push_back("CXXABI_1.3.11");
- known_versions.push_back("CXXABI_1.3.12");
- known_versions.push_back("CXXABI_1.3.13");
- known_versions.push_back("CXXABI_IEEE128_1.3.13");
- known_versions.push_back("CXXABI_TM_1");
- known_versions.push_back("CXXABI_FLOAT128");
- }
- compat_list::iterator begin = known_versions.begin();
- compat_list::iterator end = known_versions.end();
- // Check for compatible version.
- if (test.version_name.size())
- {
- compat_list::iterator it1 = find(begin, end, test.version_name);
- compat_list::iterator it2 = find(begin, end, test.name);
- if (it1 != end)
- test.version_status = symbol::compatible;
- else
- test.version_status = symbol::incompatible;
- // Check that added symbols are added in the latest pre-release version.
- bool latestp = (test.version_name == "GLIBCXX_3.4.30"
- // XXX remove next line when baselines have been regenerated.
- || test.version_name == "GLIBCXX_IEEE128_3.4.30"
- || test.version_name == "CXXABI_1.3.13"
- || test.version_name == "CXXABI_FLOAT128"
- || test.version_name == "CXXABI_TM_1");
- if (added && !latestp)
- test.version_status = symbol::incompatible;
- // Check that long double compatibility symbols demangled as
- // __float128 and regular __float128 symbols are put into some _LDBL_
- // or _FLOAT128 version name.
- if (added && test.demangled_name.find("__float128") != std::string::npos
- && test.demangled_name.find("std::__cxx11::") != 0)
- {
- if (test.version_name.find("_LDBL_") == std::string::npos
- && test.version_name.find("_FLOAT128") == std::string::npos
- && test.version_name.find("_IEEE128") == std::string::npos)
- test.version_status = symbol::incompatible;
- }
- // Check that IEEE128 long double compatibility symbols demangled as
- // __ieee128 are put into some _LDBL_IEEE version name.
- // XXX is this right? might not want *everything* for __ieee128 in here.
- if (added && test.demangled_name.find("__ieee128") != std::string::npos)
- {
- if (test.version_name.find("_IEEE128") == std::string::npos)
- test.version_status = symbol::incompatible;
- }
- // Check for weak label.
- if (it1 == end && it2 == end)
- test.version_status = symbol::incompatible;
- // Check that
- // GLIBCXX_3.4
- // GLIBCXX_3.4.5
- // version as compatible
- // XXX
- }
- else
- {
- if (added)
- {
- // New version labels are ok. The rest are not.
- compat_list::iterator it2 = find(begin, end, test.name);
- if (it2 != end)
- test.version_status = symbol::compatible;
- else
- test.version_status = symbol::incompatible;
- }
- }
- return test.version_status == symbol::compatible;
- }
- bool
- check_compatible(symbol& lhs, symbol& rhs, bool verbose)
- {
- bool ret = true;
- const char tab = '\t';
- // Check to see if symbol_objects are compatible.
- if (lhs.type != rhs.type)
- {
- ret = false;
- if (verbose)
- cout << tab << "incompatible types" << endl;
- }
- if (lhs.name != rhs.name)
- {
- ret = false;
- if (verbose)
- cout << tab << "incompatible names" << endl;
- }
- if (lhs.size != rhs.size)
- {
- ret = false;
- if (verbose)
- {
- cout << tab << "incompatible sizes" << endl;
- cout << tab << lhs.size << endl;
- cout << tab << rhs.size << endl;
- }
- }
- if (lhs.version_name != rhs.version_name
- && !check_version(lhs) && !check_version(rhs))
- {
- ret = false;
- if (verbose)
- {
- cout << tab << "incompatible versions" << endl;
- cout << tab << lhs.version_name << endl;
- cout << tab << rhs.version_name << endl;
- }
- }
- if (verbose)
- cout << endl;
- return ret;
- }
- inline bool
- has_symbol(const string& name, const symbols& s) throw()
- { return s.find(name) != s.end(); }
- const symbol&
- get_symbol(const string& name, const symbols& s)
- {
- symbols::const_iterator i = s.find(name);
- if (i != s.end())
- {
- return i->second;
- }
- else
- {
- ostringstream os;
- os << "get_symbol failed for symbol " << name;
- __throw_logic_error(os.str().c_str());
- }
- }
- void
- examine_symbol(const char* name, const char* file)
- {
- symbols s = create_symbols(file);
- const symbol& sym = get_symbol(name, s);
- sym.print();
- }
- int
- compare_symbols(const char* baseline_file, const char* test_file,
- bool verbose)
- {
- // Input both lists of symbols into container.
- symbols baseline = create_symbols(baseline_file);
- symbols test = create_symbols(test_file);
- // Sanity check results.
- if (!baseline.size() || !test.size())
- {
- cerr << "Problems parsing the list of exported symbols." << endl;
- exit(2);
- }
- // Check to see if any long double compatibility symbols are produced.
- bool ld_version_found(false);
- symbols::iterator li(test.begin());
- while (!ld_version_found && li != test.end())
- {
- if (li->second.version_name.find("_LDBL_") != std::string::npos)
- ld_version_found = true;
- ++li;
- }
- // Similarly for IEEE128 symbols.
- bool ieee_version_found(false);
- for (li = test.begin(); li != test.end(); ++li)
- if (li->second.version_name.find("_IEEE128_") != std::string::npos)
- {
- ieee_version_found = true;
- break;
- }
- // Sort out names.
- // Assuming all baseline names and test names are both unique w/ no
- // duplicates.
- //
- // The names added to missing_names are baseline names not found in
- // test names
- // -> symbols that have been deleted.
- //
- // The names added to added_names are test names not in
- // baseline names
- // -> symbols that have been added.
- typedef std::vector<std::string> symbol_names;
- symbol_names shared_names;
- symbol_names missing_names;
- symbol_names added_names;
- for (li = test.begin(); li != test.end(); ++li)
- added_names.push_back(li->first);
- for (symbols::iterator i = baseline.begin(); i != baseline.end(); ++i)
- {
- string name(i->first);
- symbol_names::iterator end = added_names.end();
- symbol_names::iterator it = find(added_names.begin(), end, name);
- if (it != end)
- {
- // Found.
- shared_names.push_back(name);
- added_names.erase(it);
- }
- else
- {
- // Iff no test long double compatibility symbols at all and the symbol
- // missing is a baseline long double compatibility symbol, skip.
- string version_name(i->second.version_name.size()
- ? i->second.version_name : i->first);
- bool base_ld(version_name.find("_LDBL_") != std::string::npos);
- bool base_ieee(version_name.find("_IEEE128_") != std::string::npos);
- if ((!base_ld || (base_ld && ld_version_found))
- && (!base_ieee || (base_ieee && ieee_version_found)))
- missing_names.push_back(name);
- }
- }
- // Fill out list of incompatible symbols.
- typedef pair<symbol, symbol> symbol_pair;
- vector<symbol_pair> incompatible;
- // Fill out list of undesignated symbols.
- vector<symbol> undesignated;
- // Check missing names for compatibility.
- for (size_t j = 0; j < missing_names.size(); ++j)
- {
- symbol& sbase = baseline[missing_names[j]];
- sbase.status = symbol::subtracted;
- incompatible.push_back(symbol_pair(sbase, sbase));
- }
- // Check shared names for compatibility.
- const symbol_names::size_type shared_size = shared_names.size();
- for (size_t k = 0; k < shared_size; ++k)
- {
- symbol& sbase = baseline[shared_names[k]];
- symbol& stest = test[shared_names[k]];
- stest.status = symbol::existing;
- if (!check_compatible(sbase, stest))
- incompatible.push_back(symbol_pair(sbase, stest));
- }
- // Check added names for compatibility.
- const symbol_names::size_type added_size = added_names.size();
- for (size_t l = 0; l < added_size; ++l)
- {
- symbol& stest = test[added_names[l]];
- // Mark TLS as undesignated, remove from added.
- if (stest.type == symbol::tls)
- {
- stest.status = symbol::undesignated;
- if (!check_version(stest, false))
- incompatible.push_back(symbol_pair(stest, stest));
- else
- undesignated.push_back(stest);
- }
- // See PR libstdc++/103407 - abi_check FAILs on Solaris
- else if (stest.type == symbol::function
- && stest.name.compare(0, 23, "_ZSt10from_charsPKcS0_R") == 0
- && stest.name.find_first_of("def", 23) == 23
- && (stest.version_name == "GLIBCXX_3.4.29"
- || stest.version_name == "GLIBCXX_3.4.30"))
- {
- stest.status = symbol::undesignated;
- if (!check_version(stest, false))
- incompatible.push_back(symbol_pair(stest, stest));
- else
- undesignated.push_back(stest);
- }
- else
- {
- stest.status = symbol::added;
- if (!check_version(stest, true))
- incompatible.push_back(symbol_pair(stest, stest));
- }
- }
- // Normalize added names and undesignated names.
- const size_t undesignated_size = undesignated.size();
- for (size_t l = 0; l < undesignated_size; ++l)
- {
- symbol& sundes = undesignated[l];
- symbol_names::iterator end = added_names.end();
- symbol_names::iterator it = find(added_names.begin(), end, sundes.name);
- if (it != end)
- {
- // Found.
- added_names.erase(it);
- }
- else
- __throw_runtime_error(sundes.name.c_str());
- }
- // Report results.
- if (verbose && added_names.size())
- {
- cout << endl << added_names.size() << " added symbols " << endl;
- for (size_t j = 0; j < added_names.size() ; ++j)
- {
- cout << j << endl;
- test[added_names[j]].print();
- }
- }
- if (verbose && missing_names.size())
- {
- cout << endl << missing_names.size() << " missing symbols " << endl;
- for (size_t j = 0; j < missing_names.size() ; ++j)
- {
- cout << j << endl;
- baseline[missing_names[j]].print();
- }
- }
- if (verbose && undesignated.size())
- {
- cout << endl << undesignated.size() << " undesignated symbols " << endl;
- for (size_t j = 0; j < undesignated.size() ; ++j)
- {
- // First, print index.
- cout << j << endl;
- // Second, report name.
- symbol& s = undesignated[j];
- s.print();
- }
- }
- if (verbose && incompatible.size())
- {
- cout << endl << incompatible.size() << " incompatible symbols " << endl;
- for (size_t j = 0; j < incompatible.size() ; ++j)
- {
- // First, print index.
- cout << j << endl;
- // Second, report name.
- symbol& sbase = incompatible[j].first;
- symbol& stest = incompatible[j].second;
- stest.print();
- // Third, report reason or reasons incompatible.
- check_compatible(sbase, stest, true);
- }
- }
- cout << "\n\t\t==== libstdc++-v3 check-abi Summary ====" << endl;
- cout << endl;
- cout << "# of added symbols:\t\t " << added_names.size() << endl;
- cout << "# of missing symbols:\t\t " << missing_names.size() << endl;
- cout << "# of undesignated symbols:\t " << undesignated.size() << endl;
- cout << "# of incompatible symbols:\t " << incompatible.size() << endl;
- cout << endl;
- cout << "using: " << baseline_file << endl;
- return !(missing_names.size() || incompatible.size());
- }
- symbols
- create_symbols(const char* file)
- {
- symbols s;
- ifstream ifs(file);
- if (ifs.is_open())
- {
- // Organize file data into an associated container (symbols) of symbol
- // objects mapped to mangled names without versioning
- // information.
- const string empty;
- string line = empty;
- while (getline(ifs, line).good())
- {
- symbol tmp;
- tmp.init(line);
- s[tmp.name] = tmp;
- line = empty;
- }
- }
- else
- {
- ostringstream os;
- os << "create_symbols failed for file " << file;
- __throw_runtime_error(os.str().c_str());
- }
- return s;
- }
- std::string
- demangle(const std::string& mangled)
- {
- std::string name;
- if (mangled[0] != '_' || mangled[1] != 'Z')
- {
- // This is not a mangled symbol, thus has "C" linkage.
- name = mangled;
- }
- else
- {
- // Use __cxa_demangle to demangle.
- int status = 0;
- char* ptr = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status);
- if (ptr)
- {
- name = ptr;
- free(ptr);
- }
- else
- {
- switch (status)
- {
- case 0:
- name = "error code = 0: success";
- break;
- case -1:
- name = "error code = -1: memory allocation failure";
- break;
- case -2:
- name = "error code = -2: invalid mangled name";
- break;
- case -3:
- name = "error code = -3: invalid arguments";
- break;
- default:
- name = "error code unknown - who knows what happened";
- }
- }
- }
- return name;
- }
|