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