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