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