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