]>
Commit | Line | Data |
---|---|---|
4b260c20 BK |
1 | // -*- C++ -*- |
2 | ||
56ffd9b3 | 3 | // Copyright (C) 2004, 2005 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 | |
17 | // the Free Software Foundation, 59 Temple Place - Suite 330, Boston, | |
18 | // MA 02111-1307, 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 | ||
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; | |
52 | else | |
53 | type = symbol::error; | |
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 size(data.begin(), data.begin() + n); | |
65 | istringstream iss(size); | |
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. | |
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()); | |
90 | data.erase(data.begin(), data.end()); | |
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'; | |
101 | cout << tab << name << endl; | |
102 | cout << tab << demangled_name << endl; | |
103 | cout << tab << version_name << endl; | |
104 | ||
105 | string type_string; | |
106 | switch (type) | |
107 | { | |
108 | case none: | |
109 | type_string = "none"; | |
110 | break; | |
111 | case function: | |
112 | type_string = "function"; | |
113 | break; | |
114 | case object: | |
115 | type_string = "object"; | |
116 | break; | |
117 | case error: | |
118 | type_string = "error"; | |
119 | break; | |
120 | default: | |
121 | type_string = "<default>"; | |
122 | } | |
123 | cout << tab << type_string << endl; | |
124 | ||
125 | if (type == object) | |
126 | cout << tab << size << endl; | |
127 | ||
128 | string status_string; | |
129 | switch (status) | |
130 | { | |
131 | case unknown: | |
132 | status_string = "unknown"; | |
133 | break; | |
134 | case added: | |
135 | status_string = "added"; | |
136 | break; | |
137 | case subtracted: | |
138 | status_string = "subtracted"; | |
139 | break; | |
140 | case compatible: | |
141 | status_string = "compatible"; | |
142 | break; | |
143 | case incompatible: | |
144 | status_string = "incompatible"; | |
145 | break; | |
146 | default: | |
147 | status_string = "<default>"; | |
148 | } | |
149 | cout << tab << status_string << endl; | |
150 | } | |
151 | ||
152 | ||
153 | bool | |
154 | check_version(const symbol& test, bool added) | |
155 | { | |
156 | typedef std::vector<std::string> compat_list; | |
157 | static compat_list known_versions; | |
158 | if (known_versions.empty()) | |
159 | { | |
160 | known_versions.push_back("GLIBCPP_3.2"); // base version | |
161 | known_versions.push_back("GLIBCPP_3.2.1"); | |
162 | known_versions.push_back("GLIBCPP_3.2.2"); | |
163 | known_versions.push_back("GLIBCPP_3.2.3"); // gcc-3.3.0 | |
164 | known_versions.push_back("GLIBCXX_3.4"); | |
7c9fee34 | 165 | known_versions.push_back("GLIBCXX_3.4.1"); |
9e802114 | 166 | known_versions.push_back("GLIBCXX_3.4.2"); |
0e98ac62 | 167 | known_versions.push_back("GLIBCXX_3.4.3"); |
fcb94d10 | 168 | known_versions.push_back("GLIBCXX_3.4.4"); |
36b72d8d | 169 | known_versions.push_back("GLIBCXX_3.4.5"); |
4b260c20 BK |
170 | known_versions.push_back("CXXABI_1.2"); |
171 | known_versions.push_back("CXXABI_1.2.1"); | |
172 | known_versions.push_back("CXXABI_1.3"); | |
44dd2da2 | 173 | known_versions.push_back("CXXABI_1.3.1"); |
4b260c20 BK |
174 | } |
175 | compat_list::iterator begin = known_versions.begin(); | |
176 | compat_list::iterator end = known_versions.end(); | |
177 | ||
178 | // Check version names for compatibility... | |
179 | compat_list::iterator it1 = find(begin, end, test.version_name); | |
180 | ||
181 | // Check for weak label. | |
182 | compat_list::iterator it2 = find(begin, end, test.name); | |
183 | ||
184 | // Check that added symbols aren't added in the base version. | |
185 | bool compat = true; | |
186 | if (added && test.version_name == known_versions[0]) | |
187 | compat = false; | |
188 | ||
189 | if (it1 == end && it2 == end) | |
190 | compat = false; | |
191 | ||
192 | return compat; | |
193 | } | |
194 | ||
195 | bool | |
196 | check_compatible(const symbol& lhs, const symbol& rhs, bool verbose) | |
197 | { | |
198 | bool ret = true; | |
199 | const char tab = '\t'; | |
200 | ||
201 | // Check to see if symbol_objects are compatible. | |
202 | if (lhs.type != rhs.type) | |
203 | { | |
204 | ret = false; | |
205 | if (verbose) | |
206 | cout << tab << "incompatible types" << endl; | |
207 | } | |
208 | ||
209 | if (lhs.name != rhs.name) | |
210 | { | |
211 | ret = false; | |
212 | if (verbose) | |
213 | cout << tab << "incompatible names" << endl; | |
214 | } | |
215 | ||
216 | if (lhs.size != rhs.size) | |
217 | { | |
218 | ret = false; | |
219 | if (verbose) | |
220 | { | |
221 | cout << tab << "incompatible sizes" << endl; | |
222 | cout << tab << lhs.size << endl; | |
223 | cout << tab << rhs.size << endl; | |
224 | } | |
225 | } | |
226 | ||
227 | if (lhs.version_name != rhs.version_name | |
228 | && !check_version(lhs) && !check_version(rhs)) | |
229 | { | |
230 | ret = false; | |
231 | if (verbose) | |
232 | { | |
233 | cout << tab << "incompatible versions" << endl; | |
234 | cout << tab << lhs.version_name << endl; | |
235 | cout << tab << rhs.version_name << endl; | |
236 | } | |
237 | } | |
238 | ||
239 | if (verbose) | |
240 | cout << endl; | |
241 | ||
242 | return ret; | |
243 | } | |
244 | ||
245 | ||
246 | bool | |
247 | has_symbol(const string& mangled, const symbols& s) throw() | |
248 | { | |
249 | const symbol_names& names = s.first; | |
250 | symbol_names::const_iterator i = find(names.begin(), names.end(), mangled); | |
251 | return i != names.end(); | |
252 | } | |
253 | ||
254 | symbol& | |
255 | get_symbol(const string& mangled, const symbols& s) | |
256 | { | |
257 | const symbol_names& names = s.first; | |
258 | symbol_names::const_iterator i = find(names.begin(), names.end(), mangled); | |
259 | if (i != names.end()) | |
260 | { | |
261 | symbol_objects objects = s.second; | |
262 | return objects[mangled]; | |
263 | } | |
264 | else | |
265 | { | |
266 | ostringstream os; | |
267 | os << "get_symbol failed for symbol " << mangled; | |
56ffd9b3 | 268 | __throw_logic_error(os.str().c_str()); |
4b260c20 BK |
269 | } |
270 | } | |
271 | ||
272 | void | |
273 | examine_symbol(const char* name, const char* file) | |
274 | { | |
275 | try | |
276 | { | |
277 | symbols s = create_symbols(file); | |
278 | symbol& sym = get_symbol(name, s); | |
279 | sym.print(); | |
280 | } | |
281 | catch(...) | |
d98fd134 | 282 | { __throw_exception_again; } |
4b260c20 BK |
283 | } |
284 | ||
fdbba6bc | 285 | int |
4b260c20 BK |
286 | compare_symbols(const char* baseline_file, const char* test_file, |
287 | bool verbose) | |
288 | { | |
289 | // Input both lists of symbols into container. | |
290 | symbols baseline = create_symbols(baseline_file); | |
291 | symbols test = create_symbols(test_file); | |
292 | symbol_names& baseline_names = baseline.first; | |
293 | symbol_objects& baseline_objects = baseline.second; | |
294 | symbol_names& test_names = test.first; | |
295 | symbol_objects& test_objects = test.second; | |
296 | ||
297 | // Sanity check results. | |
298 | const symbol_names::size_type baseline_size = baseline_names.size(); | |
299 | const symbol_names::size_type test_size = test_names.size(); | |
300 | if (!baseline_size || !test_size) | |
301 | { | |
302 | cerr << "Problems parsing the list of exported symbols." << endl; | |
303 | exit(2); | |
304 | } | |
305 | ||
306 | // Sort out names. | |
307 | // Assuming baseline_names, test_names are both unique w/ no duplicates. | |
308 | // | |
309 | // The names added to missing_names are baseline_names not found in | |
310 | // test_names | |
311 | // -> symbols that have been deleted. | |
312 | // | |
313 | // The names added to added_names are test_names are names not in | |
314 | // baseline_names | |
315 | // -> symbols that have been added. | |
316 | symbol_names shared_names; | |
317 | symbol_names missing_names; | |
318 | symbol_names added_names = test_names; | |
319 | for (size_t i = 0; i < baseline_size; ++i) | |
320 | { | |
321 | string what(baseline_names[i]); | |
322 | symbol_names::iterator end = added_names.end(); | |
323 | symbol_names::iterator it = find(added_names.begin(), end, what); | |
324 | if (it != end) | |
325 | { | |
326 | // Found. | |
327 | shared_names.push_back(what); | |
328 | added_names.erase(it); | |
329 | } | |
330 | else | |
331 | missing_names.push_back(what); | |
332 | } | |
333 | ||
334 | // Check missing names for compatibility. | |
335 | typedef pair<symbol, symbol> symbol_pair; | |
336 | vector<symbol_pair> incompatible; | |
95b147fe | 337 | for (size_t j = 0; j < missing_names.size(); ++j) |
4b260c20 | 338 | { |
95b147fe | 339 | symbol base = baseline_objects[missing_names[j]]; |
4b260c20 BK |
340 | incompatible.push_back(symbol_pair(base, base)); |
341 | } | |
342 | ||
343 | // Check shared names for compatibility. | |
95b147fe | 344 | for (size_t k = 0; k < shared_names.size(); ++k) |
4b260c20 | 345 | { |
95b147fe SM |
346 | symbol base = baseline_objects[shared_names[k]]; |
347 | symbol test = test_objects[shared_names[k]]; | |
4b260c20 BK |
348 | if (!check_compatible(base, test)) |
349 | incompatible.push_back(symbol_pair(base, test)); | |
350 | } | |
351 | ||
352 | // Check added names for compatibility. | |
95b147fe | 353 | for (size_t l = 0; l < added_names.size(); ++l) |
4b260c20 | 354 | { |
95b147fe | 355 | symbol test = test_objects[added_names[l]]; |
4b260c20 BK |
356 | if (!check_version(test, true)) |
357 | incompatible.push_back(symbol_pair(test, test)); | |
358 | } | |
359 | ||
360 | // Report results. | |
361 | if (verbose && added_names.size()) | |
362 | { | |
363 | cout << added_names.size() << " added symbols " << endl; | |
364 | for (size_t j = 0; j < added_names.size() ; ++j) | |
365 | test_objects[added_names[j]].print(); | |
366 | } | |
367 | ||
368 | if (verbose && missing_names.size()) | |
369 | { | |
370 | cout << missing_names.size() << " missing symbols " << endl; | |
371 | for (size_t j = 0; j < missing_names.size() ; ++j) | |
372 | baseline_objects[missing_names[j]].print(); | |
373 | } | |
374 | ||
375 | if (verbose && incompatible.size()) | |
376 | { | |
377 | cout << incompatible.size() << " incompatible symbols " << endl; | |
378 | for (size_t j = 0; j < incompatible.size() ; ++j) | |
379 | { | |
380 | // First, report name. | |
381 | const symbol& base = incompatible[j].first; | |
382 | const symbol& test = incompatible[j].second; | |
383 | test.print(); | |
384 | ||
385 | // Second, report reason or reasons incompatible. | |
386 | check_compatible(base, test, true); | |
387 | } | |
388 | } | |
389 | ||
390 | cout << "\n\t\t=== libstdc++-v3 check-abi Summary ===" << endl; | |
391 | cout << endl; | |
392 | cout << "# of added symbols:\t\t " << added_names.size() << endl; | |
393 | cout << "# of missing symbols:\t\t " << missing_names.size() << endl; | |
394 | cout << "# of incompatible symbols:\t " << incompatible.size() << endl; | |
395 | cout << endl; | |
396 | cout << "using: " << baseline_file << endl; | |
fdbba6bc MM |
397 | |
398 | return !(missing_names.size() || incompatible.size()); | |
4b260c20 BK |
399 | } |
400 | ||
401 | ||
402 | symbols | |
403 | create_symbols(const char* file) | |
404 | { | |
405 | symbols s; | |
406 | ifstream ifs(file); | |
407 | if (ifs.is_open()) | |
408 | { | |
409 | // Organize file data into container of symbol objects. | |
410 | symbol_names& names = s.first; | |
411 | symbol_objects& objects = s.second; | |
412 | const string empty; | |
413 | string line = empty; | |
414 | while (getline(ifs, line).good()) | |
415 | { | |
416 | symbol tmp; | |
417 | tmp.init(line); | |
418 | objects[tmp.name] = tmp; | |
419 | names.push_back(tmp.name); | |
420 | line = empty; | |
421 | } | |
422 | } | |
423 | else | |
424 | { | |
425 | ostringstream os; | |
426 | os << "create_symbols failed for file " << file; | |
56ffd9b3 | 427 | __throw_runtime_error(os.str().c_str()); |
4b260c20 BK |
428 | } |
429 | return s; | |
430 | } | |
431 | ||
432 | ||
433 | const char* | |
434 | demangle(const std::string& mangled) | |
435 | { | |
436 | const char* name; | |
437 | if (mangled[0] != '_' || mangled[1] != 'Z') | |
438 | { | |
439 | // This is not a mangled symbol, thus has "C" linkage. | |
440 | name = mangled.c_str(); | |
441 | } | |
442 | else | |
443 | { | |
444 | // Use __cxa_demangle to demangle. | |
445 | int status = 0; | |
446 | name = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status); | |
447 | if (!name) | |
448 | { | |
449 | switch (status) | |
450 | { | |
451 | case 0: | |
452 | name = "error code = 0: success"; | |
453 | break; | |
454 | case -1: | |
455 | name = "error code = -1: memory allocation failure"; | |
456 | break; | |
457 | case -2: | |
458 | name = "error code = -2: invalid mangled name"; | |
459 | break; | |
460 | case -3: | |
461 | name = "error code = -3: invalid arguments"; | |
462 | break; | |
463 | default: | |
464 | name = "error code unknown - who knows what happened"; | |
465 | } | |
466 | } | |
467 | } | |
468 | return name; | |
469 | } | |
470 |