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-2026 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/// Get the prefix of a string, given a suffix to consider.
1110///
1111/// @param the input_string to consider.
1112///
1113/// @param suffix the suffix to consider.
1114///
1115/// @param output parameter. Is set to the resulting prefix iff the
1116/// function returns true.
1117///
1118/// @return true iff @p prefix is set to the prefix.
1119bool
1120string_prefix(const string& input_string,
1121 const string& suffix,
1122 string& prefix)
1123{
1124 if (input_string.empty() || suffix.length() > input_string.length())
1125 return false;
1126
1127 if (suffix.empty())
1128 {
1129 prefix = input_string;
1130 return true;
1131 }
1132
1133 if (input_string.compare(input_string.length() - suffix.length(),
1134 suffix.length(), suffix) != 0)
1135 return false;
1136
1137 prefix = input_string.substr(0, input_string.length() - suffix.length());
1138
1139 return true;
1140}
1141
1142/// Return the prefix that is common to two strings.
1143///
1144/// @param s1 the first input string to consider.
1145///
1146/// @param s2 the second input string to consider.
1147///
1148/// @param result output parameter. The resulting common prefix found
1149/// between @p s1 and @p s2. This is set iff the function returns
1150/// true.
1151///
1152/// @return true iff @p result was set by this function with the
1153/// common prefix of @p s1 and @p s2.
1154static bool
1155common_prefix(const string& s1, const string& s2, string &result)
1156{
1157 if (s1.length() == 0 || s2.length() == 0)
1158 return false;
1159
1160 result.clear();
1161 for (size_t i = 0; i < s1.length() && i< s2.length(); ++i)
1162 if (s1[i] == s2[i])
1163 result += s1[i];
1164 else
1165 break;
1166
1167 return !result.empty();
1168}
1169
1170/// Find the prefix common to a *SORTED* vector of strings.
1171///
1172/// @param input_strings a lexycographically sorted vector of
1173/// strings. Please note that this vector absolutely needs to be
1174/// sorted for the function to work correctly. Otherwise the results
1175/// are going to be wrong.
1176///
1177/// @param prefix output parameter. This is set by this function with
1178/// the prefix common to the strings found in @p input_strings, iff
1179/// the function returns true.
1180///
1181/// @return true iff the function could find a common prefix to the
1182/// strings in @p input_strings.
1183bool
1184sorted_strings_common_prefix(vector<string>& input_strings, string& prefix)
1185{
1186 string prefix_candidate;
1187 bool found_prefix = false;
1188
1189 if (input_strings.size() == 1)
1190 {
1191 if (dir_name(input_strings.front(), prefix,
1192 /*keep_separator_at_end=*/true))
1193 return true;
1194 return false;
1195 }
1196
1197 string cur_str;
1198 for (vector<string>::const_iterator i = input_strings.begin();
1199 i != input_strings.end();
1200 ++i)
1201 {
1202 dir_name(*i, cur_str, /*keep_separator_at_end=*/true);
1203 if (prefix_candidate.empty())
1204 {
1205 prefix_candidate = cur_str;
1206 continue;
1207 }
1208
1209 string s;
1210 if (common_prefix(prefix_candidate, cur_str, s))
1211 {
1212 ABG_ASSERT(!s.empty());
1213 prefix_candidate = s;
1214 found_prefix = true;
1215 }
1216 }
1217
1218 if (found_prefix)
1219 {
1220 prefix = prefix_candidate;
1221 return true;
1222 }
1223
1224 return false;
1225}
1226
1227/// Return the version string of the library.
1228///
1229/// @return the version string of the library.
1230string
1232{
1233 string major, minor, revision, version_string, suffix;
1234 abigail::abigail_get_library_version(major, minor, revision, suffix);
1235 version_string = major + "." + minor + "." + revision + suffix;
1236 return version_string;
1237}
1238
1239/// Return the version string for the ABIXML format.
1240///
1241/// @return the version string of the ABIXML format.
1242string
1244{
1245 string major, minor, version_string;
1247 version_string = major + "." + minor;
1248 return version_string;
1249}
1250
1251/// Execute a shell command and returns its output.
1252///
1253/// @param cmd the shell command to execute.
1254///
1255/// @param lines output parameter. This is set with the lines that
1256/// constitute the output of the process that executed the command @p
1257/// cmd.
1258///
1259/// @return true iff the command was executed properly and no error
1260/// was encountered.
1261bool
1262execute_command_and_get_output(const string& cmd, vector<string>& lines)
1263{
1264 if (cmd.empty())
1265 return false;
1266
1267 FILE *stream=
1268 popen(cmd.c_str(),
1269 /*open 'stream' in
1270 read-only mode: type=*/"r");
1271
1272 if (stream == NULL)
1273 return false;
1274
1275 string result;
1276
1277#define TMP_BUF_LEN 1024 + 1
1278 char tmp_buf[TMP_BUF_LEN];
1279 memset(tmp_buf, 0, TMP_BUF_LEN);
1280
1281 while (fgets(tmp_buf, TMP_BUF_LEN, stream))
1282 {
1283 lines.push_back(tmp_buf);
1284 memset(tmp_buf, 0, TMP_BUF_LEN);
1285 }
1286
1287 if (pclose(stream) == -1)
1288 return false;
1289
1290 return true;
1291}
1292
1293/// Get a vector of arguments from a string containing a
1294/// comma-separated list of those arguments.
1295///
1296/// @param input_str the input string containing the comma-separated
1297/// list of arguments The input string has the form
1298/// "option=arg1,arg2,arg3,arg4".
1299///
1300/// @param option if the content of the input string @p input_str is
1301/// "option=arg1,arg2,arg3", then this parameter should be "option".
1302///
1303/// @param arguments this is set by the fonction the the arguments
1304/// that were a comma-separated list of arguments on the right hand
1305/// side of the '=' sign in the string @p input_str.
1306void
1308 const string& option,
1309 vector<string>& arguments)
1310{
1311 string s = input_str;
1312
1313 string_suffix(s, option, s);
1314 if (string_begins_with(s, "\""))
1315 s = s.substr(1);
1316 if (string_ends_with(s, "\""))
1317 s = s.substr(0, s.size() - 1);
1318 split_string(s, ",", arguments);
1319}
1320
1321/// Get the SONAMEs of the DSOs advertised as being "provided" by a
1322/// given RPM. That set can be considered as being the set of
1323/// "public" DSOs of the RPM.
1324///
1325/// This runs the command "rpm -qp --provides <rpm> | grep .so" and
1326/// filters its result.
1327///
1328/// @param rpm_path the path to the RPM to consider.
1329///
1330/// @param provided_dsos output parameter. This is set to the set of
1331/// SONAMEs of the DSOs advertised as being provided by the RPM
1332/// designated by @p rpm_path.
1333///
1334/// @return true iff we could successfully query the RPM to see what
1335/// DSOs it provides.
1336bool
1337get_dsos_provided_by_rpm(const string& rpm_path, set<string>& provided_dsos)
1338{
1339 vector<string> query_output;
1340 // We don't check the return value of this command because on some
1341 // system, the command can issue errors but still emit a valid
1342 // output. We'll rather rely on the fact that the command emits a
1343 // valid output or not.
1344 execute_command_and_get_output("rpm -qp --provides "
1345 + rpm_path + " 2> /dev/null | grep .so",
1346 query_output);
1347
1348 for (vector<string>::const_iterator line = query_output.begin();
1349 line != query_output.end();
1350 ++line)
1351 {
1352 string dso = line->substr(0, line->find('('));
1353 dso = trim_white_space(dso);
1354 if (!dso.empty())
1355 provided_dsos.insert(dso);
1356 }
1357 return true;
1358}
1359
1360/// Remove spaces at the beginning and at the end of a given string.
1361///
1362/// @param str the input string to consider.
1363///
1364/// @return the @p str string with leading and trailing white spaces removed.
1365string
1366trim_white_space(const string& str)
1367{
1368 if (str.empty())
1369 return "";
1370
1371 string result;
1372 string::size_type start, end;
1373 for (start = 0; start < str.length(); ++start)
1374 if (!isspace(str[start]))
1375 break;
1376
1377 for (end = str.length() - 1; end > 0; --end)
1378 if (!isspace(str[end]))
1379 break;
1380
1381 result = str.substr(start, end - start + 1);
1382 return result;
1383}
1384
1385/// Remove white spaces from a string.
1386///
1387/// @param str the string to remove the white spaces from.
1388///
1389/// @return true iff any white space was removed from @p str.
1390bool
1392{
1393 if (str.erase(std::remove_if(str.begin(), str.end(), isspace),
1394 str.end()) == str.end())
1395 return false;
1396 return true;
1397}
1398
1399/// Getter of a global instance of std::regex that matches numerical
1400/// litterals.
1401///
1402/// The regular expression is the following: "([0-9]+)([uUlL])+"
1403///
1404/// The purpose of this is to compile the regular expression only
1405/// once, the first time this function is invoked. Subsquent
1406/// invocations return the already compiled regular expression.
1407///
1408/// @return a reference to a global instance of std::regex
1409static std::regex&
1410get_litteral_regex()
1411{
1412 static std::regex re("([0-9]+)([uUlL])+");
1413 return re;
1414}
1415
1416/// Normalize the numerical litteral in a string.
1417///
1418/// Basically, if a litteral is present as 10u or 100UL, change it
1419/// into 10 or 100.
1420///
1421/// @param str the string to normalize.
1422///
1423/// @return true iff @p str was normalized.
1424bool
1426{
1427 bool begin_pattern = false, middle_pattern = false, found_litteral = false;
1428 for (string::iterator i = str.begin(); i < str.end(); ++i)
1429 {
1430 if (isdigit(*i))
1431 begin_pattern = true;
1432 else
1433 {
1434 if (begin_pattern
1435 && (*i == 'u' || *i == 'U' || *i == 'l' || *i == 'L'))
1436 middle_pattern = true;
1437 else
1438 {
1439 if (middle_pattern)
1440 {
1441 found_litteral = true;
1442 break;
1443 }
1444 }
1445 }
1446 }
1447
1448 if (found_litteral)
1449 {
1450 std::regex& re = get_litteral_regex();
1451 str = std::regex_replace(str, re, "$1");
1452 return true;
1453 }
1454
1455 return false;
1456}
1457
1458/// Remove a string of pattern in front of a given string.
1459///
1460/// For instance, consider this string:
1461/// "../../../foo"
1462///
1463/// The pattern "../" is repeated three times in front of the
1464/// sub-string "foo". Thus, the call:
1465/// trim_leading_string("../../../foo", "../")
1466/// will return the string "foo".
1467///
1468/// @param from the string to trim the leading repetition of pattern from.
1469///
1470/// @param to_trim the pattern to consider (and to trim).
1471///
1472/// @return the resulting string where the leading patter @p to_trim
1473/// has been removed from.
1474string
1475trim_leading_string(const string& from, const string& to_trim)
1476{
1477 string str = from;
1478
1479 while (string_begins_with(str, to_trim))
1480 string_suffix(str, to_trim, str);
1481 return str;
1482}
1483
1484/// Convert a vector<char*> into a vector<char**>.
1485///
1486/// @param char_stars the input vector.
1487///
1488/// @param char_star_stars the output vector.
1489void
1490convert_char_stars_to_char_star_stars(const vector<char*> &char_stars,
1491 vector<char**>& char_star_stars)
1492{
1493 for (vector<char*>::const_iterator i = char_stars.begin();
1494 i != char_stars.end();
1495 ++i)
1496 char_star_stars.push_back(const_cast<char**>(&*i));
1497}
1498
1499/// The private data of the @ref temp_file type.
1500struct temp_file::priv
1501{
1502 char* path_template_;
1503 int fd_;
1504 shared_ptr<std::fstream> fstream_;
1505
1506 priv()
1507 {
1508 const char* templat = "/tmp/libabigail-tmp-file-XXXXXX";
1509 int s = strlen(templat);
1510 path_template_ = new char[s + 1];
1511 memset(path_template_, 0, s + 1);
1512 memcpy(path_template_, templat, s);
1513
1514 fd_ = mkstemp(path_template_);
1515 if (fd_ == -1)
1516 return;
1517
1518 fstream_.reset(new std::fstream(path_template_,
1519 std::ios::trunc
1520 | std::ios::in
1521 | std::ios::out));
1522 }
1523
1524 ~priv()
1525 {
1526 if (fd_ && fd_ != -1)
1527 {
1528 fstream_.reset();
1529 close(fd_);
1530 remove(path_template_);
1531 }
1532 delete [] path_template_;
1533 }
1534};
1535
1536/// Default constructor of @ref temp_file.
1537///
1538/// It actually creates the temporary file.
1539temp_file::temp_file()
1540 : priv_(new priv)
1541{}
1542
1543/// Test if the temporary file has been created and is usable.
1544///
1545/// @return true iff the temporary file has been created and is
1546/// useable.
1547bool
1548temp_file::is_good() const
1549{return priv_->fstream_->good();}
1550
1551/// Return the path to the temporary file.
1552///
1553/// @return the path to the temporary file if it's usable, otherwise
1554/// return nil.
1555const char*
1556temp_file::get_path() const
1557{
1558 if (is_good())
1559 return priv_->path_template_;
1560
1561 return 0;
1562}
1563
1564/// Get the fstream to the temporary file.
1565///
1566/// Note that the current process is aborted if this member function
1567/// is invoked on an instance of @ref temp_file that is not usable.
1568/// So please test that the instance is usable by invoking the
1569/// temp_file::is_good() member function on it first.
1570///
1571/// @return the fstream to the temporary file.
1572std::fstream&
1573temp_file::get_stream()
1574{
1575 ABG_ASSERT(is_good());
1576 return *priv_->fstream_;
1577}
1578
1579/// Create the temporary file and return it if it's usable.
1580///
1581/// @return the newly created temporary file if it's usable, nil
1582/// otherwise.
1584temp_file::create()
1585{
1586 temp_file_sptr result(new temp_file);
1587 if (result->is_good())
1588 return result;
1589
1590 return temp_file_sptr();
1591}
1592
1593/// Get a pseudo random number.
1594///
1595/// @return a pseudo random number.
1596size_t
1598{
1599 static __thread bool initialized = false;
1600
1601 if (!initialized)
1602 {
1603 srand(time(NULL));
1604 initialized = true;
1605 }
1606
1607 return rand();
1608}
1609
1610/// Get a pseudo random number as string.
1611///
1612/// @return a pseudo random number as string.
1613string
1615{
1616 std::ostringstream o;
1617 o << get_random_number();
1618
1619 return o.str();
1620}
1621
1622ostream&
1623operator<<(ostream& output,
1624 file_type r)
1625{
1626 string repr;
1627
1628 switch(r)
1629 {
1630 case FILE_TYPE_UNKNOWN:
1631 repr = "unknown file type";
1632 break;
1633 case FILE_TYPE_NATIVE_BI:
1634 repr = "native binary instrumentation file type";
1635 break;
1636 case FILE_TYPE_ELF:
1637 repr = "ELF file type";
1638 break;
1639 case FILE_TYPE_AR:
1640 repr = "archive file type";
1641 break;
1642 case FILE_TYPE_XML_CORPUS:
1643 repr = "native XML corpus file type";
1644 break;
1645 case FILE_TYPE_XML_CORPUS_GROUP:
1646 repr = "native XML corpus group file type";
1647 break;
1648 case FILE_TYPE_RPM:
1649 repr = "RPM file type";
1650 break;
1651 case FILE_TYPE_SRPM:
1652 repr = "SRPM file type";
1653 break;
1654 case FILE_TYPE_DEB:
1655 repr = "Debian binary file type";
1656 break;
1657 case FILE_TYPE_DIR:
1658 repr = "Directory type";
1659 break;
1660 case FILE_TYPE_TAR:
1661 repr = "GNU tar archive type";
1662 break;
1663 case FILE_TYPE_XZ:
1664 repr = "XZ compressed file";
1665 }
1666
1667 output << repr;
1668 return output;
1669}
1670
1671/// The kind of compression we want a de-compression std::streambuf
1672/// for.
1673///
1674/// This enum must be amended to add support for new compression
1675/// schemes, especially whenever a new enumerator is added to the enum
1676/// @ref file_type.
1678{
1679 COMPRESSION_KIND_UNKNOWN,
1680 /// The LZMA compression (used by the xz tool).
1682}; //end enum compression_kind
1683
1684/// Test if one of the enumerators of @ref file_type designates a
1685/// compression scheme.
1686///
1687/// This helper function needs to be updated whenever a new
1688/// compression-related enumerator is added to @ref file_type.
1689///
1690/// @return the kind of compression designated by @p t.
1691static compression_kind
1692is_compressed_file_type(file_type t)
1693{
1694 if (t == FILE_TYPE_XZ)
1695 return COMPRESSION_KIND_XZ;
1696
1697 return COMPRESSION_KIND_UNKNOWN;
1698}
1699
1700/// Guess the type of the content of an input stream.
1701///
1702/// @param in the input stream to guess the content type for.
1703///
1704/// @return the type of content guessed.
1705file_type
1707{
1708 const unsigned BUF_LEN = 264;
1709 const unsigned NB_BYTES_TO_READ = 263;
1710
1711 unsigned char buf[BUF_LEN];
1712 memset(buf, 0, BUF_LEN);
1713
1714 std::streampos initial_pos = in.tellg();
1715 in.read(reinterpret_cast<char*>(buf), NB_BYTES_TO_READ);
1716 in.seekg(initial_pos);
1717
1718 if (in.gcount() < 4 || in.bad())
1719 return FILE_TYPE_UNKNOWN;
1720
1721 if (buf[0] == 0x7f
1722 && buf[1] == 'E'
1723 && buf[2] == 'L'
1724 && buf[3] == 'F')
1725 return FILE_TYPE_ELF;
1726
1727 // XZ format. Described at
1728 // https://tukaani.org/xz/xz-file-format.txt.
1729 if (in.gcount() >= 6
1730 && buf[0] == 0xFD
1731 && buf[1] == '7'
1732 && buf[2] == 'z'
1733 && buf[3] == 'X'
1734 && buf[4] == 'Z'
1735 && buf[5] == 0)
1736 return FILE_TYPE_XZ;
1737
1738 if (buf[0] == '!'
1739 && buf[1] == '<'
1740 && buf[2] == 'a'
1741 && buf[3] == 'r'
1742 && buf[4] == 'c'
1743 && buf[5] == 'h'
1744 && buf[6] == '>')
1745 {
1746 if (strstr(reinterpret_cast<char*>(buf), "debian-binary"))
1747 return FILE_TYPE_DEB;
1748 else
1749 return FILE_TYPE_AR;
1750 }
1751
1752 if (buf[0] == '<'
1753 && buf[1] == 'a'
1754 && buf[2] == 'b'
1755 && buf[3] == 'i'
1756 && buf[4] == '-'
1757 && buf[5] == 'i'
1758 && buf[6] == 'n'
1759 && buf[7] == 's'
1760 && buf[8] == 't'
1761 && buf[9] == 'r'
1762 && buf[10] == ' ')
1763 return FILE_TYPE_NATIVE_BI;
1764
1765 if (buf[0] == '<'
1766 && buf[1] == 'a'
1767 && buf[2] == 'b'
1768 && buf[3] == 'i'
1769 && buf[4] == '-'
1770 && buf[5] == 'c'
1771 && buf[6] == 'o'
1772 && buf[7] == 'r'
1773 && buf[8] == 'p'
1774 && buf[9] == 'u'
1775 && buf[10] == 's'
1776 && buf[11] == '-'
1777 && buf[12] == 'g'
1778 && buf[13] == 'r'
1779 && buf[14] == 'o'
1780 && buf[15] == 'u'
1781 && buf[16] == 'p'
1782 && buf[17] == ' ')
1783 return FILE_TYPE_XML_CORPUS_GROUP;
1784
1785 if (buf[0] == '<'
1786 && buf[1] == 'a'
1787 && buf[2] == 'b'
1788 && buf[3] == 'i'
1789 && buf[4] == '-'
1790 && buf[5] == 'c'
1791 && buf[6] == 'o'
1792 && buf[7] == 'r'
1793 && buf[8] == 'p'
1794 && buf[9] == 'u'
1795 && buf[10] == 's'
1796 && buf[11] == ' ')
1797 return FILE_TYPE_XML_CORPUS;
1798
1799 // Detect RPM format. Documented at
1800 // http://ftp.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html.
1801 if ((unsigned char) buf[0] == 0xed
1802 && (unsigned char) buf[1] == 0xab
1803 && (unsigned char) buf[2] == 0xee
1804 && (unsigned char) buf[3] == 0xdb)
1805 {
1806 if (buf[7] == 0x00)
1807 return FILE_TYPE_RPM;
1808 else if (buf[7] == 0x01)
1809 return FILE_TYPE_SRPM;
1810 else
1811 return FILE_TYPE_UNKNOWN;
1812 }
1813
1814 if (buf[257] == 'u'
1815 && buf[258] == 's'
1816 && buf[259] == 't'
1817 && buf[260] == 'a'
1818 && buf[261] == 'r')
1819 return FILE_TYPE_TAR;
1820
1821 return FILE_TYPE_UNKNOWN;
1822}
1823
1824/// The factory of an std::streambuf aimed at decompressing data
1825/// coming from an input stream compressed with a particular
1826/// compression scheme.
1827///
1828/// This function must be amended to add support for new compression
1829/// schemes.
1830///
1831/// @param compressed_input the compressed input to create the
1832/// decompressor std::streambuf for.
1833///
1834/// @param compr the compression scheme kind.
1835///
1836/// @return a pointer to the std::streambuf to use for decompression.
1837/// If the compression scheme is not supported, the function returns
1838/// nil.
1839static shared_ptr<std::streambuf>
1840get_decompressed_streambuf(std::istream& compressed_input,
1841 compression_kind compr)
1842{
1843 shared_ptr<std::streambuf> result;
1844
1845 switch(compr)
1846 {
1847 case COMPRESSION_KIND_UNKNOWN:
1849 break;
1850
1851 case COMPRESSION_KIND_XZ:
1852 shared_ptr<std::streambuf> r(new xz_decompressor_type(compressed_input));
1853 result = r;
1854 break;
1855 };
1856
1857 return result;
1858};// end struct compression_handler_type
1859
1860/// Guess the type of the content of an file.
1861///
1862/// @param file_path the path to the file to consider.
1863///
1864/// @param look_through_compression if true, then decompress the file
1865/// and try to guess the type of the decompressed content. Otherwise,
1866/// just return that it's a compressed type.
1867///
1868/// @return the type of content guessed.
1870guess_file_type(const string& file_path, bool look_through_compression)
1871{
1872 if (is_dir(file_path))
1873 return FILE_TYPE_DIR;
1874
1875 if (string_ends_with(file_path, ".tar")
1876 || string_ends_with(file_path, ".tar.gz")
1877 || string_ends_with(file_path, ".tgz")
1878 || string_ends_with(file_path, ".tar.bz2")
1879 || string_ends_with(file_path, ".tbz2")
1880 || string_ends_with(file_path, ".tbz")
1881 || string_ends_with(file_path, ".tb2")
1882 || string_ends_with(file_path, ".tar.xz")
1883 || string_ends_with(file_path, ".txz")
1884 || string_ends_with(file_path, ".tar.lzma")
1885 || string_ends_with(file_path, ".tar.lz")
1886 || string_ends_with(file_path, ".tlz")
1887 || string_ends_with(file_path, ".tar.Z")
1888 || string_ends_with(file_path, ".taz")
1889 || string_ends_with(file_path, ".tz"))
1890 return FILE_TYPE_TAR;
1891
1893 compression_kind compr_kind = COMPRESSION_KIND_UNKNOWN;
1894 shared_ptr<std::streambuf> decompressor_streambuf;
1895
1896 if (string_ends_with(file_path, ".lzma")
1897 || string_ends_with(file_path, ".lz")
1898 || string_ends_with(file_path, ".xz"))
1899 {
1900 r = FILE_TYPE_XZ;
1901 compr_kind = COMPRESSION_KIND_XZ;
1902 }
1903 // else if there are other compression schemes supported, recognize
1904 // their file suffix here!
1905
1906 if (is_compressed_file_type(r) && !look_through_compression)
1907 return r;
1908
1909 do
1910 {
1911 shared_ptr<ifstream> input_fstream(new ifstream(file_path.c_str(),
1912 ifstream::binary));
1913 shared_ptr<istream> input_stream = input_fstream;
1914
1915 if (compr_kind != COMPRESSION_KIND_UNKNOWN)
1916 decompressor_streambuf = get_decompressed_streambuf(*input_stream,
1917 compr_kind);
1918
1919 if (decompressor_streambuf)
1920 input_stream.reset(new istream(decompressor_streambuf.get()));
1921
1922 r = guess_file_type(*input_stream);
1923
1924 input_fstream->close();
1925
1926 if (!decompressor_streambuf)
1927 {
1928 // So we haven't attempted to decompress the input stream.
1929 //
1930 // Have we found out that it was compressed nonetheless?
1931 compr_kind = is_compressed_file_type(r);
1932 if (compr_kind)
1933 {
1934 if (!look_through_compression)
1935 // The caller wants us to report that this file is
1936 // compressed.
1937 return r;
1938
1939 // We found out the input file is compressed, so we do
1940 // have the means to decompress it. However, we haven't
1941 // yet gotten the de-compressor; that might be because
1942 // we detected the compression just by looking at the
1943 // file name suffix. Let's go back to calling
1944 // get_decompressed_streambuf again to get the
1945 // decompressor.
1946 ;
1947 }
1948 else
1949 // No the file is not compressed let's get out of here.
1950 break;
1951 }
1952 } while (!decompressor_streambuf && compr_kind);
1953
1954 return r;
1955}
1956
1957/// Get the package name of a .deb package.
1958///
1959/// @param str the string containing the .deb NVR.
1960///
1961/// @param name output parameter. This is set with the package name
1962/// of the .deb package iff the function returns true.
1963///
1964/// @return true iff the function successfully finds the .deb package
1965/// name.
1966bool
1967get_deb_name(const string& str, string& name)
1968{
1969 if (str.empty() || str[0] == '_')
1970 return false;
1971
1972 string::size_type str_len = str.length(), i = 0 ;
1973
1974 for (; i < str_len; ++i)
1975 {
1976 if (str[i] == '_')
1977 break;
1978 }
1979
1980 if (i == str_len)
1981 return false;
1982
1983 name = str.substr(0, i);
1984 return true;
1985}
1986
1987/// Get the package name of an rpm package.
1988///
1989/// @param str the string containing the NVR of the rpm.
1990///
1991/// @param name output parameter. This is set with the package name
1992/// of the rpm package iff the function returns true.
1993///
1994/// @return true iff the function successfully finds the rpm package
1995/// name.
1996bool
1997get_rpm_name(const string& str, string& name)
1998{
1999 if (str.empty() || str[0] == '-')
2000 return false;
2001
2002 string::size_type str_len = str.length(), i = 0;
2003 string::value_type c;
2004
2005 for (; i < str_len; ++i)
2006 {
2007 c = str[i];
2008 string::size_type next_index = i + 1;
2009 if ((next_index < str_len) && c == '-' && isdigit(str[next_index]))
2010 break;
2011 }
2012
2013 if (i == str_len)
2014 return false;
2015
2016 name = str.substr(0, i);
2017
2018 return true;
2019}
2020
2021/// Get the architecture string from the NVR of an rpm.
2022///
2023/// @param str the NVR to consider.
2024///
2025/// @param arch output parameter. Is set to the resulting
2026/// archirecture string iff the function returns true.
2027///
2028/// @return true iff the function could find the architecture string
2029/// from the NVR.
2030bool
2031get_rpm_arch(const string& str, string& arch)
2032{
2033 if (str.empty())
2034 return false;
2035
2036 if (!string_ends_with(str, ".rpm"))
2037 return false;
2038
2039 string::size_type str_len = str.length(), i = 0;
2040 string::value_type c;
2041 string::size_type last_dot_index = 0, dot_before_last_index = 0;
2042
2043 for (i = str_len - 1; i > 0; --i)
2044 {
2045 c = str[i];
2046 if (c == '.')
2047 {
2048 last_dot_index = i;
2049 break;
2050 }
2051 }
2052
2053 if (i == 0)
2054 return false;
2055
2056 for(--i; i > 0; --i)
2057 {
2058 c = str[i];
2059 if (c == '.')
2060 {
2061 dot_before_last_index = i;
2062 break;
2063 }
2064 }
2065
2066 if (i == 0)
2067 return false;
2068
2069 arch = str.substr(dot_before_last_index + 1,
2070 last_dot_index - dot_before_last_index - 1);
2071
2072 return true;
2073}
2074
2075/// Tests if a given file name designates a kernel package.
2076///
2077/// @param file_path the path to the file to consider.
2078///
2079/// @param file_type the type of the file @p file_name.
2080///
2081/// @return true iff @p file_name of kind @p file_type designates a
2082/// kernel package.
2083bool
2085{
2086 bool result = false;
2087
2088 if (file_type == FILE_TYPE_RPM)
2089 {
2090 if (rpm_contains_file(file_path, "vmlinuz"))
2091 result = true;
2092 }
2093 else if (file_type == FILE_TYPE_DEB)
2094 {
2095 string file_name;
2096 base_name(file_path, file_name);
2097 string package_name;
2098 if (get_deb_name(file_name, package_name))
2099 result = (string_begins_with(package_name, "linux-image"));
2100 }
2101
2102 return result;
2103}
2104
2105/// Test if an RPM package contains a given file.
2106///
2107/// @param rpm_path the path to the RPM package.
2108///
2109/// @param file_name the file name to test the presence for in the
2110/// rpm.
2111///
2112/// @return true iff the file named @file_name is present in the RPM.
2113bool
2114rpm_contains_file(const string& rpm_path, const string& file_name)
2115{
2116 vector<string> query_output;
2117 // We don't check the return value of this command because on some
2118 // system, the command can issue errors but still emit a valid
2119 // output. We'll rather rely on the fact that the command emits a
2120 // valid output or not.
2122 + rpm_path + " 2> /dev/null",
2123 query_output);
2124
2125 for (auto& line : query_output)
2126 {
2127 line = trim_white_space(line);
2128 if (string_ends_with(line, file_name))
2129 return true;
2130 }
2131
2132 return false;
2133}
2134
2135/// Tests if a given file name designates a kernel debuginfo package.
2136///
2137/// @param file_name the file name to consider.
2138///
2139/// @param file_type the type of the file @p file_name.
2140///
2141/// @return true iff @p file_name of kind @p file_type designates a
2142/// kernel debuginfo package.
2143bool
2145{
2146 bool result = false;
2147 string package_name;
2148
2149 if (file_type == FILE_TYPE_RPM)
2150 {
2151 if (!get_rpm_name(file_name, package_name))
2152 return false;
2153 result = (package_name == "kernel-debuginfo");
2154 }
2155 else if (file_type == FILE_TYPE_DEB)
2156 {
2157 if (!get_deb_name(file_name, package_name))
2158 return false;
2159 result = (string_begins_with(package_name, "linux-image")
2160 && (string_ends_with(package_name, "-dbg")
2161 || string_ends_with(package_name, "-dbgsyms")));
2162 }
2163
2164 return result;
2165}
2166
2167/// The delete functor of a char buffer that has been created using
2168/// malloc.
2169struct malloced_char_star_deleter
2170{
2171 void
2172 operator()(char* ptr)
2173 {free(ptr);}
2174};
2175
2176/// Return a copy of the path given in argument, turning it into an
2177/// absolute path by prefixing it with the concatenation of the result
2178/// of get_current_dir_name() and the '/' character.
2179///
2180/// The result being an shared_ptr to char*, it should manage its
2181/// memory by itself and the user shouldn't need to wory too much for
2182/// that.
2183///
2184/// @param p the path to turn into an absolute path.
2185///
2186/// @return a shared pointer to the resulting absolute path.
2187std::shared_ptr<char>
2189{
2190 using std::shared_ptr;
2191
2192 shared_ptr<char> result;
2193
2194 if (p && p[0] != '/')
2195 {
2196 shared_ptr<char> pwd(get_current_dir_name(),
2197 malloced_char_star_deleter());
2198 string s = string(pwd.get()) + "/" + p;
2199 result.reset(strdup(s.c_str()), malloced_char_star_deleter());
2200 }
2201 else
2202 result.reset(strdup(p), malloced_char_star_deleter());
2203
2204 return result;
2205}
2206
2207/// Return a copy of the path given in argument, turning it into an
2208/// absolute path by prefixing it with the concatenation of the result
2209/// of get_current_dir_name() and the '/' character.
2210///
2211///
2212/// @param p the path to turn into an absolute path.
2213///
2214/// @return The resulting absolute path.
2215string
2216make_path_absolute(const string& p)
2217{
2218 string result;
2219
2220 if (!p.empty() && p[0] != '/')
2221 {
2222 shared_ptr<char> pwd(get_current_dir_name(),
2223 malloced_char_star_deleter());
2224 result = string(pwd.get()) + "/" + p;
2225 }
2226 else if (!p.empty())
2227 result = p;
2228
2229 return result;
2230}
2231
2232/// This is a sub-routine of gen_suppr_spec_from_headers and
2233/// handle_fts_entry.
2234///
2235/// It setups a type suppression which is meant to keep types defined
2236/// in a given file and suppress all other types.
2237///
2238/// @param file_path the path to the file that defines types that are
2239/// meant to be kept by the type suppression. All other types defined
2240/// in other files are to be suppressed. Note that this file path is
2241/// added to the vector returned by
2242/// type_suppression::get_source_locations_to_keep()
2243///
2244/// @param suppr the type suppression to setup. If this smart pointer
2245/// is nil then a new instance @ref type_suppression is created and
2246/// this variable is made to point to it.
2247static void
2248handle_file_entry(const string& file_path,
2249 type_suppression_sptr& suppr)
2250{
2251 if (!suppr)
2252 {
2254 /*type_name_regexp=*/"",
2255 /*type_name=*/""));
2256
2257 // Types that are defined in system headers are usually
2258 // OK to be considered as public types.
2259 suppr->set_source_location_to_keep_regex_str("^/usr/include/");
2260 suppr->set_is_artificial(true);
2261 }
2262
2263 // And types that are defined in header files that are under
2264 // the header directory file we are looking are to be
2265 // considered public types too.
2266 suppr->get_source_locations_to_keep().insert(file_path);
2267}
2268
2269/// This is a sub-routine of gen_suppr_spec_from_headers.
2270///
2271/// @param entry if this file represents a regular (or symlink) file,
2272/// then its file name is going to be added to the vector returned by
2273/// type_suppression::get_source_locations_to_keep().
2274///
2275/// @param if @p entry represents a file, then its file name is going
2276/// to be added to the vector returned by the method
2277/// type_suppression::get_source_locations_to_keep of this instance.
2278/// If this smart pointer is nil then a new instance @ref
2279/// type_suppression is created and this variable is made to point to
2280/// it.
2281static void
2282handle_fts_entry(const FTSENT *entry,
2283 type_suppression_sptr& suppr)
2284{
2285 if (entry == NULL
2286 || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2287 || entry->fts_info == FTS_ERR
2288 || entry->fts_info == FTS_NS)
2289 return;
2290
2291 string fname = entry->fts_name;
2292 if (!fname.empty())
2293 {
2294 if (string_ends_with(fname, ".h")
2295 || string_ends_with(fname, ".hpp")
2296 || string_ends_with(fname, ".hxx"))
2297 handle_file_entry (fname, suppr);
2298 }
2299}
2300
2301/// Populate a type_supression from header files found in a given
2302/// directory tree.
2303///
2304/// The suppression suppresses types defined in source files that are
2305/// *NOT* found in the directory tree.
2306///
2307/// This is a subroutine for gen_suppr_spect_from_headers.
2308///
2309/// @param headers_root_dir the directory tree to consider for header
2310/// files.
2311///
2312/// @param result the type_supression to populate from the content of
2313/// @p headers_root_dir.
2314static void
2315gen_suppr_spec_from_headers_root_dir(const string& headers_root_dir,
2316 type_suppression_sptr &result)
2317{
2318 if (!headers_root_dir.empty())
2319 {
2320 char* paths[] = {const_cast<char*>(headers_root_dir.c_str()), 0};
2321
2322 if (FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL))
2323 {
2324 FTSENT *entry;
2325 while ((entry = fts_read(file_hierarchy)))
2326 handle_fts_entry(entry, result);
2327 fts_close(file_hierarchy);
2328 }
2329 }
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 set of header root directories nor in a set of header
2335/// files.
2336///
2337/// @param headers_root_dirs ABI changes in types defined in files
2338/// *NOT* found in these directory trees are going be suppressed.
2339///
2340/// @param header_files a set of additional header files that define
2341/// types that are to be kept (not supressed) by the returned type
2342/// suppression.
2343///
2344/// @return the resulting type suppression generated, if any file was
2345/// found in the directory tree @p headers_root_dir.
2347gen_suppr_spec_from_headers(const vector<string>& headers_root_dirs,
2348 const vector<string>& header_files)
2349{
2350 type_suppression_sptr result;
2351
2352 for (vector<string>::const_iterator root_dir = headers_root_dirs.begin();
2353 root_dir != headers_root_dirs.end();
2354 ++root_dir)
2355 gen_suppr_spec_from_headers_root_dir(*root_dir, result);
2356
2357 for (vector<string>::const_iterator file = header_files.begin();
2358 file != header_files.end();
2359 ++file)
2360 handle_file_entry(*file, result);
2361
2362 return result;
2363}
2364
2365/// Generate a type suppression specification that suppresses ABI
2366/// changes for types defined in source files that are neither in a
2367/// given header root dir, not in a set of header files.
2368///
2369/// @param headers_root_dir ABI changes in types defined in files
2370/// *NOT* found in this directory tree are going be suppressed.
2371///
2372/// @param header_files a set of additional header files that define
2373/// types that are to be kept (not supressed) by the returned type
2374/// suppression.
2375///
2376/// @return the resulting type suppression generated, if any file was
2377/// found in the directory tree @p headers_root_dir.
2379gen_suppr_spec_from_headers(const string& headers_root_dir,
2380 const vector<string>& header_files)
2381{
2382 type_suppression_sptr result;
2383 vector<string> root_dirs;
2384
2385 if (!headers_root_dir.empty())
2386 root_dirs.push_back(headers_root_dir);
2387
2388 return gen_suppr_spec_from_headers(root_dirs, header_files);
2389}
2390
2391/// Generate a type suppression specification that suppresses ABI
2392/// changes for types defined in source files that are not in a given
2393/// header root dir.
2394///
2395/// @param headers_root_dir ABI changes in types defined in files
2396/// *NOT* found in this directory tree are going be suppressed.
2397///
2398/// @return the resulting type suppression generated, if any file was
2399/// found in the directory tree @p headers_root_dir.
2401gen_suppr_spec_from_headers(const string& headers_root_dir)
2402{
2403 // We don't list individual files, just look under the headers_path.
2404 vector<string> header_files;
2405 return gen_suppr_spec_from_headers(headers_root_dir, header_files);
2406}
2407
2408/// Generate a suppression specification from kernel abi whitelist
2409/// files.
2410///
2411/// A kernel ABI whitelist file is an INI file that usually has only
2412/// one section. The name of the section is a string that ends up
2413/// with the sub-string "whitelist". For instance
2414/// RHEL7_x86_64_whitelist.
2415///
2416/// Then the content of the section is a set of function or variable
2417/// names, one name per line. Each function or variable name is the
2418/// name of a function or a variable whose changes are to be keept.
2419///
2420/// A whitelist file can have multiple sections (adhering to the naming
2421/// conventions and multiple files can be passed. The suppression that
2422/// is created takes all whitelist sections from all files into account.
2423/// Symbols (or expression of such) are deduplicated in the final
2424/// suppression expression.
2425///
2426/// This function reads the white lists and generates a
2427/// function_suppression_sptr and variable_suppression_sptr and returns
2428/// a vector containing those.
2429///
2430/// @param abi_whitelist_paths a vector of KMI whitelist paths
2431///
2432/// @return a vector or suppressions
2435 (const std::vector<std::string>& abi_whitelist_paths)
2436{
2437
2438 std::vector<std::string> whitelisted_names;
2439 for (std::vector<std::string>::const_iterator
2440 path_iter = abi_whitelist_paths.begin(),
2441 path_end = abi_whitelist_paths.end();
2442 path_iter != path_end;
2443 ++path_iter)
2444 {
2445
2446 abigail::ini::config whitelist;
2447 if (!read_config(*path_iter, whitelist))
2448 continue;
2449
2450 const ini::config::sections_type& whitelist_sections =
2451 whitelist.get_sections();
2452
2453 for (ini::config::sections_type::const_iterator
2454 section_iter = whitelist_sections.begin(),
2455 section_end = whitelist_sections.end();
2456 section_iter != section_end;
2457 ++section_iter)
2458 {
2459 std::string section_name = (*section_iter)->get_name();
2460 if (!string_ends_with(section_name, "whitelist")
2461 && !string_ends_with(section_name, "stablelist"))
2462 continue;
2463 for (ini::config::properties_type::const_iterator
2464 prop_iter = (*section_iter)->get_properties().begin(),
2465 prop_end = (*section_iter)->get_properties().end();
2466 prop_iter != prop_end;
2467 ++prop_iter)
2468 {
2469 if (const simple_property_sptr& prop =
2470 is_simple_property(*prop_iter))
2471 if (prop->has_empty_value())
2472 {
2473 const std::string& name = prop->get_name();
2474 if (!name.empty())
2475 whitelisted_names.push_back(name);
2476 }
2477 }
2478 }
2479 }
2480
2481 suppressions_type result;
2482 if (!whitelisted_names.empty())
2483 {
2484 // Drop duplicates to simplify the regex we are generating
2485 std::sort(whitelisted_names.begin(), whitelisted_names.end());
2486 whitelisted_names.erase(std::unique(whitelisted_names.begin(),
2487 whitelisted_names.end()),
2488 whitelisted_names.end());
2489
2490 // Build a regular expression representing the union of all
2491 // the function and variable names expressed in the white list.
2492 const std::string regex = regex::generate_from_strings(whitelisted_names);
2493
2494 // Build a suppression specification which *keeps* functions
2495 // whose ELF symbols match the regular expression contained
2496 // in function_names_regexp. This will also keep the ELF
2497 // symbols (not designated by any debug info) whose names
2498 // match this regexp.
2500 fn_suppr->set_label("whitelist");
2501 fn_suppr->set_symbol_name_not_regex_str(regex);
2502 fn_suppr->set_drops_artifact_from_ir(true);
2503 result.push_back(fn_suppr);
2504
2505 // Build a suppression specification which *keeps* variables
2506 // whose ELF symbols match the regular expression contained
2507 // in function_names_regexp. This will also keep the ELF
2508 // symbols (not designated by any debug info) whose names
2509 // match this regexp.
2511 var_suppr->set_label("whitelist");
2512 var_suppr->set_symbol_name_not_regex_str(regex);
2513 var_suppr->set_drops_artifact_from_ir(true);
2514 result.push_back(var_suppr);
2515 }
2516 return result;
2517}
2518
2519/// Get the path to the default system suppression file.
2520///
2521/// @return a copy of the default system suppression file.
2522string
2524{
2525 string default_system_suppr_path;
2526
2527 const char *s = getenv("LIBABIGAIL_DEFAULT_SYSTEM_SUPPRESSION_FILE");
2528 if (s)
2529 default_system_suppr_path = s;
2530
2531 if (default_system_suppr_path.empty())
2532 default_system_suppr_path =
2533 get_system_libdir() + string("/libabigail/default.abignore");
2534
2535 return default_system_suppr_path;
2536}
2537
2538/// Get the path to the default user suppression file.
2539///
2540/// @return a copy of the default user suppression file.
2541string
2543{
2544 string default_user_suppr_path;
2545 const char *s = getenv("LIBABIGAIL_DEFAULT_USER_SUPPRESSION_FILE");
2546
2547 if (s == NULL)
2548 {
2549 s = getenv("HOME");
2550 if (s == NULL)
2551 return "";
2552 default_user_suppr_path = s;
2553 if (default_user_suppr_path.empty())
2554 default_user_suppr_path = "~";
2555 default_user_suppr_path += "/.abignore";
2556 }
2557 else
2558 default_user_suppr_path = s;
2559
2560 return default_user_suppr_path;
2561}
2562
2563/// Load the default system suppression specification file and
2564/// populate a vector of @ref suppression_sptr with its content.
2565///
2566/// The default system suppression file is located at
2567/// $libdir/libabigail/default-libabigail.abignore.
2568///
2569/// @param supprs the vector to add the suppression specifications
2570/// read from the file to.
2571void
2573{
2574 string default_system_suppr_path =
2576
2577 read_suppressions(default_system_suppr_path, supprs);
2578}
2579
2580/// Load the default user suppression specification file and populate
2581/// a vector of @ref suppression_sptr with its content.
2582///
2583/// The default user suppression file is located at $HOME~/.abignore.
2584///
2585/// @param supprs the vector to add the suppression specifications
2586/// read from the file to.
2587void
2589{
2590 string default_user_suppr_path =
2592
2593 read_suppressions(default_user_suppr_path, supprs);
2594}
2595
2596/// Test if a given FTSENT* denotes a file with a given name.
2597///
2598/// @param entry the FTSENT* to consider.
2599///
2600/// @param fname the file name (or end of path) to consider. The file
2601/// name can also be a path that is relative to the root directory the
2602/// current visit is started from. The root directory is given by @p
2603/// root_dir.
2604///
2605/// @param root_dir the root dir from which the directory visit is
2606/// being performed.
2607///
2608/// @return true iff @p entry denotes a file which path ends with @p
2609/// fname.
2610static bool
2611entry_of_file_with_name(const FTSENT *entry,
2612 const string& fname,
2613 const string& root_dir)
2614{
2615 if (entry == NULL
2616 || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2617 || entry->fts_info == FTS_ERR
2618 || entry->fts_info == FTS_NS)
2619 return false;
2620
2621 string fpath = ::basename(entry->fts_path);
2622 if (fpath == fname)
2623 return true;
2624
2625 fpath = trim_leading_string(entry->fts_path, root_dir);
2626 if (fpath == fname)
2627 return true;
2628
2629 return false;
2630}
2631
2632/// Find a given file under a root directory and return its absolute
2633/// path.
2634///
2635/// @param root_dir the root directory under which to look for.
2636///
2637/// @param file_path_to_look_for the file to look for under the
2638/// directory @p root_dir.
2639///
2640/// @param result the resulting path to @p file_path_to_look_for.
2641/// This is set iff the file has been found.
2642bool
2643find_file_under_dir(const string& root_dir,
2644 const string& file_path_to_look_for,
2645 string& result)
2646{
2647 char* paths[] = {const_cast<char*>(root_dir.c_str()), 0};
2648
2649 FTS *file_hierarchy = fts_open(paths,
2650 FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
2651 if (!file_hierarchy)
2652 return false;
2653
2654 string r = root_dir;
2655 if (!string_ends_with(r, "/"))
2656 r += "/";
2657
2658 FTSENT *entry;
2659 while ((entry = fts_read(file_hierarchy)))
2660 {
2661 if (entry_of_file_with_name(entry, file_path_to_look_for, r))
2662 {
2663 result = entry->fts_path;
2664 fts_close(file_hierarchy);
2665 return true;
2666 }
2667 // Skip descendents of symbolic links.
2668 if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
2669 {
2670 fts_set(file_hierarchy, entry, FTS_SKIP);
2671 continue;
2672 }
2673 }
2674
2675 fts_close(file_hierarchy);
2676 return false;
2677}
2678
2679/// Find a given file possibly under a set of directories and return
2680/// its absolute path.
2681///
2682/// @param root_dirs the vector of root directories under which to
2683/// look for.
2684///
2685/// @param file_path_to_look_for the file to look for under the
2686/// directory @p root_dir.
2687///
2688/// @param result the resulting path to @p file_path_to_look_for.
2689/// This is set iff the file has been found.
2690bool
2691find_file_under_dirs(const vector<string>& root_dirs,
2692 const string& file_path_to_look_for,
2693 string& result)
2694{
2695 if (root_dirs.empty())
2696 return find_file_under_dir(".", file_path_to_look_for, result);
2697
2698 for (const auto& root_dir : root_dirs)
2699 if (find_file_under_dir(root_dir, file_path_to_look_for, result))
2700 return true;
2701
2702 return false;
2703}
2704
2705/// Get the dependencies of an ABI corpus, which are found in a set of
2706/// directories. Note that the dependencies are listed as properties
2707/// of the ABI corpus.
2708///
2709/// If the corpus has a dependency that is not found under any of the
2710/// given directories, then the dependency is ignored and not
2711/// returned.
2712///
2713/// @param korpus the ABI corpus to consider.
2714///
2715/// @param deps_dirs the list of directories where to look for the
2716/// dependencies.
2717///
2718/// @param dependencies output parameter that is set the dependencies
2719/// of the corpus denoted by @p korpus which are found in the
2720/// directories @p deps_dirs. This is set iff the function returns
2721/// true.
2722///
2723/// @return true iff some dependencies of the corpus @p korpus were
2724/// found in directories @p deps_dirs.
2725bool
2727 const vector<string>& deps_dirs,
2728 set<string>& dependencies)
2729{
2730 const vector<string>& set_of_needed = korpus.get_needed();
2731 if (set_of_needed.empty())
2732 return false;
2733
2734 bool found_at_least_one_dependency =false;
2735 for (const auto& n :set_of_needed)
2736 {
2737 string dependency;
2738 if (dependencies.find(n) == dependencies.end()
2739 && find_file_under_dirs(deps_dirs, n, dependency))
2740 {
2741 dependencies.insert(dependency);
2742 found_at_least_one_dependency = true;
2743 }
2744 }
2745
2746 return found_at_least_one_dependency;
2747}
2748
2749/// For each binary of a vector of binaries, if the binary is present
2750/// in at least one of the directories listed in a given vector,
2751/// construct a corpus and add it to a corpus group.
2752///
2753/// @param reader the reader used to read the binaries into an ABI corpus.
2754///
2755/// @param binaries the vector of binaries to read and add to a corpus
2756/// group.
2757///
2758/// @param deps_dirs the vector of directories where to look for the
2759/// binaries in @p binaries.
2760///
2761/// @param group the corpus group to add the corpus.
2762void
2763add_binaries_into_corpus_group(const fe_iface_sptr& reader,
2764 const vector<string>& binaries,
2765 const vector<string>& deps_dirs,
2766 corpus_group& group)
2767{
2768 vector<string> bins;
2769
2770 for (const auto& b : binaries)
2771 {
2772 string bin;
2773 if (find_file_under_dirs(deps_dirs, b, bin))
2774 bins.push_back(bin);
2775 }
2776
2777 for (const auto& b : bins)
2778 {
2779 if (group.has_corpus(b))
2780 continue;
2781
2782 reader->initialize(b);
2784 corpus_sptr c = reader->read_corpus(stat);
2785 if (c && (stat & fe_iface::STATUS_OK))
2786 group.add_corpus(c);
2787 }
2788}
2789
2790/// For each dependency of a given corpus, if it is present in at
2791/// least one of the directories listed in a given vector, construct a
2792/// corpus and add it to a corpus group.
2793///
2794/// @param reader the reader used to read the binaries into an ABI corpus.
2795///
2796/// @param korpus the corpus to consider.
2797///
2798/// @param deps_dirs the vector of directories where to look for the
2799/// dependencies of @p korpus.
2800///
2801/// @param group the corpus group to add the corpus.
2802void
2803add_dependencies_into_corpus_group(const fe_iface_sptr& reader,
2804 const corpus& korpus,
2805 const vector<string>& deps_dirs,
2806 corpus_group& group)
2807
2808{
2809 set<string> deps;
2810 if (!get_dependencies(korpus, deps_dirs, deps))
2811 return;
2812
2813 for (const auto& dep: deps)
2814 {
2815 if (group.has_corpus(dep))
2816 continue;
2817
2818 reader->initialize(dep);
2820 corpus_sptr c = reader->read_corpus(stat);
2821 if (c && (stat & fe_iface::STATUS_OK))
2822 {
2823 group.add_corpus(c);
2824 add_dependencies_into_corpus_group(reader, *c, deps_dirs, group);
2825 }
2826 }
2827}
2828
2829/// Create a corpus group made of a given korpus and a set of binaries
2830/// found in a set of directories.
2831///
2832/// @param reader the reader to use to read the binaries.
2833///
2834/// @param korpus the ABI corpus to add to the corpus group.
2835///
2836/// @param binaries the set of binaries to add to the corpus group, if
2837/// they are present one of the directories denoted by the vector @p
2838/// deps_dirs.
2839///
2840/// @param bins_dirs the directories where the binaries listed in @p
2841/// binaries are to be found.
2842///
2843/// @return a corpus group made of @p korpus and the binaries listed
2844/// in @p binaries and found in at least one of the directories found
2845/// in @p bins_dirs.
2846corpus_group_sptr
2848 const corpus_sptr& korpus,
2849 const vector<string>& binaries,
2850 const vector<string>& bins_dirs)
2851{
2852 corpus_group_sptr result (new corpus_group(korpus->get_environment(),
2853 korpus->get_path()));
2854 result->add_corpus(korpus);
2855
2856 add_binaries_into_corpus_group(reader, binaries, bins_dirs, *result);
2857
2858 return result;
2859}
2860
2861/// Create a corpus group made of a given korpus and the subset of its
2862/// dependencies that can be found found in a set of directories.
2863///
2864/// @param reader the reader to use to read the binaries.
2865///
2866/// @param korpus the ABI corpus to add to the corpus group along with
2867/// its dependencies that can be found in a subset of directories.
2868///
2869/// @param deps_dirs the directories where the dependencies of the ABI
2870/// corpus denoted by @p korpus binaries are to be found.
2871///
2872/// @return a corpus group made of @p korpus and the subset of its
2873/// dependencies found in at least one of the directories denoted by
2874/// @p deps_dirs.
2875corpus_group_sptr
2877 const corpus_sptr& korpus,
2878 const vector<string>& deps_dirs)
2879{
2880 corpus_group_sptr result (new corpus_group(korpus->get_environment(),
2881 korpus->get_path()));
2882 result->add_corpus(korpus);
2883
2884 add_dependencies_into_corpus_group(reader, *korpus, deps_dirs, *result);
2885
2886 return result;
2887}
2888
2889/// If we were given suppression specification files or kabi whitelist
2890/// files, this function parses those, come up with suppression
2891/// specifications as a result, and set them to the read context.
2892///
2893/// @param read_ctxt the read context to consider.
2894///
2895/// @param suppr_paths paths to suppression specification files that
2896/// we were given. If empty, it means we were not given any
2897/// suppression specification path.
2898///
2899/// @param kabi_whitelist_paths paths to kabi whitelist files that we
2900/// were given. If empty, it means we were not given any kabi
2901/// whitelist.
2902///
2903/// @param supprs the suppressions specifications resulting from
2904/// parsing the suppression specification files at @p suppr_paths and
2905/// the kabi whitelist at @p kabi_whitelist_paths.
2906///
2907/// @param opts the options to consider.
2908static void
2909load_generate_apply_suppressions(elf_based_reader& rdr,
2910 vector<string>& suppr_paths,
2911 vector<string>& kabi_whitelist_paths,
2912 suppressions_type& supprs)
2913{
2914 if (supprs.empty())
2915 {
2916 for (vector<string>::const_iterator i = suppr_paths.begin();
2917 i != suppr_paths.end();
2918 ++i)
2919 read_suppressions(*i, supprs);
2920
2921 const suppressions_type& wl_suppr =
2922 gen_suppr_spec_from_kernel_abi_whitelists(kabi_whitelist_paths);
2923
2924 supprs.insert(supprs.end(), wl_suppr.begin(), wl_suppr.end());
2925 }
2926
2927 rdr.add_suppressions(supprs);
2928}
2929
2930/// Test if an FTSENT pointer (resulting from fts_read) represents the
2931/// vmlinux binary.
2932///
2933/// @param entry the FTSENT to consider.
2934///
2935/// @return true iff @p entry is for a vmlinux binary.
2936static bool
2937is_vmlinux(const FTSENT *entry)
2938{
2939 if (entry == NULL
2940 || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2941 || entry->fts_info == FTS_ERR
2942 || entry->fts_info == FTS_NS)
2943 return false;
2944
2945 string fname = entry->fts_name;
2946
2947 if (fname == "vmlinux")
2948 {
2949 string dirname;
2950 dir_name(entry->fts_path, dirname);
2951 if (string_ends_with(dirname, "compressed"))
2952 return false;
2953
2954 return true;
2955 }
2956
2957 return false;
2958}
2959
2960/// Test if an FTSENT pointer (resulting from fts_read) represents a a
2961/// linux kernel module binary.
2962///
2963/// @param entry the FTSENT to consider.
2964///
2965/// @return true iff @p entry is for a linux kernel module binary.
2966static bool
2967is_kernel_module(const FTSENT *entry)
2968{
2969 if (entry == NULL
2970 || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2971 || entry->fts_info == FTS_ERR
2972 || entry->fts_info == FTS_NS)
2973 return false;
2974
2975 string fname = entry->fts_name;
2976 if (string_ends_with(fname, ".ko")
2977 || string_ends_with(fname, ".ko.xz")
2978 || string_ends_with(fname, ".ko.gz"))
2979 return true;
2980
2981 return false;
2982}
2983
2984/// Test if a given file denoted by a FTSENT* is a regular file or a
2985/// symlink.
2986///
2987/// @param entry returned by the combo fts_open/fts_read.
2988///
2989/// @return true iff @p entry is for a regular file or a symlink.
2990static bool
2991is_file(const FTSENT *entry)
2992{
2993 return (entry
2994 && (entry->fts_info == FTS_F
2995 || entry->fts_info == FTS_SL));
2996}
2997
2998/// Find a vmlinux and its kernel modules in a given directory tree.
2999///
3000/// @param from the directory tree to start looking from.
3001///
3002/// @param vmlinux_path output parameter. This is set to the path
3003/// where the vmlinux binary is found. This is set iff the returns
3004/// true and if this argument was empty to begin with.
3005///
3006/// @param module_paths output parameter. This is set to the paths of
3007/// the linux kernel module binaries.
3008///
3009/// @return true iff at least the vmlinux binary was found.
3010static bool
3011find_vmlinux_and_module_paths(const string& from,
3012 string &vmlinux_path,
3013 vector<string> &module_paths)
3014{
3015 char* path[] = {const_cast<char*>(from.c_str()), 0};
3016
3017 FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
3018 if (!file_hierarchy)
3019 return false;
3020
3021 bool found_vmlinux = !vmlinux_path.empty();
3022 FTSENT *entry;
3023 while ((entry = fts_read(file_hierarchy)))
3024 {
3025 // Skip descendents of symbolic links.
3026 if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
3027 {
3028 fts_set(file_hierarchy, entry, FTS_SKIP);
3029 continue;
3030 }
3031
3032 if (!found_vmlinux && is_vmlinux(entry))
3033 {
3034 vmlinux_path = entry->fts_path;
3035 found_vmlinux = true;
3036 }
3037 else if (is_kernel_module(entry))
3038 module_paths.push_back(entry->fts_path);
3039 }
3040
3041 fts_close(file_hierarchy);
3042
3043 return found_vmlinux;
3044}
3045
3046/// Find a vmlinux binary in a given directory tree.
3047///
3048/// @param from the directory tree to start looking from.
3049///
3050/// @param vmlinux_path output parameter
3051///
3052/// return true iff the vmlinux binary was found
3053static bool
3054find_vmlinux_path(const string& from,
3055 string &vmlinux_path)
3056{
3057 char* path[] = {const_cast<char*>(from.c_str()), 0};
3058
3059 FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
3060 if (!file_hierarchy)
3061 return false;
3062
3063 bool found_vmlinux = false;
3064 FTSENT *entry;
3065 while ((entry = fts_read(file_hierarchy)))
3066 {
3067 // Skip descendents of symbolic links.
3068 if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
3069 {
3070 fts_set(file_hierarchy, entry, FTS_SKIP);
3071 continue;
3072 }
3073
3074 if (!found_vmlinux && is_vmlinux(entry))
3075 {
3076 vmlinux_path = entry->fts_path;
3077 found_vmlinux = true;
3078 break;
3079 }
3080 }
3081
3082 fts_close(file_hierarchy);
3083
3084 return found_vmlinux;
3085}
3086
3087/// Get all the sub-directories (which contain a regular file) of a
3088/// given directory.
3089///
3090/// @param root_dir the root directory to consider.
3091///
3092/// @param dirs the sub-directories of @p root_dir which contain a
3093/// file.
3094bool
3095get_file_path_dirs_under_dir(const string& root_dir, vector<string>& dirs)
3096{
3097 char* paths[] = {const_cast<char*>(root_dir.c_str()), 0};
3098 FTS *file_hierarchy = fts_open(paths,
3099 FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
3100 if (!file_hierarchy)
3101 return false;
3102
3103 string r = root_dir;
3104 if (!string_ends_with(r, "/"))
3105 r += "/";
3106
3107 bool found_file = false;
3108 FTSENT *entry;
3109 while ((entry = fts_read(file_hierarchy)))
3110 {
3111 // Skip descendents of symbolic links.
3112 if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
3113 {
3114 fts_set(file_hierarchy, entry, FTS_SKIP);
3115 continue;
3116 }
3117
3118 if (is_file(entry))
3119 found_file = true;
3120
3121 string path = entry->fts_path;
3122 dir_name(path, path);
3123 dirs.push_back(path);
3124 }
3125
3126 fts_close(file_hierarchy);
3127
3128 return found_file;
3129}
3130
3131/// Get the paths of the vmlinux and kernel module binaries under
3132/// given directory.
3133///
3134/// @param dist_root the directory under which to look for.
3135///
3136/// @param debug_info_root_path the path to the directory under which
3137/// debug info is going to be found for binaries under @p dist_root.
3138///
3139/// @param vmlinux_path output parameter. The path of the vmlinux
3140/// binary that was found.
3141///
3142/// @param module_paths output parameter. The paths of the kernel
3143/// module binaries that were found, sorted to impose a deterministic
3144/// ordering.
3145///
3146/// @return true if at least the path to the vmlinux binary was found.
3147bool
3149 const string& debug_info_root_path,
3150 string& vmlinux_path,
3151 vector<string>& module_paths)
3152{
3153 if (!dir_exists(dist_root))
3154 return false;
3155
3156 // For now, we assume either an Enterprise Linux or a Fedora kernel
3157 // distribution directory.
3158 //
3159 // We also take into account split debug info package for these. In
3160 // this case, the content split debug info package is installed
3161 // under the 'debug_info_root_path' directory and its content is
3162 // accessible from <debug_info_root_path>/usr/lib/debug directory.
3163
3164 string kernel_modules_root = dist_root;
3165 string debug_info_root;
3166 if (dir_exists(dist_root + "/lib/modules"))
3167 {
3168 kernel_modules_root = dist_root + "/lib/modules";
3169 debug_info_root = debug_info_root_path.empty()
3170 ? dist_root + "/usr/lib/debug"
3171 : debug_info_root_path;
3172 }
3173
3174 if (dir_is_empty(debug_info_root))
3175 debug_info_root.clear();
3176
3177 bool found = false;
3178 // If vmlinux_path is empty, we want to look for it under
3179 // debug_info_root, because this is where Enterprise Linux packages
3180 // put it. Modules however are to be looked for under
3181 // kernel_modules_root.
3182 if (// So, Let's look for modules under kernel_modules_root ...
3183 find_vmlinux_and_module_paths(kernel_modules_root,
3184 vmlinux_path,
3185 module_paths)
3186 // ... and if vmlinux_path is empty, look for vmlinux under the
3187 // debug info root.
3188 || find_vmlinux_and_module_paths(debug_info_root,
3189 vmlinux_path,
3190 module_paths))
3191 found = true;
3192
3193 std::sort(module_paths.begin(), module_paths.end());
3194
3195 return found;
3196}
3197
3198/// Get the path of the vmlinux binary under the given directory, that
3199/// must have been generated either from extracting a package.
3200///
3201/// @param from 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/// @return true if the path to the vmlinux binary was found.
3207bool
3209 string& vmlinux_path)
3210{
3211 if (!dir_exists(from))
3212 return false;
3213
3214 // For now, we assume the possibility of having either an Enterprise
3215 // Linux or a Fedora kernel distribution directory. In those cases,
3216 // the vmlinux binary is located under the /lib/modules
3217 // sub-directory. So we might as well save some time by picking it
3218 // from there directly.
3219
3220 string dist_root = from;
3221 if (dir_exists(dist_root + "/lib/modules"))
3222 dist_root += "/lib/modules";
3223
3224 bool found = false;
3225 if (find_vmlinux_path(dist_root, vmlinux_path))
3226 found = true;
3227
3228 return found;
3229}
3230
3231/// Get the paths of the vmlinux and kernel module binaries under
3232/// given directory.
3233///
3234/// @param dist_root the directory under which to look for.
3235///
3236/// @param vmlinux_path output parameter. The path of the vmlinux
3237/// binary that was found.
3238///
3239/// @param module_paths output parameter. The paths of the kernel
3240/// module binaries that were found.
3241///
3242/// @return true if at least the path to the vmlinux binary was found.
3243bool
3245 string& vmlinux_path,
3246 vector<string>& module_paths)
3247{
3248 string debug_info_root_path;
3249 return get_binary_paths_from_kernel_dist(dist_root,
3250 debug_info_root_path,
3251 vmlinux_path,
3252 module_paths);
3253}
3254
3255/// It builds a @ref corpus_group made of vmlinux kernel file and
3256/// the kernel modules found under @p root directory and under its
3257/// sub-directories, recursively.
3258///
3259/// @param rdr the raeder that should be used to extract the debug
3260/// infomation from the linux kernel and its modules used to build
3261/// the corpora @p group.
3262///
3263/// @param the group @ref corpus_group to be built.
3264///
3265/// @param vmlinux the path to the vmlinux binary.
3266///
3267/// @param modules a vector with the paths to the linux kernel
3268/// modules.
3269///
3270/// @param root the path of the directory under which the kernel
3271/// kernel modules were found.
3272///
3273/// @param di_root the directory in absolute path which debug
3274/// info is to be found for binaries under director @p root
3275///
3276/// @param suppr_paths the paths to the suppression specifications to
3277/// apply while loading the binaries.
3278///
3279/// @param kabi_wl_path the paths to the kabi whitelist files to take
3280/// into account while loading the binaries.
3281///
3282/// @param supprs the suppressions resulting from parsing the
3283/// suppression specifications at @p suppr_paths. This is set by this
3284/// function.
3285///
3286/// @param verbose true if the function has to emit some verbose
3287/// messages.
3288///
3289/// @param t time to trace time spent in each step.
3290///
3291/// @param env the environment to create the corpus_group in.
3292static void
3293load_vmlinux_corpus(elf_based_reader_sptr rdr,
3294 corpus_group_sptr& group,
3295 const string& vmlinux,
3296 vector<string>& modules,
3297 const string& root,
3298 vector<string>& di_roots,
3299 vector<string>& suppr_paths,
3300 vector<string>& kabi_wl_paths,
3301 suppressions_type& supprs,
3302 bool verbose,
3303 timer& t,
3304 environment& env)
3305{
3307 rdr->options().do_log = verbose;
3308
3309 if (verbose)
3310 {
3311 std::cerr << "Loading stable lists:'";
3312 for (auto s : kabi_wl_paths)
3313 std::cerr << s << ",";
3314 std::cerr << "'...\n";
3315 }
3316 t.start();
3317 load_generate_apply_suppressions(*rdr, suppr_paths,
3318 kabi_wl_paths, supprs);
3319 t.stop();
3320
3321 if (verbose)
3322 std::cerr << "loaded white list and generated suppr spec in: "
3323 << t
3324 << "\n";
3325
3326 group.reset(new corpus_group(env, root));
3327
3328 rdr->corpus_group(group);
3329
3330 if (verbose)
3331 std::cerr << "reading kernel binary '"
3332 << vmlinux << "' ...\n" << std::flush;
3333
3334 // Read the vmlinux corpus and add it to the group.
3335 rdr->options().load_in_linux_kernel_mode = true;
3336 t.start();
3337 rdr->read_and_add_corpus_to_group(*group, status);
3338 t.stop();
3339
3340 if (verbose)
3341 std::cerr << vmlinux
3342 << " reading DONE in:"
3343 << t << "\n";
3344
3345 if (group->is_empty())
3346 return;
3347
3348 // Now add the corpora of the modules to the corpus group.
3349 int total_nb_modules = modules.size();
3350 int cur_module_index = 1;
3351 for (vector<string>::const_iterator m = modules.begin();
3352 m != modules.end();
3353 ++m, ++cur_module_index)
3354 {
3355 if (verbose)
3356 std::cerr << "reading module '"
3357 << *m << "' ("
3358 << cur_module_index
3359 << "/" << total_nb_modules
3360 << ") ...\n" << std::flush;
3361
3362 rdr->initialize(*m, di_roots);
3363
3364 load_generate_apply_suppressions(*rdr, suppr_paths,
3365 kabi_wl_paths, supprs);
3366
3367 rdr->corpus_group(group);
3368
3369 t.start();
3370 rdr->read_and_add_corpus_to_group(*group, status);
3371 t.stop();
3372 if (verbose)
3373 std::cerr << "Module reading DONE in: "
3374 << t << " for '" << *m
3375 << "' (" << cur_module_index << "/" << total_nb_modules << ")"
3376 << "'\n";
3377 }
3378
3379 if (verbose)
3380 {
3381 std::cerr << "Total number of functions: "
3382 << group->get_functions().size() << "\n";
3383 std::cerr << "Total number of variables: "
3384 << group->get_variables().size() << "\n";
3385 }
3386}
3387
3388/// Walk a given directory and build an instance of @ref corpus_group
3389/// from the vmlinux kernel binary and the linux kernel modules found
3390/// under that directory and under its sub-directories, recursively.
3391///
3392/// The main corpus of the @ref corpus_group is made of the vmlinux
3393/// binary. The other corpora are made of the linux kernel binaries.
3394///
3395/// @param root the path of the directory under which the kernel
3396/// kernel modules are to be found. The vmlinux can also be found
3397/// somewhere under that directory, but if it's not in there, its path
3398/// can be set to the @p vmlinux_path parameter.
3399///
3400/// @param debug_info_root the directory under which debug info is to
3401/// be found for binaries under director @p root.
3402///
3403/// @param vmlinux_path the path to the vmlinux binary, if that binary
3404/// is not under the @p root directory. If this is empty, then it
3405/// means the vmlinux binary is to be found under the @p root
3406/// directory.
3407///
3408/// @param suppr_paths the paths to the suppression specifications to
3409/// apply while loading the binaries.
3410///
3411/// @param kabi_wl_path the paths to the kabi whitelist files to take
3412/// into account while loading the binaries.
3413///
3414/// @param supprs the suppressions resulting from parsing the
3415/// suppression specifications at @p suppr_paths. This is set by this
3416/// function.
3417///
3418/// @param verbose true if the function has to emit some verbose
3419/// messages.
3420///
3421/// @param env the environment to create the corpus_group in.
3422///
3423/// @param options the options to be used by the @abigail::fe_iface
3424/// reader to construct the ABI corpus. The options object needs to
3425/// be created by the caller code.
3426///
3427/// @param requested_fe_kind the kind of front-end requested by the
3428/// user.
3429corpus_group_sptr
3431 const string debug_info_root,
3432 const string& vmlinux_path,
3433 vector<string>& suppr_paths,
3434 vector<string>& kabi_wl_paths,
3435 suppressions_type& supprs,
3436 bool verbose,
3437 environment& env,
3438 const fe_iface::options_type& options,
3439 corpus::origin requested_fe_kind)
3440{
3441 string vmlinux = vmlinux_path;
3442 corpus_group_sptr group;
3443 vector<string> modules;
3444
3445 if (verbose)
3446 std::cerr << "Analysing kernel dist root '"
3447 << root
3448 << "' with vmlinux path: '"
3449 << vmlinux_path
3450 << "' ... \n" << std::flush;
3451
3452 timer t;
3453
3454 t.start();
3455 bool got_binary_paths =
3456 get_binary_paths_from_kernel_dist(root, debug_info_root, vmlinux, modules);
3457 t.stop();
3458
3459 if (verbose)
3460 std::cerr << "Kernel tree binary paths analysis DONE in: " << t << "\n";
3461
3462 if (got_binary_paths)
3463 {
3464 string di_root =
3465 make_path_absolute(debug_info_root);
3466 vector<string> di_roots;
3467 di_roots.push_back(di_root);
3468
3469#ifdef WITH_CTF
3470 string di_root_ctf;
3471 if (requested_fe_kind & corpus::CTF_ORIGIN)
3472 {
3473 di_root_ctf = make_path_absolute(root);
3474 di_roots.push_back(di_root_ctf);
3475 }
3476#endif
3477
3478 abigail::elf_based_reader_sptr reader =
3480 di_roots,
3481 env,
3482 requested_fe_kind,
3483 options);
3484 ABG_ASSERT(reader);
3485 load_vmlinux_corpus(reader, group, vmlinux,
3486 modules, root, di_roots,
3487 suppr_paths, kabi_wl_paths,
3488 supprs, verbose, t, env);
3489 }
3490
3491 return group;
3492}
3493
3494/// Walk a given directory and build an instance of @ref corpus_group
3495/// from the vmlinux kernel binary and the linux kernel modules found
3496/// under that directory and under its sub-directories, recursively.
3497///
3498/// The main corpus of the @ref corpus_group is made of the vmlinux
3499/// binary. The other corpora are made of the linux kernel binaries.
3500///
3501/// @param root the path of the directory under which the kernel
3502/// kernel modules are to be found. The vmlinux can also be found
3503/// somewhere under that directory, but if it's not in there, its path
3504/// can be set to the @p vmlinux_path parameter.
3505///
3506/// @param debug_info_root the directory under which debug info is to
3507/// be found for binaries under director @p root.
3508///
3509/// @param vmlinux_path the path to the vmlinux binary, if that binary
3510/// is not under the @p root directory. If this is empty, then it
3511/// means the vmlinux binary is to be found under the @p root
3512/// directory.
3513///
3514/// @param suppr_paths the paths to the suppression specifications to
3515/// apply while loading the binaries.
3516///
3517/// @param kabi_wl_path the paths to the kabi whitelist files to take
3518/// into account while loading the binaries.
3519///
3520/// @param supprs the suppressions resulting from parsing the
3521/// suppression specifications at @p suppr_paths. This is set by this
3522/// function.
3523///
3524/// @param verbose true if the function has to emit some verbose
3525/// messages.
3526///
3527/// @param env the environment to create the corpus_group in.
3528///
3529/// @param requested_fe_kind the kind of front-end requested by the
3530/// user.
3531corpus_group_sptr
3533 const string debug_info_root,
3534 const string& vmlinux_path,
3535 vector<string>& suppr_paths,
3536 vector<string>& kabi_wl_paths,
3537 suppressions_type& supprs,
3538 bool verbose,
3539 environment& env,
3540 corpus::origin requested_fe_kind)
3541{
3543 return build_corpus_group_from_kernel_dist_under(root, debug_info_root,
3544 vmlinux_path, suppr_paths,
3545 kabi_wl_paths, supprs,
3546 verbose, env, o,
3547 requested_fe_kind);
3548}
3549
3550/// Create the best elf based reader (or front-end), given an ELF
3551/// file.
3552///
3553/// This function looks into the ELF file; depending on the kind of
3554/// debug info it contains and on the request of the user, the "best"
3555/// front-end is created.
3556///
3557/// If the user requested the use of the CTF front-end, then, if the
3558/// file contains CTF debug info, the CTF front-end is created,
3559/// assuming libabigail is built with CTF support.
3560///
3561/// If the binary ONLY has CTF debug info, then CTF front-end is
3562/// created, even if the user hasn't explicitly requested the creation
3563/// of the CTF front-end.
3564///
3565/// Otherwise, by default, the DWARF front-end is created.
3566///
3567/// @param elf_file_path a path to the ELF file to consider
3568///
3569/// @param debug_info_root_paths a vector of the paths where to look
3570/// for debug info, if applicable.
3571///
3572/// @param env the environment to use for the front-end.
3573///
3574/// @param requested_fe_kind the kind of front-end specifically
3575/// requested by the user. At the moment, only the CTF front-end can
3576/// be requested, using the "--ctf" command line option on some tools
3577/// using the library.
3578///
3579/// @param options the options to set to the newly created instance of
3580/// @ref fe_iface. The options object needs to be created by the
3581/// caller code.
3582///
3583/// @return the ELF based Reader that is better adapted for the binary
3584/// designated by @p elf_file_path.
3585elf_based_reader_sptr
3586create_best_elf_based_reader(const string& elf_file_path,
3587 const vector<string>& debug_info_root_paths,
3588 environment& env,
3589 corpus::origin requested_fe_kind,
3590 const abigail::fe_iface::options_type& options)
3591{
3592 elf_based_reader_sptr result;
3593 if (guess_file_type(elf_file_path) != FILE_TYPE_ELF)
3594 return result;
3595
3596 if (requested_fe_kind & corpus::CTF_ORIGIN)
3597 {
3598#ifdef WITH_CTF
3599 if (file_has_ctf_debug_info(elf_file_path, debug_info_root_paths))
3600 result = ctf::create_reader(elf_file_path, debug_info_root_paths,
3601 env, options);
3602#endif
3603 }
3604 else if (requested_fe_kind & corpus::BTF_ORIGIN)
3605 {
3606#ifdef WITH_BTF
3607 if (file_has_btf_debug_info(elf_file_path, debug_info_root_paths))
3608 result = btf::create_reader(elf_file_path, debug_info_root_paths,
3609 env, options);
3610#endif
3611 }
3612 else
3613 {
3614 // The user hasn't formally requested the use of the CTF front-end.
3615#ifdef WITH_CTF
3616 if (!file_has_dwarf_debug_info(elf_file_path, debug_info_root_paths)
3617 && file_has_ctf_debug_info(elf_file_path, debug_info_root_paths))
3618 // The file has CTF debug info and no DWARF, let's use the CTF
3619 // front end even if it wasn't formally requested by the user.
3620 result = ctf::create_reader(elf_file_path, debug_info_root_paths,
3621 env, options);
3622#endif
3623
3624#ifdef WITH_BTF
3625 if (!file_has_dwarf_debug_info(elf_file_path, debug_info_root_paths)
3626 && file_has_btf_debug_info(elf_file_path, debug_info_root_paths))
3627 // The file has BTF debug info and no BTF, let's use the BTF
3628 // front-end even if it wasn't formally requested by the user.
3629 result = btf::create_reader(elf_file_path, debug_info_root_paths,
3630 env, options);
3631#endif
3632 }
3633
3634 if (!result)
3635 {
3636 // This is the default case. At worst, the DWARF reader knows
3637 // how to handle just ELF data for the case where there is no
3638 // DWARF debug info present.
3639 result = dwarf::create_reader(elf_file_path,
3640 debug_info_root_paths,
3641 env, options);
3642 }
3643
3644 return result;
3645}
3646
3647
3648/// Create the best elf based reader (or front-end), given an ELF
3649/// file.
3650///
3651/// This function looks into the ELF file; depending on the kind of
3652/// debug info it contains and on the request of the user, the "best"
3653/// front-end is created.
3654///
3655/// If the user requested the use of the CTF front-end, then, if the
3656/// file contains CTF debug info, the CTF front-end is created,
3657/// assuming libabigail is built with CTF support.
3658///
3659/// If the binary ONLY has CTF debug info, then CTF front-end is
3660/// created, even if the user hasn't explicitly requested the creation
3661/// of the CTF front-end.
3662///
3663/// Otherwise, by default, the DWARF front-end is created.
3664///
3665/// @param elf_file_path a path to the ELF file to consider
3666///
3667/// @param debug_info_root_paths a vector of the paths where to look
3668/// for debug info, if applicable.
3669///
3670/// @param env the environment to use for the front-end.
3671///
3672/// @param requested_fe_kind the kind of front-end specifically
3673/// requested by the user. At the moment, only the CTF front-end can
3674/// be requested, using the "--ctf" command line option on some tools
3675/// using the library.
3676///
3677/// @return the ELF based Reader that is better adapted for the binary
3678/// designated by @p elf_file_path.
3679elf_based_reader_sptr
3680create_best_elf_based_reader(const string& elf_file_path,
3681 const vector<string>& debug_info_root_paths,
3682 environment& env,
3683 corpus::origin requested_debug_info_kind)
3684{
3686 return create_best_elf_based_reader(elf_file_path, debug_info_root_paths,
3687 env, requested_debug_info_kind, o);
3688}
3689
3690/// ---------------------------------------------------
3691/// <xz_decompressor definition>
3692///----------------------------------------------------
3693
3694/// The private data of the @ref xz_decompressor_type class.
3695struct xz_decompressor_type::priv
3696{
3697 std::istream& xz_istream;
3698 lzma_stream lzma;
3699 // A 100k bytes buffer for xz data coming from the xz'ed istream.
3700 // That buffer is going to be fed into the lzma decoding machinery.
3701 char inbuf[1024 * 100] = {};
3702 // A 100k bytes buffer for decompressed data coming out of the lzma
3703 // machinery
3704 char outbuf[1024 * 100] = {};
3705
3706 priv(std::istream& i)
3707 : xz_istream(i),
3708 lzma(LZMA_STREAM_INIT)
3709 {}
3710};// end xz_decompressor_type::priv
3711
3712/// Constructor of the @ref xz_decompressor_type class.
3713///
3714/// @param xz_istream the input stream containing the xz-compressed
3715/// data to decompress.
3716xz_decompressor_type::xz_decompressor_type(std::istream& xz_istream)
3717 : priv_(new priv(xz_istream))
3718{
3719 // Initialize the native LZMA stream to decompress.
3720 lzma_ret status = lzma_stream_decoder(&priv_->lzma,
3721 UINT64_MAX,
3722 LZMA_CONCATENATED);
3723 ABG_ASSERT(status == LZMA_OK);
3724}
3725
3726/// Destructor of the @ref xz_decompressor_type class.
3728{
3729 lzma_end(&priv_->lzma);
3730}
3731
3732/// The implementation of the virtual protected
3733/// std:streambuf::underflow method. This method is invoked by the
3734/// std::streambuf facility to re-fill its internals buffers with data
3735/// coming from the associated input stream and to update the gptr()
3736/// and egptr() pointers by using the std::streambuf::setg method.
3737///
3738/// This is where the decompression using the lzma library is
3739/// performed.
3740std::streambuf::int_type
3742{
3743 if (gptr() < egptr())
3744 return *gptr();
3745
3746 // Let's read 'nr' bytes of xz data into inbuf
3747 priv_->xz_istream.read(priv_->inbuf, sizeof(priv_->inbuf));
3748 size_t nr = priv_->xz_istream.gcount();
3749
3750 if (nr != 0)
3751 {
3752 // So there is fresh compressed input to be decompressed. Let's
3753 // prepare the lzma input stream machinery then.
3754 priv_->lzma.avail_in = nr;
3755 priv_->lzma.next_in = reinterpret_cast<uint8_t*>(priv_->inbuf);
3756 }
3757
3758 if (priv_->lzma.avail_out || priv_->lzma.avail_in)
3759 {
3760 // There is still compressed data in the lzma context to
3761 // decompress, so let's tell lzma where to put the decompressed
3762 // data.
3763 priv_->lzma.avail_out = sizeof(priv_->outbuf);
3764 priv_->lzma.next_out = reinterpret_cast<uint8_t*>(priv_->outbuf);
3765 }
3766
3767 // Let's now ask the lzma machinery to decompress the next_in buffer
3768 // and put the result into the next_out buffer.
3769 lzma_ret result = lzma_code(&priv_->lzma, LZMA_RUN);
3770 if (result != LZMA_OK && result != LZMA_STREAM_END)
3771 {
3772 // TODO: list the possible error codes and tell them explicitely
3773 // to the user, just like what is done in
3774 // https://github.com/tukaani-project/xz/blob/master/doc/examples/02_decompress.c.
3775 std::ostringstream o;
3776 o << "LZMA decompression failed;"
3777 << " return code of lzma_code() is : "
3778 << result;
3779 throw std::runtime_error(o.str());
3780 }
3781
3782 // Let's get the number of bytes decompressed by the lzma
3783 // machinery. I got this from the example in the xz code base at
3784 // https://github.com/tukaani-project/xz/blob/master/doc/examples/02_decompress.c.
3785 size_t nr_decompressed_bytes = sizeof(priv_->outbuf) - priv_->lzma.avail_out;
3786
3787 // Now set the relevant index pointers of this streambuf.
3788 setg(priv_->outbuf, priv_->outbuf, priv_->outbuf + nr_decompressed_bytes);
3789
3790 if (nr_decompressed_bytes > 0)
3791 return *gptr();
3792
3793 // If we reached this point, then it means we there is no more
3794 // decompressed bytes in the decompressed stream. Tell the lzma
3795 // machinery that we've reached the end of the data.
3796 result = lzma_code(&priv_->lzma, LZMA_FINISH);
3797 ABG_ASSERT(result == LZMA_OK || result == LZMA_STREAM_END);
3798 return traits_type::eof();
3799}
3800
3801/// ---------------------------------------------------
3802/// </xz_decompressor definition>
3803///----------------------------------------------------
3804
3805}//end namespace tools_utils
3806
3808
3809/// Dump (to the standard error stream) two sequences of strings where
3810/// each string represent one of the functions in the two sequences of
3811/// functions given in argument to this function.
3812///
3813/// @param a_begin the begin iterator for the first input sequence of
3814/// functions.
3815///
3816/// @parm a_end the end iterator for the first input sequence of
3817/// functions.
3818///
3819/// @param b_begin the begin iterator for the second input sequence of
3820/// functions.
3821///
3822/// @param b_end the end iterator for the second input sequence of functions.
3823void
3824dump_functions_as_string(std::vector<function_decl*>::const_iterator a_begin,
3825 std::vector<function_decl*>::const_iterator a_end,
3826 std::vector<function_decl*>::const_iterator b_begin,
3827 std::vector<function_decl*>::const_iterator b_end)
3828{abigail::fns_to_str(a_begin, a_end, b_begin, b_end, std::cerr);}
3829
3830/// Dump (to the standard error output stream) a pretty representation
3831/// of the signatures of two sequences of functions.
3832///
3833/// @param a_begin the start iterator of the first input sequence of functions.
3834///
3835/// @param a_end the end iterator of the first input sequence of functions.
3836///
3837/// @param b_begin the start iterator of the second input sequence of functions.
3838///
3839/// @param b_end the end iterator of the second input sequence of functions.
3840void
3841dump_function_names(std::vector<function_decl*>::const_iterator a_begin,
3842 std::vector<function_decl*>::const_iterator a_end,
3843 std::vector<function_decl*>::const_iterator b_begin,
3844 std::vector<function_decl*>::const_iterator b_end)
3845{
3846 std::vector<function_decl*>::const_iterator i;
3847 std::ostream& o = std::cerr;
3848 for (i = a_begin; i != a_end; ++i)
3849 o << (*i)->get_pretty_representation() << "\n";
3850
3851 o << " ->|<- \n";
3852 for (i = b_begin; i != b_end; ++i)
3853 o << (*i)->get_pretty_representation() << "\n";
3854 o << "\n";
3855}
3856
3857/// Compare two functions that are in a vector of functions.
3858///
3859/// @param an iterator to the beginning of the the sequence of functions.
3860///
3861/// @param f1_index the index of the first function to compare.
3862///
3863/// @param f2_inde the index of the second function to compare
3864bool
3865compare_functions(vector<function_decl*>::const_iterator base,
3866 unsigned f1_index, unsigned f2_index)
3867{
3868 function_decl* fn1 = base[f1_index];
3869 function_decl* fn2 = base[f2_index];
3870
3871 return *fn1 == *fn2;
3872}
3873
3874}//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:1790
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:470
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:95
origin
This abstracts where the corpus comes from. That is, either it has been read from the native xml form...
Definition abg-corpus.h:121
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:216
Abstraction for a function declaration.
Definition abg-ir.h:3167
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...
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, const fe_iface::options_type &options)
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, const fe_iface::options_type &options)
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:1734
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.
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, const abigail::fe_iface::options_type &options)
Create the best elf based reader (or front-end), given an ELF file.
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_prefix(const string &input_string, const string &suffix, string &prefix)
Get the prefix of a string, given a suffix to consider.
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.
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**>.
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, const fe_iface::options_type &options, corpus::origin requested_fe_kind)
Walk a given directory and build an instance of corpus_group from the vmlinux kernel binary and the l...
bool 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.
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...
The generic options that control the behaviour of all Front-End interfaces.