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