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