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