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