• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2023 Red Hat, Inc.
5 
6 ///@file
7 
8 // In case we have a bad fts we include this before config.h because
9 // it can't handle _FILE_OFFSET_BITS.  Everything we need here is fine
10 // if its declarations just come first.  Also, include sys/types.h
11 // before fts. On some systems fts.h is not self contained.
12 #ifdef BAD_FTS
13   #include <sys/types.h>
14   #include <fts.h>
15 #endif
16 
17 // For package configuration macros.
18 #include "config.h"
19 
20 // In case we don't have a bad fts then we need to include fts.h after
21 // config.h.
22 #ifndef BAD_FTS
23   #include <sys/types.h>
24   #include <fts.h>
25 #endif
26 
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/time.h>
31 #include <dirent.h>
32 #include <time.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <libgen.h>
36 #include <libxml/parser.h>
37 #include <libxml/xmlversion.h>
38 #include <algorithm>
39 #include <cstdlib>
40 #include <cstring>
41 #include <fstream>
42 #include <iostream>
43 #include <iterator>
44 #include <memory>
45 #include <sstream>
46 
47 #include "abg-dwarf-reader.h"
48 #ifdef WITH_CTF
49 #include "abg-ctf-reader.h"
50 #endif
51 #ifdef WITH_BTF
52 #include "abg-btf-reader.h"
53 #endif
54 #include "abg-internal.h"
55 #include "abg-regex.h"
56 
57 // <headers defining libabigail's API go under here>
58 ABG_BEGIN_EXPORT_DECLARATIONS
59 
60 #include <abg-ir.h>
61 #include "abg-config.h"
62 #include "abg-tools-utils.h"
63 
64 ABG_END_EXPORT_DECLARATIONS
65 // </headers defining libabigail's API>
66 
67 using std::string;
68 
69 namespace abigail
70 {
71 
72 using namespace abigail::suppr;
73 using namespace abigail::ini;
74 
75 /// @brief Namespace for a set of utility function used by tools based
76 /// on libabigail.
77 namespace tools_utils
78 {
79 
80 /// This function needs to be called before any libabigail function.
81 ///
82 /// Users of libabigail must call it prior to using any of the
83 /// functions of the library.
84 ///
85 /// It intends to initialize the underlying libraries that might need
86 /// initialization, especially, libxml2, in multi-threaded environments.
87 void
initialize()88 initialize()
89 {
90   LIBXML_TEST_VERSION;
91   xmlInitParser();
92 }
93 
94 /// Get the value of $libdir variable of the autotools build
95 /// system.  This is where shared libraries are usually installed.
96 ///
97 /// @return a constant string (doesn't have to be free-ed by the
98 /// caller) that represent the value of the $libdir variable in the
99 /// autotools build system, or NULL if it's not set.
100 const char*
get_system_libdir()101 get_system_libdir()
102 {
103 #ifndef ABIGAIL_ROOT_SYSTEM_LIBDIR
104 #error the macro ABIGAIL_ROOT_SYSTEM_LIBDIR must be set at compile time
105 #endif
106 
107   static __thread const char* system_libdir(ABIGAIL_ROOT_SYSTEM_LIBDIR);
108   return system_libdir;
109 }
110 
111 /// The bitwise 'OR' operator for abidiff_status bit masks.
112 ///
113 /// @param l the left hand side operand of the OR operator.
114 ///
115 /// @param r the right hand side operand of the OR operator.
116 ///
117 /// @return the result of the OR expression.
118 abidiff_status
operator |(abidiff_status l,abidiff_status r)119 operator|(abidiff_status l, abidiff_status r)
120 {return static_cast<abidiff_status>(static_cast<unsigned>(l)
121 				     | static_cast<unsigned>(r));}
122 
123 /// The bitwise 'AND' operator for abidiff_status bit masks.
124 ///
125 /// @param l the left hand side operand of the AND operator.
126 ///
127 /// @param r the right hand side operand of the AND operator.
128 ///
129 /// @return the result of the AND expression.
130 abidiff_status
operator &(abidiff_status l,abidiff_status r)131 operator&(abidiff_status l, abidiff_status r)
132 {return static_cast<abidiff_status>(static_cast<unsigned>(l)
133 				     & static_cast<unsigned>(r));}
134 
135 /// The |= operator.
136 ///
137 /// @param l the left hand side operand of the operator.
138 ///
139 /// @param r the right hand side operand of the operator.
140 ///
141 /// @param the resulting bit mask.
142 abidiff_status&
operator |=(abidiff_status & l,abidiff_status r)143 operator|=(abidiff_status&l, abidiff_status r)
144 {
145   l = static_cast<abidiff_status>(static_cast<unsigned>(l)
146 				  | static_cast<unsigned>(r));
147   return l;
148 }
149 
150 /// Test if an instance of @param abidiff_status bits mask represents
151 /// an error.
152 ///
153 /// This functions tests if the @ref ABIDIFF_ERROR bit is set in the
154 /// given bits mask.
155 ///
156 /// @param s the bit mask to consider.
157 ///
158 /// @return true iff @p s has its ABIDIFF_ERROR bit set.
159 bool
abidiff_status_has_error(abidiff_status s)160 abidiff_status_has_error(abidiff_status s)
161 {return s & (ABIDIFF_ERROR | ABIDIFF_USAGE_ERROR);}
162 
163 /// Test if an instance of @param abidiff_status bits mask represents
164 /// an abi change.
165 ///
166 /// This functions tests if the @ref ABIDIFF_ABI_CHANGE bit is set in the
167 /// given bits mask.
168 ///
169 /// @param s the bit mask to consider.
170 ///
171 /// @return true iff @p s has its @ref ABIDIFF_ABI_CHANGE bit set.
172 bool
abidiff_status_has_abi_change(abidiff_status s)173 abidiff_status_has_abi_change(abidiff_status s)
174 {return s & ABIDIFF_ABI_CHANGE;}
175 
176 /// Test if an instance of @param abidiff_status bits mask represents
177 /// an incompatible abi change.
178 ///
179 /// This functions tests if the @ref ABIDIFF_INCOMPATIBLE_ABI_CHANGE
180 /// bit is set in the given bits mask.  Note that the this bit is set
181 /// then the bit @ref ABIDIFF_ABI_CHANGE must be set as well.
182 ///
183 /// @param s the bit mask to consider.
184 ///
185 /// @return true iff @p s has its @ref ABIDIFF_INCOMPATIBLE ABI_CHANGE
186 /// set.
187 bool
abidiff_status_has_incompatible_abi_change(abidiff_status s)188 abidiff_status_has_incompatible_abi_change(abidiff_status s)
189 {return s & ABIDIFF_ABI_INCOMPATIBLE_CHANGE;}
190 
191 #define DECLARE_STAT(st) \
192   struct stat st; \
193   memset(&st, 0, sizeof(st))
194 
195 // <class timer stuff>
196 
197 /// The private data type of the @ref timer class.
198 struct timer::priv
199 {
200   timer::kind timer_kind;
201   struct timeval begin_timeval;
202   struct timeval end_timeval;
203 
privabigail::tools_utils::timer::priv204   priv(timer::kind k)
205     : timer_kind(k),
206       begin_timeval(),
207       end_timeval()
208   {}
209 }; // end struct timer::priv
210 
211 /// Constructor of the @ref timer type.
212 ///
213 /// @param k the kind of timer to instantiate.
timer(timer::kind k)214 timer::timer(timer::kind k)
215   : priv_(new timer::priv(k))
216 {
217   if (priv_->timer_kind == START_ON_INSTANTIATION_TIMER_KIND)
218     start();
219 }
220 
221 /// Start the timer.
222 ///
223 /// To stop the timer (and record the time elapsed since the timer was
224 /// started), call the timer::stop member function.
225 ///
226 /// @return true upon successful completion.
227 bool
start()228 timer::start()
229 {
230   if (gettimeofday(&priv_->begin_timeval, 0))
231     return false;
232   return true;
233 }
234 
235 /// Stop the timer.
236 ///
237 /// This records the time elapsed since the timer was started using
238 /// the timer::start member function.
239 ///
240 /// @return true upon successful completion.
241 bool
stop()242 timer::stop()
243 {
244   if (gettimeofday(&priv_->end_timeval, 0))
245     return false;
246   return true;
247 }
248 
249 /// Get the elapsed time in seconds.
250 ///
251 /// @return the time elapsed between the invocation of the methods
252 /// timer::start() and timer::stop, in seconds.
253 time_t
value_in_seconds() const254 timer::value_in_seconds() const
255 {return priv_->end_timeval.tv_sec - priv_->begin_timeval.tv_sec;}
256 
257 /// Get the elapsed time in hour:minutes:seconds:milliseconds.
258 ///
259 /// @param hours out parameter. This is set to the number of hours elapsed.
260 ///
261 /// @param minutes out parameter.  This is set to the number of minutes
262 /// (passed the number of hours) elapsed.
263 ///
264 /// @param seconds out parameter.  This is se to the number of
265 /// seconds (passed the number of hours and minutes) elapsed.
266 ///
267 /// @param milliseconds.  This is set ot the number of milliseconds
268 /// (passed the number of hours, minutes and seconds) elapsed.
269 ///
270 /// @return true upon successful completion.
271 bool
value(time_t & hours,time_t & minutes,time_t & seconds,time_t & milliseconds) const272 timer::value(time_t&		hours,
273 	     time_t&		minutes,
274 	     time_t&		seconds,
275 	     time_t&	milliseconds) const
276 {
277   time_t elapsed_seconds =
278     priv_->end_timeval.tv_sec - priv_->begin_timeval.tv_sec;
279   suseconds_t elapsed_usecs =
280     ((priv_->end_timeval.tv_sec * 1000000) + priv_->end_timeval.tv_usec)
281     - ((priv_->begin_timeval.tv_sec * 1000000) + priv_->begin_timeval.tv_usec);
282 
283   milliseconds = 0;
284 
285   hours = elapsed_seconds / 3600;
286   minutes = (elapsed_seconds % 3600) / 60;
287   seconds = (elapsed_seconds % 3600) % 60;
288   if (elapsed_seconds == 0)
289     milliseconds = elapsed_usecs / 1000;
290 
291   return true;
292 }
293 
294 /// Get the elapsed time as a human-readable string.
295 ///
296 /// @return the elapsed time as a human-readable string.
297 string
value_as_string() const298 timer::value_as_string() const
299 {
300   time_t hours = 0, minutes = 0, seconds = 0;
301   time_t msecs = 0;
302 
303   value(hours, minutes, seconds, msecs);
304 
305   std::ostringstream o;
306 
307   if (hours)
308     o << hours << "h";
309 
310   if (minutes)
311     o << minutes << "m";
312 
313   o << seconds << "s";
314 
315   if (msecs)
316     o <<msecs <<"ms";
317 
318   return o.str();
319 }
320 
321 /// Destructor of the @ref timer type.
~timer()322 timer::~timer()
323 {
324 }
325 
326 /// Streaming operator for the @ref timer type.
327 ///
328 /// Emit a string representing the elapsed time (in a human-readable
329 /// manner) to an output stream.
330 ///
331 /// @param o the output stream to emit the elapsed time string to.
332 ///
333 /// @param t the timer to consider.
334 ///
335 /// @return the output stream considered.
336 ostream&
operator <<(ostream & o,const timer & t)337 operator<<(ostream& o, const timer& t)
338 {
339   o << t.value_as_string();
340   return o;
341 }
342 
343 /// Get the stat struct (as returned by the lstat() function of the C
344 /// library) of a file.  Note that the function uses lstat, so that
345 /// callers can detect symbolic links.
346 ///
347 /// @param path the path to the function to stat.
348 ///
349 /// @param s the resulting stat struct.
350 ///
351 /// @return true iff the stat function completed successfully.
352 static bool
get_stat(const string & path,struct stat * s)353 get_stat(const string& path,
354 	 struct stat* s)
355 {return (lstat(path.c_str(), s) == 0);}
356 
357 /// Tests whether a path exists;
358 ///
359 /// @param path the path to test for.
360 ///
361 /// @return true iff the path at @p path exist.
362 bool
file_exists(const string & path)363 file_exists(const string& path)
364 {
365   DECLARE_STAT(st);
366 
367   return get_stat(path, &st);
368 }
369 
370 /// Test that a given directory exists.
371 ///
372 /// @param path the path of the directory to consider.
373 ///
374 /// @return true iff a directory exists with the name @p path
375 bool
dir_exists(const string & path)376 dir_exists(const string &path)
377 {return file_exists(path) && is_dir(path);}
378 
379 /// Test if a given directory exists and is empty.
380 ///
381 /// @param path the path of the directory to consider
382 bool
dir_is_empty(const string & path)383 dir_is_empty(const string &path)
384 {
385   if (!dir_exists(path))
386     return false;
387 
388   DIR* dir = opendir(path.c_str());
389   if (!dir)
390     return false;
391 
392   errno = 0;
393   dirent *result = readdir(dir);
394   if (result == NULL && errno != 0)
395     return false;
396 
397   closedir(dir);
398 
399   return result == NULL;
400 }
401 
402 /// Test if path is a path to a regular file or a symbolic link to a
403 /// regular file.
404 ///
405 /// @param path the path to consider.
406 ///
407 /// @return true iff path is a regular path.
408 bool
is_regular_file(const string & path)409 is_regular_file(const string& path)
410 {
411   DECLARE_STAT(st);
412 
413   if (!get_stat(path, &st))
414     return false;
415 
416   if (S_ISREG(st.st_mode))
417     return true;
418 
419   string symlink_target_path;
420   if (maybe_get_symlink_target_file_path(path, symlink_target_path))
421     return is_regular_file(symlink_target_path);
422 
423   return false;
424 }
425 
426 /// Test if a directory contains a CTF archive.
427 ///
428 /// @param directory the directory to consider.
429 ///
430 /// @param archive_prefix the prefix of the archive file.
431 ///
432 /// @return true iff @p directory contains a CTF archive file.
433 bool
dir_contains_ctf_archive(const string & directory,const string & archive_prefix)434 dir_contains_ctf_archive(const string& directory,
435 			 const string& archive_prefix)
436 {
437   string ctf_archive = directory + "/" + archive_prefix + ".ctfa";
438   if (file_exists(ctf_archive))
439     return true;
440   return false;
441 }
442 
443 /// Test if an ELF file has DWARF debug info.
444 ///
445 /// This function supports split debug info files as well.
446 ///
447 /// @param elf_file_path the path to the ELF file to consider.
448 ///
449 /// @param debug_info_root a vector of pointer to directory to look
450 /// for debug info, in case the file is associated to split debug
451 /// info.  If there is no split debug info then this vector can be
452 /// empty.  Note that convert_char_stars_to_char_star_stars() can be
453 /// used to ease the construction of this vector.
454 ///
455 /// @return true iff the ELF file at @elf_file_path is an ELF file
456 /// that contains debug info.
457 bool
file_has_dwarf_debug_info(const string & elf_file_path,const vector<char ** > & debug_info_root_paths)458 file_has_dwarf_debug_info(const string& elf_file_path,
459 			  const vector<char**>& debug_info_root_paths)
460 {
461   if (guess_file_type(elf_file_path) != FILE_TYPE_ELF)
462     return false;
463 
464   environment env;
465   elf::reader r(elf_file_path,
466 		debug_info_root_paths,
467 		env);
468 
469   if (r.dwarf_debug_info())
470     return true;
471 
472   return false;
473 }
474 
475 /// Test if an ELF file has CTF debug info.
476 ///
477 /// This function supports split debug info files as well.
478 /// Linux Kernel with CTF debug information generates a CTF archive:
479 /// a special file containing debug information for vmlinux and its
480 /// modules (*.ko) files it is located by default in the Kernel build
481 /// directory as "vmlinux.ctfa".
482 ///
483 /// @param elf_file_path the path to the ELF file to consider.
484 ///
485 /// @param debug_info_root a vector of pointer to directory to look
486 /// for debug info, in case the file is associated to split debug
487 /// info.  If there is no split debug info then this vector can be
488 /// empty.  Note that convert_char_stars_to_char_star_stars() can be
489 /// used to ease the construction of this vector.
490 ///
491 /// @return true iff the ELF file at @elf_file_path is an ELF file
492 /// that contains debug info.
493 bool
file_has_ctf_debug_info(const string & elf_file_path,const vector<char ** > & debug_info_root_paths)494 file_has_ctf_debug_info(const string& elf_file_path,
495 			const vector<char**>& debug_info_root_paths)
496 {
497   if (guess_file_type(elf_file_path) != FILE_TYPE_ELF)
498     return false;
499 
500   environment env;
501   elf::reader r(elf_file_path,
502 		debug_info_root_paths,
503 		env);
504 
505   if (r.find_ctf_section())
506     return true;
507 
508   string vmlinux;
509   if (base_name(elf_file_path, vmlinux))
510     {
511       string dirname;
512       if (dir_name(elf_file_path, dirname)
513 	    && dir_contains_ctf_archive(dirname, vmlinux))
514 	return true;
515     }
516 
517   // vmlinux.ctfa could be provided with --debug-info-dir
518   for (const auto& path : debug_info_root_paths)
519     if (find_file_under_dir(*path, "vmlinux.ctfa", vmlinux))
520       return true;
521 
522   return false;
523 }
524 
525 /// Test if an ELF file has BTFG debug info.
526 ///
527 /// @param elf_file_path the path to the ELF file to consider.
528 ///
529 /// @param debug_info_root a vector of pointer to directory to look
530 /// for debug info, in case the file is associated to split debug
531 /// info.  If there is no split debug info then this vector can be
532 /// empty.  Note that convert_char_stars_to_char_star_stars() can be
533 /// used to ease the construction of this vector.
534 ///
535 /// @return true iff the ELF file at @elf_file_path is an ELF file
536 /// that contains debug info.
537 bool
file_has_btf_debug_info(const string & elf_file_path,const vector<char ** > & debug_info_root_paths)538 file_has_btf_debug_info(const string& elf_file_path,
539 			const vector<char**>& debug_info_root_paths)
540 {
541     if (guess_file_type(elf_file_path) != FILE_TYPE_ELF)
542     return false;
543 
544   environment env;
545   elf::reader r(elf_file_path, debug_info_root_paths, env);
546 
547   if (r.find_btf_section())
548     return true;
549 
550   return false;
551 }
552 
553 /// Tests if a given path is a directory or a symbolic link to a
554 /// directory.
555 ///
556 /// @param path the path to test for.
557 ///
558 /// @return true iff @p path is a directory.
559 bool
is_dir(const string & path)560 is_dir(const string& path)
561 {
562   DECLARE_STAT(st);
563 
564   if (!get_stat(path, &st))
565     return false;
566 
567   if (S_ISDIR(st.st_mode))
568     return true;
569 
570   if (S_ISLNK(st.st_mode))
571     {
572       string symlink_target_path;
573       if (maybe_get_symlink_target_file_path(path, symlink_target_path))
574 	{
575 	  if (!get_stat(path, &st))
576 	    return false;
577 
578 	  if (S_ISDIR(st.st_mode))
579 	    return true;
580 	}
581     }
582   return false;
583 }
584 
585 static const char* ANONYMOUS_STRUCT_INTERNAL_NAME =   "__anonymous_struct__";
586 static const char* ANONYMOUS_UNION_INTERNAL_NAME =    "__anonymous_union__";
587 static const char* ANONYMOUS_ENUM_INTERNAL_NAME =     "__anonymous_enum__";
588 static const char* ANONYMOUS_SUBRANGE_INTERNAL_NAME = "__anonymous_range__";
589 
590 static int ANONYMOUS_STRUCT_INTERNAL_NAME_LEN =
591   strlen(ANONYMOUS_STRUCT_INTERNAL_NAME);
592 
593 static int ANONYMOUS_UNION_INTERNAL_NAME_LEN =
594   strlen(ANONYMOUS_UNION_INTERNAL_NAME);
595 
596 static int ANONYMOUS_ENUM_INTERNAL_NAME_LEN =
597   strlen(ANONYMOUS_ENUM_INTERNAL_NAME);
598 
599 /// Getter of the prefix for the name of anonymous structs.
600 ///
601 /// @reaturn the prefix for the name of anonymous structs.
602 const char*
get_anonymous_struct_internal_name_prefix()603 get_anonymous_struct_internal_name_prefix()
604 {return ANONYMOUS_STRUCT_INTERNAL_NAME;}
605 
606 /// Getter of the prefix for the name of anonymous unions.
607 ///
608 /// @reaturn the prefix for the name of anonymous unions.
609 const char*
get_anonymous_union_internal_name_prefix()610 get_anonymous_union_internal_name_prefix()
611 {return ANONYMOUS_UNION_INTERNAL_NAME;}
612 
613 static int ANONYMOUS_SUBRANGE_INTERNAL_NAME_LEN =
614   strlen(ANONYMOUS_SUBRANGE_INTERNAL_NAME);
615 
616 /// Getter of the prefix for the name of anonymous enums.
617 ///
618 /// @reaturn the prefix for the name of anonymous enums.
619 const char*
get_anonymous_enum_internal_name_prefix()620 get_anonymous_enum_internal_name_prefix()
621 {return ANONYMOUS_ENUM_INTERNAL_NAME;}
622 
623 /// Getter of the prefix for the name of anonymous range.
624 ///
625 /// @reaturn the prefix for the name of anonymous range.
626 const char*
get_anonymous_subrange_internal_name_prefix()627 get_anonymous_subrange_internal_name_prefix()
628 {return ANONYMOUS_SUBRANGE_INTERNAL_NAME;}
629 
630 /// Compare two fully qualified decl names by taking into account that
631 /// they might have compontents that are anonymous types/namespace names.
632 ///
633 /// For instance:
634 ///
635 /// __anonymous_struct__1::foo and __anonymous_struct__2::foo are
636 /// considered being equivalent qualified names because both are data
637 /// members that belong to anonymous structs.  The anonymous structs
638 /// are numbered so that we can tell them appart (and look them up)
639 /// where there are several of them in the same scope.  But during
640 /// comparison, for various purposes, we want to consider them as
641 /// equivalent.
642 ///
643 /// Similarly, __anonymous_struct__1::foo::__anonymous_struct__2::bar
644 /// and __anonymous_struct__10::foo::__anonymous_struct__11::bar are
645 /// equivalent.
646 ///
647 /// But __anonymous_struct__1::foo::__anonymous_struct__2::bar and
648 /// __anonymous_struct__10::foo::__anonymous_union__11::bar are not
649 /// equivalent because the former designates a member of an anonymous
650 /// struct and the latter designates a member of an anonymous union.
651 ///
652 /// So this function handles those cases.
653 ///
654 /// @param l the name of the first (left hand side) decl to consider.
655 ///
656 /// @param r the name of the second (right hand side) decl to consider.
657 ///
658 /// @return true iff @p l is equivalent to @p r when taking into
659 /// account the anonymous scopes that both might have and if they
660 /// might be anonymous themselves.
661 bool
decl_names_equal(const string & l,const string & r)662 decl_names_equal(const string& l, const string& r)
663 {
664   string::size_type l_pos1 = 0, r_pos1 = 0;
665   const string::size_type l_length = l.length(), r_length = r.length();
666 
667   while (l_pos1 < l_length && r_pos1 < r_length)
668     {
669       string::size_type l_pos2 = l.find("::", l_pos1);
670       string::size_type r_pos2 = r.find("::", r_pos1);
671       if (l_pos2 == string::npos)
672 	l_pos2 = l_length;
673       if (r_pos2 == string::npos)
674 	r_pos2 = r_length;
675 
676       if (l.compare(l_pos1, l_pos2 - l_pos1, r,
677 		    r_pos1, r_pos2 - r_pos1)
678 	  && (l.compare(l_pos1,
679 			ANONYMOUS_STRUCT_INTERNAL_NAME_LEN,
680 			ANONYMOUS_STRUCT_INTERNAL_NAME)
681 	      || r.compare(r_pos1,
682 			   ANONYMOUS_STRUCT_INTERNAL_NAME_LEN,
683 			   ANONYMOUS_STRUCT_INTERNAL_NAME))
684 	  && (l.compare(l_pos1,
685 			ANONYMOUS_UNION_INTERNAL_NAME_LEN,
686 			ANONYMOUS_UNION_INTERNAL_NAME)
687 	      || r.compare(r_pos1,
688 			   ANONYMOUS_UNION_INTERNAL_NAME_LEN,
689 			   ANONYMOUS_UNION_INTERNAL_NAME))
690 	  && (l.compare(l_pos1,
691 			ANONYMOUS_ENUM_INTERNAL_NAME_LEN,
692 			ANONYMOUS_ENUM_INTERNAL_NAME)
693 	      || r.compare(r_pos1,
694 			   ANONYMOUS_ENUM_INTERNAL_NAME_LEN,
695 			   ANONYMOUS_ENUM_INTERNAL_NAME)))
696 	return false;
697 
698       l_pos1 = l_pos2 == l_length ? l_pos2 : l_pos2 + 2;
699       r_pos1 = r_pos2 == r_length ? r_pos2 : r_pos2 + 2;
700     }
701 
702   return (l_pos1 == l_length) == (r_pos1 == r_length);
703 }
704 
705 /// If a given file is a symbolic link, get the canonicalized absolute
706 /// path to the target file.
707 ///
708 /// @param file_path the path to the file to consider.
709 ///
710 /// @param target_path this parameter is set by the function to the
711 /// canonicalized path to the target file, if @p file_path is a
712 /// symbolic link.  In that case, the function returns true.
713 ///
714 /// @return true iff @p file_path is a symbolic link.  In that case,
715 /// the function sets @p target_path to the canonicalized absolute
716 /// path of the target file.
717 bool
maybe_get_symlink_target_file_path(const string & file_path,string & target_path)718 maybe_get_symlink_target_file_path(const string& file_path,
719 				   string& target_path)
720 {
721   DECLARE_STAT(st);
722 
723   if (!get_stat(file_path, &st))
724     return false;
725 
726   char *link_target_path = realpath(file_path.c_str(), NULL);
727   if (!link_target_path)
728     return false;
729 
730   target_path = link_target_path;
731   free(link_target_path);
732   return true;
733 }
734 
735 /// Return the directory part of a file path.
736 ///
737 /// @param path the file path to consider
738 ///
739 /// @param dirnam the resulting directory part, or "." if the couldn't
740 /// figure out anything better (for now; maybe we should do something
741 /// better than this later ...).
742 ///
743 /// @param keep_separator_at_end if true, then keep the separator at
744 /// the end of the resulting dir name.
745 ///
746 /// @return true upon successful completion, false otherwise (okay,
747 /// for now it always return true, but that might change in the future).
748 bool
dir_name(string const & path,string & dir_name,bool keep_separator_at_end)749 dir_name(string const& path,
750 	 string& dir_name,
751 	 bool keep_separator_at_end)
752 {
753   if (path.empty())
754     {
755       dir_name = ".";
756       return true;
757     }
758 
759   char *p = strdup(path.c_str());
760   char *r = ::dirname(p);
761   dir_name = r;
762   free(p);
763   if (keep_separator_at_end
764       && dir_name.length() < path.length())
765     dir_name += "/";
766   return true;
767 }
768 
769 /// Return the file name part of a file part.
770 ///
771 /// @param path the file path to consider.
772 ///
773 /// @param file_name the name part of the file to consider.
774 ///
775 ///@return true upon successful completion, false otherwise (okay it
776 ///always return true for now, but that might change in the future).
777 bool
base_name(string const & path,string & file_name)778 base_name(string const &path,
779 	 string& file_name)
780 {
781   if (path.empty())
782     {
783       file_name = ".";
784       return true;
785     }
786 
787   char *p = strdup(path.c_str());
788   char *f = ::basename(p);
789   file_name = f;
790   free(p);
791   return true;
792 }
793 
794 /// Return the real path of a given path.
795 ///
796 /// The real path of path 'foo_path' is the same path as foo_path, but
797 /// with symlinks and relative paths resolved.
798 ///
799 /// @param path the path to consider.
800 ///
801 /// @param result the computed real_path;
802 void
real_path(const string & path,string & result)803 real_path(const string&path, string& result)
804 {
805   if (path.empty())
806     {
807       result.clear();
808       return;
809     }
810 
811   char *realp = realpath(path.c_str(), NULL);
812   if (realp)
813     {
814       result = realp;
815       free(realp);
816     }
817 }
818 
819 /// Ensures #dir_path is a directory and is created.  If #dir_path is
820 /// not created, this function creates it.
821 ///
822 /// \return true if #dir_path is a directory that is already present,
823 /// of if the function has successfuly created it.
824 bool
ensure_dir_path_created(const string & dir_path)825 ensure_dir_path_created(const string& dir_path)
826 {
827   struct stat st;
828   memset(&st, 0, sizeof (st));
829 
830   int stat_result = 0;
831 
832   stat_result = stat(dir_path.c_str(), &st);
833   if (stat_result == 0)
834     {
835       // A file or directory already exists with the same name.
836       if (!S_ISDIR (st.st_mode))
837 	return false;
838       return true;
839     }
840 
841   string cmd;
842   cmd = "mkdir -p " + dir_path;
843 
844   if (system(cmd.c_str()))
845     return false;
846 
847   return true;
848 }
849 
850 /// Ensures that the parent directory of #path is created.
851 ///
852 /// \return true if the parent directory of #path is already present,
853 /// or if this function has successfuly created it.
854 bool
ensure_parent_dir_created(const string & path)855 ensure_parent_dir_created(const string& path)
856 {
857   bool is_ok = false;
858 
859   if (path.empty())
860     return is_ok;
861 
862   string parent;
863   if (dir_name(path, parent))
864     is_ok = ensure_dir_path_created(parent);
865 
866   return is_ok;
867 }
868 
869 /// Emit a prefix made of the name of the program which is emitting a
870 /// message to an output stream.
871 ///
872 /// The prefix is a string which looks like:
873 ///
874 ///   "<program-name> : "
875 ///
876 /// @param prog_name the name of the program to use in the prefix.
877 /// @param out the output stream where to emit the prefix.
878 ///
879 /// @return the output stream where the prefix was emitted.
880 ostream&
emit_prefix(const string & prog_name,ostream & out)881 emit_prefix(const string& prog_name, ostream& out)
882 {
883   if (!prog_name.empty())
884     out << prog_name << ": ";
885   return out;
886 }
887 
888 /// Check if a given path exists and is readable.
889 ///
890 /// @param path the path to consider.
891 ///
892 /// @param out the out stream to report errors to.
893 ///
894 /// @return true iff path exists and is readable.
895 bool
check_file(const string & path,ostream & out,const string & prog_name)896 check_file(const string& path, ostream& out, const string& prog_name)
897 {
898   if (!file_exists(path))
899     {
900       emit_prefix(prog_name, out) << "file " << path << " does not exist\n";
901       return false;
902     }
903 
904   if (!is_regular_file(path))
905     {
906       emit_prefix(prog_name, out) << path << " is not a regular file\n";
907       return false;
908     }
909 
910   return true;
911 }
912 
913 /// Check if a given path exists, is readable and is a directory.
914 ///
915 /// @param path the path to consider.
916 ///
917 /// @param out the out stream to report errors to.
918 ///
919 /// @param prog_name the program name on behalf of which to report the
920 /// error, if any.
921 ///
922 /// @return true iff @p path exists and is for a directory.
923 bool
check_dir(const string & path,ostream & out,const string & prog_name)924 check_dir(const string& path, ostream& out, const string& prog_name)
925 {
926   if (!file_exists(path))
927     {
928       emit_prefix(prog_name, out) << "path " << path << " does not exist\n";
929       return false;
930     }
931 
932   if (!is_dir(path))
933     {
934       emit_prefix(prog_name, out) << path << " is not a directory\n";
935       return false;
936     }
937 
938   return true;
939 }
940 
941 /// Test if a given string ends with a particular suffix.
942 ///
943 /// @param str the string to consider.
944 ///
945 /// @param suffix the suffix to test for.
946 ///
947 /// @return true iff string @p str ends with suffix @p suffix.
948 bool
string_ends_with(const string & str,const string & suffix)949 string_ends_with(const string& str, const string& suffix)
950 {
951   string::size_type str_len = str.length(), suffix_len = suffix.length();
952 
953   if (str_len < suffix_len)
954     return false;
955   return str.compare(str_len - suffix_len, suffix_len, suffix) == 0;
956 }
957 
958 /// Test if a given string begins with a particular prefix.
959 ///
960 /// @param str the string consider.
961 ///
962 /// @param prefix the prefix to look for.
963 ///
964 /// @return true iff string @p str begins with prefix @p prefix.
965 bool
string_begins_with(const string & str,const string & prefix)966 string_begins_with(const string& str, const string& prefix)
967 {
968   if (str.empty())
969     return false;
970 
971   if (prefix.empty())
972     return true;
973 
974   string::size_type prefix_len = prefix.length();
975   if (prefix_len > str.length())
976     return false;
977 
978   return str.compare(0, prefix.length(), prefix) == 0;
979 }
980 
981 /// Test if a string is made of ascii characters.
982 ///
983 /// @param str the string to consider.
984 ///
985 /// @return true iff @p str is made of ascii characters.
986 bool
string_is_ascii(const string & str)987 string_is_ascii(const string& str)
988 {
989   for (string::const_iterator i = str.begin(); i != str.end(); ++i)
990     if (!isascii(*i))
991       return false;
992 
993   return true;
994 }
995 
996 /// Test if a string is made of ascii characters which are identifiers
997 /// acceptable in C or C++ programs.
998 ///
999 ///
1000 /// In the C++ spec, [lex.charset]/2, we can read:
1001 ///
1002 /// "if the hexadecimal value for a universal-character-name [...]  or
1003 ///  string literal corresponds to a control character (in either of
1004 ///  the ranges 0x00-0x1F or 0x7F-0x9F, both inclusive) [...] the
1005 ///  program is ill-formed."
1006 ///
1007 /// @param str the string to consider.
1008 ///
1009 /// @return true iff @p str is made of ascii characters, and is an
1010 /// identifier.
1011 bool
string_is_ascii_identifier(const string & str)1012 string_is_ascii_identifier(const string& str)
1013 {
1014   for (string::const_iterator i = str.begin(); i != str.end(); ++i)
1015     {
1016       unsigned char c = *i;
1017     if (!isascii(c)
1018 	|| (c <= 0x1F) // Rule out control characters
1019 	|| (c >= 0x7F && c <= 0x9F)) // Rule out special extended
1020 				     // ascii characters.
1021       return false;
1022     }
1023 
1024   return true;
1025 }
1026 
1027 /// Split a given string into substrings, given some delimiters.
1028 ///
1029 /// @param input_string the input string to split.
1030 ///
1031 /// @param delims a string containing the delimiters to consider.
1032 ///
1033 /// @param result a vector of strings containing the splitted result.
1034 ///
1035 /// @return true iff the function found delimiters in the input string
1036 /// and did split it as a result.  Note that if no delimiter was found
1037 /// in the input string, then the input string is added at the end of
1038 /// the output vector of strings.
1039 bool
split_string(const string & input_string,const string & delims,vector<string> & result)1040 split_string(const string& input_string,
1041 	     const string& delims,
1042 	     vector<string>& result)
1043 {
1044   size_t current = 0, next;
1045   bool did_split = false;
1046 
1047   do
1048     {
1049       // Trim leading white spaces
1050       while (current < input_string.size() && isspace(input_string[current]))
1051 	++current;
1052 
1053       if (current >= input_string.size())
1054 	break;
1055 
1056       next = input_string.find_first_of(delims, current);
1057       if (next == string::npos)
1058 	{
1059 	  string s = input_string.substr(current);
1060 	  if (!s.empty())
1061 	    result.push_back(input_string.substr(current));
1062 	  did_split = (current != 0);
1063 	  break;
1064 	}
1065       string s = input_string.substr(current, next - current);
1066       if (!s.empty())
1067 	{
1068 	  result.push_back(input_string.substr(current, next - current));
1069 	  did_split = true;
1070 	}
1071       current = next + 1;
1072     }
1073   while (next != string::npos);
1074 
1075   return did_split;
1076 }
1077 
1078 /// Get the suffix of a string, given a prefix to consider.
1079 ///
1080 /// @param input_string the input string to consider.
1081 ///
1082 /// @param prefix the prefix of the input string to consider.
1083 ///
1084 /// @param suffix output parameter. This is set by the function to the
1085 /// the computed suffix iff a suffix was found for prefix @p prefix.
1086 ///
1087 /// @return true iff the function could find a prefix for the suffix
1088 /// @p suffix in the input string @p input_string.
1089 bool
string_suffix(const string & input_string,const string & prefix,string & suffix)1090 string_suffix(const string&	input_string,
1091 	      const string&	prefix,
1092 	      string&		suffix)
1093 {
1094   // Some basic sanity check before we start hostilities.
1095   if (prefix.length() >= input_string.length())
1096     return false;
1097 
1098   if (input_string.compare(0, prefix.length(), prefix) != 0)
1099     // The input string does not start with the string contained in
1100     // the prefix parameter.
1101     return false;
1102 
1103   suffix = input_string.substr(prefix.length());
1104   return true;
1105 }
1106 
1107 /// Return the prefix that is common to two strings.
1108 ///
1109 /// @param s1 the first input string to consider.
1110 ///
1111 /// @param s2 the second input string to consider.
1112 ///
1113 /// @param result output parameter.  The resulting common prefix found
1114 /// between @p s1 and @p s2.  This is set iff the function returns
1115 /// true.
1116 ///
1117 /// @return true iff @p result was set by this function with the
1118 /// common prefix of @p s1 and @p s2.
1119 static bool
common_prefix(const string & s1,const string & s2,string & result)1120 common_prefix(const string& s1, const string& s2, string &result)
1121 {
1122   if (s1.length() == 0 || s2.length() == 0)
1123     return false;
1124 
1125   result.clear();
1126   for (size_t i = 0; i < s1.length() && i< s2.length(); ++i)
1127     if (s1[i] == s2[i])
1128       result += s1[i];
1129     else
1130       break;
1131 
1132   return !result.empty();
1133 }
1134 
1135 /// Find the prefix common to a *SORTED* vector of strings.
1136 ///
1137 /// @param input_strings a lexycographically sorted vector of
1138 /// strings.  Please note that this vector absolutely needs to be
1139 /// sorted for the function to work correctly.  Otherwise the results
1140 /// are going to be wrong.
1141 ///
1142 /// @param prefix output parameter.  This is set by this function with
1143 /// the prefix common to the strings found in @p input_strings, iff
1144 /// the function returns true.
1145 ///
1146 /// @return true iff the function could find a common prefix to the
1147 /// strings in @p input_strings.
1148 bool
sorted_strings_common_prefix(vector<string> & input_strings,string & prefix)1149 sorted_strings_common_prefix(vector<string>& input_strings, string& prefix)
1150 {
1151   string prefix_candidate;
1152   bool found_prefix = false;
1153 
1154   if (input_strings.size() == 1)
1155     {
1156       if (dir_name(input_strings.front(), prefix,
1157 		   /*keep_separator_at_end=*/true))
1158 	return true;
1159       return false;
1160     }
1161 
1162   string cur_str;
1163   for (vector<string>::const_iterator i = input_strings.begin();
1164        i != input_strings.end();
1165        ++i)
1166     {
1167       dir_name(*i, cur_str, /*keep_separator_at_end=*/true);
1168       if (prefix_candidate.empty())
1169 	{
1170 	  prefix_candidate = cur_str;
1171 	  continue;
1172 	}
1173 
1174       string s;
1175       if (common_prefix(prefix_candidate, cur_str, s))
1176 	{
1177 	  ABG_ASSERT(!s.empty());
1178 	  prefix_candidate = s;
1179 	  found_prefix = true;
1180 	}
1181     }
1182 
1183   if (found_prefix)
1184     {
1185       prefix = prefix_candidate;
1186       return true;
1187     }
1188 
1189   return false;
1190 }
1191 
1192 /// Return the version string of the library.
1193 ///
1194 /// @return the version string of the library.
1195 string
get_library_version_string()1196 get_library_version_string()
1197 {
1198   string major, minor, revision, version_string, suffix;
1199   abigail::abigail_get_library_version(major, minor, revision, suffix);
1200   version_string = major + "." + minor + "." + revision + suffix;
1201   return version_string;
1202 }
1203 
1204 /// Return the version string for the ABIXML format.
1205 ///
1206 /// @return the version string of the ABIXML format.
1207 string
get_abixml_version_string()1208 get_abixml_version_string()
1209 {
1210   string major, minor, version_string;
1211   abigail::abigail_get_abixml_version(major, minor);
1212   version_string = major + "." + minor;
1213   return version_string;
1214 }
1215 
1216 /// Execute a shell command and returns its output.
1217 ///
1218 /// @param cmd the shell command to execute.
1219 ///
1220 /// @param lines output parameter.  This is set with the lines that
1221 /// constitute the output of the process that executed the command @p
1222 /// cmd.
1223 ///
1224 /// @return true iff the command was executed properly and no error
1225 /// was encountered.
1226 bool
execute_command_and_get_output(const string & cmd,vector<string> & lines)1227 execute_command_and_get_output(const string& cmd, vector<string>& lines)
1228 {
1229   if (cmd.empty())
1230     return false;
1231 
1232   FILE *stream=
1233     popen(cmd.c_str(),
1234 	  /*open 'stream' in
1235 	    read-only mode: type=*/"r");
1236 
1237   if (stream == NULL)
1238     return false;
1239 
1240   string result;
1241 
1242 #define TMP_BUF_LEN 1024 + 1
1243   char tmp_buf[TMP_BUF_LEN];
1244   memset(tmp_buf, 0, TMP_BUF_LEN);
1245 
1246   while (fgets(tmp_buf, TMP_BUF_LEN, stream))
1247     {
1248       lines.push_back(tmp_buf);
1249       memset(tmp_buf, 0, TMP_BUF_LEN);
1250     }
1251 
1252   if (pclose(stream) == -1)
1253     return false;
1254 
1255   return true;
1256 }
1257 
1258 /// Get a vector of arguments from a string containing a
1259 /// comma-separated list of those arguments.
1260 ///
1261 /// @param input_str the input string containing the comma-separated
1262 /// list of arguments The input string has the form
1263 /// "option=arg1,arg2,arg3,arg4".
1264 ///
1265 /// @param option if the content of the input string @p input_str is
1266 /// "option=arg1,arg2,arg3", then this parameter should be "option".
1267 ///
1268 /// @param arguments this is set by the fonction the the arguments
1269 /// that were a comma-separated list of arguments on the right hand
1270 /// side of the '=' sign in the string @p input_str.
1271 void
get_comma_separated_args_of_option(const string & input_str,const string & option,vector<string> & arguments)1272 get_comma_separated_args_of_option(const string& input_str,
1273 				   const string& option,
1274 				   vector<string>& arguments)
1275 {
1276   string s = input_str;
1277 
1278   string_suffix(s, option, s);
1279   if (string_begins_with(s, "\""))
1280     s = s.substr(1);
1281   if (string_ends_with(s, "\""))
1282     s = s.substr(0, s.size() - 1);
1283   split_string(s, ",", arguments);
1284 }
1285 
1286 /// Get the SONAMEs of the DSOs advertised as being "provided" by a
1287 /// given RPM.  That set can be considered as being the set of
1288 /// "public" DSOs of the RPM.
1289 ///
1290 /// This runs the command "rpm -qp --provides <rpm> | grep .so" and
1291 /// filters its result.
1292 ///
1293 /// @param rpm_path the path to the RPM to consider.
1294 ///
1295 /// @param provided_dsos output parameter.  This is set to the set of
1296 /// SONAMEs of the DSOs advertised as being provided by the RPM
1297 /// designated by @p rpm_path.
1298 ///
1299 /// @return true iff we could successfully query the RPM to see what
1300 /// DSOs it provides.
1301 bool
get_dsos_provided_by_rpm(const string & rpm_path,set<string> & provided_dsos)1302 get_dsos_provided_by_rpm(const string& rpm_path, set<string>& provided_dsos)
1303 {
1304   vector<string> query_output;
1305   // We don't check the return value of this command because on some
1306   // system, the command can issue errors but still emit a valid
1307   // output.  We'll rather rely on the fact that the command emits a
1308   // valid output or not.
1309   execute_command_and_get_output("rpm -qp --provides "
1310 				 + rpm_path + " 2> /dev/null | grep .so",
1311 				 query_output);
1312 
1313   for (vector<string>::const_iterator line = query_output.begin();
1314        line != query_output.end();
1315        ++line)
1316     {
1317       string dso = line->substr(0, line->find('('));
1318       dso = trim_white_space(dso);
1319       if (!dso.empty())
1320 	provided_dsos.insert(dso);
1321     }
1322   return true;
1323 }
1324 
1325 /// Remove spaces at the beginning and at the end of a given string.
1326 ///
1327 /// @param str the input string to consider.
1328 ///
1329 /// @return the @p str string with leading and trailing white spaces removed.
1330 string
trim_white_space(const string & str)1331 trim_white_space(const string& str)
1332 {
1333   if (str.empty())
1334     return "";
1335 
1336   string result;
1337   string::size_type start, end;
1338   for (start = 0; start < str.length(); ++start)
1339     if (!isspace(str[start]))
1340       break;
1341 
1342   for (end = str.length() - 1; end > 0; --end)
1343     if (!isspace(str[end]))
1344       break;
1345 
1346   result = str.substr(start, end - start + 1);
1347   return result;
1348 }
1349 
1350 /// Remove a string of pattern in front of a given string.
1351 ///
1352 /// For instance, consider this string:
1353 ///    "../../../foo"
1354 ///
1355 /// The pattern "../" is repeated three times in front of the
1356 /// sub-string "foo".  Thus, the call:
1357 ///    trim_leading_string("../../../foo", "../")
1358 /// will return the string "foo".
1359 ///
1360 /// @param from the string to trim the leading repetition of pattern from.
1361 ///
1362 /// @param to_trim the pattern to consider (and to trim).
1363 ///
1364 /// @return the resulting string where the leading patter @p to_trim
1365 /// has been removed from.
1366 string
trim_leading_string(const string & from,const string & to_trim)1367 trim_leading_string(const string& from, const string& to_trim)
1368 {
1369   string str = from;
1370 
1371   while (string_begins_with(str, to_trim))
1372     string_suffix(str, to_trim, str);
1373   return str;
1374 }
1375 
1376 /// Convert a vector<char*> into a vector<char**>.
1377 ///
1378 /// @param char_stars the input vector.
1379 ///
1380 /// @param char_star_stars the output vector.
1381 void
convert_char_stars_to_char_star_stars(const vector<char * > & char_stars,vector<char ** > & char_star_stars)1382 convert_char_stars_to_char_star_stars(const vector<char*> &char_stars,
1383 				      vector<char**>& char_star_stars)
1384 {
1385   for (vector<char*>::const_iterator i = char_stars.begin();
1386        i != char_stars.end();
1387        ++i)
1388     char_star_stars.push_back(const_cast<char**>(&*i));
1389 }
1390 
1391 /// The private data of the @ref temp_file type.
1392 struct temp_file::priv
1393 {
1394   char*			   path_template_;
1395   int			   fd_;
1396   shared_ptr<std::fstream> fstream_;
1397 
privabigail::tools_utils::temp_file::priv1398   priv()
1399   {
1400     const char* templat = "/tmp/libabigail-tmp-file-XXXXXX";
1401     int s = strlen(templat);
1402     path_template_ = new char[s + 1];
1403     memset(path_template_, 0, s + 1);
1404     memcpy(path_template_, templat, s);
1405 
1406     fd_ = mkstemp(path_template_);
1407     if (fd_ == -1)
1408       return;
1409 
1410     fstream_.reset(new std::fstream(path_template_,
1411 				    std::ios::trunc
1412 				    | std::ios::in
1413 				    | std::ios::out));
1414   }
1415 
~privabigail::tools_utils::temp_file::priv1416   ~priv()
1417   {
1418     if (fd_ && fd_ != -1)
1419       {
1420 	fstream_.reset();
1421 	close(fd_);
1422 	remove(path_template_);
1423       }
1424     delete [] path_template_;
1425   }
1426 };
1427 
1428 /// Default constructor of @ref temp_file.
1429 ///
1430 /// It actually creates the temporary file.
temp_file()1431 temp_file::temp_file()
1432   : priv_(new priv)
1433 {}
1434 
1435 /// Test if the temporary file has been created and is usable.
1436 ///
1437 /// @return true iff the temporary file has been created and is
1438 /// useable.
1439 bool
is_good() const1440 temp_file::is_good() const
1441 {return priv_->fstream_->good();}
1442 
1443 /// Return the path to the temporary file.
1444 ///
1445 /// @return the path to the temporary file if it's usable, otherwise
1446 /// return nil.
1447 const char*
get_path() const1448 temp_file::get_path() const
1449 {
1450   if (is_good())
1451     return priv_->path_template_;
1452 
1453   return 0;
1454 }
1455 
1456 /// Get the fstream to the temporary file.
1457 ///
1458 /// Note that the current process is aborted if this member function
1459 /// is invoked on an instance of @ref temp_file that is not usable.
1460 /// So please test that the instance is usable by invoking the
1461 /// temp_file::is_good() member function on it first.
1462 ///
1463 /// @return the fstream to the temporary file.
1464 std::fstream&
get_stream()1465 temp_file::get_stream()
1466 {
1467   ABG_ASSERT(is_good());
1468   return *priv_->fstream_;
1469 }
1470 
1471 /// Create the temporary file and return it if it's usable.
1472 ///
1473 /// @return the newly created temporary file if it's usable, nil
1474 /// otherwise.
1475 temp_file_sptr
create()1476 temp_file::create()
1477 {
1478   temp_file_sptr result(new temp_file);
1479   if (result->is_good())
1480     return result;
1481 
1482   return temp_file_sptr();
1483 }
1484 
1485 /// Get a pseudo random number.
1486 ///
1487 /// @return a pseudo random number.
1488 size_t
get_random_number()1489 get_random_number()
1490 {
1491   static __thread bool initialized = false;
1492 
1493   if (!initialized)
1494     {
1495       srand(time(NULL));
1496       initialized = true;
1497     }
1498 
1499   return rand();
1500 }
1501 
1502 /// Get a pseudo random number as string.
1503 ///
1504 /// @return a pseudo random number as string.
1505 string
get_random_number_as_string()1506 get_random_number_as_string()
1507 {
1508   std::ostringstream o;
1509   o << get_random_number();
1510 
1511   return o.str();
1512 }
1513 
1514 ostream&
operator <<(ostream & output,file_type r)1515 operator<<(ostream& output,
1516 	   file_type r)
1517 {
1518   string repr;
1519 
1520   switch(r)
1521     {
1522     case FILE_TYPE_UNKNOWN:
1523       repr = "unknown file type";
1524       break;
1525     case FILE_TYPE_NATIVE_BI:
1526       repr = "native binary instrumentation file type";
1527       break;
1528     case FILE_TYPE_ELF:
1529       repr = "ELF file type";
1530       break;
1531     case FILE_TYPE_AR:
1532       repr = "archive file type";
1533       break;
1534     case FILE_TYPE_XML_CORPUS:
1535       repr = "native XML corpus file type";
1536       break;
1537     case FILE_TYPE_XML_CORPUS_GROUP:
1538       repr = "native XML corpus group file type";
1539       break;
1540     case FILE_TYPE_RPM:
1541       repr = "RPM file type";
1542       break;
1543     case FILE_TYPE_SRPM:
1544       repr = "SRPM file type";
1545       break;
1546     case FILE_TYPE_DEB:
1547       repr = "Debian binary file type";
1548       break;
1549     case FILE_TYPE_DIR:
1550       repr = "Directory type";
1551       break;
1552     case FILE_TYPE_TAR:
1553       repr = "GNU tar archive type";
1554       break;
1555     }
1556 
1557   output << repr;
1558   return output;
1559 }
1560 
1561 /// Guess the type of the content of an input stream.
1562 ///
1563 /// @param in the input stream to guess the content type for.
1564 ///
1565 /// @return the type of content guessed.
1566 file_type
guess_file_type(istream & in)1567 guess_file_type(istream& in)
1568 {
1569   const unsigned BUF_LEN = 264;
1570   const unsigned NB_BYTES_TO_READ = 263;
1571 
1572   char buf[BUF_LEN];
1573   memset(buf, 0, BUF_LEN);
1574 
1575   std::streampos initial_pos = in.tellg();
1576   in.read(buf, NB_BYTES_TO_READ);
1577   in.seekg(initial_pos);
1578 
1579   if (in.gcount() < 4 || in.bad())
1580     return FILE_TYPE_UNKNOWN;
1581 
1582   if (buf[0] == 0x7f
1583       && buf[1] == 'E'
1584       && buf[2] == 'L'
1585       && buf[3] == 'F')
1586     return FILE_TYPE_ELF;
1587 
1588   if (buf[0] == '!'
1589       && buf[1] == '<'
1590       && buf[2] == 'a'
1591       && buf[3] == 'r'
1592       && buf[4] == 'c'
1593       && buf[5] == 'h'
1594       && buf[6] == '>')
1595     {
1596       if (strstr(buf, "debian-binary"))
1597 	return FILE_TYPE_DEB;
1598       else
1599 	return FILE_TYPE_AR;
1600     }
1601 
1602   if (buf[0] == '<'
1603       && buf[1] == 'a'
1604       && buf[2] == 'b'
1605       && buf[3] == 'i'
1606       && buf[4] == '-'
1607       && buf[5] == 'i'
1608       && buf[6] == 'n'
1609       && buf[7] == 's'
1610       && buf[8] == 't'
1611       && buf[9] == 'r'
1612       && buf[10] == ' ')
1613     return FILE_TYPE_NATIVE_BI;
1614 
1615   if (buf[0]     == '<'
1616       && buf[1]  == 'a'
1617       && buf[2]  == 'b'
1618       && buf[3]  == 'i'
1619       && buf[4]  == '-'
1620       && buf[5]  == 'c'
1621       && buf[6]  == 'o'
1622       && buf[7]  == 'r'
1623       && buf[8]  == 'p'
1624       && buf[9]  == 'u'
1625       && buf[10] == 's'
1626       && buf[11] == '-'
1627       && buf[12] == 'g'
1628       && buf[13] == 'r'
1629       && buf[14] == 'o'
1630       && buf[15] == 'u'
1631       && buf[16] == 'p'
1632       && buf[17] == ' ')
1633     return FILE_TYPE_XML_CORPUS_GROUP;
1634 
1635   if (buf[0]     == '<'
1636       && buf[1]  == 'a'
1637       && buf[2]  == 'b'
1638       && buf[3]  == 'i'
1639       && buf[4]  == '-'
1640       && buf[5]  == 'c'
1641       && buf[6]  == 'o'
1642       && buf[7]  == 'r'
1643       && buf[8]  == 'p'
1644       && buf[9]  == 'u'
1645       && buf[10] == 's'
1646       && buf[11] == ' ')
1647     return FILE_TYPE_XML_CORPUS;
1648 
1649   if ((unsigned char) buf[0]    == 0xed
1650       && (unsigned char) buf[1] == 0xab
1651       && (unsigned char) buf[2] == 0xee
1652       && (unsigned char) buf[3] == 0xdb)
1653     {
1654         if (buf[7] == 0x00)
1655           return FILE_TYPE_RPM;
1656         else if (buf[7] == 0x01)
1657           return FILE_TYPE_SRPM;
1658         else
1659           return FILE_TYPE_UNKNOWN;
1660     }
1661 
1662   if (buf[257]    == 'u'
1663       && buf[258] == 's'
1664       && buf[259] == 't'
1665       && buf[260] == 'a'
1666       && buf[261] == 'r')
1667     return FILE_TYPE_TAR;
1668 
1669   return FILE_TYPE_UNKNOWN;
1670 }
1671 
1672 /// Guess the type of the content of an file.
1673 ///
1674 /// @param file_path the path to the file to consider.
1675 ///
1676 /// @return the type of content guessed.
1677 file_type
guess_file_type(const string & file_path)1678 guess_file_type(const string& file_path)
1679 {
1680   if (is_dir(file_path))
1681     return FILE_TYPE_DIR;
1682 
1683   if (string_ends_with(file_path, ".tar")
1684       || string_ends_with(file_path, ".tar.gz")
1685       || string_ends_with(file_path, ".tgz")
1686       || string_ends_with(file_path, ".tar.bz2")
1687       || string_ends_with(file_path, ".tbz2")
1688       || string_ends_with(file_path, ".tbz")
1689       || string_ends_with(file_path, ".tb2")
1690       || string_ends_with(file_path, ".tar.xz")
1691       || string_ends_with(file_path, ".txz")
1692       || string_ends_with(file_path, ".tar.lzma")
1693       || string_ends_with(file_path, ".tar.lz")
1694       || string_ends_with(file_path, ".tlz")
1695       || string_ends_with(file_path, ".tar.Z")
1696       || string_ends_with(file_path, ".taz")
1697       || string_ends_with(file_path, ".tz"))
1698     return FILE_TYPE_TAR;
1699 
1700   ifstream in(file_path.c_str(), ifstream::binary);
1701   file_type r = guess_file_type(in);
1702   in.close();
1703   return r;
1704 }
1705 
1706 /// Get the package name of a .deb package.
1707 ///
1708 /// @param str the string containing the .deb NVR.
1709 ///
1710 /// @param name output parameter.  This is set with the package name
1711 /// of the .deb package iff the function returns true.
1712 ///
1713 /// @return true iff the function successfully finds the .deb package
1714 /// name.
1715 bool
get_deb_name(const string & str,string & name)1716 get_deb_name(const string& str, string& name)
1717 {
1718   if (str.empty() || str[0] == '_')
1719     return false;
1720 
1721   string::size_type str_len = str.length(), i = 0 ;
1722 
1723   for (; i < str_len; ++i)
1724     {
1725       if (str[i] == '_')
1726 	break;
1727     }
1728 
1729   if (i == str_len)
1730     return false;
1731 
1732   name = str.substr(0, i);
1733   return true;
1734 }
1735 
1736 /// Get the package name of an rpm package.
1737 ///
1738 /// @param str the string containing the NVR of the rpm.
1739 ///
1740 /// @param name output parameter.  This is set with the package name
1741 /// of the rpm package iff the function returns true.
1742 ///
1743 /// @return true iff the function successfully finds the rpm package
1744 /// name.
1745 bool
get_rpm_name(const string & str,string & name)1746 get_rpm_name(const string& str, string& name)
1747 {
1748   if (str.empty() || str[0] == '-')
1749     return false;
1750 
1751   string::size_type str_len = str.length(), i = 0;
1752   string::value_type c;
1753 
1754   for (; i < str_len; ++i)
1755     {
1756       c = str[i];
1757       string::size_type next_index = i + 1;
1758       if ((next_index < str_len) && c == '-' && isdigit(str[next_index]))
1759 	break;
1760     }
1761 
1762   if (i == str_len)
1763     return false;
1764 
1765   name = str.substr(0, i);
1766 
1767   return true;
1768 }
1769 
1770 /// Get the architecture string from the NVR of an rpm.
1771 ///
1772 /// @param str the NVR to consider.
1773 ///
1774 /// @param arch output parameter.  Is set to the resulting
1775 /// archirecture string iff the function returns true.
1776 ///
1777 /// @return true iff the function could find the architecture string
1778 /// from the NVR.
1779 bool
get_rpm_arch(const string & str,string & arch)1780 get_rpm_arch(const string& str, string& arch)
1781 {
1782   if (str.empty())
1783     return false;
1784 
1785   if (!string_ends_with(str, ".rpm"))
1786     return false;
1787 
1788   string::size_type str_len = str.length(), i = 0;
1789   string::value_type c;
1790   string::size_type last_dot_index = 0, dot_before_last_index = 0;
1791 
1792   for (i = str_len - 1; i > 0; --i)
1793     {
1794       c = str[i];
1795       if (c == '.')
1796 	{
1797 	  last_dot_index = i;
1798 	  break;
1799 	}
1800     }
1801 
1802   if (i == 0)
1803     return false;
1804 
1805   for(--i; i > 0; --i)
1806     {
1807       c = str[i];
1808       if (c == '.')
1809 	{
1810 	  dot_before_last_index = i;
1811 	  break;
1812 	}
1813     }
1814 
1815   if (i == 0)
1816     return false;
1817 
1818   arch = str.substr(dot_before_last_index + 1,
1819 		    last_dot_index - dot_before_last_index - 1);
1820 
1821   return true;
1822 }
1823 
1824 /// Tests if a given file name designates a kernel package.
1825 ///
1826 /// @param file_path the path to the file to consider.
1827 ///
1828 /// @param file_type the type of the file @p file_name.
1829 ///
1830 /// @return true iff @p file_name of kind @p file_type designates a
1831 /// kernel package.
1832 bool
file_is_kernel_package(const string & file_path,file_type file_type)1833 file_is_kernel_package(const string& file_path, file_type file_type)
1834 {
1835   bool result = false;
1836 
1837   if (file_type == FILE_TYPE_RPM)
1838     {
1839       if (rpm_contains_file(file_path, "vmlinuz"))
1840 	result = true;
1841     }
1842   else if (file_type == FILE_TYPE_DEB)
1843     {
1844       string file_name;
1845       base_name(file_path, file_name);
1846       string package_name;
1847       if (get_deb_name(file_name, package_name))
1848 	result = (string_begins_with(package_name, "linux-image"));
1849     }
1850 
1851   return result;
1852 }
1853 
1854 /// Test if an RPM package contains a given file.
1855 ///
1856 /// @param rpm_path the path to the RPM package.
1857 ///
1858 /// @param file_name the file name to test the presence for in the
1859 /// rpm.
1860 ///
1861 /// @return true iff the file named @file_name is present in the RPM.
1862 bool
rpm_contains_file(const string & rpm_path,const string & file_name)1863 rpm_contains_file(const string& rpm_path, const string& file_name)
1864 {
1865     vector<string> query_output;
1866   // We don't check the return value of this command because on some
1867   // system, the command can issue errors but still emit a valid
1868   // output.  We'll rather rely on the fact that the command emits a
1869   // valid output or not.
1870   execute_command_and_get_output("rpm -qlp "
1871 				 + rpm_path + " 2> /dev/null",
1872 				 query_output);
1873 
1874   for (auto& line : query_output)
1875     {
1876       line = trim_white_space(line);
1877       if (string_ends_with(line, file_name))
1878 	return true;
1879     }
1880 
1881   return false;
1882 }
1883 
1884 /// Tests if a given file name designates a kernel debuginfo package.
1885 ///
1886 /// @param file_name the file name to consider.
1887 ///
1888 /// @param file_type the type of the file @p file_name.
1889 ///
1890 /// @return true iff @p file_name of kind @p file_type designates a
1891 /// kernel debuginfo package.
1892 bool
file_is_kernel_debuginfo_package(const string & file_name,file_type file_type)1893 file_is_kernel_debuginfo_package(const string& file_name, file_type file_type)
1894 {
1895   bool result = false;
1896   string package_name;
1897 
1898   if (file_type == FILE_TYPE_RPM)
1899     {
1900       if (!get_rpm_name(file_name, package_name))
1901 	return false;
1902       result = (package_name == "kernel-debuginfo");
1903     }
1904   else if (file_type == FILE_TYPE_DEB)
1905     {
1906       if (!get_deb_name(file_name, package_name))
1907 	return false;
1908       result = (string_begins_with(package_name, "linux-image")
1909 		&& (string_ends_with(package_name, "-dbg")
1910 		    || string_ends_with(package_name, "-dbgsyms")));
1911     }
1912 
1913   return result;
1914 }
1915 
1916 /// The delete functor of a char buffer that has been created using
1917 /// malloc.
1918 struct malloced_char_star_deleter
1919 {
1920   void
operator ()abigail::tools_utils::malloced_char_star_deleter1921   operator()(char* ptr)
1922   {free(ptr);}
1923 };
1924 
1925 /// Return a copy of the path given in argument, turning it into an
1926 /// absolute path by prefixing it with the concatenation of the result
1927 /// of get_current_dir_name() and the '/' character.
1928 ///
1929 /// The result being an shared_ptr to char*, it should manage its
1930 /// memory by itself and the user shouldn't need to wory too much for
1931 /// that.
1932 ///
1933 /// @param p the path to turn into an absolute path.
1934 ///
1935 /// @return a shared pointer to the resulting absolute path.
1936 std::shared_ptr<char>
make_path_absolute(const char * p)1937 make_path_absolute(const char*p)
1938 {
1939   using std::shared_ptr;
1940 
1941   shared_ptr<char> result;
1942 
1943   if (p && p[0] != '/')
1944     {
1945       shared_ptr<char> pwd(get_current_dir_name(),
1946 			   malloced_char_star_deleter());
1947       string s = string(pwd.get()) + "/" + p;
1948       result.reset(strdup(s.c_str()), malloced_char_star_deleter());
1949     }
1950   else
1951     result.reset(strdup(p), malloced_char_star_deleter());
1952 
1953   return result;
1954 }
1955 
1956 /// Return a copy of the path given in argument, turning it into an
1957 /// absolute path by prefixing it with the concatenation of the result
1958 /// of get_current_dir_name() and the '/' character.
1959 ///
1960 /// The result being a pointer to an allocated memory region, it must
1961 /// be freed by the caller.
1962 ///
1963 /// @param p the path to turn into an absolute path.
1964 ///
1965 /// @return a pointer to the resulting absolute path.  It must be
1966 /// freed by the caller.
1967 char*
make_path_absolute_to_be_freed(const char * p)1968 make_path_absolute_to_be_freed(const char*p)
1969 {
1970     char* result = 0;
1971 
1972   if (p && p[0] != '/')
1973     {
1974       char* pwd = get_current_dir_name();
1975       string s = string(pwd) + "/" + p;
1976       free(pwd);
1977       result = strdup(s.c_str());
1978     }
1979   else
1980     result = strdup(p);
1981 
1982   return result;
1983 }
1984 
1985 /// This is a sub-routine of gen_suppr_spec_from_headers and
1986 /// handle_fts_entry.
1987 ///
1988 /// It setups a type suppression which is meant to keep types defined
1989 /// in a given file and suppress all other types.
1990 ///
1991 /// @param file_path the path to the file that defines types that are
1992 /// meant to be kept by the type suppression.  All other types defined
1993 /// in other files are to be suppressed.  Note that this file path is
1994 /// added to the vector returned by
1995 /// type_suppression::get_source_locations_to_keep()
1996 ///
1997 /// @param suppr the type suppression to setup.  If this smart pointer
1998 /// is nil then a new instance @ref type_suppression is created and
1999 /// this variable is made to point to it.
2000 static void
handle_file_entry(const string & file_path,type_suppression_sptr & suppr)2001 handle_file_entry(const string& file_path,
2002 		  type_suppression_sptr& suppr)
2003 {
2004   if (!suppr)
2005     {
2006       suppr.reset(new type_suppression(get_private_types_suppr_spec_label(),
2007 				       /*type_name_regexp=*/"",
2008 				       /*type_name=*/""));
2009 
2010       // Types that are defined in system headers are usually
2011       // OK to be considered as public types.
2012       suppr->set_source_location_to_keep_regex_str("^/usr/include/");
2013       suppr->set_is_artificial(true);
2014     }
2015 
2016   // And types that are defined in header files that are under
2017   // the header directory file we are looking are to be
2018   // considered public types too.
2019   suppr->get_source_locations_to_keep().insert(file_path);
2020 }
2021 
2022 /// This is a sub-routine of gen_suppr_spec_from_headers.
2023 ///
2024 /// @param entry if this file represents a regular (or symlink) file,
2025 /// then its file name is going to be added to the vector returned by
2026 /// type_suppression::get_source_locations_to_keep().
2027 ///
2028 /// @param if @p entry represents a file, then its file name is going
2029 /// to be added to the vector returned by the method
2030 /// type_suppression::get_source_locations_to_keep of this instance.
2031 /// If this smart pointer is nil then a new instance @ref
2032 /// type_suppression is created and this variable is made to point to
2033 /// it.
2034 static void
handle_fts_entry(const FTSENT * entry,type_suppression_sptr & suppr)2035 handle_fts_entry(const FTSENT *entry,
2036 		 type_suppression_sptr& suppr)
2037 {
2038   if (entry == NULL
2039       || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2040       || entry->fts_info == FTS_ERR
2041       || entry->fts_info == FTS_NS)
2042     return;
2043 
2044   string fname = entry->fts_name;
2045   if (!fname.empty())
2046     {
2047       if (string_ends_with(fname, ".h")
2048 	  || string_ends_with(fname, ".hpp")
2049 	  || string_ends_with(fname, ".hxx"))
2050 	handle_file_entry (fname, suppr);
2051     }
2052 }
2053 
2054 /// Populate a type_supression from header files found in a given
2055 /// directory tree.
2056 ///
2057 /// The suppression suppresses types defined in source files that are
2058 /// *NOT* found in the directory tree.
2059 ///
2060 /// This is a subroutine for gen_suppr_spect_from_headers.
2061 ///
2062 /// @param headers_root_dir the directory tree to consider for header
2063 /// files.
2064 ///
2065 /// @param result the type_supression to populate from the content of
2066 /// @p headers_root_dir.
2067 static void
gen_suppr_spec_from_headers_root_dir(const string & headers_root_dir,type_suppression_sptr & result)2068 gen_suppr_spec_from_headers_root_dir(const string& headers_root_dir,
2069 				     type_suppression_sptr &result)
2070 {
2071   if (!headers_root_dir.empty())
2072     {
2073       char* paths[] = {const_cast<char*>(headers_root_dir.c_str()), 0};
2074 
2075       if (FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL))
2076 	{
2077 	  FTSENT *entry;
2078 	  while ((entry = fts_read(file_hierarchy)))
2079 	    handle_fts_entry(entry, result);
2080 	  fts_close(file_hierarchy);
2081 	}
2082     }
2083 }
2084 
2085 /// Generate a type suppression specification that suppresses ABI
2086 /// changes for types defined in source files that are neither in a
2087 /// given set of header root directories nor in a set of header
2088 /// files.
2089 ///
2090 /// @param headers_root_dirs ABI changes in types defined in files
2091 /// *NOT* found in these directory trees are going be suppressed.
2092 ///
2093 /// @param header_files a set of additional header files that define
2094 /// types that are to be kept (not supressed) by the returned type
2095 /// suppression.
2096 ///
2097 /// @return the resulting type suppression generated, if any file was
2098 /// found in the directory tree @p headers_root_dir.
2099 type_suppression_sptr
gen_suppr_spec_from_headers(const vector<string> & headers_root_dirs,const vector<string> & header_files)2100 gen_suppr_spec_from_headers(const vector<string>& headers_root_dirs,
2101 			    const vector<string>& header_files)
2102 {
2103   type_suppression_sptr result;
2104 
2105   for (vector<string>::const_iterator root_dir = headers_root_dirs.begin();
2106        root_dir != headers_root_dirs.end();
2107        ++root_dir)
2108     gen_suppr_spec_from_headers_root_dir(*root_dir, result);
2109 
2110   for (vector<string>::const_iterator file = header_files.begin();
2111        file != header_files.end();
2112        ++file)
2113     handle_file_entry(*file, result);
2114 
2115   return result;
2116 }
2117 
2118 /// Generate a type suppression specification that suppresses ABI
2119 /// changes for types defined in source files that are neither in a
2120 /// given header root dir, not in a set of header files.
2121 ///
2122 /// @param headers_root_dir ABI changes in types defined in files
2123 /// *NOT* found in this directory tree are going be suppressed.
2124 ///
2125 /// @param header_files a set of additional header files that define
2126 /// types that are to be kept (not supressed) by the returned type
2127 /// suppression.
2128 ///
2129 /// @return the resulting type suppression generated, if any file was
2130 /// found in the directory tree @p headers_root_dir.
2131 type_suppression_sptr
gen_suppr_spec_from_headers(const string & headers_root_dir,const vector<string> & header_files)2132 gen_suppr_spec_from_headers(const string& headers_root_dir,
2133 			    const vector<string>& header_files)
2134 {
2135   type_suppression_sptr result;
2136   vector<string> root_dirs;
2137 
2138   if (!headers_root_dir.empty())
2139     root_dirs.push_back(headers_root_dir);
2140 
2141   return gen_suppr_spec_from_headers(root_dirs, header_files);
2142 }
2143 
2144 /// Generate a type suppression specification that suppresses ABI
2145 /// changes for types defined in source files that are not in a given
2146 /// header root dir.
2147 ///
2148 /// @param headers_root_dir ABI changes in types defined in files
2149 /// *NOT* found in this directory tree are going be suppressed.
2150 ///
2151 /// @return the resulting type suppression generated, if any file was
2152 /// found in the directory tree @p headers_root_dir.
2153 type_suppression_sptr
gen_suppr_spec_from_headers(const string & headers_root_dir)2154 gen_suppr_spec_from_headers(const string& headers_root_dir)
2155 {
2156   // We don't list individual files, just look under the headers_path.
2157   vector<string> header_files;
2158   return gen_suppr_spec_from_headers(headers_root_dir, header_files);
2159 }
2160 
2161 /// Generate a suppression specification from kernel abi whitelist
2162 /// files.
2163 ///
2164 /// A kernel ABI whitelist file is an INI file that usually has only
2165 /// one section.  The name of the section is a string that ends up
2166 /// with the sub-string "whitelist".  For instance
2167 /// RHEL7_x86_64_whitelist.
2168 ///
2169 /// Then the content of the section is a set of function or variable
2170 /// names, one name per line.  Each function or variable name is the
2171 /// name of a function or a variable whose changes are to be keept.
2172 ///
2173 /// A whitelist file can have multiple sections (adhering to the naming
2174 /// conventions and multiple files can be passed. The suppression that
2175 /// is created takes all whitelist sections from all files into account.
2176 /// Symbols (or expression of such) are deduplicated in the final
2177 /// suppression expression.
2178 ///
2179 /// This function reads the white lists and generates a
2180 /// function_suppression_sptr and variable_suppression_sptr and returns
2181 /// a vector containing those.
2182 ///
2183 /// @param abi_whitelist_paths a vector of KMI whitelist paths
2184 ///
2185 /// @return a vector or suppressions
2186 suppressions_type
gen_suppr_spec_from_kernel_abi_whitelists(const std::vector<std::string> & abi_whitelist_paths)2187 gen_suppr_spec_from_kernel_abi_whitelists
2188    (const std::vector<std::string>& abi_whitelist_paths)
2189 {
2190 
2191   std::vector<std::string> whitelisted_names;
2192   for (std::vector<std::string>::const_iterator
2193 	   path_iter = abi_whitelist_paths.begin(),
2194 	   path_end = abi_whitelist_paths.end();
2195        path_iter != path_end;
2196        ++path_iter)
2197     {
2198 
2199       abigail::ini::config whitelist;
2200       if (!read_config(*path_iter, whitelist))
2201 	continue;
2202 
2203       const ini::config::sections_type& whitelist_sections =
2204 	  whitelist.get_sections();
2205 
2206       for (ini::config::sections_type::const_iterator
2207 	       section_iter = whitelist_sections.begin(),
2208 	       section_end = whitelist_sections.end();
2209 	   section_iter != section_end;
2210 	   ++section_iter)
2211 	{
2212 	  std::string section_name = (*section_iter)->get_name();
2213 	  if (!string_ends_with(section_name, "whitelist")
2214 	      && !string_ends_with(section_name, "stablelist"))
2215 	    continue;
2216 	  for (ini::config::properties_type::const_iterator
2217 		   prop_iter = (*section_iter)->get_properties().begin(),
2218 		   prop_end = (*section_iter)->get_properties().end();
2219 	       prop_iter != prop_end;
2220 	       ++prop_iter)
2221 	    {
2222 	      if (const simple_property_sptr& prop =
2223 		      is_simple_property(*prop_iter))
2224 		if (prop->has_empty_value())
2225 		  {
2226 		    const std::string& name = prop->get_name();
2227 		    if (!name.empty())
2228 		      whitelisted_names.push_back(name);
2229 		  }
2230 	    }
2231 	}
2232     }
2233 
2234   suppressions_type result;
2235   if (!whitelisted_names.empty())
2236     {
2237       // Drop duplicates to simplify the regex we are generating
2238       std::sort(whitelisted_names.begin(), whitelisted_names.end());
2239       whitelisted_names.erase(std::unique(whitelisted_names.begin(),
2240 					  whitelisted_names.end()),
2241 			      whitelisted_names.end());
2242 
2243       // Build a regular expression representing the union of all
2244       // the function and variable names expressed in the white list.
2245       const std::string regex = regex::generate_from_strings(whitelisted_names);
2246 
2247       // Build a suppression specification which *keeps* functions
2248       // whose ELF symbols match the regular expression contained
2249       // in function_names_regexp.  This will also keep the ELF
2250       // symbols (not designated by any debug info) whose names
2251       // match this regexp.
2252       function_suppression_sptr fn_suppr(new function_suppression);
2253       fn_suppr->set_label("whitelist");
2254       fn_suppr->set_symbol_name_not_regex_str(regex);
2255       fn_suppr->set_drops_artifact_from_ir(true);
2256       result.push_back(fn_suppr);
2257 
2258       // Build a suppression specification which *keeps* variables
2259       // whose ELF symbols match the regular expression contained
2260       // in function_names_regexp.  This will also keep the ELF
2261       // symbols (not designated by any debug info) whose names
2262       // match this regexp.
2263       variable_suppression_sptr var_suppr(new variable_suppression);
2264       var_suppr->set_label("whitelist");
2265       var_suppr->set_symbol_name_not_regex_str(regex);
2266       var_suppr->set_drops_artifact_from_ir(true);
2267       result.push_back(var_suppr);
2268     }
2269   return result;
2270 }
2271 
2272 /// Get the path to the default system suppression file.
2273 ///
2274 /// @return a copy of the default system suppression file.
2275 string
get_default_system_suppression_file_path()2276 get_default_system_suppression_file_path()
2277 {
2278   string default_system_suppr_path;
2279 
2280   const char *s = getenv("LIBABIGAIL_DEFAULT_SYSTEM_SUPPRESSION_FILE");
2281   if (s)
2282     default_system_suppr_path = s;
2283 
2284   if (default_system_suppr_path.empty())
2285     default_system_suppr_path =
2286       get_system_libdir() + string("/libabigail/default.abignore");
2287 
2288   return default_system_suppr_path;
2289 }
2290 
2291 /// Get the path to the default user suppression file.
2292 ///
2293 /// @return a copy of the default user suppression file.
2294 string
get_default_user_suppression_file_path()2295 get_default_user_suppression_file_path()
2296 {
2297   string default_user_suppr_path;
2298   const char *s = getenv("LIBABIGAIL_DEFAULT_USER_SUPPRESSION_FILE");
2299 
2300   if (s == NULL)
2301     {
2302       s = getenv("HOME");
2303       if (s == NULL)
2304 	return "";
2305       default_user_suppr_path  = s;
2306       if (default_user_suppr_path.empty())
2307 	default_user_suppr_path = "~";
2308       default_user_suppr_path += "/.abignore";
2309     }
2310   else
2311     default_user_suppr_path = s;
2312 
2313   return default_user_suppr_path;
2314 }
2315 
2316 /// Load the default system suppression specification file and
2317 /// populate a vector of @ref suppression_sptr with its content.
2318 ///
2319 /// The default system suppression file is located at
2320 /// $libdir/libabigail/default-libabigail.abignore.
2321 ///
2322 /// @param supprs the vector to add the suppression specifications
2323 /// read from the file to.
2324 void
load_default_system_suppressions(suppr::suppressions_type & supprs)2325 load_default_system_suppressions(suppr::suppressions_type& supprs)
2326 {
2327   string default_system_suppr_path =
2328     get_default_system_suppression_file_path();
2329 
2330   read_suppressions(default_system_suppr_path, supprs);
2331 }
2332 
2333 /// Load the default user suppression specification file and populate
2334 /// a vector of @ref suppression_sptr with its content.
2335 ///
2336 /// The default user suppression file is located at $HOME~/.abignore.
2337 ///
2338 /// @param supprs the vector to add the suppression specifications
2339 /// read from the file to.
2340 void
load_default_user_suppressions(suppr::suppressions_type & supprs)2341 load_default_user_suppressions(suppr::suppressions_type& supprs)
2342 {
2343   string default_user_suppr_path =
2344     get_default_user_suppression_file_path();
2345 
2346   read_suppressions(default_user_suppr_path, supprs);
2347 }
2348 
2349 /// Test if a given FTSENT* denotes a file with a given name.
2350 ///
2351 /// @param entry the FTSENT* to consider.
2352 ///
2353 /// @param fname the file name (or end of path) to consider.  The file
2354 /// name can also be a path that is relative to the root directory the
2355 /// current visit is started from.  The root directory is given by @p
2356 /// root_dir.
2357 ///
2358 /// @param root_dir the root dir from which the directory visit is
2359 /// being performed.
2360 ///
2361 /// @return true iff @p entry denotes a file which path ends with @p
2362 /// fname.
2363 static bool
entry_of_file_with_name(const FTSENT * entry,const string & fname,const string & root_dir)2364 entry_of_file_with_name(const FTSENT *entry,
2365 			const string& fname,
2366 			const string& root_dir)
2367 {
2368   if (entry == NULL
2369       || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2370       || entry->fts_info == FTS_ERR
2371       || entry->fts_info == FTS_NS)
2372     return false;
2373 
2374   string fpath = ::basename(entry->fts_path);
2375   if (fpath == fname)
2376     return true;
2377 
2378   fpath = trim_leading_string(entry->fts_path, root_dir);
2379   if (fpath == fname)
2380     return true;
2381 
2382   return false;
2383 }
2384 
2385 /// Find a given file under a root directory and return its absolute
2386 /// path.
2387 ///
2388 /// @param root_dir the root directory under which to look for.
2389 ///
2390 /// @param file_path_to_look_for the file to look for under the
2391 /// directory @p root_dir.
2392 ///
2393 /// @param result the resulting path to @p file_path_to_look_for.
2394 /// This is set iff the file has been found.
2395 bool
find_file_under_dir(const string & root_dir,const string & file_path_to_look_for,string & result)2396 find_file_under_dir(const string& root_dir,
2397 		    const string& file_path_to_look_for,
2398 		    string& result)
2399 {
2400   char* paths[] = {const_cast<char*>(root_dir.c_str()), 0};
2401 
2402   FTS *file_hierarchy = fts_open(paths,
2403 				 FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
2404   if (!file_hierarchy)
2405     return false;
2406 
2407   string r = root_dir;
2408   if (!string_ends_with(r, "/"))
2409     r += "/";
2410 
2411   FTSENT *entry;
2412   while ((entry = fts_read(file_hierarchy)))
2413     {
2414       if (entry_of_file_with_name(entry, file_path_to_look_for, r))
2415 	{
2416 	  result = entry->fts_path;
2417 	  return true;
2418 	}
2419       // Skip descendents of symbolic links.
2420       if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
2421 	{
2422 	  fts_set(file_hierarchy, entry, FTS_SKIP);
2423 	  continue;
2424 	}
2425     }
2426 
2427   fts_close(file_hierarchy);
2428   return false;
2429 }
2430 
2431 /// Find a given file possibly under a set of directories and return
2432 /// its absolute path.
2433 ///
2434 /// @param root_dirs the vector of root directories under which to
2435 /// look for.
2436 ///
2437 /// @param file_path_to_look_for the file to look for under the
2438 /// directory @p root_dir.
2439 ///
2440 /// @param result the resulting path to @p file_path_to_look_for.
2441 /// This is set iff the file has been found.
2442 bool
find_file_under_dirs(const vector<string> & root_dirs,const string & file_path_to_look_for,string & result)2443 find_file_under_dirs(const vector<string>& root_dirs,
2444 		     const string& file_path_to_look_for,
2445 		     string& result)
2446 {
2447   if (root_dirs.empty())
2448     return find_file_under_dir(".", file_path_to_look_for, result);
2449 
2450   for (const auto& root_dir : root_dirs)
2451     if (find_file_under_dir(root_dir, file_path_to_look_for, result))
2452       return true;
2453 
2454   return false;
2455 }
2456 
2457 /// Get the dependencies of an ABI corpus, which are found in a set of
2458 /// directories.  Note that the dependencies are listed as properties
2459 /// of the ABI corpus.
2460 ///
2461 /// If the corpus has a dependency that is not found under any of the
2462 /// given directories, then the dependency is ignored and not
2463 /// returned.
2464 ///
2465 /// @param korpus the ABI corpus to consider.
2466 ///
2467 /// @param deps_dirs the list of directories where to look for the
2468 /// dependencies.
2469 ///
2470 /// @param dependencies output parameter that is set the dependencies
2471 /// of the corpus denoted by @p korpus which are found in the
2472 /// directories @p deps_dirs.  This is set iff the function returns
2473 /// true.
2474 ///
2475 /// @return true iff some dependencies of the corpus @p korpus were
2476 /// found in directories @p deps_dirs.
2477 bool
get_dependencies(const corpus & korpus,const vector<string> & deps_dirs,set<string> & dependencies)2478 get_dependencies(const corpus&		korpus,
2479 		 const vector<string>&	deps_dirs,
2480 		 set<string>&		dependencies)
2481 {
2482   const vector<string>& set_of_needed = korpus.get_needed();
2483   if (set_of_needed.empty())
2484     return false;
2485 
2486   bool found_at_least_one_dependency =false;
2487   for (const auto& n :set_of_needed)
2488     {
2489       string dependency;
2490       if (dependencies.find(n) == dependencies.end()
2491 	  && find_file_under_dirs(deps_dirs, n, dependency))
2492 	{
2493 	  dependencies.insert(dependency);
2494 	  found_at_least_one_dependency = true;
2495 	}
2496     }
2497 
2498   return found_at_least_one_dependency;
2499 }
2500 
2501 /// For each binary of a vector of binaries, if the binary is present
2502 /// in at least one of the directories listed in a given vector,
2503 /// construct a corpus and add it to a corpus group.
2504 ///
2505 /// @param reader the reader used to read the binaries into an ABI corpus.
2506 ///
2507 /// @param binaries the vector of binaries to read and add to a corpus
2508 /// group.
2509 ///
2510 /// @param deps_dirs the vector of directories where to look for the
2511 /// binaries in @p binaries.
2512 ///
2513 /// @param group the corpus group to add the corpus.
2514 void
add_binaries_into_corpus_group(const fe_iface_sptr & reader,const vector<string> & binaries,const vector<string> & deps_dirs,corpus_group & group)2515 add_binaries_into_corpus_group(const fe_iface_sptr&	reader,
2516 			       const vector<string>&	binaries,
2517 			       const vector<string>&	deps_dirs,
2518 			       corpus_group&		group)
2519 {
2520   vector<string> bins;
2521 
2522   for (const auto& b : binaries)
2523     {
2524       string bin;
2525       if (find_file_under_dirs(deps_dirs, b, bin))
2526 	bins.push_back(bin);
2527     }
2528 
2529   for (const auto& b : bins)
2530     {
2531       if (group.has_corpus(b))
2532 	continue;
2533 
2534       reader->initialize(b);
2535       fe_iface::status stat = fe_iface::STATUS_UNKNOWN;
2536       corpus_sptr c = reader->read_corpus(stat);
2537       if (c && (stat & fe_iface::STATUS_OK))
2538 	group.add_corpus(c);
2539     }
2540 }
2541 
2542 /// For each dependency of a given corpus, if it is present in at
2543 /// least one of the directories listed in a given vector, construct a
2544 /// corpus and add it to a corpus group.
2545 ///
2546 /// @param reader the reader used to read the binaries into an ABI corpus.
2547 ///
2548 /// @param korpus the corpus to consider.
2549 ///
2550 /// @param deps_dirs the vector of directories where to look for the
2551 /// dependencies of @p korpus.
2552 ///
2553 /// @param group the corpus group to add the corpus.
2554 void
add_dependencies_into_corpus_group(const fe_iface_sptr & reader,const corpus & korpus,const vector<string> & deps_dirs,corpus_group & group)2555 add_dependencies_into_corpus_group(const fe_iface_sptr&	reader,
2556 				   const corpus&		korpus,
2557 				   const vector<string>&	deps_dirs,
2558 				   corpus_group&		group)
2559 
2560 {
2561   set<string> deps;
2562   if (!get_dependencies(korpus, deps_dirs, deps))
2563     return;
2564 
2565   for (const auto& dep: deps)
2566     {
2567       if (group.has_corpus(dep))
2568 	continue;
2569 
2570       reader->initialize(dep);
2571       fe_iface::status stat = fe_iface::STATUS_UNKNOWN;
2572       corpus_sptr c = reader->read_corpus(stat);
2573       if (c && (stat & fe_iface::STATUS_OK))
2574 	{
2575 	  group.add_corpus(c);
2576 	  add_dependencies_into_corpus_group(reader, *c, deps_dirs, group);
2577 	}
2578     }
2579 }
2580 
2581 /// Create a corpus group made of a given korpus and a set of binaries
2582 /// found in a set of directories.
2583 ///
2584 /// @param reader the reader to use to read the binaries.
2585 ///
2586 /// @param korpus the ABI corpus to add to the corpus group.
2587 ///
2588 /// @param binaries the set of binaries to add to the corpus group, if
2589 /// they are present one of the directories denoted by the vector @p
2590 /// deps_dirs.
2591 ///
2592 /// @param bins_dirs the directories where the binaries listed in @p
2593 /// binaries are to be found.
2594 ///
2595 /// @return a corpus group made of @p korpus and the binaries listed
2596 /// in @p binaries and found in at least one of the directories found
2597 /// in @p bins_dirs.
2598 corpus_group_sptr
stick_corpus_and_binaries_into_corpus_group(const fe_iface_sptr & reader,const corpus_sptr & korpus,const vector<string> & binaries,const vector<string> & bins_dirs)2599 stick_corpus_and_binaries_into_corpus_group(const fe_iface_sptr&	reader,
2600 					    const corpus_sptr&		korpus,
2601 					    const vector<string>&	binaries,
2602 					    const vector<string>&	bins_dirs)
2603 {
2604     corpus_group_sptr result (new corpus_group(korpus->get_environment(),
2605 					       korpus->get_path()));
2606     result->add_corpus(korpus);
2607 
2608     add_binaries_into_corpus_group(reader, binaries, bins_dirs, *result);
2609 
2610     return result;
2611 }
2612 
2613 /// Create a corpus group made of a given korpus and the subset of its
2614 /// dependencies that can be found found in a set of directories.
2615 ///
2616 /// @param reader the reader to use to read the binaries.
2617 ///
2618 /// @param korpus the ABI corpus to add to the corpus group along with
2619 /// its dependencies that can be found in a subset of directories.
2620 ///
2621 /// @param deps_dirs the directories where the dependencies of the ABI
2622 /// corpus denoted by @p korpus binaries are to be found.
2623 ///
2624 /// @return a corpus group made of @p korpus and the subset of its
2625 /// dependencies found in at least one of the directories denoted by
2626 /// @p deps_dirs.
2627 corpus_group_sptr
stick_corpus_and_dependencies_into_corpus_group(const fe_iface_sptr & reader,const corpus_sptr & korpus,const vector<string> & deps_dirs)2628 stick_corpus_and_dependencies_into_corpus_group(const fe_iface_sptr&	reader,
2629 						const corpus_sptr&	korpus,
2630 						const vector<string>&	deps_dirs)
2631 {
2632   corpus_group_sptr result (new corpus_group(korpus->get_environment(),
2633 					     korpus->get_path()));
2634   result->add_corpus(korpus);
2635 
2636   add_dependencies_into_corpus_group(reader, *korpus, deps_dirs, *result);
2637 
2638   return result;
2639 }
2640 
2641 /// If we were given suppression specification files or kabi whitelist
2642 /// files, this function parses those, come up with suppression
2643 /// specifications as a result, and set them to the read context.
2644 ///
2645 /// @param read_ctxt the read context to consider.
2646 ///
2647 /// @param suppr_paths paths to suppression specification files that
2648 /// we were given.  If empty, it means we were not given any
2649 /// suppression specification path.
2650 ///
2651 /// @param kabi_whitelist_paths paths to kabi whitelist files that we
2652 /// were given.  If empty, it means we were not given any kabi
2653 /// whitelist.
2654 ///
2655 /// @param supprs the suppressions specifications resulting from
2656 /// parsing the suppression specification files at @p suppr_paths and
2657 /// the kabi whitelist at @p kabi_whitelist_paths.
2658 ///
2659 /// @param opts the options to consider.
2660 static void
load_generate_apply_suppressions(elf_based_reader & rdr,vector<string> & suppr_paths,vector<string> & kabi_whitelist_paths,suppressions_type & supprs)2661 load_generate_apply_suppressions(elf_based_reader&	rdr,
2662 				 vector<string>&	suppr_paths,
2663 				 vector<string>&	kabi_whitelist_paths,
2664 				 suppressions_type&	supprs)
2665 {
2666   if (supprs.empty())
2667     {
2668       for (vector<string>::const_iterator i = suppr_paths.begin();
2669 	   i != suppr_paths.end();
2670 	   ++i)
2671 	read_suppressions(*i, supprs);
2672 
2673       const suppressions_type& wl_suppr =
2674 	  gen_suppr_spec_from_kernel_abi_whitelists(kabi_whitelist_paths);
2675 
2676       supprs.insert(supprs.end(), wl_suppr.begin(), wl_suppr.end());
2677     }
2678 
2679   rdr.add_suppressions(supprs);
2680 }
2681 
2682 /// Test if an FTSENT pointer (resulting from fts_read) represents the
2683 /// vmlinux binary.
2684 ///
2685 /// @param entry the FTSENT to consider.
2686 ///
2687 /// @return true iff @p entry is for a vmlinux binary.
2688 static bool
is_vmlinux(const FTSENT * entry)2689 is_vmlinux(const FTSENT *entry)
2690 {
2691   if (entry == NULL
2692       || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2693       || entry->fts_info == FTS_ERR
2694       || entry->fts_info == FTS_NS)
2695     return false;
2696 
2697   string fname = entry->fts_name;
2698 
2699   if (fname == "vmlinux")
2700     {
2701       string dirname;
2702       dir_name(entry->fts_path, dirname);
2703       if (string_ends_with(dirname, "compressed"))
2704 	return false;
2705 
2706       return true;
2707     }
2708 
2709   return false;
2710 }
2711 
2712 /// Test if an FTSENT pointer (resulting from fts_read) represents a a
2713 /// linux kernel module binary.
2714 ///
2715 /// @param entry the FTSENT to consider.
2716 ///
2717 /// @return true iff @p entry is for a linux kernel module binary.
2718 static bool
is_kernel_module(const FTSENT * entry)2719 is_kernel_module(const FTSENT *entry)
2720 {
2721     if (entry == NULL
2722       || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2723       || entry->fts_info == FTS_ERR
2724       || entry->fts_info == FTS_NS)
2725     return false;
2726 
2727   string fname = entry->fts_name;
2728   if (string_ends_with(fname, ".ko")
2729       || string_ends_with(fname, ".ko.xz")
2730       || string_ends_with(fname, ".ko.gz"))
2731     return true;
2732 
2733   return false;
2734 }
2735 
2736 /// Find a vmlinux and its kernel modules in a given directory tree.
2737 ///
2738 /// @param from the directory tree to start looking from.
2739 ///
2740 /// @param vmlinux_path output parameter.  This is set to the path
2741 /// where the vmlinux binary is found.  This is set iff the returns
2742 /// true and if this argument was empty to begin with.
2743 ///
2744 /// @param module_paths output parameter.  This is set to the paths of
2745 /// the linux kernel module binaries.
2746 ///
2747 /// @return true iff at least the vmlinux binary was found.
2748 static bool
find_vmlinux_and_module_paths(const string & from,string & vmlinux_path,vector<string> & module_paths)2749 find_vmlinux_and_module_paths(const string&	from,
2750 			      string		&vmlinux_path,
2751 			      vector<string>	&module_paths)
2752 {
2753   char* path[] = {const_cast<char*>(from.c_str()), 0};
2754 
2755   FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
2756   if (!file_hierarchy)
2757     return false;
2758 
2759   bool found_vmlinux = !vmlinux_path.empty();
2760   FTSENT *entry;
2761   while ((entry = fts_read(file_hierarchy)))
2762     {
2763       // Skip descendents of symbolic links.
2764       if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
2765 	{
2766 	  fts_set(file_hierarchy, entry, FTS_SKIP);
2767 	  continue;
2768 	}
2769 
2770       if (!found_vmlinux && is_vmlinux(entry))
2771 	{
2772 	  vmlinux_path = entry->fts_path;
2773 	  found_vmlinux = true;
2774 	}
2775       else if (is_kernel_module(entry))
2776 	module_paths.push_back(entry->fts_path);
2777     }
2778 
2779   fts_close(file_hierarchy);
2780 
2781   return found_vmlinux;
2782 }
2783 
2784 /// Find a vmlinux binary in a given directory tree.
2785 ///
2786 /// @param from the directory tree to start looking from.
2787 ///
2788 /// @param vmlinux_path output parameter
2789 ///
2790 /// return true iff the vmlinux binary was found
2791 static bool
find_vmlinux_path(const string & from,string & vmlinux_path)2792 find_vmlinux_path(const string&	from,
2793 		  string		&vmlinux_path)
2794 {
2795   char* path[] = {const_cast<char*>(from.c_str()), 0};
2796 
2797   FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
2798   if (!file_hierarchy)
2799     return false;
2800 
2801   bool found_vmlinux = false;
2802   FTSENT *entry;
2803   while ((entry = fts_read(file_hierarchy)))
2804     {
2805       // Skip descendents of symbolic links.
2806       if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
2807 	{
2808 	  fts_set(file_hierarchy, entry, FTS_SKIP);
2809 	  continue;
2810 	}
2811 
2812       if (!found_vmlinux && is_vmlinux(entry))
2813 	{
2814 	  vmlinux_path = entry->fts_path;
2815 	  found_vmlinux = true;
2816 	  break;
2817 	}
2818     }
2819 
2820   fts_close(file_hierarchy);
2821 
2822   return found_vmlinux;
2823 }
2824 
2825 /// Get the paths of the vmlinux and kernel module binaries under
2826 /// given directory.
2827 ///
2828 /// @param dist_root the directory under which to look for.
2829 ///
2830 /// @param debug_info_root_path the path to the directory under which
2831 /// debug info is going to be found for binaries under @p dist_root.
2832 ///
2833 /// @param vmlinux_path output parameter.  The path of the vmlinux
2834 /// binary that was found.
2835 ///
2836 /// @param module_paths output parameter.  The paths of the kernel
2837 /// module binaries that were found, sorted to impose a deterministic
2838 /// ordering.
2839 ///
2840 /// @return true if at least the path to the vmlinux binary was found.
2841 bool
get_binary_paths_from_kernel_dist(const string & dist_root,const string & debug_info_root_path,string & vmlinux_path,vector<string> & module_paths)2842 get_binary_paths_from_kernel_dist(const string&	dist_root,
2843 				  const string&	debug_info_root_path,
2844 				  string&		vmlinux_path,
2845 				  vector<string>&	module_paths)
2846 {
2847   if (!dir_exists(dist_root))
2848     return false;
2849 
2850   // For now, we assume either an Enterprise Linux or a Fedora kernel
2851   // distribution directory.
2852   //
2853   // We also take into account split debug info package for these.  In
2854   // this case, the content split debug info package is installed
2855   // under the 'debug_info_root_path' directory and its content is
2856   // accessible from <debug_info_root_path>/usr/lib/debug directory.
2857 
2858   string kernel_modules_root;
2859   string debug_info_root;
2860   if (dir_exists(dist_root + "/lib/modules"))
2861     {
2862       kernel_modules_root = dist_root + "/lib/modules";
2863       debug_info_root = debug_info_root_path.empty()
2864 	? dist_root + "/usr/lib/debug"
2865 	: debug_info_root_path;
2866     }
2867 
2868   if (dir_is_empty(debug_info_root))
2869     debug_info_root.clear();
2870 
2871   bool found = false;
2872   // If vmlinux_path is empty, we want to look for it under
2873   // debug_info_root, because this is where Enterprise Linux packages
2874   // put it.  Modules however are to be looked for under
2875   // kernel_modules_root.
2876   if (// So, Let's look for modules under kernel_modules_root ...
2877       find_vmlinux_and_module_paths(kernel_modules_root,
2878 				    vmlinux_path,
2879 				    module_paths)
2880       // ... and if vmlinux_path is empty, look for vmlinux under the
2881       // debug info root.
2882       || find_vmlinux_and_module_paths(debug_info_root,
2883 				       vmlinux_path,
2884 				       module_paths))
2885     found = true;
2886 
2887   std::sort(module_paths.begin(), module_paths.end());
2888 
2889   return found;
2890 }
2891 
2892 /// Get the path of the vmlinux binary under the given directory, that
2893 /// must have been generated either from extracting a package.
2894 ///
2895 /// @param from the directory under which to look for.
2896 ///
2897 /// @param vmlinux_path output parameter.  The path of the vmlinux
2898 /// binary that was found.
2899 ///
2900 /// @return true if the path to the vmlinux binary was found.
2901 bool
get_vmlinux_path_from_kernel_dist(const string & from,string & vmlinux_path)2902 get_vmlinux_path_from_kernel_dist(const string&	from,
2903 				  string&		vmlinux_path)
2904 {
2905     if (!dir_exists(from))
2906     return false;
2907 
2908   // For now, we assume the possibility of having either an Enterprise
2909   // Linux or a Fedora kernel distribution directory.  In those cases,
2910   // the vmlinux binary is located under the /lib/modules
2911   // sub-directory.  So we might as well save some time by picking it
2912   // from there directly.
2913 
2914     string dist_root = from;
2915   if (dir_exists(dist_root + "/lib/modules"))
2916     dist_root += "/lib/modules";
2917 
2918   bool found = false;
2919   if (find_vmlinux_path(dist_root, vmlinux_path))
2920     found = true;
2921 
2922   return found;
2923 }
2924 
2925 /// Get the paths of the vmlinux and kernel module binaries under
2926 /// given directory.
2927 ///
2928 /// @param dist_root the directory under which to look for.
2929 ///
2930 /// @param vmlinux_path output parameter.  The path of the vmlinux
2931 /// binary that was found.
2932 ///
2933 /// @param module_paths output parameter.  The paths of the kernel
2934 /// module binaries that were found.
2935 ///
2936 /// @return true if at least the path to the vmlinux binary was found.
2937 bool
get_binary_paths_from_kernel_dist(const string & dist_root,string & vmlinux_path,vector<string> & module_paths)2938 get_binary_paths_from_kernel_dist(const string&	dist_root,
2939 				  string&		vmlinux_path,
2940 				  vector<string>&	module_paths)
2941 {
2942   string debug_info_root_path;
2943   return get_binary_paths_from_kernel_dist(dist_root,
2944 					   debug_info_root_path,
2945 					   vmlinux_path,
2946 					   module_paths);
2947 }
2948 
2949 /// It builds a @ref corpus_group made of vmlinux kernel file and
2950 /// the kernel modules found under @p root directory and under its
2951 /// sub-directories, recursively.
2952 ///
2953 /// @param rdr the raeder that should be used to extract the debug
2954 /// infomation from the linux kernel and its modules used to build
2955 /// the corpora @p group.
2956 ///
2957 /// @param the group @ref corpus_group to be built.
2958 ///
2959 /// @param vmlinux the path to the vmlinux binary.
2960 ///
2961 /// @param modules a vector with the paths to the linux kernel
2962 /// modules.
2963 ///
2964 /// @param root the path of the directory under which the kernel
2965 /// kernel modules were found.
2966 ///
2967 /// @param di_root the directory in aboslute path which debug
2968 /// info is to be found for binaries under director @p root
2969 ///
2970 /// @param suppr_paths the paths to the suppression specifications to
2971 /// apply while loading the binaries.
2972 ///
2973 /// @param kabi_wl_path the paths to the kabi whitelist files to take
2974 /// into account while loading the binaries.
2975 ///
2976 /// @param supprs the suppressions resulting from parsing the
2977 /// suppression specifications at @p suppr_paths.  This is set by this
2978 /// function.
2979 ///
2980 /// @param verbose true if the function has to emit some verbose
2981 /// messages.
2982 ///
2983 /// @param t time to trace time spent in each step.
2984 ///
2985 /// @param env the environment to create the corpus_group in.
2986 static void
load_vmlinux_corpus(elf_based_reader_sptr rdr,corpus_group_sptr & group,const string & vmlinux,vector<string> & modules,const string & root,vector<char ** > & di_roots,vector<string> & suppr_paths,vector<string> & kabi_wl_paths,suppressions_type & supprs,bool verbose,timer & t,environment & env)2987 load_vmlinux_corpus(elf_based_reader_sptr rdr,
2988                     corpus_group_sptr&  group,
2989                     const string&       vmlinux,
2990                     vector<string>&     modules,
2991                     const string&       root,
2992                     vector<char**>&     di_roots,
2993                     vector<string>&     suppr_paths,
2994                     vector<string>&     kabi_wl_paths,
2995                     suppressions_type&  supprs,
2996                     bool                verbose,
2997                     timer&              t,
2998                     environment&        env)
2999 {
3000   abigail::fe_iface::status status = abigail::fe_iface::STATUS_OK;
3001   rdr->options().do_log = verbose;
3002 
3003   t.start();
3004   load_generate_apply_suppressions(*rdr, suppr_paths,
3005                                    kabi_wl_paths, supprs);
3006   t.stop();
3007 
3008   if (verbose)
3009     std::cerr << "loaded white list and generated suppr spec in: "
3010      << t
3011      << "\n";
3012 
3013   group.reset(new corpus_group(env, root));
3014 
3015   rdr->corpus_group(group);
3016 
3017   if (verbose)
3018     std::cerr << "reading kernel binary '"
3019      << vmlinux << "' ...\n" << std::flush;
3020 
3021   // Read the vmlinux corpus and add it to the group.
3022   t.start();
3023   rdr->read_and_add_corpus_to_group(*group, status);
3024   t.stop();
3025 
3026   if (verbose)
3027     std::cerr << vmlinux
3028      << " reading DONE:"
3029      << t << "\n";
3030 
3031   if (group->is_empty())
3032     return;
3033 
3034   // Now add the corpora of the modules to the corpus group.
3035   int total_nb_modules = modules.size();
3036   int cur_module_index = 1;
3037   for (vector<string>::const_iterator m = modules.begin();
3038        m != modules.end();
3039        ++m, ++cur_module_index)
3040     {
3041       if (verbose)
3042         std::cerr << "reading module '"
3043          << *m << "' ("
3044          << cur_module_index
3045          << "/" << total_nb_modules
3046          << ") ... " << std::flush;
3047 
3048       rdr->initialize(*m, di_roots,
3049                       /*read_all_types=*/false,
3050                       /*linux_kernel_mode=*/true);
3051 
3052       load_generate_apply_suppressions(*rdr, suppr_paths,
3053                                        kabi_wl_paths, supprs);
3054 
3055       rdr->corpus_group(group);
3056 
3057       t.start();
3058       rdr->read_and_add_corpus_to_group(*group, status);
3059       t.stop();
3060       if (verbose)
3061         std::cerr << "module '"
3062          << *m
3063          << "' reading DONE: "
3064          << t << "\n";
3065     }
3066 }
3067 
3068 /// Walk a given directory and build an instance of @ref corpus_group
3069 /// from the vmlinux kernel binary and the linux kernel modules found
3070 /// under that directory and under its sub-directories, recursively.
3071 ///
3072 /// The main corpus of the @ref corpus_group is made of the vmlinux
3073 /// binary.  The other corpora are made of the linux kernel binaries.
3074 ///
3075 /// @param root the path of the directory under which the kernel
3076 /// kernel modules are to be found.  The vmlinux can also be found
3077 /// somewhere under that directory, but if it's not in there, its path
3078 /// can be set to the @p vmlinux_path parameter.
3079 ///
3080 /// @param debug_info_root the directory under which debug info is to
3081 /// be found for binaries under director @p root.
3082 ///
3083 /// @param vmlinux_path the path to the vmlinux binary, if that binary
3084 /// is not under the @p root directory.  If this is empty, then it
3085 /// means the vmlinux binary is to be found under the @p root
3086 /// directory.
3087 ///
3088 /// @param suppr_paths the paths to the suppression specifications to
3089 /// apply while loading the binaries.
3090 ///
3091 /// @param kabi_wl_path the paths to the kabi whitelist files to take
3092 /// into account while loading the binaries.
3093 ///
3094 /// @param supprs the suppressions resulting from parsing the
3095 /// suppression specifications at @p suppr_paths.  This is set by this
3096 /// function.
3097 ///
3098 /// @param verbose true if the function has to emit some verbose
3099 /// messages.
3100 ///
3101 /// @param env the environment to create the corpus_group in.
3102 ///
3103 /// @param requested_fe_kind the kind of front-end requested by the
3104 /// user.
3105 corpus_group_sptr
build_corpus_group_from_kernel_dist_under(const string & root,const string debug_info_root,const string & vmlinux_path,vector<string> & suppr_paths,vector<string> & kabi_wl_paths,suppressions_type & supprs,bool verbose,environment & env,corpus::origin requested_fe_kind)3106 build_corpus_group_from_kernel_dist_under(const string&	root,
3107 					  const string		debug_info_root,
3108 					  const string&	vmlinux_path,
3109 					  vector<string>&	suppr_paths,
3110 					  vector<string>&	kabi_wl_paths,
3111 					  suppressions_type&	supprs,
3112 					  bool			verbose,
3113 					  environment&		env,
3114 					  corpus::origin	requested_fe_kind)
3115 {
3116   string vmlinux = vmlinux_path;
3117   corpus_group_sptr group;
3118   vector<string> modules;
3119 
3120   if (verbose)
3121     std::cerr << "Analysing kernel dist root '"
3122 	      << root
3123 	      << "' with vmlinux path: '"
3124 	      << vmlinux_path
3125 	      << "' ... " << std::flush;
3126 
3127   timer t;
3128 
3129   t.start();
3130   bool got_binary_paths =
3131     get_binary_paths_from_kernel_dist(root, debug_info_root, vmlinux, modules);
3132   t.stop();
3133 
3134   if (verbose)
3135     std::cerr << "DONE: " << t << "\n";
3136 
3137   if (got_binary_paths)
3138     {
3139       shared_ptr<char> di_root =
3140 	make_path_absolute(debug_info_root.c_str());
3141       char *di_root_ptr = di_root.get();
3142       vector<char**> di_roots;
3143       di_roots.push_back(&di_root_ptr);
3144 
3145 #ifdef WITH_CTF
3146       shared_ptr<char> di_root_ctf;
3147       if (requested_fe_kind & corpus::CTF_ORIGIN)
3148         {
3149           di_root_ctf = make_path_absolute(root.c_str());
3150           char *di_root_ctf_ptr = di_root_ctf.get();
3151           di_roots.push_back(&di_root_ctf_ptr);
3152         }
3153 #endif
3154 
3155       abigail::elf_based_reader_sptr reader =
3156         create_best_elf_based_reader(vmlinux,
3157                                      di_roots,
3158                                      env,
3159 				     requested_fe_kind,
3160                                      /*read_all_types=*/false,
3161                                      /*linux_kernel_mode=*/true);
3162       ABG_ASSERT(reader);
3163       load_vmlinux_corpus(reader, group, vmlinux,
3164                           modules, root, di_roots,
3165                           suppr_paths, kabi_wl_paths,
3166                           supprs, verbose, t, env);
3167     }
3168 
3169   return group;
3170 }
3171 
3172 /// Create the best elf based reader (or front-end), given an ELF
3173 /// file.
3174 ///
3175 /// This function looks into the ELF file; depending on the kind of
3176 /// debug info it contains and on the request of the user, the "best"
3177 /// front-end is created.
3178 ///
3179 /// If the user requested the use of the CTF front-end, then, if the
3180 /// file contains CTF debug info, the CTF front-end is created,
3181 /// assuming libabigail is built with CTF support.
3182 ///
3183 /// If the binary ONLY has CTF debug info, then CTF front-end is
3184 /// created, even if the user hasn't explicitly requested the creation
3185 /// of the CTF front-end.
3186 ///
3187 /// Otherwise, by default, the DWARF front-end is created.
3188 ///
3189 /// @param elf_file_path a path to the ELF file to consider
3190 ///
3191 /// @param debug_info_root_paths a vector of the paths where to look
3192 /// for debug info, if applicable.
3193 ///
3194 /// @param env the environment to use for the front-end.
3195 ///
3196 /// @param requested_fe_kind the kind of front-end specifically
3197 /// requested by the user. At the moment, only the CTF front-end can
3198 /// be requested, using the "--ctf" command line option on some tools
3199 /// using the library.
3200 ///
3201 /// @param show_all_types option to be passed to elf based readers.
3202 ///
3203 /// @param linux_kernel_mode option to bed passed to elf based readers,
3204 ///
3205 /// @return the ELF based Reader that is better adapted for the binary
3206 /// designated by @p elf_file_path.
3207 elf_based_reader_sptr
create_best_elf_based_reader(const string & elf_file_path,const vector<char ** > & debug_info_root_paths,environment & env,corpus::origin requested_fe_kind,bool show_all_types,bool linux_kernel_mode)3208 create_best_elf_based_reader(const string& elf_file_path,
3209 			     const vector<char**>& debug_info_root_paths,
3210 			     environment& env,
3211 			     corpus::origin requested_fe_kind,
3212 			     bool show_all_types,
3213 			     bool linux_kernel_mode)
3214 {
3215   elf_based_reader_sptr result;
3216   if (guess_file_type(elf_file_path) != FILE_TYPE_ELF)
3217     return result;
3218 
3219   if (requested_fe_kind & corpus::CTF_ORIGIN)
3220     {
3221 #ifdef WITH_CTF
3222       if (file_has_ctf_debug_info(elf_file_path, debug_info_root_paths))
3223 	result = ctf::create_reader(elf_file_path, debug_info_root_paths, env);
3224 #endif
3225     }
3226   else if (requested_fe_kind & corpus::BTF_ORIGIN)
3227     {
3228 #ifdef WITH_BTF
3229       if (file_has_btf_debug_info(elf_file_path, debug_info_root_paths))
3230 	result = btf::create_reader(elf_file_path, debug_info_root_paths, env);
3231 #endif
3232     }
3233   else
3234     {
3235       // The user hasn't formally requested the use of the CTF front-end.
3236 #ifdef WITH_CTF
3237       if (!file_has_dwarf_debug_info(elf_file_path, debug_info_root_paths)
3238 	  && file_has_ctf_debug_info(elf_file_path, debug_info_root_paths))
3239 	// The file has CTF debug info and no DWARF, let's use the CTF
3240 	// front end even if it wasn't formally requested by the user.
3241 	result = ctf::create_reader(elf_file_path, debug_info_root_paths, env);
3242 #endif
3243 
3244 #ifdef WITH_BTF
3245       if (!file_has_dwarf_debug_info(elf_file_path, debug_info_root_paths)
3246 	  && file_has_btf_debug_info(elf_file_path, debug_info_root_paths))
3247 	// The file has BTF debug info and no BTF, let's use the BTF
3248 	// front-end even if it wasn't formally requested by the user.
3249 	result = btf::create_reader(elf_file_path, debug_info_root_paths, env);
3250 #endif
3251     }
3252 
3253   if (!result)
3254     {
3255       // This is the default case.  At worst, the DWARF reader knows
3256       // how to handle just ELF data for the case where there is no
3257       // DWARF debug info present.
3258       result = dwarf::create_reader(elf_file_path,
3259 				    debug_info_root_paths,
3260 				    env,
3261 				    show_all_types,
3262 				    linux_kernel_mode);
3263     }
3264 
3265   return result;
3266 }
3267 
3268 }//end namespace tools_utils
3269 
3270 using abigail::ir::function_decl;
3271 
3272 /// Dump (to the standard error stream) two sequences of strings where
3273 /// each string represent one of the functions in the two sequences of
3274 /// functions given in argument to this function.
3275 ///
3276 /// @param a_begin the begin iterator for the first input sequence of
3277 /// functions.
3278 ///
3279 /// @parm a_end the end iterator for the first input sequence of
3280 /// functions.
3281 ///
3282 /// @param b_begin the begin iterator for the second input sequence of
3283 /// functions.
3284 ///
3285 /// @param b_end the end iterator for the second input sequence of functions.
3286 void
dump_functions_as_string(std::vector<function_decl * >::const_iterator a_begin,std::vector<function_decl * >::const_iterator a_end,std::vector<function_decl * >::const_iterator b_begin,std::vector<function_decl * >::const_iterator b_end)3287 dump_functions_as_string(std::vector<function_decl*>::const_iterator a_begin,
3288 			 std::vector<function_decl*>::const_iterator a_end,
3289 			 std::vector<function_decl*>::const_iterator b_begin,
3290 			 std::vector<function_decl*>::const_iterator b_end)
3291 {abigail::fns_to_str(a_begin, a_end, b_begin, b_end, std::cerr);}
3292 
3293 /// Dump (to the standard error output stream) a pretty representation
3294 /// of the signatures of two sequences of functions.
3295 ///
3296 /// @param a_begin the start iterator of the first input sequence of functions.
3297 ///
3298 /// @param a_end the end iterator of the first input sequence of functions.
3299 ///
3300 /// @param b_begin the start iterator of the second input sequence of functions.
3301 ///
3302 /// @param b_end the end iterator of the second input sequence of functions.
3303 void
dump_function_names(std::vector<function_decl * >::const_iterator a_begin,std::vector<function_decl * >::const_iterator a_end,std::vector<function_decl * >::const_iterator b_begin,std::vector<function_decl * >::const_iterator b_end)3304 dump_function_names(std::vector<function_decl*>::const_iterator a_begin,
3305 		    std::vector<function_decl*>::const_iterator a_end,
3306 		    std::vector<function_decl*>::const_iterator b_begin,
3307 		    std::vector<function_decl*>::const_iterator b_end)
3308 {
3309   std::vector<function_decl*>::const_iterator i;
3310   std::ostream& o = std::cerr;
3311   for (i = a_begin; i != a_end; ++i)
3312     o << (*i)->get_pretty_representation() << "\n";
3313 
3314   o << "  ->|<-  \n";
3315   for (i = b_begin; i != b_end; ++i)
3316     o << (*i)->get_pretty_representation() << "\n";
3317   o << "\n";
3318 }
3319 
3320 /// Compare two functions that are in a vector of functions.
3321 ///
3322 /// @param an iterator to the beginning of the the sequence of functions.
3323 ///
3324 /// @param f1_index the index of the first function to compare.
3325 ///
3326 /// @param f2_inde the index of the second function to compare
3327 bool
compare_functions(vector<function_decl * >::const_iterator base,unsigned f1_index,unsigned f2_index)3328 compare_functions(vector<function_decl*>::const_iterator base,
3329 		  unsigned f1_index, unsigned f2_index)
3330 {
3331   function_decl* fn1 = base[f1_index];
3332   function_decl* fn2 = base[f2_index];
3333 
3334   return *fn1 == *fn2;
3335 }
3336 
3337 }//end namespace abigail
3338