1 // Utility for libstdc++ ABI analysis -*- C++ -*-
3 // Copyright (C) 2002 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
;
79 check_compatible(const symbol_info
& lhs
, const symbol_info
& rhs
,
84 const char tab
= '\t';
86 // Check to see if symbol_infos are compatible.
87 if (lhs
.type
!= rhs
.type
)
92 cout
<< tab
<< "incompatible types" << endl
;
96 if (lhs
.name
!= rhs
.name
)
101 cout
<< tab
<< "incompatible names" << endl
;
105 if (lhs
.size
!= rhs
.size
)
110 cout
<< tab
<< "incompatible sizes" << endl
;
111 cout
<< tab
<< lhs
.size
<< endl
;
112 cout
<< tab
<< rhs
.size
<< endl
;
116 if (lhs
.version_name
!= rhs
.version_name
)
121 cout
<< tab
<< "incompatible versions" << endl
;
122 cout
<< tab
<< lhs
.version_name
<< endl
;
123 cout
<< tab
<< rhs
.version_name
<< endl
;
134 demangle(const std::string
& mangled
)
137 if (mangled
[0] != '_' || mangled
[1] != 'Z')
139 // This is not a mangled symbol, thus has "C" linkage.
140 name
= mangled
.c_str();
144 // Use __cxa_demangle to demangle.
146 name
= abi::__cxa_demangle(mangled
.c_str(), 0, 0, &status
);
152 name
= "error code = 0: success";
155 name
= "error code = -1: memory allocation failure";
158 name
= "error code = -2: invalid mangled name";
161 name
= "error code = -3: invalid arguments";
164 name
= "error code unknown - who knows what happened";
172 line_to_symbol_info(std::string
& input
, symbol_info
& output
)
175 const char delim
= ':';
176 const char version_delim
= '@';
177 const string::size_type npos
= string::npos
;
178 string::size_type n
= 0;
181 if (input
.find("FUNC") == 0)
182 output
.type
= symbol_info::function
;
183 else if (input
.find("OBJECT") == 0)
184 output
.type
= symbol_info::object
;
186 output
.type
= symbol_info::error
;
187 n
= input
.find_first_of(delim
);
189 input
.erase(input
.begin(), input
.begin() + n
+ 1);
191 // Iff object, get size info.
192 if (output
.type
== symbol_info::object
)
194 n
= input
.find_first_of(delim
);
197 string
size(input
.begin(), input
.begin() + n
);
198 istringstream
iss(size
);
203 input
.erase(input
.begin(), input
.begin() + n
+ 1);
208 n
= input
.find_first_of(version_delim
);
211 // Found version string.
212 output
.name
= string(input
.begin(), input
.begin() + n
);
213 n
= input
.find_last_of(version_delim
);
214 input
.erase(input
.begin(), input
.begin() + n
+ 1);
217 output
.version_name
= input
;
221 // No versioning info.
222 output
.name
= string(input
.begin(), input
.end());
223 input
.erase(input
.begin(), input
.end());
226 // Set the demangled name.
227 output
.demangled_name
= demangle(output
.name
);
231 create_symbol_data(const char* file
, symbol_infos
& symbols
,
234 // Parse list of symbols in file into vectors of symbol_info.
235 // For 3.2.0 on x86/linux, this usually is
236 // 947 non-weak symbols
242 // Organize input into container of symbol_info objects.
245 while (getline(ifs
, line
).good())
248 line_to_symbol_info(line
, symbol
);
249 symbols
[symbol
.name
] = symbol
;
250 names
.push_back(symbol
.name
);
257 report_symbol_info(const symbol_info
& symbol
, std::size_t n
, bool ret
= true)
260 const char tab
= '\t';
261 cout
<< tab
<< n
<< endl
;
262 cout
<< tab
<< "symbol"<< endl
;
263 cout
<< tab
<< symbol
.name
<< endl
;
265 // Add any other information to display here.
266 cout
<< tab
<< "demangled symbol"<< endl
;
267 cout
<< tab
<< symbol
.demangled_name
<< endl
;
275 main(int argc
, char** argv
)
279 // Get arguments. (Heading towards getopt_long, I can feel it.)
281 if (argc
< 4 || (string("--help") == (argv1
= argv
[1])))
283 cerr
<< "Usage: abi_check --check cur baseline\n"
285 "Where CUR is a file containing the current results from\n"
286 "extract_symvers, and BASELINE is one from config/abi.\n"
292 // Quick sanity/setup check for arguments.
293 const char* test_file
= argv
[2];
294 const char* baseline_file
= argv
[3];
295 if (access(test_file
, R_OK
) != 0)
297 cerr
<< "Cannot read symbols file " << test_file
298 << ", did you forget to build first?" << endl
;
301 if (access(baseline_file
, R_OK
) != 0)
303 cerr
<< "Cannot read baseline file " << baseline_file
<< endl
;
307 // Input both lists of symbols into container.
308 symbol_infos baseline_symbols
;
309 symbol_names baseline_names
;
310 symbol_infos test_symbols
;
311 symbol_names test_names
;
312 create_symbol_data(baseline_file
, baseline_symbols
, baseline_names
);
313 create_symbol_data(test_file
, test_symbols
, test_names
);
315 // Sanity check results.
316 const symbol_names::size_type baseline_size
= baseline_names
.size();
317 const symbol_names::size_type test_size
= test_names
.size();
318 if (!baseline_size
|| !test_size
)
320 cerr
<< "Problems parsing the list of exported symbols." << endl
;
325 // Assuming baseline_names, test_names are both unique w/ no duplicates.
327 // The names added to missing_names are baseline_names not found in
329 // -> symbols that have been deleted.
331 // The names added to added_names are test_names are names not in
333 // -> symbols that have been added.
334 symbol_names shared_names
;
335 symbol_names missing_names
;
336 symbol_names added_names
= test_names
;
337 for (size_t i
= 0; i
< baseline_size
; ++i
)
339 string
what(baseline_names
[i
]);
340 symbol_names::iterator end
= added_names
.end();
341 symbol_names::iterator it
= find(added_names
.begin(), end
, what
);
345 shared_names
.push_back(what
);
346 added_names
.erase(it
);
349 missing_names
.push_back(what
);
352 // Check shared names for compatibility.
353 typedef pair
<symbol_info
, symbol_info
> symbol_pair
;
354 vector
<symbol_pair
> incompatible
;
355 for (size_t i
= 0; i
< shared_names
.size(); ++i
)
357 symbol_info base
= baseline_symbols
[shared_names
[i
]];
358 symbol_info test
= test_symbols
[shared_names
[i
]];
359 if (!check_compatible(base
, test
))
360 incompatible
.push_back(symbol_pair(base
, test
));
363 // Check added names for compatibility.
364 for (size_t i
= 0; i
< added_names
.size(); ++i
)
366 vector
<string
> compatible_versions
;
367 compatible_versions
.push_back("GLIBCPP_3.2.1");
368 compatible_versions
.push_back("GLIBCPP_3.2.2");
369 compatible_versions
.push_back("CXXABI_1.2.1");
371 symbol_info test
= test_symbols
[added_names
[i
]];
372 vector
<string
>::iterator end
= compatible_versions
.end();
374 // Check version names for compatibility...
375 vector
<string
>::iterator it1
= find(compatible_versions
.begin(), end
,
378 // Check for weak label.
379 vector
<string
>::iterator it2
= find(compatible_versions
.begin(), end
,
382 if (it1
== end
&& it2
== end
)
384 incompatible
.push_back(symbol_pair(test
, test
));
385 cout
<< test
.version_name
<< endl
;
390 cout
<< added_names
.size() << " added symbols " << endl
;
391 for (size_t j
= 0; j
< added_names
.size() ; ++j
)
392 report_symbol_info(test_symbols
[added_names
[j
]], j
+ 1);
394 cout
<< missing_names
.size() << " missing symbols " << endl
;
395 for (size_t j
= 0; j
< missing_names
.size() ; ++j
)
396 report_symbol_info(baseline_symbols
[missing_names
[j
]], j
+ 1);
398 cout
<< incompatible
.size() << " incompatible symbols " << endl
;
399 for (size_t j
= 0; j
< incompatible
.size() ; ++j
)
401 // First, report name.
402 const symbol_info
& base
= incompatible
[j
].first
;
403 const symbol_info
& test
= incompatible
[j
].second
;
404 report_symbol_info(test
, j
+ 1, false);
406 // Second, report reason or reasons incompatible.
407 check_compatible(base
, test
, true);