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