• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2023 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 <set>
17 #include "abg-config.h"
18 #include "abg-comp-filter.h"
19 #include "abg-suppression.h"
20 #include "abg-tools-utils.h"
21 #include "abg-reader.h"
22 #include "abg-dwarf-reader.h"
23 #ifdef WITH_CTF
24 #include "abg-ctf-reader.h"
25 #endif
26 
27 #ifdef WITH_BTF
28 #include "abg-btf-reader.h"
29 #endif
30 
31 using std::vector;
32 using std::set;
33 using std::string;
34 using std::ostream;
35 using std::cout;
36 using std::cerr;
37 using std::shared_ptr;
38 using abg_compat::optional;
39 using namespace abigail;
40 using abigail::ir::environment;
41 using abigail::ir::environment_sptr;
42 using abigail::translation_unit;
43 using abigail::translation_unit_sptr;
44 using abigail::corpus_sptr;
45 using abigail::corpus_group_sptr;
46 using abigail::comparison::translation_unit_diff_sptr;
47 using abigail::comparison::corpus_diff;
48 using abigail::comparison::corpus_diff_sptr;
49 using abigail::comparison::compute_diff;
50 using abigail::comparison::get_default_harmless_categories_bitmap;
51 using abigail::comparison::get_default_harmful_categories_bitmap;
52 using abigail::suppr::suppression_sptr;
53 using abigail::suppr::suppressions_type;
54 using abigail::suppr::read_suppressions;
55 
56 using abigail::tools_utils::emit_prefix;
57 using abigail::tools_utils::check_file;
58 using abigail::tools_utils::guess_file_type;
59 using abigail::tools_utils::gen_suppr_spec_from_headers;
60 using abigail::tools_utils::gen_suppr_spec_from_kernel_abi_whitelists;
61 using abigail::tools_utils::load_default_system_suppressions;
62 using abigail::tools_utils::load_default_user_suppressions;
63 using abigail::tools_utils::abidiff_status;
64 using abigail::tools_utils::create_best_elf_based_reader;
65 using abigail::tools_utils::stick_corpus_and_dependencies_into_corpus_group;
66 using abigail::tools_utils::stick_corpus_and_binaries_into_corpus_group;
67 using abigail::tools_utils::add_dependencies_into_corpus_group;
68 using abigail::tools_utils::get_dependencies;
69 
70 using namespace abigail;
71 
72 struct options
73 {
74   bool display_usage;
75   bool display_version;
76   bool missing_operand;
77   string		wrong_option;
78   string		file1;
79   string		file2;
80   vector<string>	suppression_paths;
81   vector<string>	kernel_abi_whitelist_paths;
82   vector<string>	drop_fn_regex_patterns;
83   vector<string>	drop_var_regex_patterns;
84   vector<string>	keep_fn_regex_patterns;
85   vector<string>	keep_var_regex_patterns;
86   vector<string>	headers_dirs1;
87   vector<string>        header_files1;
88   vector<string>	headers_dirs2;
89   vector<string>        header_files2;
90   bool			drop_private_types;
91   optional<bool>	exported_interfaces_only;
92   bool			linux_kernel_mode;
93   bool			no_default_supprs;
94   bool			no_arch;
95   bool			no_corpus;
96   bool			ignore_soname;
97   bool			leaf_changes_only;
98   bool			fail_no_debug_info;
99   bool			show_hexadecimal_values;
100   bool			show_offsets_sizes_in_bits;
101   bool			show_relative_offset_changes;
102   bool			show_stats_only;
103   bool			show_symtabs;
104   bool			show_deleted_fns;
105   bool			show_changed_fns;
106   bool			show_added_fns;
107   bool			show_added_syms;
108   bool			show_all_fns;
109   bool			show_deleted_vars;
110   bool			show_changed_vars;
111   bool			show_added_vars;
112   bool			show_all_vars;
113   bool			show_all_types;
114   bool			show_linkage_names;
115   bool			show_locs;
116   bool			show_harmful_changes;
117   bool			show_harmless_changes;
118   bool			show_redundant_changes;
119   bool			show_symbols_not_referenced_by_debug_info;
120   bool			show_impacted_interfaces;
121   bool			assume_odr_for_cplusplus;
122   bool			leverage_dwarf_factorization;
123   bool			perform_change_categorization;
124   bool			follow_dependencies;
125   bool			list_dependencies;
126   bool			dump_diff_tree;
127   bool			show_stats;
128   bool			do_log;
129 #ifdef WITH_DEBUG_SELF_COMPARISON
130   bool			do_debug_self_comparison;
131 #endif
132 #ifdef WITH_DEBUG_TYPE_CANONICALIZATION
133   bool			do_debug_type_canonicalization;
134 #endif
135 #ifdef WITH_CTF
136   bool			use_ctf;
137 #endif
138 #ifdef WITH_BTF
139   bool			use_btf;
140 #endif
141   vector<char*> di_root_paths1;
142   vector<char*> di_root_paths2;
143   vector<char**> prepared_di_root_paths1;
144   vector<char**> prepared_di_root_paths2;
145   vector<string> added_bins_dirs1;
146   vector<string> added_bins_dirs2;
147   vector<string> added_bins1;
148   vector<string> added_bins2;
149 
optionsoptions150   options()
151     : display_usage(),
152       display_version(),
153       missing_operand(),
154       drop_private_types(false),
155       linux_kernel_mode(true),
156       no_default_supprs(),
157       no_arch(),
158       no_corpus(),
159       ignore_soname(false),
160       leaf_changes_only(),
161       fail_no_debug_info(),
162       show_hexadecimal_values(),
163       show_offsets_sizes_in_bits(true),
164       show_relative_offset_changes(true),
165       show_stats_only(),
166       show_symtabs(),
167       show_deleted_fns(),
168       show_changed_fns(),
169       show_added_fns(),
170       show_added_syms(true),
171       show_all_fns(true),
172       show_deleted_vars(),
173       show_changed_vars(),
174       show_added_vars(),
175       show_all_vars(true),
176       show_all_types(false),
177       show_linkage_names(true),
178       show_locs(true),
179       show_harmful_changes(true),
180       show_harmless_changes(),
181       show_redundant_changes(),
182       show_symbols_not_referenced_by_debug_info(true),
183       show_impacted_interfaces(),
184       assume_odr_for_cplusplus(true),
185       leverage_dwarf_factorization(true),
186       perform_change_categorization(true),
187       follow_dependencies(),
188       list_dependencies(),
189       dump_diff_tree(),
190       show_stats(),
191       do_log()
192 #ifdef WITH_DEBUG_SELF_COMPARISON
193     ,
194       do_debug_self_comparison()
195 #endif
196 #ifdef WITH_DEBUG_TYPE_CANONICALIZATION
197     ,
198       do_debug_type_canonicalization()
199 #endif
200 #ifdef WITH_CTF
201     ,
202       use_ctf()
203 #endif
204 #ifdef WITH_BTF
205     ,
206       use_btf()
207 #endif
208   {}
209 
~optionsoptions210   ~options()
211   {
212     for (vector<char*>::iterator i = di_root_paths1.begin();
213 	 i != di_root_paths1.end();
214 	 ++i)
215       free(*i);
216 
217     for (vector<char*>::iterator i = di_root_paths2.begin();
218 	 i != di_root_paths2.end();
219 	 ++i)
220       free(*i);
221 
222     prepared_di_root_paths1.clear();
223     prepared_di_root_paths2.clear();
224   }
225 };//end struct options;
226 
227 static void
display_usage(const string & prog_name,ostream & out)228 display_usage(const string& prog_name, ostream& out)
229 {
230   emit_prefix(prog_name, out)
231     << "usage: " << prog_name << " [options] [<file1> <file2>]\n"
232     << " where options can be:\n"
233     << " --help|-h  display this message\n "
234     << " --version|-v  display program version information and exit\n"
235     << " --debug-info-dir1|--d1 <path> the root for the debug info of file1\n"
236     << " --debug-info-dir2|--d2 <path> the root for the debug info of file2\n"
237     << " --headers-dir1|--hd1 <path>  the path to headers of file1\n"
238     << " --header-file1|--hf1 <path>  the path to one header of file1\n"
239     << " --headers-dir2|--hd2 <path>  the path to headers of file2\n"
240     << " --header-file2|--hf2 <path>  the path to one header of file2\n"
241     << " --added-binaries-dir1  the path to the dependencies of file1\n"
242     << " --added-binaries-dir2  the path to the dependencies of file2\n"
243     << " --add-binaries1 <bin1,bin2,.>. build corpus groups with "
244     "extra binaries added to the first one and compare them\n"
245     << " --add-binaries2 <bin1,bin2,..> build corpus groups with "
246     "extra binaries added to the second one and compare them\n"
247     << " --follow-dependencies|--fdeps build corpus groups with the "
248     "dependencies of the input files\n"
249     << " --list-dependencies|--ldeps show the dependencies of the input files\n"
250     << " --drop-private-types  drop private types from "
251     "internal representation\n"
252     << "  --exported-interfaces-only  analyze exported interfaces only\n"
253     << "  --allow-non-exported-interfaces  analyze interfaces that "
254     "might not be exported\n"
255     << " --no-linux-kernel-mode  don't consider the input binaries as "
256        "linux kernel binaries\n"
257     << " --kmi-whitelist|-w  path to a "
258        "linux kernel abi whitelist\n"
259     << " --stat  only display the diff stats\n"
260     << " --symtabs  only display the symbol tables of the corpora\n"
261     << " --no-default-suppression  don't load any "
262        "default suppression specification\n"
263     << " --no-architecture  do not take architecture in account\n"
264     << " --no-corpus-path  do not take the path to the corpora into account\n"
265     << " --ignore-soname  do not take the SONAMEs into account\n"
266     << " --fail-no-debug-info  bail out if no debug info was found\n"
267     << " --leaf-changes-only|-l  only show leaf changes, "
268     "so no change impact analysis (implies --redundant)\n"
269     << " --deleted-fns  display deleted public functions\n"
270     << " --changed-fns  display changed public functions\n"
271     << " --added-fns  display added public functions\n"
272     << " --deleted-vars  display deleted global public variables\n"
273     << " --changed-vars  display changed global public variables\n"
274     << " --added-vars  display added global public variables\n"
275     << " --non-reachable-types|-t  consider types non reachable"
276     " from public interfaces\n"
277     << " --no-added-syms  do not display added functions or variables\n"
278     << " --no-linkage-name  do not display linkage names of "
279     "added/removed/changed\n"
280     << " --no-unreferenced-symbols  do not display changes "
281     "about symbols not referenced by debug info\n"
282     << " --no-show-locs  do now show location information\n"
283     << " --show-bytes  show size and offsets in bytes\n"
284     << " --show-bits  show size and offsets in bits\n"
285     << " --show-hex  show size and offset in hexadecimal\n"
286     << " --show-dec  show size and offset in decimal\n"
287     << " --no-show-relative-offset-changes  do not show relative"
288     " offset changes\n"
289     << " --suppressions|--suppr <path> specify a suppression file\n"
290     << " --drop <regex>  drop functions and variables matching a regexp\n"
291     << " --drop-fn <regex> drop functions matching a regexp\n"
292     << " --drop-var <regex> drop variables matching a regexp\n"
293     << " --keep <regex>  keep only functions and variables matching a regex\n"
294     << " --keep-fn <regex>  keep only functions matching a regex\n"
295     << " --keep-var  <regex>  keep only variables matching a regex\n"
296     << " --harmless  display the harmless changes\n"
297     << " --no-harmful  do not display the harmful changes\n"
298     << " --redundant  display redundant changes\n"
299     << " --no-redundant  do not display redundant changes "
300     "(this is the default)\n"
301     << " --impacted-interfaces  display interfaces impacted by leaf changes\n"
302     << " --no-leverage-dwarf-factorization  do not use DWZ optimisations to "
303     "speed-up the analysis of the binary\n"
304     << " --no-change-categorization | -x don't perform categorization "
305     "of changes, for speed purposes\n"
306     << " --no-assume-odr-for-cplusplus  do not assume the ODR to speed-up the "
307     "analysis of the binary\n"
308     << " --dump-diff-tree  emit a debug dump of the internal diff tree to "
309     "the error output stream\n"
310     <<  " --stats  show statistics about various internal stuff\n"
311 #ifdef WITH_CTF
312     << " --ctf use CTF instead of DWARF in ELF files\n"
313 #endif
314 #ifdef WITH_BTF
315     << " --btf use BTF instead of DWARF in ELF files\n"
316 #endif
317 #ifdef WITH_DEBUG_SELF_COMPARISON
318     << " --debug-self-comparison debug the process of comparing "
319     "an ABI corpus against itself"
320 #endif
321 #ifdef WITH_DEBUG_TYPE_CANONICALIZATION
322     << " --debug-tc debug the type canonicalization process"
323 #endif
324     << " --verbose show verbose messages about internal stuff\n";
325 }
326 
327 /// Parse the command line and set the options accordingly.
328 ///
329 /// @param argc the number of words on the command line
330 ///
331 /// @param argv the command line, which is an array of words.
332 ///
333 /// @param opts the options data structure.  This is set by the
334 /// function iff it returns true.
335 ///
336 /// @return true if the command line could be parsed and opts filed,
337 /// false otherwise.
338 bool
parse_command_line(int argc,char * argv[],options & opts)339 parse_command_line(int argc, char* argv[], options& opts)
340 {
341   if (argc < 2)
342     return false;
343 
344   for (int i = 1; i < argc; ++i)
345     {
346       if (argv[i][0] != '-')
347 	{
348 	  if (opts.file1.empty())
349 	    opts.file1 = argv[i];
350 	  else if (opts.file2.empty())
351 	    opts.file2 = argv[i];
352 	  else
353 	    return false;
354 	}
355       else if (!strcmp(argv[i], "--version")
356 	       || !strcmp(argv[i], "-v"))
357 	{
358 	  opts.display_version = true;
359 	  return true;
360 	}
361       else if (!strcmp(argv[i], "--debug-info-dir1")
362 	       || !strcmp(argv[i], "--d1"))
363 	{
364 	  int j = i + 1;
365 	  if (j >= argc)
366 	    {
367 	      opts.missing_operand = true;
368 	      opts.wrong_option = argv[i];
369 	      return true;
370 	    }
371 	  // elfutils wants the root path to the debug info to be
372 	  // absolute.
373 	  opts.di_root_paths1.push_back
374 	    (abigail::tools_utils::make_path_absolute_to_be_freed(argv[j]));
375 	  ++i;
376 	}
377       else if (!strcmp(argv[i], "--debug-info-dir2")
378 	       || !strcmp(argv[i], "--d2"))
379 	{
380 	  int j = i + 1;
381 	  if (j >= argc)
382 	    {
383 	      opts.missing_operand = true;
384 	      opts.wrong_option = argv[i];
385 	      return true;
386 	    }
387 	  // elfutils wants the root path to the debug info to be
388 	  // absolute.
389 	  opts.di_root_paths2.push_back
390 	    (abigail::tools_utils::make_path_absolute_to_be_freed(argv[j]));
391 	  ++i;
392 	}
393       else if (!strcmp(argv[i], "--headers-dir1")
394 	       || !strcmp(argv[i], "--hd1"))
395 	{
396 	  int j = i + 1;
397 	  if (j >= argc)
398 	    {
399 	      opts.missing_operand = true;
400 	      opts.wrong_option = argv[i];
401 	      return true;
402 	    }
403 	  // The user can specify several header files directories for
404 	  // the first binary.
405 	  opts.headers_dirs1.push_back(argv[j]);
406 	  ++i;
407 	}
408       else if (!strcmp(argv[i], "--header-file1")
409 	       || !strcmp(argv[i], "--hf1"))
410 	{
411 	  int j = i + 1;
412 	  if (j >= argc)
413 	    {
414 	      opts.missing_operand = true;
415 	      opts.wrong_option = argv[i];
416 	      return true;
417 	    }
418 	  opts.header_files1.push_back(argv[j]);
419 	  ++i;
420 	}
421       else if (!strcmp(argv[i], "--headers-dir2")
422 	       || !strcmp(argv[i], "--hd2"))
423 	{
424 	  int j = i + 1;
425 	  if (j >= argc)
426 	    {
427 	      opts.missing_operand = true;
428 	      opts.wrong_option = argv[i];
429 	      return true;
430 	    }
431 	  // The user can specify several header files directories for
432 	  // the first binary.
433 	  opts.headers_dirs2.push_back(argv[j]);
434 	  ++i;
435 	}
436       else if (!strcmp(argv[i], "--header-file2")
437 	       || !strcmp(argv[i], "--hf2"))
438 	{
439 	  int j = i + 1;
440 	  if (j >= argc)
441 	    {
442 	      opts.missing_operand = true;
443 	      opts.wrong_option = argv[i];
444 	      return true;
445 	    }
446 	  opts.header_files2.push_back(argv[j]);
447 	  ++i;
448 	}
449       else if (!strcmp(argv[i], "--follow-dependencies")
450 	       || !strcmp(argv[i], "--fdeps"))
451 	opts.follow_dependencies = true;
452       else if (!strcmp(argv[i], "--list-dependencies")
453 	       || !strcmp(argv[i], "--ldeps"))
454 	opts.list_dependencies = true;
455       else if (!strcmp(argv[i], "--added-binaries-dir1")
456 	       || !strcmp(argv[i], "--abd1"))
457 	{
458 	  int j = i + 1;
459 	  if (j >= argc)
460 	    {
461 	      opts.missing_operand = true;
462 	      opts.wrong_option = argv[i];
463 	      return true;
464 	    }
465 	  opts.added_bins_dirs1.push_back(argv[j]);
466 	  ++i;
467 	}
468       else if (!strcmp(argv[i], "--added-binaries-dir2")
469 	       || !strcmp(argv[i], "--abd2"))
470 	{
471 	  int j = i + 1;
472 	  if (j >= argc)
473 	    {
474 	      opts.missing_operand = true;
475 	      opts.wrong_option = argv[i];
476 	      return true;
477 	    }
478 	  opts.added_bins_dirs2.push_back(argv[j]);
479 	  ++i;
480 	}
481       else if (!strncmp(argv[i], "--add-binaries1=",
482 			strlen("--add-binaries1=")))
483 	tools_utils::get_comma_separated_args_of_option(argv[i],
484 							"--add-binaries1=",
485 							opts.added_bins1);
486       else if (!strcmp(argv[i], "--add-binaries1"))
487 	{
488 	  int j = i + 1;
489 	  if (j >= argc)
490 	    {
491 	      opts.missing_operand = true;
492 	      opts.wrong_option = argv[i];
493 	      return true;
494 	    }
495 	  string s = argv[j];
496 	  if (s.find(','))
497 	    tools_utils::split_string(s, ",", opts.added_bins1);
498 	  else
499 	    opts.added_bins1.push_back(s);
500 	  ++i;
501 	}
502       else if (!strncmp(argv[i], "--add-binaries2=",
503 			strlen("--add-binaries2=")))
504 	tools_utils::get_comma_separated_args_of_option(argv[i],
505 							"--add-binaries2=",
506 							opts.added_bins2);
507       else if (!strcmp(argv[i], "--add-binaries2"))
508 	{
509 	  int j = i + 1;
510 	  if (j >= argc)
511 	    {
512 	      opts.missing_operand = true;
513 	      opts.wrong_option = argv[i];
514 	      return true;
515 	    }
516 	  string s = argv[j];
517 	  if (s.find(','))
518 	    tools_utils::split_string(s, ",", opts.added_bins2);
519 	  else
520 	    opts.added_bins2.push_back(s);
521 	  ++i;
522 	}
523       else if (!strcmp(argv[i], "--kmi-whitelist")
524 	       || !strcmp(argv[i], "-w"))
525 	{
526 	  int j = i + 1;
527 	  if (j >= argc)
528 	    {
529 	      opts.missing_operand = true;
530 	      opts.wrong_option = argv[i];
531 	      return true;
532 	    }
533 	  opts.kernel_abi_whitelist_paths.push_back(argv[j]);
534 	  ++i;
535 	}
536       else if (!strcmp(argv[i], "--stat"))
537 	opts.show_stats_only = true;
538       else if (!strcmp(argv[i], "--symtabs"))
539 	opts.show_symtabs = true;
540       else if (!strcmp(argv[i], "--help")
541 	       || !strcmp(argv[i], "-h"))
542 	{
543 	  opts.display_usage = true;
544 	  return true;
545 	}
546       else if (!strcmp(argv[i], "--drop-private-types"))
547 	opts.drop_private_types = true;
548       else if (!strcmp(argv[i], "--exported-interfaces-only"))
549 	opts.exported_interfaces_only = true;
550       else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
551 	opts.exported_interfaces_only = false;
552       else if (!strcmp(argv[i], "--no-linux-kernel-mode"))
553 	opts.linux_kernel_mode = false;
554       else if (!strcmp(argv[i], "--no-default-suppression"))
555 	opts.no_default_supprs = true;
556       else if (!strcmp(argv[i], "--no-architecture"))
557 	opts.no_arch = true;
558       else if (!strcmp(argv[i], "--no-corpus-path"))
559 	opts.no_corpus = true;
560       else if (!strcmp(argv[i], "--ignore-soname"))
561 	opts.ignore_soname = true;
562       else if (!strcmp(argv[i], "--fail-no-debug-info"))
563 	opts.fail_no_debug_info = true;
564       else if (!strcmp(argv[i], "--leaf-changes-only")
565 	       ||!strcmp(argv[i], "-l"))
566 	opts.leaf_changes_only = true;
567       else if (!strcmp(argv[i], "--deleted-fns"))
568 	{
569 	  opts.show_deleted_fns = true;
570 	  opts.show_all_fns = false;
571 	  opts.show_all_vars = false;
572 	}
573       else if (!strcmp(argv[i], "--changed-fns"))
574 	{
575 	  opts.show_changed_fns = true;
576 	  opts.show_all_fns = false;
577 	  opts.show_all_vars = false;
578 	}
579       else if (!strcmp(argv[i], "--added-fns"))
580 	{
581 	  opts.show_added_fns = true;
582 	  opts.show_all_fns = false;
583 	  opts.show_all_vars = false;
584 	}
585       else if (!strcmp(argv[i], "--deleted-vars"))
586 	{
587 	  opts.show_deleted_vars = true;
588 	  opts.show_all_fns = false;
589 	  opts.show_all_vars = false;
590 	}
591       else if (!strcmp(argv[i], "--changed-vars"))
592 	{
593 	  opts.show_changed_vars = true;
594 	  opts.show_all_fns = false;
595 	  opts.show_all_vars = false;
596 	}
597       else if (!strcmp(argv[i], "--added-vars"))
598 	{
599 	  opts.show_added_vars = true;
600 	  opts.show_all_fns = false;
601 	  opts.show_all_vars = false;
602 	}
603       else if (!strcmp(argv[i], "--non-reachable-types")
604 	       || !strcmp(argv[i], "-t"))
605 	  opts.show_all_types = true;
606       else if (!strcmp(argv[i], "--no-added-syms"))
607 	{
608 	  opts.show_added_syms = false;
609 	  opts.show_added_vars = false;
610 	  opts.show_added_fns = false;
611 
612 	  // If any of the {changed,deleted}_{vars,fns} is already
613 	  // specified, --no-added-syms has no further effect.  If it
614 	  // is the only option specified (as of the time of parsing
615 	  // it), it shall mean "show everything, except added vars,
616 	  // fns and unreferenced symbols.
617 	  if (!(opts.show_changed_fns
618 		|| opts.show_changed_vars
619 		|| opts.show_deleted_fns
620 		|| opts.show_deleted_vars))
621 	    {
622 	      opts.show_changed_fns = true;
623 	      opts.show_changed_vars = true;
624 
625 	      opts.show_deleted_vars = true;
626 	      opts.show_deleted_fns = true;
627 	    }
628 
629 	  opts.show_all_fns = false;
630 	  opts.show_all_vars = false;
631 	}
632       else if (!strcmp(argv[i], "--no-linkage-name"))
633 	opts.show_linkage_names = false;
634       else if (!strcmp(argv[i], "--no-unreferenced-symbols"))
635 	opts.show_symbols_not_referenced_by_debug_info = false;
636       else if (!strcmp(argv[i], "--no-show-locs"))
637 	opts.show_locs = false;
638       else if (!strcmp(argv[i], "--show-bytes"))
639 	opts.show_offsets_sizes_in_bits = false;
640       else if (!strcmp(argv[i], "--show-bits"))
641 	opts.show_offsets_sizes_in_bits = true;
642       else if (!strcmp(argv[i], "--show-hex"))
643 	opts.show_hexadecimal_values = true;
644       else if (!strcmp(argv[i], "--show-dec"))
645 	opts.show_hexadecimal_values = false;
646       else if (!strcmp(argv[i], "--no-show-relative-offset-changes"))
647 	opts.show_relative_offset_changes = false;
648       else if (!strcmp(argv[i], "--suppressions")
649 	       || !strcmp(argv[i], "--suppr"))
650 	{
651 	  int j = i + 1;
652 	  if (j >= argc)
653 	    {
654 	      opts.missing_operand = true;
655 	      opts.wrong_option = argv[i];
656 	      return true;
657 	    }
658 	  opts.suppression_paths.push_back(argv[j]);
659 	  ++i;
660 	}
661       else if (!strcmp(argv[i], "--drop"))
662 	{
663 	  int j = i + 1;
664 	  if (j >= argc)
665 	    {
666 	      opts.missing_operand = true;
667 	      opts.wrong_option = argv[i];
668 	      return true;
669 	    }
670 	  opts.drop_fn_regex_patterns.push_back(argv[j]);
671 	  opts.drop_var_regex_patterns.push_back(argv[j]);
672 	  ++i;
673 	}
674       else if (!strcmp(argv[i], "--drop-fn"))
675 	{
676 	  int j = i + 1;
677 	  if (j >= argc)
678 	    {
679 	      opts.missing_operand = true;
680 	      opts.wrong_option = argv[i];
681 	      return true;
682 	    }
683 	  opts.drop_fn_regex_patterns.push_back(argv[j]);
684 	  ++i;
685 	}
686       else if (!strcmp(argv[i], "--drop-var"))
687 	{
688 	  int j = i + 1;
689 	  if (j >= argc)
690 	    {
691 	      opts.missing_operand = true;
692 	      opts.wrong_option = argv[i];
693 	      return true;
694 	    }
695 	  opts.drop_var_regex_patterns.push_back(argv[j]);
696 	  ++i;
697 	}
698       else if (!strcmp(argv[i], "--keep"))
699 	{
700 	  int j = i + 1;
701 	  if (j >= argc)
702 	    {
703 	      opts.missing_operand = true;
704 	      opts.wrong_option = argv[i];
705 	      return true;
706 	    }
707 	  opts.keep_fn_regex_patterns.push_back(argv[j]);
708 	  opts.keep_var_regex_patterns.push_back(argv[j]);
709 	  ++i;
710 	}
711       else if (!strcmp(argv[i], "--keep-fn"))
712 	{
713 	  int j = i + 1;
714 	  if (j >= argc)
715 	    {
716 	      opts.missing_operand = true;
717 	      opts.wrong_option = argv[i];
718 	      return true;
719 	    }
720 	  opts.keep_fn_regex_patterns.push_back(argv[j]);
721 	}
722       else if (!strcmp(argv[i], "--keep-var"))
723 	{
724 	  int j = i + 1;
725 	  if (j >= argc)
726 	    {
727 	      opts.missing_operand = true;
728 	      opts.wrong_option = argv[i];
729 	      return true;
730 	    }
731 	  opts.keep_var_regex_patterns.push_back(argv[j]);
732 	}
733       else if (!strcmp(argv[i], "--harmless"))
734 	opts.show_harmless_changes = true;
735       else if (!strcmp(argv[i], "--no-harmful"))
736 	opts.show_harmful_changes = false;
737       else if (!strcmp(argv[i], "--redundant"))
738 	opts.show_redundant_changes = true;
739       else if (!strcmp(argv[i], "--no-redundant"))
740 	opts.show_redundant_changes = false;
741       else if (!strcmp(argv[i], "--impacted-interfaces"))
742 	opts.show_impacted_interfaces = true;
743       else if (!strcmp(argv[i], "--no-leverage-dwarf-factorization"))
744 	opts.leverage_dwarf_factorization = false;
745       else if (!strcmp(argv[i], "--no-change-categorization")
746 	       || !strcmp(argv[i], "-x"))
747 	opts.perform_change_categorization = false;
748       else if (!strcmp(argv[i], "--no-assume-odr-for-cplusplus"))
749 	opts.leverage_dwarf_factorization = false;
750       else if (!strcmp(argv[i], "--dump-diff-tree"))
751 	opts.dump_diff_tree = true;
752       else if (!strcmp(argv[i], "--stats"))
753 	opts.show_stats = true;
754       else if (!strcmp(argv[i], "--verbose"))
755 	opts.do_log = true;
756 #ifdef WITH_CTF
757       else if (!strcmp(argv[i], "--ctf"))
758         opts.use_ctf = true;
759 #endif
760 #ifdef WITH_BTF
761       else if (!strcmp(argv[i], "--btf"))
762         opts.use_btf = true;
763 #endif
764 #ifdef WITH_DEBUG_SELF_COMPARISON
765       else if (!strcmp(argv[i], "--debug-self-comparison"))
766 	opts.do_debug_self_comparison = true;
767 #endif
768 #ifdef WITH_DEBUG_TYPE_CANONICALIZATION
769       else if (!strcmp(argv[i], "--debug-tc"))
770 	opts.do_debug_type_canonicalization = true;
771 #endif
772       else
773 	{
774 	  if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
775 	    opts.wrong_option = argv[i];
776 	  return false;
777 	}
778     }
779 
780   return true;
781 }
782 
783 /// Display the function symbol tables for the two corpora.
784 ///
785 /// @param c1 the first corpus to display the symbol table for.
786 ///
787 /// @param c2 the second corpus to display the symbol table for.
788 ///
789 /// @param o the output stream to emit the symbol tables to.
790 static void
display_symtabs(const corpus_sptr c1,const corpus_sptr c2,ostream & o)791 display_symtabs(const corpus_sptr c1, const corpus_sptr c2, ostream& o)
792 {
793   o << "size of the functions symtabs: "
794     << c1->get_functions().size()
795     << " and "
796     << c2->get_functions().size()
797     << "\n\n";
798 
799   if (c1->get_functions().size())
800     o << "First functions symbol table\n\n";
801   for (abigail::corpus::functions::const_iterator i =
802 	 c1->get_functions().begin();
803        i != c1->get_functions().end();
804        ++i)
805     o << (*i)->get_pretty_representation() << std::endl;
806 
807   if (c1->get_functions().size() != 0)
808     o << "\n";
809 
810   if (c2->get_functions().size())
811     o << "Second functions symbol table\n\n";
812   for (abigail::corpus::functions::const_iterator i =
813 	 c2->get_functions().begin();
814        i != c2->get_functions().end();
815        ++i)
816     o << (*i)->get_pretty_representation() << std::endl;
817 }
818 
819 using abigail::comparison::diff_context_sptr;
820 using abigail::comparison::diff_context;
821 
822 /// Check that the suppression specification files supplied are
823 /// present.  If not, emit an error on stderr.
824 ///
825 /// @param opts the options instance to use.
826 ///
827 /// @return true if all suppression specification files are present,
828 /// false otherwise.
829 static bool
maybe_check_suppression_files(const options & opts)830 maybe_check_suppression_files(const options& opts)
831 {
832   for (vector<string>::const_iterator i = opts.suppression_paths.begin();
833        i != opts.suppression_paths.end();
834        ++i)
835     if (!check_file(*i, cerr, "abidiff"))
836       return false;
837 
838   for (vector<string>::const_iterator i =
839 	 opts.kernel_abi_whitelist_paths.begin();
840        i != opts.kernel_abi_whitelist_paths.end();
841        ++i)
842     if (!check_file(*i, cerr, "abidiff"))
843       return false;
844 
845   return true;
846 }
847 
848 /// Update the diff context from the @ref options data structure.
849 ///
850 /// @param ctxt the diff context to update.
851 ///
852 /// @param opts the instance of @ref options to consider.
853 static void
set_diff_context_from_opts(diff_context_sptr ctxt,options & opts)854 set_diff_context_from_opts(diff_context_sptr ctxt,
855 			   options& opts)
856 {
857   ctxt->default_output_stream(&cout);
858   ctxt->error_output_stream(&cerr);
859   ctxt->perform_change_categorization(opts.perform_change_categorization);
860   ctxt->show_leaf_changes_only(opts.leaf_changes_only);
861   ctxt->show_hex_values(opts.show_hexadecimal_values);
862   ctxt->show_offsets_sizes_in_bits(opts.show_offsets_sizes_in_bits);
863   ctxt->show_relative_offset_changes(opts.show_relative_offset_changes);
864   ctxt->show_stats_only(opts.show_stats_only);
865   ctxt->show_deleted_fns(opts.show_all_fns || opts.show_deleted_fns);
866   ctxt->show_changed_fns(opts.show_all_fns || opts.show_changed_fns);
867   ctxt->show_added_fns(opts.show_all_fns || opts.show_added_fns);
868   ctxt->show_deleted_vars(opts.show_all_vars || opts.show_deleted_vars);
869   ctxt->show_changed_vars(opts.show_all_vars || opts.show_changed_vars);
870   ctxt->show_added_vars(opts.show_all_vars || opts.show_added_vars);
871   ctxt->show_linkage_names(opts.show_linkage_names);
872   ctxt->show_locs(opts.show_locs);
873   // Intentional logic flip of ignore_soname
874   ctxt->show_soname_change(!opts.ignore_soname);
875   // So when we are showing only leaf changes, we want to show
876   // redundant changes because of this: Suppose several functions have
877   // their return type changed from void* to int*.  We want them all
878   // to be reported.  In that case the change is not redundant.  As
879   // far as user-defined type changes (like struct/class) they are
880   // already put inside a map which makes them be non-redundant, so we
881   // don't have to worry about that case.
882   //
883   // TODO: maybe that in this case we should avoid firing the
884   // redundancy analysis pass altogether.  That could help save a
885   // couple of CPU cycle here and there!
886   ctxt->show_redundant_changes(opts.show_redundant_changes
887                                || opts.leaf_changes_only);
888   ctxt->show_symbols_unreferenced_by_debug_info
889     (opts.show_symbols_not_referenced_by_debug_info);
890   ctxt->show_added_symbols_unreferenced_by_debug_info
891     (opts.show_symbols_not_referenced_by_debug_info && opts.show_added_syms);
892   ctxt->show_unreachable_types(opts.show_all_types);
893   ctxt->show_impacted_interfaces(opts.show_impacted_interfaces);
894 
895   if (!opts.show_harmless_changes)
896       ctxt->switch_categories_off(get_default_harmless_categories_bitmap());
897 
898   if (!opts.show_harmful_changes)
899     ctxt->switch_categories_off(get_default_harmful_categories_bitmap());
900 
901   suppressions_type supprs;
902   for (vector<string>::const_iterator i = opts.suppression_paths.begin();
903        i != opts.suppression_paths.end();
904        ++i)
905     read_suppressions(*i, supprs);
906   ctxt->add_suppressions(supprs);
907 
908   if (!opts.no_default_supprs && opts.suppression_paths.empty())
909     {
910       // Load the default system and user suppressions.
911       suppressions_type& supprs = ctxt->suppressions();
912 
913       load_default_system_suppressions(supprs);
914       load_default_user_suppressions(supprs);
915     }
916 
917   if (!opts.headers_dirs1.empty() || !opts.header_files1.empty())
918     {
919       // Generate suppression specification to avoid showing ABI
920       // changes on types that are not defined in public headers.
921       suppression_sptr suppr =
922 	gen_suppr_spec_from_headers(opts.headers_dirs1, opts.header_files1);
923       if (suppr)
924 	ctxt->add_suppression(suppr);
925     }
926 
927   if (!opts.headers_dirs2.empty() || !opts.header_files2.empty())
928     {
929       // Generate suppression specification to avoid showing ABI
930       // changes on types that are not defined in public headers.
931       suppression_sptr suppr =
932 	gen_suppr_spec_from_headers(opts.headers_dirs2, opts.header_files2);
933       if (suppr)
934 	ctxt->add_suppression(suppr);
935     }
936 
937   ctxt->dump_diff_tree(opts.dump_diff_tree);
938 
939   ctxt->do_log(opts.do_log);
940 }
941 
942 /// Set a bunch of tunable buttons on the ELF-based reader from the
943 /// command-line options.
944 ///
945 /// @param rdr the reader to tune.
946 ///
947 /// @param opts the command line options.
948 static void
set_generic_options(abigail::elf_based_reader & rdr,options & opts)949 set_generic_options(abigail::elf_based_reader& rdr, options& opts)
950 {
951   rdr.options().show_stats = opts.show_stats;
952   rdr.options().do_log = opts.do_log;
953   rdr.options().leverage_dwarf_factorization =
954     opts.leverage_dwarf_factorization;
955   rdr.options().assume_odr_for_cplusplus =
956     opts.assume_odr_for_cplusplus;
957 }
958 
959 /// Set suppression specifications to the @p read_context used to load
960 /// the ABI corpus from the ELF/DWARF file.
961 ///
962 /// These suppression specifications are going to be applied to drop
963 /// some ABI artifacts on the floor (while reading the ELF/DWARF file
964 /// or the native XML ABI file) and thus minimize the size of the
965 /// resulting ABI corpus.
966 ///
967 /// @param read_ctxt the read context to apply the suppression
968 /// specifications to.  Note that the type of this parameter is
969 /// generic (class template) because in practise, it can be either an
970 /// dwarf::read_context type or an
971 /// abigail::abiabixml_reader::reader type.
972 ///
973 /// @param opts the options where to get the suppression
974 /// specifications from.
975 static void
set_suppressions(abigail::fe_iface & reader,const options & opts)976 set_suppressions(abigail::fe_iface& reader, const options& opts)
977 {
978   suppressions_type supprs;
979   for (vector<string>::const_iterator i = opts.suppression_paths.begin();
980        i != opts.suppression_paths.end();
981        ++i)
982     read_suppressions(*i, supprs);
983 
984   if (reader.corpus_path() == opts.file1
985       && (!opts.headers_dirs1.empty() || !opts.header_files1.empty()))
986     {
987       // Generate suppression specification to avoid showing ABI
988       // changes on types that are not defined in public headers for
989       // the first binary.
990       //
991       // As these suppression specifications are applied during the
992       // corpus loading, they are going to be dropped from the
993       // internal representation altogether.
994       suppression_sptr suppr =
995 	gen_suppr_spec_from_headers(opts.headers_dirs1, opts.header_files1);
996       if (suppr)
997 	{
998 	  if (opts.drop_private_types)
999 	    suppr->set_drops_artifact_from_ir(true);
1000 	  supprs.push_back(suppr);
1001 	}
1002     }
1003 
1004   if (reader.corpus_path() == opts.file2
1005       && (!opts.headers_dirs2.empty() || !opts.header_files2.empty()))
1006     {
1007       // Generate suppression specification to avoid showing ABI
1008       // changes on types that are not defined in public headers for
1009       // the second binary.
1010       //
1011       // As these suppression specifications are applied during the
1012       // corpus loading, they are going to be dropped from the
1013       // internal representation altogether.
1014       suppression_sptr suppr =
1015 	gen_suppr_spec_from_headers(opts.headers_dirs2, opts.header_files2);
1016       if (suppr)
1017 	{
1018 	  if (opts.drop_private_types)
1019 	    suppr->set_drops_artifact_from_ir(true);
1020 	  supprs.push_back(suppr);
1021 	}
1022     }
1023 
1024   const suppressions_type& wl_suppr =
1025       gen_suppr_spec_from_kernel_abi_whitelists(
1026 	  opts.kernel_abi_whitelist_paths);
1027 
1028   supprs.insert(supprs.end(), wl_suppr.begin(), wl_suppr.end());
1029 
1030   reader.add_suppressions(supprs);
1031 }
1032 
1033 /// Configure the abigail::xml_reacher::read_context based on the
1034 /// relevant command-line options.
1035 ///
1036 /// @param ctxt the read context to configure.
1037 ///
1038 /// @param opts the command-line options to configure @p ctxt from.
1039 static void
set_native_xml_reader_options(abigail::fe_iface & rdr,const options & opts)1040 set_native_xml_reader_options(abigail::fe_iface& rdr,
1041 			      const options& opts)
1042 {
1043   abixml::consider_types_not_reachable_from_public_interfaces(rdr,
1044 							      opts.show_all_types);
1045   rdr.options().do_log = opts.do_log;
1046 
1047 }
1048 
1049 /// Set the regex patterns describing the functions to drop from the
1050 /// symbol table of a given corpus.
1051 ///
1052 /// @param opts the options to the regex patterns from.
1053 ///
1054 /// @param c the corpus to set the regex patterns into.
1055 static void
set_corpus_keep_drop_regex_patterns(options & opts,corpus_sptr c)1056 set_corpus_keep_drop_regex_patterns(options& opts, corpus_sptr c)
1057 {
1058   if (!opts.drop_fn_regex_patterns.empty())
1059     {
1060       vector<string>& v = opts.drop_fn_regex_patterns;
1061       vector<string>& p = c->get_regex_patterns_of_fns_to_suppress();
1062       p.assign(v.begin(), v.end());
1063     }
1064 
1065   if (!opts.keep_fn_regex_patterns.empty())
1066     {
1067       vector<string>& v = opts.keep_fn_regex_patterns;
1068       vector<string>& p = c->get_regex_patterns_of_fns_to_keep();
1069       p.assign(v.begin(), v.end());
1070     }
1071 
1072   if (!opts.drop_var_regex_patterns.empty())
1073     {
1074       vector<string>& v = opts.drop_var_regex_patterns;
1075       vector<string>& p = c->get_regex_patterns_of_vars_to_suppress();
1076       p.assign(v.begin(), v.end());
1077     }
1078 
1079  if (!opts.keep_var_regex_patterns.empty())
1080     {
1081       vector<string>& v = opts.keep_var_regex_patterns;
1082       vector<string>& p = c->get_regex_patterns_of_vars_to_keep();
1083       p.assign(v.begin(), v.end());
1084     }
1085 }
1086 
1087 /// This function sets diff context options that are specific to
1088 /// kernel module interface comparison.
1089 ///
1090 /// @param ctxt the diff context to consider.
1091 static void
adjust_diff_context_for_kmidiff(diff_context & ctxt)1092 adjust_diff_context_for_kmidiff(diff_context &ctxt)
1093 {
1094   ctxt.show_linkage_names(false);
1095 }
1096 
1097 /// Convert options::di_root_paths{1,2} into
1098 /// options::prepared_di_root_paths{1,2} which is the suitable type
1099 /// format that the dwarf_reader expects.
1100 ///
1101 /// @param o the options to consider.
1102 static void
prepare_di_root_paths(options & o)1103 prepare_di_root_paths(options& o)
1104 {
1105   abigail::tools_utils::convert_char_stars_to_char_star_stars
1106     (o.di_root_paths1, o.prepared_di_root_paths1);
1107 
1108   abigail::tools_utils::convert_char_stars_to_char_star_stars
1109     (o.di_root_paths2, o.prepared_di_root_paths2);
1110 }
1111 
1112 /// Emit an appropriate error message if necessary, given an error
1113 /// code.
1114 ///
1115 /// To emit the appropriate error message the function might need to
1116 /// access the context in which the (ELF) input file was being loaded,
1117 /// if it's present.
1118 ///
1119 /// @param status_code the status code returned after trying to load
1120 /// the input file.
1121 ///
1122 /// @param ctxt the context used to load the ELF file, if we still
1123 /// have it.  If this is nil, then it's ignored.
1124 ///
1125 /// @param prog_name the name of the current program.  This is
1126 /// important as it's used in the error message.
1127 ///
1128 /// @param input_file_name the name of the input file that we are
1129 /// tryin to load.
1130 ///
1131 /// @param debug_info_dir1 if non nil, then this points to the path of
1132 /// the root debug info directory of the first binary that we are
1133 /// trying to load..  If nil, then it's ignored.
1134 ///
1135 /// @param debug_info_dir2 if non nil, then this points to the path of
1136 /// the root debug info directory of the second binary that we are
1137 /// trying to load..  If nil, then it's ignored.
1138 ///
1139 /// @return abigail::tools_utils::ABIDIFF_ERROR if an error was
1140 /// detected, abigail::tools_utils::ABIDIFF_OK otherwise.
1141 static abigail::tools_utils::abidiff_status
handle_error(abigail::fe_iface::status status_code,const abigail::elf_based_reader * rdr,const string & prog_name,const options & opts)1142 handle_error(abigail::fe_iface::status status_code,
1143 	     const abigail::elf_based_reader* rdr,
1144 	     const string& prog_name,
1145 	     const options& opts)
1146 {
1147   if (!(status_code & abigail::fe_iface::STATUS_OK)
1148       || status_code & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND
1149       || status_code & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
1150     {
1151       emit_prefix(prog_name, cerr)
1152 	<< "failed to read input file " << opts.file1 << "\n";
1153 
1154       if (status_code & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND)
1155 	{
1156 	  emit_prefix(prog_name, cerr) <<
1157 	    "could not find the debug info\n";
1158 	  {
1159 	    if (opts.prepared_di_root_paths1.empty() == 0)
1160 	      emit_prefix(prog_name, cerr)
1161 		<< "Maybe you should consider using the "
1162 		"--debug-info-dir1 option to tell me about the "
1163 		"root directory of the debuginfo? "
1164 		"(e.g, --debug-info-dir1 /usr/lib/debug)\n";
1165 	    else
1166 	      {
1167 		emit_prefix(prog_name, cerr)
1168 		  << "Maybe the root path to the debug information '";
1169 		for (vector<char**>::const_iterator i
1170 		       = opts.prepared_di_root_paths1.begin();
1171 		     i != opts.prepared_di_root_paths1.end();
1172 		     ++i)
1173 		  {
1174 		    if (i != opts.prepared_di_root_paths1.end())
1175 		      cerr << ", ";
1176 		    cerr << **i;
1177 		  }
1178 		cerr << "' is wrong?\n";
1179 	      }
1180 	  }
1181 
1182 	  {
1183 	    if (opts.prepared_di_root_paths2.empty())
1184 	      emit_prefix(prog_name, cerr)
1185 		<< "Maybe you should consider using the "
1186 		"--debug-info-dir2 option to tell me about the "
1187 		"root directory of the debuginfo? "
1188 		"(e.g, --debug-info-dir2 /usr/lib/debug)\n";
1189 	    else
1190 	      {
1191 		emit_prefix(prog_name, cerr)
1192 		  << "Maybe the root path to the debug information '";
1193 		for (vector<char**>::const_iterator i
1194 		       = opts.prepared_di_root_paths2.begin();
1195 		     i != opts.prepared_di_root_paths2.end();
1196 		     ++i)
1197 		  {
1198 		    if (i != opts.prepared_di_root_paths2.end())
1199 		      cerr << ", ";
1200 		    cerr << **i;
1201 		  }
1202 		  cerr << "' is wrong?\n";
1203 	      }
1204 	  }
1205 	}
1206 
1207       if (status_code & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
1208 	{
1209 	  emit_prefix(prog_name, cerr)
1210 	    << "could not find the alternate debug info file";
1211 
1212 	  if (!rdr->alternate_dwarf_debug_info_path().empty())
1213 	    cerr << " at: "
1214 		 << rdr->alternate_dwarf_debug_info_path();
1215 	  cerr << "\n";
1216 	}
1217 
1218       if (status_code & abigail::fe_iface::STATUS_NO_SYMBOLS_FOUND)
1219 	emit_prefix(prog_name, cerr)
1220 	  << "could not find the ELF symbols in the file '"
1221 	  << opts.file1
1222 	  << "'\n";
1223 
1224       return abigail::tools_utils::ABIDIFF_ERROR;
1225     }
1226 
1227   return abigail::tools_utils::ABIDIFF_OK;
1228 }
1229 
1230 /// Emit an error message saying that the two files have incompatible
1231 /// format versions.
1232 ///
1233 /// @param file_path1 the first file path to consider.
1234 ///
1235 /// @param version1 the second version to consider.
1236 ///
1237 /// @param file_path2 the second file path to consider.
1238 ///
1239 /// @param version2 the second version to consider.
1240 ///
1241 /// @param prog_name the name of the current program.
1242 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)1243 emit_incompatible_format_version_error_message(const string& file_path1,
1244 					       const string& version1,
1245 					       const string& file_path2,
1246 					       const string& version2,
1247 					       const string& prog_name)
1248 {
1249   emit_prefix(prog_name, cerr)
1250     << "incompatible format version between the two input files:\n"
1251     << "'" << file_path1 << "' (" << version1 << ")\n"
1252     << "and\n"
1253     << "'" << file_path2 << "' (" << version2 << ")\n";
1254 }
1255 
1256 /// Display the dependencies of two corpora.
1257 ///
1258 /// @param prog_name the name of the current abidiff program.
1259 ///
1260 /// @param corp1 the first corpus to consider.
1261 ///
1262 /// @param corp2 the second corpus to consider.
1263 ///
1264 /// @param deps1 the dependencies to display.
1265 ///
1266 /// @param deps2 the dependencies to display.
1267 static void
display_dependencies(const string & prog_name,const corpus_sptr & corp1,const corpus_sptr & corp2,const set<string> & deps1,const set<string> & deps2)1268 display_dependencies(const string& prog_name,
1269 		     const corpus_sptr& corp1,
1270 		     const corpus_sptr& corp2,
1271 		     const set<string>& deps1,
1272 		     const set<string>& deps2)
1273 {
1274   if (deps1.empty())
1275     emit_prefix(prog_name, cout)
1276     << "No dependencies found for '" << corp1->get_path() << "':\n";
1277   else
1278     {
1279       emit_prefix(prog_name, cout)
1280 	<< "dependencies of '" << corp1->get_path() << "':\n\t";
1281 
1282       int n = 0;
1283       for (const auto& dep : deps1)
1284 	{
1285 	  if (n)
1286 	    cout << ", ";
1287 	  cout << dep;
1288 	  ++n;
1289 	}
1290       cout << "\n";
1291     }
1292 
1293   if (deps2.empty())
1294     emit_prefix(prog_name, cout)
1295       << "No dependencies found for '" << corp2->get_path() << "':\n";
1296   else
1297     {
1298       emit_prefix(prog_name, cout)
1299 	<< "dependencies of '" << corp2->get_path() << "':\n\t";
1300 
1301       int n = 0;
1302       for (const auto& dep : deps2)
1303 	{
1304 	  if (n)
1305 	    cout << ", ";
1306 	  cout << dep;
1307 	  ++n;
1308 	}
1309       cout << "\n";
1310     }
1311 }
1312 
1313 int
main(int argc,char * argv[])1314 main(int argc, char* argv[])
1315 {
1316   options opts;
1317   if (!parse_command_line(argc, argv, opts))
1318     {
1319       emit_prefix(argv[0], cerr)
1320 	<< "unrecognized option: "
1321 	<< opts.wrong_option << "\n"
1322 	<< "try the --help option for more information\n";
1323       return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
1324 	      | abigail::tools_utils::ABIDIFF_ERROR);
1325     }
1326 
1327   if (opts.missing_operand)
1328     {
1329       emit_prefix(argv[0], cerr)
1330 	<< "missing operand to option: " << opts.wrong_option <<"\n"
1331 	<< "try the --help option for more information\n";
1332       return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
1333 	      | abigail::tools_utils::ABIDIFF_ERROR);
1334     }
1335 
1336   if (opts.display_usage)
1337     {
1338       display_usage(argv[0], cout);
1339       return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
1340 	      | abigail::tools_utils::ABIDIFF_ERROR);
1341     }
1342 
1343   if (opts.display_version)
1344     {
1345       emit_prefix(argv[0], cout)
1346 	<< abigail::tools_utils::get_library_version_string()
1347 	<< "\n";
1348       return 0;
1349     }
1350 
1351   prepare_di_root_paths(opts);
1352 
1353   if (!maybe_check_suppression_files(opts))
1354     return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
1355 	    | abigail::tools_utils::ABIDIFF_ERROR);
1356 
1357   abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
1358   if (!opts.file1.empty() && !opts.file2.empty())
1359     {
1360       if (!check_file(opts.file1, cerr))
1361 	return abigail::tools_utils::ABIDIFF_ERROR;
1362 
1363       if (!check_file(opts.file2, cerr))
1364 	return abigail::tools_utils::ABIDIFF_ERROR;
1365 
1366       abigail::tools_utils::file_type t1_type, t2_type;
1367 
1368       t1_type = guess_file_type(opts.file1);
1369       t2_type = guess_file_type(opts.file2);
1370 
1371       environment env;
1372       if (opts.exported_interfaces_only.has_value())
1373 	env.analyze_exported_interfaces_only(*opts.exported_interfaces_only);
1374 
1375 #ifdef WITH_DEBUG_SELF_COMPARISON
1376 	    if (opts.do_debug_self_comparison)
1377 	      env.self_comparison_debug_is_on(true);
1378 #endif
1379 #ifdef WITH_DEBUG_TYPE_CANONICALIZATION
1380 	    if (opts.do_debug_type_canonicalization)
1381 	      env.debug_type_canonicalization_is_on(true);
1382 #endif
1383       translation_unit_sptr t1, t2;
1384       abigail::fe_iface::status c1_status =
1385 	abigail::fe_iface::STATUS_OK,
1386 	c2_status = abigail::fe_iface::STATUS_OK;
1387       corpus_sptr c1, c2;
1388       corpus_group_sptr g1, g2;
1389       bool files_suppressed = false;
1390 
1391       diff_context_sptr ctxt(new diff_context);
1392       set_diff_context_from_opts(ctxt, opts);
1393       suppressions_type& supprs = ctxt->suppressions();
1394       files_suppressed = (file_is_suppressed(opts.file1, supprs)
1395 			  || file_is_suppressed(opts.file2, supprs));
1396 
1397       if (files_suppressed)
1398 	// We don't have to compare anything because a user
1399 	// suppression specification file instructs us to avoid
1400 	// loading either one of the input files.
1401 	return abigail::tools_utils::ABIDIFF_OK;
1402 
1403       switch (t1_type)
1404 	{
1405 	case abigail::tools_utils::FILE_TYPE_UNKNOWN:
1406 	  emit_prefix(argv[0], cerr)
1407 	    << "Unknown content type for file " << opts.file1 << "\n";
1408 	  return abigail::tools_utils::ABIDIFF_ERROR;
1409 	  break;
1410 	case abigail::tools_utils::FILE_TYPE_NATIVE_BI:
1411 	  t1 = abixml::read_translation_unit_from_file(opts.file1,
1412 								       env);
1413 	  break;
1414 	case abigail::tools_utils::FILE_TYPE_ELF: // fall through
1415 	case abigail::tools_utils::FILE_TYPE_AR:
1416 	  {
1417 	    corpus::origin requested_fe_kind = corpus::DWARF_ORIGIN;
1418 #ifdef WITH_CTF
1419 	    if (opts.use_ctf)
1420 	      requested_fe_kind = corpus::CTF_ORIGIN;
1421 #endif
1422 #ifdef WITH_BTF
1423 	    if (opts.use_btf)
1424 	      requested_fe_kind = corpus::BTF_ORIGIN;
1425 #endif
1426 	    abigail::elf_based_reader_sptr rdr =
1427 	      create_best_elf_based_reader(opts.file1,
1428 					   opts.prepared_di_root_paths1,
1429 					   env, requested_fe_kind,
1430 					   opts.show_all_types,
1431 					   opts.linux_kernel_mode);
1432             ABG_ASSERT(rdr);
1433 	    set_generic_options(*rdr, opts);
1434 	    set_suppressions(*rdr, opts);
1435 	    c1 = rdr->read_corpus(c1_status);
1436 
1437 	    if (!c1
1438 		|| (opts.fail_no_debug_info
1439 		    && (c1_status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
1440 		    && (c1_status & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND)))
1441 	      return handle_error(c1_status, rdr.get(),
1442 				  argv[0], opts);
1443 
1444 	    if (!opts.added_bins1.empty())
1445 	      g1 = stick_corpus_and_binaries_into_corpus_group(rdr, c1,
1446 							       opts.added_bins1,
1447 							       opts.added_bins_dirs1);
1448 	    if (opts.follow_dependencies)
1449 	      {
1450 		if (g1)
1451 		  add_dependencies_into_corpus_group(rdr, *c1,
1452 						     opts.added_bins_dirs1,
1453 						     *g1);
1454 		else
1455 		  g1 = stick_corpus_and_dependencies_into_corpus_group(rdr, c1,
1456 								       opts.added_bins_dirs1);
1457 	      }
1458 	  }
1459 	  break;
1460 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
1461 	  {
1462 	    abigail::fe_iface_sptr rdr =
1463 	      abixml::create_reader(opts.file1, env);
1464 	    assert(rdr);
1465 	    set_suppressions(*rdr, opts);
1466 	    set_native_xml_reader_options(*rdr, opts);
1467 	    c1 = rdr->read_corpus(c1_status);
1468 	    if (!c1)
1469 	      return handle_error(c1_status, /*ctxt=*/0, argv[0], opts);
1470 	  }
1471 	  break;
1472 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
1473 	  {
1474 	    abigail::fe_iface_sptr rdr =
1475 	      abixml::create_reader(opts.file1, env);
1476 	    assert(rdr);
1477 	    set_suppressions(*rdr, opts);
1478 	    set_native_xml_reader_options(*rdr, opts);
1479 	    g1 = abixml::read_corpus_group_from_input(*rdr);
1480 	    if (!g1)
1481 	      return handle_error(c1_status, /*ctxt=*/0,
1482 				  argv[0], opts);
1483 	  }
1484 	  break;
1485 	case abigail::tools_utils::FILE_TYPE_RPM:
1486 	case abigail::tools_utils::FILE_TYPE_SRPM:
1487 	case abigail::tools_utils::FILE_TYPE_DEB:
1488 	case abigail::tools_utils::FILE_TYPE_DIR:
1489 	case abigail::tools_utils::FILE_TYPE_TAR:
1490 	  break;
1491 	}
1492 
1493       switch (t2_type)
1494 	{
1495 	case abigail::tools_utils::FILE_TYPE_UNKNOWN:
1496 	  emit_prefix(argv[0], cerr)
1497 	    << "Unknown content type for file " << opts.file2 << "\n";
1498 	  return abigail::tools_utils::ABIDIFF_ERROR;
1499 	  break;
1500 	case abigail::tools_utils::FILE_TYPE_NATIVE_BI:
1501 	  t2 = abixml::read_translation_unit_from_file(opts.file2,
1502 								       env);
1503 	  break;
1504 	case abigail::tools_utils::FILE_TYPE_ELF: // Fall through
1505 	case abigail::tools_utils::FILE_TYPE_AR:
1506 	  {
1507 	    corpus::origin requested_fe_kind = corpus::DWARF_ORIGIN;
1508 #ifdef WITH_CTF
1509 	    if (opts.use_ctf)
1510 	      requested_fe_kind = corpus::CTF_ORIGIN;
1511 #endif
1512 #ifdef WITH_BTF
1513 	    if (opts.use_btf)
1514 	      requested_fe_kind = corpus::BTF_ORIGIN;
1515 #endif
1516             abigail::elf_based_reader_sptr rdr =
1517 	      create_best_elf_based_reader(opts.file2,
1518 					   opts.prepared_di_root_paths2,
1519 					   env, requested_fe_kind,
1520 					   opts.show_all_types,
1521 					   opts.linux_kernel_mode);
1522             ABG_ASSERT(rdr);
1523 
1524 	    set_generic_options(*rdr, opts);
1525 	    set_suppressions(*rdr, opts);
1526 
1527 	    c2 = rdr->read_corpus(c2_status);
1528 
1529 	    if (!c2
1530 		|| (opts.fail_no_debug_info
1531 		    && (c2_status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
1532 		    && (c2_status & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND)))
1533 	      return handle_error(c2_status, rdr.get(), argv[0], opts);
1534 
1535 	  if (!opts.added_bins2.empty())
1536 	    g2 = stick_corpus_and_binaries_into_corpus_group(rdr, c2,
1537 							     opts.added_bins2,
1538 							     opts.added_bins_dirs2);
1539 	  if (opts.follow_dependencies)
1540 	    {
1541 	      if (g2)
1542 		add_dependencies_into_corpus_group(rdr, *c2,
1543 						   opts.added_bins_dirs2,
1544 						   *g2);
1545 	      else
1546 		g2 = stick_corpus_and_dependencies_into_corpus_group(rdr, c2,
1547 								     opts.added_bins_dirs2);
1548 	    }
1549 	  }
1550 	  break;
1551 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
1552 	  {
1553 	    abigail::fe_iface_sptr rdr = abixml::create_reader(opts.file2, env);
1554 	    assert(rdr);
1555 	    set_suppressions(*rdr, opts);
1556 	    set_native_xml_reader_options(*rdr, opts);
1557 	    c2 = rdr->read_corpus(c2_status);
1558 	    if (!c2)
1559 	      return handle_error(c2_status, /*ctxt=*/0, argv[0], opts);
1560 
1561 	  }
1562 	  break;
1563 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
1564 	  {
1565 	    abigail::fe_iface_sptr rdr = abixml::create_reader(opts.file2, env);
1566 	    assert(rdr);
1567 	    set_suppressions(*rdr, opts);
1568 	    set_native_xml_reader_options(*rdr, opts);
1569 	    g2 = abixml::read_corpus_group_from_input(*rdr);
1570 	    if (!g2)
1571 	      return handle_error(c2_status, /*ctxt=*/0, argv[0], opts);
1572 	  }
1573 	  break;
1574 	case abigail::tools_utils::FILE_TYPE_RPM:
1575 	case abigail::tools_utils::FILE_TYPE_SRPM:
1576 	case abigail::tools_utils::FILE_TYPE_DEB:
1577 	case abigail::tools_utils::FILE_TYPE_DIR:
1578 	case abigail::tools_utils::FILE_TYPE_TAR:
1579 	  break;
1580 	}
1581 
1582       if (!opts.added_bins1.empty()
1583 	  || !opts.added_bins2.empty())
1584 	{
1585 	  // We were requested to compare a set of binaries against
1586 	  // another set of binaries.  Let's make sure we construct
1587 	  // two ABI construct groups in all cases.
1588 
1589 	  if (!g1 && c1)
1590 	    {
1591 	      // We don't have a corpus group for the first argument.
1592 	      // Let's build one and stick the ABI corpus at hand in
1593 	      // it.
1594 	      g1.reset(new corpus_group(c1->get_environment(),
1595 					c1->get_path()));
1596 	      g1->add_corpus(c1);
1597 	    }
1598 
1599 	  if (!g2 && c2)
1600 	    {
1601 	      // We don't have a corpus group for the second argument.
1602 	      // Let's build one and stick the ABI corpus at hand in
1603 	      // it.
1604 	      g2.reset(new corpus_group(c2->get_environment(),
1605 					c2->get_path()));
1606 	      g2->add_corpus(c1);
1607 	    }
1608 	}
1609 
1610       if (!!c1 != !!c2
1611 	  || !!t1 != !!t2
1612 	  || !!g1 != !!g2)
1613 	{
1614 	  emit_prefix(argv[0], cerr)
1615 	    << "the two input should be of the same kind\n";
1616 	  return abigail::tools_utils::ABIDIFF_ERROR;
1617 	}
1618 
1619       if (opts.no_arch)
1620 	{
1621 	  if (c1)
1622 	    c1->set_architecture_name("");
1623 	  if (c2)
1624 	    c2->set_architecture_name("");
1625 	}
1626       if (opts.no_corpus)
1627 	{
1628 	  if (c1)
1629 	    c1->set_path("");
1630 	  if (c2)
1631 	    c2->set_path("");
1632 	}
1633 
1634       if (t1)
1635 	{
1636 	  tools_utils::timer t;
1637 	  if (opts.do_log)
1638 	    {
1639 	      t.start();
1640 	      std::cerr << "Compute diff ...\n";
1641 	    }
1642 
1643 	  translation_unit_diff_sptr diff = compute_diff(t1, t2, ctxt);
1644 
1645 	  if (opts.do_log)
1646 	    {
1647 	      t.stop();
1648 	      std::cerr << "diff computed!:" << t << "\n";
1649 	    }
1650 
1651 	  if (diff->has_changes())
1652 	    {
1653 	      tools_utils::timer t;
1654 	      if (opts.do_log)
1655 		{
1656 		  t.start();
1657 		  std::cerr << "Computing the report ...\n";
1658 		}
1659 
1660 	      diff->report(cout);
1661 
1662 	      if (opts.do_log)
1663 		{
1664 		  t.stop();
1665 		  std::cerr << "Report computed!:" << t << "\n";
1666 		}
1667 	    }
1668 	}
1669       else if (g1)
1670 	{
1671 	  if (opts.show_symtabs)
1672 	    {
1673 	      display_symtabs(c1, c2, cout);
1674 	      return abigail::tools_utils::ABIDIFF_OK;
1675 	    }
1676 
1677 	  const auto g1_version = g1->get_format_major_version_number();
1678 	  const auto g2_version = g2->get_format_major_version_number();
1679 	  if (g1_version != g2_version)
1680 	    {
1681 	      emit_incompatible_format_version_error_message(opts.file1,
1682 							     g1_version,
1683 							     opts.file2,
1684 							     g2_version,
1685 							     argv[0]);
1686 	      return abigail::tools_utils::ABIDIFF_ERROR;
1687 	    }
1688 
1689 	  adjust_diff_context_for_kmidiff(*ctxt);
1690 	  tools_utils::timer t;
1691 	  if (opts.do_log)
1692 	    {
1693 	      t.start();
1694 	      std::cerr << "Compute diff ...\n";
1695 	    }
1696 
1697 	  corpus_diff_sptr diff = compute_diff(g1, g2, ctxt);
1698 
1699 	  if (opts.do_log)
1700 	    {
1701 	      t.stop();
1702 	      diff->do_log(true);
1703 	      std::cerr << "diff computed!:" << t << "\n";
1704 	    }
1705 
1706 	  if (opts.do_log)
1707 	    {
1708 	      std::cerr << "Computing net changes ...\n";
1709 	      t.start();
1710 	    }
1711 
1712 	  if (diff->has_net_changes())
1713 	    status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
1714 	  if (opts.do_log)
1715 	    {
1716 	      t.stop();
1717 	      std::cerr << "net changes computed!: "<< t << "\n";
1718 	    }
1719 
1720 	  if (opts.do_log)
1721 	    {
1722 	      t.start();
1723 	      std::cerr << "Computing incompatible changes ...\n";
1724 	    }
1725 
1726 	  if (diff->has_incompatible_changes())
1727 	    status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
1728 
1729 	  if (opts.do_log)
1730 	    {
1731 	      t.stop();
1732 	      std::cerr << "incompatible changes computed!: "<< t << "\n";
1733 	    }
1734 
1735 	  if (opts.do_log)
1736 	    {
1737 	      t.start();
1738 	      std::cerr << "Computing changes ...\n";
1739 	    }
1740 
1741 	  if (diff->has_changes())
1742 	    {
1743 	      if (opts.do_log)
1744 		{
1745 		  t.stop();
1746 		  std::cerr << "changes computed!: "<< t << "\n";
1747 		}
1748 
1749 	      if (opts.do_log)
1750 		{
1751 		  t.start();
1752 		  std::cerr << "Computing report ...\n";
1753 		}
1754 
1755 	      diff->report(cout);
1756 
1757 	      if (opts.do_log)
1758 		{
1759 		  t.stop();
1760 		  std::cerr << "Report computed!:" << t << "\n";
1761 		}
1762 	    }
1763 	  else
1764 	    {
1765 	      if (opts.do_log)
1766 		{
1767 		  t.stop();
1768 		  std::cerr << "changes computed!: "<< t << "\n";
1769 		}
1770 	    }
1771 
1772 	  if (opts.list_dependencies)
1773 	    {
1774 	      set<string> deps1, deps2;
1775 	      get_dependencies(*c1, opts.added_bins_dirs1, deps1);
1776 	      get_dependencies(*c2, opts.added_bins_dirs2, deps2);
1777 	      display_dependencies(argv[0], c1, c2, deps1, deps2);
1778 	    }
1779 	}
1780       else if (c1)
1781 	{
1782 	  if (opts.show_symtabs)
1783 	    {
1784 	      display_symtabs(c1, c2, cout);
1785 	      return abigail::tools_utils::ABIDIFF_OK;
1786 	    }
1787 
1788 	  if (opts.list_dependencies)
1789 	    {
1790 	      set<string> deps1, deps2;
1791 	      get_dependencies(*c1, opts.added_bins_dirs1, deps1);
1792 	      get_dependencies(*c2, opts.added_bins_dirs2, deps2);
1793 	      display_dependencies(argv[0], c1, c2, deps1, deps2);
1794 	      return abigail::tools_utils::ABIDIFF_OK;
1795 	    }
1796 	  const auto c1_version = c1->get_format_major_version_number();
1797 	  const auto c2_version = c2->get_format_major_version_number();
1798 	  if (c1_version != c2_version)
1799 	    {
1800 	      emit_incompatible_format_version_error_message(opts.file1,
1801 							     c1_version,
1802 							     opts.file2,
1803 							     c2_version,
1804 							     argv[0]);
1805 	      return abigail::tools_utils::ABIDIFF_ERROR;
1806 	    }
1807 
1808 	  set_corpus_keep_drop_regex_patterns(opts, c1);
1809 	  set_corpus_keep_drop_regex_patterns(opts, c2);
1810 
1811 	  tools_utils::timer t;
1812 	  if (opts.do_log)
1813 	    {
1814 	      t.start();
1815 	      std::cerr << "Compute diff ...\n";
1816 	    }
1817 
1818 	  corpus_diff_sptr diff = compute_diff(c1, c2, ctxt);
1819 
1820 	  if (opts.do_log)
1821 	    {
1822 	      t.stop();
1823 	      std::cerr << "diff computed!:" << t << "\n";
1824 	    }
1825 
1826 	  if (opts.do_log)
1827 	    {
1828 	      t.start();
1829 	      std::cerr << "Computing net changes ...\n";
1830 	    }
1831 
1832 	  if (diff->has_net_changes())
1833 	    {
1834 	      if (opts.do_log)
1835 		{
1836 		  t.stop();
1837 		  std::cerr << "net changes computed!: "<< t << "\n";
1838 		}
1839 	      status = abigail::tools_utils::ABIDIFF_ABI_CHANGE;
1840 	    }
1841 
1842 	  if (opts.do_log)
1843 	    {
1844 	      t.start();
1845 	      std::cerr << "Computing incompatible changes ...\n";
1846 	    }
1847 
1848 	  if (diff->has_incompatible_changes())
1849 	    {
1850 	      if (opts.do_log)
1851 		{
1852 		  t.stop();
1853 		  std::cerr << "incompatible changes computed!: "<< t << "\n";
1854 		}
1855 	      status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
1856 	    }
1857 
1858 	  if (opts.do_log)
1859 	    {
1860 	      t.start();
1861 	      std::cerr << "Computing changes ...\n";
1862 	    }
1863 
1864 	  if (diff->has_changes())
1865 	    {
1866 	      if (opts.do_log)
1867 		{
1868 		  t.stop();
1869 		  std::cerr << "changes computed!: "<< t << "\n";
1870 		}
1871 
1872 	      if (opts.do_log)
1873 		{
1874 		  t.start();
1875 		  std::cerr << "Computing report ...\n";
1876 		}
1877 
1878 	      diff->report(cout);
1879 
1880 	      if (opts.do_log)
1881 		{
1882 		  t.stop();
1883 		  std::cerr << "Report computed!:" << t << "\n";
1884 		}
1885 	    }
1886 	}
1887       else
1888 	status = abigail::tools_utils::ABIDIFF_ERROR;
1889     }
1890 
1891   return status;
1892 }
1893 
1894 #ifdef __ABIGAIL_IN_THE_DEBUGGER__
1895 
1896 /// Emit a textual representation of a given @ref corpus_diff tree to
1897 /// stdout.
1898 ///
1899 /// This is useful when debugging this program.
1900 ///
1901 /// @param diff_tree the diff tree to emit a textual representation
1902 /// for.
1903 void
print_diff_tree(abigail::comparison::corpus_diff * diff_tree)1904 print_diff_tree(abigail::comparison::corpus_diff* diff_tree)
1905 {
1906   print_diff_tree(diff_tree, std::cout);
1907 }
1908 
1909 /// Emit a textual representation of a given @ref corpus_diff tree to
1910 /// stdout.
1911 ///
1912 /// This is useful when debugging this program.
1913 ///
1914 /// @param diff_tree the diff tree to emit a textual representation
1915 /// for.
1916 void
print_diff_tree(abigail::comparison::corpus_diff_sptr diff_tree)1917 print_diff_tree(abigail::comparison::corpus_diff_sptr diff_tree)
1918 {
1919   print_diff_tree(diff_tree, std::cout);
1920 }
1921 
1922 /// Emit a textual representation of a given @ref corpus_diff tree to
1923 /// stdout.
1924 ///
1925 /// This is useful when debugging this program.
1926 ///
1927 /// @param diff_tree the diff tree to emit a textual representation
1928 /// for.
1929 void
print_diff_tree(abigail::comparison::diff_sptr diff_tree)1930 print_diff_tree(abigail::comparison::diff_sptr diff_tree)
1931 {
1932   print_diff_tree(diff_tree.get(), std::cout);
1933 }
1934 
1935 /// Emit a textual representation of a given @ref diff tree to
1936 /// stdout.
1937 ///
1938 /// This is useful when debugging this program.
1939 ///
1940 /// @param diff_tree the diff tree to emit a textual representation
1941 /// for.
1942 void
print_diff_tree(abigail::comparison::diff * diff_tree)1943 print_diff_tree(abigail::comparison::diff* diff_tree)
1944 {
1945   print_diff_tree(diff_tree, std::cout);
1946 }
1947 #endif // __ABIGAIL_IN_THE_DEBUGGER__
1948