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