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