1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2022-2023 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7
8 /// @file
9 ///
10 /// This file contains the definitions of the the fe_iface base type.
11
12 #include "abg-internal.h"
13 // <headers defining libabigail's API go under here>
14 ABG_BEGIN_EXPORT_DECLARATIONS
15
16 #include "abg-corpus.h"
17 #include "abg-fe-iface.h"
18
19 ABG_END_EXPORT_DECLARATIONS
20 // </headers defining libabigail's API>
21
22 namespace abigail
23 {
24
25 /// The private data structure for the @ref fe_iface type.
26 struct fe_iface::priv
27 {
28 std::string corpus_path;
29 std::string dt_soname;
30 fe_iface::options_type options;
31 suppr::suppressions_type suppressions;
32 ir::corpus_sptr corpus;
33 ir::corpus_group_sptr corpus_group;
34
privabigail::fe_iface::priv35 priv(const std::string& path, environment& e)
36 : corpus_path(path), options(e)
37 {
38 initialize();
39 }
40
41 /// This function resets the data of @ref fe_iface::priv data so
42 /// that it can be re-used again.
43 void
initializeabigail::fe_iface::priv44 initialize()
45 {
46 corpus_path.clear();
47 dt_soname.clear();
48 suppressions.clear();
49 corpus_group.reset();
50 corpus.reset();
51 }
52 }; //end struct fe_iface::priv
53
54 /// Constructor of the type @ref fe_iface::options_type.
55 ///
56 /// @param e the environment used by the Front End Interface.
options_type(environment & e)57 fe_iface::options_type::options_type(environment& e)
58 : env(e)
59 {
60 }
61
62 /// Constructor of the type @ref fe_iface.
63 ///
64 /// @param corpus_path the path to the file represented by the ABI
65 /// corpus that is going to be built by this Front End.
66 ///
67 /// @param e the environment in which the Front End operates.
fe_iface(const std::string & corpus_path,environment & e)68 fe_iface::fe_iface(const std::string& corpus_path, environment& e)
69 : priv_(new priv(corpus_path, e))
70 {
71 }
72
73 /// Desctructor of the Front End Interface.
~fe_iface()74 fe_iface::~fe_iface()
75 {delete priv_;}
76
77 /// Re-initialize the current Front End.
78 ///
79 /// @param corpus_path the path to the file for which a new corpus is
80 /// to be created.
81 void
initialize(const std::string & corpus_path)82 fe_iface::initialize(const std::string& corpus_path)
83 {
84 priv_->initialize();
85 priv_->corpus_path = corpus_path;
86 }
87
88 /// Getter of the the options of the current Front End Interface.
89 ///
90 /// @return the options of the current Front End Interface.
91 const fe_iface::options_type&
options() const92 fe_iface::options() const
93 {return priv_->options;}
94
95 /// Getter of the the options of the current Front End Interface.
96 ///
97 /// @return the options of the current Front End Interface.
98 fe_iface::options_type&
options()99 fe_iface::options()
100 {return priv_->options;}
101
102 /// Getter of the path to the file which an ABI corpus is to be
103 /// created for.
104 ///
105 /// @return the path to the file which an ABI corpus is to be created
106 /// for.
107 const std::string&
corpus_path() const108 fe_iface::corpus_path() const
109 {return priv_->corpus_path;}
110
111 /// Setter of the path to the file which an ABI corpus is to be
112 /// created for.
113 ///
114 /// @param p the new path to the file which an ABI corpus is to be
115 /// created for.
116 void
corpus_path(const std::string & p)117 fe_iface::corpus_path(const std::string& p)
118 {priv_->corpus_path = p;}
119
120 /// Getter for the SONAME of the analyzed binary.
121 ///
122 /// @return the SONAME of the analyzed binary.
123 const string&
dt_soname() const124 fe_iface::dt_soname() const
125 {return priv_->dt_soname;}
126
127 /// Getter for the SONAME of the analyzed binary.
128 ///
129 /// @return the SONAME of the analyzed binary.
130 void
dt_soname(const string & soname)131 fe_iface::dt_soname(const string& soname)
132 {priv_->dt_soname = soname;}
133
134 /// Test if the input binary is to be considered as a Linux Kernel
135 /// binary.
136 ///
137 /// @return true iff the input binary is to be considered as a Linux
138 /// Kernel binary.
139 bool
load_in_linux_kernel_mode() const140 fe_iface::load_in_linux_kernel_mode() const
141 {return priv_->options.load_in_linux_kernel_mode;}
142
143 /// Getter of the vector of suppression specifications associated with
144 /// the current front-end.
145 ///
146 /// @return the vector of suppression specifications associated with
147 /// the current front-end.
148 suppr::suppressions_type&
suppressions()149 fe_iface::suppressions()
150 {return priv_->suppressions;}
151
152 /// Getter of the vector of suppression specifications associated with
153 /// the current front-end.
154 ///
155 /// @return the vector of suppression specifications associated with
156 /// the current front-end.
157 const suppr::suppressions_type&
suppressions() const158 fe_iface::suppressions() const
159 {return priv_->suppressions;}
160
161 /// Setter of the vector of suppression specifications associated with
162 /// the current front-end.
163 ///
164 /// @param supprs the new vector of suppression specifications
165 /// associated with the current front-end.
166 void
suppressions(suppr::suppressions_type & supprs)167 fe_iface::suppressions(suppr::suppressions_type& supprs)
168 {priv_->suppressions = supprs;}
169
170 /// Add suppressions specifications to the set of suppressions to be
171 /// used during the construction of the ABI internal representation
172 /// (the ABI corpus) from the input file.
173 ///
174 /// During the construction of the ABI corpus, ABI artifacts that
175 /// match a given suppression specification are dropped on the floor;
176 /// that is, they are discarded and won't be part of the final ABI
177 /// corpus. This is a way to reduce the amount of data held by the
178 /// final ABI corpus.
179 ///
180 /// Note that the suppression specifications provided to this function
181 /// are only considered during the construction of the ABI corpus.
182 /// For instance, they are not taken into account during e.g
183 /// comparisons of two ABI corpora that might happen later. If you
184 /// want to apply suppression specificatins to the comparison (or
185 /// reporting) of ABI corpora please refer to the documentation of the
186 /// @ref diff_context type to learn how to set suppressions that are
187 /// to be used in that context.
188 ///
189 /// @param supprs the suppression specifications to be applied during
190 /// the construction of the ABI corpus.
191 void
add_suppressions(const suppr::suppressions_type & supprs)192 fe_iface::add_suppressions(const suppr::suppressions_type& supprs)
193 {
194 for (const auto& s : supprs)
195 if (s->get_drops_artifact_from_ir())
196 suppressions().push_back(s);
197 }
198
199 /// Getter for the ABI corpus being built by the current front-end.
200 ///
201 /// @return the ABI corpus being built by the current front-end.
202 corpus_sptr
corpus()203 fe_iface::corpus()
204 {
205 if (!priv_->corpus)
206 {
207 priv_->corpus = std::make_shared<ir::corpus>(options().env,
208 corpus_path());
209 }
210 return priv_->corpus;
211 }
212
213 /// Getter for the ABI corpus being built by the current front-end.
214 ///
215 /// @return the ABI corpus being built by the current front-end.
216 const corpus_sptr
corpus() const217 fe_iface::corpus() const
218 {return const_cast<fe_iface*>(this)->corpus();}
219
220 /// Getter for the ABI corpus group being built by the current front-end.
221 ///
222 /// @return the ABI corpus group being built by the current front-end.
223 corpus_group_sptr&
corpus_group()224 fe_iface::corpus_group()
225 {return priv_->corpus_group;}
226
227 /// Getter for the ABI corpus group being built by the current front-end.
228 ///
229 /// @return the ABI corpus group being built by the current front-end.
230 const corpus_group_sptr&
corpus_group() const231 fe_iface::corpus_group() const
232 {return const_cast<fe_iface*>(this)->corpus_group();}
233
234 /// Setter for the ABI corpus group being built by the current
235 /// front-end.
236 ///
237 /// @param cg the new ABI corpus group being built by the current
238 /// front-end.
239 void
corpus_group(const ir::corpus_group_sptr & cg)240 fe_iface::corpus_group(const ir::corpus_group_sptr& cg)
241 {priv_->corpus_group = cg;}
242
243 /// Test if there is a corpus group being built.
244 ///
245 /// @return if there is a corpus group being built, false otherwise.
246 bool
has_corpus_group() const247 fe_iface::has_corpus_group() const
248 {return bool(corpus_group());}
249
250 /// Return the main corpus from the current corpus group, if any.
251 ///
252 /// @return the main corpus of the current corpus group, if any, nil
253 /// if no corpus group is being constructed.
254 corpus_sptr
main_corpus_from_current_group()255 fe_iface::main_corpus_from_current_group()
256 {
257 if (corpus_group())
258 return corpus_group()->get_main_corpus();
259 return corpus_sptr();
260 }
261
262 /// Test if the current corpus being built is the main corpus of the
263 /// current corpus group.
264 ///
265 /// @return return true iff the current corpus being built is the
266 /// main corpus of the current corpus group.
267 bool
current_corpus_is_main_corpus_from_current_group()268 fe_iface::current_corpus_is_main_corpus_from_current_group()
269 {
270 corpus_sptr main_corpus = main_corpus_from_current_group();
271
272 if (main_corpus.get() == corpus().get())
273 return true;
274
275 return false;
276 }
277
278 /// Return true if the current corpus is part of a corpus group
279 /// being built and if it's not the main corpus of the group.
280 ///
281 /// For instance, this would return true if we are loading a linux
282 /// kernel *module* that is part of the current corpus group that is
283 /// being built. In this case, it means we should re-use types
284 /// coming from the "vmlinux" binary that is the main corpus of the
285 /// group.
286 ///
287 /// @return the corpus group the current corpus belongs to, if the
288 /// current corpus is part of a corpus group being built. Nil otherwise.
289 corpus_sptr
should_reuse_type_from_corpus_group()290 fe_iface::should_reuse_type_from_corpus_group()
291 {
292 if (has_corpus_group())
293 if (corpus_sptr main_corpus = main_corpus_from_current_group())
294 if (!current_corpus_is_main_corpus_from_current_group())
295 return corpus_group();
296
297 return corpus_sptr();
298 }
299
300 /// Try and add the representation of the ABI of a function to the set
301 /// of exported declarations of the current corpus.
302 ///
303 /// @param fn the internal representation of the ABI of a function.
304 void
maybe_add_fn_to_exported_decls(const function_decl * fn)305 fe_iface::maybe_add_fn_to_exported_decls(const function_decl* fn)
306 {
307 if (fn)
308 if (corpus::exported_decls_builder* b =
309 corpus()->get_exported_decls_builder().get())
310 b->maybe_add_fn_to_exported_fns(const_cast<function_decl*>(fn));
311 }
312
313 /// Try and add the representation of the ABI of a variable to the set
314 /// of exported declarations of the current corpus.
315 ///
316 /// @param var the internal representation of the ABI of a variable.
317 void
maybe_add_var_to_exported_decls(const var_decl * var)318 fe_iface::maybe_add_var_to_exported_decls(const var_decl* var)
319 {
320 if (var)
321 if (corpus::exported_decls_builder* b =
322 corpus()->get_exported_decls_builder().get())
323 b->maybe_add_var_to_exported_vars(var);
324 }
325
326 /// The bitwise OR operator for the @ref fe_iface::status type.
327 ///
328 /// @param l the left-hand side operand.
329 ///
330 /// @param r the right-hand side operand.
331 ///
332 /// @return the result of the operation.
333 fe_iface::status
operator |(fe_iface::status l,fe_iface::status r)334 operator|(fe_iface::status l, fe_iface::status r)
335 {
336 return static_cast<fe_iface::status>(static_cast<unsigned>(l)
337 | static_cast<unsigned>(r));
338 }
339
340 /// The bitwise AND operator for the @ref fe_iface::status type.
341 ///
342 /// @param l the left-hand side operand.
343 ///
344 /// @param r the right-hand side operand.
345 ///
346 /// @return the result of the operation.
347 fe_iface::status
operator &(fe_iface::status l,fe_iface::status r)348 operator&(fe_iface::status l, fe_iface::status r)
349 {
350 return static_cast<fe_iface::status>(static_cast<unsigned>(l)
351 & static_cast<unsigned>(r));
352 }
353
354 /// The bitwise |= operator for the @ref fe_iface::status type.
355 ///
356 /// @param l the left-hand side operand.
357 ///
358 /// @param r the right-hand side operand.
359 ///
360 /// @return the result of the operation.
361 fe_iface::status&
operator |=(fe_iface::status & l,fe_iface::status r)362 operator|=(fe_iface::status& l, fe_iface::status r)
363 {
364 l = l | r;
365 return l;
366 }
367
368 /// The bitwise &= operator for the @ref fe_iface::status type.
369 ///
370 /// @param l the left-hand side operand.
371 ///
372 /// @param r the right-hand side operand.
373 ///
374 /// @return the result of the operation.
375 fe_iface::status&
operator &=(fe_iface::status & l,fe_iface::status r)376 operator&=(fe_iface::status& l, fe_iface::status r)
377 {
378 l = l & r;
379 return l;
380 }
381
382 /// Return a diagnostic status with english sentences to describe the
383 /// problems encoded in a given abigail::elf_reader::status, if
384 /// there is an error.
385 ///
386 /// @param status the status to diagnose
387 ///
388 /// @return a string containing sentences that describe the possible
389 /// errors encoded in @p s. If there is no error to encode, then the
390 /// empty string is returned.
391 std::string
status_to_diagnostic_string(fe_iface::status s)392 status_to_diagnostic_string(fe_iface::status s)
393 {
394 std::string str;
395
396 if (s & fe_iface::STATUS_DEBUG_INFO_NOT_FOUND)
397 str += "could not find debug info";
398
399 if (s & fe_iface::STATUS_ALT_DEBUG_INFO_NOT_FOUND)
400 str += "could not find alternate debug info";
401
402 if (s & fe_iface::STATUS_NO_SYMBOLS_FOUND)
403 str += "could not load ELF symbols";
404
405 return str;
406 }
407
408 }// namespace abigail
409