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