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