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
)
84 typedef std::vector
<std::string
> compat_list
;
85 static compat_list known
;
88 known
.push_back("GLIBCPP_3.2");
89 known
.push_back("GLIBCPP_3.2.1");
90 known
.push_back("GLIBCPP_3.2.2");
91 known
.push_back("GLIBCPP_3.4");
92 known
.push_back("CXXABI_1.2");
93 known
.push_back("CXXABI_1.2.1");
94 known
.push_back("CXXABI_1.3");
97 compat_list::iterator end
= known
.end();
99 // Check version names for compatibility...
100 compat_list::iterator it1
= find(known
.begin(), end
, test
.version_name
);
102 // Check for weak label.
103 compat_list::iterator it2
= find(known
.begin(), end
, test
.name
);
104 if (it1
!= end
|| it2
!= end
)
111 check_compatible(const symbol_info
& lhs
, const symbol_info
& rhs
,
112 bool verbose
= false)
116 const char tab
= '\t';
118 // Check to see if symbol_infos are compatible.
119 if (lhs
.type
!= rhs
.type
)
124 cout
<< tab
<< "incompatible types" << endl
;
128 if (lhs
.name
!= rhs
.name
)
133 cout
<< tab
<< "incompatible names" << endl
;
137 if (lhs
.size
!= rhs
.size
)
142 cout
<< tab
<< "incompatible sizes" << endl
;
143 cout
<< tab
<< lhs
.size
<< endl
;
144 cout
<< tab
<< rhs
.size
<< endl
;
148 if (lhs
.version_name
!= rhs
.version_name
149 && !check_version(lhs
) && !check_version(rhs
))
154 cout
<< tab
<< "incompatible versions" << endl
;
155 cout
<< tab
<< lhs
.version_name
<< endl
;
156 cout
<< tab
<< rhs
.version_name
<< endl
;
167 demangle(const std::string
& mangled
)
170 if (mangled
[0] != '_' || mangled
[1] != 'Z')
172 // This is not a mangled symbol, thus has "C" linkage.
173 name
= mangled
.c_str();
177 // Use __cxa_demangle to demangle.
179 name
= abi::__cxa_demangle(mangled
.c_str(), 0, 0, &status
);
185 name
= "error code = 0: success";
188 name
= "error code = -1: memory allocation failure";
191 name
= "error code = -2: invalid mangled name";
194 name
= "error code = -3: invalid arguments";
197 name
= "error code unknown - who knows what happened";
205 line_to_symbol_info(std::string
& input
, symbol_info
& output
)
208 const char delim
= ':';
209 const char version_delim
= '@';
210 const string::size_type npos
= string::npos
;
211 string::size_type n
= 0;
214 if (input
.find("FUNC") == 0)
215 output
.type
= symbol_info::function
;
216 else if (input
.find("OBJECT") == 0)
217 output
.type
= symbol_info::object
;
219 output
.type
= symbol_info::error
;
220 n
= input
.find_first_of(delim
);
222 input
.erase(input
.begin(), input
.begin() + n
+ 1);
224 // Iff object, get size info.
225 if (output
.type
== symbol_info::object
)
227 n
= input
.find_first_of(delim
);
230 string
size(input
.begin(), input
.begin() + n
);
231 istringstream
iss(size
);
236 input
.erase(input
.begin(), input
.begin() + n
+ 1);
241 n
= input
.find_first_of(version_delim
);
244 // Found version string.
245 output
.name
= string(input
.begin(), input
.begin() + n
);
246 n
= input
.find_last_of(version_delim
);
247 input
.erase(input
.begin(), input
.begin() + n
+ 1);
250 output
.version_name
= input
;
254 // No versioning info.
255 output
.name
= string(input
.begin(), input
.end());
256 input
.erase(input
.begin(), input
.end());
259 // Set the demangled name.
260 output
.demangled_name
= demangle(output
.name
);
264 create_symbol_data(const char* file
, symbol_infos
& symbols
,
267 // Parse list of symbols in file into vectors of symbol_info.
268 // For 3.2.0 on x86/linux, this usually is
269 // 947 non-weak symbols
275 // Organize input into container of symbol_info objects.
278 while (getline(ifs
, line
).good())
281 line_to_symbol_info(line
, symbol
);
282 symbols
[symbol
.name
] = symbol
;
283 names
.push_back(symbol
.name
);
290 report_symbol_info(const symbol_info
& symbol
, std::size_t n
, bool ret
= true)
293 const char tab
= '\t';
294 cout
<< tab
<< n
<< endl
;
295 cout
<< tab
<< "symbol"<< endl
;
296 cout
<< tab
<< symbol
.name
<< endl
;
298 // Add any other information to display here.
299 cout
<< tab
<< "demangled symbol"<< endl
;
300 cout
<< tab
<< symbol
.demangled_name
<< endl
;
308 main(int argc
, char** argv
)
312 // Get arguments. (Heading towards getopt_long, I can feel it.)
314 if (argc
< 4 || (string("--help") == (argv1
= argv
[1])))
316 cerr
<< "Usage: abi_check --check cur baseline\n"
318 "Where CUR is a file containing the current results from\n"
319 "extract_symvers, and BASELINE is one from config/abi.\n"
325 // Quick sanity/setup check for arguments.
326 const char* test_file
= argv
[2];
327 const char* baseline_file
= argv
[3];
328 if (access(test_file
, R_OK
) != 0)
330 cerr
<< "Cannot read symbols file " << test_file
331 << ", did you forget to build first?" << endl
;
334 if (access(baseline_file
, R_OK
) != 0)
336 cerr
<< "Cannot read baseline file " << baseline_file
<< endl
;
340 // Input both lists of symbols into container.
341 symbol_infos baseline_symbols
;
342 symbol_names baseline_names
;
343 symbol_infos test_symbols
;
344 symbol_names test_names
;
345 create_symbol_data(baseline_file
, baseline_symbols
, baseline_names
);
346 create_symbol_data(test_file
, test_symbols
, test_names
);
348 // Sanity check results.
349 const symbol_names::size_type baseline_size
= baseline_names
.size();
350 const symbol_names::size_type test_size
= test_names
.size();
351 if (!baseline_size
|| !test_size
)
353 cerr
<< "Problems parsing the list of exported symbols." << endl
;
358 // Assuming baseline_names, test_names are both unique w/ no duplicates.
360 // The names added to missing_names are baseline_names not found in
362 // -> symbols that have been deleted.
364 // The names added to added_names are test_names are names not in
366 // -> symbols that have been added.
367 symbol_names shared_names
;
368 symbol_names missing_names
;
369 symbol_names added_names
= test_names
;
370 for (size_t i
= 0; i
< baseline_size
; ++i
)
372 string
what(baseline_names
[i
]);
373 symbol_names::iterator end
= added_names
.end();
374 symbol_names::iterator it
= find(added_names
.begin(), end
, what
);
378 shared_names
.push_back(what
);
379 added_names
.erase(it
);
382 missing_names
.push_back(what
);
385 // Check shared names for compatibility.
386 typedef pair
<symbol_info
, symbol_info
> symbol_pair
;
387 vector
<symbol_pair
> incompatible
;
388 for (size_t i
= 0; i
< shared_names
.size(); ++i
)
390 symbol_info base
= baseline_symbols
[shared_names
[i
]];
391 symbol_info test
= test_symbols
[shared_names
[i
]];
392 if (!check_compatible(base
, test
))
393 incompatible
.push_back(symbol_pair(base
, test
));
396 // Check added names for compatibility.
397 for (size_t i
= 0; i
< added_names
.size(); ++i
)
399 symbol_info test
= test_symbols
[added_names
[i
]];
400 if (!check_version(test
))
402 incompatible
.push_back(symbol_pair(test
, test
));
403 cout
<< test
.version_name
<< endl
;
408 cout
<< added_names
.size() << " added symbols " << endl
;
409 for (size_t j
= 0; j
< added_names
.size() ; ++j
)
410 report_symbol_info(test_symbols
[added_names
[j
]], j
+ 1);
412 cout
<< missing_names
.size() << " missing symbols " << endl
;
413 for (size_t j
= 0; j
< missing_names
.size() ; ++j
)
414 report_symbol_info(baseline_symbols
[missing_names
[j
]], j
+ 1);
416 cout
<< incompatible
.size() << " incompatible symbols " << endl
;
417 for (size_t j
= 0; j
< incompatible
.size() ; ++j
)
419 // First, report name.
420 const symbol_info
& base
= incompatible
[j
].first
;
421 const symbol_info
& test
= incompatible
[j
].second
;
422 report_symbol_info(test
, j
+ 1, false);
424 // Second, report reason or reasons incompatible.
425 check_compatible(base
, test
, true);