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