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