• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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