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