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