1// -*- C++ -*- 2 3// Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. 4 5// This library is free software; you can redistribute it and/or 6// modify it under the terms of the GNU General Public License as 7// published by the Free Software Foundation; either version 3, or (at 8// your option) any later version. 9 10// This library is distributed in the hope that it will be useful, but 11// WITHOUT ANY WARRANTY; without even the implied warranty of 12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13// General Public License for more details. 14 15// You should have received a copy of the GNU General Public License 16// along with this library; see the file COPYING3. If not see 17// <http://www.gnu.org/licenses/>. 18 19 20// Benjamin Kosnik <bkoz@redhat.com> 21 22#include "testsuite_abi.h" 23#include <cstdlib> 24#include <sstream> 25#include <fstream> 26#include <iostream> 27#include <vector> 28#include <algorithm> 29 30using namespace std; 31 32void 33symbol::init(string& data) 34{ 35 const char delim = ':'; 36 const char version_delim = '@'; 37 const string::size_type npos = string::npos; 38 string::size_type n = 0; 39 40 // Set the type. 41 if (data.find("FUNC") == 0) 42 type = symbol::function; 43 else if (data.find("OBJECT") == 0) 44 type = symbol::object; 45 46 n = data.find_first_of(delim); 47 if (n != npos) 48 data.erase(data.begin(), data.begin() + n + 1); 49 50 // Iff object, get size info. 51 if (type == symbol::object) 52 { 53 n = data.find_first_of(delim); 54 if (n != npos) 55 { 56 string objectsize(data.begin(), data.begin() + n); 57 istringstream iss(objectsize); 58 int x; 59 iss >> x; 60 if (!iss.fail()) 61 size = x; 62 data.erase(data.begin(), data.begin() + n + 1); 63 } 64 } 65 66 // Set the name and raw_name. 67 raw_name = string(data.begin(), data.end()); 68 n = data.find_first_of(version_delim); 69 if (n != npos) 70 { 71 // Found version string. 72 name = string(data.begin(), data.begin() + n); 73 n = data.find_last_of(version_delim); 74 data.erase(data.begin(), data.begin() + n + 1); 75 76 // Set version name. 77 version_name = data; 78 } 79 else 80 { 81 // No versioning info. 82 name = string(data.begin(), data.end()); 83 version_status = symbol::none; 84 } 85 86 // Set the demangled name. 87 demangled_name = demangle(name); 88} 89 90void 91symbol::print() const 92{ 93 const char tab = '\t'; 94 cout << name << endl; 95 96 if (demangled_name != name) 97 cout << demangled_name << endl; 98 99 string vers; 100 switch (version_status) 101 { 102 case none: 103 vers = "none"; 104 break; 105 case compatible: 106 vers = "compatible"; 107 break; 108 case incompatible: 109 vers = "incompatible"; 110 break; 111 case unversioned: 112 vers = "unversioned"; 113 break; 114 default: 115 vers = "<default>"; 116 } 117 cout << "version status: " << vers << endl; 118 119 if (version_name.size() 120 && (version_status == compatible || version_status == incompatible)) 121 cout << version_name << endl; 122 123 string type_string; 124 switch (type) 125 { 126 case function: 127 type_string = "function"; 128 break; 129 case object: 130 type_string = "object"; 131 break; 132 case uncategorized: 133 type_string = "uncategorized"; 134 break; 135 default: 136 type_string = "<default>"; 137 } 138 cout << "type: " << type_string << endl; 139 140 if (type == object) 141 cout << "type size: " << size << endl; 142 143 string status_string; 144 switch (status) 145 { 146 case added: 147 status_string = "added"; 148 break; 149 case subtracted: 150 status_string = "subtracted"; 151 break; 152 case undesignated: 153 status_string = "undesignated"; 154 break; 155 default: 156 status_string = "<default>"; 157 } 158 cout << "status: " << status_string << endl; 159 160 cout << endl; 161} 162 163 164bool 165check_version(symbol& test, bool added) 166{ 167 // Construct list of compatible versions. 168 typedef std::vector<std::string> compat_list; 169 static compat_list known_versions; 170 if (known_versions.empty()) 171 { 172 // NB: First version here must be the default version for this 173 // version of DT_SONAME. 174 known_versions.push_back("GLIBCXX_3.4"); 175 known_versions.push_back("GLIBCXX_3.4.1"); 176 known_versions.push_back("GLIBCXX_3.4.2"); 177 known_versions.push_back("GLIBCXX_3.4.3"); 178 known_versions.push_back("GLIBCXX_3.4.4"); 179 known_versions.push_back("GLIBCXX_3.4.5"); 180 known_versions.push_back("GLIBCXX_3.4.6"); 181 known_versions.push_back("GLIBCXX_3.4.7"); 182 known_versions.push_back("GLIBCXX_3.4.8"); 183 known_versions.push_back("GLIBCXX_3.4.9"); 184 known_versions.push_back("GLIBCXX_3.4.10"); 185 known_versions.push_back("GLIBCXX_3.4.11"); 186 known_versions.push_back("GLIBCXX_3.4.12"); 187 known_versions.push_back("GLIBCXX_3.4.13"); 188 known_versions.push_back("GLIBCXX_3.4.14"); 189 known_versions.push_back("GLIBCXX_LDBL_3.4"); 190 known_versions.push_back("GLIBCXX_LDBL_3.4.7"); 191 known_versions.push_back("GLIBCXX_LDBL_3.4.10"); 192 known_versions.push_back("CXXABI_1.3"); 193 known_versions.push_back("CXXABI_1.3.1"); 194 known_versions.push_back("CXXABI_1.3.2"); 195 known_versions.push_back("CXXABI_1.3.3"); 196 known_versions.push_back("CXXABI_1.3.4"); 197 known_versions.push_back("CXXABI_LDBL_1.3"); 198 } 199 compat_list::iterator begin = known_versions.begin(); 200 compat_list::iterator end = known_versions.end(); 201 202 // Check for compatible version. 203 if (test.version_name.size()) 204 { 205 compat_list::iterator it1 = find(begin, end, test.version_name); 206 compat_list::iterator it2 = find(begin, end, test.name); 207 if (it1 != end) 208 test.version_status = symbol::compatible; 209 else 210 test.version_status = symbol::incompatible; 211 212 // Check that added symbols aren't added in the base version. 213 if (added && test.version_name == known_versions[0]) 214 test.version_status = symbol::incompatible; 215 216 // Check that long double compatibility symbols demangled as 217 // __float128 are put into some _LDBL_ version name. 218 if (added && test.demangled_name.find("__float128") != std::string::npos) 219 { 220 // Has to be in _LDBL_ version name. 221 if (test.version_name.find("_LDBL_") == std::string::npos) 222 test.version_status = symbol::incompatible; 223 } 224 225 // Check for weak label. 226 if (it1 == end && it2 == end) 227 test.version_status = symbol::incompatible; 228 229 // Check that 230 // GLIBCXX_3.4 231 // GLIBCXX_3.4.5 232 // version as compatible 233 // XXX 234 } 235 else 236 { 237 if (added) 238 { 239 // New version labels are ok. The rest are not. 240 compat_list::iterator it2 = find(begin, end, test.name); 241 if (it2 != end) 242 test.version_status = symbol::compatible; 243 else 244 test.version_status = symbol::incompatible; 245 } 246 } 247 return test.version_status == symbol::compatible; 248} 249 250bool 251check_compatible(symbol& lhs, symbol& rhs, bool verbose) 252{ 253 bool ret = true; 254 const char tab = '\t'; 255 256 // Check to see if symbol_objects are compatible. 257 if (lhs.type != rhs.type) 258 { 259 ret = false; 260 if (verbose) 261 cout << tab << "incompatible types" << endl; 262 } 263 264 if (lhs.name != rhs.name) 265 { 266 ret = false; 267 if (verbose) 268 cout << tab << "incompatible names" << endl; 269 } 270 271 if (lhs.size != rhs.size) 272 { 273 ret = false; 274 if (verbose) 275 { 276 cout << tab << "incompatible sizes" << endl; 277 cout << tab << lhs.size << endl; 278 cout << tab << rhs.size << endl; 279 } 280 } 281 282 if (lhs.version_name != rhs.version_name 283 && !check_version(lhs) && !check_version(rhs)) 284 { 285 ret = false; 286 if (verbose) 287 { 288 cout << tab << "incompatible versions" << endl; 289 cout << tab << lhs.version_name << endl; 290 cout << tab << rhs.version_name << endl; 291 } 292 } 293 294 if (verbose) 295 cout << endl; 296 297 return ret; 298} 299 300 301inline bool 302has_symbol(const string& name, const symbols& s) throw() 303{ return s.find(name) != s.end(); } 304 305const symbol& 306get_symbol(const string& name, const symbols& s) 307{ 308 symbols::const_iterator i = s.find(name); 309 if (i != s.end()) 310 { 311 return i->second; 312 } 313 else 314 { 315 ostringstream os; 316 os << "get_symbol failed for symbol " << name; 317 __throw_logic_error(os.str().c_str()); 318 } 319} 320 321void 322examine_symbol(const char* name, const char* file) 323{ 324 try 325 { 326 symbols s = create_symbols(file); 327 const symbol& sym = get_symbol(name, s); 328 sym.print(); 329 } 330 catch(...) 331 { __throw_exception_again; } 332} 333 334int 335compare_symbols(const char* baseline_file, const char* test_file, 336 bool verbose) 337{ 338 // Input both lists of symbols into container. 339 symbols baseline = create_symbols(baseline_file); 340 symbols test = create_symbols(test_file); 341 342 // Sanity check results. 343 if (!baseline.size() || !test.size()) 344 { 345 cerr << "Problems parsing the list of exported symbols." << endl; 346 exit(2); 347 } 348 349 // Check to see if any long double compatibility symbols are produced. 350 bool ld_version_found(false); 351 symbols::iterator li(test.begin()); 352 while (!ld_version_found && li != test.end()) 353 { 354 if (li->second.version_name.find("_LDBL_") != std::string::npos) 355 ld_version_found = true; 356 ++li; 357 } 358 359 // Sort out names. 360 // Assuming all baseline names and test names are both unique w/ no 361 // duplicates. 362 // 363 // The names added to missing_names are baseline names not found in 364 // test names 365 // -> symbols that have been deleted. 366 // 367 // The names added to added_names are test names not in 368 // baseline names 369 // -> symbols that have been added. 370 typedef std::vector<std::string> symbol_names; 371 symbol_names shared_names; 372 symbol_names missing_names; 373 symbol_names added_names; 374 for (li = test.begin(); li != test.end(); ++li) 375 added_names.push_back(li->first); 376 377 for (symbols::iterator i = baseline.begin(); i != baseline.end(); ++i) 378 { 379 string name(i->first); 380 symbol_names::iterator end = added_names.end(); 381 symbol_names::iterator it = find(added_names.begin(), end, name); 382 if (it != end) 383 { 384 // Found. 385 shared_names.push_back(name); 386 added_names.erase(it); 387 } 388 else 389 { 390 // Iff no test long double compatibility symbols at all and the symbol 391 // missing is a baseline long double compatibility symbol, skip. 392 string version_name(i->second.version_name); 393 bool base_ld(version_name.find("_LDBL_") != std::string::npos); 394 if (!base_ld || base_ld && ld_version_found) 395 missing_names.push_back(name); 396 } 397 } 398 399 // Fill out list of incompatible symbols. 400 typedef pair<symbol, symbol> symbol_pair; 401 vector<symbol_pair> incompatible; 402 403 // Check missing names for compatibility. 404 for (size_t j = 0; j < missing_names.size(); ++j) 405 { 406 symbol& sbase = baseline[missing_names[j]]; 407 sbase.status = symbol::subtracted; 408 incompatible.push_back(symbol_pair(sbase, sbase)); 409 } 410 411 // Check shared names for compatibility. 412 const symbol_names::size_type shared_size = shared_names.size(); 413 for (size_t k = 0; k < shared_size; ++k) 414 { 415 symbol& sbase = baseline[shared_names[k]]; 416 symbol& stest = test[shared_names[k]]; 417 stest.status = symbol::existing; 418 if (!check_compatible(sbase, stest)) 419 incompatible.push_back(symbol_pair(sbase, stest)); 420 } 421 422 // Check added names for compatibility. 423 const symbol_names::size_type added_size = added_names.size(); 424 for (size_t l = 0; l < added_size; ++l) 425 { 426 symbol& stest = test[added_names[l]]; 427 stest.status = symbol::added; 428 if (!check_version(stest, true)) 429 incompatible.push_back(symbol_pair(stest, stest)); 430 } 431 432 // Report results. 433 if (verbose && added_names.size()) 434 { 435 cout << endl << added_names.size() << " added symbols " << endl; 436 for (size_t j = 0; j < added_names.size() ; ++j) 437 { 438 cout << j << endl; 439 test[added_names[j]].print(); 440 } 441 } 442 443 if (verbose && missing_names.size()) 444 { 445 cout << endl << missing_names.size() << " missing symbols " << endl; 446 for (size_t j = 0; j < missing_names.size() ; ++j) 447 { 448 cout << j << endl; 449 baseline[missing_names[j]].print(); 450 } 451 } 452 453 if (verbose && incompatible.size()) 454 { 455 cout << endl << incompatible.size() << " incompatible symbols " << endl; 456 for (size_t j = 0; j < incompatible.size() ; ++j) 457 { 458 // First, print index. 459 cout << j << endl; 460 461 // Second, report name. 462 symbol& sbase = incompatible[j].first; 463 symbol& stest = incompatible[j].second; 464 stest.print(); 465 466 // Second, report reason or reasons incompatible. 467 check_compatible(sbase, stest, true); 468 } 469 } 470 471 cout << "\n\t\t=== libstdc++-v3 check-abi Summary ===" << endl; 472 cout << endl; 473 cout << "# of added symbols:\t\t " << added_names.size() << endl; 474 cout << "# of missing symbols:\t\t " << missing_names.size() << endl; 475 cout << "# of incompatible symbols:\t " << incompatible.size() << endl; 476 cout << endl; 477 cout << "using: " << baseline_file << endl; 478 479 return !(missing_names.size() || incompatible.size()); 480} 481 482 483symbols 484create_symbols(const char* file) 485{ 486 symbols s; 487 ifstream ifs(file); 488 if (ifs.is_open()) 489 { 490 // Organize file data into an associated container (symbols) of symbol 491 // objects mapped to mangled names without versioning 492 // information. 493 const string empty; 494 string line = empty; 495 while (getline(ifs, line).good()) 496 { 497 symbol tmp; 498 tmp.init(line); 499 s[tmp.name] = tmp; 500 line = empty; 501 } 502 } 503 else 504 { 505 ostringstream os; 506 os << "create_symbols failed for file " << file; 507 __throw_runtime_error(os.str().c_str()); 508 } 509 return s; 510} 511 512 513const char* 514demangle(const std::string& mangled) 515{ 516 const char* name; 517 if (mangled[0] != '_' || mangled[1] != 'Z') 518 { 519 // This is not a mangled symbol, thus has "C" linkage. 520 name = mangled.c_str(); 521 } 522 else 523 { 524 // Use __cxa_demangle to demangle. 525 int status = 0; 526 name = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status); 527 if (!name) 528 { 529 switch (status) 530 { 531 case 0: 532 name = "error code = 0: success"; 533 break; 534 case -1: 535 name = "error code = -1: memory allocation failure"; 536 break; 537 case -2: 538 name = "error code = -2: invalid mangled name"; 539 break; 540 case -3: 541 name = "error code = -3: invalid arguments"; 542 break; 543 default: 544 name = "error code unknown - who knows what happened"; 545 } 546 } 547 } 548 return name; 549} 550 551