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