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