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