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