1 // Utility for libstdc++ ABI analysis -*- C++ -*-
3 // Copyright (C) 2002, 2003 Free Software Foundation, Inc.
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 2, or (at your option)
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with this library; see the file COPYING. If not, write to the Free
18 // Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21 // As a special exception, you may use this file as part of a free software
22 // library without restriction. Specifically, if other files instantiate
23 // templates or use macros or inline functions from this file, or you compile
24 // this file and link it with other files to produce an executable, this
25 // file does not by itself cause the resulting executable to be covered by
26 // the GNU General Public License. This exception does not however
27 // invalidate any other reasons why the executable file might be covered by
28 // the GNU General Public License.
30 // Benjamin Kosnik <bkoz@redhat.com>
31 // Blame subsequent hacks on Loren J. Rittle <ljrittle@acm.org>, Phil
32 // Edwards <pme@gcc.gnu.org>, and a cast of dozens at libstdc++@gcc.gnu.org.
35 #include <ext/hash_map>
41 #include <stdlib.h> // for system(3)
42 #include <unistd.h> // for access(2)
46 enum category
{ none
, function
, object
, error
};
49 std::string demangled_name
;
51 std::string version_name
;
53 symbol_info() : type(none
), size(0) { }
55 symbol_info(const symbol_info
& other
)
56 : type(other
.type
), name(other
.name
), demangled_name(other
.demangled_name
),
57 size(other
.size
), version_name(other
.version_name
) { }
67 size_t operator()(const string
& s
) const
69 const collate
<char>& c
= use_facet
<collate
<char> >(locale::classic());
70 return c
.hash(s
.c_str(), s
.c_str() + s
.size());
75 typedef std::deque
<std::string
> symbol_names
;
76 typedef __gnu_cxx::hash_map
<std::string
, symbol_info
> symbol_infos
;
80 check_version(const symbol_info
& test
, bool added
= false)
82 typedef std::vector
<std::string
> compat_list
;
83 static compat_list known_versions
;
84 if (known_versions
.empty())
86 known_versions
.push_back("GLIBCPP_3.2"); // base version
87 known_versions
.push_back("GLIBCPP_3.2.1");
88 known_versions
.push_back("GLIBCPP_3.2.2");
89 known_versions
.push_back("GLIBCPP_3.2.3"); // gcc-3.3.0
90 known_versions
.push_back("GLIBCPP_3.4");
91 known_versions
.push_back("CXXABI_1.2");
92 known_versions
.push_back("CXXABI_1.2.1");
93 known_versions
.push_back("CXXABI_1.3");
95 compat_list::iterator begin
= known_versions
.begin();
96 compat_list::iterator end
= known_versions
.end();
98 // Check version names for compatibility...
99 compat_list::iterator it1
= find(begin
, end
, test
.version_name
);
101 // Check for weak label.
102 compat_list::iterator it2
= find(begin
, end
, test
.name
);
104 // Check that added symbols aren't added in the base version.
106 if (added
&& test
.version_name
== known_versions
[0])
109 if (it1
== end
&& it2
== end
)
116 check_compatible(const symbol_info
& lhs
, const symbol_info
& rhs
,
117 bool verbose
= false)
121 const char tab
= '\t';
123 // Check to see if symbol_infos are compatible.
124 if (lhs
.type
!= rhs
.type
)
129 cout
<< tab
<< "incompatible types" << endl
;
133 if (lhs
.name
!= rhs
.name
)
138 cout
<< tab
<< "incompatible names" << endl
;
142 if (lhs
.size
!= rhs
.size
)
147 cout
<< tab
<< "incompatible sizes" << endl
;
148 cout
<< tab
<< lhs
.size
<< endl
;
149 cout
<< tab
<< rhs
.size
<< endl
;
153 if (lhs
.version_name
!= rhs
.version_name
154 && !check_version(lhs
) && !check_version(rhs
))
159 cout
<< tab
<< "incompatible versions" << endl
;
160 cout
<< tab
<< lhs
.version_name
<< endl
;
161 cout
<< tab
<< rhs
.version_name
<< endl
;
172 demangle(const std::string
& mangled
)
175 if (mangled
[0] != '_' || mangled
[1] != 'Z')
177 // This is not a mangled symbol, thus has "C" linkage.
178 name
= mangled
.c_str();
182 // Use __cxa_demangle to demangle.
184 name
= abi::__cxa_demangle(mangled
.c_str(), 0, 0, &status
);
190 name
= "error code = 0: success";
193 name
= "error code = -1: memory allocation failure";
196 name
= "error code = -2: invalid mangled name";
199 name
= "error code = -3: invalid arguments";
202 name
= "error code unknown - who knows what happened";
210 line_to_symbol_info(std::string
& input
, symbol_info
& output
)
213 const char delim
= ':';
214 const char version_delim
= '@';
215 const string::size_type npos
= string::npos
;
216 string::size_type n
= 0;
219 if (input
.find("FUNC") == 0)
220 output
.type
= symbol_info::function
;
221 else if (input
.find("OBJECT") == 0)
222 output
.type
= symbol_info::object
;
224 output
.type
= symbol_info::error
;
225 n
= input
.find_first_of(delim
);
227 input
.erase(input
.begin(), input
.begin() + n
+ 1);
229 // Iff object, get size info.
230 if (output
.type
== symbol_info::object
)
232 n
= input
.find_first_of(delim
);
235 string
size(input
.begin(), input
.begin() + n
);
236 istringstream
iss(size
);
241 input
.erase(input
.begin(), input
.begin() + n
+ 1);
246 n
= input
.find_first_of(version_delim
);
249 // Found version string.
250 output
.name
= string(input
.begin(), input
.begin() + n
);
251 n
= input
.find_last_of(version_delim
);
252 input
.erase(input
.begin(), input
.begin() + n
+ 1);
255 output
.version_name
= input
;
259 // No versioning info.
260 output
.name
= string(input
.begin(), input
.end());
261 input
.erase(input
.begin(), input
.end());
264 // Set the demangled name.
265 output
.demangled_name
= demangle(output
.name
);
269 create_symbol_data(const char* file
, symbol_infos
& symbols
,
272 // Parse list of symbols in file into vectors of symbol_info.
273 // For 3.2.0 on x86/linux, this usually is
274 // 947 non-weak symbols
280 // Organize input into container of symbol_info objects.
283 while (getline(ifs
, line
).good())
286 line_to_symbol_info(line
, symbol
);
287 symbols
[symbol
.name
] = symbol
;
288 names
.push_back(symbol
.name
);
295 report_symbol_info(const symbol_info
& symbol
, std::size_t n
, bool ret
= true)
298 const char tab
= '\t';
300 // Add any other information to display here.
301 cout
<< tab
<< symbol
.demangled_name
<< endl
;
302 cout
<< tab
<< symbol
.name
<< endl
;
303 cout
<< tab
<< symbol
.version_name
<< endl
;
311 main(int argc
, char** argv
)
315 // Get arguments. (Heading towards getopt_long, I can feel it.)
317 if (argc
< 4 || (string("--help") == (argv1
= argv
[1])))
319 cerr
<< "Usage: abi_check --check cur baseline\n"
321 "Where CUR is a file containing the current results from\n"
322 "extract_symvers, and BASELINE is one from config/abi.\n"
328 // Quick sanity/setup check for arguments.
329 const char* test_file
= argv
[2];
330 const char* baseline_file
= argv
[3];
331 if (access(test_file
, R_OK
) != 0)
333 cerr
<< "Cannot read symbols file " << test_file
334 << ", did you forget to build first?" << endl
;
337 if (access(baseline_file
, R_OK
) != 0)
339 cerr
<< "Cannot read baseline file " << baseline_file
<< endl
;
343 // Input both lists of symbols into container.
344 symbol_infos baseline_symbols
;
345 symbol_names baseline_names
;
346 symbol_infos test_symbols
;
347 symbol_names test_names
;
348 create_symbol_data(baseline_file
, baseline_symbols
, baseline_names
);
349 create_symbol_data(test_file
, test_symbols
, test_names
);
351 // Sanity check results.
352 const symbol_names::size_type baseline_size
= baseline_names
.size();
353 const symbol_names::size_type test_size
= test_names
.size();
354 if (!baseline_size
|| !test_size
)
356 cerr
<< "Problems parsing the list of exported symbols." << endl
;
361 // Assuming baseline_names, test_names are both unique w/ no duplicates.
363 // The names added to missing_names are baseline_names not found in
365 // -> symbols that have been deleted.
367 // The names added to added_names are test_names are names not in
369 // -> symbols that have been added.
370 symbol_names shared_names
;
371 symbol_names missing_names
;
372 symbol_names added_names
= test_names
;
373 for (size_t i
= 0; i
< baseline_size
; ++i
)
375 string
what(baseline_names
[i
]);
376 symbol_names::iterator end
= added_names
.end();
377 symbol_names::iterator it
= find(added_names
.begin(), end
, what
);
381 shared_names
.push_back(what
);
382 added_names
.erase(it
);
385 missing_names
.push_back(what
);
388 // Check missing names for compatibility.
389 typedef pair
<symbol_info
, symbol_info
> symbol_pair
;
390 vector
<symbol_pair
> incompatible
;
391 for (size_t i
= 0; i
< missing_names
.size(); ++i
)
393 symbol_info base
= baseline_symbols
[missing_names
[i
]];
394 incompatible
.push_back(symbol_pair(base
, base
));
397 // Check shared names for compatibility.
398 for (size_t i
= 0; i
< shared_names
.size(); ++i
)
400 symbol_info base
= baseline_symbols
[shared_names
[i
]];
401 symbol_info test
= test_symbols
[shared_names
[i
]];
402 if (!check_compatible(base
, test
))
403 incompatible
.push_back(symbol_pair(base
, test
));
406 // Check added names for compatibility.
407 for (size_t i
= 0; i
< added_names
.size(); ++i
)
409 symbol_info test
= test_symbols
[added_names
[i
]];
410 if (!check_version(test
, true))
411 incompatible
.push_back(symbol_pair(test
, test
));
415 cout
<< added_names
.size() << " added symbols " << endl
;
416 for (size_t j
= 0; j
< added_names
.size() ; ++j
)
417 report_symbol_info(test_symbols
[added_names
[j
]], j
+ 1);
419 cout
<< missing_names
.size() << " missing symbols " << endl
;
420 for (size_t j
= 0; j
< missing_names
.size() ; ++j
)
421 report_symbol_info(baseline_symbols
[missing_names
[j
]], j
+ 1);
423 cout
<< incompatible
.size() << " incompatible symbols " << endl
;
424 for (size_t j
= 0; j
< incompatible
.size() ; ++j
)
426 // First, report name.
427 const symbol_info
& base
= incompatible
[j
].first
;
428 const symbol_info
& test
= incompatible
[j
].second
;
429 report_symbol_info(test
, j
+ 1, false);
431 // Second, report reason or reasons incompatible.
432 check_compatible(base
, test
, true);