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