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