1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2015-2020 Red Hat, Inc.
5 //
6 // Author: Sinny Kumari
7
8 /// @file
9
10 /// This program compares the ABIs of binaries inside two packages.
11 ///
12 /// For now, the supported package formats are Deb and RPM, but
13 /// support for other formats would be greatly appreciated.
14 ///
15 /// The program takes the two packages to compare as well as their
16 /// associated debug info packages.
17 ///
18 /// The program extracts the content of the two packages into a
19 /// temporary directory , looks for the ELF binaries in there,
20 /// compares their ABIs and emit a report about the changes.
21 /// As this program uses libpthread to perform several tasks
22 /// concurrently, here is a coarse grain description of the sequence
23 /// of actions performed, including where things are done
24 /// concurrently.
25 ///
26 /// (steps 1/ and 2/ are performed concurrently. Then steps 3/ and 4/
27 /// are performed in sequence)
28 ///
29 /// 1/ the first package and its ancillary packages (debug info and
30 /// devel packages) are extracted concurrently.
31 /// There is one thread per package being extracted. So if there are
32 /// 3 thread packages (one package, one debug info package and one
33 /// devel package), then there are 3 threads to extracts them. Then
34 /// when the extracting is done, another thread performs the analysis
35 /// of th1 extracted content.
36 ///
37 /// 2/ A similar thing is done for the second package.
38 ///
39 /// 3/ comparisons are performed concurrently.
40 ///
41 /// 4/ the reports are then emitted to standard output, always in the same
42 /// order.
43
44
45 // In case we have a bad fts we include this before config.h because
46 // it can't handle _FILE_OFFSET_BITS. Everything we need here is fine
47 // if its declarations just come first. Also, include sys/types.h
48 // before fts. On some systems fts.h is not self contained.
49 #ifdef BAD_FTS
50 #include <sys/types.h>
51 #include <fts.h>
52 #endif
53
54 // For package configuration macros.
55 #include "config.h"
56
57 #include <assert.h>
58 #include <fcntl.h>
59 #include <sys/stat.h>
60
61 // If fts.h is included before config.h, its indirect inclusions may
62 // not give us the right LFS aliases of these functions, so map them
63 // manually.
64 #ifdef BAD_FTS
65 #ifdef _FILE_OFFSET_BITS
66 #define open open64
67 #define fopen fopen64
68 #endif
69 #else
70 #include <sys/types.h>
71 #include <fts.h>
72 #endif
73
74 #include <algorithm>
75 #include <cstdlib>
76 #include <cstring>
77 #include <fstream>
78 #include <iostream>
79 #include <map>
80 #include <memory>
81 #include <string>
82 #include <unordered_set>
83 #include <vector>
84
85 #include "abg-workers.h"
86 #include "abg-config.h"
87 #include "abg-tools-utils.h"
88 #include "abg-comparison.h"
89 #include "abg-suppression.h"
90 #include "abg-dwarf-reader.h"
91 #include "abg-reader.h"
92 #include "abg-writer.h"
93
94 using std::cout;
95 using std::cerr;
96 using std::string;
97 using std::ostream;
98 using std::ofstream;
99 using std::vector;
100 using std::map;
101 using std::unordered_set;
102 using std::set;
103 using std::ostringstream;
104 using std::shared_ptr;
105 using std::dynamic_pointer_cast;
106 using abigail::workers::task;
107 using abigail::workers::task_sptr;
108 using abigail::workers::queue;
109 using abigail::tools_utils::maybe_get_symlink_target_file_path;
110 using abigail::tools_utils::file_exists;
111 using abigail::tools_utils::is_dir;
112 using abigail::tools_utils::emit_prefix;
113 using abigail::tools_utils::check_file;
114 using abigail::tools_utils::ensure_dir_path_created;
115 using abigail::tools_utils::guess_file_type;
116 using abigail::tools_utils::string_ends_with;
117 using abigail::tools_utils::dir_name;
118 using abigail::tools_utils::real_path;
119 using abigail::tools_utils::string_suffix;
120 using abigail::tools_utils::sorted_strings_common_prefix;
121 using abigail::tools_utils::file_type;
122 using abigail::tools_utils::make_path_absolute;
123 using abigail::tools_utils::base_name;
124 using abigail::tools_utils::get_rpm_arch;
125 using abigail::tools_utils::file_is_kernel_package;
126 using abigail::tools_utils::gen_suppr_spec_from_headers;
127 using abigail::tools_utils::get_default_system_suppression_file_path;
128 using abigail::tools_utils::get_default_user_suppression_file_path;
129 using abigail::tools_utils::get_vmlinux_path_from_kernel_dist;
130 using abigail::tools_utils::get_dsos_provided_by_rpm;
131 using abigail::tools_utils::build_corpus_group_from_kernel_dist_under;
132 using abigail::tools_utils::load_default_system_suppressions;
133 using abigail::tools_utils::load_default_user_suppressions;
134 using abigail::tools_utils::abidiff_status;
135 using abigail::ir::corpus_sptr;
136 using abigail::ir::corpus_group_sptr;
137 using abigail::comparison::diff_context;
138 using abigail::comparison::diff_context_sptr;
139 using abigail::comparison::compute_diff;
140 using abigail::comparison::corpus_diff_sptr;
141 using abigail::comparison::get_default_harmless_categories_bitmap;
142 using abigail::comparison::get_default_harmful_categories_bitmap;
143 using abigail::suppr::suppression_sptr;
144 using abigail::suppr::suppressions_type;
145 using abigail::suppr::read_suppressions;
146 using abigail::dwarf_reader::read_context_sptr;
147 using abigail::dwarf_reader::create_read_context;
148 using abigail::dwarf_reader::get_soname_of_elf_file;
149 using abigail::dwarf_reader::get_type_of_elf_file;
150 using abigail::dwarf_reader::read_corpus_from_elf;
151 using abigail::xml_writer::create_write_context;
152 using abigail::xml_writer::write_context_sptr;
153 using abigail::xml_writer::write_corpus;
154
155 class package;
156
157 /// Convenience typedef for a shared pointer to a @ref package.
158 typedef shared_ptr<package> package_sptr;
159
160 /// The options passed to the current program.
161 class options
162 {
163 options();
164
165 public:
166 string wrong_option;
167 string wrong_arg;
168 string prog_name;
169 bool display_usage;
170 bool display_version;
171 bool missing_operand;
172 bool nonexistent_file;
173 bool abignore;
174 bool parallel;
175 string package1;
176 string package2;
177 vector<string> debug_packages1;
178 vector<string> debug_packages2;
179 string devel_package1;
180 string devel_package2;
181 size_t num_workers;
182 bool verbose;
183 bool drop_private_types;
184 bool show_relative_offset_changes;
185 bool no_default_suppression;
186 bool keep_tmp_files;
187 bool compare_dso_only;
188 bool compare_private_dsos;
189 bool leaf_changes_only;
190 bool show_all_types;
191 bool show_hexadecimal_values;
192 bool show_offsets_sizes_in_bits;
193 bool show_impacted_interfaces;
194 bool show_full_impact_report;
195 bool show_linkage_names;
196 bool show_redundant_changes;
197 bool show_harmless_changes;
198 bool show_locs;
199 bool show_added_syms;
200 bool show_symbols_not_referenced_by_debug_info;
201 bool show_added_binaries;
202 bool fail_if_no_debug_info;
203 bool show_identical_binaries;
204 bool self_check;
205 vector<string> kabi_whitelist_packages;
206 vector<string> suppression_paths;
207 vector<string> kabi_whitelist_paths;
208 suppressions_type kabi_suppressions;
209 package_sptr pkg1;
210 package_sptr pkg2;
211
options(const string & program_name)212 options(const string& program_name)
213 : prog_name(program_name),
214 display_usage(),
215 display_version(),
216 missing_operand(),
217 nonexistent_file(),
218 abignore(true),
219 parallel(true),
220 verbose(),
221 drop_private_types(),
222 show_relative_offset_changes(true),
223 no_default_suppression(),
224 keep_tmp_files(),
225 compare_dso_only(),
226 compare_private_dsos(),
227 leaf_changes_only(),
228 show_all_types(),
229 show_hexadecimal_values(),
230 show_offsets_sizes_in_bits(true),
231 show_impacted_interfaces(),
232 show_full_impact_report(),
233 show_linkage_names(true),
234 show_redundant_changes(),
235 show_harmless_changes(),
236 show_locs(true),
237 show_added_syms(true),
238 show_symbols_not_referenced_by_debug_info(true),
239 show_added_binaries(true),
240 fail_if_no_debug_info(),
241 show_identical_binaries(),
242 self_check()
243 {
244 // set num_workers to the default number of threads of the
245 // underlying maching. This is the default value for the number
246 // of workers to use in workers queues throughout the code.
247 num_workers = abigail::workers::get_number_of_threads();
248 }
249 };
250
251 static bool
252 get_interesting_files_under_dir(const string dir,
253 const string& file_name_to_look_for,
254 options& opts,
255 vector<string>& interesting_files);
256
257 /// Abstract ELF files from the packages which ABIs ought to be
258 /// compared
259 class elf_file
260 {
261 private:
262 elf_file();
263
264 public:
265 string path;
266 string name;
267 string soname;
268 off_t size;
269 abigail::dwarf_reader::elf_type type;
270
271 /// The path to the elf file.
272 ///
273 /// @param path the path to the elf file.
elf_file(const string & path)274 elf_file(const string& path)
275 : path(path)
276 {
277 abigail::tools_utils::base_name(path, name);
278 get_soname_of_elf_file(path, soname);
279 get_type_of_elf_file(path, type);
280 struct stat estat;
281 stat(path.c_str(), &estat);
282 size = estat.st_size;
283 }
284 };
285
286 /// A convenience typedef for a shared pointer to elf_file.
287 typedef shared_ptr<elf_file> elf_file_sptr;
288
289 /// Abstract the result of comparing two packages.
290 ///
291 /// This contains the the paths of the set of added binaries, removed
292 /// binaries, and binaries whic ABI changed.
293 struct abi_diff
294 {
295 vector<elf_file_sptr> added_binaries;
296 vector<elf_file_sptr> removed_binaries;
297 vector<string> changed_binaries;
298
299 /// Test if the current diff carries changes.
300 ///
301 /// @return true iff the current diff carries changes.
302 bool
has_changesabi_diff303 has_changes()
304 {
305 return (!added_binaries.empty()
306 || !removed_binaries.empty()
307 ||!changed_binaries.empty());
308 }
309 };
310
311 /// Abstracts a package.
312 class package
313 {
314 public:
315
316 /// The kind of package we are looking at.
317 enum kind
318 {
319 /// Main package. Contains binaries to ABI-compare.
320 KIND_MAIN = 0,
321 /// Devel package. Contains public headers files in which public
322 /// types are defined.
323 KIND_DEVEL,
324 /// Debug info package. Contains the debug info for the binaries
325 /// int he main packge.
326 KIND_DEBUG_INFO,
327 /// Contains kernel ABI whitelists
328 KIND_KABI_WHITELISTS,
329 /// Source package. Contains the source of the binaries in the
330 /// main package.
331 KIND_SRC
332 };
333
334 private:
335 string path_;
336 string extracted_dir_path_;
337 string common_paths_prefix_;
338 abigail::tools_utils::file_type type_;
339 kind kind_;
340 map<string, elf_file_sptr> path_elf_file_sptr_map_;
341 vector<package_sptr> debug_info_packages_;
342 package_sptr devel_package_;
343 package_sptr kabi_whitelist_package_;
344 vector<string> elf_file_paths_;
345 set<string> public_dso_sonames_;
346
347 public:
348 /// Constructor for the @ref package type.
349 ///
350 /// @param path the path to the package.
351 ///
352 /// @parm dir the temporary directory where to extract the content
353 /// of the package.
354 ///
355 /// @param pkg_kind the kind of package.
package(const string & path,const string & dir,kind pkg_kind=package::KIND_MAIN)356 package(const string& path,
357 const string& dir,
358 kind pkg_kind = package::KIND_MAIN)
359 : path_(path),
360 kind_(pkg_kind)
361 {
362 type_ = guess_file_type(path);
363 if (type_ == abigail::tools_utils::FILE_TYPE_DIR)
364 extracted_dir_path_ = path;
365 else
366 extracted_dir_path_ = extracted_packages_parent_dir() + "/" + dir;
367 }
368
369 /// Getter of the path of the package.
370 ///
371 /// @return the path of the package.
372 const string&
path() const373 path() const
374 {return path_;}
375
376 /// Setter of the path of the package.
377 ///
378 /// @param s the new path.
379 void
path(const string & s)380 path(const string& s)
381 {path_ = s;}
382
383 /// Getter of the base name of the package.
384 ///
385 /// @return the base name of the package.
386 string
base_name() const387 base_name() const
388 {
389 string name;
390 abigail::tools_utils::base_name(path(), name);
391 return name;
392 }
393
394 /// Getter for the path to the root dir where the packages are
395 /// extracted.
396 ///
397 /// @return the path to the root dir where the packages are
398 /// extracted.
399 static const string&
400 extracted_packages_parent_dir();
401
402 /// Getter for the path to the directory where the packages are
403 /// extracted for the current thread.
404 ///
405 /// @return the path to the directory where the packages are
406 /// extracted for the current thread.
407 const string&
extracted_dir_path() const408 extracted_dir_path() const
409 {return extracted_dir_path_;}
410
411 /// Setter for the path to the directory where the packages are
412 /// extracted for the current thread.
413 ///
414 /// @param p the new path.
415 void
extracted_dir_path(const string & p)416 extracted_dir_path(const string& p)
417 {extracted_dir_path_ = p;}
418
419 /// Getter of the the prefix that is common to all the paths of all
420 /// the elements of the package.
421 ///
422 /// @return the common path prefix of package elements.
423 const string&
common_paths_prefix() const424 common_paths_prefix() const
425 {return common_paths_prefix_;}
426
427 /// Getter of the the prefix that is common to all the paths of all
428 /// the elements of the package.
429 ///
430 /// @return the common path prefix of package elements.
431 string&
common_paths_prefix()432 common_paths_prefix()
433 {return common_paths_prefix_;}
434
435 /// Setter of the the prefix that is common to all the paths of all
436 /// the elements of the package.
437 ///
438 ///
439 ///@param p the new prefix.
440 void
common_paths_prefix(const string & p)441 common_paths_prefix(const string& p)
442 {common_paths_prefix_ = p;}
443
444 /// Getter for the file type of the current package.
445 ///
446 /// @return the file type of the current package.
447 abigail::tools_utils::file_type
type() const448 type() const
449 {return type_;}
450
451 /// Setter for the file type of the current package.
452 ///
453 /// @param t the new file type.
type(abigail::tools_utils::file_type t)454 void type(abigail::tools_utils::file_type t)
455 {type_ = t;}
456
457 /// Get the package kind
458 ///
459 /// @return the package kind
460 kind
get_kind() const461 get_kind() const
462 {return kind_;}
463
464 /// Set the package kind
465 ///
466 /// @param k the package kind.
467 void
set_kind(kind k)468 set_kind(kind k)
469 {kind_ = k;}
470
471 /// Getter for the path <-> elf_file map.
472 ///
473 /// @return the the path <-> elf_file map.
474 const map<string, elf_file_sptr>&
path_elf_file_sptr_map() const475 path_elf_file_sptr_map() const
476 {return path_elf_file_sptr_map_;}
477
478 /// Getter for the path <-> elf_file map.
479 ///
480 /// @return the the path <-> elf_file map.
481 map<string, elf_file_sptr>&
path_elf_file_sptr_map()482 path_elf_file_sptr_map()
483 {return path_elf_file_sptr_map_;}
484
485 /// Getter for the debug info packages associated to the current
486 /// package.
487 ///
488 /// There can indeed be several debug info packages needed for one
489 /// input package, as the debug info for that input package can be
490 /// split across several debuginfo packages.
491 ///
492 /// @return the debug info packages associated to the current
493 /// package.
494 const vector<package_sptr>&
debug_info_packages() const495 debug_info_packages() const
496 {return debug_info_packages_;}
497
498 /// Getter for the debug info packages associated to the current
499 /// package.
500 ///
501 /// There can indeed be several debug info packages needed for one
502 /// input package, as the debug info for that input package can be
503 /// split across several debuginfo packages.
504 ///
505 /// @return the debug info packages associated to the current
506 /// package.
507 vector<package_sptr>&
debug_info_packages()508 debug_info_packages()
509 {return debug_info_packages_;}
510
511 /// Setter for the debug info packages associated to the current
512 /// package.
513 ///
514 /// There can indeed be several debug info packages needed for one
515 /// input package, as the debug info for that input package can be
516 /// split across several debuginfo packages.
517 ///
518 /// @param p the new debug info package.
519 void
debug_info_packages(const vector<package_sptr> & p)520 debug_info_packages(const vector<package_sptr> &p)
521 {debug_info_packages_ = p;}
522
523 /// Getter for the devel package associated to the current package.
524 ///
525 /// @return the devel package associated to the current package.
526 const package_sptr&
devel_package() const527 devel_package() const
528 {return devel_package_;}
529
530 /// Setter of the devel package associated to the current package.
531 ///
532 /// @param p the new devel package associated to the current package.
533 void
devel_package(const package_sptr & p)534 devel_package(const package_sptr& p)
535 {devel_package_ = p;}
536
537 /// Getter of the associated kernel abi whitelist package, if any.
538 ///
539 /// @return the associated kernel abi whitelist package.
540 const package_sptr
kabi_whitelist_package() const541 kabi_whitelist_package() const
542 {return kabi_whitelist_package_;}
543
544 /// Setter of the associated kernel abi whitelist package.
545 ///
546 /// @param p the new kernel abi whitelist package.
547 void
kabi_whitelist_package(const package_sptr & p)548 kabi_whitelist_package(const package_sptr& p)
549 {kabi_whitelist_package_ = p;}
550
551 /// Getter of the path to the elf files of the package.
552 ///
553 /// @return the path tothe elf files of the package.
554 const vector<string>&
elf_file_paths() const555 elf_file_paths() const
556 {return elf_file_paths_;}
557
558 /// Getter of the path to the elf files of the package.
559 ///
560 /// @return the path tothe elf files of the package.
561 vector<string>&
elf_file_paths()562 elf_file_paths()
563 {return elf_file_paths_;}
564
565 /// Getter of the SONAMEs of the public DSOs carried by this
566 /// package.
567 ///
568 /// This is relevant only if the --private-dso option was *NOT*
569 /// provided.
570 ///
571 /// @return the SONAMEs of the public DSOs carried by this package.
572 const set<string>&
public_dso_sonames() const573 public_dso_sonames() const
574 {return public_dso_sonames_;}
575
576 /// Getter of the SONAMEs of the public DSOs carried by this
577 /// package.
578 ///
579 /// This is relevant only if the --private-dso option was *NOT*
580 /// provided.
581 ///
582 /// @return the SONAMEs of the public DSOs carried by this package.
583 set<string>&
public_dso_sonames()584 public_dso_sonames()
585 {return public_dso_sonames_;}
586
587 /// Convert the absolute path of an element of this package into a
588 /// path relative to the root path pointing to this package.
589 ///
590 /// That is, suppose the content of a package named 'pkg' is located
591 /// at /root/path/pkg. Suppose an element of that package is named
592 /// is at '/root/path/pkg/somewhere/inside/element'.
593 ///
594 /// This function will return the path:
595 /// /pkg/somewhere/inside/element.
596 ///
597 /// @param path the path to consider.
598 ///
599 /// @param converted_path the resulting converted path. This is set
600 /// iff the function returns true.
601 ///
602 /// @return true if the path could be converted to being relative to
603 /// the extracted directory.
604 bool
convert_path_to_relative(const string & path,string & converted_path) const605 convert_path_to_relative(const string& path, string& converted_path) const
606 {
607 string root = extracted_dir_path_;
608 real_path(root, root);
609 string p = path;
610 real_path(p, p);
611 return string_suffix(p, root, converted_path);
612 }
613
614 // Convert the absolute path of an element of this package into a
615 // path relative to the prefix common to the paths of all elements
616 // of the package.
617 //
618 // @param path the path to conver.
619 //
620 // @param converted_path the resulting converted path. This is set
621 // iff the function returns true.
622 //
623 // @return true iff the function could successfully convert @p path
624 // and put the result into @p converted_path.
625 bool
convert_path_to_unique_suffix(const string & path,string & converted_path) const626 convert_path_to_unique_suffix(const string& path,
627 string& converted_path) const
628 {return string_suffix(path, common_paths_prefix(), converted_path);}
629
630 /// Retrieve the set of "interesting" package element paths by
631 /// walking the package.
632 ///
633 /// And then compute the path prefix that is common to all the
634 /// collected elements.
635 ///
636 /// @param the options of this application.
637 void
load_elf_file_paths(options & opts)638 load_elf_file_paths(options& opts)
639 {
640 if (!common_paths_prefix().empty()
641 || !elf_file_paths().empty())
642 // We have already loaded the elf file paths, don't do it again.
643 return;
644
645 get_interesting_files_under_dir(extracted_dir_path(),
646 /*file_name_to_look_for=*/"",
647 opts, elf_file_paths());
648 std::sort(elf_file_paths().begin(), elf_file_paths().end());
649 string common_prefix;
650 sorted_strings_common_prefix(elf_file_paths(), common_paths_prefix());
651 }
652
653 /// Create the path of an ABI file to be associated with a given
654 /// binary.
655 ///
656 /// @param elf_file_path the path to the binary to consider.
657 ///
658 /// @param abi_file_path the resulting ABI file path. This is set
659 /// iff the function return true.
660 ///
661 /// @return true if the ABI file path could be constructed and the
662 /// directory tree containing it could be created. In that case,
663 /// the resulting ABI file path is set to the @p abi_file_path
664 /// output parameter.
665 bool
create_abi_file_path(const string & elf_file_path,string & abi_file_path) const666 create_abi_file_path(const string &elf_file_path,
667 string &abi_file_path) const
668 {
669 string abi_path, dir, parent;
670 if (!abigail::tools_utils::string_suffix(elf_file_path,
671 extracted_dir_path(),
672 abi_path))
673 return false;
674 abi_path = extracted_dir_path() + "/abixml" + abi_path + ".abi";
675 if (!abigail::tools_utils::ensure_parent_dir_created(abi_path))
676 return false;
677 abi_file_path = abi_path;
678 return true;
679 }
680
681 /// Erase the content of the temporary extraction directory that has
682 /// been populated by the @ref extract_package() function;
683 ///
684 /// @param opts the options passed to the current program.
685 void
erase_extraction_directory(const options & opts) const686 erase_extraction_directory(const options &opts) const
687 {
688 if (type() == abigail::tools_utils::FILE_TYPE_DIR)
689 // If we are comparing two directories, do not erase the
690 // directory as it was provided by the user; it's not a
691 // temporary directory we created ourselves.
692 return;
693
694 if (opts.verbose)
695 emit_prefix("abipkgdiff", cerr)
696 << "Erasing temporary extraction directory "
697 << extracted_dir_path()
698 << " ...";
699
700 string cmd = "rm -rf " + extracted_dir_path();
701 if (system(cmd.c_str()))
702 {
703 if (opts.verbose)
704 emit_prefix("abipkgdiff", cerr) << " FAILED\n";
705 }
706 else
707 {
708 if (opts.verbose)
709 emit_prefix("abipkgdiff", cerr) << " DONE\n";
710 }
711 }
712
713 /// Erase the content of all the temporary extraction directories.
714 ///
715 /// @param opts the options passed to the current program.
716 void
erase_extraction_directories(const options & opts) const717 erase_extraction_directories(const options &opts) const
718 {
719 erase_extraction_directory(opts);
720 if (!debug_info_packages().empty())
721 debug_info_packages().front()->erase_extraction_directory(opts);
722 if (devel_package())
723 devel_package()->erase_extraction_directory(opts);
724 if (kabi_whitelist_package())
725 kabi_whitelist_package()->erase_extraction_directory(opts);
726 }
727 }; // end class package.
728
729 /// Arguments passed to the comparison tasks.
730 struct compare_args
731 {
732 const elf_file elf1;
733 const string& debug_dir1;
734 const suppressions_type private_types_suppr1;
735 const elf_file elf2;
736 const string& debug_dir2;
737 const suppressions_type private_types_suppr2;
738 const options& opts;
739
740 /// Constructor for compare_args, which is used to pass
741 /// information to the comparison threads.
742 ///
743 /// @param elf1 the first elf file to consider.
744 ///
745 /// @param debug_dir1 the directory where the debug info file for @p
746 /// elf1 is stored.
747 ///
748 /// @param elf2 the second elf file to consider.
749 ///
750 /// @param debug_dir2 the directory where the debug info file for @p
751 /// elf2 is stored.
752 ///
753 /// @param opts the options the current program has been called with.
compare_argscompare_args754 compare_args(const elf_file &elf1, const string& debug_dir1,
755 const suppressions_type& priv_types_suppr1,
756 const elf_file &elf2, const string& debug_dir2,
757 const suppressions_type& priv_types_suppr2,
758 const options& opts)
759 : elf1(elf1), debug_dir1(debug_dir1),
760 private_types_suppr1(priv_types_suppr1),
761 elf2(elf2), debug_dir2(debug_dir2),
762 private_types_suppr2(priv_types_suppr2),
763 opts(opts)
764 {}
765 }; // end struct compare_args
766
767 /// A convenience typedef for arguments passed to the comparison workers.
768 typedef shared_ptr<compare_args> compare_args_sptr;
769
770 static bool extract_package_and_map_its_content(const package_sptr &pkg,
771 options &opts);
772
773 /// Getter for the path to the parent directory under which packages
774 /// extracted by the current thread are placed.
775 ///
776 /// @return the path to the parent directory under which packages
777 /// extracted by the current thread are placed.
778 const string&
extracted_packages_parent_dir()779 package::extracted_packages_parent_dir()
780 {
781 // I tried to declare this in thread-local storage, but GCC 4.4.7
782 // won't let me. So for now, I am just making it static. I'll deal
783 // with this later when I have to.
784
785 //static __thread string p;
786 static string p;
787
788 if (p.empty())
789 {
790 const char *cachedir = getenv("XDG_CACHE_HOME");
791
792 if (cachedir != NULL)
793 p = cachedir;
794 else
795 {
796 const char* s = getenv("HOME");
797 if (s != NULL)
798 p = s;
799 if (p.empty())
800 {
801 s = getenv("TMPDIR");
802 if (s != NULL)
803 p = s;
804 else
805 p = "/tmp";
806 }
807 p += "/.cache/libabigail";
808 }
809
810 // Create the cache directory if it doesn't exist
811 ABG_ASSERT(ensure_dir_path_created(p));
812
813 string libabigail_tmp_dir_template = p;
814 libabigail_tmp_dir_template += "/abipkgdiff-tmp-dir-XXXXXX";
815
816 if (!mkdtemp(const_cast<char*>(libabigail_tmp_dir_template.c_str())))
817 abort();
818
819 p = libabigail_tmp_dir_template;
820 }
821
822 return p;
823 }
824
825 /// A convenience typedef for shared_ptr of package.
826 typedef shared_ptr<package> package_sptr;
827
828 /// Show the usage of this program.
829 ///
830 /// @param prog_name the name of the program.
831 ///
832 /// @param out the output stream to emit the usage to .
833 static void
display_usage(const string & prog_name,ostream & out)834 display_usage(const string& prog_name, ostream& out)
835 {
836 emit_prefix(prog_name, out)
837 << "usage: " << prog_name << " [options] <package1> <package2>\n"
838 << " where options can be:\n"
839 << " --debug-info-pkg1|--d1 <path> path of debug-info package of package1\n"
840 << " --debug-info-pkg2|--d2 <path> path of debug-info package of package2\n"
841 << " --devel-pkg1|--devel1 <path> path of devel package of pakage1\n"
842 << " --devel-pkg2|--devel2 <path> path of devel package of pakage1\n"
843 << " --drop-private-types drop private types from "
844 "internal representation\n"
845 << " --no-default-suppression don't load any default "
846 "suppression specifications\n"
847 << " --suppressions|--suppr <path> specify supression specification path\n"
848 << " --linux-kernel-abi-whitelist|-w path to a "
849 "linux kernel abi whitelist\n"
850 << " --wp <path> path to a linux kernel abi whitelist package\n"
851 << " --keep-tmp-files don't erase created temporary files\n"
852 << " --dso-only compare shared libraries only\n"
853 << " --private-dso compare DSOs that are private "
854 "to the package as well\n"
855 << " --leaf-changes-only|-l only show leaf changes, "
856 "so no change impact analysis (implies --redundant)\n"
857 << " --impacted-interfaces|-i display interfaces impacted by leaf changes\n"
858 << " --full-impact|-f when comparing kernel packages, show the "
859 "full impact analysis report rather than the default leaf changes reports\n"
860 << " --non-reachable-types|-t consider types non reachable"
861 " from public interfaces\n"
862 << " --no-linkage-name do not display linkage names of "
863 "added/removed/changed\n"
864 << " --redundant display redundant changes\n"
865 << " --harmless display the harmless changes\n"
866 << " --no-show-locs do not show location information\n"
867 << " --show-bytes show size and offsets in bytes\n"
868 << " --show-bits show size and offsets in bits\n"
869 << " --show-hex show size and offset in hexadecimal\n"
870 << " --show-dec show size and offset in decimal\n"
871 << " --no-show-relative-offset-changes do not show relative"
872 " offset changes\n"
873 << " --no-added-syms do not display added functions or variables\n"
874 << " --no-unreferenced-symbols do not display changes "
875 "about symbols not referenced by debug info\n"
876 << " --no-added-binaries do not display added binaries\n"
877 << " --no-abignore do not look for *.abignore files\n"
878 << " --no-parallel do not execute in parallel\n"
879 << " --fail-no-dbg fail if no debug info was found\n"
880 << " --show-identical-binaries show the names of identical binaries\n"
881 << " --verbose emit verbose progress messages\n"
882 << " --self-check perform a sanity check by comparing "
883 "binaries inside the input package against their ABIXML representation\n"
884 << " --help|-h display this help message\n"
885 << " --version|-v display program version information"
886 " and exit\n";
887 }
888
889 #ifdef WITH_RPM
890
891 /// Extract an RPM package.
892 ///
893 /// @param package_path the path to the package to extract.
894 ///
895 /// @param extracted_package_dir_path the path where to extract the
896 /// package to.
897 ///
898 /// @param opts the options passed to the current program.
899 ///
900 /// @return true upon successful completion, false otherwise.
901 static bool
extract_rpm(const string & package_path,const string & extracted_package_dir_path,const options & opts)902 extract_rpm(const string& package_path,
903 const string& extracted_package_dir_path,
904 const options &opts)
905 {
906 if (opts.verbose)
907 emit_prefix("abipkgdiff", cerr)
908 << "Extracting package "
909 << package_path
910 << " to "
911 << extracted_package_dir_path
912 << " ...";
913
914 string cmd = "test -d " + extracted_package_dir_path
915 + " || mkdir -p " + extracted_package_dir_path + " ; cd " +
916 extracted_package_dir_path + " && rpm2cpio " + package_path +
917 " | cpio -dium --quiet";
918
919 if (system(cmd.c_str()))
920 {
921 if (opts.verbose)
922 emit_prefix("abipkgdiff", cerr) << " FAILED\n";
923 return false;
924 }
925
926 if (opts.verbose)
927 emit_prefix("abipkgdiff", cerr) << " DONE\n";
928
929 return true;
930 }
931
932 #endif // WITH_RPM
933
934 #ifdef WITH_DEB
935
936 /// Extract a Debian binary package.
937 ///
938 /// @param package_path the path to the package to extract.
939 ///
940 /// @param extracted_package_dir_path the path where to extract the
941 /// package to.
942 ///
943 /// @param opts the options passed to the current program.
944 ///
945 /// @return true upon successful completion, false otherwise.
946 static bool
extract_deb(const string & package_path,const string & extracted_package_dir_path,const options & opts)947 extract_deb(const string& package_path,
948 const string& extracted_package_dir_path,
949 const options &opts)
950 {
951 if (opts.verbose)
952 emit_prefix("abipkgdiff", cerr)
953 << "Extracting package "
954 << package_path
955 << " to "
956 << extracted_package_dir_path
957 << " ...\n";
958
959 string cmd = "mkdir -p " + extracted_package_dir_path + " && dpkg -x " +
960 package_path + " " + extracted_package_dir_path;
961
962 if (system(cmd.c_str()))
963 {
964 if (opts.verbose)
965 emit_prefix("abipkgdiff", cerr) << " FAILED\n";
966 return false;
967 }
968
969 if (opts.verbose)
970 emit_prefix("abipkgdiff", cerr) << " DONE\n";
971
972 return true;
973 }
974
975 #endif // WITH_DEB
976
977 #ifdef WITH_TAR
978
979 /// Extract a GNU Tar archive.
980 ///
981 /// @param package_path the path to the archive to extract.
982 ///
983 /// @param extracted_package_dir_path the path where to extract the
984 /// archive to.
985 ///
986 /// @param opts the options passed to the current program.
987 ///
988 /// @return true upon successful completion, false otherwise.
989 static bool
extract_tar(const string & package_path,const string & extracted_package_dir_path,const options & opts)990 extract_tar(const string& package_path,
991 const string& extracted_package_dir_path,
992 const options &opts)
993 {
994 if (opts.verbose)
995 emit_prefix("abipkgdiff", cerr)
996 << "Extracting tar archive "
997 << package_path
998 << " to "
999 << extracted_package_dir_path
1000 << " ...";
1001
1002 string cmd = "test -d " +
1003 extracted_package_dir_path +
1004 " && rm -rf " + extracted_package_dir_path;
1005
1006 if (system(cmd.c_str()))
1007 {
1008 if (opts.verbose)
1009 emit_prefix("abipkgdiff", cerr) << "command " << cmd << " FAILED\n";
1010 }
1011
1012 cmd = "mkdir -p " + extracted_package_dir_path + " && cd " +
1013 extracted_package_dir_path + " && tar -xf " + package_path;
1014
1015 if (system(cmd.c_str()))
1016 {
1017 if (opts.verbose)
1018 emit_prefix("abipkgdiff", cerr) << " FAILED\n";
1019 return false;
1020 }
1021
1022 if (opts.verbose)
1023 emit_prefix("abipkgdiff", cerr) << " DONE\n";
1024
1025 return true;
1026 }
1027
1028 #endif // WITH_TAR
1029
1030 /// Erase the temporary directories created for the extraction of two
1031 /// packages.
1032 ///
1033 /// @param first_package the first package to consider.
1034 ///
1035 /// @param opts the options passed to the current program.
1036 ///
1037 /// @param second_package the second package to consider.
1038 static void
erase_created_temporary_directories(const package & first_package,const package & second_package,const options & opts)1039 erase_created_temporary_directories(const package& first_package,
1040 const package& second_package,
1041 const options &opts)
1042 {
1043 first_package.erase_extraction_directories(opts);
1044 second_package.erase_extraction_directories(opts);
1045 }
1046
1047 /// Erase the root of all the temporary directories created by the
1048 /// current thread.
1049 static void
erase_created_temporary_directories_parent(const options & opts)1050 erase_created_temporary_directories_parent(const options &opts)
1051 {
1052 if (opts.verbose)
1053 emit_prefix("abipkgdiff", cerr)
1054 << "Erasing temporary extraction parent directory "
1055 << package::extracted_packages_parent_dir()
1056 << " ...";
1057
1058 string cmd = "rm -rf " + package::extracted_packages_parent_dir();
1059 if (system(cmd.c_str()))
1060 {
1061 if (opts.verbose)
1062 emit_prefix("abipkgdiff", cerr) << "FAILED\n";
1063 }
1064 else
1065 {
1066 if (opts.verbose)
1067 emit_prefix("abipkgdiff", cerr) << "DONE\n";
1068 }
1069 }
1070
1071 /// Extract the content of a package.
1072 ///
1073 /// @param package the package we are looking at.
1074 ///
1075 /// @param opts the options passed to the current program.
1076 static bool
extract_package(const package & package,const options & opts)1077 extract_package(const package& package,
1078 const options &opts)
1079 {
1080 switch(package.type())
1081 {
1082 case abigail::tools_utils::FILE_TYPE_RPM:
1083 #ifdef WITH_RPM
1084 if (!extract_rpm(package.path(), package.extracted_dir_path(), opts))
1085 {
1086 emit_prefix("abipkgdiff", cerr)
1087 << "Error while extracting package " << package.path() << "\n";
1088 return false;
1089 }
1090 return true;
1091 #else
1092 emit_prefix("abipkgdiff", cerr)
1093 << "Support for rpm hasn't been enabled. Please consider "
1094 "enabling it at package configure time\n";
1095 return false;
1096 #endif // WITH_RPM
1097 break;
1098 case abigail::tools_utils::FILE_TYPE_DEB:
1099 #ifdef WITH_DEB
1100 if (!extract_deb(package.path(), package.extracted_dir_path(), opts))
1101 {
1102 emit_prefix("abipkgdiff", cerr)
1103 << "Error while extracting package" << package.path() << "\n";
1104 return false;
1105 }
1106 return true;
1107 #else
1108 emit_prefix("abipkgdiff", cerr)
1109 << "Support for deb hasn't been enabled. Please consider "
1110 "enabling it at package configure time\n";
1111 return false;
1112 #endif // WITH_DEB
1113 break;
1114
1115 case abigail::tools_utils::FILE_TYPE_DIR:
1116 // The input package is just a directory that contains binaries,
1117 // there is nothing to extract.
1118 break;
1119
1120 case abigail::tools_utils::FILE_TYPE_TAR:
1121 #ifdef WITH_TAR
1122 if (!extract_tar(package.path(), package.extracted_dir_path(), opts))
1123 {
1124 emit_prefix("abipkgdiff", cerr)
1125 << "Error while extracting GNU tar archive "
1126 << package.path() << "\n";
1127 return false;
1128 }
1129 return true;
1130 #else
1131 emit_prefix("abipkgdiff", cerr)
1132 << "Support for GNU tar hasn't been enabled. Please consider "
1133 "enabling it at package configure time\n";
1134 return false;
1135 #endif // WITH_TAR
1136 break;
1137
1138 default:
1139 return false;
1140 }
1141 return true;
1142 }
1143
1144 /// Check that the suppression specification files supplied are
1145 /// present. If not, emit an error on stderr.
1146 ///
1147 /// @param opts the options instance to use.
1148 ///
1149 /// @return true if all suppression specification files are present,
1150 /// false otherwise.
1151 static bool
maybe_check_suppression_files(const options & opts)1152 maybe_check_suppression_files(const options& opts)
1153 {
1154 for (vector<string>::const_iterator i = opts.suppression_paths.begin();
1155 i != opts.suppression_paths.end();
1156 ++i)
1157 if (!check_file(*i, cerr, opts.prog_name))
1158 return false;
1159
1160 for (vector<string>::const_iterator i =
1161 opts.kabi_whitelist_paths.begin();
1162 i != opts.kabi_whitelist_paths.end();
1163 ++i)
1164 if (!check_file(*i, cerr, "abidiff"))
1165 return false;
1166
1167 return true;
1168 }
1169
1170 /// Update the diff context from the @ref options data structure.
1171 ///
1172 /// @param ctxt the diff context to update.
1173 ///
1174 /// @param opts the instance of @ref options to consider.
1175 static void
set_diff_context_from_opts(diff_context_sptr ctxt,const options & opts)1176 set_diff_context_from_opts(diff_context_sptr ctxt,
1177 const options& opts)
1178 {
1179 ctxt->default_output_stream(&cout);
1180 ctxt->error_output_stream(&cerr);
1181 // See comment in abidiff.cc's set_diff_context_from_opts.
1182 ctxt->show_redundant_changes(opts.show_redundant_changes
1183 || opts.leaf_changes_only);
1184 ctxt->show_leaf_changes_only(opts.leaf_changes_only);
1185 ctxt->show_impacted_interfaces(opts.show_impacted_interfaces);
1186 ctxt->show_unreachable_types(opts.show_all_types);
1187 ctxt->show_hex_values(opts.show_hexadecimal_values);
1188 ctxt->show_offsets_sizes_in_bits(opts.show_offsets_sizes_in_bits);
1189 ctxt->show_relative_offset_changes(opts.show_relative_offset_changes);
1190 ctxt->show_locs(opts.show_locs);
1191 ctxt->show_linkage_names(opts.show_linkage_names);
1192 ctxt->show_added_fns(opts.show_added_syms);
1193 ctxt->show_added_vars(opts.show_added_syms);
1194 ctxt->show_added_symbols_unreferenced_by_debug_info
1195 (opts.show_added_syms);
1196 ctxt->show_symbols_unreferenced_by_debug_info
1197 (opts.show_symbols_not_referenced_by_debug_info);
1198
1199 if (!opts.show_harmless_changes)
1200 ctxt->switch_categories_off(get_default_harmless_categories_bitmap());
1201
1202 suppressions_type supprs;
1203 for (vector<string>::const_iterator i = opts.suppression_paths.begin();
1204 i != opts.suppression_paths.end();
1205 ++i)
1206 read_suppressions(*i, supprs);
1207 ctxt->add_suppressions(supprs);
1208 }
1209
1210 /// Compare the ABI two elf files, using their associated debug info.
1211 ///
1212 /// The result of the comparison is emitted to standard output.
1213 ///
1214 /// @param elf1 the first elf file to consider.
1215 ///
1216 /// @param debug_dir1 the directory where the debug info file for @p
1217 /// elf1 is stored.
1218 /// The result of the comparison is saved to a global corpus map.
1219 ///
1220 /// @param elf2 the second eld file to consider.
1221 /// @args the list of argument sets used for comparison
1222 ///
1223 /// @param debug_dir2 the directory where the debug info file for @p
1224 /// elf2 is stored.
1225 ///
1226 /// @param opts the options the current program has been called with.
1227 ///
1228 /// @param env the environment encapsulating the entire comparison.
1229 ///
1230 /// @param diff the shared pointer to be set to the result of the comparison.
1231 ///
1232 /// @param detailed_error_status is this pointer is non-null and if
1233 /// the function returns ABIDIFF_ERROR, then the function sets the
1234 /// pointed-to parameter to the abigail::dwarf_reader::status value
1235 /// that gives details about the rror.
1236 ///
1237 /// @return the status of the comparison.
1238 static abidiff_status
compare(const elf_file & elf1,const string & debug_dir1,const suppressions_type & priv_types_supprs1,const elf_file & elf2,const string & debug_dir2,const suppressions_type & priv_types_supprs2,const options & opts,abigail::ir::environment_sptr & env,corpus_diff_sptr & diff,diff_context_sptr & ctxt,abigail::dwarf_reader::status * detailed_error_status=0)1239 compare(const elf_file& elf1,
1240 const string& debug_dir1,
1241 const suppressions_type& priv_types_supprs1,
1242 const elf_file& elf2,
1243 const string& debug_dir2,
1244 const suppressions_type& priv_types_supprs2,
1245 const options& opts,
1246 abigail::ir::environment_sptr &env,
1247 corpus_diff_sptr &diff,
1248 diff_context_sptr &ctxt,
1249 abigail::dwarf_reader::status *detailed_error_status = 0)
1250 {
1251 char *di_dir1 = (char*) debug_dir1.c_str(),
1252 *di_dir2 = (char*) debug_dir2.c_str();
1253
1254 vector<char**> di_dirs1, di_dirs2;
1255 di_dirs1.push_back(&di_dir1);
1256 di_dirs2.push_back(&di_dir2);
1257
1258 if (opts.verbose)
1259 emit_prefix("abipkgdiff", cerr)
1260 << "Comparing the ABIs of file "
1261 << elf1.path
1262 << " and "
1263 << elf2.path
1264 << "...\n";
1265
1266 abigail::dwarf_reader::status c1_status = abigail::dwarf_reader::STATUS_OK,
1267 c2_status = abigail::dwarf_reader::STATUS_OK;
1268
1269 ctxt.reset(new diff_context);
1270 set_diff_context_from_opts(ctxt, opts);
1271 suppressions_type& supprs = ctxt->suppressions();
1272 bool files_suppressed = (file_is_suppressed(elf1.path, supprs)
1273 ||file_is_suppressed(elf2.path, supprs));
1274
1275 if (files_suppressed)
1276 {
1277 if (opts.verbose)
1278 emit_prefix("abipkgdiff", cerr)
1279 << " input file "
1280 << elf1.path << " or " << elf2.path
1281 << " has been suppressed by a suppression specification.\n"
1282 << " Not reading any of them\n";
1283 return abigail::tools_utils::ABIDIFF_OK;
1284 }
1285
1286 // Add the first private type suppressions set to the set of
1287 // suppressions.
1288 for (suppressions_type::const_iterator i = priv_types_supprs1.begin();
1289 i != priv_types_supprs1.end();
1290 ++i)
1291 supprs.push_back(*i);
1292
1293 // Add the second private type suppressions set to the set of
1294 // suppressions.
1295 for (suppressions_type::const_iterator i = priv_types_supprs2.begin();
1296 i != priv_types_supprs2.end();
1297 ++i)
1298 supprs.push_back(*i);
1299
1300 if (opts.verbose)
1301 emit_prefix("abipkgdiff", cerr)
1302 << "Reading file "
1303 << elf1.path
1304 << " ...\n";
1305
1306 corpus_sptr corpus1;
1307 {
1308 read_context_sptr c =
1309 create_read_context(elf1.path, di_dirs1, env.get(),
1310 /*load_all_types=*/opts.show_all_types);
1311 add_read_context_suppressions(*c, priv_types_supprs1);
1312 if (!opts.kabi_suppressions.empty())
1313 add_read_context_suppressions(*c, opts.kabi_suppressions);
1314
1315 corpus1 = read_corpus_from_elf(*c, c1_status);
1316
1317 bool bail_out = false;
1318 if (!(c1_status & abigail::dwarf_reader::STATUS_OK))
1319 {
1320 if (opts.verbose)
1321 emit_prefix("abipkgdiff", cerr)
1322 << "Could not read file '"
1323 << elf1.path
1324 << "' properly\n";
1325
1326 if (detailed_error_status)
1327 *detailed_error_status = c1_status;
1328
1329 bail_out = true;
1330 }
1331
1332 if (opts.fail_if_no_debug_info)
1333 {
1334 bool debug_info_error = false;
1335 if (c1_status & abigail::dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
1336 {
1337 if (opts.verbose)
1338 emit_prefix("abipkgdiff", cerr)
1339 << "while reading file" << elf1.path << "\n";
1340
1341 emit_prefix("abipkgdiff", cerr) << "Could not find debug info file";
1342 if (di_dir1 && strcmp(di_dir1, ""))
1343 cerr << " under " << di_dir1 << "\n";
1344 else
1345 cerr << "\n";
1346
1347 if (detailed_error_status)
1348 *detailed_error_status = c1_status;
1349 debug_info_error = true;
1350 }
1351
1352 if (c1_status & abigail::dwarf_reader::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
1353 {
1354 if (opts.verbose)
1355 emit_prefix("abipkgdiff", cerr)
1356 << "while reading file" << elf1.path << "\n";
1357
1358 emit_prefix("abipkgdiff", cerr)
1359 << "Could not find alternate debug info file";
1360 string alt_di_path;
1361 abigail::dwarf_reader::refers_to_alt_debug_info(*c, alt_di_path);
1362 if (!alt_di_path.empty())
1363 cerr << ": " << alt_di_path << "\n";
1364 else
1365 cerr << "\n";
1366
1367 if (detailed_error_status)
1368 *detailed_error_status = c1_status;
1369 debug_info_error = true;
1370 }
1371
1372 if (debug_info_error)
1373 bail_out = true;
1374 }
1375
1376 if (bail_out)
1377 return abigail::tools_utils::ABIDIFF_ERROR;
1378 }
1379
1380 if (opts.verbose)
1381 emit_prefix("abipkgdiff", cerr)
1382 << "DONE reading file "
1383 << elf1.path
1384 << "\n";
1385
1386 if (opts.verbose)
1387 emit_prefix("abipkgdiff", cerr)
1388 << "Reading file "
1389 << elf2.path
1390 << " ...\n";
1391
1392 corpus_sptr corpus2;
1393 {
1394 read_context_sptr c =
1395 create_read_context(elf2.path, di_dirs2, env.get(),
1396 /*load_all_types=*/opts.show_all_types);
1397 add_read_context_suppressions(*c, priv_types_supprs2);
1398
1399 if (!opts.kabi_suppressions.empty())
1400 add_read_context_suppressions(*c, opts.kabi_suppressions);
1401
1402 corpus2 = read_corpus_from_elf(*c, c2_status);
1403
1404 bool bail_out = false;
1405 if (!(c2_status & abigail::dwarf_reader::STATUS_OK))
1406 {
1407 if (opts.verbose)
1408 emit_prefix("abipkgdiff", cerr)
1409 << "Could not find the read file '"
1410 << elf2.path
1411 << "' properly\n";
1412
1413 if (detailed_error_status)
1414 *detailed_error_status = c2_status;
1415
1416 bail_out = true;
1417 }
1418
1419 if (opts.fail_if_no_debug_info)
1420 {
1421 bool debug_info_error = false;
1422 if (c2_status & abigail::dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
1423 {
1424 if (opts.verbose)
1425 emit_prefix("abipkgdiff", cerr)
1426 << "while reading file" << elf2.path << "\n";
1427
1428 emit_prefix("abipkgdiff", cerr) << "Could not find debug info file";
1429 if (di_dir2 && strcmp(di_dir2, ""))
1430 cerr << " under " << di_dir2 << "\n";
1431 else
1432 cerr << "\n";
1433
1434 if (detailed_error_status)
1435 *detailed_error_status = c2_status;
1436 debug_info_error = true;
1437 }
1438
1439 if (c2_status & abigail::dwarf_reader::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
1440 {
1441 if (opts.verbose)
1442 emit_prefix("abipkgdiff", cerr)
1443 << "while reading file" << elf2.path << "\n";
1444
1445 emit_prefix("abipkgdiff", cerr)
1446 << "Could not find alternate debug info file";
1447 string alt_di_path;
1448 abigail::dwarf_reader::refers_to_alt_debug_info(*c, alt_di_path);
1449 if (!alt_di_path.empty())
1450 cerr << ": " << alt_di_path << "\n";
1451 else
1452 cerr << "\n";
1453
1454 if (detailed_error_status)
1455 *detailed_error_status = c2_status;
1456 debug_info_error = true;
1457 }
1458
1459 if (debug_info_error)
1460 bail_out = true;
1461 }
1462
1463 if (bail_out)
1464 return abigail::tools_utils::ABIDIFF_ERROR;
1465 }
1466
1467 if (opts.verbose)
1468 emit_prefix("abipkgdiff", cerr)
1469 << " DONE reading file " << elf2.path << "\n";
1470
1471 if (opts.verbose)
1472 emit_prefix("abipkgdiff", cerr)
1473 << " Comparing the ABIs of: \n"
1474 << " " << elf1.path << "\n"
1475 << " " << elf2.path << "\n";
1476
1477 diff = compute_diff(corpus1, corpus2, ctxt);
1478
1479 if (opts.verbose)
1480 emit_prefix("abipkgdiff", cerr)
1481 << "Comparing the ABIs of file "
1482 << elf1.path
1483 << " and "
1484 << elf2.path
1485 << " is DONE\n";
1486
1487 abidiff_status s = abigail::tools_utils::ABIDIFF_OK;
1488 if (diff->has_net_changes())
1489 s |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
1490 if (diff->has_incompatible_changes())
1491 s |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
1492
1493 return s;
1494 }
1495
1496 /// Compare an ELF file to its ABIXML representation.
1497 ///
1498 /// @param elf the ELF file to compare.
1499 ///
1500 /// @param debug_dir the debug directory of the ELF file.
1501 ///
1502 /// @param opts the options passed the user.
1503 ///
1504 /// @param env the environment to use for the comparison.
1505 ///
1506 /// @param diff the diff object resulting from the comparison of @p
1507 /// elf against its ABIXML representation.
1508 ///
1509 /// @param ctxt the resulting diff context used for the comparison
1510 /// that yielded @p diff.
1511 ///
1512 /// @param detailed_error_status the detailed error satus returned by
1513 /// this function.
1514 ///
1515 /// @return the status of the self comparison.
1516 static abidiff_status
compare_to_self(const elf_file & elf,const string & debug_dir,const options & opts,abigail::ir::environment_sptr & env,corpus_diff_sptr & diff,diff_context_sptr & ctxt,abigail::dwarf_reader::status * detailed_error_status=0)1517 compare_to_self(const elf_file& elf,
1518 const string& debug_dir,
1519 const options& opts,
1520 abigail::ir::environment_sptr &env,
1521 corpus_diff_sptr &diff,
1522 diff_context_sptr &ctxt,
1523 abigail::dwarf_reader::status *detailed_error_status = 0)
1524 {
1525 char *di_dir = (char*) debug_dir.c_str();
1526
1527 vector<char**> di_dirs;
1528 di_dirs.push_back(&di_dir);
1529
1530 abigail::dwarf_reader::status c_status = abigail::dwarf_reader::STATUS_OK;
1531
1532 if (opts.verbose)
1533 emit_prefix("abipkgdiff", cerr)
1534 << "Comparing the ABI of file '"
1535 << elf.path
1536 << "' against itself ...\n";
1537
1538 if (opts.verbose)
1539 emit_prefix("abipkgdiff", cerr)
1540 << "Reading file "
1541 << elf.path
1542 << " ...\n";
1543
1544 corpus_sptr corp;
1545 {
1546 read_context_sptr c =
1547 create_read_context(elf.path, di_dirs, env.get(),
1548 /*read_all_types=*/opts.show_all_types);
1549
1550 corp = read_corpus_from_elf(*c, c_status);
1551
1552 if (!(c_status & abigail::dwarf_reader::STATUS_OK))
1553 {
1554 if (opts.verbose)
1555 emit_prefix("abipkgdiff", cerr)
1556 << "Could not read file '"
1557 << elf.path
1558 << "' propertly\n";
1559
1560 if (detailed_error_status)
1561 *detailed_error_status = c_status;
1562
1563 return abigail::tools_utils::ABIDIFF_ERROR;
1564 }
1565
1566 if (opts.verbose)
1567 emit_prefix("abipkgdiff", cerr)
1568 << "Read file '"
1569 << elf.path
1570 << "' OK\n";
1571
1572
1573 ABG_ASSERT(corp);
1574 }
1575
1576 corpus_sptr reread_corp;
1577 string abi_file_path;
1578 {
1579 if (!opts.pkg1->create_abi_file_path(elf.path, abi_file_path))
1580 {
1581 if (opts.verbose)
1582 emit_prefix("abipkgdiff", cerr)
1583 << "Could not create the directory tree to store the abi for '"
1584 << elf.path
1585 << "'\n";
1586 }
1587 ofstream of(abi_file_path.c_str(), std::ios_base::trunc);
1588
1589 {
1590 const abigail::xml_writer::write_context_sptr c =
1591 abigail::xml_writer::create_write_context(env.get(), of);
1592
1593 if (opts.verbose)
1594 emit_prefix("abipkgdiff", cerr)
1595 << "Writting ABIXML file '"
1596 << abi_file_path
1597 << "' ...\n";
1598
1599 if (!write_corpus(*c, corp, 0))
1600 {
1601 if (opts.verbose)
1602 emit_prefix("abipkgdiff", cerr)
1603 << "Could not write the ABIXML file to '"
1604 << abi_file_path << "'\n";
1605
1606 return abigail::tools_utils::ABIDIFF_ERROR;
1607 }
1608
1609 of.flush();
1610 of.close();
1611
1612 if (opts.verbose)
1613 emit_prefix("abipkgdiff", cerr)
1614 << "Wrote ABIXML file '"
1615 << abi_file_path
1616 << "' OK\n";
1617 }
1618
1619 {
1620 abigail::xml_reader::read_context_sptr c =
1621 abigail::xml_reader::create_native_xml_read_context(abi_file_path,
1622 env.get());
1623 if (!c)
1624 {
1625 if (opts.verbose)
1626 emit_prefix("abipkgdiff", cerr)
1627 << "Could not create read context for ABIXML file '"
1628 << abi_file_path << "'\n";
1629
1630 return abigail::tools_utils::ABIDIFF_ERROR;
1631 }
1632
1633 if (opts.verbose)
1634 emit_prefix("abipkgdiff", cerr)
1635 << "Reading ABIXML file '"
1636 << abi_file_path
1637 << "' ...\n";
1638
1639 reread_corp = read_corpus_from_input(*c);
1640 if (!reread_corp)
1641 {
1642 if (opts.verbose)
1643 emit_prefix("abipkgdiff", cerr)
1644 << "Could not read temporary ABIXML file '"
1645 << abi_file_path << "'\n";
1646
1647 return abigail::tools_utils::ABIDIFF_ERROR;
1648 }
1649
1650 if (opts.verbose)
1651 emit_prefix("abipkgdiff", cerr)
1652 << "Read file '"
1653 << abi_file_path
1654 << "' OK\n";
1655 }
1656 }
1657
1658 ctxt.reset(new diff_context);
1659 set_diff_context_from_opts(ctxt, opts);
1660
1661 if (opts.verbose)
1662 emit_prefix("abipkgdiff", cerr)
1663 << "Comparing the ABIs of: \n"
1664 << " '" << corp->get_path() << "' against \n"
1665 << " '" << abi_file_path << "'...\n";
1666
1667 diff = compute_diff(corp, reread_corp, ctxt);
1668 if (opts.verbose)
1669 emit_prefix("abipkgdfiff", cerr)
1670 << "... Comparing the ABIs: DONE\n";
1671
1672 abidiff_status s = abigail::tools_utils::ABIDIFF_OK;
1673 if (diff->has_changes())
1674 s |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
1675 if (diff->has_incompatible_changes())
1676 s |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
1677
1678 if (opts.verbose)
1679 emit_prefix("abipkgdfiff", cerr)
1680 << "Comparison against self "
1681 << (s == abigail::tools_utils::ABIDIFF_OK ? "SUCCEEDED" : "FAILED")
1682 << '\n';
1683
1684 return s;
1685 }
1686
1687 /// If devel packages were associated to the main package we are
1688 /// looking at, use the names of the header files (extracted from the
1689 /// package) to generate suppression specification to filter out types
1690 /// that are not defined in those header files.
1691 ///
1692 /// Filtering out types not defined in publi headers amounts to filter
1693 /// out types that are deemed private to the package we are looking
1694 /// at.
1695 ///
1696 /// If the function succeeds, it returns a non-empty vector of
1697 /// suppression specifications.
1698 ///
1699 /// @param pkg the main package we are looking at.
1700 ///
1701 /// @param opts the options of the current program.
1702 ///
1703 /// @return a vector of suppression_sptr. If no suppressions
1704 /// specification were constructed, the returned vector is empty.
1705 static suppressions_type
create_private_types_suppressions(const package & pkg,const options & opts)1706 create_private_types_suppressions(const package& pkg, const options &opts)
1707 {
1708 suppressions_type supprs;
1709
1710 package_sptr devel_pkg = pkg.devel_package();
1711 if (!devel_pkg
1712 || !file_exists(devel_pkg->extracted_dir_path())
1713 || !is_dir(devel_pkg->extracted_dir_path()))
1714 return supprs;
1715
1716 string headers_path = devel_pkg->extracted_dir_path();
1717 if (devel_pkg->type() == abigail::tools_utils::FILE_TYPE_RPM
1718 ||devel_pkg->type() == abigail::tools_utils::FILE_TYPE_DEB)
1719 // For RPM and DEB packages, header files are under the
1720 // /usr/include sub-directories.
1721 headers_path += "/usr/include";
1722
1723 if (!is_dir(headers_path))
1724 return supprs;
1725
1726 suppression_sptr suppr =
1727 gen_suppr_spec_from_headers(headers_path);
1728
1729 if (suppr)
1730 {
1731 if (opts.drop_private_types)
1732 suppr->set_drops_artifact_from_ir(true);
1733 supprs.push_back(suppr);
1734 }
1735
1736 return supprs;
1737 }
1738
1739 /// If the user wants to avoid comparing DSOs that are private to this
1740 /// package, then we build the set of public DSOs as advertised in the
1741 /// package's "provides" property.
1742 ///
1743 /// Note that at the moment this function only works for RPMs. It
1744 /// doesn't yet support other packaging formats.
1745 ///
1746 /// @param pkg the package to consider.
1747 ///
1748 /// @param opts the options of this program.
1749 ///
1750 /// @return true iff the set of public DSOs was built.
1751 static bool
maybe_create_public_dso_sonames_set(package & pkg,const options & opts)1752 maybe_create_public_dso_sonames_set(package& pkg, const options &opts)
1753 {
1754 if (opts.compare_private_dsos || !pkg.public_dso_sonames().empty())
1755 return false;
1756
1757 if (pkg.type() == abigail::tools_utils::FILE_TYPE_RPM)
1758 return get_dsos_provided_by_rpm(pkg.path(), pkg.public_dso_sonames());
1759
1760 // We don't support this yet for non-RPM packages.
1761 return false;
1762 }
1763
1764 /// Test if we should only compare the public DSOs of a given package.
1765 ///
1766 /// @param pkg the package to consider.
1767 ///
1768 /// @param opts the options of this program
1769 static bool
must_compare_public_dso_only(package & pkg,options & opts)1770 must_compare_public_dso_only(package& pkg, options& opts)
1771 {
1772 if (pkg.type() == abigail::tools_utils::FILE_TYPE_RPM
1773 && !opts.compare_private_dsos)
1774 return true;
1775
1776 return false;
1777 }
1778
1779 /// While walking a file directory, check if a directory entry is a
1780 /// kabi whitelist of a particular architecture.
1781 ///
1782 /// If it is, then save its file path in a vector of whitelists.
1783 ///
1784 /// @param entry the directory entry to consider.
1785 ///
1786 /// @param arch the architecture to consider.
1787 ///
1788 /// @param whitelists out parameter. If @p entry is the whitelist we
1789 /// are looking for, add its path to this output parameter.
1790 static void
maybe_collect_kabi_whitelists(const FTSENT * entry,const string arch,vector<string> & whitelists)1791 maybe_collect_kabi_whitelists(const FTSENT *entry,
1792 const string arch,
1793 vector<string> &whitelists)
1794 {
1795 if (entry == NULL
1796 || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
1797 || entry->fts_info == FTS_ERR
1798 || entry->fts_info == FTS_NS)
1799 return;
1800
1801 string path = entry->fts_path;
1802 maybe_get_symlink_target_file_path(path, path);
1803
1804 string kabi_whitelist_name = "kabi_whitelist_" + arch;
1805
1806 if (string_ends_with(path, kabi_whitelist_name))
1807 whitelists.push_back(path);
1808 }
1809
1810 /// Get the kabi whitelist for a particular architecture under a given
1811 /// directory.
1812 ///
1813 /// @param dir the directory to look at.
1814 ///
1815 /// @param arch the architecture to consider.
1816 ///
1817 /// @param whitelist_paths the vector where to add the whitelists
1818 /// found. Note that a whitelist is added to this parameter iff the
1819 /// function returns true.
1820 ///
1821 /// @return true iff the function found a whitelist at least.
1822 static bool
get_kabi_whitelists_from_arch_under_dir(const string & dir,const string & arch,vector<string> & whitelist_paths)1823 get_kabi_whitelists_from_arch_under_dir(const string& dir,
1824 const string& arch,
1825 vector<string>& whitelist_paths)
1826 {
1827 bool is_ok = false;
1828 char* paths[] = {const_cast<char*>(dir.c_str()), 0};
1829
1830 FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL);
1831 if (!file_hierarchy)
1832 return is_ok;
1833
1834 FTSENT *entry;
1835 while ((entry = fts_read(file_hierarchy)))
1836 maybe_collect_kabi_whitelists(entry, arch, whitelist_paths);
1837
1838 fts_close(file_hierarchy);
1839
1840 return true;
1841 }
1842
1843 /// Find a kabi whitelist in a linux kernel RPM package.
1844 ///
1845 /// Note that the linux kernel RPM package must have been extracted
1846 /// somewhere already.
1847 ///
1848 /// This function then looks for the whitelist under the /lib/modules
1849 /// directory inside the extracted content of the package. If it
1850 /// finds it and saves its file path in the
1851 /// options::kabi_whitelist_paths data member.
1852 ///
1853 /// @param pkg the linux kernel package to consider.
1854 ///
1855 /// @param opts the options the program was invoked with.
1856 static bool
maybe_handle_kabi_whitelist_pkg(const package & pkg,options & opts)1857 maybe_handle_kabi_whitelist_pkg(const package& pkg, options &opts)
1858 {
1859 if (opts.kabi_whitelist_packages.empty()
1860 || !opts.kabi_whitelist_paths.empty()
1861 || !pkg.kabi_whitelist_package())
1862 return false;
1863
1864 if (pkg.type() != abigail::tools_utils::FILE_TYPE_RPM)
1865 return false;
1866
1867 string pkg_name = pkg.base_name();
1868 bool is_linux_kernel_package = file_is_kernel_package(pkg_name, pkg.type());
1869
1870 if (!is_linux_kernel_package)
1871 return false;
1872
1873 package_sptr kabi_wl_pkg = pkg.kabi_whitelist_package();
1874 assert(kabi_wl_pkg);
1875
1876 if (!file_exists(kabi_wl_pkg->extracted_dir_path())
1877 || !is_dir(kabi_wl_pkg->extracted_dir_path()))
1878 return false;
1879
1880 string rpm_arch;
1881 if (!get_rpm_arch(pkg_name, rpm_arch))
1882 return false;
1883
1884 string kabi_wl_path = kabi_wl_pkg->extracted_dir_path();
1885 kabi_wl_path += "/lib/modules";
1886 vector<string> whitelist_paths;
1887
1888 get_kabi_whitelists_from_arch_under_dir(kabi_wl_path, rpm_arch,
1889 whitelist_paths);
1890
1891 if (!whitelist_paths.empty())
1892 {
1893 std::sort(whitelist_paths.begin(), whitelist_paths.end());
1894 opts.kabi_whitelist_paths.push_back(whitelist_paths.back());
1895 }
1896
1897 return true;
1898 }
1899
1900 /// The task that performs the extraction of the content of several
1901 /// packages into a temporary directory.
1902 ///
1903 /// If this task has several packages to extract, then it extracts
1904 /// them in sequence.
1905 ///
1906 /// Note that several instances of tasks can perform their jobs (i.e
1907 /// extract packages in sequence) in parallel.
1908 class pkg_extraction_task : public task
1909 {
1910 pkg_extraction_task();
1911
1912 public:
1913 vector<package_sptr> pkgs;
1914 const options &opts;
1915 bool is_ok;
1916
pkg_extraction_task(const package_sptr & p,const options & o)1917 pkg_extraction_task(const package_sptr &p, const options &o)
1918 : opts(o), is_ok(true)
1919 {pkgs.push_back(p);}
1920
pkg_extraction_task(const vector<package_sptr> & packages,const options & o)1921 pkg_extraction_task(const vector<package_sptr> &packages, const options &o)
1922 : pkgs(packages), opts(o), is_ok(true)
1923 {}
1924
1925 /// The job performed by the current task, which is to extract its
1926 /// packages in sequence. This job is to be performed in parallel
1927 /// with other jobs of other tasks.
1928 virtual void
perform()1929 perform()
1930 {
1931 for (vector<package_sptr>::const_iterator p = pkgs.begin();
1932 p != pkgs.end();
1933 ++p)
1934 is_ok &= extract_package(**p, opts);
1935 }
1936 }; //end class pkg_extraction_task
1937
1938 /// A convenience typedef for a shared pointer to @f pkg_extraction_task.
1939 typedef shared_ptr<pkg_extraction_task> pkg_extraction_task_sptr;
1940
1941 /// The worker task which job is to prepares a package.
1942 ///
1943 /// Preparing a package means:
1944 ///
1945 /// 1/ Extract the package and its ancillary packages.
1946 ///
1947 /// 2/ Analyze the extracted content, map that content so that we
1948 /// determine what the ELF files to be analyze are.
1949 class pkg_prepare_task : public abigail::workers::task
1950 {
1951 pkg_prepare_task();
1952
1953 public:
1954 package_sptr pkg;
1955 options &opts;
1956 bool is_ok;
1957
pkg_prepare_task(package_sptr & p,options & o)1958 pkg_prepare_task(package_sptr &p, options &o)
1959 : pkg(p), opts(o), is_ok(false)
1960 {}
1961
1962 /// The job performed by this task.
1963 virtual void
perform()1964 perform()
1965 {
1966 is_ok = pkg && extract_package_and_map_its_content(pkg, opts);
1967 }
1968 }; //end class pkg_prepare_task
1969
1970 /// A convenience typedef for a shared_ptr to @ref pkg_prepare_task
1971 typedef shared_ptr<pkg_prepare_task> pkg_prepare_task_sptr;
1972
1973 /// The worker task which job is to compare two ELF binaries
1974 class compare_task : public abigail::workers::task
1975 {
1976 public:
1977
1978 compare_args_sptr args;
1979 abidiff_status status;
1980 ostringstream out;
1981 string pretty_output;
1982
compare_task()1983 compare_task()
1984 : status(abigail::tools_utils::ABIDIFF_OK)
1985 {}
1986
compare_task(const compare_args_sptr & a)1987 compare_task(const compare_args_sptr& a)
1988 : args(a),
1989 status(abigail::tools_utils::ABIDIFF_OK)
1990 {}
1991
1992 /// The job performed by the task.
1993 ///
1994 /// This compares two ELF files, gets the resulting test report and
1995 /// stores it in an output stream.
1996 virtual void
perform()1997 perform()
1998 {
1999 abigail::ir::environment_sptr env(new abigail::ir::environment);
2000 diff_context_sptr ctxt;
2001 corpus_diff_sptr diff;
2002
2003 abigail::dwarf_reader::status detailed_status =
2004 abigail::dwarf_reader::STATUS_UNKNOWN;
2005
2006 status |= compare(args->elf1, args->debug_dir1, args->private_types_suppr1,
2007 args->elf2, args->debug_dir2, args->private_types_suppr2,
2008 args->opts, env, diff, ctxt, &detailed_status);
2009
2010 // If there is an ABI change, tell the user about it.
2011 if ((status & abigail::tools_utils::ABIDIFF_ABI_CHANGE)
2012 ||( diff && diff->has_net_changes()))
2013 {
2014 diff->report(out, /*prefix=*/" ");
2015 string name = args->elf1.name;
2016
2017 pretty_output +=
2018 string("================ changes of '") + name + "'===============\n"
2019 + out.str()
2020 + "================ end of changes of '"
2021 + name + "'===============\n\n";
2022 }
2023 else
2024 {
2025 if (args->opts.show_identical_binaries)
2026 out << "No ABI change detected\n";
2027 }
2028
2029 // If an error happened while comparing the two binaries, tell the
2030 // user about it.
2031 if (status & abigail::tools_utils::ABIDIFF_ERROR)
2032 {
2033 string diagnostic =
2034 abigail::dwarf_reader::status_to_diagnostic_string(detailed_status);
2035 if (diagnostic.empty())
2036 diagnostic =
2037 "Unknown error. Please run the tool again with --verbose\n";
2038
2039 string name = args->elf1.name;
2040 pretty_output +=
2041 "==== Error happened during processing of '" + name + "' ====\n";
2042 pretty_output += diagnostic;
2043 pretty_output +=
2044 "==== End of error for '" + name + "' ====\n";
2045 }
2046 }
2047 }; // end class compare_task
2048
2049 /// Convenience typedef for a shared_ptr of @ref compare_task.
2050 typedef shared_ptr<compare_task> compare_task_sptr;
2051
2052 /// The worker task which job is to compare an ELF binary to its ABI
2053 /// representation.
2054 class self_compare_task : public compare_task
2055 {
2056 public:
self_compare_task(const compare_args_sptr & a)2057 self_compare_task(const compare_args_sptr& a)
2058 : compare_task(a)
2059 {}
2060
2061 /// The job performed by the task.
2062 ///
2063 /// This compares an ELF file to its ABIXML representation and
2064 /// expects the result to be the empty set.
2065 virtual void
perform()2066 perform()
2067 {
2068 abigail::ir::environment_sptr env(new abigail::ir::environment);
2069 diff_context_sptr ctxt;
2070 corpus_diff_sptr diff;
2071
2072 abigail::dwarf_reader::status detailed_status =
2073 abigail::dwarf_reader::STATUS_UNKNOWN;
2074
2075 status |= compare_to_self(args->elf1, args->debug_dir1,
2076 args->opts, env, diff, ctxt,
2077 &detailed_status);
2078
2079 string name = args->elf1.name;
2080 if (status == abigail::tools_utils::ABIDIFF_OK)
2081 pretty_output += "==== SELF CHECK SUCCEEDED for '"+ name + "' ====\n";
2082 else if ((status & abigail::tools_utils::ABIDIFF_ABI_CHANGE)
2083 ||( diff && diff->has_net_changes()))
2084 {
2085 // There is an ABI change, tell the user about it.
2086 diff->report(out, /*indent=*/" ");
2087
2088 pretty_output +=
2089 string("======== comparing'") + name +
2090 "' to itself wrongly yielded result: ===========\n"
2091 + out.str()
2092 + "===SELF CHECK FAILED for '"+ name + "'\n";
2093 }
2094
2095 // If an error happened while comparing the two binaries, tell the
2096 // user about it.
2097 if (status & abigail::tools_utils::ABIDIFF_ERROR)
2098 {
2099 string diagnostic =
2100 abigail::dwarf_reader::status_to_diagnostic_string(detailed_status);
2101
2102 if (diagnostic.empty())
2103 diagnostic =
2104 "Unknown error. Please run the tool again with --verbose\n";
2105
2106 string name = args->elf1.name;
2107 pretty_output +=
2108 "==== Error happened during self check of '" + name + "' ====\n";
2109 pretty_output += diagnostic;
2110 pretty_output +=
2111 "==== SELF CHECK FAILED for '" + name + "' ====\n";
2112
2113 }
2114 }
2115 }; // end class self_compare
2116
2117 /// Convenience typedef for a shared_ptr of @ref compare_task.
2118 typedef shared_ptr<self_compare_task> self_compare_task_sptr;
2119
2120 /// This function is a sub-routine of create_maps_of_package_content.
2121 ///
2122 /// It's called during the walking of the directory tree containing
2123 /// the extracted content of package. It's called with an entry of
2124 /// that directory tree.
2125 ///
2126 /// Depending on the kind of file this function is called on, it
2127 /// updates the vector of paths of the directory and the set of
2128 /// suppression paths found.
2129 ///
2130 /// @param entry the directory entry to analyze.
2131 ///
2132 /// @param opts the options of the current program.
2133 ///
2134 /// @param file_name_to_look_for if this parameter is set, the
2135 /// function only looks for a file name which name is the same as the
2136 /// value of this parameter.
2137 ///
2138 /// @param paths out parameter. This is the set of meaningful paths
2139 /// of the current directory tree being analyzed. These paths are
2140 /// those that are going to be involved in ABI comparison.
2141 static void
maybe_update_package_content(const FTSENT * entry,options & opts,const string & file_name_to_look_for,unordered_set<string> & paths)2142 maybe_update_package_content(const FTSENT *entry,
2143 options &opts,
2144 const string& file_name_to_look_for,
2145 unordered_set<string>& paths)
2146 {
2147 if (entry == NULL
2148 || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2149 || entry->fts_info == FTS_ERR
2150 || entry->fts_info == FTS_NS)
2151 return;
2152
2153 string path = entry->fts_path;
2154 maybe_get_symlink_target_file_path(path, path);
2155
2156 if (!file_name_to_look_for.empty())
2157 {
2158 string name;
2159 abigail::tools_utils::base_name(path, name);
2160 if (name == file_name_to_look_for)
2161 paths.insert(path);
2162 return;
2163 }
2164
2165 if (guess_file_type(path) == abigail::tools_utils::FILE_TYPE_ELF)
2166 paths.insert(path);
2167 else if (opts.abignore && string_ends_with(path, ".abignore"))
2168 opts.suppression_paths.push_back(path);
2169 }
2170
2171 /// Walk a given directory to collect files that are "interesting" to
2172 /// analyze. By default, "interesting" means interesting from either
2173 /// a kernel package or a userspace binary analysis point of view.
2174 ///
2175 /// @param dir the directory to walk.
2176 ///
2177 /// @param file_name_to_look_for if this parameter is set, only a file
2178 /// with this name is going to be collected.
2179 ///
2180 /// @param interesting_files out parameter. This parameter is
2181 /// populated with the interesting files found by the function iff the
2182 /// function returns true.
2183 ///
2184 /// @return true iff the function completed successfully.
2185 static bool
get_interesting_files_under_dir(const string dir,const string & file_name_to_look_for,options & opts,vector<string> & interesting_files)2186 get_interesting_files_under_dir(const string dir,
2187 const string& file_name_to_look_for,
2188 options& opts,
2189 vector<string>& interesting_files)
2190 {
2191 bool is_ok = false;
2192 string root;
2193 real_path(dir, root);
2194 if (root.empty())
2195 root = dir;
2196
2197 char* paths[] = {const_cast<char*>(root.c_str()), 0};
2198
2199 FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL);
2200 if (!file_hierarchy)
2201 return is_ok;
2202
2203 FTSENT *entry;
2204 unordered_set<string> files;
2205 while ((entry = fts_read(file_hierarchy)))
2206 maybe_update_package_content(entry, opts, file_name_to_look_for, files);
2207
2208 for (unordered_set<string>::const_iterator i = files.begin();
2209 i != files.end();
2210 ++i)
2211 interesting_files.push_back(*i);
2212
2213 fts_close(file_hierarchy);
2214
2215 is_ok = true;
2216
2217 return is_ok;
2218 }
2219
2220 /// Create maps of the content of a given package.
2221 ///
2222 /// The maps contain relevant metadata about the content of the
2223 /// files. These maps are used afterwards during the comparison of
2224 /// the content of the package. Note that the maps are stored in the
2225 /// object that represents that package.
2226 ///
2227 /// @param package the package to consider.
2228 ///
2229 /// @param opts the options the current program has been called with.
2230 ///
2231 /// @param true upon successful completion, false otherwise.
2232 static bool
create_maps_of_package_content(package & package,options & opts)2233 create_maps_of_package_content(package& package, options& opts)
2234 {
2235 if (opts.verbose)
2236 emit_prefix("abipkgdiff", cerr)
2237 << "Analyzing the content of package "
2238 << package.path()
2239 << " extracted to "
2240 << package.extracted_dir_path()
2241 << " ...\n";
2242
2243 bool is_ok = true;
2244 vector<string> elf_file_paths;
2245
2246 // if package is linux kernel package and its associated debug
2247 // info package looks like a kernel debuginfo package, then try to
2248 // go find the vmlinux file in that debug info file.
2249 string pkg_name = package.base_name();
2250 bool is_linux_kernel_package = file_is_kernel_package(pkg_name,
2251 package.type());
2252 if (is_linux_kernel_package)
2253 {
2254 // For a linux kernel package, no analysis is done. It'll be
2255 // done later at comparison time by
2256 // compare_prepared_linux_kernel_packages
2257 is_ok = true;
2258 if (opts.verbose)
2259 emit_prefix("abipkgdiff", cerr)
2260 << " Analysis of " << package.path() << " DONE\n";
2261 return is_ok;
2262 }
2263
2264 is_ok &= get_interesting_files_under_dir(package.extracted_dir_path(),
2265 /*file_name_to_look_for=*/"",
2266 opts, elf_file_paths);
2267
2268 if (opts.verbose)
2269 emit_prefix("abipkgdiff", cerr)
2270 << "Found " << elf_file_paths.size() << " files in "
2271 << package.extracted_dir_path() << "\n";
2272
2273 // determine if all files have the same prefix. Compute that prefix
2274 // and stick it into the package! That prefix is going to be used
2275 // later by the package::convert_path_to_unique_suffix method.
2276 package.load_elf_file_paths(opts);
2277
2278 maybe_create_public_dso_sonames_set(package, opts);
2279
2280 for (vector<string>::const_iterator file = elf_file_paths.begin();
2281 file != elf_file_paths.end();
2282 ++file)
2283 {
2284 elf_file_sptr e (new elf_file(*file));
2285 if (opts.compare_dso_only)
2286 {
2287 if (e->type != abigail::dwarf_reader::ELF_TYPE_DSO)
2288 {
2289 if (opts.verbose)
2290 emit_prefix("abipkgdiff", cerr)
2291 << "skipping non-DSO file " << e->path << "\n";
2292 continue;
2293 }
2294 }
2295 else
2296 {
2297 if (e->type != abigail::dwarf_reader::ELF_TYPE_DSO
2298 && e->type != abigail::dwarf_reader::ELF_TYPE_EXEC
2299 && e->type != abigail::dwarf_reader::ELF_TYPE_PI_EXEC)
2300 {
2301 if (is_linux_kernel_package)
2302 {
2303 if (e->type == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)
2304 {
2305 // This is a Linux Kernel module.
2306 ;
2307 }
2308 }
2309 else if (opts.verbose)
2310 {
2311 emit_prefix("abipkgdiff", cerr)
2312 << "skipping non-DSO non-executable file "
2313 << e->path
2314 << "\n";
2315 continue;
2316 }
2317 }
2318 }
2319
2320 if (e->soname.empty())
2321 {
2322 if (e->type == abigail::dwarf_reader::ELF_TYPE_DSO
2323 && must_compare_public_dso_only(package, opts))
2324 {
2325 // We are instructed to compare public DSOs only. Yet
2326 // this DSO does not have a soname. so it can not be a
2327 // public DSO. Let's skip it.
2328 if (opts.verbose)
2329 emit_prefix("abipkgdiff", cerr)
2330 << "DSO " << e->path
2331 << " does not have a soname so it's private. Skipping it\n";
2332 continue;
2333 }
2334
2335 // Several binaries at different paths can have the same
2336 // base name. So let's consider the full path of the binary
2337 // inside the extracted directory.
2338 string key = e->name;
2339 package.convert_path_to_unique_suffix(e->path, key);
2340 package.path_elf_file_sptr_map()[key] = e;
2341 if (opts.verbose)
2342 emit_prefix("abipkgdiff", cerr)
2343 << "mapped binary with key '" << key << "'"
2344 << "\n";
2345 }
2346 else
2347 {
2348 // Several binaries at different paths can have the same
2349 // soname. So let's *also* consider the full path of the
2350 // binary inside the extracted directory, not just the
2351 // soname.
2352 string key = e->soname;
2353
2354 if (must_compare_public_dso_only(package, opts))
2355 {
2356 if (package.public_dso_sonames().find(key)
2357 == package.public_dso_sonames().end())
2358 {
2359 // We are instructed to compare public DSOs only and
2360 // this one seems to be private. So skip it.
2361 if (opts.verbose)
2362 emit_prefix("abipkgdiff", cerr)
2363 << "DSO " << e->path << " of soname " << key
2364 << " seems to be private. Skipping it\n";
2365 continue;
2366 }
2367 }
2368
2369 if (package.convert_path_to_unique_suffix(e->path, key))
2370 {
2371 dir_name(key, key);
2372 key += string("/@soname:") + e->soname;
2373 }
2374 package.path_elf_file_sptr_map()[key] = e;
2375 if (opts.verbose)
2376 emit_prefix("abipkgdiff", cerr)
2377 << "mapped binary with key '" << key << "'"
2378 << "\n";
2379 }
2380 }
2381
2382 if (opts.verbose)
2383 emit_prefix("abipkgdiff", cerr)
2384 << " Analysis of " << package.path() << " DONE\n";
2385
2386 is_ok = true;
2387
2388 return is_ok;
2389 }
2390
2391 /// Extract the content of a package (and its ancillary packages) and
2392 /// map its content.
2393 ///
2394 /// First, the content of the package and its ancillary packages are
2395 /// extracted, in parallel.
2396 ///
2397 /// Then, after that extraction is done, the content of the package if
2398 /// walked and analyzed.
2399 ///
2400 /// @param pkg the package to extract and to analyze.
2401 ///
2402 /// @param opts the options of the current program.
2403 ///
2404 /// @return true iff the extraction and analyzing went well.
2405 static bool
extract_package_and_map_its_content(const package_sptr & pkg,options & opts)2406 extract_package_and_map_its_content(const package_sptr &pkg, options &opts)
2407 {
2408 assert(pkg);
2409
2410 pkg_extraction_task_sptr main_pkg_extraction;
2411 pkg_extraction_task_sptr dbg_extraction;
2412 pkg_extraction_task_sptr devel_extraction;
2413 pkg_extraction_task_sptr kabi_whitelist_extraction;
2414
2415 size_t NUM_EXTRACTIONS = 1;
2416
2417 main_pkg_extraction.reset(new pkg_extraction_task(pkg, opts));
2418
2419 if (!pkg->debug_info_packages().empty())
2420 {
2421 dbg_extraction.reset(new pkg_extraction_task(pkg->debug_info_packages(),
2422 opts));
2423 ++NUM_EXTRACTIONS;
2424 }
2425
2426 if (package_sptr devel_pkg = pkg->devel_package())
2427 {
2428 devel_extraction.reset(new pkg_extraction_task(devel_pkg, opts));
2429 ++NUM_EXTRACTIONS;
2430 }
2431
2432 if (package_sptr kabi_wl_pkg = pkg->kabi_whitelist_package())
2433 {
2434 kabi_whitelist_extraction.reset(new pkg_extraction_task(kabi_wl_pkg,
2435 opts));
2436 ++NUM_EXTRACTIONS;
2437 }
2438
2439 size_t num_workers = (opts.parallel
2440 ? std::min(opts.num_workers, NUM_EXTRACTIONS)
2441 : 1);
2442 abigail::workers::queue extraction_queue(num_workers);
2443
2444 // Perform the extraction of the NUM_WORKERS packages in parallel.
2445 extraction_queue.schedule_task(dbg_extraction);
2446 extraction_queue.schedule_task(main_pkg_extraction);
2447 extraction_queue.schedule_task(devel_extraction);
2448 extraction_queue.schedule_task(kabi_whitelist_extraction);
2449
2450 // Wait for the extraction to be done.
2451 extraction_queue.wait_for_workers_to_complete();
2452
2453 // Analyze and map the content of the extracted package.
2454 bool is_ok = false;
2455 if (main_pkg_extraction->is_ok)
2456 is_ok = create_maps_of_package_content(*pkg, opts);
2457
2458 if (is_ok)
2459 maybe_handle_kabi_whitelist_pkg(*pkg, opts);
2460
2461 return is_ok;
2462 }
2463
2464 /// Extract the two packages (and their ancillary packages) and
2465 /// analyze their content, so that we later know what files from the
2466 /// first package to compare against what files from the second
2467 /// package.
2468 ///
2469 /// Note that preparing the first package and its ancillary packages
2470 /// happens in parallel with preparing the second package and its
2471 /// ancillary packages. The function then waits for the two
2472 /// preparations to complete before returning.
2473 ///
2474 /// @param first_package the first package to consider.
2475 ///
2476 /// @param second_package the second package to consider.
2477 ///
2478 /// @param opts the options of the current program.
2479 ///
2480 /// @return true iff the preparation went well.
2481 static bool
prepare_packages(package_sptr & first_package,package_sptr & second_package,options & opts)2482 prepare_packages(package_sptr &first_package,
2483 package_sptr &second_package,
2484 options &opts)
2485 {
2486 pkg_prepare_task_sptr first_pkg_prepare;
2487 pkg_prepare_task_sptr second_pkg_prepare;
2488 size_t NUM_PREPARATIONS = 2;
2489
2490 first_pkg_prepare.reset(new pkg_prepare_task(first_package, opts));
2491 second_pkg_prepare.reset(new pkg_prepare_task(second_package, opts));
2492
2493 size_t num_workers = (opts.parallel
2494 ? std::min(opts.num_workers, NUM_PREPARATIONS)
2495 : 1);
2496 abigail::workers::queue preparation_queue(num_workers);
2497
2498 preparation_queue.schedule_task(first_pkg_prepare);
2499 preparation_queue.schedule_task(second_pkg_prepare);
2500
2501 preparation_queue.wait_for_workers_to_complete();
2502
2503 return first_pkg_prepare->is_ok && second_pkg_prepare->is_ok;
2504 }
2505
2506 /// Prepare one package for the sake of comparing it to its ABIXML
2507 /// representation.
2508 ///
2509 /// The preparation entails unpacking the content of the package into
2510 /// a temporary directory and mapping its content.
2511 ///
2512 /// @param pkg the package to prepare.
2513 ///
2514 /// @param opts the options provided by the user.
2515 ///
2516 /// @return true iff the preparation succeeded.
2517 static bool
prepare_package(package_sptr & pkg,options & opts)2518 prepare_package(package_sptr& pkg, options &opts)
2519 {return extract_package_and_map_its_content(pkg, opts);}
2520
2521 /// Compare the added sizes of an ELF pair (specified by a comparison
2522 /// task that compares two ELF files) against the added sizes of a
2523 /// second ELF pair.
2524 ///
2525 /// Larger filesize strongly raises the possibility of larger debug-info,
2526 /// hence longer diff time. For a package containing several relatively
2527 /// large and small ELFs, it is often more efficient to start working on
2528 /// the larger ones first. This function is used to order the pairs by
2529 /// size, starting from the largest.
2530 ///
2531 /// @param t1 the first comparison task that compares a pair of ELF
2532 /// files.
2533 ///
2534 /// @param t2 the second comparison task that compares a pair of ELF
2535 /// files.
2536 ///
2537 /// @return true if @p task1 is greater than @p task2.
2538 bool
elf_size_is_greater(const task_sptr & task1,const task_sptr & task2)2539 elf_size_is_greater(const task_sptr &task1,
2540 const task_sptr &task2)
2541 {
2542 compare_task_sptr t1 = dynamic_pointer_cast<compare_task>(task1);
2543 compare_task_sptr t2 = dynamic_pointer_cast<compare_task>(task2);
2544
2545 ABG_ASSERT(t1->args && t2->args);
2546 off_t s1 = t1->args->elf1.size + t1->args->elf2.size;
2547 off_t s2 = t2->args->elf1.size + t2->args->elf2.size;
2548
2549 if (s1 != s2)
2550 return s1 > s2;
2551
2552 // The sizes of the compared binaries are the same. So sort them
2553 // lexicographically.
2554 return t1->args->elf1.name < t2->args->elf1.name;
2555
2556 }
2557
2558 /// This type is used to notify the calling thread that the comparison
2559 /// of two ELF files is done.
2560 class comparison_done_notify : public abigail::workers::queue::task_done_notify
2561 {
2562 comparison_done_notify();
2563
2564 public:
2565 abi_diff& diff;
2566 abidiff_status status;
2567
comparison_done_notify(abi_diff & d)2568 comparison_done_notify(abi_diff &d)
2569 : diff(d),
2570 status(abigail::tools_utils::ABIDIFF_OK)
2571 {}
2572
2573 /// This operator is invoked by the worker queue whenever a
2574 /// comparison task is done.
2575 ///
2576 /// The operator collects the status of the job of the task and also
2577 /// updates the the count of binaries that have ABI changes.
2578 ///
2579 /// @param task_done the task that is done.
2580 virtual void
operator ()(const task_sptr & task_done)2581 operator()(const task_sptr& task_done)
2582 {
2583 compare_task_sptr comp_task = dynamic_pointer_cast<compare_task>(task_done);
2584 assert(comp_task);
2585
2586 status |= comp_task->status;
2587
2588 if (status != abigail::tools_utils::ABIDIFF_OK)
2589 {
2590 string name = comp_task->args->elf1.name;
2591
2592 if (status & abigail::tools_utils::ABIDIFF_ABI_CHANGE)
2593 diff.changed_binaries.push_back(name);
2594 }
2595 }
2596 }; // end struct comparison_done_notify
2597
2598 /// Erase the temporary directories that might have been created while
2599 /// handling two packages, unless the user asked to keep the temporary
2600 /// directories around.
2601 ///
2602 /// @param first_package the first package to consider.
2603 ///
2604 /// @param second_package the second package to consider.
2605 ///
2606 /// @param opts the options passed to the program.
2607 static void
maybe_erase_temp_dirs(package & first_package,package & second_package,options & opts)2608 maybe_erase_temp_dirs(package& first_package, package& second_package,
2609 options& opts)
2610 {
2611 if (opts.keep_tmp_files)
2612 return;
2613
2614 erase_created_temporary_directories(first_package, second_package, opts);
2615 erase_created_temporary_directories_parent(opts);
2616 }
2617
2618 /// Compare the ABI of two prepared packages that contain userspace
2619 /// binaries.
2620 ///
2621 /// A prepared package is a package which content has been extracted
2622 /// and mapped.
2623 ///
2624 /// @param first_package the first package to consider.
2625 ///
2626 /// @param second_package the second package to consider.
2627 ///
2628 /// @param options the options the current program has been called
2629 /// with.
2630 ///
2631 /// @param diff out parameter. If this function returns true, then
2632 /// this parameter is set to the result of the comparison.
2633 ///
2634 /// @param opts the options of the current program.
2635 ///
2636 /// @return the status of the comparison.
2637 static abidiff_status
compare_prepared_userspace_packages(package & first_package,package & second_package,abi_diff & diff,options & opts)2638 compare_prepared_userspace_packages(package& first_package,
2639 package& second_package,
2640 abi_diff& diff, options& opts)
2641 {
2642 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
2643 abigail::workers::queue::tasks_type compare_tasks;
2644 string pkg_name = first_package.base_name();
2645
2646 // Setting debug-info path of libraries
2647 string debug_dir1, debug_dir2, relative_debug_path = "/usr/lib/debug/";
2648 if (!first_package.debug_info_packages().empty()
2649 && !second_package.debug_info_packages().empty())
2650 {
2651 debug_dir1 =
2652 first_package.debug_info_packages().front()->extracted_dir_path() +
2653 relative_debug_path;
2654 debug_dir2 =
2655 second_package.debug_info_packages().front()->extracted_dir_path() +
2656 relative_debug_path;
2657 }
2658
2659 for (map<string, elf_file_sptr>::iterator it =
2660 first_package.path_elf_file_sptr_map().begin();
2661 it != first_package.path_elf_file_sptr_map().end();
2662 ++it)
2663 {
2664 map<string, elf_file_sptr>::iterator iter =
2665 second_package.path_elf_file_sptr_map().find(it->first);
2666
2667 if (iter != second_package.path_elf_file_sptr_map().end()
2668 && (iter->second->type == abigail::dwarf_reader::ELF_TYPE_DSO
2669 || iter->second->type == abigail::dwarf_reader::ELF_TYPE_EXEC
2670 || iter->second->type == abigail::dwarf_reader::ELF_TYPE_PI_EXEC
2671 || iter->second->type == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE))
2672 {
2673 if (iter->second->type != abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)
2674 {
2675 compare_args_sptr args
2676 (new compare_args(*it->second,
2677 debug_dir1,
2678 create_private_types_suppressions
2679 (first_package, opts),
2680 *iter->second,
2681 debug_dir2,
2682 create_private_types_suppressions
2683 (second_package, opts), opts));
2684 compare_task_sptr t(new compare_task(args));
2685 compare_tasks.push_back(t);
2686 }
2687 second_package.path_elf_file_sptr_map().erase(iter);
2688 }
2689 else if (iter == second_package.path_elf_file_sptr_map().end())
2690 {
2691 diff.removed_binaries.push_back(it->second);
2692 status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
2693 status |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
2694 }
2695 }
2696
2697 if (compare_tasks.empty())
2698 {
2699 maybe_erase_temp_dirs(first_package, second_package, opts);
2700 return abigail::tools_utils::ABIDIFF_OK;
2701 }
2702
2703 // Larger elfs are processed first, since it's usually safe to assume
2704 // their debug-info is larger as well, but the results are still
2705 // in a map ordered by looked up in elf.name order.
2706 std::sort(compare_tasks.begin(), compare_tasks.end(), elf_size_is_greater);
2707
2708 // There's no reason to spawn more workers than there are ELF pairs
2709 // to be compared.
2710 size_t num_workers = (opts.parallel
2711 ? std::min(opts.num_workers, compare_tasks.size())
2712 : 1);
2713 assert(num_workers >= 1);
2714
2715 comparison_done_notify notifier(diff);
2716 abigail::workers::queue comparison_queue(num_workers, notifier);
2717
2718 // Compare all the binaries, in parallel and then wait for the
2719 // comparisons to complete.
2720 comparison_queue.schedule_tasks(compare_tasks);
2721 comparison_queue.wait_for_workers_to_complete();
2722
2723 // Get the set of comparison tasks that were perform and sort them.
2724 queue::tasks_type& done_tasks = comparison_queue.get_completed_tasks();
2725 std::sort(done_tasks.begin(), done_tasks.end(), elf_size_is_greater);
2726
2727 // Print the reports of the comparison to standard output.
2728 for (queue::tasks_type::const_iterator i = done_tasks.begin();
2729 i != done_tasks.end();
2730 ++i)
2731 {
2732 compare_task_sptr t = dynamic_pointer_cast<compare_task>(*i);
2733 cout << t->pretty_output;
2734 }
2735
2736 // Update the count of added binaries.
2737 for (map<string, elf_file_sptr>::iterator it =
2738 second_package.path_elf_file_sptr_map().begin();
2739 it != second_package.path_elf_file_sptr_map().end();
2740 ++it)
2741 diff.added_binaries.push_back(it->second);
2742
2743 // Print information about removed binaries on standard output.
2744 if (diff.removed_binaries.size())
2745 {
2746 cout << "Removed binaries:\n";
2747 for (vector<elf_file_sptr>::iterator it = diff.removed_binaries.begin();
2748 it != diff.removed_binaries.end(); ++it)
2749 {
2750 string relative_path;
2751 first_package.convert_path_to_relative((*it)->path, relative_path);
2752 cout << " [D] " << relative_path << ", ";
2753 string soname;
2754 get_soname_of_elf_file((*it)->path, soname);
2755 if (!soname.empty())
2756 cout << "SONAME: " << soname;
2757 else
2758 cout << "no SONAME";
2759 cout << "\n";
2760 }
2761 }
2762
2763 // Print information about added binaries on standard output.
2764 if (opts.show_added_binaries && diff.added_binaries.size())
2765 {
2766 cout << "Added binaries:\n";
2767 for (vector<elf_file_sptr>::iterator it = diff.added_binaries.begin();
2768 it != diff.added_binaries.end(); ++it)
2769 {
2770 string relative_path;
2771 second_package.convert_path_to_relative((*it)->path, relative_path);
2772 cout << " [A] " << relative_path << ", ";
2773 string soname;
2774 get_soname_of_elf_file((*it)->path, soname);
2775 if (!soname.empty())
2776 cout << "SONAME: " << soname;
2777 else
2778 cout << "no SONAME";
2779 cout << "\n";
2780 }
2781 }
2782
2783 // Erase temporary directory tree we might have left behind.
2784 maybe_erase_temp_dirs(first_package, second_package, opts);
2785
2786 status = notifier.status;
2787
2788 return status;
2789 }
2790
2791 /// In the context of the unpacked content of a given package, compare
2792 /// the binaries inside the package against their ABIXML
2793 /// representation. This should yield the empty set.
2794 ///
2795 /// @param pkg (unpacked) package
2796 ///
2797 /// @param diff the representation of the changes between the binaries
2798 /// and their ABIXML. This should obviously be the empty set.
2799 ///
2800 /// @param diff a textual representation of the diff.
2801 ///
2802 /// @param opts the options provided by the user.
2803 static abidiff_status
self_compare_prepared_userspace_package(package & pkg,abi_diff & diff,options & opts)2804 self_compare_prepared_userspace_package(package& pkg,
2805 abi_diff& diff,
2806 options& opts)
2807 {
2808 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
2809 abigail::workers::queue::tasks_type self_compare_tasks;
2810 string pkg_name = pkg.base_name();
2811
2812 // Setting debug-info path of libraries
2813 string debug_dir, relative_debug_path = "/usr/lib/debug/";
2814 if (!pkg.debug_info_packages().empty())
2815 debug_dir =
2816 pkg.debug_info_packages().front()->extracted_dir_path() +
2817 relative_debug_path;
2818
2819 suppressions_type supprs;
2820 for (map<string, elf_file_sptr>::iterator it =
2821 pkg.path_elf_file_sptr_map().begin();
2822 it != pkg.path_elf_file_sptr_map().end();
2823 ++it)
2824 {
2825 if (it != pkg.path_elf_file_sptr_map().end()
2826 && (it->second->type == abigail::dwarf_reader::ELF_TYPE_DSO
2827 || it->second->type == abigail::dwarf_reader::ELF_TYPE_EXEC
2828 || it->second->type == abigail::dwarf_reader::ELF_TYPE_PI_EXEC
2829 || it->second->type == abigail::dwarf_reader::ELF_TYPE_RELOCATABLE))
2830 {
2831 if (it->second->type != abigail::dwarf_reader::ELF_TYPE_RELOCATABLE)
2832 {
2833 compare_args_sptr args
2834 (new compare_args(*it->second,
2835 debug_dir,
2836 supprs,
2837 *it->second,
2838 debug_dir,
2839 supprs,
2840 opts));
2841 self_compare_task_sptr t(new self_compare_task(args));
2842 self_compare_tasks.push_back(t);
2843 }
2844 }
2845 }
2846
2847 if (self_compare_tasks.empty())
2848 {
2849 maybe_erase_temp_dirs(pkg, pkg, opts);
2850 return abigail::tools_utils::ABIDIFF_OK;
2851 }
2852
2853 // Larger elfs are processed first, since it's usually safe to assume
2854 // their debug-info is larger as well, but the results are still
2855 // in a map ordered by looked up in elf.name order.
2856 std::sort(self_compare_tasks.begin(),
2857 self_compare_tasks.end(),
2858 elf_size_is_greater);
2859
2860 // There's no reason to spawn more workers than there are ELF pairs
2861 // to be compared.
2862 size_t num_workers = (opts.parallel
2863 ? std::min(opts.num_workers, self_compare_tasks.size())
2864 : 1);
2865 assert(num_workers >= 1);
2866
2867 comparison_done_notify notifier(diff);
2868 abigail::workers::queue comparison_queue(num_workers, notifier);
2869
2870 // Compare all the binaries, in parallel and then wait for the
2871 // comparisons to complete.
2872 comparison_queue.schedule_tasks(self_compare_tasks);
2873 comparison_queue.wait_for_workers_to_complete();
2874
2875 // Get the set of comparison tasks that were perform and sort them.
2876 queue::tasks_type& done_tasks = comparison_queue.get_completed_tasks();
2877 std::sort(done_tasks.begin(), done_tasks.end(), elf_size_is_greater);
2878
2879 // Print the reports of the comparison to standard output.
2880 for (queue::tasks_type::const_iterator i = done_tasks.begin();
2881 i != done_tasks.end();
2882 ++i)
2883 {
2884 self_compare_task_sptr t = dynamic_pointer_cast<self_compare_task>(*i);
2885 if (t)
2886 cout << t->pretty_output;
2887 }
2888
2889 // Erase temporary directory tree we might have left behind.
2890 maybe_erase_temp_dirs(pkg, pkg, opts);
2891
2892 status = notifier.status;
2893
2894 return status;
2895 }
2896
2897 /// Compare the ABI of two prepared packages that contain linux kernel
2898 /// binaries.
2899 ///
2900 /// A prepared package is a package which content has been extracted
2901 /// and mapped.
2902 ///
2903 /// @param first_package the first package to consider.
2904 ///
2905 /// @param second_package the second package to consider.
2906 ///
2907 /// @param options the options the current program has been called
2908 /// with.
2909 ///
2910 /// @param diff out parameter. If this function returns true, then
2911 /// this parameter is set to the result of the comparison.
2912 ///
2913 /// @param opts the options of the current program.
2914 ///
2915 /// @return the status of the comparison.
2916 static abidiff_status
compare_prepared_linux_kernel_packages(package & first_package,package & second_package,options & opts)2917 compare_prepared_linux_kernel_packages(package& first_package,
2918 package& second_package,
2919 options& opts)
2920 {
2921 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
2922 string pkg_name = first_package.base_name();
2923
2924 // Setting debug-info path of binaries
2925 string debug_dir1, debug_dir2, relative_debug_path = "/usr/lib/debug/";
2926 if (!first_package.debug_info_packages().empty()
2927 && !second_package.debug_info_packages().empty())
2928 {
2929 debug_dir1 =
2930 first_package.debug_info_packages().front()->extracted_dir_path() +
2931 relative_debug_path;
2932 debug_dir2 =
2933 second_package.debug_info_packages().front()->extracted_dir_path() +
2934 relative_debug_path;
2935 }
2936
2937 string vmlinux_path1, vmlinux_path2;
2938
2939 if (!get_vmlinux_path_from_kernel_dist(debug_dir1, vmlinux_path1))
2940 return abigail::tools_utils::ABIDIFF_ERROR;
2941
2942 if (!get_vmlinux_path_from_kernel_dist(debug_dir2, vmlinux_path2))
2943 return abigail::tools_utils::ABIDIFF_ERROR;
2944
2945 string dist_root1 = first_package.extracted_dir_path();
2946 string dist_root2 = second_package.extracted_dir_path();
2947
2948 abigail::ir::environment_sptr env(new abigail::ir::environment);
2949 suppressions_type supprs;
2950 corpus_group_sptr corpus1, corpus2;
2951 corpus1 = build_corpus_group_from_kernel_dist_under(dist_root1,
2952 debug_dir1,
2953 vmlinux_path1,
2954 opts.suppression_paths,
2955 opts.kabi_whitelist_paths,
2956 supprs,
2957 opts.verbose,
2958 env);
2959
2960 if (!corpus1)
2961 return abigail::tools_utils::ABIDIFF_ERROR;
2962
2963 corpus2 = build_corpus_group_from_kernel_dist_under(dist_root2,
2964 debug_dir2,
2965 vmlinux_path2,
2966 opts.suppression_paths,
2967 opts.kabi_whitelist_paths,
2968 supprs,
2969 opts.verbose,
2970 env);
2971
2972 if (!corpus2)
2973 return abigail::tools_utils::ABIDIFF_ERROR;
2974
2975 diff_context_sptr diff_ctxt(new diff_context);
2976 set_diff_context_from_opts(diff_ctxt, opts);
2977
2978 corpus_diff_sptr diff = compute_diff(corpus1, corpus2, diff_ctxt);
2979
2980 if (diff->has_net_changes())
2981 status |= abigail::tools_utils::ABIDIFF_ABI_CHANGE;
2982 if (diff->has_incompatible_changes())
2983 status |= abigail::tools_utils::ABIDIFF_ABI_INCOMPATIBLE_CHANGE;
2984
2985 if (status & abigail::tools_utils::ABIDIFF_ABI_CHANGE)
2986 {
2987 cout << "== Kernel ABI changes between packages '"
2988 << first_package.path() << "' and '"
2989 << second_package.path() << "' are: ===\n";
2990 diff->report(cout);
2991 cout << "== End of kernel ABI changes between packages '"
2992 << first_package.path()
2993 << "' and '"
2994 << second_package.path() << "' ===\n\n";
2995 }
2996
2997 return status;
2998 }
2999
3000 /// Compare the ABI of two prepared packages.
3001 ///
3002 /// A prepared package is a package which content has been extracted
3003 /// and mapped.
3004 ///
3005 /// @param first_package the first package to consider.
3006 ///
3007 /// @param second_package the second package to consider.
3008 ///
3009 /// @param options the options the current program has been called
3010 /// with.
3011 ///
3012 /// @param diff out parameter. If this function returns true, then
3013 /// this parameter is set to the result of the comparison.
3014 ///
3015 /// @param opts the options of the current program.
3016 ///
3017 /// @return the status of the comparison.
3018 static abidiff_status
compare_prepared_package(package & first_package,package & second_package,abi_diff & diff,options & opts)3019 compare_prepared_package(package& first_package, package& second_package,
3020 abi_diff& diff, options& opts)
3021 {
3022 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
3023
3024 if (abigail::tools_utils::file_is_kernel_package(first_package.base_name(),
3025 first_package.type()))
3026 {
3027 opts.show_symbols_not_referenced_by_debug_info = false;
3028 status = compare_prepared_linux_kernel_packages(first_package,
3029 second_package,
3030 opts);
3031 }
3032 else
3033 status = compare_prepared_userspace_packages(first_package,
3034 second_package,
3035 diff, opts);
3036
3037 return status;
3038 }
3039
3040 /// Compare binaries in a package against their ABIXML
3041 /// representations.
3042 ///
3043 /// @param pkg the package to consider.
3044 ///
3045 /// @param diff the textual representation of the resulting
3046 /// comparison.
3047 ///
3048 /// @param opts the options provided by the user
3049 ///
3050 /// @return the status of the comparison.
3051 static abidiff_status
self_compare_prepared_package(package & pkg,abi_diff & diff,options & opts)3052 self_compare_prepared_package(package& pkg,
3053 abi_diff& diff,
3054 options& opts)
3055 {
3056 abidiff_status status = abigail::tools_utils::ABIDIFF_OK;
3057
3058 status = self_compare_prepared_userspace_package(pkg, diff, opts);
3059
3060 return status;
3061 }
3062
3063 /// Compare the ABI of two packages
3064 ///
3065 /// @param first_package the first package to consider.
3066 ///
3067 /// @param second_package the second package to consider.
3068 ///
3069 /// @param options the options the current program has been called
3070 /// with.
3071 ///
3072 /// @param diff out parameter. If this function returns true, then
3073 /// this parameter is set to the result of the comparison.
3074 ///
3075 /// @param opts the options of the current program.
3076 ///
3077 /// @return the status of the comparison.
3078 static abidiff_status
compare(package_sptr & first_package,package_sptr & second_package,abi_diff & diff,options & opts)3079 compare(package_sptr& first_package, package_sptr& second_package,
3080 abi_diff& diff, options& opts)
3081 {
3082 // Prepare (extract and analyze the contents) the packages and their
3083 // ancillary packages.
3084 //
3085 // Note that the package preparations happens in parallel.
3086 if (!prepare_packages(first_package, second_package, opts))
3087 {
3088 maybe_erase_temp_dirs(*first_package, *second_package, opts);
3089 return abigail::tools_utils::ABIDIFF_ERROR;
3090 }
3091
3092 return compare_prepared_package(*first_package, *second_package, diff, opts);
3093 }
3094
3095 /// Compare binaries in a package against their ABIXML
3096 /// representations.
3097 ///
3098 /// @param pkg the package to consider.
3099 ///
3100 /// @param opts the options provided by the user
3101 ///
3102 /// @return the status of the comparison.
3103 static abidiff_status
compare_to_self(package_sptr & pkg,options & opts)3104 compare_to_self(package_sptr& pkg, options& opts)
3105 {
3106 if (!prepare_package(pkg, opts))
3107 return abigail::tools_utils::ABIDIFF_ERROR;
3108
3109 abi_diff diff;
3110 return self_compare_prepared_package(*pkg, diff, opts);
3111 }
3112
3113 /// Compare the ABI of two packages.
3114 ///
3115 /// @param first_package the first package to consider.
3116 ///
3117 /// @param second_package the second package to consider.
3118 ///
3119 /// @param opts the options the current program has been called with.
3120 ///
3121 /// @return the status of the comparison.
3122 static abidiff_status
compare(package_sptr & first_package,package_sptr & second_package,options & opts)3123 compare(package_sptr& first_package,
3124 package_sptr& second_package,
3125 options& opts)
3126 {
3127 abi_diff diff;
3128 return compare(first_package, second_package, diff, opts);
3129 }
3130
3131 /// Parse the command line of the current program.
3132 ///
3133 /// @param argc the number of arguments in the @p argv parameter.
3134 ///
3135 /// @param argv the array of arguemnts passed to the function. The
3136 /// first argument is the name of this program.
3137 ///
3138 /// @param opts the resulting options.
3139 ///
3140 /// @return true upon successful parsing.
3141 static bool
parse_command_line(int argc,char * argv[],options & opts)3142 parse_command_line(int argc, char* argv[], options& opts)
3143 {
3144 if (argc < 2)
3145 return false;
3146
3147 for (int i = 1; i < argc; ++i)
3148 {
3149 if (argv[i][0] != '-')
3150 {
3151 if (opts.package1.empty())
3152 {
3153 opts.package1 = make_path_absolute(argv[i]).get();
3154 opts.nonexistent_file = !file_exists(opts.package1);
3155 }
3156 else if (opts.package2.empty())
3157 {
3158 opts.package2 = make_path_absolute(argv[i]).get();
3159 opts.nonexistent_file = !file_exists(opts.package2);
3160 }
3161 else
3162 {
3163 opts.wrong_arg = argv[i];
3164 return false;
3165 }
3166
3167 if (opts.nonexistent_file)
3168 {
3169 opts.wrong_option = argv[i];
3170 return true;
3171 }
3172 }
3173 else if (!strcmp(argv[i], "--debug-info-pkg1")
3174 || !strcmp(argv[i], "--d1"))
3175 {
3176 int j = i + 1;
3177 if (j >= argc)
3178 {
3179 opts.missing_operand = true;
3180 opts.wrong_option = argv[i];
3181 return true;
3182 }
3183 opts.debug_packages1.push_back
3184 (abigail::tools_utils::make_path_absolute(argv[j]).get());
3185 ++i;
3186 }
3187 else if (!strcmp(argv[i], "--debug-info-pkg2")
3188 || !strcmp(argv[i], "--d2"))
3189 {
3190 int j = i + 1;
3191 if (j >= argc)
3192 {
3193 opts.missing_operand = true;
3194 opts.wrong_option = argv[i];
3195 return true;
3196 }
3197 opts.debug_packages2.push_back
3198 (abigail::tools_utils::make_path_absolute(argv[j]).get());
3199 ++i;
3200 }
3201 else if (!strcmp(argv[i], "--devel-pkg1")
3202 || !strcmp(argv[i], "--devel1"))
3203 {
3204 int j = i + 1;
3205 if (j >= argc)
3206 {
3207 opts.missing_operand = true;
3208 opts.wrong_option = argv[i];
3209 return true;
3210 }
3211 opts.devel_package1 =
3212 abigail::tools_utils::make_path_absolute(argv[j]).get();
3213 ++i;
3214 }
3215 else if (!strcmp(argv[i], "--devel-pkg2")
3216 || !strcmp(argv[i], "--devel2"))
3217 {
3218 int j = i + 1;
3219 if (j >= argc)
3220 {
3221 opts.missing_operand = true;
3222 opts.wrong_option = argv[i];
3223 return true;
3224 }
3225 opts.devel_package2 =
3226 abigail::tools_utils::make_path_absolute(argv[j]).get();
3227 ++i;
3228 }
3229 else if (!strcmp(argv[i], "--drop-private-types"))
3230 opts.drop_private_types = true;
3231 else if (!strcmp(argv[i], "--no-default-suppression"))
3232 opts.no_default_suppression = true;
3233 else if (!strcmp(argv[i], "--keep-tmp-files"))
3234 opts.keep_tmp_files = true;
3235 else if (!strcmp(argv[i], "--dso-only"))
3236 opts.compare_dso_only = true;
3237 else if (!strcmp(argv[i], "--private-dso"))
3238 opts.compare_private_dsos = true;
3239 else if (!strcmp(argv[i], "--leaf-changes-only")
3240 ||!strcmp(argv[i], "-l"))
3241 opts.leaf_changes_only = true;
3242 else if (!strcmp(argv[i], "--impacted-interfaces")
3243 ||!strcmp(argv[i], "-i"))
3244 opts.show_impacted_interfaces = true;
3245 else if (!strcmp(argv[i], "--non-reachable-types")
3246 ||!strcmp(argv[i], "-t"))
3247 opts.show_all_types = true;
3248 else if (!strcmp(argv[i], "--full-impact")
3249 ||!strcmp(argv[i], "-f"))
3250 opts.show_full_impact_report = true;
3251 else if (!strcmp(argv[i], "--no-linkage-name"))
3252 opts.show_linkage_names = false;
3253 else if (!strcmp(argv[i], "--redundant"))
3254 opts.show_redundant_changes = true;
3255 else if (!strcmp(argv[i], "--harmless"))
3256 opts.show_harmless_changes = true;
3257 else if (!strcmp(argv[i], "--no-show-locs"))
3258 opts.show_locs = false;
3259 else if (!strcmp(argv[i], "--show-bytes"))
3260 opts.show_offsets_sizes_in_bits = false;
3261 else if (!strcmp(argv[i], "--show-bits"))
3262 opts.show_offsets_sizes_in_bits = true;
3263 else if (!strcmp(argv[i], "--show-hex"))
3264 opts.show_hexadecimal_values = true;
3265 else if (!strcmp(argv[i], "--show-dec"))
3266 opts.show_hexadecimal_values = false;
3267 else if (!strcmp(argv[i], "--no-show-relative-offset-changes"))
3268 opts.show_relative_offset_changes = false;
3269 else if (!strcmp(argv[i], "--no-added-syms"))
3270 opts.show_added_syms = false;
3271 else if (!strcmp(argv[i], "--no-unreferenced-symbols"))
3272 opts.show_symbols_not_referenced_by_debug_info = false;
3273 else if (!strcmp(argv[i], "--no-added-binaries"))
3274 opts.show_added_binaries = false;
3275 else if (!strcmp(argv[i], "--fail-no-dbg"))
3276 opts.fail_if_no_debug_info = true;
3277 else if (!strcmp(argv[i], "--verbose"))
3278 opts.verbose = true;
3279 else if (!strcmp(argv[i], "--no-abignore"))
3280 opts.abignore = false;
3281 else if (!strcmp(argv[i], "--no-parallel"))
3282 opts.parallel = false;
3283 else if (!strcmp(argv[i], "--show-identical-binaries"))
3284 opts.show_identical_binaries = true;
3285 else if (!strcmp(argv[i], "--self-check"))
3286 opts.self_check = true;
3287 else if (!strcmp(argv[i], "--suppressions")
3288 || !strcmp(argv[i], "--suppr"))
3289 {
3290 int j = i + 1;
3291 if (j >= argc)
3292 return false;
3293 opts.suppression_paths.push_back(argv[j]);
3294 ++i;
3295 }
3296 else if (!strcmp(argv[i], "--linux-kernel-abi-whitelist")
3297 || !strcmp(argv[i], "-w"))
3298 {
3299 int j = i + 1;
3300 if (j >= argc)
3301 {
3302 opts.missing_operand = true;
3303 opts.wrong_option = argv[i];
3304 return true;
3305 }
3306 if (guess_file_type(argv[j]) == abigail::tools_utils::FILE_TYPE_RPM)
3307 // The kernel abi whitelist is actually a whitelist
3308 // *package*. Take that into account.
3309 opts.kabi_whitelist_packages.push_back
3310 (make_path_absolute(argv[j]).get());
3311 else
3312 // We assume the kernel abi whitelist is a white list
3313 // file.
3314 opts.kabi_whitelist_paths.push_back(argv[j]);
3315 ++i;
3316 }
3317 else if (!strcmp(argv[i], "--wp"))
3318 {
3319 int j = i + 1;
3320 if (j >= argc)
3321 {
3322 opts.missing_operand = true;
3323 opts.wrong_option = argv[i];
3324 return true;
3325 }
3326 opts.kabi_whitelist_packages.push_back
3327 (make_path_absolute(argv[j]).get());
3328 ++i;
3329 }
3330 else if (!strcmp(argv[i], "--help")
3331 || !strcmp(argv[i], "-h"))
3332 {
3333 opts.display_usage = true;
3334 return true;
3335 }
3336 else if (!strcmp(argv[i], "--version")
3337 || !strcmp(argv[i], "-v"))
3338 {
3339 opts.display_version = true;
3340 return true;
3341 }
3342 else
3343 {
3344 if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
3345 opts.wrong_option = argv[i];
3346 return false;
3347 }
3348 }
3349
3350 return true;
3351 }
3352
3353 int
main(int argc,char * argv[])3354 main(int argc, char* argv[])
3355 {
3356 options opts(argv[0]);
3357
3358 if (!parse_command_line(argc, argv, opts))
3359 {
3360 if (!opts.wrong_option.empty())
3361 emit_prefix("abipkgdiff", cerr)
3362 << "unrecognized option: " << opts.wrong_option
3363 << "\ntry the --help option for more information\n";
3364 else
3365 emit_prefix("abipkgdiff", cerr)
3366 << "unrecognized argument: " << opts.wrong_arg
3367 << "\ntry the --help option for more information\n";
3368 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3369 | abigail::tools_utils::ABIDIFF_ERROR);
3370 }
3371
3372 if (opts.missing_operand)
3373 {
3374 emit_prefix("abipkgdiff", cerr)
3375 << "missing operand\n"
3376 "try the --help option for more information\n";
3377 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3378 | abigail::tools_utils::ABIDIFF_ERROR);
3379 }
3380
3381 if (opts.nonexistent_file)
3382 {
3383 string input_file;
3384 base_name(opts.wrong_option, input_file);
3385 emit_prefix("abipkgdiff", cerr)
3386 << "The input file " << input_file << " doesn't exist\n"
3387 "try the --help option for more information\n";
3388 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3389 | abigail::tools_utils::ABIDIFF_ERROR);
3390 }
3391
3392 if (opts.kabi_whitelist_packages.size() > 2)
3393 {
3394 emit_prefix("abipkgdiff", cerr)
3395 << "no more than 2 Linux kernel white list packages can be provided\n";
3396 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3397 | abigail::tools_utils::ABIDIFF_ERROR);
3398 }
3399
3400 if (opts.display_usage)
3401 {
3402 display_usage(argv[0], cout);
3403 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3404 | abigail::tools_utils::ABIDIFF_ERROR);
3405 }
3406
3407 if (opts.display_version)
3408 {
3409 emit_prefix(argv[0], cout)
3410 << abigail::tools_utils::get_library_version_string()
3411 << "\n";
3412 return 0;
3413 }
3414
3415 if (!opts.no_default_suppression && opts.suppression_paths.empty())
3416 {
3417 // Load the default system and user suppressions.
3418 string default_system_suppr_file =
3419 get_default_system_suppression_file_path();
3420 if (file_exists(default_system_suppr_file))
3421 opts.suppression_paths.push_back(default_system_suppr_file);
3422
3423 string default_user_suppr_file =
3424 get_default_user_suppression_file_path();
3425 if (file_exists(default_user_suppr_file))
3426 opts.suppression_paths.push_back(default_user_suppr_file);
3427 }
3428
3429 if (!maybe_check_suppression_files(opts))
3430 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3431 | abigail::tools_utils::ABIDIFF_ERROR);
3432
3433 bool need_just_one_input_package = opts.self_check;
3434
3435 if (need_just_one_input_package)
3436 {
3437 bool bail_out = false;
3438 if (!opts.package2.empty())
3439 {
3440 // We don't need the second package, we'll ignore it later
3441 // down below.
3442 ;
3443 }
3444 if (opts.package1.empty())
3445 {
3446 // We need at least one package to work with!
3447 emit_prefix("abipkgdiff", cerr)
3448 << "missing input package\n";
3449 if (bail_out)
3450 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3451 | abigail::tools_utils::ABIDIFF_ERROR);
3452 }
3453 }
3454 else if(opts.package1.empty() || opts.package2.empty())
3455 {
3456 emit_prefix("abipkgdiff", cerr)
3457 << "please enter two packages to compare" << "\n";
3458 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3459 | abigail::tools_utils::ABIDIFF_ERROR);
3460 }
3461
3462 package_sptr first_package(new package(opts.package1, "package1"));
3463
3464 package_sptr second_package(new package(opts.package2, "package2"));
3465 opts.pkg1 = first_package;
3466 opts.pkg2 = second_package;
3467
3468 for (vector<string>::const_iterator p = opts.debug_packages1.begin();
3469 p != opts.debug_packages1.end();
3470 ++p)
3471 first_package->debug_info_packages().push_back
3472 (package_sptr(new package(*p,
3473 "debug_package1",
3474 /*pkg_kind=*/package::KIND_DEBUG_INFO)));
3475
3476 for (vector<string>::const_iterator p = opts.debug_packages2.begin();
3477 p != opts.debug_packages2.end();
3478 ++p)
3479 second_package->debug_info_packages().push_back
3480 (package_sptr(new package(*p,
3481 "debug_package2",
3482 /*pkg_kind=*/package::KIND_DEBUG_INFO)));
3483
3484 if (!opts.devel_package1.empty())
3485 first_package->devel_package
3486 (package_sptr(new package(opts.devel_package1,
3487 "devel_package1",
3488 /*pkg_kind=*/package::KIND_DEVEL)));
3489 ;
3490
3491 if (!opts.devel_package2.empty())
3492 second_package->devel_package
3493 (package_sptr(new package(opts.devel_package2,
3494 "devel_package2",
3495 /*pkg_kind=*/package::KIND_DEVEL)));
3496
3497 if (!opts.kabi_whitelist_packages.empty())
3498 {
3499 first_package->kabi_whitelist_package
3500 (package_sptr(new package
3501 (opts.kabi_whitelist_packages[0],
3502 "kabi_whitelist_package1",
3503 /*pkg_kind=*/package::KIND_KABI_WHITELISTS)));
3504 if (opts.kabi_whitelist_packages.size() >= 2)
3505 second_package->kabi_whitelist_package
3506 (package_sptr(new package
3507 (opts.kabi_whitelist_packages[1],
3508 "kabi_whitelist_package2",
3509 /*pkg_kind=*/package::KIND_KABI_WHITELISTS)));
3510 }
3511
3512 string package_name;
3513 switch (first_package->type())
3514 {
3515 case abigail::tools_utils::FILE_TYPE_RPM:
3516 if (!second_package->path().empty()
3517 && second_package->type() != abigail::tools_utils::FILE_TYPE_RPM)
3518 {
3519 base_name(opts.package2, package_name);
3520 emit_prefix("abipkgdiff", cerr)
3521 << package_name << " should be an RPM file\n";
3522 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3523 | abigail::tools_utils::ABIDIFF_ERROR);
3524 }
3525
3526 if (file_is_kernel_package(first_package->base_name(),
3527 abigail::tools_utils::FILE_TYPE_RPM)
3528 || file_is_kernel_package(second_package->base_name(),
3529 abigail::tools_utils::FILE_TYPE_RPM))
3530 {
3531 if (file_is_kernel_package(first_package->base_name(),
3532 abigail::tools_utils::FILE_TYPE_RPM)
3533 != file_is_kernel_package(second_package->base_name(),
3534 abigail::tools_utils::FILE_TYPE_RPM))
3535 {
3536 emit_prefix("abipkgdiff", cerr)
3537 << "a Linux kernel package can only be compared to another "
3538 "Linux kernel package\n";
3539 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3540 | abigail::tools_utils::ABIDIFF_ERROR);
3541 }
3542
3543 if (first_package->debug_info_packages().empty()
3544 || (!second_package->path().empty()
3545 && second_package->debug_info_packages().empty()))
3546 {
3547 emit_prefix("abipkgdiff", cerr)
3548 << "a Linux Kernel package must be accompanied with its "
3549 "debug info package\n";
3550 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3551 | abigail::tools_utils::ABIDIFF_ERROR);
3552 }
3553 // We are looking at kernel packages. If the user provided
3554 // the --full-impact option then it means we want to display
3555 // the default libabigail report format where a full impact
3556 // analysis is done for each ABI change.
3557 //
3558 // Otherwise, let's just emit the leaf change report.
3559 if (opts.show_full_impact_report)
3560 opts.leaf_changes_only = false;
3561 else
3562 opts.leaf_changes_only = true;
3563 }
3564
3565 break;
3566
3567 case abigail::tools_utils::FILE_TYPE_DEB:
3568 if (!second_package->path().empty()
3569 && second_package->type() != abigail::tools_utils::FILE_TYPE_DEB)
3570 {
3571 base_name(opts.package2, package_name);
3572 emit_prefix("abipkgdiff", cerr)
3573 << package_name << " should be a DEB file\n";
3574 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3575 | abigail::tools_utils::ABIDIFF_ERROR);
3576 }
3577 break;
3578
3579 case abigail::tools_utils::FILE_TYPE_DIR:
3580 if (!second_package->path().empty()
3581 && second_package->type() != abigail::tools_utils::FILE_TYPE_DIR)
3582 {
3583 base_name(opts.package2, package_name);
3584 emit_prefix("abipkgdiff", cerr)
3585 << package_name << " should be a directory\n";
3586 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3587 | abigail::tools_utils::ABIDIFF_ERROR);
3588 }
3589 break;
3590
3591 case abigail::tools_utils::FILE_TYPE_TAR:
3592 if (!second_package->path().empty()
3593 && second_package->type() != abigail::tools_utils::FILE_TYPE_TAR)
3594 {
3595 base_name(opts.package2, package_name);
3596 emit_prefix("abipkgdiff", cerr)
3597 << package_name << " should be a GNU tar archive\n";
3598 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3599 | abigail::tools_utils::ABIDIFF_ERROR);
3600 }
3601 break;
3602
3603 default:
3604 base_name(opts.package1, package_name);
3605 emit_prefix("abipkgdiff", cerr)
3606 << package_name << " should be a valid package file \n";
3607 return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
3608 | abigail::tools_utils::ABIDIFF_ERROR);
3609 }
3610
3611 if (opts.self_check)
3612 return compare_to_self(first_package, opts);
3613
3614 return compare(first_package, second_package, opts);
3615 }
3616