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