1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2014-2020 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7
8 /// @file
9 ///
10 /// This program reads a program A, one library L in version V which A
11 /// links against, and the same library L in a different version, V+P.
12 /// The program then checks that A is still ABI compatible with L in
13 /// version V+P.
14 ///
15 /// The program also comes with a "weak mode" in which just the
16 /// application and the library in version V+P need to be provided by
17 /// the user. In that case, the types of functions and variables of
18 /// the library that are consumed by the application are compared to
19 /// the types of the functions and variables expected by the
20 /// application. If they match exactly, then the types of functions
21 /// and variables that the application expects from the library are
22 /// honoured by the library. Otherwise, the library might provide
23 /// functions and variables that mean something different from what
24 /// the application expects and that might signal an ABI
25 /// incompatibility between what the application expects and what the
26 /// library provides.
27
28 #include <unistd.h>
29 #include <cassert>
30 #include <cstdio>
31 #include <cstdlib>
32 #include <cstring>
33 #include <fstream>
34 #include <iostream>
35 #include <memory>
36 #include <string>
37 #include "abg-config.h"
38 #include "abg-tools-utils.h"
39 #include "abg-corpus.h"
40 #include "abg-reader.h"
41 #include "abg-dwarf-reader.h"
42 #include "abg-comparison.h"
43 #include "abg-suppression.h"
44 #ifdef WITH_CTF
45 #include "abg-ctf-reader.h"
46 #endif
47
48 using std::string;
49 using std::cerr;
50 using std::cout;
51 using std::ostream;
52 using std::ofstream;
53 using std::vector;
54 using std::shared_ptr;
55
56 using abigail::tools_utils::emit_prefix;
57
58 class options
59 {
60 options();
61
62 public:
63 string prog_name;
64 string unknow_option;
65 string app_path;
66 string lib1_path;
67 string lib2_path;
68 shared_ptr<char> app_di_root_path;
69 shared_ptr<char> lib1_di_root_path;
70 shared_ptr<char> lib2_di_root_path;
71 vector<string> suppression_paths;
72 bool display_help;
73 bool display_version;
74 bool weak_mode;
75 bool list_undefined_symbols_only;
76 bool show_base_names;
77 bool show_redundant;
78 bool redundant_opt_set;
79 bool no_redundant_opt_set;
80 bool show_locs;
81 bool fail_no_debug_info;
82 bool ignore_soname;
83 #ifdef WITH_CTF
84 bool use_ctf;
85 #endif
86
options(const char * program_name)87 options(const char* program_name)
88 :prog_name(program_name),
89 display_help(),
90 display_version(),
91 weak_mode(),
92 list_undefined_symbols_only(),
93 show_base_names(),
94 show_redundant(true),
95 redundant_opt_set(),
96 no_redundant_opt_set(),
97 show_locs(true),
98 fail_no_debug_info(),
99 ignore_soname(false)
100 #ifdef WITH_CTF
101 ,
102 use_ctf()
103 #endif
104 {}
105 }; // end struct options
106
107 static void
display_usage(const string & prog_name,ostream & out)108 display_usage(const string& prog_name, ostream& out)
109 {
110 emit_prefix(prog_name, out)
111 << "usage: " << prog_name
112 << " [options] [application-path] [lib-v1-path] [lib-v2-path]"
113 << "\n"
114 << " where options can be: \n"
115 << " --help|-h display this help message\n"
116 << " --version|-v show program version information and exit\n"
117 << " --list-undefined-symbols|-u display the list of "
118 "undefined symbols of the application\n"
119 << " --show-base-names|b in the report, only show the base names "
120 " of the files; not the full paths\n"
121 << " --app-debug-info-dir|--appd <path-to-app-debug-info> set the path "
122 "to the debug information directory for the application\n"
123 << " --lib-debug-info-dir1|--libd1 <path-to-lib-debug-info1> set the path "
124 "to the debug information directory for the first library\n"
125 << " --lib-debug-info-dir2|--libd2 <path-to-lib-debug-info2> set the path "
126 "to the debug information directory for the second library\n"
127 << " --suppressions|--suppr <path> specify a suppression file\n"
128 << " --no-redundant do not display redundant changes\n"
129 << " --no-show-locs do now show location information\n"
130 << " --ignore-soname do not take the SONAMEs into account\n"
131 << " --fail-no-debug-info bail out if no debug info was found\n"
132 << " --redundant display redundant changes (this is the default)\n"
133 << " --weak-mode check compatibility between the application and "
134 "just one version of the library.\n"
135 #ifdef WITH_CTF
136 << " --ctf use CTF instead of DWARF in ELF files\n"
137 #endif
138 ;
139 }
140
141 static bool
parse_command_line(int argc,char * argv[],options & opts)142 parse_command_line(int argc, char* argv[], options& opts)
143 {
144 if (argc < 2)
145 return false;
146
147 for (int i = 1; i < argc; ++i)
148 {
149 if (argv[i][0] != '-')
150 {
151 if (opts.app_path.empty())
152 opts.app_path = argv[i];
153 else if (opts.lib1_path.empty())
154 opts.lib1_path = argv[i];
155 else if (opts.lib2_path.empty())
156 opts.lib2_path = argv[i];
157 else
158 return false;
159 }
160 else if (!strcmp(argv[i], "--version")
161 || !strcmp(argv[i], "-v"))
162 {
163 opts.display_version = true;
164 return true;
165 }
166 else if (!strcmp(argv[i], "--list-undefined-symbols")
167 || !strcmp(argv[i], "-u"))
168 opts.list_undefined_symbols_only = true;
169 else if (!strcmp(argv[i], "--show-base-names")
170 || !strcmp(argv[i], "-b"))
171 opts.show_base_names = true;
172 else if (!strcmp(argv[i], "--app-debug-info-dir")
173 || !strcmp(argv[i], "--appd"))
174 {
175 if (argc <= i + 1
176 || argv[i + 1][0] == '-')
177 return false;
178 // elfutils wants the root path to the debug info to be
179 // absolute.
180 opts.app_di_root_path =
181 abigail::tools_utils::make_path_absolute(argv[i + 1]);
182 ++i;
183 }
184 else if (!strcmp(argv[i], "--lib-debug-info-dir1")
185 || !strcmp(argv[i], "--libd1"))
186 {
187 if (argc <= i + 1
188 || argv[i + 1][0] == '-')
189 return false;
190 // elfutils wants the root path to the debug info to be
191 // absolute.
192 opts.lib1_di_root_path =
193 abigail::tools_utils::make_path_absolute(argv[i + 1]);
194 ++i;
195 }
196 else if (!strcmp(argv[i], "--lib-debug-info-dir2")
197 || !strcmp(argv[i], "--libd2"))
198 {
199 if (argc <= i + 1
200 || argv[i + 1][0] == '-')
201 return false;
202 // elfutils wants the root path to the debug info to be
203 // absolute.
204 opts.lib2_di_root_path =
205 abigail::tools_utils::make_path_absolute(argv[i + 1]);
206 ++i;
207 }
208 else if (!strcmp(argv[i], "--suppressions")
209 || !strcmp(argv[i], "--suppr"))
210 {
211 int j = i + 1;
212 if (j >= argc)
213 return false;
214 opts.suppression_paths.push_back(argv[j]);
215 ++i;
216 }
217 else if (!strcmp(argv[i], "--redundant"))
218 {
219 opts.show_redundant = true;
220 opts.redundant_opt_set = true;
221 }
222 else if (!strcmp(argv[i], "--no-redundant"))
223 {
224 opts.show_redundant = false;
225 opts.no_redundant_opt_set = true;
226 }
227 else if (!strcmp(argv[i], "--no-show-locs"))
228 opts.show_locs = false;
229 else if (!strcmp(argv[i], "--ignore-soname"))
230 opts.ignore_soname=true;
231 else if (!strcmp(argv[i], "--fail-no-debug-info"))
232 opts.fail_no_debug_info = true;
233 else if (!strcmp(argv[i], "--help")
234 || !strcmp(argv[i], "-h"))
235 {
236 opts.display_help = true;
237 return true;
238 }
239 else if (!strcmp(argv[i], "--weak-mode"))
240 opts.weak_mode = true;
241 #ifdef WITH_CTF
242 else if (!strcmp(argv[i], "--ctf"))
243 opts.use_ctf = true;
244 #endif
245 else
246 {
247 opts.unknow_option = argv[i];
248 return false;
249 }
250 }
251
252 if (!opts.list_undefined_symbols_only)
253 {
254 if (opts.app_path.empty()
255 || opts.lib1_path.empty())
256 return false;
257 if (!opts.weak_mode && opts.lib2_path.empty())
258 opts.weak_mode = true;
259 }
260
261 return true;
262 }
263
264 using abigail::tools_utils::check_file;
265 using abigail::tools_utils::base_name;
266 using abigail::tools_utils::abidiff_status;
267 using abigail::ir::environment;
268 using abigail::ir::environment_sptr;
269 using abigail::corpus;
270 using abigail::corpus_sptr;
271 using abigail::ir::elf_symbols;
272 using abigail::ir::demangle_cplus_mangled_name;
273 using abigail::ir::type_base_sptr;
274 using abigail::ir::function_type_sptr;
275 using abigail::ir::function_decl;
276 using abigail::ir::var_decl;
277 using abigail::elf_reader::status;
278 using abigail::elf_reader::STATUS_ALT_DEBUG_INFO_NOT_FOUND;
279 using abigail::elf_reader::STATUS_DEBUG_INFO_NOT_FOUND;
280 using abigail::dwarf_reader::read_corpus_from_elf;
281 using abigail::comparison::diff_context_sptr;
282 using abigail::comparison::diff_context;
283 using abigail::comparison::diff_sptr;
284 using abigail::comparison::corpus_diff;
285 using abigail::comparison::corpus_diff_sptr;
286 using abigail::comparison::function_type_diff_sptr;
287 using abigail::comparison::compute_diff;
288 using abigail::suppr::suppression_sptr;
289 using abigail::suppr::suppressions_type;
290 using abigail::suppr::read_suppressions;
291
292 /// Create the context of a diff.
293 ///
294 /// Create the diff context, initialize it and return a smart pointer
295 /// to it.
296 ///
297 /// @param opts the options of the program.
298 ///
299 /// @return a smart pointer to the newly created diff context.
300 static diff_context_sptr
create_diff_context(const options & opts)301 create_diff_context(const options& opts)
302 {
303 diff_context_sptr ctxt(new diff_context());
304 ctxt->show_added_fns(false);
305 ctxt->show_added_vars(false);
306 ctxt->show_added_symbols_unreferenced_by_debug_info(false);
307 ctxt->show_linkage_names(true);
308 ctxt->show_redundant_changes(opts.show_redundant);
309 ctxt->show_locs(opts.show_locs);
310 // Intentional logic flip of ignore_soname
311 ctxt->show_soname_change(!opts.ignore_soname);
312 ctxt->switch_categories_off
313 (abigail::comparison::ACCESS_CHANGE_CATEGORY
314 | abigail::comparison::COMPATIBLE_TYPE_CHANGE_CATEGORY
315 | abigail::comparison::HARMLESS_DECL_NAME_CHANGE_CATEGORY
316 | abigail::comparison::NON_VIRT_MEM_FUN_CHANGE_CATEGORY
317 | abigail::comparison::STATIC_DATA_MEMBER_CHANGE_CATEGORY
318 | abigail::comparison::HARMLESS_ENUM_CHANGE_CATEGORY
319 | abigail::comparison::HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY);
320
321 // Load suppression specifications, if there are any.
322 suppressions_type supprs;
323 for (vector<string>::const_iterator i = opts.suppression_paths.begin();
324 i != opts.suppression_paths.end();
325 ++i)
326 if (check_file(*i, cerr, opts.prog_name))
327 read_suppressions(*i, supprs);
328
329 if (!supprs.empty())
330 ctxt->add_suppressions(supprs);
331
332 return ctxt;
333 }
334
335 /// Perform a compatibility check of an application corpus linked
336 /// against a first version of library corpus, with a second version
337 /// of the same library.
338 ///
339 /// @param opts the options the tool got invoked with.
340 ///
341 /// @param ctxt the context of the diff to be performed.
342 ///
343 /// @param app_corpus the application corpus to consider.
344 ///
345 /// @param lib1_corpus the library corpus that got linked with the
346 /// application which corpus is @p app_corpus.
347 ///
348 /// @param lib2_corpus the second version of the library corpus @p
349 /// lib1_corpus. This function checks that the functions and
350 /// variables that @p app_corpus expects from lib1_corpus are still
351 /// present in @p lib2_corpus and that their types mean the same
352 /// thing.
353 ///
354 /// @return a status bitfield.
355 static abidiff_status
perform_compat_check_in_normal_mode(options & opts,diff_context_sptr & ctxt,corpus_sptr app_corpus,corpus_sptr lib1_corpus,corpus_sptr lib2_corpus)356 perform_compat_check_in_normal_mode(options& opts,
357 diff_context_sptr& ctxt,
358 corpus_sptr app_corpus,
359 corpus_sptr lib1_corpus,
360 corpus_sptr lib2_corpus)
361 {
362 ABG_ASSERT(lib1_corpus);
363 ABG_ASSERT(lib2_corpus);
364 ABG_ASSERT(app_corpus);
365
366 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
367
368 // compare lib1 and lib2 only by looking at the functions and
369 // variables which symbols are those undefined in the app.
370
371 for (elf_symbols::const_iterator i =
372 app_corpus->get_sorted_undefined_fun_symbols().begin();
373 i != app_corpus->get_sorted_undefined_fun_symbols().end();
374 ++i)
375 {
376 string id = (*i)->get_id_string();
377 lib1_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
378 lib2_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
379 }
380 for (elf_symbols::const_iterator i =
381 app_corpus->get_sorted_undefined_var_symbols().begin();
382 i != app_corpus->get_sorted_undefined_var_symbols().end();
383 ++i)
384 {
385 string id = (*i)->get_id_string();
386 lib1_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
387 lib2_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
388 }
389
390 if (!app_corpus->get_sorted_undefined_var_symbols().empty()
391 || !app_corpus->get_sorted_undefined_fun_symbols().empty())
392 {
393 lib1_corpus->maybe_drop_some_exported_decls();
394 lib2_corpus->maybe_drop_some_exported_decls();
395 }
396
397 // Now really do the diffing.
398 corpus_diff_sptr changes = compute_diff(lib1_corpus, lib2_corpus, ctxt);
399
400 if (changes->has_net_changes())
401 {
402 string app_path = opts.app_path,
403 lib1_path = opts.lib1_path,
404 lib2_path = opts.lib2_path;
405
406 if (opts.show_base_names)
407 {
408 base_name(opts.app_path, app_path);
409 base_name(opts.lib1_path, lib1_path);
410 base_name(opts.lib2_path, lib2_path);
411 }
412
413 status |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
414
415 bool abi_broke_for_sure = changes->has_incompatible_changes();
416
417 cout << "ELF file '" << app_path << "'";
418 if (abi_broke_for_sure)
419 {
420 cout << " is not ";
421 status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
422 }
423 else
424 cout << " might not be ";
425
426 cout << "ABI compatible with '" << lib2_path
427 << "' due to differences with '" << lib1_path
428 << "' below:\n";
429 changes->report(cout);
430 }
431
432 return status;
433 }
434
435 /// A description of a change of the type of a function. It contains
436 /// the declaration of the function we are interested in, as well as
437 /// the differences found in the type of that function.
438 struct fn_change
439 {
440 function_decl* decl;
441 function_type_diff_sptr diff;
442
fn_changefn_change443 fn_change()
444 : decl()
445 {}
446
fn_changefn_change447 fn_change(function_decl* decl,
448 function_type_diff_sptr difference)
449 : decl(decl),
450 diff(difference)
451 {}
452 }; // end struct fn_change
453
454 /// An description of a change of the type of a variable. It contains
455 /// the declaration of the variable we are interested in, as well as
456 /// the differences found in the type of that variable.
457 struct var_change
458 {
459 var_decl* decl;
460 diff_sptr diff;
461
var_changevar_change462 var_change()
463 : decl()
464 {}
465
var_changevar_change466 var_change(var_decl* var,
467 diff_sptr difference)
468 : decl(var),
469 diff(difference)
470 {}
471 }; // end struct var_change
472
473 /// Perform a compatibility check of an application corpus and a
474 /// library corpus.
475 ///
476 /// The types of the variables and functions exported by the library
477 /// and consumed by the application are compared with the types
478 /// expected by the application. This function checks that the types
479 /// mean the same thing; otherwise it emits on standard output type
480 /// layout differences found.
481 ///
482 /// @param opts the options the tool got invoked with.
483 ///
484 /// @param app_corpus the application corpus to consider.
485 ///
486 /// @param lib_corpus the library corpus to consider.
487 ///
488 /// @return a status bitfield.
489 static abidiff_status
perform_compat_check_in_weak_mode(options & opts,diff_context_sptr & ctxt,corpus_sptr app_corpus,corpus_sptr lib_corpus)490 perform_compat_check_in_weak_mode(options& opts,
491 diff_context_sptr& ctxt,
492 corpus_sptr app_corpus,
493 corpus_sptr lib_corpus)
494 {
495 ABG_ASSERT(lib_corpus);
496 ABG_ASSERT(app_corpus);
497
498 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
499
500 // Functions and variables defined and exported by lib_corpus which
501 // symbols are undefined in app_corpus are the artifacts we are
502 // interested in.
503 //
504 // So let's drop all functions and variables from lib_corpus that
505 // are so that their symbols are *NOT* undefined in app_corpus.
506 //
507 // In other words, let's only keep the functiond and variables from
508 // lib_corpus that are consumed by app_corpus.
509
510 for (elf_symbols::const_iterator i =
511 app_corpus->get_sorted_undefined_fun_symbols().begin();
512 i != app_corpus->get_sorted_undefined_fun_symbols().end();
513 ++i)
514 {
515 string id = (*i)->get_id_string();
516 lib_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
517 }
518
519 for (elf_symbols::const_iterator i =
520 app_corpus->get_sorted_undefined_var_symbols().begin();
521 i != app_corpus->get_sorted_undefined_var_symbols().end();
522 ++i)
523 {
524 string id = (*i)->get_id_string();
525 lib_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
526 }
527
528 if (!app_corpus->get_sorted_undefined_var_symbols().empty()
529 || !app_corpus->get_sorted_undefined_fun_symbols().empty())
530 lib_corpus->maybe_drop_some_exported_decls();
531
532 // OK now, lib_corpus only contains functions and variables which
533 // symbol are consumed by app_corpus.
534
535 // So we are now going to compare the functions that are exported by
536 // lib_corpus against those that app_corpus expects.
537 //
538 // In other words, the functions which symbols are defined by
539 // lib_corpus are going to be compared to the functions and
540 // variables which are undefined in app_corpus.
541
542 {
543 function_type_sptr lib_fn_type, app_fn_type;
544 vector<fn_change> fn_changes;
545 for (corpus::functions::const_iterator i =
546 lib_corpus->get_functions().begin();
547 i != lib_corpus->get_functions().end();
548 ++i)
549 {
550 // lib_fn_type contains the type of a function that is defined
551 // in lib_corpus.
552 lib_fn_type = (*i)->get_type();
553 ABG_ASSERT(lib_fn_type);
554
555 // app_fn_type contains the the "version" of lib_fn_type that
556 // is expected by app_corpus.
557 app_fn_type = lookup_or_synthesize_fn_type(lib_fn_type, *app_corpus);
558
559 // Now lets compare the type expected by app_corpus against
560 // the type actually provided by lib_fn_type.
561 function_type_diff_sptr fn_type_diff;
562 if (app_fn_type)
563 fn_type_diff = compute_diff(app_fn_type, lib_fn_type, ctxt);
564
565 // If the two types of functions are different, then let's
566 // store their difference in the "fn_changes" vector.
567 if (fn_type_diff && fn_type_diff->to_be_reported())
568 fn_changes.push_back(fn_change(*i, fn_type_diff));
569 }
570
571 string lib1_path = opts.lib1_path, app_path = opts.app_path;
572 if (opts.show_base_names)
573 {
574 base_name(opts.lib1_path, lib1_path);
575 base_name(opts.app_path, app_path);
576 }
577
578 // If some function changes were detected, then report them.
579 if (!fn_changes.empty())
580 {
581 cout << "functions defined in library "
582 << "'" << lib1_path << "'\n"
583 << "have sub-types that are different from what application "
584 << "'" << app_path << "' "
585 << "expects:\n\n";
586 for (vector<fn_change>::const_iterator i = fn_changes.begin();
587 i != fn_changes.end();
588 ++i)
589 {
590 cout << " "
591 << i->decl->get_pretty_representation()
592 << ":\n";
593 i->diff->report(cout, " ");
594 cout << "\n";
595 }
596 }
597
598 if (!fn_changes.empty())
599 status |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
600
601 // OK now, let's do something similar for *variables* changes.
602 //
603 // That is, let's compare the variables expected by app_corpus
604 // against the variables actually provided by lib_corpus and
605 // report the difference that might have been found.
606
607 type_base_sptr lib_var_type, app_var_type;
608 vector<var_change> var_changes;
609 for (corpus::variables::const_iterator i =
610 lib_corpus->get_variables().begin();
611 i != lib_corpus->get_variables().end();
612 ++i)
613 {
614 lib_var_type = (*i)->get_type();
615 ABG_ASSERT(lib_var_type);
616 app_var_type = lookup_type(lib_var_type, *app_corpus);
617 diff_sptr type_diff;
618 if (app_var_type)
619 type_diff = compute_diff(app_var_type, lib_var_type, ctxt);
620 if (type_diff && type_diff->to_be_reported())
621 var_changes.push_back(var_change(*i, type_diff));
622 }
623 if (!var_changes.empty())
624 {
625 cout << "variables defined in library "
626 << "'" << lib1_path << "'\n"
627 << "have sub-types that are different from what application "
628 << "'" << app_path << "' "
629 << "expects:\n\n";
630 for (vector<var_change>::const_iterator i = var_changes.begin();
631 i != var_changes.end();
632 ++i)
633 {
634 cout << " "
635 << i->decl->get_pretty_representation()
636 << ":\n";
637 i->diff->report(cout, " ");
638 cout << "\n";
639 }
640 }
641 }
642 return status;
643 }
644
645 /// Read an ABI corpus, be it from ELF or abixml.
646 ///
647 /// @param opts the options passed from the user to the program.
648 ///
649 /// @param status the resulting elf_reader::status to send back to the
650 /// caller.
651 ///
652 /// @param di_roots the directories from where to look for debug info.
653 ///
654 /// @param env the environment used for libabigail.
655 ///
656 /// @param path the path to the ABI corpus to read from.
657 static corpus_sptr
read_corpus(options opts,status & status,const vector<char ** > di_roots,const environment_sptr & env,const string & path)658 read_corpus(options opts, status &status,
659 const vector<char**> di_roots,
660 const environment_sptr &env,
661 const string &path)
662 {
663 corpus_sptr retval = NULL;
664 abigail::tools_utils::file_type type =
665 abigail::tools_utils::guess_file_type(path);
666
667 switch (type)
668 {
669 case abigail::tools_utils::FILE_TYPE_UNKNOWN:
670 emit_prefix(opts.prog_name, cerr)
671 << "Unknown content type for file " << path << "\n";
672 break;
673 case abigail::tools_utils::FILE_TYPE_ELF:
674 {
675 #ifdef WITH_CTF
676 if (opts.use_ctf)
677 {
678 abigail::ctf_reader::read_context_sptr r_ctxt
679 = abigail::ctf_reader::create_read_context(path,
680 env.get());
681 ABG_ASSERT(r_ctxt);
682
683 retval = abigail::ctf_reader::read_corpus(r_ctxt.get(), status);
684 }
685 else
686 #endif
687 retval = read_corpus_from_elf(path, di_roots, env.get(),
688 /*load_all_types=*/opts.weak_mode,
689 status);
690 }
691 break;
692 case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
693 {
694 abigail::xml_reader::read_context_sptr r_ctxt =
695 abigail::xml_reader::create_native_xml_read_context(path, env.get());
696 assert(r_ctxt);
697 retval = abigail::xml_reader::read_corpus_from_input(*r_ctxt);
698 }
699 break;
700 case abigail::tools_utils::FILE_TYPE_AR:
701 case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
702 case abigail::tools_utils::FILE_TYPE_RPM:
703 case abigail::tools_utils::FILE_TYPE_SRPM:
704 case abigail::tools_utils::FILE_TYPE_DEB:
705 case abigail::tools_utils::FILE_TYPE_DIR:
706 case abigail::tools_utils::FILE_TYPE_TAR:
707 case abigail::tools_utils::FILE_TYPE_NATIVE_BI:
708 break;
709 }
710
711 return retval;
712 }
713
714 int
main(int argc,char * argv[])715 main(int argc, char* argv[])
716 {
717 options opts(argv[0]);
718
719 if (!parse_command_line(argc, argv, opts))
720 {
721 if (!opts.unknow_option.empty())
722 {
723 emit_prefix(argv[0], cerr)
724 << "unrecognized option: " << opts.unknow_option << "\n"
725 << "try the --help option for more information\n";
726 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
727 | abigail::tools_utils::ABIDIFF_ERROR);
728 }
729
730 emit_prefix(argv[0], cerr)
731 << "wrong invocation\n"
732 << "try the --help option for more information\n";
733 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
734 | abigail::tools_utils::ABIDIFF_ERROR);
735 }
736
737 if (opts.display_help)
738 {
739 display_usage(argv[0], cout);
740 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
741 | abigail::tools_utils::ABIDIFF_ERROR);
742 }
743
744 if (opts.display_version)
745 {
746 emit_prefix(argv[0], cout)
747 << abigail::tools_utils::get_library_version_string()
748 << "\n";
749 return 0;
750 }
751
752 if (opts.weak_mode && !opts.lib2_path.empty())
753 {
754 emit_prefix(argv[0], cout)
755 << "WARNING: The \'--weak-mode\' option is used. The "
756 << opts.lib2_path << " will be ignored automatically\n";
757 }
758
759 if (opts.redundant_opt_set && opts.no_redundant_opt_set)
760 {
761 emit_prefix(argv[0], cerr)
762 << "ERROR: The \'--redundant\' and '--no-redundant' option are in conflict. "
763 << "Please select only one option to use.\n";
764 return 1;
765 }
766
767 ABG_ASSERT(!opts.app_path.empty());
768 if (!abigail::tools_utils::check_file(opts.app_path, cerr, opts.prog_name))
769 return abigail::tools_utils::ABIDIFF_ERROR;
770
771 // Create the context of the diff
772 diff_context_sptr ctxt = create_diff_context(opts);
773
774 // Check if any suppression specification prevents us from
775 // performing the compatibility checking.
776 suppressions_type& supprs = ctxt->suppressions();
777 bool files_suppressed = (file_is_suppressed(opts.app_path, supprs)
778 || file_is_suppressed(opts.lib1_path, supprs)
779 || file_is_suppressed(opts.lib2_path, supprs));
780
781 if (files_suppressed)
782 // We don't have to compare anything because a user
783 // suppression specification file instructs us to avoid
784 // loading either one of the input files.
785 return abigail::tools_utils::ABIDIFF_OK;
786
787 // Read the application ELF file.
788 char * app_di_root = opts.app_di_root_path.get();
789 vector<char**> app_di_roots;
790 app_di_roots.push_back(&app_di_root);
791 status status = abigail::elf_reader::STATUS_UNKNOWN;
792 environment_sptr env(new environment);
793
794 corpus_sptr app_corpus = read_corpus(opts, status,
795 app_di_roots, env,
796 opts.app_path);
797 if (!app_corpus)
798 {
799 emit_prefix(argv[0], cerr) << opts.app_path
800 << " is not a supported file\n";
801 return abigail::tools_utils::ABIDIFF_ERROR;
802 }
803
804 if (opts.fail_no_debug_info && (status & STATUS_ALT_DEBUG_INFO_NOT_FOUND)
805 && (status & STATUS_DEBUG_INFO_NOT_FOUND))
806 {
807 emit_prefix(argv[0], cerr) << opts.app_path
808 << " does not have debug symbols\n";
809 return abigail::tools_utils::ABIDIFF_ERROR;
810 }
811 if (status & abigail::elf_reader::STATUS_NO_SYMBOLS_FOUND)
812 {
813 emit_prefix(argv[0], cerr)
814 << "could not read symbols from " << opts.app_path << "\n";
815 return abigail::tools_utils::ABIDIFF_ERROR;
816 }
817 if (!(status & abigail::elf_reader::STATUS_OK))
818 {
819 emit_prefix(argv[0], cerr)
820 << "could not read file " << opts.app_path << "\n";
821 return abigail::tools_utils::ABIDIFF_ERROR;
822 }
823
824 if (opts.list_undefined_symbols_only)
825 {
826 for (elf_symbols::const_iterator i =
827 app_corpus->get_sorted_undefined_fun_symbols().begin();
828 i != app_corpus->get_sorted_undefined_fun_symbols().end();
829 ++i)
830 {
831 string id = (*i)->get_id_string();
832 string sym_name = (*i)->get_name();
833 string demangled_name = demangle_cplus_mangled_name(sym_name);
834 if (demangled_name != sym_name)
835 cout << demangled_name << " {" << id << "}\n";
836 else
837 cout << id << "\n";
838 }
839 return abigail::tools_utils::ABIDIFF_OK;
840 }
841
842 // Read the first version of the library.
843 ABG_ASSERT(!opts.lib1_path.empty());
844 if (!abigail::tools_utils::check_file(opts.lib1_path, cerr, opts.prog_name))
845 return abigail::tools_utils::ABIDIFF_ERROR;
846
847 char * lib1_di_root = opts.lib1_di_root_path.get();
848 vector<char**> lib1_di_roots;
849 lib1_di_roots.push_back(&lib1_di_root);
850 corpus_sptr lib1_corpus = read_corpus(opts, status,
851 lib1_di_roots,
852 env, opts.lib1_path);
853 if (!lib1_corpus)
854 {
855 emit_prefix(argv[0], cerr) << opts.lib1_path
856 << " is not a supported file\n";
857 return abigail::tools_utils::ABIDIFF_ERROR;
858 }
859 if (opts.fail_no_debug_info && (status & STATUS_ALT_DEBUG_INFO_NOT_FOUND)
860 && (status & STATUS_DEBUG_INFO_NOT_FOUND))
861 emit_prefix(argv[0], cerr)
862 << "could not read debug info for " << opts.lib1_path << "\n";
863 if (status & abigail::elf_reader::STATUS_NO_SYMBOLS_FOUND)
864 {
865 emit_prefix(argv[0], cerr) << "could not read symbols from "
866 << opts.lib1_path << "\n";
867 return abigail::tools_utils::ABIDIFF_ERROR;
868 }
869 if (!(status & abigail::elf_reader::STATUS_OK))
870 {
871 emit_prefix(argv[0], cerr)
872 << "could not read file " << opts.lib1_path << "\n";
873 return abigail::tools_utils::ABIDIFF_ERROR;
874 }
875
876 // Read the second version of the library.
877 corpus_sptr lib2_corpus;
878 if (!opts.weak_mode)
879 {
880 ABG_ASSERT(!opts.lib2_path.empty());
881 char * lib2_di_root = opts.lib2_di_root_path.get();
882 vector<char**> lib2_di_roots;
883 lib2_di_roots.push_back(&lib2_di_root);
884 lib2_corpus = read_corpus(opts, status,
885 lib2_di_roots, env,
886 opts.lib2_path);
887 if (!lib2_corpus)
888 {
889 emit_prefix(argv[0], cerr) << opts.lib2_path
890 << " is not a supported file\n";
891 return abigail::tools_utils::ABIDIFF_ERROR;
892 }
893
894 if (opts.fail_no_debug_info && (status & STATUS_ALT_DEBUG_INFO_NOT_FOUND)
895 && (status & STATUS_DEBUG_INFO_NOT_FOUND))
896 {
897 emit_prefix(argv[0], cerr)
898 << "could not read debug info for " << opts.lib2_path << "\n";
899 return abigail::tools_utils::ABIDIFF_ERROR;
900 }
901 if (status & abigail::elf_reader::STATUS_NO_SYMBOLS_FOUND)
902 {
903 emit_prefix(argv[0], cerr)
904 << "could not read symbols from " << opts.lib2_path << "\n";
905 return abigail::tools_utils::ABIDIFF_ERROR;
906 }
907 if (!(status & abigail::elf_reader::STATUS_OK))
908 {
909 emit_prefix(argv[0], cerr)
910 << "could not read file " << opts.lib2_path << "\n";
911 return abigail::tools_utils::ABIDIFF_ERROR;
912 }
913 }
914
915 abidiff_status s = abigail::tools_utils::ABIDIFF_OK;
916
917 if (opts.weak_mode)
918 s = perform_compat_check_in_weak_mode(opts, ctxt,
919 app_corpus,
920 lib1_corpus);
921 else
922 s = perform_compat_check_in_normal_mode(opts, ctxt,
923 app_corpus,
924 lib1_corpus,
925 lib2_corpus);
926
927 return s;
928 }
929