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