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