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