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