• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2022 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7 
8 /// @file
9 ///
10 /// This is a program aimed at checking that a binary instrumentation
11 /// (bi) file is well formed and valid enough.  It acts by loading an
12 /// input bi file and saving it back to a temporary file.  It then
13 /// runs a diff on the two files and expects the result of the diff to
14 /// be empty.
15 
16 #include "config.h"
17 #include <cstdio>
18 #include <cstdlib>
19 #include <cstring>
20 #include <fstream>
21 #include <iostream>
22 #include <memory>
23 #include <string>
24 #include <vector>
25 #include "abg-config.h"
26 #include "abg-tools-utils.h"
27 #include "abg-ir.h"
28 #include "abg-corpus.h"
29 #include "abg-reader.h"
30 #include "abg-dwarf-reader.h"
31 #ifdef WITH_CTF
32 #include "abg-ctf-reader.h"
33 #endif
34 #include "abg-writer.h"
35 #include "abg-suppression.h"
36 
37 using std::string;
38 using std::cerr;
39 using std::cin;
40 using std::cout;
41 using std::ostream;
42 using std::ofstream;
43 using std::vector;
44 using std::unordered_set;
45 using std::unique_ptr;
46 using abigail::tools_utils::emit_prefix;
47 using abigail::tools_utils::check_file;
48 using abigail::tools_utils::file_type;
49 using abigail::tools_utils::guess_file_type;
50 using abigail::suppr::suppression_sptr;
51 using abigail::suppr::suppressions_type;
52 using abigail::suppr::read_suppressions;
53 using abigail::type_base;
54 using abigail::type_or_decl_base;
55 using abigail::type_base_sptr;
56 using abigail::type_or_decl_base_sptr;
57 using abigail::corpus;
58 using abigail::corpus_sptr;
59 using abigail::abixml::read_translation_unit_from_file;
60 using abigail::abixml::read_translation_unit_from_istream;
61 using abigail::abixml::read_corpus_from_abixml;
62 using abigail::abixml::read_corpus_from_abixml_file;
63 using abigail::abixml::read_corpus_group_from_input;
64 #ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
65 using abigail::abixml::get_types_from_type_id;
66 using abigail::abixml::get_artifact_used_by_relation_map;
67 #endif
68 
69 using abigail::xml_writer::write_translation_unit;
70 using abigail::xml_writer::write_context_sptr;
71 using abigail::xml_writer::create_write_context;
72 using abigail::xml_writer::write_corpus;
73 using abigail::xml_writer::write_corpus_to_archive;
74 
75 struct options
76 {
77   string			wrong_option;
78   string			file_path;
79   bool				display_version;
80   bool				read_from_stdin;
81   bool				read_tu;
82   bool				diff;
83   bool				noout;
84 #ifdef WITH_CTF
85   bool				use_ctf;
86 #endif
87   std::shared_ptr<char>	di_root_path;
88   vector<string>		suppression_paths;
89   string			headers_dir;
90   vector<string>		header_files;
91 #if WITH_SHOW_TYPE_USE_IN_ABILINT
92   string			type_id_to_show;
93 #endif
94 
optionsoptions95   options()
96     : display_version(false),
97       read_from_stdin(false),
98       read_tu(false),
99       diff(false),
100       noout(false)
101 #ifdef WITH_CTF
102     ,
103       use_ctf(false)
104 #endif
105   {}
106 };//end struct options;
107 
108 #ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
109 /// A tree node representing the "use" relation between an artifact A
110 /// (e.g, a type) and a set of artifacts {A'} that use "A" as in "A"
111 /// is a sub-type of A'.
112 ///
113 /// So the node contains the artifact A and a vector children nodes
114 /// that contain the A' artifacts that use A.
115 struct artifact_use_relation_tree
116 {
117   artifact_use_relation_tree *root_node = nullptr;
118   /// The parent node of this one.  Is nullptr if this node is the root
119   /// node.
120   artifact_use_relation_tree *parent = nullptr;
121   /// The artifact contained in this node.
122   type_or_decl_base* artifact = nullptr;
123   /// The vector of children nodes that carry the artifacts that
124   /// actually use the 'artifact' above.  In other words, the
125   /// 'artifact" data member above is a sub-type of each artifact
126   /// contained in this vector.
127   vector<unique_ptr<artifact_use_relation_tree>> artifact_users;
128   /// This is the set of artifacts that have been added to the tree.
129   /// This is useful to ensure that all artifacts are added just once
130   /// in the tree to prevent infinite loops.
131   unordered_set<type_or_decl_base *> artifacts;
132 
133   /// The constructor of the tree node.
134   ///
135   /// @param the artifact to consider.
artifact_use_relation_treeartifact_use_relation_tree136   artifact_use_relation_tree(type_or_decl_base* t)
137     : artifact (t)
138   {
139     ABG_ASSERT(t && !artifact_in_tree(t));
140     record_artifact(t);
141   }
142 
143   /// Add a user artifact node for the artifact carried by this node.
144   ///
145   /// The artifact carried by the current node is a sub-type of the
146   /// artifact carried by the 'user' node being added.
147   ///
148   /// @param user a tree node that carries an artifact that uses the
149   /// artifact carried by the current node.
150   void
add_artifact_userartifact_use_relation_tree151   add_artifact_user(artifact_use_relation_tree *user)
152   {
153     ABG_ASSERT(user && !artifact_in_tree(user->artifact ));
154     artifact_users.push_back(unique_ptr<artifact_use_relation_tree>(user));
155     user->parent = this;
156     record_artifact(user->artifact);
157   }
158 
159   /// Move constructor.
160   ///
161   /// @param o the source of the move.
artifact_use_relation_treeartifact_use_relation_tree162   artifact_use_relation_tree(artifact_use_relation_tree &&o)
163   {
164     parent = o.parent;
165     artifact = o.artifact;
166     artifact_users = std::move(o.artifact_users);
167     artifacts = std::move(o.artifacts);
168   }
169 
170   /// Move assignment operator.
171   ///
172   /// @param o the source of the assignment.
operator =artifact_use_relation_tree173   artifact_use_relation_tree& operator=(artifact_use_relation_tree&& o)
174   {
175     parent = o.parent;
176     artifact = o.artifact;
177     artifact_users = std::move(o.artifact_users);
178     artifacts = std::move(o.artifacts);
179     return *this;
180   }
181 
182   /// Test if the current node is a leaf node.
183   ///
184   /// @return true if the artifact carried by the current node has no
185   /// user artifacts.
186   bool
is_leafartifact_use_relation_tree187   is_leaf() const
188   {return artifact_users.empty();}
189 
190   /// Test if the current node is a root node.
191   ///
192   /// @return true if the current artifact uses no other artifact.
193   bool
is_rootartifact_use_relation_tree194   is_root() const
195   {return parent == nullptr;}
196 
197   /// Test wether a given artifact has been added to the tree.
198   ///
199   /// Here, the tree means the tree that the current tree node is part
200   /// of.
201   ///
202   /// An artifact is considered as having been added to the tree if
203   /// artifact_use_relation_tree::record_artifact has been invoked on
204   /// it.
205   ///
206   /// @param artifact the artifact to consider.
207   ///
208   /// @return true iff @p artifact is present in the tree.
209   bool
artifact_in_treeartifact_use_relation_tree210   artifact_in_tree(type_or_decl_base *artifact)
211   {
212     artifact_use_relation_tree *root_node = get_root_node();
213     ABG_ASSERT(root_node);
214     return root_node->artifacts.find(artifact) != root_node->artifacts.end();
215   }
216 
217   /// Record an artifact as being added to the current tree.
218   ///
219   /// Note that this function assumes the artifact is not already
220   /// present in the tree containing the current tree node.
221   ///
222   /// @param artifact the artifact to consider.
223   void
record_artifactartifact_use_relation_tree224   record_artifact(type_or_decl_base *artifact)
225   {
226     ABG_ASSERT(!artifact_in_tree(artifact));
227     artifact_use_relation_tree *root_node = get_root_node();
228     ABG_ASSERT(root_node);
229     root_node->artifacts.insert(artifact);
230   }
231 
232   /// Get the root node of the current tree.
233   ///
234   /// @return the root node of the current tree.
235   artifact_use_relation_tree*
get_root_nodeartifact_use_relation_tree236   get_root_node()
237   {
238     if (root_node)
239       return root_node;
240 
241     if (parent == nullptr)
242       return this;
243 
244     root_node = parent->get_root_node();
245     return root_node;
246   }
247 
248   artifact_use_relation_tree(const artifact_use_relation_tree&) = delete;
249   artifact_use_relation_tree& operator=(const artifact_use_relation_tree&) = delete;
250 }; // end struct artifact_use_relation_tree
251 
252 /// Fill an "artifact use" tree from a map that associates a type T
253 /// (or artifact) to artifacts that use T as a sub-type.
254 ///
255 /// @param artifact_use_rel the map that establishes the relation
256 /// between a type T and the artifacts that use T as a sub-type.
257 ///
258 /// @parm tree output parameter.  This function will fill up this tree
259 /// from the information carried in @p artifact_use_rel.  Each node of
260 /// the tree contains an artifact A and its children nodes contain the
261 /// artifacts A' that use A as a sub-type.
262 static void
fill_artifact_use_tree(const std::unordered_map<type_or_decl_base *,vector<type_or_decl_base * >> & artifact_use_rel,artifact_use_relation_tree & tree)263 fill_artifact_use_tree(const std::unordered_map<type_or_decl_base*,
264 						vector<type_or_decl_base*>>& artifact_use_rel,
265 		       artifact_use_relation_tree& tree)
266 {
267   auto r = artifact_use_rel.find(tree.artifact);
268   if (r == artifact_use_rel.end())
269     return;
270 
271   // Walk the users of "artifact", create a tree node for each one of
272   // them, and add them as children node of the current tree node
273   // named 'tree'.
274   for (auto user : r->second)
275     {
276       if (tree.artifact_in_tree(user))
277 	// The artifact has already been added to the tree, so skip it
278 	// otherwise we can loop for ever.
279 	continue;
280 
281       artifact_use_relation_tree *user_tree =
282 	new artifact_use_relation_tree(user);
283 
284       // Now add the new user node as a child of the current tree
285       // node.
286       tree.add_artifact_user(user_tree);
287 
288       // Recursively fill the newly created tree node.
289       fill_artifact_use_tree(artifact_use_rel, *user_tree);
290     }
291 }
292 
293 /// construct an "artifact use tree" for a type designated by a "type-id".
294 /// (or artifact) to artifacts that use T as a sub-type.
295 ///
296 /// Each node of the "artifact use tree" contains a type T and its
297 /// children nodes contain the artifacts A' that use T as a sub-type.
298 /// The root node is the type designed by a given type-id.
299 ///
300 /// @param ctxt the abixml read context to consider.
301 ///
302 /// @param type_id the type-id of the type to construct the "use tree"
303 /// for.
304 static unique_ptr<artifact_use_relation_tree>
build_type_use_tree(abigail::abixml::reader & ctxt,const string & type_id)305 build_type_use_tree(abigail::abixml::reader &ctxt,
306 		    const string& type_id)
307 {
308   unique_ptr<artifact_use_relation_tree> result;
309   vector<type_base_sptr>* types = get_types_from_type_id(ctxt, type_id);
310   if (!types)
311     return result;
312 
313   std::unordered_map<type_or_decl_base*, vector<type_or_decl_base*>>*
314     artifact_use_rel = get_artifact_used_by_relation_map(ctxt);
315   if (!artifact_use_rel)
316     return result;
317 
318   type_or_decl_base_sptr type = types->front();
319   unique_ptr<artifact_use_relation_tree> use_tree
320     (new artifact_use_relation_tree(type.get()));
321 
322   fill_artifact_use_tree(*artifact_use_rel, *use_tree);
323 
324   result = std::move(use_tree);
325   return result;
326 }
327 
328 /// Emit a visual representation of a "type use trace".
329 ///
330 /// The trace is vector of strings.  Each string is the textual
331 /// representation of a type.  The next element in the vector is a
332 /// type using the previous element, as in, the "previous element is a
333 /// sub-type of the next element".
334 ///
335 /// This is a sub-routine of emit_artifact_use_trace.
336 ///
337 /// @param the trace vector to emit.
338 ///
339 /// @param out the output stream to emit the trace to.
340 static void
emit_trace(const vector<string> & trace,ostream & out)341 emit_trace(const vector<string>& trace, ostream& out)
342 {
343   if (trace.empty())
344     return;
345 
346   if (!trace.empty())
347     // Make the beginning of the trace line of the usage of a given
348     // type be easily recognizeable by a "pattern".
349     out << "===";
350 
351   for (auto element : trace)
352     out << "-> " << element << " ";
353 
354   if (!trace.empty())
355     // Make the end of the trace line of the usage of a given type be
356     // easily recognizeable by another "pattern".
357     out << " <-~~~";
358 
359   out << "\n";
360 }
361 
362 /// Walk a @ref artifact_use_relation_tree to emit a "type-is-used-by"
363 /// trace.
364 ///
365 /// The tree carries the information about how a given type is used by
366 /// other types.  This function walks the tree by visiting a node
367 /// carrying a given type T, and then the nodes for which T is a
368 /// sub-type.  The function accumulates a trace made of the textual
369 /// representation of the visited nodes and then emits that trace on
370 /// an output stream.
371 ///
372 /// @param artifact_use_tree the tree to walk.
373 ///
374 /// @param trace the accumulated vector of the textual representations
375 /// of the types carried by the visited nodes.
376 ///
377 /// @param out the output stream to emit the trace to.
378 static void
emit_artifact_use_trace(const artifact_use_relation_tree & artifact_use_tree,vector<string> & trace,ostream & out)379 emit_artifact_use_trace(const artifact_use_relation_tree& artifact_use_tree,
380 			vector<string>& trace, ostream& out)
381 {
382   type_or_decl_base* artifact = artifact_use_tree.artifact;
383   if (!artifact)
384     return;
385 
386   string repr = artifact->get_pretty_representation();
387   trace.push_back(repr);
388 
389   if (artifact_use_tree.artifact_users.empty())
390     {
391       // We reached a leaf node.  This means that no other artifact
392       // uses the artifact carried by this leaf node.  So, we want to
393       // emit the trace accumulated to this point.
394 
395       // But we only want to emit the usage traces that end up with a
396       // function of variable that have an associated ELF symbol.
397       bool do_emit_trace = false;
398       if (is_decl(artifact))
399 	{
400 	  if (abigail::ir::var_decl* v = is_var_decl(artifact))
401 	    if (v->get_symbol()
402 		|| is_at_global_scope(v)
403 		|| !v->get_linkage_name().empty())
404 	      do_emit_trace = true;
405 	  if (abigail::ir::function_decl* f = is_function_decl(artifact))
406 	    if (f->get_symbol()
407 		|| is_at_global_scope(f)
408 		|| !f->get_linkage_name().empty())
409 	      do_emit_trace = true;
410 	}
411 
412       // OK now, really emit the trace.
413       if (do_emit_trace)
414 	emit_trace(trace, out);
415 
416       trace.pop_back();
417       return;
418     }
419 
420   for (const auto &user : artifact_use_tree.artifact_users)
421     emit_artifact_use_trace(*user, trace, out);
422 
423   trace.pop_back();
424 }
425 
426 /// Walk a @ref artifact_use_relation_tree to emit a "type-is-used-by"
427 /// trace.
428 ///
429 /// The tree carries the information about how a given type is used by
430 /// other types.  This function walks the tree by visiting a node
431 /// carrying a given type T, and then the nodes for which T is a
432 /// sub-type.  The function then emits a trace of how the root type is
433 /// used.
434 ///
435 /// @param artifact_use_tree the tree to walk.
436 ///
437 /// @param out the output stream to emit the trace to.
438 static void
emit_artifact_use_trace(const artifact_use_relation_tree & artifact_use_tree,ostream & out)439 emit_artifact_use_trace(const artifact_use_relation_tree& artifact_use_tree,
440 			ostream& out)
441 {
442   vector<string> trace;
443   emit_artifact_use_trace(artifact_use_tree, trace, out);
444 }
445 
446 /// Show how a type is used.
447 ///
448 /// The type to consider is designated by a type-id string that is
449 /// carried by the options data structure.
450 ///
451 /// @param ctxt the abixml read context to consider.
452 ///
453 /// @param the type_id of the type which usage to analyse.
454 static bool
show_how_type_is_used(abigail::abixml::reader & ctxt,const string & type_id)455 show_how_type_is_used(abigail::abixml::reader &ctxt,
456 		      const string& type_id)
457 {
458   if (type_id.empty())
459     return false;
460 
461   unique_ptr<artifact_use_relation_tree> use_tree =
462     build_type_use_tree(ctxt, type_id);
463   if (!use_tree)
464     return false;
465 
466   // Now walk the use_tree to emit the type use trace
467   if (use_tree->artifact)
468     {
469       std::cout << "Type ID '"
470 		<< type_id << "' is for type '"
471 		<< use_tree->artifact->get_pretty_representation()
472 		<< "'\n"
473 		<< "The usage graph for that type is:\n";
474       emit_artifact_use_trace(*use_tree, std::cout);
475     }
476   return true;
477 }
478 #endif // WITH_SHOW_TYPE_USE_IN_ABILINT
479 
480 static void
display_usage(const string & prog_name,ostream & out)481 display_usage(const string& prog_name, ostream& out)
482 {
483   emit_prefix(prog_name, out)
484     << "usage: " << prog_name << " [options] [<abi-file1>]\n"
485     << " where options can be:\n"
486     << "  --help  display this message\n"
487     << "  --version|-v  display program version information and exit\n"
488     << "  --debug-info-dir <path> the path under which to look for "
489     "debug info for the elf <abi-file>\n"
490     << "  --headers-dir|--hd <path> the path to headers of the elf file\n"
491     << "  --header-file|--hf <path> the path to one header of the elf file\n"
492     << "  --suppressions|--suppr <path> specify a suppression file\n"
493     << "  --diff  for xml inputs, perform a text diff between "
494     "the input and the memory model saved back to disk\n"
495     << "  --noout  do not display anything on stdout\n"
496     << "  --stdin  read abi-file content from stdin\n"
497     << "  --tu  expect a single translation unit file\n"
498 #ifdef WITH_CTF
499     << "  --ctf use CTF instead of DWARF in ELF files\n"
500 #endif
501 #ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
502     << "  --show-type-use <type-id>  show how a type is used from the abixml file\n"
503 #endif
504     ;
505 }
506 
507 bool
parse_command_line(int argc,char * argv[],options & opts)508 parse_command_line(int argc, char* argv[], options& opts)
509 {
510   if (argc < 2)
511     {
512       opts.read_from_stdin = true;
513       return true;
514     }
515 
516     for (int i = 1; i < argc; ++i)
517       {
518 	if (argv[i][0] != '-')
519 	  {
520 	    if (opts.file_path.empty())
521 	      opts.file_path = argv[i];
522 	    else
523 	      return false;
524 	  }
525 	else if (!strcmp(argv[i], "--help"))
526 	  return false;
527 	else if (!strcmp(argv[i], "--version")
528 		 || !strcmp(argv[i], "-v"))
529 	  {
530 	    opts.display_version = true;
531 	    return true;
532 	  }
533 	else if (!strcmp(argv[i], "--debug-info-dir"))
534 	  {
535 	    if (argc <= i + 1
536 		|| argv[i + 1][0] == '-')
537 	      return false;
538 	    // elfutils wants the root path to the debug info to be
539 	    // absolute.
540 	    opts.di_root_path =
541 	      abigail::tools_utils::make_path_absolute(argv[i + 1]);
542 	    ++i;
543 	  }
544       else if (!strcmp(argv[i], "--headers-dir")
545 	       || !strcmp(argv[i], "--hd"))
546 	{
547 	  int j = i + 1;
548 	  if (j >= argc)
549 	    return false;
550 	  opts.headers_dir = argv[j];
551 	  ++i;
552 	}
553       else if (!strcmp(argv[i], "--header-file")
554 	       || !strcmp(argv[i], "--hf"))
555 	{
556 	  int j = i + 1;
557 	  if (j >= argc)
558 	    return false;
559 	  opts.header_files.push_back(argv[j]);
560 	  ++i;
561 	}
562       else if (!strcmp(argv[i], "--suppressions")
563 	       || !strcmp(argv[i], "--suppr"))
564 	{
565 	  int j = i + 1;
566 	  if (j >= argc)
567 	    {
568 	      opts.wrong_option = argv[i];
569 	      return true;
570 	    }
571 	  opts.suppression_paths.push_back(argv[j]);
572 	  ++i;
573 	}
574 	else if (!strcmp(argv[i], "--stdin"))
575 	  opts.read_from_stdin = true;
576 	else if (!strcmp(argv[i], "--tu"))
577 	  opts.read_tu = true;
578 #ifdef WITH_CTF
579         else if (!strcmp(argv[i], "--ctf"))
580           opts.use_ctf = true;
581 #endif
582 	else if (!strcmp(argv[i], "--diff"))
583 	  opts.diff = true;
584 	else if (!strcmp(argv[i], "--noout"))
585 	  opts.noout = true;
586 #ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
587       else if (!strcmp(argv[i], "--show-type-use"))
588 	{
589 	  ++i;
590 	  if (i >= argc || argv[i][0] == '-')
591 	    return false;
592 	  opts.type_id_to_show = argv[i];
593 	}
594 #endif
595 	else
596 	  {
597 	    if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
598 	      opts.wrong_option = argv[i];
599 	    return false;
600 	  }
601       }
602 
603 #ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
604     if (!opts.type_id_to_show.empty()
605 	&& opts.file_path.empty())
606       emit_prefix(argv[0], cout)
607 	<< "WARNING: --show-type-use <type-id> "
608 	"must be accompanied with an abixml file\n";
609 
610     if (opts.file_path.empty()
611 	&& opts.type_id_to_show.empty())
612       opts.read_from_stdin = true;
613 #endif
614 
615     if (opts.read_from_stdin && !opts.file_path.empty())
616     {
617       emit_prefix(argv[0], cout)
618         << "WARNING: The \'--stdin\' option is used. The "
619         << opts.file_path << " will be ignored automatically\n";
620     }
621 
622     return true;
623 }
624 
625 /// Check that the suppression specification files supplied are
626 /// present.  If not, emit an error on stderr.
627 ///
628 /// @param opts the options instance to use.
629 ///
630 /// @return true if all suppression specification files are present,
631 /// false otherwise.
632 static bool
maybe_check_suppression_files(const options & opts)633 maybe_check_suppression_files(const options& opts)
634 {
635   for (vector<string>::const_iterator i = opts.suppression_paths.begin();
636        i != opts.suppression_paths.end();
637        ++i)
638     if (!check_file(*i, cerr, "abidiff"))
639       return false;
640 
641   return true;
642 }
643 
644 /// Set suppression specifications to the @p reader used to load
645 /// the ABI corpus from the ELF/DWARF file.
646 ///
647 /// These suppression specifications are going to be applied to drop
648 /// some ABI artifacts on the floor (while reading the ELF/DWARF file
649 /// or the native XML ABI file) and thus minimize the size of the
650 /// resulting ABI corpus.
651 ///
652 /// @param read_ctxt the read context to apply the suppression
653 /// specifications to.  Note that the type of this parameter is
654 /// generic (class template) because in practise, it can be either an
655 /// abigail::dwarf_reader::reader type or an
656 /// abigail::abixml::reader type.
657 ///
658 /// @param opts the options where to get the suppression
659 /// specifications from.
660 static void
set_suppressions(abigail::fe_iface & reader,const options & opts)661 set_suppressions(abigail::fe_iface& reader, const options& opts)
662 {
663   suppressions_type supprs;
664   for (vector<string>::const_iterator i = opts.suppression_paths.begin();
665        i != opts.suppression_paths.end();
666        ++i)
667     read_suppressions(*i, supprs);
668 
669   suppression_sptr suppr =
670     abigail::tools_utils::gen_suppr_spec_from_headers(opts.headers_dir,
671 						      opts.header_files);
672   if (suppr)
673     supprs.push_back(suppr);
674 
675   reader.add_suppressions(supprs);
676 }
677 
678 /// Reads a bi (binary instrumentation) file, saves it back to a
679 /// temporary file and run a diff on the two versions.
680 int
main(int argc,char * argv[])681 main(int argc, char* argv[])
682 {
683   options opts;
684   if (!parse_command_line(argc, argv, opts))
685     {
686       if (!opts.wrong_option.empty())
687 	emit_prefix(argv[0], cerr)
688 	  << "unrecognized option: " << opts.wrong_option << "\n";
689       display_usage(argv[0], cerr);
690       return 1;
691     }
692 
693   if (opts.display_version)
694     {
695       emit_prefix(argv[0], cout)
696 	<< abigail::tools_utils::get_library_version_string()
697 	<< "\n";
698       return 0;
699     }
700 
701   if (!maybe_check_suppression_files(opts))
702     return 1;
703 
704   abigail::ir::environment env;
705   if (opts.read_from_stdin)
706     {
707       if (!cin.good())
708 	return 1;
709 
710       if (opts.read_tu)
711 	{
712 	  abigail::translation_unit_sptr tu =
713 	    read_translation_unit_from_istream(&cin, env);
714 
715 	  if (!tu)
716 	    {
717 	      emit_prefix(argv[0], cerr)
718 		<< "failed to read the ABI instrumentation from stdin\n";
719 	      return 1;
720 	    }
721 
722 	  if (!opts.noout)
723 	    {
724 	      const write_context_sptr& ctxt
725 		  = create_write_context(env, cout);
726 	      write_translation_unit(*ctxt, *tu, 0);
727 	    }
728 	  return 0;
729 	}
730       else
731 	{
732 	  abigail::fe_iface_sptr rdr =
733 	    abigail::abixml::create_reader(&cin, env);
734 	  assert(rdr);
735 	  set_suppressions(*rdr, opts);
736 	  abigail::fe_iface::status sts;
737 	  corpus_sptr corp = rdr->read_corpus(sts);
738 	  if (!opts.noout)
739 	    {
740 	      const write_context_sptr& ctxt
741 		  = create_write_context(env, cout);
742 	      write_corpus(*ctxt, corp, /*indent=*/0);
743 	    }
744 	  return 0;
745 	}
746     }
747   else if (!opts.file_path.empty())
748     {
749       if (!check_file(opts.file_path, cerr, argv[0]))
750 	return 1;
751       abigail::translation_unit_sptr tu;
752       abigail::corpus_sptr corp;
753       abigail::corpus_group_sptr group;
754       abigail::fe_iface::status s = abigail::fe_iface::STATUS_OK;
755       char* di_root_path = 0;
756       file_type type = guess_file_type(opts.file_path);
757 
758       switch (type)
759 	{
760 	case abigail::tools_utils::FILE_TYPE_UNKNOWN:
761 	  emit_prefix(argv[0], cerr)
762 	    << "Unknown file type given in input: " << opts.file_path
763 	    << "\n";
764 	  return 1;
765 	case abigail::tools_utils::FILE_TYPE_NATIVE_BI:
766 	  {
767 	    abigail::fe_iface_sptr rdr =
768 	      abigail::abixml::create_reader(opts.file_path,
769 							   env);
770 	    tu = abigail::abixml::read_translation_unit(*rdr);
771 	  }
772 	  break;
773 	case abigail::tools_utils::FILE_TYPE_ELF:
774 	case abigail::tools_utils::FILE_TYPE_AR:
775 	  {
776 	    di_root_path = opts.di_root_path.get();
777 	    vector<char**> di_roots;
778 	    di_roots.push_back(&di_root_path);
779 	    abigail::elf_based_reader_sptr rdr;
780 #ifdef WITH_CTF
781             if (opts.use_ctf)
782 	      rdr =
783 		abigail::ctf::create_reader(opts.file_path,
784 					    di_roots, env);
785             else
786 #endif
787 	      rdr =
788 		abigail::dwarf::create_reader(opts.file_path,
789 					      di_roots, env,
790 					      /*load_all_types=*/false);
791 	    set_suppressions(*rdr, opts);
792 	    corp = rdr->read_corpus(s);
793 	  }
794 	  break;
795 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
796 	  {
797 	    abigail::fe_iface_sptr rdr =
798 	      abigail::abixml::create_reader(opts.file_path, env);
799 	    assert(rdr);
800 	    set_suppressions(*rdr, opts);
801 	    corp = rdr->read_corpus(s);
802 	    break;
803 	  }
804 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
805 	  {
806 	    abigail::fe_iface_sptr rdr =
807 	      abigail::abixml::create_reader(opts.file_path, env);
808 	    assert(rdr);
809 	    set_suppressions(*rdr, opts);
810 	    group = read_corpus_group_from_input(*rdr);
811 	  }
812 	  break;
813 	case abigail::tools_utils::FILE_TYPE_RPM:
814 	  break;
815 	case abigail::tools_utils::FILE_TYPE_SRPM:
816 	  break;
817 	case abigail::tools_utils::FILE_TYPE_DEB:
818 	  break;
819 	case abigail::tools_utils::FILE_TYPE_DIR:
820 	  break;
821 	case abigail::tools_utils::FILE_TYPE_TAR:
822 	  break;
823 	}
824 
825       if (!tu && !corp && !group)
826 	{
827 	  emit_prefix(argv[0], cerr)
828 	    << "failed to read " << opts.file_path << "\n";
829 	  if (!(s & abigail::fe_iface::STATUS_OK))
830 	    {
831 	      if (s & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND)
832 		{
833 		  cerr << "could not find the debug info";
834 		  if(di_root_path == 0)
835 		    emit_prefix(argv[0], cerr)
836 		      << " Maybe you should consider using the "
837 		      "--debug-info-dir1 option to tell me about the "
838 		      "root directory of the debuginfo? "
839 		      "(e.g, --debug-info-dir1 /usr/lib/debug)\n";
840 		  else
841 		    emit_prefix(argv[0], cerr)
842 		      << "Maybe the root path to the debug "
843 		      "information is wrong?\n";
844 		}
845 	      if (s & abigail::fe_iface::STATUS_NO_SYMBOLS_FOUND)
846 		emit_prefix(argv[0], cerr)
847 		  << "could not find the ELF symbols in the file "
848 		  << opts.file_path
849 		  << "\n";
850 	    }
851 	  return 1;
852 	}
853 
854       using abigail::tools_utils::temp_file;
855       using abigail::tools_utils::temp_file_sptr;
856 
857       temp_file_sptr tmp_file = temp_file::create();
858       if (!tmp_file)
859 	{
860 	  emit_prefix(argv[0], cerr) << "failed to create temporary file\n";
861 	  return 1;
862 	}
863 
864       std::ostream& of = opts.diff ? tmp_file->get_stream() : cout;
865       const write_context_sptr ctxt = create_write_context(env, of);
866 
867       bool is_ok = true;
868 
869       if (tu)
870 	{
871 	  if (!opts.noout)
872 	    is_ok = write_translation_unit(*ctxt, *tu, 0);
873 	}
874       else
875 	{
876 	  if (type == abigail::tools_utils::FILE_TYPE_XML_CORPUS
877 	      || type == abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP
878 	      || type == abigail::tools_utils::FILE_TYPE_ELF)
879 	    {
880 	      if (!opts.noout)
881 		{
882 		  if (corp)
883 		    is_ok = write_corpus(*ctxt, corp, 0);
884 		  else if (group)
885 		    is_ok = write_corpus_group(*ctxt, group, 0);
886 		}
887 	    }
888 	}
889 
890       if (!is_ok)
891 	{
892 	  string output =
893 	    (type == abigail::tools_utils::FILE_TYPE_NATIVE_BI)
894 	    ? "translation unit"
895 	    : "ABI corpus";
896 	  emit_prefix(argv[0], cerr)
897 	    << "failed to write the translation unit "
898 	    << opts.file_path << " back\n";
899 	}
900 
901       if (is_ok
902 	  && opts.diff
903 	  && ((type == abigail::tools_utils::FILE_TYPE_XML_CORPUS)
904 	      ||type == abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP
905 	      || type == abigail::tools_utils::FILE_TYPE_NATIVE_BI))
906 	{
907 	  string cmd = "diff -u " + opts.file_path + " " + tmp_file->get_path();
908 	  if (system(cmd.c_str()))
909 	    is_ok = false;
910 	}
911 
912 #ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
913       if (is_ok
914 	  && !opts.type_id_to_show.empty())
915 	{
916 	  ABG_ASSERT(abixml_read_ctxt);
917 	  show_how_type_is_used(*abixml_read_ctxt, opts.type_id_to_show);
918 	}
919 #endif
920       return is_ok ? 0 : 1;
921     }
922 
923   return 1;
924 }
925