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