• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2021 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7 
8 /// @file
9 
10 #include "config.h"
11 #include <cstring>
12 #include <iostream>
13 #include <memory>
14 #include <string>
15 #include <vector>
16 #include "abg-config.h"
17 #include "abg-comp-filter.h"
18 #include "abg-suppression.h"
19 #include "abg-tools-utils.h"
20 #include "abg-reader.h"
21 #include "abg-dwarf-reader.h"
22 #ifdef WITH_CTF
23 #include "abg-ctf-reader.h"
24 #endif
25 
26 using std::vector;
27 using std::string;
28 using std::ostream;
29 using std::cout;
30 using std::cerr;
31 using std::shared_ptr;
32 using abigail::ir::environment;
33 using abigail::ir::environment_sptr;
34 using abigail::translation_unit;
35 using abigail::translation_unit_sptr;
36 using abigail::corpus_sptr;
37 using abigail::corpus_group_sptr;
38 using abigail::comparison::translation_unit_diff_sptr;
39 using abigail::comparison::corpus_diff;
40 using abigail::comparison::corpus_diff_sptr;
41 using abigail::comparison::compute_diff;
42 using abigail::comparison::get_default_harmless_categories_bitmap;
43 using abigail::comparison::get_default_harmful_categories_bitmap;
44 using abigail::suppr::suppression_sptr;
45 using abigail::suppr::suppressions_type;
46 using abigail::suppr::read_suppressions;
47 using namespace abigail::dwarf_reader;
48 using namespace abigail::elf_reader;
49 using abigail::tools_utils::emit_prefix;
50 using abigail::tools_utils::check_file;
51 using abigail::tools_utils::guess_file_type;
52 using abigail::tools_utils::gen_suppr_spec_from_headers;
53 using abigail::tools_utils::gen_suppr_spec_from_kernel_abi_whitelists;
54 using abigail::tools_utils::load_default_system_suppressions;
55 using abigail::tools_utils::load_default_user_suppressions;
56 using abigail::tools_utils::abidiff_status;
57 
58 struct options
59 {
60   bool display_usage;
61   bool display_version;
62   bool missing_operand;
63   string		wrong_option;
64   string		file1;
65   string		file2;
66   vector<string>	suppression_paths;
67   vector<string>	kernel_abi_whitelist_paths;
68   vector<string>	drop_fn_regex_patterns;
69   vector<string>	drop_var_regex_patterns;
70   vector<string>	keep_fn_regex_patterns;
71   vector<string>	keep_var_regex_patterns;
72   vector<string>	headers_dirs1;
73   vector<string>        header_files1;
74   vector<string>	headers_dirs2;
75   vector<string>        header_files2;
76   bool			drop_private_types;
77   bool			linux_kernel_mode;
78   bool			no_default_supprs;
79   bool			no_arch;
80   bool			no_corpus;
81   bool			ignore_soname;
82   bool			leaf_changes_only;
83   bool			fail_no_debug_info;
84   bool			show_hexadecimal_values;
85   bool			show_offsets_sizes_in_bits;
86   bool			show_relative_offset_changes;
87   bool			show_stats_only;
88   bool			show_symtabs;
89   bool			show_deleted_fns;
90   bool			show_changed_fns;
91   bool			show_added_fns;
92   bool			show_added_syms;
93   bool			show_all_fns;
94   bool			show_deleted_vars;
95   bool			show_changed_vars;
96   bool			show_added_vars;
97   bool			show_all_vars;
98   bool			show_all_types;
99   bool			show_linkage_names;
100   bool			show_locs;
101   bool			show_harmful_changes;
102   bool			show_harmless_changes;
103   bool			show_redundant_changes;
104   bool			show_symbols_not_referenced_by_debug_info;
105   bool			show_impacted_interfaces;
106   bool			dump_diff_tree;
107   bool			show_stats;
108   bool			do_log;
109 #ifdef WITH_DEBUG_SELF_COMPARISON
110   bool			do_debug;
111 #endif
112 #ifdef WITH_CTF
113   bool			use_ctf;
114 #endif
115   vector<char*> di_root_paths1;
116   vector<char*> di_root_paths2;
117   vector<char**> prepared_di_root_paths1;
118   vector<char**> prepared_di_root_paths2;
119 
optionsoptions120   options()
121     : display_usage(),
122       display_version(),
123       missing_operand(),
124       drop_private_types(false),
125       linux_kernel_mode(true),
126       no_default_supprs(),
127       no_arch(),
128       no_corpus(),
129       ignore_soname(false),
130       leaf_changes_only(),
131       fail_no_debug_info(),
132       show_hexadecimal_values(),
133       show_offsets_sizes_in_bits(true),
134       show_relative_offset_changes(true),
135       show_stats_only(),
136       show_symtabs(),
137       show_deleted_fns(),
138       show_changed_fns(),
139       show_added_fns(),
140       show_added_syms(true),
141       show_all_fns(true),
142       show_deleted_vars(),
143       show_changed_vars(),
144       show_added_vars(),
145       show_all_vars(true),
146       show_all_types(false),
147       show_linkage_names(true),
148       show_locs(true),
149       show_harmful_changes(true),
150       show_harmless_changes(),
151       show_redundant_changes(),
152       show_symbols_not_referenced_by_debug_info(true),
153       show_impacted_interfaces(),
154       dump_diff_tree(),
155       show_stats(),
156       do_log()
157 #ifdef WITH_CTF
158     ,
159       use_ctf()
160 #endif
161 #ifdef WITH_DEBUG_SELF_COMPARISON
162     ,
163     do_debug()
164 #endif
165   {}
166 
~optionsoptions167   ~options()
168   {
169     for (vector<char*>::iterator i = di_root_paths1.begin();
170 	 i != di_root_paths1.end();
171 	 ++i)
172       free(*i);
173 
174     for (vector<char*>::iterator i = di_root_paths2.begin();
175 	 i != di_root_paths2.end();
176 	 ++i)
177       free(*i);
178 
179     prepared_di_root_paths1.clear();
180     prepared_di_root_paths2.clear();
181   }
182 };//end struct options;
183 
184 static void
display_usage(const string & prog_name,ostream & out)185 display_usage(const string& prog_name, ostream& out)
186 {
187   emit_prefix(prog_name, out)
188     << "usage: " << prog_name << " [options] [<file1> <file2>]\n"
189     << " where options can be:\n"
190     << " --help|-h  display this message\n "
191     << " --version|-v  display program version information and exit\n"
192     << " --debug-info-dir1|--d1 <path> the root for the debug info of file1\n"
193     << " --debug-info-dir2|--d2 <path> the root for the debug info of file2\n"
194     << " --headers-dir1|--hd1 <path>  the path to headers of file1\n"
195     << " --header-file1|--hf1 <path>  the path to one header of file1\n"
196     << " --headers-dir2|--hd2 <path>  the path to headers of file2\n"
197     << " --header-file2|--hf2 <path>  the path to one header of file2\n"
198     << " --drop-private-types  drop private types from "
199     "internal representation\n"
200     << " --no-linux-kernel-mode  don't consider the input binaries as "
201        "linux kernel binaries\n"
202     << " --kmi-whitelist|-w  path to a "
203        "linux kernel abi whitelist\n"
204     << " --stat  only display the diff stats\n"
205     << " --symtabs  only display the symbol tables of the corpora\n"
206     << " --no-default-suppression  don't load any "
207        "default suppression specification\n"
208     << " --no-architecture  do not take architecture in account\n"
209     << " --no-corpus-path  do not take the path to the corpora into account\n"
210     << " --ignore-soname  do not take the SONAMEs into account\n"
211     << " --fail-no-debug-info  bail out if no debug info was found\n"
212     << " --leaf-changes-only|-l  only show leaf changes, "
213     "so no change impact analysis (implies --redundant)\n"
214     << " --deleted-fns  display deleted public functions\n"
215     << " --changed-fns  display changed public functions\n"
216     << " --added-fns  display added public functions\n"
217     << " --deleted-vars  display deleted global public variables\n"
218     << " --changed-vars  display changed global public variables\n"
219     << " --added-vars  display added global public variables\n"
220     << " --non-reachable-types|-t  consider types non reachable"
221     " from public interfaces\n"
222     << " --no-added-syms  do not display added functions or variables\n"
223     << " --no-linkage-name  do not display linkage names of "
224     "added/removed/changed\n"
225     << " --no-unreferenced-symbols  do not display changes "
226     "about symbols not referenced by debug info\n"
227     << " --no-show-locs  do now show location information\n"
228     << " --show-bytes  show size and offsets in bytes\n"
229     << " --show-bits  show size and offsets in bits\n"
230     << " --show-hex  show size and offset in hexadecimal\n"
231     << " --show-dec  show size and offset in decimal\n"
232     << " --no-show-relative-offset-changes  do not show relative"
233     " offset changes\n"
234     << " --suppressions|--suppr <path> specify a suppression file\n"
235     << " --drop <regex>  drop functions and variables matching a regexp\n"
236     << " --drop-fn <regex> drop functions matching a regexp\n"
237     << " --drop-var <regex> drop variables matching a regexp\n"
238     << " --keep <regex>  keep only functions and variables matching a regex\n"
239     << " --keep-fn <regex>  keep only functions matching a regex\n"
240     << " --keep-var  <regex>  keep only variables matching a regex\n"
241     << " --harmless  display the harmless changes\n"
242     << " --no-harmful  do not display the harmful changes\n"
243     << " --redundant  display redundant changes\n"
244     << " --no-redundant  do not display redundant changes "
245     "(this is the default)\n"
246     << " --impacted-interfaces  display interfaces impacted by leaf changes\n"
247     << " --dump-diff-tree  emit a debug dump of the internal diff tree to "
248     "the error output stream\n"
249     <<  " --stats  show statistics about various internal stuff\n"
250 #ifdef WITH_CTF
251     << " --ctf use CTF instead of DWARF in ELF files\n"
252 #endif
253 #ifdef WITH_DEBUG_SELF_COMPARISON
254     << " --debug debug the process of comparing an ABI corpus against itself"
255 #endif
256     << " --verbose show verbose messages about internal stuff\n";
257 }
258 
259 /// Parse the command line and set the options accordingly.
260 ///
261 /// @param argc the number of words on the command line
262 ///
263 /// @param argv the command line, which is an array of words.
264 ///
265 /// @param opts the options data structure.  This is set by the
266 /// function iff it returns true.
267 ///
268 /// @return true if the command line could be parsed and opts filed,
269 /// false otherwise.
270 bool
parse_command_line(int argc,char * argv[],options & opts)271 parse_command_line(int argc, char* argv[], options& opts)
272 {
273   if (argc < 2)
274     return false;
275 
276   for (int i = 1; i < argc; ++i)
277     {
278       if (argv[i][0] != '-')
279 	{
280 	  if (opts.file1.empty())
281 	    opts.file1 = argv[i];
282 	  else if (opts.file2.empty())
283 	    opts.file2 = argv[i];
284 	  else
285 	    return false;
286 	}
287       else if (!strcmp(argv[i], "--version")
288 	       || !strcmp(argv[i], "-v"))
289 	{
290 	  opts.display_version = true;
291 	  return true;
292 	}
293       else if (!strcmp(argv[i], "--debug-info-dir1")
294 	       || !strcmp(argv[i], "--d1"))
295 	{
296 	  int j = i + 1;
297 	  if (j >= argc)
298 	    {
299 	      opts.missing_operand = true;
300 	      opts.wrong_option = argv[i];
301 	      return true;
302 	    }
303 	  // elfutils wants the root path to the debug info to be
304 	  // absolute.
305 	  opts.di_root_paths1.push_back
306 	    (abigail::tools_utils::make_path_absolute_to_be_freed(argv[j]));
307 	  ++i;
308 	}
309       else if (!strcmp(argv[i], "--debug-info-dir2")
310 	       || !strcmp(argv[i], "--d2"))
311 	{
312 	  int j = i + 1;
313 	  if (j >= argc)
314 	    {
315 	      opts.missing_operand = true;
316 	      opts.wrong_option = argv[i];
317 	      return true;
318 	    }
319 	  // elfutils wants the root path to the debug info to be
320 	  // absolute.
321 	  opts.di_root_paths2.push_back
322 	    (abigail::tools_utils::make_path_absolute_to_be_freed(argv[j]));
323 	  ++i;
324 	}
325       else if (!strcmp(argv[i], "--headers-dir1")
326 	       || !strcmp(argv[i], "--hd1"))
327 	{
328 	  int j = i + 1;
329 	  if (j >= argc)
330 	    {
331 	      opts.missing_operand = true;
332 	      opts.wrong_option = argv[i];
333 	      return true;
334 	    }
335 	  // The user can specify several header files directories for
336 	  // the first binary.
337 	  opts.headers_dirs1.push_back(argv[j]);
338 	  ++i;
339 	}
340       else if (!strcmp(argv[i], "--header-file1")
341 	       || !strcmp(argv[i], "--hf1"))
342 	{
343 	  int j = i + 1;
344 	  if (j >= argc)
345 	    {
346 	      opts.missing_operand = true;
347 	      opts.wrong_option = argv[i];
348 	      return true;
349 	    }
350 	  opts.header_files1.push_back(argv[j]);
351 	  ++i;
352 	}
353       else if (!strcmp(argv[i], "--headers-dir2")
354 	       || !strcmp(argv[i], "--hd2"))
355 	{
356 	  int j = i + 1;
357 	  if (j >= argc)
358 	    {
359 	      opts.missing_operand = true;
360 	      opts.wrong_option = argv[i];
361 	      return true;
362 	    }
363 	  // The user can specify several header files directories for
364 	  // the first binary.
365 	  opts.headers_dirs2.push_back(argv[j]);
366 	  ++i;
367 	}
368       else if (!strcmp(argv[i], "--header-file2")
369 	       || !strcmp(argv[i], "--hf2"))
370 	{
371 	  int j = i + 1;
372 	  if (j >= argc)
373 	    {
374 	      opts.missing_operand = true;
375 	      opts.wrong_option = argv[i];
376 	      return true;
377 	    }
378 	  opts.header_files2.push_back(argv[j]);
379 	  ++i;
380 	}
381       else if (!strcmp(argv[i], "--kmi-whitelist")
382 	       || !strcmp(argv[i], "-w"))
383 	{
384 	  int j = i + 1;
385 	  if (j >= argc)
386 	    {
387 	      opts.missing_operand = true;
388 	      opts.wrong_option = argv[i];
389 	      return true;
390 	    }
391 	  opts.kernel_abi_whitelist_paths.push_back(argv[j]);
392 	  ++i;
393 	}
394       else if (!strcmp(argv[i], "--stat"))
395 	opts.show_stats_only = true;
396       else if (!strcmp(argv[i], "--symtabs"))
397 	opts.show_symtabs = true;
398       else if (!strcmp(argv[i], "--help")
399 	       || !strcmp(argv[i], "-h"))
400 	{
401 	  opts.display_usage = true;
402 	  return true;
403 	}
404       else if (!strcmp(argv[i], "--drop-private-types"))
405 	opts.drop_private_types = true;
406       else if (!strcmp(argv[i], "--no-default-suppression"))
407 	opts.no_default_supprs = true;
408       else if (!strcmp(argv[i], "--no-architecture"))
409 	opts.no_arch = true;
410       else if (!strcmp(argv[i], "--no-corpus-path"))
411 	opts.no_corpus = true;
412       else if (!strcmp(argv[i], "--ignore-soname"))
413 	opts.ignore_soname = true;
414       else if (!strcmp(argv[i], "--fail-no-debug-info"))
415 	opts.fail_no_debug_info = true;
416       else if (!strcmp(argv[i], "--leaf-changes-only")
417 	       ||!strcmp(argv[i], "-l"))
418 	opts.leaf_changes_only = true;
419       else if (!strcmp(argv[i], "--deleted-fns"))
420 	{
421 	  opts.show_deleted_fns = true;
422 	  opts.show_all_fns = false;
423 	  opts.show_all_vars = false;
424 	}
425       else if (!strcmp(argv[i], "--changed-fns"))
426 	{
427 	  opts.show_changed_fns = true;
428 	  opts.show_all_fns = false;
429 	  opts.show_all_vars = false;
430 	}
431       else if (!strcmp(argv[i], "--added-fns"))
432 	{
433 	  opts.show_added_fns = true;
434 	  opts.show_all_fns = false;
435 	  opts.show_all_vars = false;
436 	}
437       else if (!strcmp(argv[i], "--deleted-vars"))
438 	{
439 	  opts.show_deleted_vars = true;
440 	  opts.show_all_fns = false;
441 	  opts.show_all_vars = false;
442 	}
443       else if (!strcmp(argv[i], "--changed-vars"))
444 	{
445 	  opts.show_changed_vars = true;
446 	  opts.show_all_fns = false;
447 	  opts.show_all_vars = false;
448 	}
449       else if (!strcmp(argv[i], "--added-vars"))
450 	{
451 	  opts.show_added_vars = true;
452 	  opts.show_all_fns = false;
453 	  opts.show_all_vars = false;
454 	}
455       else if (!strcmp(argv[i], "--non-reachable-types")
456 	       || !strcmp(argv[i], "-t"))
457 	  opts.show_all_types = true;
458       else if (!strcmp(argv[i], "--no-added-syms"))
459 	{
460 	  opts.show_added_syms = false;
461 	  opts.show_added_vars = false;
462 	  opts.show_added_fns = false;
463 
464 	  // If any of the {changed,deleted}_{vars,fns} is already
465 	  // specified, --no-added-syms has no further effect.  If it
466 	  // is the only option specified (as of the time of parsing
467 	  // it), it shall mean "show everything, except added vars,
468 	  // fns and unreferenced symbols.
469 	  if (!(opts.show_changed_fns
470 		|| opts.show_changed_vars
471 		|| opts.show_deleted_fns
472 		|| opts.show_deleted_vars))
473 	    {
474 	      opts.show_changed_fns = true;
475 	      opts.show_changed_vars = true;
476 
477 	      opts.show_deleted_vars = true;
478 	      opts.show_deleted_fns = true;
479 	    }
480 
481 	  opts.show_all_fns = false;
482 	  opts.show_all_vars = false;
483 	}
484       else if (!strcmp(argv[i], "--no-linkage-name"))
485 	opts.show_linkage_names = false;
486       else if (!strcmp(argv[i], "--no-unreferenced-symbols"))
487 	opts.show_symbols_not_referenced_by_debug_info = false;
488       else if (!strcmp(argv[i], "--no-show-locs"))
489 	opts.show_locs = false;
490       else if (!strcmp(argv[i], "--show-bytes"))
491 	opts.show_offsets_sizes_in_bits = false;
492       else if (!strcmp(argv[i], "--show-bits"))
493 	opts.show_offsets_sizes_in_bits = true;
494       else if (!strcmp(argv[i], "--show-hex"))
495 	opts.show_hexadecimal_values = true;
496       else if (!strcmp(argv[i], "--show-dec"))
497 	opts.show_hexadecimal_values = false;
498       else if (!strcmp(argv[i], "--no-show-relative-offset-changes"))
499 	opts.show_relative_offset_changes = false;
500       else if (!strcmp(argv[i], "--suppressions")
501 	       || !strcmp(argv[i], "--suppr"))
502 	{
503 	  int j = i + 1;
504 	  if (j >= argc)
505 	    {
506 	      opts.missing_operand = true;
507 	      opts.wrong_option = argv[i];
508 	      return true;
509 	    }
510 	  opts.suppression_paths.push_back(argv[j]);
511 	  ++i;
512 	}
513       else if (!strcmp(argv[i], "--drop"))
514 	{
515 	  int j = i + 1;
516 	  if (j >= argc)
517 	    {
518 	      opts.missing_operand = true;
519 	      opts.wrong_option = argv[i];
520 	      return true;
521 	    }
522 	  opts.drop_fn_regex_patterns.push_back(argv[j]);
523 	  opts.drop_var_regex_patterns.push_back(argv[j]);
524 	  ++i;
525 	}
526       else if (!strcmp(argv[i], "--drop-fn"))
527 	{
528 	  int j = i + 1;
529 	  if (j >= argc)
530 	    {
531 	      opts.missing_operand = true;
532 	      opts.wrong_option = argv[i];
533 	      return true;
534 	    }
535 	  opts.drop_fn_regex_patterns.push_back(argv[j]);
536 	  ++i;
537 	}
538       else if (!strcmp(argv[i], "--drop-var"))
539 	{
540 	  int j = i + 1;
541 	  if (j >= argc)
542 	    {
543 	      opts.missing_operand = true;
544 	      opts.wrong_option = argv[i];
545 	      return true;
546 	    }
547 	  opts.drop_var_regex_patterns.push_back(argv[j]);
548 	  ++i;
549 	}
550       else if (!strcmp(argv[i], "--keep"))
551 	{
552 	  int j = i + 1;
553 	  if (j >= argc)
554 	    {
555 	      opts.missing_operand = true;
556 	      opts.wrong_option = argv[i];
557 	      return true;
558 	    }
559 	  opts.keep_fn_regex_patterns.push_back(argv[j]);
560 	  opts.keep_var_regex_patterns.push_back(argv[j]);
561 	  ++i;
562 	}
563       else if (!strcmp(argv[i], "--keep-fn"))
564 	{
565 	  int j = i + 1;
566 	  if (j >= argc)
567 	    {
568 	      opts.missing_operand = true;
569 	      opts.wrong_option = argv[i];
570 	      return true;
571 	    }
572 	  opts.keep_fn_regex_patterns.push_back(argv[j]);
573 	}
574       else if (!strcmp(argv[i], "--keep-var"))
575 	{
576 	  int j = i + 1;
577 	  if (j >= argc)
578 	    {
579 	      opts.missing_operand = true;
580 	      opts.wrong_option = argv[i];
581 	      return true;
582 	    }
583 	  opts.keep_var_regex_patterns.push_back(argv[j]);
584 	}
585       else if (!strcmp(argv[i], "--harmless"))
586 	opts.show_harmless_changes = true;
587       else if (!strcmp(argv[i], "--no-harmful"))
588 	opts.show_harmful_changes = false;
589       else if (!strcmp(argv[i], "--redundant"))
590 	opts.show_redundant_changes = true;
591       else if (!strcmp(argv[i], "--no-redundant"))
592 	opts.show_redundant_changes = false;
593       else if (!strcmp(argv[i], "--impacted-interfaces"))
594 	opts.show_impacted_interfaces = true;
595       else if (!strcmp(argv[i], "--dump-diff-tree"))
596 	opts.dump_diff_tree = true;
597       else if (!strcmp(argv[i], "--stats"))
598 	opts.show_stats = true;
599       else if (!strcmp(argv[i], "--verbose"))
600 	opts.do_log = true;
601 #ifdef WITH_CTF
602       else if (!strcmp(argv[i], "--ctf"))
603         opts.use_ctf = true;
604 #endif
605 #ifdef WITH_DEBUG_SELF_COMPARISON
606       else if (!strcmp(argv[i], "--debug"))
607 	opts.do_debug = true;
608 #endif
609       else
610 	{
611 	  if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
612 	    opts.wrong_option = argv[i];
613 	  return false;
614 	}
615     }
616 
617   return true;
618 }
619 
620 /// Display the function symbol tables for the two corpora.
621 ///
622 /// @param c1 the first corpus to display the symbol table for.
623 ///
624 /// @param c2 the second corpus to display the symbol table for.
625 ///
626 /// @param o the output stream to emit the symbol tables to.
627 static void
display_symtabs(const corpus_sptr c1,const corpus_sptr c2,ostream & o)628 display_symtabs(const corpus_sptr c1, const corpus_sptr c2, ostream& o)
629 {
630   o << "size of the functions symtabs: "
631     << c1->get_functions().size()
632     << " and "
633     << c2->get_functions().size()
634     << "\n\n";
635 
636   if (c1->get_functions().size())
637     o << "First functions symbol table\n\n";
638   for (abigail::corpus::functions::const_iterator i =
639 	 c1->get_functions().begin();
640        i != c1->get_functions().end();
641        ++i)
642     o << (*i)->get_pretty_representation() << std::endl;
643 
644   if (c1->get_functions().size() != 0)
645     o << "\n";
646 
647   if (c2->get_functions().size())
648     o << "Second functions symbol table\n\n";
649   for (abigail::corpus::functions::const_iterator i =
650 	 c2->get_functions().begin();
651        i != c2->get_functions().end();
652        ++i)
653     o << (*i)->get_pretty_representation() << std::endl;
654 }
655 
656 using abigail::comparison::diff_context_sptr;
657 using abigail::comparison::diff_context;
658 
659 /// Check that the suppression specification files supplied are
660 /// present.  If not, emit an error on stderr.
661 ///
662 /// @param opts the options instance to use.
663 ///
664 /// @return true if all suppression specification files are present,
665 /// false otherwise.
666 static bool
maybe_check_suppression_files(const options & opts)667 maybe_check_suppression_files(const options& opts)
668 {
669   for (vector<string>::const_iterator i = opts.suppression_paths.begin();
670        i != opts.suppression_paths.end();
671        ++i)
672     if (!check_file(*i, cerr, "abidiff"))
673       return false;
674 
675   for (vector<string>::const_iterator i =
676 	 opts.kernel_abi_whitelist_paths.begin();
677        i != opts.kernel_abi_whitelist_paths.end();
678        ++i)
679     if (!check_file(*i, cerr, "abidiff"))
680       return false;
681 
682   return true;
683 }
684 
685 /// Update the diff context from the @ref options data structure.
686 ///
687 /// @param ctxt the diff context to update.
688 ///
689 /// @param opts the instance of @ref options to consider.
690 static void
set_diff_context_from_opts(diff_context_sptr ctxt,options & opts)691 set_diff_context_from_opts(diff_context_sptr ctxt,
692 			   options& opts)
693 {
694   ctxt->default_output_stream(&cout);
695   ctxt->error_output_stream(&cerr);
696   ctxt->show_leaf_changes_only(opts.leaf_changes_only);
697   ctxt->show_hex_values(opts.show_hexadecimal_values);
698   ctxt->show_offsets_sizes_in_bits(opts.show_offsets_sizes_in_bits);
699   ctxt->show_relative_offset_changes(opts.show_relative_offset_changes);
700   ctxt->show_stats_only(opts.show_stats_only);
701   ctxt->show_deleted_fns(opts.show_all_fns || opts.show_deleted_fns);
702   ctxt->show_changed_fns(opts.show_all_fns || opts.show_changed_fns);
703   ctxt->show_added_fns(opts.show_all_fns || opts.show_added_fns);
704   ctxt->show_deleted_vars(opts.show_all_vars || opts.show_deleted_vars);
705   ctxt->show_changed_vars(opts.show_all_vars || opts.show_changed_vars);
706   ctxt->show_added_vars(opts.show_all_vars || opts.show_added_vars);
707   ctxt->show_linkage_names(opts.show_linkage_names);
708   ctxt->show_locs(opts.show_locs);
709   // Intentional logic flip of ignore_soname
710   ctxt->show_soname_change(!opts.ignore_soname);
711   // So when we are showing only leaf changes, we want to show
712   // redundant changes because of this: Suppose several functions have
713   // their return type changed from void* to int*.  We want them all
714   // to be reported.  In that case the change is not redundant.  As
715   // far as user-defined type changes (like struct/class) they are
716   // already put inside a map which makes them be non-redundant, so we
717   // don't have to worry about that case.
718   //
719   // TODO: maybe that in this case we should avoid firing the
720   // redundancy analysis pass altogether.  That could help save a
721   // couple of CPU cycle here and there!
722   ctxt->show_redundant_changes(opts.show_redundant_changes
723                                || opts.leaf_changes_only);
724   ctxt->show_symbols_unreferenced_by_debug_info
725     (opts.show_symbols_not_referenced_by_debug_info);
726   ctxt->show_added_symbols_unreferenced_by_debug_info
727     (opts.show_symbols_not_referenced_by_debug_info && opts.show_added_syms);
728   ctxt->show_unreachable_types(opts.show_all_types);
729   ctxt->show_impacted_interfaces(opts.show_impacted_interfaces);
730 
731   if (!opts.show_harmless_changes)
732       ctxt->switch_categories_off(get_default_harmless_categories_bitmap());
733 
734   if (!opts.show_harmful_changes)
735     ctxt->switch_categories_off(get_default_harmful_categories_bitmap());
736 
737   suppressions_type supprs;
738   for (vector<string>::const_iterator i = opts.suppression_paths.begin();
739        i != opts.suppression_paths.end();
740        ++i)
741     read_suppressions(*i, supprs);
742   ctxt->add_suppressions(supprs);
743 
744   if (!opts.no_default_supprs && opts.suppression_paths.empty())
745     {
746       // Load the default system and user suppressions.
747       suppressions_type& supprs = ctxt->suppressions();
748 
749       load_default_system_suppressions(supprs);
750       load_default_user_suppressions(supprs);
751     }
752 
753   if (!opts.headers_dirs1.empty() || !opts.header_files1.empty())
754     {
755       // Generate suppression specification to avoid showing ABI
756       // changes on types that are not defined in public headers.
757       suppression_sptr suppr =
758 	gen_suppr_spec_from_headers(opts.headers_dirs1, opts.header_files1);
759       if (suppr)
760 	ctxt->add_suppression(suppr);
761     }
762 
763   if (!opts.headers_dirs2.empty() || !opts.header_files2.empty())
764     {
765       // Generate suppression specification to avoid showing ABI
766       // changes on types that are not defined in public headers.
767       suppression_sptr suppr =
768 	gen_suppr_spec_from_headers(opts.headers_dirs2, opts.header_files2);
769       if (suppr)
770 	ctxt->add_suppression(suppr);
771     }
772 
773   ctxt->dump_diff_tree(opts.dump_diff_tree);
774 }
775 
776 /// Set suppression specifications to the @p read_context used to load
777 /// the ABI corpus from the ELF/DWARF file.
778 ///
779 /// These suppression specifications are going to be applied to drop
780 /// some ABI artifacts on the floor (while reading the ELF/DWARF file
781 /// or the native XML ABI file) and thus minimize the size of the
782 /// resulting ABI corpus.
783 ///
784 /// @param read_ctxt the read context to apply the suppression
785 /// specifications to.  Note that the type of this parameter is
786 /// generic (class template) because in practise, it can be either an
787 /// abigail::dwarf_reader::read_context type or an
788 /// abigail::xml_reader::read_context type.
789 ///
790 /// @param opts the options where to get the suppression
791 /// specifications from.
792 template<class ReadContextType>
793 static void
set_suppressions(ReadContextType & read_ctxt,const options & opts)794 set_suppressions(ReadContextType& read_ctxt, const options& opts)
795 {
796   suppressions_type supprs;
797   for (vector<string>::const_iterator i = opts.suppression_paths.begin();
798        i != opts.suppression_paths.end();
799        ++i)
800     read_suppressions(*i, supprs);
801 
802   if (read_context_get_path(read_ctxt) == opts.file1
803       && (!opts.headers_dirs1.empty() || !opts.header_files1.empty()))
804     {
805       // Generate suppression specification to avoid showing ABI
806       // changes on types that are not defined in public headers for
807       // the first binary.
808       //
809       // As these suppression specifications are applied during the
810       // corpus loading, they are going to be dropped from the
811       // internal representation altogether.
812       suppression_sptr suppr =
813 	gen_suppr_spec_from_headers(opts.headers_dirs1, opts.header_files1);
814       if (suppr)
815 	{
816 	  if (opts.drop_private_types)
817 	    suppr->set_drops_artifact_from_ir(true);
818 	  supprs.push_back(suppr);
819 	}
820     }
821 
822   if (read_context_get_path(read_ctxt) == opts.file2
823       && (!opts.headers_dirs2.empty() || !opts.header_files2.empty()))
824     {
825       // Generate suppression specification to avoid showing ABI
826       // changes on types that are not defined in public headers for
827       // the second binary.
828       //
829       // As these suppression specifications are applied during the
830       // corpus loading, they are going to be dropped from the
831       // internal representation altogether.
832       suppression_sptr suppr =
833 	gen_suppr_spec_from_headers(opts.headers_dirs2, opts.header_files2);
834       if (suppr)
835 	{
836 	  if (opts.drop_private_types)
837 	    suppr->set_drops_artifact_from_ir(true);
838 	  supprs.push_back(suppr);
839 	}
840     }
841 
842   const suppressions_type& wl_suppr =
843       gen_suppr_spec_from_kernel_abi_whitelists(
844 	  opts.kernel_abi_whitelist_paths);
845 
846   supprs.insert(supprs.end(), wl_suppr.begin(), wl_suppr.end());
847 
848   add_read_context_suppressions(read_ctxt, supprs);
849 }
850 
851 /// Configure the abigail::xml_reacher::read_context based on the
852 /// relevant command-line options.
853 ///
854 /// @param ctxt the read context to configure.
855 ///
856 /// @param opts the command-line options to configure @p ctxt from.
857 static void
set_native_xml_reader_options(abigail::xml_reader::read_context & ctxt,const options & opts)858 set_native_xml_reader_options(abigail::xml_reader::read_context& ctxt,
859 			      const options& opts)
860 {
861   consider_types_not_reachable_from_public_interfaces(ctxt,
862 						      opts.show_all_types);
863 }
864 
865 /// Set the regex patterns describing the functions to drop from the
866 /// symbol table of a given corpus.
867 ///
868 /// @param opts the options to the regex patterns from.
869 ///
870 /// @param c the corpus to set the regex patterns into.
871 static void
set_corpus_keep_drop_regex_patterns(options & opts,corpus_sptr c)872 set_corpus_keep_drop_regex_patterns(options& opts, corpus_sptr c)
873 {
874   if (!opts.drop_fn_regex_patterns.empty())
875     {
876       vector<string>& v = opts.drop_fn_regex_patterns;
877       vector<string>& p = c->get_regex_patterns_of_fns_to_suppress();
878       p.assign(v.begin(), v.end());
879     }
880 
881   if (!opts.keep_fn_regex_patterns.empty())
882     {
883       vector<string>& v = opts.keep_fn_regex_patterns;
884       vector<string>& p = c->get_regex_patterns_of_fns_to_keep();
885       p.assign(v.begin(), v.end());
886     }
887 
888   if (!opts.drop_var_regex_patterns.empty())
889     {
890       vector<string>& v = opts.drop_var_regex_patterns;
891       vector<string>& p = c->get_regex_patterns_of_vars_to_suppress();
892       p.assign(v.begin(), v.end());
893     }
894 
895  if (!opts.keep_var_regex_patterns.empty())
896     {
897       vector<string>& v = opts.keep_var_regex_patterns;
898       vector<string>& p = c->get_regex_patterns_of_vars_to_keep();
899       p.assign(v.begin(), v.end());
900     }
901 }
902 
903 /// This function sets diff context options that are specific to
904 /// kernel module interface comparison.
905 ///
906 /// @param ctxt the diff context to consider.
907 static void
adjust_diff_context_for_kmidiff(diff_context & ctxt)908 adjust_diff_context_for_kmidiff(diff_context &ctxt)
909 {
910   ctxt.show_linkage_names(false);
911 }
912 
913 /// Convert options::di_root_paths{1,2} into
914 /// options::prepared_di_root_paths{1,2} which is the suitable type
915 /// format that the dwarf_reader expects.
916 ///
917 /// @param o the options to consider.
918 static void
prepare_di_root_paths(options & o)919 prepare_di_root_paths(options& o)
920 {
921   abigail::tools_utils::convert_char_stars_to_char_star_stars
922     (o.di_root_paths1, o.prepared_di_root_paths1);
923 
924   abigail::tools_utils::convert_char_stars_to_char_star_stars
925     (o.di_root_paths2, o.prepared_di_root_paths2);
926 }
927 
928 /// Emit an appropriate error message if necessary, given an error
929 /// code.
930 ///
931 /// To emit the appropriate error message the function might need to
932 /// access the context in which the (ELF) input file was being loaded,
933 /// if it's present.
934 ///
935 /// @param status_code the status code returned after trying to load
936 /// the input file.
937 ///
938 /// @param ctxt the context used to load the ELF file, if we still
939 /// have it.  If this is nil, then it's ignored.
940 ///
941 /// @param prog_name the name of the current program.  This is
942 /// important as it's used in the error message.
943 ///
944 /// @param input_file_name the name of the input file that we are
945 /// tryin to load.
946 ///
947 /// @param debug_info_dir1 if non nil, then this points to the path of
948 /// the root debug info directory of the first binary that we are
949 /// trying to load..  If nil, then it's ignored.
950 ///
951 /// @param debug_info_dir2 if non nil, then this points to the path of
952 /// the root debug info directory of the second binary that we are
953 /// trying to load..  If nil, then it's ignored.
954 ///
955 /// @return abigail::tools_utils::ABIDIFF_ERROR if an error was
956 /// detected, abigail::tools_utils::ABIDIFF_OK otherwise.
957 static abigail::tools_utils::abidiff_status
handle_error(abigail::elf_reader::status status_code,const abigail::dwarf_reader::read_context * ctxt,const string & prog_name,const options & opts)958 handle_error(abigail::elf_reader::status status_code,
959 	     const abigail::dwarf_reader::read_context* ctxt,
960 	     const string& prog_name,
961 	     const options& opts)
962 {
963   if (!(status_code & abigail::elf_reader::STATUS_OK))
964     {
965       emit_prefix(prog_name, cerr)
966 	<< "failed to read input file " << opts.file1 << "\n";
967 
968       if (status_code & abigail::elf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
969 	{
970 	  emit_prefix(prog_name, cerr) <<
971 	    "could not find the debug info\n";
972 	  {
973 	    if (opts.prepared_di_root_paths1.empty() == 0)
974 	      emit_prefix(prog_name, cerr)
975 		<< "Maybe you should consider using the "
976 		"--debug-info-dir1 option to tell me about the "
977 		"root directory of the debuginfo? "
978 		"(e.g, --debug-info-dir1 /usr/lib/debug)\n";
979 	    else
980 	      {
981 		emit_prefix(prog_name, cerr)
982 		  << "Maybe the root path to the debug information '";
983 		for (vector<char**>::const_iterator i
984 		       = opts.prepared_di_root_paths1.begin();
985 		     i != opts.prepared_di_root_paths1.end();
986 		     ++i)
987 		  {
988 		    if (i != opts.prepared_di_root_paths1.end())
989 		      cerr << ", ";
990 		    cerr << **i;
991 		  }
992 		cerr << "' is wrong?\n";
993 	      }
994 	  }
995 
996 	  {
997 	    if (opts.prepared_di_root_paths2.empty())
998 	      emit_prefix(prog_name, cerr)
999 		<< "Maybe you should consider using the "
1000 		"--debug-info-dir2 option to tell me about the "
1001 		"root directory of the debuginfo? "
1002 		"(e.g, --debug-info-dir2 /usr/lib/debug)\n";
1003 	    else
1004 	      {
1005 		emit_prefix(prog_name, cerr)
1006 		  << "Maybe the root path to the debug information '";
1007 		for (vector<char**>::const_iterator i
1008 		       = opts.prepared_di_root_paths2.begin();
1009 		     i != opts.prepared_di_root_paths2.end();
1010 		     ++i)
1011 		  {
1012 		    if (i != opts.prepared_di_root_paths2.end())
1013 		      cerr << ", ";
1014 		    cerr << **i;
1015 		  }
1016 		  cerr << "' is wrong?\n";
1017 	      }
1018 	  }
1019 	}
1020 
1021       if (status_code & abigail::elf_reader::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
1022 	{
1023 	  emit_prefix(prog_name, cerr)
1024 	    << "could not find the alternate debug info file";
1025 	  if (ctxt)
1026 	    {
1027 	      string alt_di_path;
1028 	      abigail::dwarf_reader::refers_to_alt_debug_info(*ctxt,
1029 							      alt_di_path);
1030 	      if (!alt_di_path.empty())
1031 		cerr << " at: " << alt_di_path;
1032 	    }
1033 	  cerr << "\n";
1034 	}
1035 
1036       if (status_code & abigail::elf_reader::STATUS_NO_SYMBOLS_FOUND)
1037 	emit_prefix(prog_name, cerr)
1038 	  << "could not find the ELF symbols in the file '"
1039 	  << opts.file1
1040 	  << "'\n";
1041 
1042       return abigail::tools_utils::ABIDIFF_ERROR;
1043     }
1044 
1045   return abigail::tools_utils::ABIDIFF_OK;
1046 }
1047 
1048 /// Emit an error message saying that the two files have incompatible
1049 /// format versions.
1050 ///
1051 /// @param file_path1 the first file path to consider.
1052 ///
1053 /// @param version1 the second version to consider.
1054 ///
1055 /// @param file_path2 the second file path to consider.
1056 ///
1057 /// @param version2 the second version to consider.
1058 ///
1059 /// @param prog_name the name of the current program.
1060 static void
emit_incompatible_format_version_error_message(const string & file_path1,const string & version1,const string & file_path2,const string & version2,const string & prog_name)1061 emit_incompatible_format_version_error_message(const string& file_path1,
1062 					       const string& version1,
1063 					       const string& file_path2,
1064 					       const string& version2,
1065 					       const string& prog_name)
1066 {
1067   emit_prefix(prog_name, cerr)
1068     << "incompatible format version between the two input files:\n"
1069     << "'" << file_path1 << "' (" << version1 << ")\n"
1070     << "and\n"
1071     << "'" << file_path2 << "' (" << version2 << ")\n";
1072 }
1073 
1074 int
main(int argc,char * argv[])1075 main(int argc, char* argv[])
1076 {
1077   options opts;
1078   if (!parse_command_line(argc, argv, opts))
1079     {
1080       emit_prefix(argv[0], cerr)
1081 	<< "unrecognized option: "
1082 	<< opts.wrong_option << "\n"
1083 	<< "try the --help option for more information\n";
1084       return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
1085 	      | abigail::tools_utils::ABIDIFF_ERROR);
1086     }
1087 
1088   if (opts.missing_operand)
1089     {
1090       emit_prefix(argv[0], cerr)
1091 	<< "missing operand to option: " << opts.wrong_option <<"\n"
1092 	<< "try the --help option for more information\n";
1093       return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
1094 	      | abigail::tools_utils::ABIDIFF_ERROR);
1095     }
1096 
1097   if (opts.display_usage)
1098     {
1099       display_usage(argv[0], cout);
1100       return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
1101 	      | abigail::tools_utils::ABIDIFF_ERROR);
1102     }
1103 
1104   if (opts.display_version)
1105     {
1106       emit_prefix(argv[0], cout)
1107 	<< abigail::tools_utils::get_library_version_string()
1108 	<< "\n";
1109       return 0;
1110     }
1111 
1112   prepare_di_root_paths(opts);
1113 
1114   if (!maybe_check_suppression_files(opts))
1115     return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
1116 	    | abigail::tools_utils::ABIDIFF_ERROR);
1117 
1118   abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
1119   if (!opts.file1.empty() && !opts.file2.empty())
1120     {
1121       if (!check_file(opts.file1, cerr))
1122 	return abigail::tools_utils::ABIDIFF_ERROR;
1123 
1124       if (!check_file(opts.file2, cerr))
1125 	return abigail::tools_utils::ABIDIFF_ERROR;
1126 
1127       abigail::tools_utils::file_type t1_type, t2_type;
1128 
1129       t1_type = guess_file_type(opts.file1);
1130       t2_type = guess_file_type(opts.file2);
1131 
1132       environment_sptr env(new environment);
1133 #ifdef WITH_DEBUG_SELF_COMPARISON
1134 	    if (opts.do_debug)
1135 	      env->self_comparison_debug_is_on(true);
1136 #endif
1137       translation_unit_sptr t1, t2;
1138       abigail::elf_reader::status c1_status =
1139 	abigail::elf_reader::STATUS_OK,
1140 	c2_status = abigail::elf_reader::STATUS_OK;
1141       corpus_sptr c1, c2;
1142       corpus_group_sptr g1, g2;
1143       bool files_suppressed = false;
1144 
1145       diff_context_sptr ctxt(new diff_context);
1146       set_diff_context_from_opts(ctxt, opts);
1147       suppressions_type& supprs = ctxt->suppressions();
1148       files_suppressed = (file_is_suppressed(opts.file1, supprs)
1149 			  || file_is_suppressed(opts.file2, supprs));
1150 
1151       if (files_suppressed)
1152 	// We don't have to compare anything because a user
1153 	// suppression specification file instructs us to avoid
1154 	// loading either one of the input files.
1155 	return abigail::tools_utils::ABIDIFF_OK;
1156 
1157       switch (t1_type)
1158 	{
1159 	case abigail::tools_utils::FILE_TYPE_UNKNOWN:
1160 	  emit_prefix(argv[0], cerr)
1161 	    << "Unknown content type for file " << opts.file1 << "\n";
1162 	  return abigail::tools_utils::ABIDIFF_ERROR;
1163 	  break;
1164 	case abigail::tools_utils::FILE_TYPE_NATIVE_BI:
1165 	  t1 = abigail::xml_reader::read_translation_unit_from_file(opts.file1,
1166 								    env.get());
1167 	  break;
1168 	case abigail::tools_utils::FILE_TYPE_ELF: // fall through
1169 	case abigail::tools_utils::FILE_TYPE_AR:
1170 	  {
1171 #ifdef WITH_CTF
1172             if (opts.use_ctf)
1173               {
1174                 abigail::ctf_reader::read_context_sptr ctxt
1175                   = abigail::ctf_reader::create_read_context(opts.file1,
1176                                                              opts.prepared_di_root_paths1,
1177                                                              env.get());
1178                 ABG_ASSERT(ctxt);
1179                 c1 = abigail::ctf_reader::read_corpus(ctxt.get(),
1180                                                       c1_status);
1181               }
1182             else
1183 #endif
1184               {
1185                 abigail::dwarf_reader::read_context_sptr ctxt =
1186                   abigail::dwarf_reader::create_read_context
1187                   (opts.file1, opts.prepared_di_root_paths1,
1188                    env.get(), /*read_all_types=*/opts.show_all_types,
1189                    opts.linux_kernel_mode);
1190                 assert(ctxt);
1191 
1192                 abigail::dwarf_reader::set_show_stats(*ctxt, opts.show_stats);
1193                 set_suppressions(*ctxt, opts);
1194                 abigail::dwarf_reader::set_do_log(*ctxt, opts.do_log);
1195                 c1 = abigail::dwarf_reader::read_corpus_from_elf(*ctxt, c1_status);
1196                 if (!c1
1197                     || (opts.fail_no_debug_info
1198                         && (c1_status & STATUS_ALT_DEBUG_INFO_NOT_FOUND)
1199                         && (c1_status & STATUS_DEBUG_INFO_NOT_FOUND)))
1200                   return handle_error(c1_status, ctxt.get(),
1201                                       argv[0], opts);
1202               }
1203 	  }
1204 	  break;
1205 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
1206 	  {
1207 	    abigail::xml_reader::read_context_sptr ctxt =
1208 	      abigail::xml_reader::create_native_xml_read_context(opts.file1,
1209 								  env.get());
1210 	    assert(ctxt);
1211 	    set_suppressions(*ctxt, opts);
1212 	    set_native_xml_reader_options(*ctxt, opts);
1213 	    c1 = abigail::xml_reader::read_corpus_from_input(*ctxt);
1214 	    if (!c1)
1215 	      return handle_error(c1_status, /*ctxt=*/0,
1216 				  argv[0], opts);
1217 	  }
1218 	  break;
1219 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
1220 	  {
1221 	    abigail::xml_reader::read_context_sptr ctxt =
1222 	      abigail::xml_reader::create_native_xml_read_context(opts.file1,
1223 								  env.get());
1224 	    assert(ctxt);
1225 	    set_suppressions(*ctxt, opts);
1226 	    set_native_xml_reader_options(*ctxt, opts);
1227 	    g1 = abigail::xml_reader::read_corpus_group_from_input(*ctxt);
1228 	    if (!g1)
1229 	      return handle_error(c1_status, /*ctxt=*/0,
1230 				  argv[0], opts);
1231 	  }
1232 	  break;
1233 	case abigail::tools_utils::FILE_TYPE_RPM:
1234 	case abigail::tools_utils::FILE_TYPE_SRPM:
1235 	case abigail::tools_utils::FILE_TYPE_DEB:
1236 	case abigail::tools_utils::FILE_TYPE_DIR:
1237 	case abigail::tools_utils::FILE_TYPE_TAR:
1238 	  break;
1239 	}
1240 
1241       switch (t2_type)
1242 	{
1243 	case abigail::tools_utils::FILE_TYPE_UNKNOWN:
1244 	  emit_prefix(argv[0], cerr)
1245 	    << "Unknown content type for file " << opts.file2 << "\n";
1246 	  return abigail::tools_utils::ABIDIFF_ERROR;
1247 	  break;
1248 	case abigail::tools_utils::FILE_TYPE_NATIVE_BI:
1249 	  t2 = abigail::xml_reader::read_translation_unit_from_file(opts.file2,
1250 								    env.get());
1251 	  break;
1252 	case abigail::tools_utils::FILE_TYPE_ELF: // Fall through
1253 	case abigail::tools_utils::FILE_TYPE_AR:
1254 	  {
1255 #ifdef WITH_CTF
1256             if (opts.use_ctf)
1257               {
1258                 abigail::ctf_reader::read_context_sptr ctxt
1259                   = abigail::ctf_reader::create_read_context(opts.file2,
1260                                                              opts.prepared_di_root_paths2,
1261                                                              env.get());
1262                 ABG_ASSERT(ctxt);
1263                 c2 = abigail::ctf_reader::read_corpus(ctxt.get(),
1264                                                       c2_status);
1265               }
1266             else
1267 #endif
1268               {
1269                 abigail::dwarf_reader::read_context_sptr ctxt =
1270                   abigail::dwarf_reader::create_read_context
1271                   (opts.file2, opts.prepared_di_root_paths2,
1272                    env.get(), /*read_all_types=*/opts.show_all_types,
1273                    opts.linux_kernel_mode);
1274                 assert(ctxt);
1275                 abigail::dwarf_reader::set_show_stats(*ctxt, opts.show_stats);
1276                 abigail::dwarf_reader::set_do_log(*ctxt, opts.do_log);
1277                 set_suppressions(*ctxt, opts);
1278 
1279                 c2 = abigail::dwarf_reader::read_corpus_from_elf(*ctxt, c2_status);
1280                 if (!c2
1281                     || (opts.fail_no_debug_info
1282                         && (c2_status & STATUS_ALT_DEBUG_INFO_NOT_FOUND)
1283                         && (c2_status & STATUS_DEBUG_INFO_NOT_FOUND)))
1284                   return handle_error(c2_status, ctxt.get(), argv[0], opts);
1285               }
1286 	  }
1287 	  break;
1288 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
1289 	  {
1290 	    abigail::xml_reader::read_context_sptr ctxt =
1291 	      abigail::xml_reader::create_native_xml_read_context(opts.file2,
1292 								  env.get());
1293 	    assert(ctxt);
1294 	    set_suppressions(*ctxt, opts);
1295 	    set_native_xml_reader_options(*ctxt, opts);
1296 	    c2 = abigail::xml_reader::read_corpus_from_input(*ctxt);
1297 	    if (!c2)
1298 	      return handle_error(c2_status, /*ctxt=*/0, argv[0], opts);
1299 
1300 	  }
1301 	  break;
1302 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
1303 	  {
1304 	    abigail::xml_reader::read_context_sptr ctxt =
1305 	      abigail::xml_reader::create_native_xml_read_context(opts.file2,
1306 								  env.get());
1307 	    assert(ctxt);
1308 	    set_suppressions(*ctxt, opts);
1309 	    set_native_xml_reader_options(*ctxt, opts);
1310 	    g2 = abigail::xml_reader::read_corpus_group_from_input(*ctxt);
1311 	    if (!g2)
1312 	      return handle_error(c2_status, /*ctxt=*/0, argv[0], opts);
1313 	  }
1314 	  break;
1315 	case abigail::tools_utils::FILE_TYPE_RPM:
1316 	case abigail::tools_utils::FILE_TYPE_SRPM:
1317 	case abigail::tools_utils::FILE_TYPE_DEB:
1318 	case abigail::tools_utils::FILE_TYPE_DIR:
1319 	case abigail::tools_utils::FILE_TYPE_TAR:
1320 	  break;
1321 	}
1322 
1323       if (!!c1 != !!c2
1324 	  || !!t1 != !!t2
1325 	  || !!g1 != !!g2)
1326 	{
1327 	  emit_prefix(argv[0], cerr)
1328 	    << "the two input should be of the same kind\n";
1329 	  return abigail::tools_utils::ABIDIFF_ERROR;
1330 	}
1331 
1332       if (opts.no_arch)
1333 	{
1334 	  if (c1)
1335 	    c1->set_architecture_name("");
1336 	  if (c2)
1337 	    c2->set_architecture_name("");
1338 	}
1339       if (opts.no_corpus)
1340 	{
1341 	  if (c1)
1342 	    c1->set_path("");
1343 	  if (c2)
1344 	    c2->set_path("");
1345 	}
1346 
1347       if (t1)
1348 	{
1349 	  translation_unit_diff_sptr diff = compute_diff(t1, t2, ctxt);
1350 	  if (diff->has_changes())
1351 	    diff->report(cout);
1352 	}
1353       else if (c1)
1354 	{
1355 	  if (opts.show_symtabs)
1356 	    {
1357 	      display_symtabs(c1, c2, cout);
1358 	      return abigail::tools_utils::ABIDIFF_OK;
1359 	    }
1360 
1361 	  const auto c1_version = c1->get_format_major_version_number();
1362 	  const auto c2_version = c2->get_format_major_version_number();
1363 	  if (c1_version != c2_version)
1364 	    {
1365 	      emit_incompatible_format_version_error_message(opts.file1,
1366 							     c1_version,
1367 							     opts.file2,
1368 							     c2_version,
1369 							     argv[0]);
1370 	      return abigail::tools_utils::ABIDIFF_ERROR;
1371 	    }
1372 
1373 	  set_corpus_keep_drop_regex_patterns(opts, c1);
1374 	  set_corpus_keep_drop_regex_patterns(opts, c2);
1375 
1376 	  corpus_diff_sptr diff = compute_diff(c1, c2, ctxt);
1377 
1378 	  if (diff->has_net_changes())
1379 	    status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
1380 
1381 	  if (diff->has_incompatible_changes())
1382 	    status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
1383 
1384 	  if (diff->has_changes())
1385 	    diff->report(cout);
1386 	}
1387       else if (g1)
1388 	{
1389 	  if (opts.show_symtabs)
1390 	    {
1391 	      display_symtabs(c1, c2, cout);
1392 	      return abigail::tools_utils::ABIDIFF_OK;
1393 	    }
1394 
1395 	  const auto g1_version = g1->get_format_major_version_number();
1396 	  const auto g2_version = g2->get_format_major_version_number();
1397 	  if (g1_version != g2_version)
1398 	    {
1399 	      emit_incompatible_format_version_error_message(opts.file1,
1400 							     g1_version,
1401 							     opts.file2,
1402 							     g2_version,
1403 							     argv[0]);
1404 	      return abigail::tools_utils::ABIDIFF_ERROR;
1405 	    }
1406 
1407 	  adjust_diff_context_for_kmidiff(*ctxt);
1408 	  corpus_diff_sptr diff = compute_diff(g1, g2, ctxt);
1409 
1410 	  if (diff->has_net_changes())
1411 	    status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
1412 
1413 	  if (diff->has_incompatible_changes())
1414 	    status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
1415 
1416 	  if (diff->has_changes())
1417 	    diff->report(cout);
1418 
1419 	}
1420       else
1421 	status = abigail::tools_utils::ABIDIFF_ERROR;
1422     }
1423 
1424   return status;
1425 }
1426 
1427 #ifdef __ABIGAIL_IN_THE_DEBUGGER__
1428 
1429 /// Emit a textual representation of a given @ref corpus_diff tree to
1430 /// stdout.
1431 ///
1432 /// This is useful when debugging this program.
1433 ///
1434 /// @param diff_tree the diff tree to emit a textual representation
1435 /// for.
1436 void
print_diff_tree(abigail::comparison::corpus_diff * diff_tree)1437 print_diff_tree(abigail::comparison::corpus_diff* diff_tree)
1438 {
1439   print_diff_tree(diff_tree, std::cout);
1440 }
1441 
1442 /// Emit a textual representation of a given @ref corpus_diff tree to
1443 /// stdout.
1444 ///
1445 /// This is useful when debugging this program.
1446 ///
1447 /// @param diff_tree the diff tree to emit a textual representation
1448 /// for.
1449 void
print_diff_tree(abigail::comparison::corpus_diff_sptr diff_tree)1450 print_diff_tree(abigail::comparison::corpus_diff_sptr diff_tree)
1451 {
1452   print_diff_tree(diff_tree, std::cout);
1453 }
1454 
1455 /// Emit a textual representation of a given @ref corpus_diff tree to
1456 /// stdout.
1457 ///
1458 /// This is useful when debugging this program.
1459 ///
1460 /// @param diff_tree the diff tree to emit a textual representation
1461 /// for.
1462 void
print_diff_tree(abigail::comparison::diff_sptr diff_tree)1463 print_diff_tree(abigail::comparison::diff_sptr diff_tree)
1464 {
1465   print_diff_tree(diff_tree.get(), std::cout);
1466 }
1467 
1468 /// Emit a textual representation of a given @ref diff tree to
1469 /// stdout.
1470 ///
1471 /// This is useful when debugging this program.
1472 ///
1473 /// @param diff_tree the diff tree to emit a textual representation
1474 /// for.
1475 void
print_diff_tree(abigail::comparison::diff * diff_tree)1476 print_diff_tree(abigail::comparison::diff* diff_tree)
1477 {
1478   print_diff_tree(diff_tree, std::cout);
1479 }
1480 #endif // __ABIGAIL_IN_THE_DEBUGGER__
1481