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