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
59// <headers defining libabigail's API go under here>
60ABG_BEGIN_EXPORT_DECLARATIONS
61
62#include <abg-ir.h>
63#include "abg-config.h"
64#include "abg-tools-utils.h"
65
66ABG_END_EXPORT_DECLARATIONS
67// </headers defining libabigail's API>
68
69using std::string;
70
71namespace abigail
72{
73
74using namespace abigail::suppr;
75using namespace abigail::ini;
76
77/// @brief Namespace for a set of utility function used by tools based
78/// on libabigail.
79namespace tools_utils
80{
81
82/// This function needs to be called before any libabigail function.
83///
84/// Users of libabigail must call it prior to using any of the
85/// functions of the library.
86///
87/// It intends to initialize the underlying libraries that might need
88/// initialization, especially, libxml2, in multi-threaded environments.
89void
91{
92 LIBXML_TEST_VERSION;
93 xmlInitParser();
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<char**>& 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<char**>& 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 && *path && 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<char**>& 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/// @return the type of content guessed.
1836guess_file_type(const string& file_path)
1837{
1838 if (is_dir(file_path))
1839 return FILE_TYPE_DIR;
1840
1841 if (string_ends_with(file_path, ".tar")
1842 || string_ends_with(file_path, ".tar.gz")
1843 || string_ends_with(file_path, ".tgz")
1844 || string_ends_with(file_path, ".tar.bz2")
1845 || string_ends_with(file_path, ".tbz2")
1846 || string_ends_with(file_path, ".tbz")
1847 || string_ends_with(file_path, ".tb2")
1848 || string_ends_with(file_path, ".tar.xz")
1849 || string_ends_with(file_path, ".txz")
1850 || string_ends_with(file_path, ".tar.lzma")
1851 || string_ends_with(file_path, ".tar.lz")
1852 || string_ends_with(file_path, ".tlz")
1853 || string_ends_with(file_path, ".tar.Z")
1854 || string_ends_with(file_path, ".taz")
1855 || string_ends_with(file_path, ".tz"))
1856 return FILE_TYPE_TAR;
1857
1859 compression_kind compr_kind = COMPRESSION_KIND_UNKNOWN;
1860 shared_ptr<std::streambuf> decompressor_streambuf;
1861
1862 if (string_ends_with(file_path, ".lzma")
1863 || string_ends_with(file_path, ".lz")
1864 || string_ends_with(file_path, ".xz"))
1865 compr_kind = COMPRESSION_KIND_XZ;
1866 // else if there are other compression schemes supported, recognize
1867 // their file suffix here!
1868
1869 do
1870 {
1871 shared_ptr<ifstream> input_fstream(new ifstream(file_path.c_str(),
1872 ifstream::binary));
1873 shared_ptr<istream> input_stream = input_fstream;
1874
1875 if (compr_kind != COMPRESSION_KIND_UNKNOWN)
1876 decompressor_streambuf = get_decompressed_streambuf(*input_stream,
1877 compr_kind);
1878
1879 if (decompressor_streambuf)
1880 input_stream.reset(new istream(decompressor_streambuf.get()));
1881
1882 r = guess_file_type(*input_stream);
1883
1884 input_fstream->close();
1885
1886 if (!decompressor_streambuf)
1887 {
1888 // So we haven't attempted to decompress the input stream.
1889 //
1890 // Have we found out that it was compressed nonetheless?
1891 compr_kind = is_compressed_file_type(r);
1892 if (compr_kind)
1893 {
1894 // yes, we found out the input file is compressed, so we
1895 // do have the means to decompress it. However, we
1896 // haven't yet gotten the de-compressor; that might be
1897 // because we detected the compression just by looking
1898 // at the file name suffix. Let's go back to calling
1899 // get_decompressed_streambuf again to get the
1900 // decompressor.
1901 ;
1902 }
1903 else
1904 // No the file is not compressed let's get out of here.
1905 break;
1906 }
1907 } while (!decompressor_streambuf && compr_kind);
1908
1909 return r;
1910}
1911
1912/// Get the package name of a .deb package.
1913///
1914/// @param str the string containing the .deb NVR.
1915///
1916/// @param name output parameter. This is set with the package name
1917/// of the .deb package iff the function returns true.
1918///
1919/// @return true iff the function successfully finds the .deb package
1920/// name.
1921bool
1922get_deb_name(const string& str, string& name)
1923{
1924 if (str.empty() || str[0] == '_')
1925 return false;
1926
1927 string::size_type str_len = str.length(), i = 0 ;
1928
1929 for (; i < str_len; ++i)
1930 {
1931 if (str[i] == '_')
1932 break;
1933 }
1934
1935 if (i == str_len)
1936 return false;
1937
1938 name = str.substr(0, i);
1939 return true;
1940}
1941
1942/// Get the package name of an rpm package.
1943///
1944/// @param str the string containing the NVR of the rpm.
1945///
1946/// @param name output parameter. This is set with the package name
1947/// of the rpm package iff the function returns true.
1948///
1949/// @return true iff the function successfully finds the rpm package
1950/// name.
1951bool
1952get_rpm_name(const string& str, string& name)
1953{
1954 if (str.empty() || str[0] == '-')
1955 return false;
1956
1957 string::size_type str_len = str.length(), i = 0;
1958 string::value_type c;
1959
1960 for (; i < str_len; ++i)
1961 {
1962 c = str[i];
1963 string::size_type next_index = i + 1;
1964 if ((next_index < str_len) && c == '-' && isdigit(str[next_index]))
1965 break;
1966 }
1967
1968 if (i == str_len)
1969 return false;
1970
1971 name = str.substr(0, i);
1972
1973 return true;
1974}
1975
1976/// Get the architecture string from the NVR of an rpm.
1977///
1978/// @param str the NVR to consider.
1979///
1980/// @param arch output parameter. Is set to the resulting
1981/// archirecture string iff the function returns true.
1982///
1983/// @return true iff the function could find the architecture string
1984/// from the NVR.
1985bool
1986get_rpm_arch(const string& str, string& arch)
1987{
1988 if (str.empty())
1989 return false;
1990
1991 if (!string_ends_with(str, ".rpm"))
1992 return false;
1993
1994 string::size_type str_len = str.length(), i = 0;
1995 string::value_type c;
1996 string::size_type last_dot_index = 0, dot_before_last_index = 0;
1997
1998 for (i = str_len - 1; i > 0; --i)
1999 {
2000 c = str[i];
2001 if (c == '.')
2002 {
2003 last_dot_index = i;
2004 break;
2005 }
2006 }
2007
2008 if (i == 0)
2009 return false;
2010
2011 for(--i; i > 0; --i)
2012 {
2013 c = str[i];
2014 if (c == '.')
2015 {
2016 dot_before_last_index = i;
2017 break;
2018 }
2019 }
2020
2021 if (i == 0)
2022 return false;
2023
2024 arch = str.substr(dot_before_last_index + 1,
2025 last_dot_index - dot_before_last_index - 1);
2026
2027 return true;
2028}
2029
2030/// Tests if a given file name designates a kernel package.
2031///
2032/// @param file_path the path to the file to consider.
2033///
2034/// @param file_type the type of the file @p file_name.
2035///
2036/// @return true iff @p file_name of kind @p file_type designates a
2037/// kernel package.
2038bool
2040{
2041 bool result = false;
2042
2043 if (file_type == FILE_TYPE_RPM)
2044 {
2045 if (rpm_contains_file(file_path, "vmlinuz"))
2046 result = true;
2047 }
2048 else if (file_type == FILE_TYPE_DEB)
2049 {
2050 string file_name;
2051 base_name(file_path, file_name);
2052 string package_name;
2053 if (get_deb_name(file_name, package_name))
2054 result = (string_begins_with(package_name, "linux-image"));
2055 }
2056
2057 return result;
2058}
2059
2060/// Test if an RPM package contains a given file.
2061///
2062/// @param rpm_path the path to the RPM package.
2063///
2064/// @param file_name the file name to test the presence for in the
2065/// rpm.
2066///
2067/// @return true iff the file named @file_name is present in the RPM.
2068bool
2069rpm_contains_file(const string& rpm_path, const string& file_name)
2070{
2071 vector<string> query_output;
2072 // We don't check the return value of this command because on some
2073 // system, the command can issue errors but still emit a valid
2074 // output. We'll rather rely on the fact that the command emits a
2075 // valid output or not.
2077 + rpm_path + " 2> /dev/null",
2078 query_output);
2079
2080 for (auto& line : query_output)
2081 {
2082 line = trim_white_space(line);
2083 if (string_ends_with(line, file_name))
2084 return true;
2085 }
2086
2087 return false;
2088}
2089
2090/// Tests if a given file name designates a kernel debuginfo package.
2091///
2092/// @param file_name the file name to consider.
2093///
2094/// @param file_type the type of the file @p file_name.
2095///
2096/// @return true iff @p file_name of kind @p file_type designates a
2097/// kernel debuginfo package.
2098bool
2100{
2101 bool result = false;
2102 string package_name;
2103
2104 if (file_type == FILE_TYPE_RPM)
2105 {
2106 if (!get_rpm_name(file_name, package_name))
2107 return false;
2108 result = (package_name == "kernel-debuginfo");
2109 }
2110 else if (file_type == FILE_TYPE_DEB)
2111 {
2112 if (!get_deb_name(file_name, package_name))
2113 return false;
2114 result = (string_begins_with(package_name, "linux-image")
2115 && (string_ends_with(package_name, "-dbg")
2116 || string_ends_with(package_name, "-dbgsyms")));
2117 }
2118
2119 return result;
2120}
2121
2122/// The delete functor of a char buffer that has been created using
2123/// malloc.
2124struct malloced_char_star_deleter
2125{
2126 void
2127 operator()(char* ptr)
2128 {free(ptr);}
2129};
2130
2131/// Return a copy of the path given in argument, turning it into an
2132/// absolute path by prefixing it with the concatenation of the result
2133/// of get_current_dir_name() and the '/' character.
2134///
2135/// The result being an shared_ptr to char*, it should manage its
2136/// memory by itself and the user shouldn't need to wory too much for
2137/// that.
2138///
2139/// @param p the path to turn into an absolute path.
2140///
2141/// @return a shared pointer to the resulting absolute path.
2142std::shared_ptr<char>
2144{
2145 using std::shared_ptr;
2146
2147 shared_ptr<char> result;
2148
2149 if (p && p[0] != '/')
2150 {
2151 shared_ptr<char> pwd(get_current_dir_name(),
2152 malloced_char_star_deleter());
2153 string s = string(pwd.get()) + "/" + p;
2154 result.reset(strdup(s.c_str()), malloced_char_star_deleter());
2155 }
2156 else
2157 result.reset(strdup(p), malloced_char_star_deleter());
2158
2159 return result;
2160}
2161
2162/// Return a copy of the path given in argument, turning it into an
2163/// absolute path by prefixing it with the concatenation of the result
2164/// of get_current_dir_name() and the '/' character.
2165///
2166/// The result being a pointer to an allocated memory region, it must
2167/// be freed by the caller.
2168///
2169/// @param p the path to turn into an absolute path.
2170///
2171/// @return a pointer to the resulting absolute path. It must be
2172/// freed by the caller.
2173char*
2175{
2176 char* result = 0;
2177
2178 if (p && p[0] != '/')
2179 {
2180 char* pwd = get_current_dir_name();
2181 string s = string(pwd) + "/" + p;
2182 free(pwd);
2183 result = strdup(s.c_str());
2184 }
2185 else
2186 result = strdup(p);
2187
2188 return result;
2189}
2190
2191/// This is a sub-routine of gen_suppr_spec_from_headers and
2192/// handle_fts_entry.
2193///
2194/// It setups a type suppression which is meant to keep types defined
2195/// in a given file and suppress all other types.
2196///
2197/// @param file_path the path to the file that defines types that are
2198/// meant to be kept by the type suppression. All other types defined
2199/// in other files are to be suppressed. Note that this file path is
2200/// added to the vector returned by
2201/// type_suppression::get_source_locations_to_keep()
2202///
2203/// @param suppr the type suppression to setup. If this smart pointer
2204/// is nil then a new instance @ref type_suppression is created and
2205/// this variable is made to point to it.
2206static void
2207handle_file_entry(const string& file_path,
2208 type_suppression_sptr& suppr)
2209{
2210 if (!suppr)
2211 {
2213 /*type_name_regexp=*/"",
2214 /*type_name=*/""));
2215
2216 // Types that are defined in system headers are usually
2217 // OK to be considered as public types.
2218 suppr->set_source_location_to_keep_regex_str("^/usr/include/");
2219 suppr->set_is_artificial(true);
2220 }
2221
2222 // And types that are defined in header files that are under
2223 // the header directory file we are looking are to be
2224 // considered public types too.
2225 suppr->get_source_locations_to_keep().insert(file_path);
2226}
2227
2228/// This is a sub-routine of gen_suppr_spec_from_headers.
2229///
2230/// @param entry if this file represents a regular (or symlink) file,
2231/// then its file name is going to be added to the vector returned by
2232/// type_suppression::get_source_locations_to_keep().
2233///
2234/// @param if @p entry represents a file, then its file name is going
2235/// to be added to the vector returned by the method
2236/// type_suppression::get_source_locations_to_keep of this instance.
2237/// If this smart pointer is nil then a new instance @ref
2238/// type_suppression is created and this variable is made to point to
2239/// it.
2240static void
2241handle_fts_entry(const FTSENT *entry,
2242 type_suppression_sptr& suppr)
2243{
2244 if (entry == NULL
2245 || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2246 || entry->fts_info == FTS_ERR
2247 || entry->fts_info == FTS_NS)
2248 return;
2249
2250 string fname = entry->fts_name;
2251 if (!fname.empty())
2252 {
2253 if (string_ends_with(fname, ".h")
2254 || string_ends_with(fname, ".hpp")
2255 || string_ends_with(fname, ".hxx"))
2256 handle_file_entry (fname, suppr);
2257 }
2258}
2259
2260/// Populate a type_supression from header files found in a given
2261/// directory tree.
2262///
2263/// The suppression suppresses types defined in source files that are
2264/// *NOT* found in the directory tree.
2265///
2266/// This is a subroutine for gen_suppr_spect_from_headers.
2267///
2268/// @param headers_root_dir the directory tree to consider for header
2269/// files.
2270///
2271/// @param result the type_supression to populate from the content of
2272/// @p headers_root_dir.
2273static void
2274gen_suppr_spec_from_headers_root_dir(const string& headers_root_dir,
2275 type_suppression_sptr &result)
2276{
2277 if (!headers_root_dir.empty())
2278 {
2279 char* paths[] = {const_cast<char*>(headers_root_dir.c_str()), 0};
2280
2281 if (FTS *file_hierarchy = fts_open(paths, FTS_LOGICAL|FTS_NOCHDIR, NULL))
2282 {
2283 FTSENT *entry;
2284 while ((entry = fts_read(file_hierarchy)))
2285 handle_fts_entry(entry, result);
2286 fts_close(file_hierarchy);
2287 }
2288 }
2289}
2290
2291/// Generate a type suppression specification that suppresses ABI
2292/// changes for types defined in source files that are neither in a
2293/// given set of header root directories nor in a set of header
2294/// files.
2295///
2296/// @param headers_root_dirs ABI changes in types defined in files
2297/// *NOT* found in these directory trees are going be suppressed.
2298///
2299/// @param header_files a set of additional header files that define
2300/// types that are to be kept (not supressed) by the returned type
2301/// suppression.
2302///
2303/// @return the resulting type suppression generated, if any file was
2304/// found in the directory tree @p headers_root_dir.
2306gen_suppr_spec_from_headers(const vector<string>& headers_root_dirs,
2307 const vector<string>& header_files)
2308{
2309 type_suppression_sptr result;
2310
2311 for (vector<string>::const_iterator root_dir = headers_root_dirs.begin();
2312 root_dir != headers_root_dirs.end();
2313 ++root_dir)
2314 gen_suppr_spec_from_headers_root_dir(*root_dir, result);
2315
2316 for (vector<string>::const_iterator file = header_files.begin();
2317 file != header_files.end();
2318 ++file)
2319 handle_file_entry(*file, result);
2320
2321 return result;
2322}
2323
2324/// Generate a type suppression specification that suppresses ABI
2325/// changes for types defined in source files that are neither in a
2326/// given header root dir, not in a set of header files.
2327///
2328/// @param headers_root_dir ABI changes in types defined in files
2329/// *NOT* found in this directory tree are going be suppressed.
2330///
2331/// @param header_files a set of additional header files that define
2332/// types that are to be kept (not supressed) by the returned type
2333/// suppression.
2334///
2335/// @return the resulting type suppression generated, if any file was
2336/// found in the directory tree @p headers_root_dir.
2338gen_suppr_spec_from_headers(const string& headers_root_dir,
2339 const vector<string>& header_files)
2340{
2341 type_suppression_sptr result;
2342 vector<string> root_dirs;
2343
2344 if (!headers_root_dir.empty())
2345 root_dirs.push_back(headers_root_dir);
2346
2347 return gen_suppr_spec_from_headers(root_dirs, header_files);
2348}
2349
2350/// Generate a type suppression specification that suppresses ABI
2351/// changes for types defined in source files that are not in a given
2352/// header root dir.
2353///
2354/// @param headers_root_dir ABI changes in types defined in files
2355/// *NOT* found in this directory tree are going be suppressed.
2356///
2357/// @return the resulting type suppression generated, if any file was
2358/// found in the directory tree @p headers_root_dir.
2360gen_suppr_spec_from_headers(const string& headers_root_dir)
2361{
2362 // We don't list individual files, just look under the headers_path.
2363 vector<string> header_files;
2364 return gen_suppr_spec_from_headers(headers_root_dir, header_files);
2365}
2366
2367/// Generate a suppression specification from kernel abi whitelist
2368/// files.
2369///
2370/// A kernel ABI whitelist file is an INI file that usually has only
2371/// one section. The name of the section is a string that ends up
2372/// with the sub-string "whitelist". For instance
2373/// RHEL7_x86_64_whitelist.
2374///
2375/// Then the content of the section is a set of function or variable
2376/// names, one name per line. Each function or variable name is the
2377/// name of a function or a variable whose changes are to be keept.
2378///
2379/// A whitelist file can have multiple sections (adhering to the naming
2380/// conventions and multiple files can be passed. The suppression that
2381/// is created takes all whitelist sections from all files into account.
2382/// Symbols (or expression of such) are deduplicated in the final
2383/// suppression expression.
2384///
2385/// This function reads the white lists and generates a
2386/// function_suppression_sptr and variable_suppression_sptr and returns
2387/// a vector containing those.
2388///
2389/// @param abi_whitelist_paths a vector of KMI whitelist paths
2390///
2391/// @return a vector or suppressions
2394 (const std::vector<std::string>& abi_whitelist_paths)
2395{
2396
2397 std::vector<std::string> whitelisted_names;
2398 for (std::vector<std::string>::const_iterator
2399 path_iter = abi_whitelist_paths.begin(),
2400 path_end = abi_whitelist_paths.end();
2401 path_iter != path_end;
2402 ++path_iter)
2403 {
2404
2405 abigail::ini::config whitelist;
2406 if (!read_config(*path_iter, whitelist))
2407 continue;
2408
2409 const ini::config::sections_type& whitelist_sections =
2410 whitelist.get_sections();
2411
2412 for (ini::config::sections_type::const_iterator
2413 section_iter = whitelist_sections.begin(),
2414 section_end = whitelist_sections.end();
2415 section_iter != section_end;
2416 ++section_iter)
2417 {
2418 std::string section_name = (*section_iter)->get_name();
2419 if (!string_ends_with(section_name, "whitelist")
2420 && !string_ends_with(section_name, "stablelist"))
2421 continue;
2422 for (ini::config::properties_type::const_iterator
2423 prop_iter = (*section_iter)->get_properties().begin(),
2424 prop_end = (*section_iter)->get_properties().end();
2425 prop_iter != prop_end;
2426 ++prop_iter)
2427 {
2428 if (const simple_property_sptr& prop =
2429 is_simple_property(*prop_iter))
2430 if (prop->has_empty_value())
2431 {
2432 const std::string& name = prop->get_name();
2433 if (!name.empty())
2434 whitelisted_names.push_back(name);
2435 }
2436 }
2437 }
2438 }
2439
2440 suppressions_type result;
2441 if (!whitelisted_names.empty())
2442 {
2443 // Drop duplicates to simplify the regex we are generating
2444 std::sort(whitelisted_names.begin(), whitelisted_names.end());
2445 whitelisted_names.erase(std::unique(whitelisted_names.begin(),
2446 whitelisted_names.end()),
2447 whitelisted_names.end());
2448
2449 // Build a regular expression representing the union of all
2450 // the function and variable names expressed in the white list.
2451 const std::string regex = regex::generate_from_strings(whitelisted_names);
2452
2453 // Build a suppression specification which *keeps* functions
2454 // whose ELF symbols match the regular expression contained
2455 // in function_names_regexp. This will also keep the ELF
2456 // symbols (not designated by any debug info) whose names
2457 // match this regexp.
2459 fn_suppr->set_label("whitelist");
2460 fn_suppr->set_symbol_name_not_regex_str(regex);
2461 fn_suppr->set_drops_artifact_from_ir(true);
2462 result.push_back(fn_suppr);
2463
2464 // Build a suppression specification which *keeps* variables
2465 // whose ELF symbols match the regular expression contained
2466 // in function_names_regexp. This will also keep the ELF
2467 // symbols (not designated by any debug info) whose names
2468 // match this regexp.
2470 var_suppr->set_label("whitelist");
2471 var_suppr->set_symbol_name_not_regex_str(regex);
2472 var_suppr->set_drops_artifact_from_ir(true);
2473 result.push_back(var_suppr);
2474 }
2475 return result;
2476}
2477
2478/// Get the path to the default system suppression file.
2479///
2480/// @return a copy of the default system suppression file.
2481string
2483{
2484 string default_system_suppr_path;
2485
2486 const char *s = getenv("LIBABIGAIL_DEFAULT_SYSTEM_SUPPRESSION_FILE");
2487 if (s)
2488 default_system_suppr_path = s;
2489
2490 if (default_system_suppr_path.empty())
2491 default_system_suppr_path =
2492 get_system_libdir() + string("/libabigail/default.abignore");
2493
2494 return default_system_suppr_path;
2495}
2496
2497/// Get the path to the default user suppression file.
2498///
2499/// @return a copy of the default user suppression file.
2500string
2502{
2503 string default_user_suppr_path;
2504 const char *s = getenv("LIBABIGAIL_DEFAULT_USER_SUPPRESSION_FILE");
2505
2506 if (s == NULL)
2507 {
2508 s = getenv("HOME");
2509 if (s == NULL)
2510 return "";
2511 default_user_suppr_path = s;
2512 if (default_user_suppr_path.empty())
2513 default_user_suppr_path = "~";
2514 default_user_suppr_path += "/.abignore";
2515 }
2516 else
2517 default_user_suppr_path = s;
2518
2519 return default_user_suppr_path;
2520}
2521
2522/// Load the default system suppression specification file and
2523/// populate a vector of @ref suppression_sptr with its content.
2524///
2525/// The default system suppression file is located at
2526/// $libdir/libabigail/default-libabigail.abignore.
2527///
2528/// @param supprs the vector to add the suppression specifications
2529/// read from the file to.
2530void
2532{
2533 string default_system_suppr_path =
2535
2536 read_suppressions(default_system_suppr_path, supprs);
2537}
2538
2539/// Load the default user suppression specification file and populate
2540/// a vector of @ref suppression_sptr with its content.
2541///
2542/// The default user suppression file is located at $HOME~/.abignore.
2543///
2544/// @param supprs the vector to add the suppression specifications
2545/// read from the file to.
2546void
2548{
2549 string default_user_suppr_path =
2551
2552 read_suppressions(default_user_suppr_path, supprs);
2553}
2554
2555/// Test if a given FTSENT* denotes a file with a given name.
2556///
2557/// @param entry the FTSENT* to consider.
2558///
2559/// @param fname the file name (or end of path) to consider. The file
2560/// name can also be a path that is relative to the root directory the
2561/// current visit is started from. The root directory is given by @p
2562/// root_dir.
2563///
2564/// @param root_dir the root dir from which the directory visit is
2565/// being performed.
2566///
2567/// @return true iff @p entry denotes a file which path ends with @p
2568/// fname.
2569static bool
2570entry_of_file_with_name(const FTSENT *entry,
2571 const string& fname,
2572 const string& root_dir)
2573{
2574 if (entry == NULL
2575 || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2576 || entry->fts_info == FTS_ERR
2577 || entry->fts_info == FTS_NS)
2578 return false;
2579
2580 string fpath = ::basename(entry->fts_path);
2581 if (fpath == fname)
2582 return true;
2583
2584 fpath = trim_leading_string(entry->fts_path, root_dir);
2585 if (fpath == fname)
2586 return true;
2587
2588 return false;
2589}
2590
2591/// Find a given file under a root directory and return its absolute
2592/// path.
2593///
2594/// @param root_dir the root directory under which to look for.
2595///
2596/// @param file_path_to_look_for the file to look for under the
2597/// directory @p root_dir.
2598///
2599/// @param result the resulting path to @p file_path_to_look_for.
2600/// This is set iff the file has been found.
2601bool
2602find_file_under_dir(const string& root_dir,
2603 const string& file_path_to_look_for,
2604 string& result)
2605{
2606 char* paths[] = {const_cast<char*>(root_dir.c_str()), 0};
2607
2608 FTS *file_hierarchy = fts_open(paths,
2609 FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
2610 if (!file_hierarchy)
2611 return false;
2612
2613 string r = root_dir;
2614 if (!string_ends_with(r, "/"))
2615 r += "/";
2616
2617 FTSENT *entry;
2618 while ((entry = fts_read(file_hierarchy)))
2619 {
2620 if (entry_of_file_with_name(entry, file_path_to_look_for, r))
2621 {
2622 result = entry->fts_path;
2623 return true;
2624 }
2625 // Skip descendents of symbolic links.
2626 if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
2627 {
2628 fts_set(file_hierarchy, entry, FTS_SKIP);
2629 continue;
2630 }
2631 }
2632
2633 fts_close(file_hierarchy);
2634 return false;
2635}
2636
2637/// Find a given file possibly under a set of directories and return
2638/// its absolute path.
2639///
2640/// @param root_dirs the vector of root directories under which to
2641/// look for.
2642///
2643/// @param file_path_to_look_for the file to look for under the
2644/// directory @p root_dir.
2645///
2646/// @param result the resulting path to @p file_path_to_look_for.
2647/// This is set iff the file has been found.
2648bool
2649find_file_under_dirs(const vector<string>& root_dirs,
2650 const string& file_path_to_look_for,
2651 string& result)
2652{
2653 if (root_dirs.empty())
2654 return find_file_under_dir(".", file_path_to_look_for, result);
2655
2656 for (const auto& root_dir : root_dirs)
2657 if (find_file_under_dir(root_dir, file_path_to_look_for, result))
2658 return true;
2659
2660 return false;
2661}
2662
2663/// Get the dependencies of an ABI corpus, which are found in a set of
2664/// directories. Note that the dependencies are listed as properties
2665/// of the ABI corpus.
2666///
2667/// If the corpus has a dependency that is not found under any of the
2668/// given directories, then the dependency is ignored and not
2669/// returned.
2670///
2671/// @param korpus the ABI corpus to consider.
2672///
2673/// @param deps_dirs the list of directories where to look for the
2674/// dependencies.
2675///
2676/// @param dependencies output parameter that is set the dependencies
2677/// of the corpus denoted by @p korpus which are found in the
2678/// directories @p deps_dirs. This is set iff the function returns
2679/// true.
2680///
2681/// @return true iff some dependencies of the corpus @p korpus were
2682/// found in directories @p deps_dirs.
2683bool
2685 const vector<string>& deps_dirs,
2686 set<string>& dependencies)
2687{
2688 const vector<string>& set_of_needed = korpus.get_needed();
2689 if (set_of_needed.empty())
2690 return false;
2691
2692 bool found_at_least_one_dependency =false;
2693 for (const auto& n :set_of_needed)
2694 {
2695 string dependency;
2696 if (dependencies.find(n) == dependencies.end()
2697 && find_file_under_dirs(deps_dirs, n, dependency))
2698 {
2699 dependencies.insert(dependency);
2700 found_at_least_one_dependency = true;
2701 }
2702 }
2703
2704 return found_at_least_one_dependency;
2705}
2706
2707/// For each binary of a vector of binaries, if the binary is present
2708/// in at least one of the directories listed in a given vector,
2709/// construct a corpus and add it to a corpus group.
2710///
2711/// @param reader the reader used to read the binaries into an ABI corpus.
2712///
2713/// @param binaries the vector of binaries to read and add to a corpus
2714/// group.
2715///
2716/// @param deps_dirs the vector of directories where to look for the
2717/// binaries in @p binaries.
2718///
2719/// @param group the corpus group to add the corpus.
2720void
2721add_binaries_into_corpus_group(const fe_iface_sptr& reader,
2722 const vector<string>& binaries,
2723 const vector<string>& deps_dirs,
2724 corpus_group& group)
2725{
2726 vector<string> bins;
2727
2728 for (const auto& b : binaries)
2729 {
2730 string bin;
2731 if (find_file_under_dirs(deps_dirs, b, bin))
2732 bins.push_back(bin);
2733 }
2734
2735 for (const auto& b : bins)
2736 {
2737 if (group.has_corpus(b))
2738 continue;
2739
2740 reader->initialize(b);
2742 corpus_sptr c = reader->read_corpus(stat);
2743 if (c && (stat & fe_iface::STATUS_OK))
2744 group.add_corpus(c);
2745 }
2746}
2747
2748/// For each dependency of a given corpus, if it is present in at
2749/// least one of the directories listed in a given vector, construct a
2750/// corpus and add it to a corpus group.
2751///
2752/// @param reader the reader used to read the binaries into an ABI corpus.
2753///
2754/// @param korpus the corpus to consider.
2755///
2756/// @param deps_dirs the vector of directories where to look for the
2757/// dependencies of @p korpus.
2758///
2759/// @param group the corpus group to add the corpus.
2760void
2761add_dependencies_into_corpus_group(const fe_iface_sptr& reader,
2762 const corpus& korpus,
2763 const vector<string>& deps_dirs,
2764 corpus_group& group)
2765
2766{
2767 set<string> deps;
2768 if (!get_dependencies(korpus, deps_dirs, deps))
2769 return;
2770
2771 for (const auto& dep: deps)
2772 {
2773 if (group.has_corpus(dep))
2774 continue;
2775
2776 reader->initialize(dep);
2778 corpus_sptr c = reader->read_corpus(stat);
2779 if (c && (stat & fe_iface::STATUS_OK))
2780 {
2781 group.add_corpus(c);
2782 add_dependencies_into_corpus_group(reader, *c, deps_dirs, group);
2783 }
2784 }
2785}
2786
2787/// Create a corpus group made of a given korpus and a set of binaries
2788/// found in a set of directories.
2789///
2790/// @param reader the reader to use to read the binaries.
2791///
2792/// @param korpus the ABI corpus to add to the corpus group.
2793///
2794/// @param binaries the set of binaries to add to the corpus group, if
2795/// they are present one of the directories denoted by the vector @p
2796/// deps_dirs.
2797///
2798/// @param bins_dirs the directories where the binaries listed in @p
2799/// binaries are to be found.
2800///
2801/// @return a corpus group made of @p korpus and the binaries listed
2802/// in @p binaries and found in at least one of the directories found
2803/// in @p bins_dirs.
2804corpus_group_sptr
2806 const corpus_sptr& korpus,
2807 const vector<string>& binaries,
2808 const vector<string>& bins_dirs)
2809{
2810 corpus_group_sptr result (new corpus_group(korpus->get_environment(),
2811 korpus->get_path()));
2812 result->add_corpus(korpus);
2813
2814 add_binaries_into_corpus_group(reader, binaries, bins_dirs, *result);
2815
2816 return result;
2817}
2818
2819/// Create a corpus group made of a given korpus and the subset of its
2820/// dependencies that can be found found in a set of directories.
2821///
2822/// @param reader the reader to use to read the binaries.
2823///
2824/// @param korpus the ABI corpus to add to the corpus group along with
2825/// its dependencies that can be found in a subset of directories.
2826///
2827/// @param deps_dirs the directories where the dependencies of the ABI
2828/// corpus denoted by @p korpus binaries are to be found.
2829///
2830/// @return a corpus group made of @p korpus and the subset of its
2831/// dependencies found in at least one of the directories denoted by
2832/// @p deps_dirs.
2833corpus_group_sptr
2835 const corpus_sptr& korpus,
2836 const vector<string>& deps_dirs)
2837{
2838 corpus_group_sptr result (new corpus_group(korpus->get_environment(),
2839 korpus->get_path()));
2840 result->add_corpus(korpus);
2841
2842 add_dependencies_into_corpus_group(reader, *korpus, deps_dirs, *result);
2843
2844 return result;
2845}
2846
2847/// If we were given suppression specification files or kabi whitelist
2848/// files, this function parses those, come up with suppression
2849/// specifications as a result, and set them to the read context.
2850///
2851/// @param read_ctxt the read context to consider.
2852///
2853/// @param suppr_paths paths to suppression specification files that
2854/// we were given. If empty, it means we were not given any
2855/// suppression specification path.
2856///
2857/// @param kabi_whitelist_paths paths to kabi whitelist files that we
2858/// were given. If empty, it means we were not given any kabi
2859/// whitelist.
2860///
2861/// @param supprs the suppressions specifications resulting from
2862/// parsing the suppression specification files at @p suppr_paths and
2863/// the kabi whitelist at @p kabi_whitelist_paths.
2864///
2865/// @param opts the options to consider.
2866static void
2867load_generate_apply_suppressions(elf_based_reader& rdr,
2868 vector<string>& suppr_paths,
2869 vector<string>& kabi_whitelist_paths,
2870 suppressions_type& supprs)
2871{
2872 if (supprs.empty())
2873 {
2874 for (vector<string>::const_iterator i = suppr_paths.begin();
2875 i != suppr_paths.end();
2876 ++i)
2877 read_suppressions(*i, supprs);
2878
2879 const suppressions_type& wl_suppr =
2880 gen_suppr_spec_from_kernel_abi_whitelists(kabi_whitelist_paths);
2881
2882 supprs.insert(supprs.end(), wl_suppr.begin(), wl_suppr.end());
2883 }
2884
2885 rdr.add_suppressions(supprs);
2886}
2887
2888/// Test if an FTSENT pointer (resulting from fts_read) represents the
2889/// vmlinux binary.
2890///
2891/// @param entry the FTSENT to consider.
2892///
2893/// @return true iff @p entry is for a vmlinux binary.
2894static bool
2895is_vmlinux(const FTSENT *entry)
2896{
2897 if (entry == NULL
2898 || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2899 || entry->fts_info == FTS_ERR
2900 || entry->fts_info == FTS_NS)
2901 return false;
2902
2903 string fname = entry->fts_name;
2904
2905 if (fname == "vmlinux")
2906 {
2907 string dirname;
2908 dir_name(entry->fts_path, dirname);
2909 if (string_ends_with(dirname, "compressed"))
2910 return false;
2911
2912 return true;
2913 }
2914
2915 return false;
2916}
2917
2918/// Test if an FTSENT pointer (resulting from fts_read) represents a a
2919/// linux kernel module binary.
2920///
2921/// @param entry the FTSENT to consider.
2922///
2923/// @return true iff @p entry is for a linux kernel module binary.
2924static bool
2925is_kernel_module(const FTSENT *entry)
2926{
2927 if (entry == NULL
2928 || (entry->fts_info != FTS_F && entry->fts_info != FTS_SL)
2929 || entry->fts_info == FTS_ERR
2930 || entry->fts_info == FTS_NS)
2931 return false;
2932
2933 string fname = entry->fts_name;
2934 if (string_ends_with(fname, ".ko")
2935 || string_ends_with(fname, ".ko.xz")
2936 || string_ends_with(fname, ".ko.gz"))
2937 return true;
2938
2939 return false;
2940}
2941
2942/// Find a vmlinux and its kernel modules in a given directory tree.
2943///
2944/// @param from the directory tree to start looking from.
2945///
2946/// @param vmlinux_path output parameter. This is set to the path
2947/// where the vmlinux binary is found. This is set iff the returns
2948/// true and if this argument was empty to begin with.
2949///
2950/// @param module_paths output parameter. This is set to the paths of
2951/// the linux kernel module binaries.
2952///
2953/// @return true iff at least the vmlinux binary was found.
2954static bool
2955find_vmlinux_and_module_paths(const string& from,
2956 string &vmlinux_path,
2957 vector<string> &module_paths)
2958{
2959 char* path[] = {const_cast<char*>(from.c_str()), 0};
2960
2961 FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
2962 if (!file_hierarchy)
2963 return false;
2964
2965 bool found_vmlinux = !vmlinux_path.empty();
2966 FTSENT *entry;
2967 while ((entry = fts_read(file_hierarchy)))
2968 {
2969 // Skip descendents of symbolic links.
2970 if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
2971 {
2972 fts_set(file_hierarchy, entry, FTS_SKIP);
2973 continue;
2974 }
2975
2976 if (!found_vmlinux && is_vmlinux(entry))
2977 {
2978 vmlinux_path = entry->fts_path;
2979 found_vmlinux = true;
2980 }
2981 else if (is_kernel_module(entry))
2982 module_paths.push_back(entry->fts_path);
2983 }
2984
2985 fts_close(file_hierarchy);
2986
2987 return found_vmlinux;
2988}
2989
2990/// Find a vmlinux binary in a given directory tree.
2991///
2992/// @param from the directory tree to start looking from.
2993///
2994/// @param vmlinux_path output parameter
2995///
2996/// return true iff the vmlinux binary was found
2997static bool
2998find_vmlinux_path(const string& from,
2999 string &vmlinux_path)
3000{
3001 char* path[] = {const_cast<char*>(from.c_str()), 0};
3002
3003 FTS *file_hierarchy = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, 0);
3004 if (!file_hierarchy)
3005 return false;
3006
3007 bool found_vmlinux = false;
3008 FTSENT *entry;
3009 while ((entry = fts_read(file_hierarchy)))
3010 {
3011 // Skip descendents of symbolic links.
3012 if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE)
3013 {
3014 fts_set(file_hierarchy, entry, FTS_SKIP);
3015 continue;
3016 }
3017
3018 if (!found_vmlinux && is_vmlinux(entry))
3019 {
3020 vmlinux_path = entry->fts_path;
3021 found_vmlinux = true;
3022 break;
3023 }
3024 }
3025
3026 fts_close(file_hierarchy);
3027
3028 return found_vmlinux;
3029}
3030
3031/// Get the paths of the vmlinux and kernel module binaries under
3032/// given directory.
3033///
3034/// @param dist_root the directory under which to look for.
3035///
3036/// @param debug_info_root_path the path to the directory under which
3037/// debug info is going to be found for binaries under @p dist_root.
3038///
3039/// @param vmlinux_path output parameter. The path of the vmlinux
3040/// binary that was found.
3041///
3042/// @param module_paths output parameter. The paths of the kernel
3043/// module binaries that were found, sorted to impose a deterministic
3044/// ordering.
3045///
3046/// @return true if at least the path to the vmlinux binary was found.
3047bool
3049 const string& debug_info_root_path,
3050 string& vmlinux_path,
3051 vector<string>& module_paths)
3052{
3053 if (!dir_exists(dist_root))
3054 return false;
3055
3056 // For now, we assume either an Enterprise Linux or a Fedora kernel
3057 // distribution directory.
3058 //
3059 // We also take into account split debug info package for these. In
3060 // this case, the content split debug info package is installed
3061 // under the 'debug_info_root_path' directory and its content is
3062 // accessible from <debug_info_root_path>/usr/lib/debug directory.
3063
3064 string kernel_modules_root = dist_root;
3065 string debug_info_root;
3066 if (dir_exists(dist_root + "/lib/modules"))
3067 {
3068 kernel_modules_root = dist_root + "/lib/modules";
3069 debug_info_root = debug_info_root_path.empty()
3070 ? dist_root + "/usr/lib/debug"
3071 : debug_info_root_path;
3072 }
3073
3074 if (dir_is_empty(debug_info_root))
3075 debug_info_root.clear();
3076
3077 bool found = false;
3078 // If vmlinux_path is empty, we want to look for it under
3079 // debug_info_root, because this is where Enterprise Linux packages
3080 // put it. Modules however are to be looked for under
3081 // kernel_modules_root.
3082 if (// So, Let's look for modules under kernel_modules_root ...
3083 find_vmlinux_and_module_paths(kernel_modules_root,
3084 vmlinux_path,
3085 module_paths)
3086 // ... and if vmlinux_path is empty, look for vmlinux under the
3087 // debug info root.
3088 || find_vmlinux_and_module_paths(debug_info_root,
3089 vmlinux_path,
3090 module_paths))
3091 found = true;
3092
3093 std::sort(module_paths.begin(), module_paths.end());
3094
3095 return found;
3096}
3097
3098/// Get the path of the vmlinux binary under the given directory, that
3099/// must have been generated either from extracting a package.
3100///
3101/// @param from the directory under which to look for.
3102///
3103/// @param vmlinux_path output parameter. The path of the vmlinux
3104/// binary that was found.
3105///
3106/// @return true if the path to the vmlinux binary was found.
3107bool
3109 string& vmlinux_path)
3110{
3111 if (!dir_exists(from))
3112 return false;
3113
3114 // For now, we assume the possibility of having either an Enterprise
3115 // Linux or a Fedora kernel distribution directory. In those cases,
3116 // the vmlinux binary is located under the /lib/modules
3117 // sub-directory. So we might as well save some time by picking it
3118 // from there directly.
3119
3120 string dist_root = from;
3121 if (dir_exists(dist_root + "/lib/modules"))
3122 dist_root += "/lib/modules";
3123
3124 bool found = false;
3125 if (find_vmlinux_path(dist_root, vmlinux_path))
3126 found = true;
3127
3128 return found;
3129}
3130
3131/// Get the paths of the vmlinux and kernel module binaries under
3132/// given directory.
3133///
3134/// @param dist_root the directory under which to look for.
3135///
3136/// @param vmlinux_path output parameter. The path of the vmlinux
3137/// binary that was found.
3138///
3139/// @param module_paths output parameter. The paths of the kernel
3140/// module binaries that were found.
3141///
3142/// @return true if at least the path to the vmlinux binary was found.
3143bool
3145 string& vmlinux_path,
3146 vector<string>& module_paths)
3147{
3148 string debug_info_root_path;
3149 return get_binary_paths_from_kernel_dist(dist_root,
3150 debug_info_root_path,
3151 vmlinux_path,
3152 module_paths);
3153}
3154
3155/// It builds a @ref corpus_group made of vmlinux kernel file and
3156/// the kernel modules found under @p root directory and under its
3157/// sub-directories, recursively.
3158///
3159/// @param rdr the raeder that should be used to extract the debug
3160/// infomation from the linux kernel and its modules used to build
3161/// the corpora @p group.
3162///
3163/// @param the group @ref corpus_group to be built.
3164///
3165/// @param vmlinux the path to the vmlinux binary.
3166///
3167/// @param modules a vector with the paths to the linux kernel
3168/// modules.
3169///
3170/// @param root the path of the directory under which the kernel
3171/// kernel modules were found.
3172///
3173/// @param di_root the directory in aboslute path which debug
3174/// info is to be found for binaries under director @p root
3175///
3176/// @param suppr_paths the paths to the suppression specifications to
3177/// apply while loading the binaries.
3178///
3179/// @param kabi_wl_path the paths to the kabi whitelist files to take
3180/// into account while loading the binaries.
3181///
3182/// @param supprs the suppressions resulting from parsing the
3183/// suppression specifications at @p suppr_paths. This is set by this
3184/// function.
3185///
3186/// @param verbose true if the function has to emit some verbose
3187/// messages.
3188///
3189/// @param t time to trace time spent in each step.
3190///
3191/// @param env the environment to create the corpus_group in.
3192static void
3193load_vmlinux_corpus(elf_based_reader_sptr rdr,
3194 corpus_group_sptr& group,
3195 const string& vmlinux,
3196 vector<string>& modules,
3197 const string& root,
3198 vector<char**>& di_roots,
3199 vector<string>& suppr_paths,
3200 vector<string>& kabi_wl_paths,
3201 suppressions_type& supprs,
3202 bool verbose,
3203 timer& t,
3204 environment& env)
3205{
3207 rdr->options().do_log = verbose;
3208
3209 if (verbose)
3210 {
3211 std::cerr << "Loading stable lists:'";
3212 for (auto s : kabi_wl_paths)
3213 std::cerr << s << ",";
3214 std::cerr << "'...\n";
3215 }
3216 t.start();
3217 load_generate_apply_suppressions(*rdr, suppr_paths,
3218 kabi_wl_paths, supprs);
3219 t.stop();
3220
3221 if (verbose)
3222 std::cerr << "loaded white list and generated suppr spec in: "
3223 << t
3224 << "\n";
3225
3226 group.reset(new corpus_group(env, root));
3227
3228 rdr->corpus_group(group);
3229
3230 if (verbose)
3231 std::cerr << "reading kernel binary '"
3232 << vmlinux << "' ...\n" << std::flush;
3233
3234 // Read the vmlinux corpus and add it to the group.
3235 t.start();
3236 rdr->read_and_add_corpus_to_group(*group, status);
3237 t.stop();
3238
3239 if (verbose)
3240 std::cerr << vmlinux
3241 << " reading DONE in:"
3242 << t << "\n";
3243
3244 if (group->is_empty())
3245 return;
3246
3247 // Now add the corpora of the modules to the corpus group.
3248 int total_nb_modules = modules.size();
3249 int cur_module_index = 1;
3250 for (vector<string>::const_iterator m = modules.begin();
3251 m != modules.end();
3252 ++m, ++cur_module_index)
3253 {
3254 if (verbose)
3255 std::cerr << "reading module '"
3256 << *m << "' ("
3257 << cur_module_index
3258 << "/" << total_nb_modules
3259 << ") ...\n" << std::flush;
3260
3261 rdr->initialize(*m, di_roots,
3262 /*read_all_types=*/false,
3263 /*linux_kernel_mode=*/true);
3264
3265 load_generate_apply_suppressions(*rdr, suppr_paths,
3266 kabi_wl_paths, supprs);
3267
3268 rdr->corpus_group(group);
3269
3270 t.start();
3271 rdr->read_and_add_corpus_to_group(*group, status);
3272 t.stop();
3273 if (verbose)
3274 std::cerr << "Module reading DONE in: "
3275 << t << " for '" << *m
3276 << "' (" << cur_module_index << "/" << total_nb_modules << ")"
3277 << "'\n";
3278 }
3279
3280 if (verbose)
3281 {
3282 std::cerr << "Total number of functions: "
3283 << group->get_functions().size() << "\n";
3284 std::cerr << "Total number of variables: "
3285 << group->get_variables().size() << "\n";
3286 }
3287}
3288
3289/// Walk a given directory and build an instance of @ref corpus_group
3290/// from the vmlinux kernel binary and the linux kernel modules found
3291/// under that directory and under its sub-directories, recursively.
3292///
3293/// The main corpus of the @ref corpus_group is made of the vmlinux
3294/// binary. The other corpora are made of the linux kernel binaries.
3295///
3296/// @param root the path of the directory under which the kernel
3297/// kernel modules are to be found. The vmlinux can also be found
3298/// somewhere under that directory, but if it's not in there, its path
3299/// can be set to the @p vmlinux_path parameter.
3300///
3301/// @param debug_info_root the directory under which debug info is to
3302/// be found for binaries under director @p root.
3303///
3304/// @param vmlinux_path the path to the vmlinux binary, if that binary
3305/// is not under the @p root directory. If this is empty, then it
3306/// means the vmlinux binary is to be found under the @p root
3307/// directory.
3308///
3309/// @param suppr_paths the paths to the suppression specifications to
3310/// apply while loading the binaries.
3311///
3312/// @param kabi_wl_path the paths to the kabi whitelist files to take
3313/// into account while loading the binaries.
3314///
3315/// @param supprs the suppressions resulting from parsing the
3316/// suppression specifications at @p suppr_paths. This is set by this
3317/// function.
3318///
3319/// @param verbose true if the function has to emit some verbose
3320/// messages.
3321///
3322/// @param env the environment to create the corpus_group in.
3323///
3324/// @param requested_fe_kind the kind of front-end requested by the
3325/// user.
3326corpus_group_sptr
3328 const string debug_info_root,
3329 const string& vmlinux_path,
3330 vector<string>& suppr_paths,
3331 vector<string>& kabi_wl_paths,
3332 suppressions_type& supprs,
3333 bool verbose,
3334 environment& env,
3335 corpus::origin requested_fe_kind)
3336{
3337 string vmlinux = vmlinux_path;
3338 corpus_group_sptr group;
3339 vector<string> modules;
3340
3341 if (verbose)
3342 std::cerr << "Analysing kernel dist root '"
3343 << root
3344 << "' with vmlinux path: '"
3345 << vmlinux_path
3346 << "' ... \n" << std::flush;
3347
3348 timer t;
3349
3350 t.start();
3351 bool got_binary_paths =
3352 get_binary_paths_from_kernel_dist(root, debug_info_root, vmlinux, modules);
3353 t.stop();
3354
3355 if (verbose)
3356 std::cerr << "Kernel tree binary paths analysis DONE in: " << t << "\n";
3357
3358 if (got_binary_paths)
3359 {
3360 shared_ptr<char> di_root =
3361 make_path_absolute(debug_info_root.c_str());
3362 char *di_root_ptr = di_root.get();
3363 vector<char**> di_roots;
3364 di_roots.push_back(&di_root_ptr);
3365
3366#ifdef WITH_CTF
3367 shared_ptr<char> di_root_ctf;
3368 char *di_root_ctf_ptr;
3369 if (requested_fe_kind & corpus::CTF_ORIGIN)
3370 {
3371 di_root_ctf = make_path_absolute(root.c_str());
3372 di_root_ctf_ptr = di_root_ctf.get();
3373 di_roots.push_back(&di_root_ctf_ptr);
3374 }
3375#endif
3376
3377 abigail::elf_based_reader_sptr reader =
3379 di_roots,
3380 env,
3381 requested_fe_kind,
3382 /*read_all_types=*/false,
3383 /*linux_kernel_mode=*/true);
3384 ABG_ASSERT(reader);
3385 load_vmlinux_corpus(reader, group, vmlinux,
3386 modules, root, di_roots,
3387 suppr_paths, kabi_wl_paths,
3388 supprs, verbose, t, env);
3389 }
3390
3391 return group;
3392}
3393
3394/// Create the best elf based reader (or front-end), given an ELF
3395/// file.
3396///
3397/// This function looks into the ELF file; depending on the kind of
3398/// debug info it contains and on the request of the user, the "best"
3399/// front-end is created.
3400///
3401/// If the user requested the use of the CTF front-end, then, if the
3402/// file contains CTF debug info, the CTF front-end is created,
3403/// assuming libabigail is built with CTF support.
3404///
3405/// If the binary ONLY has CTF debug info, then CTF front-end is
3406/// created, even if the user hasn't explicitly requested the creation
3407/// of the CTF front-end.
3408///
3409/// Otherwise, by default, the DWARF front-end is created.
3410///
3411/// @param elf_file_path a path to the ELF file to consider
3412///
3413/// @param debug_info_root_paths a vector of the paths where to look
3414/// for debug info, if applicable.
3415///
3416/// @param env the environment to use for the front-end.
3417///
3418/// @param requested_fe_kind the kind of front-end specifically
3419/// requested by the user. At the moment, only the CTF front-end can
3420/// be requested, using the "--ctf" command line option on some tools
3421/// using the library.
3422///
3423/// @param show_all_types option to be passed to elf based readers.
3424///
3425/// @param linux_kernel_mode option to bed passed to elf based readers,
3426///
3427/// @return the ELF based Reader that is better adapted for the binary
3428/// designated by @p elf_file_path.
3429elf_based_reader_sptr
3430create_best_elf_based_reader(const string& elf_file_path,
3431 const vector<char**>& debug_info_root_paths,
3432 environment& env,
3433 corpus::origin requested_fe_kind,
3434 bool show_all_types,
3435 bool linux_kernel_mode)
3436{
3437 elf_based_reader_sptr result;
3438 if (guess_file_type(elf_file_path) != FILE_TYPE_ELF)
3439 return result;
3440
3441 if (requested_fe_kind & corpus::CTF_ORIGIN)
3442 {
3443#ifdef WITH_CTF
3444 if (file_has_ctf_debug_info(elf_file_path, debug_info_root_paths))
3445 result = ctf::create_reader(elf_file_path, debug_info_root_paths, env);
3446#endif
3447 }
3448 else if (requested_fe_kind & corpus::BTF_ORIGIN)
3449 {
3450#ifdef WITH_BTF
3451 if (file_has_btf_debug_info(elf_file_path, debug_info_root_paths))
3452 result = btf::create_reader(elf_file_path, debug_info_root_paths, env,
3453 show_all_types, linux_kernel_mode);
3454#endif
3455 }
3456 else
3457 {
3458 // The user hasn't formally requested the use of the CTF front-end.
3459#ifdef WITH_CTF
3460 if (!file_has_dwarf_debug_info(elf_file_path, debug_info_root_paths)
3461 && file_has_ctf_debug_info(elf_file_path, debug_info_root_paths))
3462 // The file has CTF debug info and no DWARF, let's use the CTF
3463 // front end even if it wasn't formally requested by the user.
3464 result = ctf::create_reader(elf_file_path, debug_info_root_paths, env);
3465#endif
3466
3467#ifdef WITH_BTF
3468 if (!file_has_dwarf_debug_info(elf_file_path, debug_info_root_paths)
3469 && file_has_btf_debug_info(elf_file_path, debug_info_root_paths))
3470 // The file has BTF debug info and no BTF, let's use the BTF
3471 // front-end even if it wasn't formally requested by the user.
3472 result = btf::create_reader(elf_file_path, debug_info_root_paths, env,
3473 show_all_types, linux_kernel_mode);
3474#endif
3475 }
3476
3477 if (!result)
3478 {
3479 // This is the default case. At worst, the DWARF reader knows
3480 // how to handle just ELF data for the case where there is no
3481 // DWARF debug info present.
3482 result = dwarf::create_reader(elf_file_path,
3483 debug_info_root_paths,
3484 env,
3485 show_all_types,
3486 linux_kernel_mode);
3487 }
3488
3489 return result;
3490}
3491
3492/// ---------------------------------------------------
3493/// <xz_decompressor definition>
3494///----------------------------------------------------
3495
3496/// The private data of the @ref xz_decompressor_type class.
3497struct xz_decompressor_type::priv
3498{
3499 std::istream& xz_istream;
3500 lzma_stream lzma;
3501 // A 10k bytes buffer for xz data coming from the
3502 // xz'ed istream. That buffer is going to be fed into the lzma
3503 // decoding machinery.
3504 char inbuf[1024 * 10] = {};
3505 // A 10k bytes buffer for decompressed data coming
3506 // out of the lzma machinery
3507 char outbuf[1024 * 10] = {};
3508
3509 priv(std::istream& i)
3510 : xz_istream(i),
3511 lzma(LZMA_STREAM_INIT)
3512 {}
3513};// end xz_decompressor_type::priv
3514
3515/// Constructor of the @ref xz_decompressor_type class.
3516///
3517/// @param xz_istream the input stream containing the xz-compressed
3518/// data to decompress.
3519xz_decompressor_type::xz_decompressor_type(std::istream& xz_istream)
3520 : priv_(new priv(xz_istream))
3521{
3522 // Initialize the native LZMA stream to decompress.
3523 lzma_ret status = lzma_stream_decoder(&priv_->lzma,
3524 UINT64_MAX,
3525 LZMA_CONCATENATED);
3526 ABG_ASSERT(status == LZMA_OK);
3527}
3528
3529/// Destructor of the @ref xz_decompressor_type class.
3531{
3532 lzma_end(&priv_->lzma);
3533}
3534
3535/// The implementation of the virtual protected
3536/// std:streambuf::underlying method. This method is invoked by the
3537/// std::streambuf facility to re-fill its internals buffers with data
3538/// coming from the associated input stream and to update the gptr()
3539/// and egptr() pointers by using the std::streambuf::setg method.
3540///
3541/// This is where the decompression using the lzma library is
3542/// performed.
3543std::streambuf::int_type
3545{
3546 if (gptr() < egptr())
3547 return *gptr();
3548
3549 // Let's read 'nr' bytes of xz data into inbuf
3550 priv_->xz_istream.read(priv_->inbuf, sizeof(priv_->inbuf));
3551 size_t nr = priv_->xz_istream.gcount();
3552 if (nr == 0)
3553 {
3554 // Tell the lzma machinery that we've reached the end of the
3555 // data.
3556 lzma_ret result = lzma_code(&priv_->lzma, LZMA_FINISH);
3557 ABG_ASSERT(result == LZMA_OK || result == LZMA_STREAM_END);
3558 return EOF;
3559 }
3560
3561 // Let's prepare the lzma input/output stream/machinery.
3562 priv_->lzma.avail_in = nr;
3563 priv_->lzma.next_in = reinterpret_cast<uint8_t*>(priv_->inbuf);
3564
3565 priv_->lzma.avail_out = sizeof(priv_->outbuf);
3566 priv_->lzma.next_out = reinterpret_cast<uint8_t*>(priv_->outbuf);
3567
3568 // Let's now ask the lzma machinery to decompress the inbuf and
3569 // put the result into outbuf.
3570 lzma_ret result = lzma_code(&priv_->lzma, LZMA_RUN);
3571 if (result != LZMA_OK && result != LZMA_STREAM_END)
3572 {
3573 // TODO: list the possible error codes and tell them explicitely
3574 // to the user, just like what is done in
3575 // https://github.com/tukaani-project/xz/blob/master/doc/examples/02_decompress.c.
3576 std::ostringstream o;
3577 o << "LZMA decompression failed;"
3578 << " return code of lzma_code() is : "
3579 << result;
3580 throw std::runtime_error(o.str());
3581 }
3582
3583 // Let's get the number of bytes decompressed by the lzma
3584 // machinery. I got this from the example in the xz code base at
3585 // https://github.com/tukaani-project/xz/blob/master/doc/examples/02_decompress.c.
3586 size_t nr_decompressed_bytes = sizeof(priv_->outbuf) - priv_->lzma.avail_out;
3587
3588 // Now set the relevant index pointers of this streambuf.
3589 setg(priv_->outbuf, priv_->outbuf, priv_->outbuf + nr_decompressed_bytes);
3590
3591 if (nr_decompressed_bytes > 0)
3592 return *gptr();
3593
3594 return EOF;
3595}
3596
3597/// ---------------------------------------------------
3598/// </xz_decompressor definition>
3599///----------------------------------------------------
3600
3601}//end namespace tools_utils
3602
3604
3605/// Dump (to the standard error stream) two sequences of strings where
3606/// each string represent one of the functions in the two sequences of
3607/// functions given in argument to this function.
3608///
3609/// @param a_begin the begin iterator for the first input sequence of
3610/// functions.
3611///
3612/// @parm a_end the end iterator for the first input sequence of
3613/// functions.
3614///
3615/// @param b_begin the begin iterator for the second input sequence of
3616/// functions.
3617///
3618/// @param b_end the end iterator for the second input sequence of functions.
3619void
3620dump_functions_as_string(std::vector<function_decl*>::const_iterator a_begin,
3621 std::vector<function_decl*>::const_iterator a_end,
3622 std::vector<function_decl*>::const_iterator b_begin,
3623 std::vector<function_decl*>::const_iterator b_end)
3624{abigail::fns_to_str(a_begin, a_end, b_begin, b_end, std::cerr);}
3625
3626/// Dump (to the standard error output stream) a pretty representation
3627/// of the signatures of two sequences of functions.
3628///
3629/// @param a_begin the start iterator of the first input sequence of functions.
3630///
3631/// @param a_end the end iterator of the first input sequence of functions.
3632///
3633/// @param b_begin the start iterator of the second input sequence of functions.
3634///
3635/// @param b_end the end iterator of the second input sequence of functions.
3636void
3637dump_function_names(std::vector<function_decl*>::const_iterator a_begin,
3638 std::vector<function_decl*>::const_iterator a_end,
3639 std::vector<function_decl*>::const_iterator b_begin,
3640 std::vector<function_decl*>::const_iterator b_end)
3641{
3642 std::vector<function_decl*>::const_iterator i;
3643 std::ostream& o = std::cerr;
3644 for (i = a_begin; i != a_end; ++i)
3645 o << (*i)->get_pretty_representation() << "\n";
3646
3647 o << " ->|<- \n";
3648 for (i = b_begin; i != b_end; ++i)
3649 o << (*i)->get_pretty_representation() << "\n";
3650 o << "\n";
3651}
3652
3653/// Compare two functions that are in a vector of functions.
3654///
3655/// @param an iterator to the beginning of the the sequence of functions.
3656///
3657/// @param f1_index the index of the first function to compare.
3658///
3659/// @param f2_inde the index of the second function to compare
3660bool
3661compare_functions(vector<function_decl*>::const_iterator base,
3662 unsigned f1_index, unsigned f2_index)
3663{
3664 function_decl* fn1 = base[f1_index];
3665 function_decl* fn2 = base[f2_index];
3666
3667 return *fn1 == *fn2;
3668}
3669
3670}//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:1737
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::underlying method. This method is invoked ...
elf_based_reader_sptr create_reader(const std::string &elf_path, const vector< char ** > &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< char ** > &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< char ** > &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:1681
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...
bool file_has_btf_debug_info(const string &elf_file_path, const vector< char ** > &debug_info_root_paths)
Test if an ELF file has BTFG debug info.
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...
const char * get_anonymous_enum_internal_name_prefix()
Getter of the prefix for the name of anonymous enums.
std::shared_ptr< char > make_path_absolute(const char *p)
Return a copy of the path given in argument, turning it into an absolute path by prefixing it with th...
const char * get_anonymous_struct_internal_name_prefix()
Getter of the prefix for the name of anonymous structs.
string trim_white_space(const string &str)
Remove spaces at the beginning and at the end of a given string.
corpus_group_sptr stick_corpus_and_binaries_into_corpus_group(const fe_iface_sptr &reader, const corpus_sptr &korpus, const vector< string > &binaries, const vector< string > &bins_dirs)
Create a corpus group made of a given korpus and a set of binaries found in a set of directories.
bool check_dir(const string &path, ostream &out, const string &prog_name)
Check if a given path exists, is readable and is a directory.
const char * get_anonymous_union_internal_name_prefix()
Getter of the prefix for the name of anonymous unions.
bool string_begins_with(const string &str, const string &prefix)
Test if a given string begins with a particular prefix.
bool file_has_dwarf_debug_info(const string &elf_file_path, const vector< char ** > &debug_info_root_paths)
Test if an ELF file has DWARF debug info.
bool abidiff_status_has_incompatible_abi_change(abidiff_status s)
Test if an instance of.
elf_based_reader_sptr create_best_elf_based_reader(const string &elf_file_path, const vector< char ** > &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.
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...
file_type guess_file_type(const string &file_path)
Guess the type of the content of an file.
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.
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_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 file_has_ctf_debug_info(const string &elf_file_path, const vector< char ** > &debug_info_root_paths)
Test if an ELF file has CTF debug info.
bool string_suffix(const string &input_string, const string &prefix, string &suffix)
Get the suffix of a string, given a prefix to consider.
void add_binaries_into_corpus_group(const fe_iface_sptr &reader, const vector< string > &binaries, const vector< string > &deps_dirs, corpus_group &group)
For each binary of a vector of binaries, if the binary is present in at least one of the directories ...
bool ensure_parent_dir_created(const string &path)
Ensures that the parent directory of #path is created.
corpus_group_sptr build_corpus_group_from_kernel_dist_under(const string &root, const string debug_info_root, const string &vmlinux_path, vector< string > &suppr_paths, vector< string > &kabi_wl_paths, suppressions_type &supprs, bool verbose, environment &env, corpus::origin requested_fe_kind)
Walk a given directory and build an instance of corpus_group from the vmlinux kernel binary and the l...
void convert_char_stars_to_char_star_stars(const vector< char * > &char_stars, vector< char ** > &char_star_stars)
Convert a vector<char*> into a vector<char**>.
bool sorted_strings_common_prefix(vector< string > &input_strings, string &prefix)
Find the prefix common to a *SORTED* vector of strings.
void get_comma_separated_args_of_option(const string &input_str, const string &option, vector< string > &arguments)
Get a vector of arguments from a string containing a comma-separated list of those arguments.
void real_path(const string &path, string &result)
Return the real path of a given path.
abidiff_status operator&(abidiff_status l, abidiff_status r)
The bitwise 'AND' operator for abidiff_status bit masks.
void add_dependencies_into_corpus_group(const fe_iface_sptr &reader, const corpus &korpus, const vector< string > &deps_dirs, corpus_group &group)
For each dependency of a given corpus, if it is present in at least one of the directories listed in ...
bool file_is_kernel_debuginfo_package(const string &file_name, file_type file_type)
Tests if a given file name designates a kernel debuginfo package.
void load_default_system_suppressions(suppr::suppressions_type &supprs)
Load the default system suppression specification file and populate a vector of suppression_sptr with...
string get_abixml_version_string()
Return the version string for the ABIXML format.
string get_random_number_as_string()
Get a pseudo random number as string.
bool decl_names_equal(const string &l, const string &r)
Compare two fully qualified decl names by taking into account that they might have compontents that a...
bool get_rpm_arch(const string &str, string &arch)
Get the architecture string from the NVR of an rpm.
string get_default_user_suppression_file_path()
Get the path to the default user suppression file.
string trim_leading_string(const string &from, const string &to_trim)
Remove a string of pattern in front of a given string.
corpus_group_sptr stick_corpus_and_dependencies_into_corpus_group(const fe_iface_sptr &reader, const corpus_sptr &korpus, const vector< string > &deps_dirs)
Create a corpus group made of a given korpus and the subset of its dependencies that can be found fou...
file_type guess_file_type(istream &in)
Guess the type of the content of an input stream.
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.
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