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