1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2014-2020 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-dwarf-reader.h"
41 #include "abg-comparison.h"
42 #include "abg-suppression.h"
43
44 using std::string;
45 using std::cerr;
46 using std::cout;
47 using std::ostream;
48 using std::ofstream;
49 using std::vector;
50 using std::shared_ptr;
51
52 using abigail::tools_utils::emit_prefix;
53
54 class options
55 {
56 options();
57
58 public:
59 string prog_name;
60 string unknow_option;
61 string app_path;
62 string lib1_path;
63 string lib2_path;
64 shared_ptr<char> app_di_root_path;
65 shared_ptr<char> lib1_di_root_path;
66 shared_ptr<char> lib2_di_root_path;
67 vector<string> suppression_paths;
68 bool display_help;
69 bool display_version;
70 bool weak_mode;
71 bool list_undefined_symbols_only;
72 bool show_base_names;
73 bool show_redundant;
74 bool show_locs;
75
options(const char * program_name)76 options(const char* program_name)
77 :prog_name(program_name),
78 display_help(),
79 display_version(),
80 weak_mode(),
81 list_undefined_symbols_only(),
82 show_base_names(),
83 show_redundant(true),
84 show_locs(true)
85 {}
86 }; // end struct options
87
88 static void
display_usage(const string & prog_name,ostream & out)89 display_usage(const string& prog_name, ostream& out)
90 {
91 emit_prefix(prog_name, out)
92 << "usage: " << prog_name
93 << " [options] [application-path] [lib-v1-path] [lib-v2-path]"
94 << "\n"
95 << " where options can be: \n"
96 << " --help|-h display this help message\n"
97 << " --version|-v show program version information and exit\n"
98 << " --list-undefined-symbols|-u display the list of "
99 "undefined symbols of the application\n"
100 << " --show-base-names|b in the report, only show the base names "
101 " of the files; not the full paths\n"
102 << " --app-debug-info-dir|--appd <path-to-app-debug-info> set the path "
103 "to the debug information directory for the application\n"
104 << " --lib-debug-info-dir1|--libd1 <path-to-lib-debug-info1> set the path "
105 "to the debug information directory for the first library\n"
106 << " --lib-debug-info-dir2|--libd2 <path-to-lib-debug-info2> set the path "
107 "to the debug information directory for the second library\n"
108 << "--suppressions|--suppr <path> specify a suppression file\n"
109 << "--no-redundant do not display redundant changes\n"
110 << "--no-show-locs do now show location information\n"
111 << "--redundant display redundant changes (this is the default)\n"
112 << "--weak-mode check compatibility between the application and "
113 "just one version of the library."
114 ;
115 }
116
117 static bool
parse_command_line(int argc,char * argv[],options & opts)118 parse_command_line(int argc, char* argv[], options& opts)
119 {
120 if (argc < 2)
121 return false;
122
123 for (int i = 1; i < argc; ++i)
124 {
125 if (argv[i][0] != '-')
126 {
127 if (opts.app_path.empty())
128 opts.app_path = argv[i];
129 else if (opts.lib1_path.empty())
130 opts.lib1_path = argv[i];
131 else if (opts.lib2_path.empty())
132 opts.lib2_path = argv[i];
133 else
134 return false;
135 }
136 else if (!strcmp(argv[i], "--version")
137 || !strcmp(argv[i], "-v"))
138 {
139 opts.display_version = true;
140 return true;
141 }
142 else if (!strcmp(argv[i], "--list-undefined-symbols")
143 || !strcmp(argv[i], "-u"))
144 opts.list_undefined_symbols_only = true;
145 else if (!strcmp(argv[i], "--show-base-names")
146 || !strcmp(argv[i], "-b"))
147 opts.show_base_names = true;
148 else if (!strcmp(argv[i], "--app-debug-info-dir")
149 || !strcmp(argv[i], "--appd"))
150 {
151 if (argc <= i + 1
152 || argv[i + 1][0] == '-')
153 return false;
154 // elfutils wants the root path to the debug info to be
155 // absolute.
156 opts.app_di_root_path =
157 abigail::tools_utils::make_path_absolute(argv[i + 1]);
158 ++i;
159 }
160 else if (!strcmp(argv[i], "--lib-debug-info-dir1")
161 || !strcmp(argv[i], "--libd1"))
162 {
163 if (argc <= i + 1
164 || argv[i + 1][0] == '-')
165 return false;
166 // elfutils wants the root path to the debug info to be
167 // absolute.
168 opts.lib1_di_root_path =
169 abigail::tools_utils::make_path_absolute(argv[i + 1]);
170 ++i;
171 }
172 else if (!strcmp(argv[i], "--lib-debug-info-dir2")
173 || !strcmp(argv[i], "--libd2"))
174 {
175 if (argc <= i + 1
176 || argv[i + 1][0] == '-')
177 return false;
178 // elfutils wants the root path to the debug info to be
179 // absolute.
180 opts.lib2_di_root_path =
181 abigail::tools_utils::make_path_absolute(argv[i + 1]);
182 ++i;
183 }
184 else if (!strcmp(argv[i], "--suppressions")
185 || !strcmp(argv[i], "--suppr"))
186 {
187 int j = i + 1;
188 if (j >= argc)
189 return false;
190 opts.suppression_paths.push_back(argv[j]);
191 ++i;
192 }
193 else if (!strcmp(argv[i], "--redundant"))
194 opts.show_redundant = true;
195 else if (!strcmp(argv[i], "--no-redundant"))
196 opts.show_redundant = false;
197 else if (!strcmp(argv[i], "--no-show-locs"))
198 opts.show_locs = false;
199 else if (!strcmp(argv[i], "--help")
200 || !strcmp(argv[i], "-h"))
201 {
202 opts.display_help = true;
203 return true;
204 }
205 else if (!strcmp(argv[i], "--weak-mode"))
206 opts.weak_mode = true;
207 else
208 {
209 opts.unknow_option = argv[i];
210 return false;
211 }
212 }
213
214 if (!opts.list_undefined_symbols_only)
215 {
216 if (opts.app_path.empty()
217 || opts.lib1_path.empty())
218 return false;
219 if (!opts.weak_mode && opts.lib2_path.empty())
220 opts.weak_mode = true;
221 }
222
223 return true;
224 }
225
226 using abigail::tools_utils::check_file;
227 using abigail::tools_utils::base_name;
228 using abigail::tools_utils::abidiff_status;
229 using abigail::ir::environment;
230 using abigail::ir::environment_sptr;
231 using abigail::corpus;
232 using abigail::corpus_sptr;
233 using abigail::ir::elf_symbols;
234 using abigail::ir::demangle_cplus_mangled_name;
235 using abigail::ir::type_base_sptr;
236 using abigail::ir::function_type_sptr;
237 using abigail::ir::function_decl;
238 using abigail::ir::var_decl;
239 using abigail::dwarf_reader::status;
240 using abigail::dwarf_reader::read_corpus_from_elf;
241 using abigail::comparison::diff_context_sptr;
242 using abigail::comparison::diff_context;
243 using abigail::comparison::diff_sptr;
244 using abigail::comparison::corpus_diff;
245 using abigail::comparison::corpus_diff_sptr;
246 using abigail::comparison::function_type_diff_sptr;
247 using abigail::comparison::compute_diff;
248 using abigail::suppr::suppression_sptr;
249 using abigail::suppr::suppressions_type;
250 using abigail::suppr::read_suppressions;
251
252 /// Create the context of a diff.
253 ///
254 /// Create the diff context, initialize it and return a smart pointer
255 /// to it.
256 ///
257 /// @param opts the options of the program.
258 ///
259 /// @return a smart pointer to the newly created diff context.
260 static diff_context_sptr
create_diff_context(const options & opts)261 create_diff_context(const options& opts)
262 {
263 diff_context_sptr ctxt(new diff_context());
264 ctxt->show_added_fns(false);
265 ctxt->show_added_vars(false);
266 ctxt->show_added_symbols_unreferenced_by_debug_info(false);
267 ctxt->show_linkage_names(true);
268 ctxt->show_redundant_changes(opts.show_redundant);
269 ctxt->show_locs(opts.show_locs);
270 ctxt->switch_categories_off
271 (abigail::comparison::ACCESS_CHANGE_CATEGORY
272 | abigail::comparison::COMPATIBLE_TYPE_CHANGE_CATEGORY
273 | abigail::comparison::HARMLESS_DECL_NAME_CHANGE_CATEGORY
274 | abigail::comparison::NON_VIRT_MEM_FUN_CHANGE_CATEGORY
275 | abigail::comparison::STATIC_DATA_MEMBER_CHANGE_CATEGORY
276 | abigail::comparison::HARMLESS_ENUM_CHANGE_CATEGORY
277 | abigail::comparison::HARMLESS_SYMBOL_ALIAS_CHANGE_CATEGORY);
278
279 // Load suppression specifications, if there are any.
280 suppressions_type supprs;
281 for (vector<string>::const_iterator i = opts.suppression_paths.begin();
282 i != opts.suppression_paths.end();
283 ++i)
284 if (check_file(*i, cerr, opts.prog_name))
285 read_suppressions(*i, supprs);
286
287 if (!supprs.empty())
288 ctxt->add_suppressions(supprs);
289
290 return ctxt;
291 }
292
293 /// Perform a compatibility check of an application corpus linked
294 /// against a first version of library corpus, with a second version
295 /// of the same library.
296 ///
297 /// @param opts the options the tool got invoked with.
298 ///
299 /// @param ctxt the context of the diff to be performed.
300 ///
301 /// @param app_corpus the application corpus to consider.
302 ///
303 /// @param lib1_corpus the library corpus that got linked with the
304 /// application which corpus is @p app_corpus.
305 ///
306 /// @param lib2_corpus the second version of the library corpus @p
307 /// lib1_corpus. This function checks that the functions and
308 /// variables that @p app_corpus expects from lib1_corpus are still
309 /// present in @p lib2_corpus and that their types mean the same
310 /// thing.
311 ///
312 /// @return a status bitfield.
313 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)314 perform_compat_check_in_normal_mode(options& opts,
315 diff_context_sptr& ctxt,
316 corpus_sptr app_corpus,
317 corpus_sptr lib1_corpus,
318 corpus_sptr lib2_corpus)
319 {
320 ABG_ASSERT(lib1_corpus);
321 ABG_ASSERT(lib2_corpus);
322 ABG_ASSERT(app_corpus);
323
324 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
325
326 // compare lib1 and lib2 only by looking at the functions and
327 // variables which symbols are those undefined in the app.
328
329 for (elf_symbols::const_iterator i =
330 app_corpus->get_sorted_undefined_fun_symbols().begin();
331 i != app_corpus->get_sorted_undefined_fun_symbols().end();
332 ++i)
333 {
334 string id = (*i)->get_id_string();
335 lib1_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
336 lib2_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
337 }
338 for (elf_symbols::const_iterator i =
339 app_corpus->get_sorted_undefined_var_symbols().begin();
340 i != app_corpus->get_sorted_undefined_var_symbols().end();
341 ++i)
342 {
343 string id = (*i)->get_id_string();
344 lib1_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
345 lib2_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
346 }
347
348 if (!app_corpus->get_sorted_undefined_var_symbols().empty()
349 || !app_corpus->get_sorted_undefined_fun_symbols().empty())
350 {
351 lib1_corpus->maybe_drop_some_exported_decls();
352 lib2_corpus->maybe_drop_some_exported_decls();
353 }
354
355 // Now really do the diffing.
356 corpus_diff_sptr changes = compute_diff(lib1_corpus, lib2_corpus, ctxt);
357
358 if (changes->has_net_changes())
359 {
360 string app_path = opts.app_path,
361 lib1_path = opts.lib1_path,
362 lib2_path = opts.lib2_path;
363
364 if (opts.show_base_names)
365 {
366 base_name(opts.app_path, app_path);
367 base_name(opts.lib1_path, lib1_path);
368 base_name(opts.lib2_path, lib2_path);
369 }
370
371 status |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
372
373 bool abi_broke_for_sure = changes->has_incompatible_changes();
374
375 cout << "ELF file '" << app_path << "'";
376 if (abi_broke_for_sure)
377 {
378 cout << " is not ";
379 status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
380 }
381 else
382 cout << " might not be ";
383
384 cout << "ABI compatible with '" << lib2_path
385 << "' due to differences with '" << lib1_path
386 << "' below:\n";
387 changes->report(cout);
388 }
389
390 return status;
391 }
392
393 /// An description of a change of the type of a function. It contains
394 /// the declaration of the function we are interested in, as well as
395 /// the differences found in the type of that function.
396 struct fn_change
397 {
398 function_decl* decl;
399 function_type_diff_sptr diff;
400
fn_changefn_change401 fn_change()
402 : decl()
403 {}
404
fn_changefn_change405 fn_change(function_decl* decl,
406 function_type_diff_sptr difference)
407 : decl(decl),
408 diff(difference)
409 {}
410 }; // end struct fn_change
411
412 /// An description of a change of the type of a variable. It contains
413 /// the declaration of the variable we are interested in, as well as
414 /// the differences found in the type of that variable.
415 struct var_change
416 {
417 var_decl* decl;
418 diff_sptr diff;
419
var_changevar_change420 var_change()
421 : decl()
422 {}
423
var_changevar_change424 var_change(var_decl* var,
425 diff_sptr difference)
426 : decl(var),
427 diff(difference)
428 {}
429 }; // end struct var_change
430
431 /// Perform a compatibility check of an application corpus and a
432 /// library corpus.
433 ///
434 /// The types of the variables and functions exported by the library
435 /// and consumed by the application are compared with the types
436 /// expected by the application. This function checks that the types
437 /// mean the same thing; otherwise it emits on standard output type
438 /// layout differences found.
439 ///
440 /// @param opts the options the tool got invoked with.
441 ///
442 /// @param app_corpus the application corpus to consider.
443 ///
444 /// @param lib_corpus the library corpus to consider.
445 ///
446 /// @return a status bitfield.
447 static abidiff_status
perform_compat_check_in_weak_mode(options & opts,diff_context_sptr & ctxt,corpus_sptr app_corpus,corpus_sptr lib_corpus)448 perform_compat_check_in_weak_mode(options& opts,
449 diff_context_sptr& ctxt,
450 corpus_sptr app_corpus,
451 corpus_sptr lib_corpus)
452 {
453 ABG_ASSERT(lib_corpus);
454 ABG_ASSERT(app_corpus);
455
456 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
457
458 // Functions and variables defined and exported by lib_corpus which
459 // symbols are undefined in app_corpus are the artifacts we are
460 // interested in.
461 //
462 // So let's drop all functions and variables from lib_corpus that
463 // are so that their symbols are *NOT* undefined in app_corpus.
464 //
465 // In other words, let's only keep the functiond and variables from
466 // lib_corpus that are consumed by app_corpus.
467
468 for (elf_symbols::const_iterator i =
469 app_corpus->get_sorted_undefined_fun_symbols().begin();
470 i != app_corpus->get_sorted_undefined_fun_symbols().end();
471 ++i)
472 {
473 string id = (*i)->get_id_string();
474 lib_corpus->get_sym_ids_of_fns_to_keep().push_back(id);
475 }
476
477 for (elf_symbols::const_iterator i =
478 app_corpus->get_sorted_undefined_var_symbols().begin();
479 i != app_corpus->get_sorted_undefined_var_symbols().end();
480 ++i)
481 {
482 string id = (*i)->get_id_string();
483 lib_corpus->get_sym_ids_of_vars_to_keep().push_back(id);
484 }
485
486 if (!app_corpus->get_sorted_undefined_var_symbols().empty()
487 || !app_corpus->get_sorted_undefined_fun_symbols().empty())
488 lib_corpus->maybe_drop_some_exported_decls();
489
490 // OK now, lib_corpus only contains functions and variables which
491 // symbol are consumed by app_corpus.
492
493 // So we are now going to compare the functions that are exported by
494 // lib_corpus against those that app_corpus expects.
495 //
496 // In other words, the functions which symbols are defined by
497 // lib_corpus are going to be compared to the functions and
498 // variables which are undefined in app_corpus.
499
500 {
501 function_type_sptr lib_fn_type, app_fn_type;
502 vector<fn_change> fn_changes;
503 for (corpus::functions::const_iterator i =
504 lib_corpus->get_functions().begin();
505 i != lib_corpus->get_functions().end();
506 ++i)
507 {
508 // lib_fn_type contains the type of a function that is defined
509 // in lib_corpus.
510 lib_fn_type = (*i)->get_type();
511 ABG_ASSERT(lib_fn_type);
512
513 // app_fn_type contains the the "version" of lib_fn_type that
514 // is expected by app_corpus.
515 app_fn_type = lookup_or_synthesize_fn_type(lib_fn_type, *app_corpus);
516
517 // Now lets compare the type expected by app_corpus against
518 // the type actually provided by lib_fn_type.
519 function_type_diff_sptr fn_type_diff;
520 if (app_fn_type)
521 fn_type_diff = compute_diff(app_fn_type, lib_fn_type, ctxt);
522
523 // If the two types of functions are different, then let's
524 // store their difference in the "fn_changes" vector.
525 if (fn_type_diff && fn_type_diff->to_be_reported())
526 fn_changes.push_back(fn_change(*i, fn_type_diff));
527 }
528
529 string lib1_path = opts.lib1_path, app_path = opts.app_path;
530 if (opts.show_base_names)
531 {
532 base_name(opts.lib1_path, lib1_path);
533 base_name(opts.app_path, app_path);
534 }
535
536 // If some function changes were detected, then report them.
537 if (!fn_changes.empty())
538 {
539 cout << "functions defined in library "
540 << "'" << lib1_path << "'\n"
541 << "have sub-types that are different from what application "
542 << "'" << app_path << "' "
543 << "expects:\n\n";
544 for (vector<fn_change>::const_iterator i = fn_changes.begin();
545 i != fn_changes.end();
546 ++i)
547 {
548 cout << " "
549 << i->decl->get_pretty_representation()
550 << ":\n";
551 i->diff->report(cout, " ");
552 cout << "\n";
553 }
554 }
555
556 if (!fn_changes.empty())
557 status |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
558
559 // OK now, let's do something similar for *variables* changes.
560 //
561 // That is, let's compare the variables expected by app_corpus
562 // against the variables actually provided by lib_corpus and
563 // report the difference that might have been found.
564
565 type_base_sptr lib_var_type, app_var_type;
566 vector<var_change> var_changes;
567 for (corpus::variables::const_iterator i =
568 lib_corpus->get_variables().begin();
569 i != lib_corpus->get_variables().end();
570 ++i)
571 {
572 lib_var_type = (*i)->get_type();
573 ABG_ASSERT(lib_var_type);
574 app_var_type = lookup_type(lib_var_type, *app_corpus);
575 diff_sptr type_diff;
576 if (app_var_type)
577 type_diff = compute_diff(app_var_type, lib_var_type, ctxt);
578 if (type_diff && type_diff->to_be_reported())
579 var_changes.push_back(var_change(*i, type_diff));
580 }
581 if (!var_changes.empty())
582 {
583 cout << "variables defined in library "
584 << "'" << lib1_path << "'\n"
585 << "have sub-types that are different from what application "
586 << "'" << app_path << "' "
587 << "expects:\n\n";
588 for (vector<var_change>::const_iterator i = var_changes.begin();
589 i != var_changes.end();
590 ++i)
591 {
592 cout << " "
593 << i->decl->get_pretty_representation()
594 << ":\n";
595 i->diff->report(cout, " ");
596 cout << "\n";
597 }
598 }
599 }
600 return status;
601 }
602
603 int
main(int argc,char * argv[])604 main(int argc, char* argv[])
605 {
606 options opts(argv[0]);
607
608 if (!parse_command_line(argc, argv, opts))
609 {
610 if (!opts.unknow_option.empty())
611 {
612 emit_prefix(argv[0], cerr)
613 << "unrecognized option: " << opts.unknow_option << "\n"
614 << "try the --help option for more information\n";
615 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
616 | abigail::tools_utils::ABIDIFF_ERROR);
617 }
618
619 emit_prefix(argv[0], cerr)
620 << "wrong invocation\n"
621 << "try the --help option for more information\n";
622 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
623 | abigail::tools_utils::ABIDIFF_ERROR);
624 }
625
626 if (opts.display_help)
627 {
628 display_usage(argv[0], cout);
629 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
630 | abigail::tools_utils::ABIDIFF_ERROR);
631 }
632
633 if (opts.display_version)
634 {
635 emit_prefix(argv[0], cout)
636 << abigail::tools_utils::get_library_version_string();
637 return 0;
638 }
639
640 ABG_ASSERT(!opts.app_path.empty());
641 if (!abigail::tools_utils::check_file(opts.app_path, cerr, opts.prog_name))
642 return abigail::tools_utils::ABIDIFF_ERROR;
643
644 abigail::tools_utils::file_type type =
645 abigail::tools_utils::guess_file_type(opts.app_path);
646 if (type != abigail::tools_utils::FILE_TYPE_ELF)
647 {
648 emit_prefix(argv[0], cerr)
649 << opts.app_path << " is not an ELF file\n";
650 return abigail::tools_utils::ABIDIFF_ERROR;
651 }
652
653 // Create the context of the diff
654 diff_context_sptr ctxt = create_diff_context(opts);
655
656 // Check if any suppression specification prevents us from
657 // performing the compatibility checking.
658 suppressions_type& supprs = ctxt->suppressions();
659 bool files_suppressed = (file_is_suppressed(opts.app_path, supprs)
660 || file_is_suppressed(opts.lib1_path, supprs)
661 ||file_is_suppressed(opts.lib2_path, supprs));
662
663 if (files_suppressed)
664 // We don't have to compare anything because a user
665 // suppression specification file instructs us to avoid
666 // loading either one of the input files.
667 return abigail::tools_utils::ABIDIFF_OK;
668
669 // Read the application ELF file.
670 char * app_di_root = opts.app_di_root_path.get();
671 vector<char**> app_di_roots;
672 app_di_roots.push_back(&app_di_root);
673 status status = abigail::dwarf_reader::STATUS_UNKNOWN;
674 environment_sptr env(new environment);
675 corpus_sptr app_corpus=
676 read_corpus_from_elf(opts.app_path,
677 app_di_roots, env.get(),
678 /*load_all_types=*/opts.weak_mode,
679 status);
680
681 if (status & abigail::dwarf_reader::STATUS_NO_SYMBOLS_FOUND)
682 {
683 emit_prefix(argv[0], cerr)
684 << "could not read symbols from " << opts.app_path << "\n";
685 return abigail::tools_utils::ABIDIFF_ERROR;
686 }
687 if (!(status & abigail::dwarf_reader::STATUS_OK))
688 {
689 emit_prefix(argv[0], cerr)
690 << "could not read file " << opts.app_path << "\n";
691 return abigail::tools_utils::ABIDIFF_ERROR;
692 }
693
694 if (opts.list_undefined_symbols_only)
695 {
696 for (elf_symbols::const_iterator i =
697 app_corpus->get_sorted_undefined_fun_symbols().begin();
698 i != app_corpus->get_sorted_undefined_fun_symbols().end();
699 ++i)
700 {
701 string id = (*i)->get_id_string();
702 string sym_name = (*i)->get_name();
703 string demangled_name = demangle_cplus_mangled_name(sym_name);
704 if (demangled_name != sym_name)
705 cout << demangled_name << " {" << id << "}\n";
706 else
707 cout << id << "\n";
708 }
709 return abigail::tools_utils::ABIDIFF_OK;
710 }
711
712 // Read the first version of the library.
713 ABG_ASSERT(!opts.lib1_path.empty());
714 if (!abigail::tools_utils::check_file(opts.lib1_path, cerr, opts.prog_name))
715 return abigail::tools_utils::ABIDIFF_ERROR;
716 type = abigail::tools_utils::guess_file_type(opts.lib1_path);
717 if (type != abigail::tools_utils::FILE_TYPE_ELF)
718 {
719 emit_prefix(argv[0], cerr) << opts.lib1_path << " is not an ELF file\n";
720 return abigail::tools_utils::ABIDIFF_ERROR;
721 }
722
723 char * lib1_di_root = opts.lib1_di_root_path.get();
724 vector<char**> lib1_di_roots;
725 lib1_di_roots.push_back(&lib1_di_root);
726 corpus_sptr lib1_corpus = read_corpus_from_elf(opts.lib1_path,
727 lib1_di_roots, env.get(),
728 /*load_all_types=*/false,
729 status);
730 if (status & abigail::dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
731 emit_prefix(argv[0], cerr)
732 << "could not read debug info for " << opts.lib1_path << "\n";
733 if (status & abigail::dwarf_reader::STATUS_NO_SYMBOLS_FOUND)
734 {
735 cerr << "could not read symbols from " << opts.lib1_path << "\n";
736 return abigail::tools_utils::ABIDIFF_ERROR;
737 }
738 if (!(status & abigail::dwarf_reader::STATUS_OK))
739 {
740 emit_prefix(argv[0], cerr)
741 << "could not read file " << opts.lib1_path << "\n";
742 return abigail::tools_utils::ABIDIFF_ERROR;
743 }
744
745 // Read the second version of the library.
746 corpus_sptr lib2_corpus;
747 if (!opts.weak_mode)
748 {
749 ABG_ASSERT(!opts.lib2_path.empty());
750 char * lib2_di_root = opts.lib2_di_root_path.get();
751 vector<char**> lib2_di_roots;
752 lib2_di_roots.push_back(&lib2_di_root);
753 lib2_corpus = read_corpus_from_elf(opts.lib2_path,
754 lib2_di_roots, env.get(),
755 /*load_all_types=*/false,
756 status);
757 if (status & abigail::dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
758 emit_prefix(argv[0], cerr)
759 << "could not read debug info for " << opts.lib2_path << "\n";
760 if (status & abigail::dwarf_reader::STATUS_NO_SYMBOLS_FOUND)
761 {
762 emit_prefix(argv[0], cerr)
763 << "could not read symbols from " << opts.lib2_path << "\n";
764 return abigail::tools_utils::ABIDIFF_ERROR;
765 }
766 if (!(status & abigail::dwarf_reader::STATUS_OK))
767 {
768 emit_prefix(argv[0], cerr)
769 << "could not read file " << opts.lib2_path << "\n";
770 return abigail::tools_utils::ABIDIFF_ERROR;
771 }
772 }
773
774 abidiff_status s = abigail::tools_utils::ABIDIFF_OK;
775
776 if (opts.weak_mode)
777 s = perform_compat_check_in_weak_mode(opts, ctxt,
778 app_corpus,
779 lib1_corpus);
780 else
781 s = perform_compat_check_in_normal_mode(opts, ctxt,
782 app_corpus,
783 lib1_corpus,
784 lib2_corpus);
785
786 return s;
787 }
788