libabigail
Loading...
Searching...
No Matches
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>
61ABG_BEGIN_EXPORT_DECLARATIONS
62
63#include <abg-ir.h>
64#include "abg-config.h"
65#include "abg-tools-utils.h"
66
67ABG_END_EXPORT_DECLARATIONS
68// </headers defining libabigail's API>
69
70using std::string;
71
72namespace abigail
73{
74
75using namespace abigail::suppr;
76using namespace abigail::ini;
77
78/// @brief Namespace for a set of utility function used by tools based
79/// on libabigail.
80namespace 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.
90void
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.
102const 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.
161bool
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.
174bool
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.
189bool
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.
200struct 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.
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.
229bool
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.
243bool
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.
255time_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.
273bool
274timer::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.
299string
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.
338ostream&
339operator<<(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.
354static bool
355get_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.
364bool
365file_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
377bool
378dir_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
384bool
385dir_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.
410bool
411is_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.
438bool
439dir_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.
462bool
463file_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.
498bool
499file_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.
542bool
543file_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.
564bool
565is_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
590static const char* ANONYMOUS_STRUCT_INTERNAL_NAME = "__anonymous_struct__";
591static const char* ANONYMOUS_UNION_INTERNAL_NAME = "__anonymous_union__";
592static const char* ANONYMOUS_ENUM_INTERNAL_NAME = "__anonymous_enum__";
593static const char* ANONYMOUS_SUBRANGE_INTERNAL_NAME = "__anonymous_range__";
594
595static int ANONYMOUS_STRUCT_INTERNAL_NAME_LEN =
596 strlen(ANONYMOUS_STRUCT_INTERNAL_NAME);
597
598static int ANONYMOUS_UNION_INTERNAL_NAME_LEN =
599 strlen(ANONYMOUS_UNION_INTERNAL_NAME);
600
601static 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.
607const 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.
614const 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.
621const 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.
628const 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.
663bool
664decl_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.
719bool
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).
750bool
751dir_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).
779bool
780base_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;
804void
805real_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.
826bool
827ensure_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.
856bool
857ensure_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.
882ostream&
883emit_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.
897bool
898check_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.
925bool
926check_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.
950bool
951string_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.
967bool
968string_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.
988bool
989string_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.
1013bool
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.
1041bool
1042split_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.
1091bool
1092string_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.
1121static bool
1122common_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.
1150bool
1151sorted_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.
1197string
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.
1209string
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.
1228bool
1229execute_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.
1273void
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.
1303bool
1304get_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.
1332string
1333trim_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.
1357bool
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
1376static std::regex&
1377get_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.
1391bool
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.
1441string
1442trim_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.
1456void
1457convert_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.
1467struct 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.
1506temp_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.
1514bool
1515temp_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.
1522const char*
1523temp_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.
1539std::fstream&
1540temp_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.
1551temp_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.
1563size_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.
1580string
1582{
1583 std::ostringstream o;
1584 o << get_random_number();
1585
1586 return o.str();
1587}
1588
1589ostream&
1590operator<<(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.
1658static compression_kind
1659is_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.
1672file_type
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.
1806static shared_ptr<std::streambuf>
1807get_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.
1837guess_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.
1933bool
1934get_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.
1963bool
1964get_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.
1997bool
1998get_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.
2050bool
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.
2080bool
2081rpm_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.
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.
2110bool
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.
2136struct 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.
2154std::shared_ptr<char>
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.
2182string
2183make_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.
2214static void
2215handle_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.
2248static void
2249handle_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.
2281static void
2282gen_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.
2314gen_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.
2346gen_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.
2368gen_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.
2489string
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.
2508string
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.
2538void
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.
2554void
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.
2577static bool
2578entry_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.
2609bool
2610find_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.
2657bool
2658find_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.
2692bool
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.
2729void
2730add_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.
2769void
2770add_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.
2813corpus_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.
2842corpus_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.
2875static void
2876load_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.
2903static bool
2904is_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.
2933static bool
2934is_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.
2957static bool
2958is_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.
2977static bool
2978find_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
3020static bool
3021find_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.
3061bool
3062get_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.
3114bool
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.
3174bool
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.
3210bool
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.
3259static void
3260load_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.
3393corpus_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.
3493elf_based_reader_sptr
3494create_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.
3561struct 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.
3582xz_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.
3606std::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.
3689void
3690dump_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.
3706void
3707dump_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
3730bool
3731compare_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
This file contains the declarations of the front-end to analyze the BTF information contained in an E...
This file contains the declarations of the entry points to de-serialize an instance of abigail::corpu...
This file contains the declarations of the entry points to de-serialize an instance of abigail::corpu...
#define ABG_ASSERT(cond)
This is a wrapper around the 'assert' glibc call. It allows for its argument to have side effects,...
Definition abg-fwd.h:1743
Types of the main internal representation of libabigail.
Wrappers around regex types and functions.
#define ABG_ASSERT_NOT_REACHED
A macro that expands to aborting the program when executed.
This is the interface an ELF reader.
const Dwarf * dwarf_debug_info() const
Getter of the handle used to access DWARF information from the current ELF file.
const Elf_Scn * find_ctf_section() const
Find and return a pointer to the the CTF section.
const Elf_Scn * find_btf_section() const
Find and return a pointer to the BTF section of the current ELF file.
The common interface of readers based on ELF.
status
The status of the fe_iface::read_corpus call.
@ STATUS_OK
This status is for when the call went OK.
@ STATUS_UNKNOWN
The status is in an unknown state.
void add_suppressions(const suppr::suppressions_type &)
Add suppressions specifications to the set of suppressions to be used during the construction of the ...
The abstraction of the structured content of an .ini file. This roughly follows what is explained at ...
Definition abg-ini.h:322
vector< section_sptr > sections_type
A convenience typedef for a vector of config::section_sptr.
Definition abg-ini.h:332
const sections_type & get_sections() const
Definition abg-ini.cc:1677
Abstraction of a group of corpora.
Definition abg-corpus.h:386
bool has_corpus(const string &)
Test if a corpus of a given path has been added to the group.
void add_corpus(const corpus_sptr &)
Add a new corpus to the current instance of corpus_group.
This is the abstraction of a set of translation units (themselves seen as bundles of unitary abi arte...
Definition abg-corpus.h:25
origin
This abstracts where the corpus comes from. That is, either it has been read from the native xml form...
Definition abg-corpus.h:51
const vector< string > & get_needed() const
Getter of the needed property of the corpus.
This is an abstraction of the set of resources necessary to manage several aspects of the internal re...
Definition abg-ir.h:148
Abstraction for a function declaration.
Definition abg-ir.h:3165
Abstraction of a function suppression specification.
Abstraction of a type suppression specification.
The abstraction of a variable suppression specification.
A type used to time various part of the libabigail system.
~timer()
Destructor of the timer type.
string value_as_string() const
Get the elapsed time as a human-readable string.
time_t value_in_seconds() const
Get the elapsed time in seconds.
bool stop()
Stop the timer.
timer(kind k=DEFAULT_TIMER_KIND)
Constructor of the timer type.
bool start()
Start the timer.
@ START_ON_INSTANTIATION_TIMER_KIND
This kind of timer starts upon instantiation.
bool value(time_t &hours, time_t &minutes, time_t &seconds, time_t &milliseconds) const
Get the elapsed time in hour:minutes:seconds:milliseconds.
~xz_decompressor_type()
Destructor of the xz_decompressor_type class.
int_type underflow() override
The implementation of the virtual protected std:streambuf::underflow method. This method is invoked b...
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.
ostream & operator<<(ostream &o, diff_category c)
Serialize an instance of diff_category to an output 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.
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.
Namespace for handling ini-style files.
Definition abg-ini.cc:33
bool read_config(istream &input, config &conf)
Parse an ini config file from an input stream.
Definition abg-ini.cc:1747
shared_ptr< simple_property > simple_property_sptr
Convenience typedef for a shared_ptr to an simple_property.
Definition abg-ini.h:206
simple_property * is_simple_property(const property *p)
Tests if a property is a simple property.
Definition abg-ini.cc:619
std::string generate_from_strings(const std::vector< std::string > &strs)
Generate a regex pattern equivalent to testing set membership.
Definition abg-regex.cc:88
an engine to suppress the parts of the result of comparing two sets of ABI artifacts.
shared_ptr< variable_suppression > variable_suppression_sptr
A convenience typedef for a shared pointer to variable_suppression.
const char * get_opaque_types_suppr_spec_label()
vector< suppression_sptr > suppressions_type
Convenience typedef for a vector of suppression_sptr.
Definition abg-fwd.h:1687
shared_ptr< function_suppression > function_suppression_sptr
Convenience typedef for a shared pointer to function_suppression.
shared_ptr< type_suppression > type_suppression_sptr
Convenience typedef for a shared pointer to type_suppression.
void read_suppressions(std::istream &input, suppressions_type &suppressions)
Read suppressions specifications from an input stream.
bool check_file(const string &path, ostream &out, const string &prog_name)
Check if a given path exists and is readable.
bool rpm_contains_file(const string &rpm_path, const string &file_name)
Test if an RPM package contains a given file.
string get_default_system_suppression_file_path()
Get the path to the default system suppression file.
type_suppression_sptr gen_suppr_spec_from_headers(const vector< string > &headers_root_dirs, const vector< string > &header_files)
Generate a type suppression specification that suppresses ABI changes for types defined in source fil...
bool split_string(const string &input_string, const string &delims, vector< string > &result)
Split a given string into substrings, given some delimiters.
bool string_ends_with(const string &str, const string &suffix)
Test if a given string ends with a particular suffix.
bool find_file_under_dir(const string &root_dir, const string &file_path_to_look_for, string &result)
Find a given file under a root directory and return its absolute path.
bool string_is_ascii(const string &str)
Test if a string is made of ascii characters.
ostream & emit_prefix(const string &prog_name, ostream &out)
Emit a prefix made of the name of the program which is emitting a message to an output stream.
bool dir_name(string const &path, string &dir_name, bool keep_separator_at_end)
Return the directory part of a file path.
bool base_name(string const &path, string &file_name)
Return the file name part of a file part.
shared_ptr< temp_file > temp_file_sptr
Convenience typedef for a shared_ptr to temp_file.
bool get_vmlinux_path_from_kernel_dist(const string &from, string &vmlinux_path)
Get the path of the vmlinux binary under the given directory, that must have been generated either fr...
bool normalize_litterals(string &str)
Normalize the numerical litteral in a string.
bool remove_white_spaces(string &str)
Remove white spaces from a string.
void initialize()
This function needs to be called before any libabigail function.
void load_default_user_suppressions(suppr::suppressions_type &supprs)
Load the default user suppression specification file and populate a vector of suppression_sptr with i...
const char * get_anonymous_subrange_internal_name_prefix()
Getter of the prefix for the name of anonymous range.
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.
const char * get_anonymous_enum_internal_name_prefix()
Getter of the prefix for the name of anonymous enums.
std::shared_ptr< char > make_path_absolute(const char *p)
Return a copy of the path given in argument, turning it into an absolute path by prefixing it with th...
const char * get_anonymous_struct_internal_name_prefix()
Getter of the prefix for the name of anonymous structs.
string trim_white_space(const string &str)
Remove spaces at the beginning and at the end of a given string.
corpus_group_sptr stick_corpus_and_binaries_into_corpus_group(const fe_iface_sptr &reader, const corpus_sptr &korpus, const vector< string > &binaries, const vector< string > &bins_dirs)
Create a corpus group made of a given korpus and a set of binaries found in a set of directories.
bool check_dir(const string &path, ostream &out, const string &prog_name)
Check if a given path exists, is readable and is a directory.
const char * get_anonymous_union_internal_name_prefix()
Getter of the prefix for the name of anonymous unions.
bool string_begins_with(const string &str, const string &prefix)
Test if a given string begins with a particular prefix.
bool abidiff_status_has_incompatible_abi_change(abidiff_status s)
Test if an instance of.
const char * get_system_libdir()
Get the value of $libdir variable of the autotools build system. This is where shared libraries are u...
bool get_binary_paths_from_kernel_dist(const string &dist_root, const string &debug_info_root_path, string &vmlinux_path, vector< string > &module_paths)
Get the paths of the vmlinux and kernel module binaries under given directory.
bool get_dependencies(const corpus &korpus, const vector< string > &deps_dirs, set< string > &dependencies)
Get the dependencies of an ABI corpus, which are found in a set of directories. Note that the depende...
bool get_dsos_provided_by_rpm(const string &rpm_path, set< string > &provided_dsos)
Get the SONAMEs of the DSOs advertised as being "provided" by a given RPM. That set can be considered...
bool ensure_dir_path_created(const string &dir_path)
Ensures #dir_path is a directory and is created. If #dir_path is not created, this function creates i...
bool get_rpm_name(const string &str, string &name)
Get the package name of an rpm package.
bool execute_command_and_get_output(const string &cmd, vector< string > &lines)
Execute a shell command and returns its output.
abidiff_status & operator|=(abidiff_status &l, abidiff_status r)
The |= operator.
bool is_dir(const string &path)
Tests if a given path is a directory or a symbolic link to a directory.
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.
bool find_file_under_dirs(const vector< string > &root_dirs, const string &file_path_to_look_for, string &result)
Find a given file possibly under a set of directories and return its absolute path.
bool dir_exists(const string &path)
Test that a given directory exists.
bool string_is_ascii_identifier(const string &str)
Test if a string is made of ascii characters which are identifiers acceptable in C or C++ programs.
ostream & operator<<(ostream &o, const timer &t)
Streaming operator for the timer type.
bool file_is_kernel_package(const string &file_path, file_type file_type)
Tests if a given file name designates a kernel package.
abidiff_status operator|(abidiff_status l, abidiff_status r)
The bitwise 'OR' operator for abidiff_status bit masks.
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.
string get_library_version_string()
Return the version string of the library.
bool dir_is_empty(const string &path)
Test if a given directory exists and is empty.
bool abidiff_status_has_abi_change(abidiff_status s)
Test if an instance of.
bool abidiff_status_has_error(abidiff_status s)
Test if an instance of.
bool get_deb_name(const string &str, string &name)
Get the package name of a .deb package.
bool maybe_get_symlink_target_file_path(const string &file_path, string &target_path)
If a given file is a symbolic link, get the canonicalized absolute path to the target file.
abidiff_status
Exit status for abidiff and abicompat tools.
@ ABIDIFF_ABI_INCOMPATIBLE_CHANGE
This bit is set if the ABIs being compared are different *and* are incompatible.
@ ABIDIFF_ABI_CHANGE
This bit is set if the ABIs being compared are different.
@ ABIDIFF_USAGE_ERROR
This bit is set if the tool is invoked in an non appropriate manner.
@ ABIDIFF_ERROR
This bit is set if there is an application error.
bool is_regular_file(const string &path)
Test if path is a path to a regular file or a symbolic link to a regular file.
bool file_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 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.
bool file_exists(const string &path)
Tests whether a path exists;.
compression_kind
The kind of compression we want a de-compression std::streambuf for.
@ COMPRESSION_KIND_XZ
The LZMA compression (used by the xz tool).
file_type
The different types of files understood the bi* suite of tools.
@ FILE_TYPE_RPM
An RPM (.rpm) binary file.
@ FILE_TYPE_NATIVE_BI
The native xml file format representing a translation unit.
@ FILE_TYPE_XZ
The XZ (lzma) compresson scheme.
@ FILE_TYPE_ELF
An elf file. Read this kind of file should yield an abigail::corpus type.
@ FILE_TYPE_DEB
A DEB (.deb) binary file.
@ FILE_TYPE_UNKNOWN
A file type we don't know about.
@ FILE_TYPE_TAR
A tar archive. The archive can be compressed with the popular compression schemes recognized by GNU t...
@ FILE_TYPE_DIR
A plain directory.
@ FILE_TYPE_AR
An archive (AR) file.
@ FILE_TYPE_SRPM
An SRPM (.src.rpm) file.
bool string_suffix(const string &input_string, const string &prefix, string &suffix)
Get the suffix of a string, given a prefix to consider.
void add_binaries_into_corpus_group(const fe_iface_sptr &reader, const vector< string > &binaries, const vector< string > &deps_dirs, corpus_group &group)
For each binary of a vector of binaries, if the binary is present in at least one of the directories ...
bool ensure_parent_dir_created(const string &path)
Ensures that the parent directory of #path is created.
corpus_group_sptr build_corpus_group_from_kernel_dist_under(const string &root, const string debug_info_root, const string &vmlinux_path, vector< string > &suppr_paths, vector< string > &kabi_wl_paths, suppressions_type &supprs, bool verbose, environment &env, corpus::origin requested_fe_kind)
Walk a given directory and build an instance of corpus_group from the vmlinux kernel binary and the l...
void convert_char_stars_to_char_star_stars(const vector< char * > &char_stars, vector< char ** > &char_star_stars)
Convert a vector<char*> into a vector<char**>.
bool sorted_strings_common_prefix(vector< string > &input_strings, string &prefix)
Find the prefix common to a *SORTED* vector of strings.
void get_comma_separated_args_of_option(const string &input_str, const string &option, vector< string > &arguments)
Get a vector of arguments from a string containing a comma-separated list of those arguments.
void real_path(const string &path, string &result)
Return the real path of a given path.
abidiff_status operator&(abidiff_status l, abidiff_status r)
The bitwise 'AND' operator for abidiff_status bit masks.
void add_dependencies_into_corpus_group(const fe_iface_sptr &reader, const corpus &korpus, const vector< string > &deps_dirs, corpus_group &group)
For each dependency of a given corpus, if it is present in at least one of the directories listed in ...
bool file_is_kernel_debuginfo_package(const string &file_name, file_type file_type)
Tests if a given file name designates a kernel debuginfo package.
void load_default_system_suppressions(suppr::suppressions_type &supprs)
Load the default system suppression specification file and populate a vector of suppression_sptr with...
string get_abixml_version_string()
Return the version string for the ABIXML format.
string get_random_number_as_string()
Get a pseudo random number as string.
bool decl_names_equal(const string &l, const string &r)
Compare two fully qualified decl names by taking into account that they might have compontents that a...
bool get_rpm_arch(const string &str, string &arch)
Get the architecture string from the NVR of an rpm.
string get_default_user_suppression_file_path()
Get the path to the default user suppression file.
string trim_leading_string(const string &from, const string &to_trim)
Remove a string of pattern in front of a given string.
corpus_group_sptr stick_corpus_and_dependencies_into_corpus_group(const fe_iface_sptr &reader, const corpus_sptr &korpus, const vector< string > &deps_dirs)
Create a corpus group made of a given korpus and the subset of its dependencies that can be found fou...
file_type guess_file_type(istream &in)
Guess the type of the content of an input stream.
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.
bool dir_contains_ctf_archive(const string &directory, const string &archive_prefix)
Test if a directory contains a CTF archive.
size_t get_random_number()
Get a pseudo random number.
void initialize()
The initialization function of libxml2 abstraction layer. This function must be called prior to using...
Toplevel namespace for libabigail.
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.
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...
void abigail_get_abixml_version(std::string &major, std::string &minor)
Return the version numbers for the ABIXML format.
Definition abg-config.cc:98
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...