1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2014-2022 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7
8 /// @file
9 ///
10 /// This program reads a program A, one library L in version V which A
11 /// links against, and the same library L in a different version, V+P.
12 /// The program then checks that A is still ABI compatible with L in
13 /// version V+P.
14 ///
15 /// The program also comes with a "weak mode" in which just the
16 /// application and the library in version V+P need to be provided by
17 /// the user. In that case, the types of functions and variables of
18 /// the library that are consumed by the application are compared to
19 /// the types of the functions and variables expected by the
20 /// application. If they match exactly, then the types of functions
21 /// and variables that the application expects from the library are
22 /// honoured by the library. Otherwise, the library might provide
23 /// functions and variables that mean something different from what
24 /// the application expects and that might signal an ABI
25 /// incompatibility between what the application expects and what the
26 /// library provides.
27
28 #include <unistd.h>
29 #include <cassert>
30 #include <cstdio>
31 #include <cstdlib>
32 #include <cstring>
33 #include <fstream>
34 #include <iostream>
35 #include <memory>
36 #include <string>
37 #include "abg-config.h"
38 #include "abg-tools-utils.h"
39 #include "abg-corpus.h"
40 #include "abg-reader.h"
41 #include "abg-dwarf-reader.h"
42 #include "abg-comparison.h"
43 #include "abg-suppression.h"
44 #ifdef WITH_CTF
45 #include "abg-ctf-reader.h"
46 #endif
47
48 using std::string;
49 using std::cerr;
50 using std::cout;
51 using std::ostream;
52 using std::ofstream;
53 using std::vector;
54 using std::shared_ptr;
55
56 using namespace abigail;
57
58 using abigail::tools_utils::emit_prefix;
59
60 class options
61 {
62 options();
63
64 public:
65 string prog_name;
66 string unknow_option;
67 string app_path;
68 string lib1_path;
69 string lib2_path;
70 shared_ptr<char> app_di_root_path;
71 shared_ptr<char> lib1_di_root_path;
72 shared_ptr<char> lib2_di_root_path;
73 vector<string> suppression_paths;
74 bool display_help;
75 bool display_version;
76 bool weak_mode;
77 bool list_undefined_symbols_only;
78 bool show_base_names;
79 bool show_redundant;
80 bool redundant_opt_set;
81 bool no_redundant_opt_set;
82 bool show_locs;
83 bool fail_no_debug_info;
84 bool ignore_soname;
85 #ifdef WITH_CTF
86 bool use_ctf;
87 #endif
88
options(const char * program_name)89 options(const char* program_name)
90 :prog_name(program_name),
91 display_help(),
92 display_version(),
93 weak_mode(),
94 list_undefined_symbols_only(),
95 show_base_names(),
96 show_redundant(true),
97 redundant_opt_set(),
98 no_redundant_opt_set(),
99 show_locs(true),
100 fail_no_debug_info(),
101 ignore_soname(false)
102 #ifdef WITH_CTF
103 ,
104 use_ctf()
105 #endif
106 {}
107 }; // end struct options
108
109 static void
display_usage(const string & prog_name,ostream & out)110 display_usage(const string& prog_name, ostream& out)
111 {
112 emit_prefix(prog_name, out)
113 << "usage: " << prog_name
114 << " [options] [application-path] [lib-v1-path] [lib-v2-path]"
115 << "\n"
116 << " where options can be: \n"
117 << " --help|-h display this help message\n"
118 << " --version|-v show program version information and exit\n"
119 << " --list-undefined-symbols|-u display the list of "
120 "undefined symbols of the application\n"
121 << " --show-base-names|b in the report, only show the base names "
122 " of the files; not the full paths\n"
123 << " --app-debug-info-dir|--appd <path-to-app-debug-info> set the path "
124 "to the debug information directory for the application\n"
125 << " --lib-debug-info-dir1|--libd1 <path-to-lib-debug-info1> set the path "
126 "to the debug information directory for the first library\n"
127 << " --lib-debug-info-dir2|--libd2 <path-to-lib-debug-info2> set the path "
128 "to the debug information directory for the second library\n"
129 << " --suppressions|--suppr <path> specify a suppression file\n"
130 << " --no-redundant do not display redundant changes\n"
131 << " --no-show-locs do now show location information\n"
132 << " --ignore-soname do not take the SONAMEs into account\n"
133 << " --fail-no-debug-info bail out if no debug info was found\n"
134 << " --redundant display redundant changes (this is the default)\n"
135 << " --weak-mode check compatibility between the application and "
136 "just one version of the library.\n"
137 #ifdef WITH_CTF
138 << " --ctf use CTF instead of DWARF in ELF files\n"
139 #endif
140 ;
141 }
142
143 static bool
parse_command_line(int argc,char * argv[],options & opts)144 parse_command_line(int argc, char* argv[], options& opts)
145 {
146 if (argc < 2)
147 return false;
148
149 for (int i = 1; i < argc; ++i)
150 {
151 if (argv[i][0] != '-')
152 {
153 if (opts.app_path.empty())
154 opts.app_path = argv[i];
155 else if (opts.lib1_path.empty())
156 opts.lib1_path = argv[i];
157 else if (opts.lib2_path.empty())
158 opts.lib2_path = argv[i];
159 else
160 return false;
161 }
162 else if (!strcmp(argv[i], "--version")
163 || !strcmp(argv[i], "-v"))
164 {
165 opts.display_version = true;
166 return true;
167 }
168 else if (!strcmp(argv[i], "--list-undefined-symbols")
169 || !strcmp(argv[i], "-u"))
170 opts.list_undefined_symbols_only = true;
171 else if (!strcmp(argv[i], "--show-base-names")
172 || !strcmp(argv[i], "-b"))
173 opts.show_base_names = true;
174 else if (!strcmp(argv[i], "--app-debug-info-dir")
175 || !strcmp(argv[i], "--appd"))
176 {
177 if (argc <= i + 1
178 || argv[i + 1][0] == '-')
179 return false;
180 // elfutils wants the root path to the debug info to be
181 // absolute.
182 opts.app_di_root_path =
183 abigail::tools_utils::make_path_absolute(argv[i + 1]);
184 ++i;
185 }
186 else if (!strcmp(argv[i], "--lib-debug-info-dir1")
187 || !strcmp(argv[i], "--libd1"))
188 {
189 if (argc <= i + 1
190 || argv[i + 1][0] == '-')
191 return false;
192 // elfutils wants the root path to the debug info to be
193 // absolute.
194 opts.lib1_di_root_path =
195 abigail::tools_utils::make_path_absolute(argv[i + 1]);
196 ++i;
197 }
198 else if (!strcmp(argv[i], "--lib-debug-info-dir2")
199 || !strcmp(argv[i], "--libd2"))
200 {
201 if (argc <= i + 1
202 || argv[i + 1][0] == '-')
203 return false;
204 // elfutils wants the root path to the debug info to be
205 // absolute.
206 opts.lib2_di_root_path =
207 abigail::tools_utils::make_path_absolute(argv[i + 1]);
208 ++i;
209 }
210 else if (!strcmp(argv[i], "--suppressions")
211 || !strcmp(argv[i], "--suppr"))
212 {
213 int j = i + 1;
214 if (j >= argc)
215 return false;
216 opts.suppression_paths.push_back(argv[j]);
217 ++i;
218 }
219 else if (!strcmp(argv[i], "--redundant"))
220 {
221 opts.show_redundant = true;
222 opts.redundant_opt_set = true;
223 }
224 else if (!strcmp(argv[i], "--no-redundant"))
225 {
226 opts.show_redundant = false;
227 opts.no_redundant_opt_set = true;
228 }
229 else if (!strcmp(argv[i], "--no-show-locs"))
230 opts.show_locs = false;
231 else if (!strcmp(argv[i], "--ignore-soname"))
232 opts.ignore_soname=true;
233 else if (!strcmp(argv[i], "--fail-no-debug-info"))
234 opts.fail_no_debug_info = true;
235 else if (!strcmp(argv[i], "--help")
236 || !strcmp(argv[i], "-h"))
237 {
238 opts.display_help = true;
239 return true;
240 }
241 else if (!strcmp(argv[i], "--weak-mode"))
242 opts.weak_mode = true;
243 #ifdef WITH_CTF
244 else if (!strcmp(argv[i], "--ctf"))
245 opts.use_ctf = true;
246 #endif
247 else
248 {
249 opts.unknow_option = argv[i];
250 return false;
251 }
252 }
253
254 if (!opts.list_undefined_symbols_only)
255 {
256 if (opts.app_path.empty()
257 || opts.lib1_path.empty())
258 return false;
259 if (!opts.weak_mode && opts.lib2_path.empty())
260 opts.weak_mode = true;
261 }
262
263 return true;
264 }
265
266 using abigail::tools_utils::check_file;
267 using abigail::tools_utils::base_name;
268 using abigail::tools_utils::abidiff_status;
269 using abigail::ir::environment;
270 using abigail::ir::environment_sptr;
271 using abigail::corpus;
272 using abigail::corpus_sptr;
273 using abigail::ir::elf_symbols;
274 using abigail::ir::demangle_cplus_mangled_name;
275 using abigail::ir::type_base_sptr;
276 using abigail::ir::function_type_sptr;
277 using abigail::ir::function_decl;
278 using abigail::ir::var_decl;
279 using abigail::comparison::diff_context_sptr;
280 using abigail::comparison::diff_context;
281 using abigail::comparison::diff_sptr;
282 using abigail::comparison::corpus_diff;
283 using abigail::comparison::corpus_diff_sptr;
284 using abigail::comparison::function_type_diff_sptr;
285 using abigail::comparison::compute_diff;
286 using abigail::suppr::suppression_sptr;
287 using abigail::suppr::suppressions_type;
288 using abigail::suppr::read_suppressions;
289
290 /// Create the context of a diff.
291 ///
292 /// Create the diff context, initialize it and return a smart pointer
293 /// to it.
294 ///
295 /// @param opts the options of the program.
296 ///
297 /// @return a smart pointer to the newly created diff context.
298 static diff_context_sptr
create_diff_context(const options & opts)299 create_diff_context(const options& opts)
300 {
301 diff_context_sptr ctxt(new diff_context());
302 ctxt->show_added_fns(false);
303 ctxt->show_added_vars(false);
304 ctxt->show_added_symbols_unreferenced_by_debug_info(false);
305 ctxt->show_linkage_names(true);
306 ctxt->show_redundant_changes(opts.show_redundant);
307 ctxt->show_locs(opts.show_locs);
308 // Intentional logic flip of ignore_soname
309 ctxt->show_soname_change(!opts.ignore_soname);
310 ctxt->switch_categories_off
311 (abigail::comparison::ACCESS_CHANGE_CATEGORY
312 | abigail::comparison::COMPATIBLE_TYPE_CHANGE_CATEGORY
313 | abigail::comparison::HARMLESS_DECL_NAME_CHANGE_CATEGORY
314 | abigail::comparison::NON_VIRT_MEM_FUN_CHANGE_CATEGORY
315 | abigail::comparison::STATIC_DATA_MEMBER_CHANGE_CATEGORY
316 | abigail::comparison::HARMLESS_ENUM_CHANGE_CATEGORY
317 | abigail::comparison::HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY);
318
319 // Load suppression specifications, if there are any.
320 suppressions_type supprs;
321 for (vector<string>::const_iterator i = opts.suppression_paths.begin();
322 i != opts.suppression_paths.end();
323 ++i)
324 if (check_file(*i, cerr, opts.prog_name))
325 read_suppressions(*i, supprs);
326
327 if (!supprs.empty())
328 ctxt->add_suppressions(supprs);
329
330 return ctxt;
331 }
332
333 /// Perform a compatibility check of an application corpus linked
334 /// against a first version of library corpus, with a second version
335 /// of the same library.
336 ///
337 /// @param opts the options the tool got invoked with.
338 ///
339 /// @param ctxt the context of the diff to be performed.
340 ///
341 /// @param app_corpus the application corpus to consider.
342 ///
343 /// @param lib1_corpus the library corpus that got linked with the
344 /// application which corpus is @p app_corpus.
345 ///
346 /// @param lib2_corpus the second version of the library corpus @p
347 /// lib1_corpus. This function checks that the functions and
348 /// variables that @p app_corpus expects from lib1_corpus are still
349 /// present in @p lib2_corpus and that their types mean the same
350 /// thing.
351 ///
352 /// @return a status bitfield.
353 static abidiff_status
perform_compat_check_in_normal_mode(options & opts,diff_context_sptr & ctxt,corpus_sptr app_corpus,corpus_sptr lib1_corpus,corpus_sptr lib2_corpus)354 perform_compat_check_in_normal_mode(options& opts,
355 diff_context_sptr& ctxt,
356 corpus_sptr app_corpus,
357 corpus_sptr lib1_corpus,
358 corpus_sptr lib2_corpus)
359 {
360 ABG_ASSERT(lib1_corpus);
361 ABG_ASSERT(lib2_corpus);
362 ABG_ASSERT(app_corpus);
363
364 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
365
366 // compare lib1 and lib2 only by looking at the functions and
367 // variables which symbols are those undefined in the app.
368
369 for (elf_symbols::const_iterator i =
370 app_corpus->get_sorted_undefined_fun_symbols().begin();
371 i != app_corpus->get_sorted_undefined_fun_symbols().end();
372 ++i)
373 {
374 string id = (*i)->get_id_string();
375 lib1_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
376 lib2_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
377 }
378 for (elf_symbols::const_iterator i =
379 app_corpus->get_sorted_undefined_var_symbols().begin();
380 i != app_corpus->get_sorted_undefined_var_symbols().end();
381 ++i)
382 {
383 string id = (*i)->get_id_string();
384 lib1_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
385 lib2_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
386 }
387
388 if (!app_corpus->get_sorted_undefined_var_symbols().empty()
389 || !app_corpus->get_sorted_undefined_fun_symbols().empty())
390 {
391 lib1_corpus->maybe_drop_some_exported_decls();
392 lib2_corpus->maybe_drop_some_exported_decls();
393 }
394
395 // Now really do the diffing.
396 corpus_diff_sptr changes = compute_diff(lib1_corpus, lib2_corpus, ctxt);
397
398 if (changes->has_net_changes())
399 {
400 string app_path = opts.app_path,
401 lib1_path = opts.lib1_path,
402 lib2_path = opts.lib2_path;
403
404 if (opts.show_base_names)
405 {
406 base_name(opts.app_path, app_path);
407 base_name(opts.lib1_path, lib1_path);
408 base_name(opts.lib2_path, lib2_path);
409 }
410
411 status |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
412
413 bool abi_broke_for_sure = changes->has_incompatible_changes();
414
415 cout << "ELF file '" << app_path << "'";
416 if (abi_broke_for_sure)
417 {
418 cout << " is not ";
419 status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
420 }
421 else
422 cout << " might not be ";
423
424 cout << "ABI compatible with '" << lib2_path
425 << "' due to differences with '" << lib1_path
426 << "' below:\n";
427 changes->report(cout);
428 }
429
430 return status;
431 }
432
433 /// A description of a change of the type of a function. It contains
434 /// the declaration of the function we are interested in, as well as
435 /// the differences found in the type of that function.
436 struct fn_change
437 {
438 function_decl* decl;
439 function_type_diff_sptr diff;
440
fn_changefn_change441 fn_change()
442 : decl()
443 {}
444
fn_changefn_change445 fn_change(function_decl* decl,
446 function_type_diff_sptr difference)
447 : decl(decl),
448 diff(difference)
449 {}
450 }; // end struct fn_change
451
452 /// An description of a change of the type of a variable. It contains
453 /// the declaration of the variable we are interested in, as well as
454 /// the differences found in the type of that variable.
455 struct var_change
456 {
457 var_decl* decl;
458 diff_sptr diff;
459
var_changevar_change460 var_change()
461 : decl()
462 {}
463
var_changevar_change464 var_change(var_decl* var,
465 diff_sptr difference)
466 : decl(var),
467 diff(difference)
468 {}
469 }; // end struct var_change
470
471 /// Perform a compatibility check of an application corpus and a
472 /// library corpus.
473 ///
474 /// The types of the variables and functions exported by the library
475 /// and consumed by the application are compared with the types
476 /// expected by the application. This function checks that the types
477 /// mean the same thing; otherwise it emits on standard output type
478 /// layout differences found.
479 ///
480 /// @param opts the options the tool got invoked with.
481 ///
482 /// @param app_corpus the application corpus to consider.
483 ///
484 /// @param lib_corpus the library corpus to consider.
485 ///
486 /// @return a status bitfield.
487 static abidiff_status
perform_compat_check_in_weak_mode(options & opts,diff_context_sptr & ctxt,corpus_sptr app_corpus,corpus_sptr lib_corpus)488 perform_compat_check_in_weak_mode(options& opts,
489 diff_context_sptr& ctxt,
490 corpus_sptr app_corpus,
491 corpus_sptr lib_corpus)
492 {
493 ABG_ASSERT(lib_corpus);
494 ABG_ASSERT(app_corpus);
495
496 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
497
498 // Functions and variables defined and exported by lib_corpus which
499 // symbols are undefined in app_corpus are the artifacts we are
500 // interested in.
501 //
502 // So let's drop all functions and variables from lib_corpus that
503 // are so that their symbols are *NOT* undefined in app_corpus.
504 //
505 // In other words, let's only keep the functiond and variables from
506 // lib_corpus that are consumed by app_corpus.
507
508 for (elf_symbols::const_iterator i =
509 app_corpus->get_sorted_undefined_fun_symbols().begin();
510 i != app_corpus->get_sorted_undefined_fun_symbols().end();
511 ++i)
512 {
513 string id = (*i)->get_id_string();
514 lib_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
515 }
516
517 for (elf_symbols::const_iterator i =
518 app_corpus->get_sorted_undefined_var_symbols().begin();
519 i != app_corpus->get_sorted_undefined_var_symbols().end();
520 ++i)
521 {
522 string id = (*i)->get_id_string();
523 lib_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
524 }
525
526 if (!app_corpus->get_sorted_undefined_var_symbols().empty()
527 || !app_corpus->get_sorted_undefined_fun_symbols().empty())
528 lib_corpus->maybe_drop_some_exported_decls();
529
530 // OK now, lib_corpus only contains functions and variables which
531 // symbol are consumed by app_corpus.
532
533 // So we are now going to compare the functions that are exported by
534 // lib_corpus against those that app_corpus expects.
535 //
536 // In other words, the functions which symbols are defined by
537 // lib_corpus are going to be compared to the functions and
538 // variables which are undefined in app_corpus.
539
540 {
541 function_type_sptr lib_fn_type, app_fn_type;
542 vector<fn_change> fn_changes;
543 for (corpus::functions::const_iterator i =
544 lib_corpus->get_functions().begin();
545 i != lib_corpus->get_functions().end();
546 ++i)
547 {
548 // lib_fn_type contains the type of a function that is defined
549 // in lib_corpus.
550 lib_fn_type = (*i)->get_type();
551 ABG_ASSERT(lib_fn_type);
552
553 // app_fn_type contains the the "version" of lib_fn_type that
554 // is expected by app_corpus.
555 app_fn_type = lookup_or_synthesize_fn_type(lib_fn_type, *app_corpus);
556
557 // Now lets compare the type expected by app_corpus against
558 // the type actually provided by lib_fn_type.
559 function_type_diff_sptr fn_type_diff;
560 if (app_fn_type)
561 fn_type_diff = compute_diff(app_fn_type, lib_fn_type, ctxt);
562
563 // If the two types of functions are different, then let's
564 // store their difference in the "fn_changes" vector.
565 if (fn_type_diff && fn_type_diff->to_be_reported())
566 fn_changes.push_back(fn_change(*i, fn_type_diff));
567 }
568
569 string lib1_path = opts.lib1_path, app_path = opts.app_path;
570 if (opts.show_base_names)
571 {
572 base_name(opts.lib1_path, lib1_path);
573 base_name(opts.app_path, app_path);
574 }
575
576 // If some function changes were detected, then report them.
577 if (!fn_changes.empty())
578 {
579 cout << "functions defined in library "
580 << "'" << lib1_path << "'\n"
581 << "have sub-types that are different from what application "
582 << "'" << app_path << "' "
583 << "expects:\n\n";
584 for (vector<fn_change>::const_iterator i = fn_changes.begin();
585 i != fn_changes.end();
586 ++i)
587 {
588 cout << " "
589 << i->decl->get_pretty_representation()
590 << ":\n";
591 i->diff->report(cout, " ");
592 cout << "\n";
593 }
594 }
595
596 if (!fn_changes.empty())
597 status |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
598
599 // OK now, let's do something similar for *variables* changes.
600 //
601 // That is, let's compare the variables expected by app_corpus
602 // against the variables actually provided by lib_corpus and
603 // report the difference that might have been found.
604
605 type_base_sptr lib_var_type, app_var_type;
606 vector<var_change> var_changes;
607 for (corpus::variables::const_iterator i =
608 lib_corpus->get_variables().begin();
609 i != lib_corpus->get_variables().end();
610 ++i)
611 {
612 lib_var_type = (*i)->get_type();
613 ABG_ASSERT(lib_var_type);
614 app_var_type = lookup_type(lib_var_type, *app_corpus);
615 diff_sptr type_diff;
616 if (app_var_type)
617 type_diff = compute_diff(app_var_type, lib_var_type, ctxt);
618 if (type_diff && type_diff->to_be_reported())
619 var_changes.push_back(var_change(*i, type_diff));
620 }
621 if (!var_changes.empty())
622 {
623 cout << "variables defined in library "
624 << "'" << lib1_path << "'\n"
625 << "have sub-types that are different from what application "
626 << "'" << app_path << "' "
627 << "expects:\n\n";
628 for (vector<var_change>::const_iterator i = var_changes.begin();
629 i != var_changes.end();
630 ++i)
631 {
632 cout << " "
633 << i->decl->get_pretty_representation()
634 << ":\n";
635 i->diff->report(cout, " ");
636 cout << "\n";
637 }
638 }
639 }
640 return status;
641 }
642
643 /// Read an ABI corpus, be it from ELF or abixml.
644 ///
645 /// @param opts the options passed from the user to the program.
646 ///
647 /// @param status the resulting fe_iface::status to send back to the
648 /// caller.
649 ///
650 /// @param di_roots the directories from where to look for debug info.
651 ///
652 /// @param env the environment used for libabigail.
653 ///
654 /// @param path the path to the ABI corpus to read from.
655 static corpus_sptr
read_corpus(options opts,abigail::fe_iface::status & status,const vector<char ** > di_roots,environment & env,const string & path)656 read_corpus(options opts,
657 abigail::fe_iface::status& status,
658 const vector<char**> di_roots,
659 environment &env,
660 const string &path)
661 {
662 corpus_sptr retval = NULL;
663 abigail::tools_utils::file_type type =
664 abigail::tools_utils::guess_file_type(path);
665 abigail::fe_iface_sptr rdr;
666
667 switch (type)
668 {
669 case abigail::tools_utils::FILE_TYPE_UNKNOWN:
670 emit_prefix(opts.prog_name, cerr)
671 << "Unknown content type for file " << path << "\n";
672 break;
673 case abigail::tools_utils::FILE_TYPE_ELF:
674 {
675 #ifdef WITH_CTF
676 if (opts.use_ctf)
677 {
678 rdr = ctf::create_reader(path, env);
679 ABG_ASSERT(rdr);
680
681 retval = ctf::read_corpus(rdr.get(), status);
682 }
683 else
684 #endif
685 retval = dwarf::read_corpus_from_elf(path, di_roots, env,
686 /*load_all_types=*/opts.weak_mode,
687 status);
688 }
689 break;
690 case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
691 {
692 rdr = abixml::create_reader(path, env);
693 assert(rdr);
694 retval = rdr->read_corpus(status);
695 }
696 break;
697 case abigail::tools_utils::FILE_TYPE_AR:
698 case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
699 case abigail::tools_utils::FILE_TYPE_RPM:
700 case abigail::tools_utils::FILE_TYPE_SRPM:
701 case abigail::tools_utils::FILE_TYPE_DEB:
702 case abigail::tools_utils::FILE_TYPE_DIR:
703 case abigail::tools_utils::FILE_TYPE_TAR:
704 case abigail::tools_utils::FILE_TYPE_NATIVE_BI:
705 break;
706 }
707
708 return retval;
709 }
710
711 int
main(int argc,char * argv[])712 main(int argc, char* argv[])
713 {
714 options opts(argv[0]);
715
716 if (!parse_command_line(argc, argv, opts))
717 {
718 if (!opts.unknow_option.empty())
719 {
720 emit_prefix(argv[0], cerr)
721 << "unrecognized option: " << opts.unknow_option << "\n"
722 << "try the --help option for more information\n";
723 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
724 | abigail::tools_utils::ABIDIFF_ERROR);
725 }
726
727 emit_prefix(argv[0], cerr)
728 << "wrong invocation\n"
729 << "try the --help option for more information\n";
730 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
731 | abigail::tools_utils::ABIDIFF_ERROR);
732 }
733
734 if (opts.display_help)
735 {
736 display_usage(argv[0], cout);
737 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
738 | abigail::tools_utils::ABIDIFF_ERROR);
739 }
740
741 if (opts.display_version)
742 {
743 emit_prefix(argv[0], cout)
744 << abigail::tools_utils::get_library_version_string()
745 << "\n";
746 return 0;
747 }
748
749 if (opts.weak_mode && !opts.lib2_path.empty())
750 {
751 emit_prefix(argv[0], cout)
752 << "WARNING: The \'--weak-mode\' option is used. The "
753 << opts.lib2_path << " will be ignored automatically\n";
754 }
755
756 if (opts.redundant_opt_set && opts.no_redundant_opt_set)
757 {
758 emit_prefix(argv[0], cerr)
759 << "ERROR: The \'--redundant\' and '--no-redundant' option are in conflict. "
760 << "Please select only one option to use.\n";
761 return 1;
762 }
763
764 ABG_ASSERT(!opts.app_path.empty());
765 if (!abigail::tools_utils::check_file(opts.app_path, cerr, opts.prog_name))
766 return abigail::tools_utils::ABIDIFF_ERROR;
767
768 // Create the context of the diff
769 diff_context_sptr ctxt = create_diff_context(opts);
770
771 // Check if any suppression specification prevents us from
772 // performing the compatibility checking.
773 suppressions_type& supprs = ctxt->suppressions();
774 bool files_suppressed = (file_is_suppressed(opts.app_path, supprs)
775 || file_is_suppressed(opts.lib1_path, supprs)
776 || file_is_suppressed(opts.lib2_path, supprs));
777
778 if (files_suppressed)
779 // We don't have to compare anything because a user
780 // suppression specification file instructs us to avoid
781 // loading either one of the input files.
782 return abigail::tools_utils::ABIDIFF_OK;
783
784 // Read the application ELF file.
785 char * app_di_root = opts.app_di_root_path.get();
786 vector<char**> app_di_roots;
787 app_di_roots.push_back(&app_di_root);
788 abigail::fe_iface::status status = abigail::fe_iface::STATUS_UNKNOWN;
789 environment env;
790
791 corpus_sptr app_corpus = read_corpus(opts, status,
792 app_di_roots, env,
793 opts.app_path);
794 if (!app_corpus)
795 {
796 emit_prefix(argv[0], cerr) << opts.app_path
797 << " is not a supported file\n";
798 return abigail::tools_utils::ABIDIFF_ERROR;
799 }
800
801 if (opts.fail_no_debug_info
802 && (status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
803 && (status & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND))
804 {
805 emit_prefix(argv[0], cerr) << opts.app_path
806 << " does not have debug symbols\n";
807 return abigail::tools_utils::ABIDIFF_ERROR;
808 }
809 if (status & abigail::fe_iface::STATUS_NO_SYMBOLS_FOUND)
810 {
811 emit_prefix(argv[0], cerr)
812 << "could not read symbols from " << opts.app_path << "\n";
813 return abigail::tools_utils::ABIDIFF_ERROR;
814 }
815 if (!(status & abigail::fe_iface::STATUS_OK))
816 {
817 emit_prefix(argv[0], cerr)
818 << "could not read file " << opts.app_path << "\n";
819 return abigail::tools_utils::ABIDIFF_ERROR;
820 }
821
822 if (opts.list_undefined_symbols_only)
823 {
824 for (elf_symbols::const_iterator i =
825 app_corpus->get_sorted_undefined_fun_symbols().begin();
826 i != app_corpus->get_sorted_undefined_fun_symbols().end();
827 ++i)
828 {
829 string id = (*i)->get_id_string();
830 string sym_name = (*i)->get_name();
831 string demangled_name = demangle_cplus_mangled_name(sym_name);
832 if (demangled_name != sym_name)
833 cout << demangled_name << " {" << id << "}\n";
834 else
835 cout << id << "\n";
836 }
837 return abigail::tools_utils::ABIDIFF_OK;
838 }
839
840 // Read the first version of the library.
841 ABG_ASSERT(!opts.lib1_path.empty());
842 if (!abigail::tools_utils::check_file(opts.lib1_path, cerr, opts.prog_name))
843 return abigail::tools_utils::ABIDIFF_ERROR;
844
845 char * lib1_di_root = opts.lib1_di_root_path.get();
846 vector<char**> lib1_di_roots;
847 lib1_di_roots.push_back(&lib1_di_root);
848 corpus_sptr lib1_corpus = read_corpus(opts, status,
849 lib1_di_roots,
850 env, opts.lib1_path);
851 if (!lib1_corpus)
852 {
853 emit_prefix(argv[0], cerr) << opts.lib1_path
854 << " is not a supported file\n";
855 return abigail::tools_utils::ABIDIFF_ERROR;
856 }
857 if (opts.fail_no_debug_info
858 && (status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
859 && (status & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND))
860 emit_prefix(argv[0], cerr)
861 << "could not read debug info for " << opts.lib1_path << "\n";
862 if (status & abigail::fe_iface::STATUS_NO_SYMBOLS_FOUND)
863 {
864 emit_prefix(argv[0], cerr) << "could not read symbols from "
865 << opts.lib1_path << "\n";
866 return abigail::tools_utils::ABIDIFF_ERROR;
867 }
868 if (!(status & abigail::fe_iface::STATUS_OK))
869 {
870 emit_prefix(argv[0], cerr)
871 << "could not read file " << opts.lib1_path << "\n";
872 return abigail::tools_utils::ABIDIFF_ERROR;
873 }
874
875 // Read the second version of the library.
876 corpus_sptr lib2_corpus;
877 if (!opts.weak_mode)
878 {
879 ABG_ASSERT(!opts.lib2_path.empty());
880 char * lib2_di_root = opts.lib2_di_root_path.get();
881 vector<char**> lib2_di_roots;
882 lib2_di_roots.push_back(&lib2_di_root);
883 lib2_corpus = read_corpus(opts, status,
884 lib2_di_roots, env,
885 opts.lib2_path);
886 if (!lib2_corpus)
887 {
888 emit_prefix(argv[0], cerr) << opts.lib2_path
889 << " is not a supported file\n";
890 return abigail::tools_utils::ABIDIFF_ERROR;
891 }
892
893 if (opts.fail_no_debug_info
894 && (status & abigail::fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
895 && (status & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND))
896 {
897 emit_prefix(argv[0], cerr)
898 << "could not read debug info for " << opts.lib2_path << "\n";
899 return abigail::tools_utils::ABIDIFF_ERROR;
900 }
901 if (status & abigail::fe_iface::STATUS_NO_SYMBOLS_FOUND)
902 {
903 emit_prefix(argv[0], cerr)
904 << "could not read symbols from " << opts.lib2_path << "\n";
905 return abigail::tools_utils::ABIDIFF_ERROR;
906 }
907 if (!(status & abigail::fe_iface::STATUS_OK))
908 {
909 emit_prefix(argv[0], cerr)
910 << "could not read file " << opts.lib2_path << "\n";
911 return abigail::tools_utils::ABIDIFF_ERROR;
912 }
913 }
914
915 abidiff_status s = abigail::tools_utils::ABIDIFF_OK;
916
917 if (opts.weak_mode)
918 s = perform_compat_check_in_weak_mode(opts, ctxt,
919 app_corpus,
920 lib1_corpus);
921 else
922 s = perform_compat_check_in_normal_mode(opts, ctxt,
923 app_corpus,
924 lib1_corpus,
925 lib2_corpus);
926
927 return s;
928 }
929