File: abilint.cc

package info (click to toggle)
libabigail 2.9-2
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 1,021,756 kB
  • sloc: xml: 572,663; cpp: 110,945; sh: 11,868; ansic: 4,329; makefile: 3,486; python: 1,684; ada: 62
file content (949 lines) | stat: -rw-r--r-- 27,504 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2025 Red Hat, Inc.
//
// Author: Dodji Seketeli

/// @file
///
/// This is a program aimed at checking that a binary instrumentation
/// (bi) file is well formed and valid enough.  It acts by loading an
/// input bi file and saving it back to a temporary file.  It then
/// runs a diff on the two files and expects the result of the diff to
/// be empty.

#include "config.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "abg-config.h"
#include "abg-tools-utils.h"
#include "abg-ir.h"
#include "abg-corpus.h"
#include "abg-reader.h"
#include "abg-dwarf-reader.h"
#ifdef WITH_CTF
#include "abg-ctf-reader.h"
#endif
#include "abg-writer.h"
#include "abg-suppression.h"

using std::string;
using std::cerr;
using std::cin;
using std::cout;
using std::ostream;
using std::ofstream;
using std::vector;
using std::unordered_set;
using std::unique_ptr;
using abigail::tools_utils::emit_prefix;
using abigail::tools_utils::check_file;
using abigail::tools_utils::file_type;
using abigail::tools_utils::guess_file_type;
using abigail::suppr::suppression_sptr;
using abigail::suppr::suppressions_type;
using abigail::suppr::read_suppressions;
using abigail::type_base;
using abigail::type_or_decl_base;
using abigail::type_base_sptr;
using abigail::type_or_decl_base_sptr;
using abigail::corpus;
using abigail::corpus_sptr;
using abigail::abixml::read_translation_unit_from_file;
using abigail::abixml::read_translation_unit_from_istream;
using abigail::abixml::read_corpus_from_abixml;
using abigail::abixml::read_corpus_from_abixml_file;
using abigail::abixml::read_corpus_group_from_input;
#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
using abigail::abixml::get_types_from_type_id;
using abigail::abixml::get_artifact_used_by_relation_map;
#endif

using abigail::xml_writer::write_translation_unit;
using abigail::xml_writer::write_context_sptr;
using abigail::xml_writer::create_write_context;
using abigail::xml_writer::write_corpus;
using abigail::xml_writer::write_corpus_to_archive;

struct options
{
  string			wrong_option;
  string			file_path;
  bool				display_version;
  bool				read_from_stdin;
  bool				read_tu;
  bool				diff;
  bool				noout;
  bool				annotate;
  bool				do_log;
#ifdef WITH_CTF
  bool				use_ctf;
#endif
  string			di_root_path;
  vector<string>		suppression_paths;
  string			headers_dir;
  vector<string>		header_files;
#if WITH_SHOW_TYPE_USE_IN_ABILINT
  string			type_id_to_show;
#endif

  options()
    : display_version(false),
      read_from_stdin(false),
      read_tu(false),
      diff(false),
      noout(false),
      annotate(false),
      do_log(false)
#ifdef WITH_CTF
    ,
      use_ctf(false)
#endif
  {}
};//end struct options;

#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
/// A tree node representing the "use" relation between an artifact A
/// (e.g, a type) and a set of artifacts {A'} that use "A" as in "A"
/// is a sub-type of A'.
///
/// So the node contains the artifact A and a vector children nodes
/// that contain the A' artifacts that use A.
struct artifact_use_relation_tree
{
  artifact_use_relation_tree *root_node = nullptr;
  /// The parent node of this one.  Is nullptr if this node is the root
  /// node.
  artifact_use_relation_tree *parent = nullptr;
  /// The artifact contained in this node.
  type_or_decl_base* artifact = nullptr;
  /// The vector of children nodes that carry the artifacts that
  /// actually use the 'artifact' above.  In other words, the
  /// 'artifact" data member above is a sub-type of each artifact
  /// contained in this vector.
  vector<unique_ptr<artifact_use_relation_tree>> artifact_users;
  /// This is the set of artifacts that have been added to the tree.
  /// This is useful to ensure that all artifacts are added just once
  /// in the tree to prevent infinite loops.
  unordered_set<type_or_decl_base *> artifacts;

  /// The constructor of the tree node.
  ///
  /// @param the artifact to consider.
  artifact_use_relation_tree(type_or_decl_base* t)
    : artifact (t)
  {
    ABG_ASSERT(t && !artifact_in_tree(t));
    record_artifact(t);
  }

  /// Add a user artifact node for the artifact carried by this node.
  ///
  /// The artifact carried by the current node is a sub-type of the
  /// artifact carried by the 'user' node being added.
  ///
  /// @param user a tree node that carries an artifact that uses the
  /// artifact carried by the current node.
  void
  add_artifact_user(artifact_use_relation_tree *user)
  {
    ABG_ASSERT(user && !artifact_in_tree(user->artifact ));
    artifact_users.push_back(unique_ptr<artifact_use_relation_tree>(user));
    user->parent = this;
    record_artifact(user->artifact);
  }

  /// Move constructor.
  ///
  /// @param o the source of the move.
  artifact_use_relation_tree(artifact_use_relation_tree &&o)
  {
    parent = o.parent;
    artifact = o.artifact;
    artifact_users = std::move(o.artifact_users);
    artifacts = std::move(o.artifacts);
  }

  /// Move assignment operator.
  ///
  /// @param o the source of the assignment.
  artifact_use_relation_tree& operator=(artifact_use_relation_tree&& o)
  {
    parent = o.parent;
    artifact = o.artifact;
    artifact_users = std::move(o.artifact_users);
    artifacts = std::move(o.artifacts);
    return *this;
  }

  /// Test if the current node is a leaf node.
  ///
  /// @return true if the artifact carried by the current node has no
  /// user artifacts.
  bool
  is_leaf() const
  {return artifact_users.empty();}

  /// Test if the current node is a root node.
  ///
  /// @return true if the current artifact uses no other artifact.
  bool
  is_root() const
  {return parent == nullptr;}

  /// Test wether a given artifact has been added to the tree.
  ///
  /// Here, the tree means the tree that the current tree node is part
  /// of.
  ///
  /// An artifact is considered as having been added to the tree if
  /// artifact_use_relation_tree::record_artifact has been invoked on
  /// it.
  ///
  /// @param artifact the artifact to consider.
  ///
  /// @return true iff @p artifact is present in the tree.
  bool
  artifact_in_tree(type_or_decl_base *artifact)
  {
    artifact_use_relation_tree *root_node = get_root_node();
    ABG_ASSERT(root_node);
    return root_node->artifacts.find(artifact) != root_node->artifacts.end();
  }

  /// Record an artifact as being added to the current tree.
  ///
  /// Note that this function assumes the artifact is not already
  /// present in the tree containing the current tree node.
  ///
  /// @param artifact the artifact to consider.
  void
  record_artifact(type_or_decl_base *artifact)
  {
    ABG_ASSERT(!artifact_in_tree(artifact));
    artifact_use_relation_tree *root_node = get_root_node();
    ABG_ASSERT(root_node);
    root_node->artifacts.insert(artifact);
  }

  /// Get the root node of the current tree.
  ///
  /// @return the root node of the current tree.
  artifact_use_relation_tree*
  get_root_node()
  {
    if (root_node)
      return root_node;

    if (parent == nullptr)
      return this;

    root_node = parent->get_root_node();
    return root_node;
  }

  artifact_use_relation_tree(const artifact_use_relation_tree&) = delete;
  artifact_use_relation_tree& operator=(const artifact_use_relation_tree&) = delete;
}; // end struct artifact_use_relation_tree

/// Fill an "artifact use" tree from a map that associates a type T
/// (or artifact) to artifacts that use T as a sub-type.
///
/// @param artifact_use_rel the map that establishes the relation
/// between a type T and the artifacts that use T as a sub-type.
///
/// @parm tree output parameter.  This function will fill up this tree
/// from the information carried in @p artifact_use_rel.  Each node of
/// the tree contains an artifact A and its children nodes contain the
/// artifacts A' that use A as a sub-type.
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)
{
  auto r = artifact_use_rel.find(tree.artifact);
  if (r == artifact_use_rel.end())
    return;

  // Walk the users of "artifact", create a tree node for each one of
  // them, and add them as children node of the current tree node
  // named 'tree'.
  for (auto user : r->second)
    {
      if (tree.artifact_in_tree(user))
	// The artifact has already been added to the tree, so skip it
	// otherwise we can loop for ever.
	continue;

      artifact_use_relation_tree *user_tree =
	new artifact_use_relation_tree(user);

      // Now add the new user node as a child of the current tree
      // node.
      tree.add_artifact_user(user_tree);

      // Recursively fill the newly created tree node.
      fill_artifact_use_tree(artifact_use_rel, *user_tree);
    }
}

/// construct an "artifact use tree" for a type designated by a "type-id".
/// (or artifact) to artifacts that use T as a sub-type.
///
/// Each node of the "artifact use tree" contains a type T and its
/// children nodes contain the artifacts A' that use T as a sub-type.
/// The root node is the type designed by a given type-id.
///
/// @param ctxt the abixml read context to consider.
///
/// @param type_id the type-id of the type to construct the "use tree"
/// for.
static unique_ptr<artifact_use_relation_tree>
build_type_use_tree(abigail::abixml::reader &ctxt,
		    const string& type_id)
{
  unique_ptr<artifact_use_relation_tree> result;
  vector<type_base_sptr>* types = get_types_from_type_id(ctxt, type_id);
  if (!types)
    return result;

  std::unordered_map<type_or_decl_base*, vector<type_or_decl_base*>>*
    artifact_use_rel = get_artifact_used_by_relation_map(ctxt);
  if (!artifact_use_rel)
    return result;

  type_or_decl_base_sptr type = types->front();
  unique_ptr<artifact_use_relation_tree> use_tree
    (new artifact_use_relation_tree(type.get()));

  fill_artifact_use_tree(*artifact_use_rel, *use_tree);

  result = std::move(use_tree);
  return result;
}

/// Emit a visual representation of a "type use trace".
///
/// The trace is vector of strings.  Each string is the textual
/// representation of a type.  The next element in the vector is a
/// type using the previous element, as in, the "previous element is a
/// sub-type of the next element".
///
/// This is a sub-routine of emit_artifact_use_trace.
///
/// @param the trace vector to emit.
///
/// @param out the output stream to emit the trace to.
static void
emit_trace(const vector<string>& trace, ostream& out)
{
  if (trace.empty())
    return;

  if (!trace.empty())
    // Make the beginning of the trace line of the usage of a given
    // type be easily recognizeable by a "pattern".
    out << "===";

  for (auto element : trace)
    out << "-> " << element << " ";

  if (!trace.empty())
    // Make the end of the trace line of the usage of a given type be
    // easily recognizeable by another "pattern".
    out << " <-~~~";

  out << "\n";
}

/// Walk a @ref artifact_use_relation_tree to emit a "type-is-used-by"
/// trace.
///
/// The tree carries the information about how a given type is used by
/// other types.  This function walks the tree by visiting a node
/// carrying a given type T, and then the nodes for which T is a
/// sub-type.  The function accumulates a trace made of the textual
/// representation of the visited nodes and then emits that trace on
/// an output stream.
///
/// @param artifact_use_tree the tree to walk.
///
/// @param trace the accumulated vector of the textual representations
/// of the types carried by the visited nodes.
///
/// @param out the output stream to emit the trace to.
static void
emit_artifact_use_trace(const artifact_use_relation_tree& artifact_use_tree,
			vector<string>& trace, ostream& out)
{
  type_or_decl_base* artifact = artifact_use_tree.artifact;
  if (!artifact)
    return;

  string repr = artifact->get_pretty_representation();
  trace.push_back(repr);

  if (artifact_use_tree.artifact_users.empty())
    {
      // We reached a leaf node.  This means that no other artifact
      // uses the artifact carried by this leaf node.  So, we want to
      // emit the trace accumulated to this point.

      // But we only want to emit the usage traces that end up with a
      // function of variable that have an associated ELF symbol.
      bool do_emit_trace = false;
      if (is_decl(artifact))
	{
	  if (abigail::ir::var_decl* v = is_var_decl(artifact))
	    if (v->get_symbol()
		|| is_at_global_scope(v)
		|| !v->get_linkage_name().empty())
	      do_emit_trace = true;
	  if (abigail::ir::function_decl* f = is_function_decl(artifact))
	    if (f->get_symbol()
		|| is_at_global_scope(f)
		|| !f->get_linkage_name().empty())
	      do_emit_trace = true;
	}

      // OK now, really emit the trace.
      if (do_emit_trace)
	emit_trace(trace, out);

      trace.pop_back();
      return;
    }

  for (const auto &user : artifact_use_tree.artifact_users)
    emit_artifact_use_trace(*user, trace, out);

  trace.pop_back();
}

/// Walk a @ref artifact_use_relation_tree to emit a "type-is-used-by"
/// trace.
///
/// The tree carries the information about how a given type is used by
/// other types.  This function walks the tree by visiting a node
/// carrying a given type T, and then the nodes for which T is a
/// sub-type.  The function then emits a trace of how the root type is
/// used.
///
/// @param artifact_use_tree the tree to walk.
///
/// @param out the output stream to emit the trace to.
static void
emit_artifact_use_trace(const artifact_use_relation_tree& artifact_use_tree,
			ostream& out)
{
  vector<string> trace;
  emit_artifact_use_trace(artifact_use_tree, trace, out);
}

/// Show how a type is used.
///
/// The type to consider is designated by a type-id string that is
/// carried by the options data structure.
///
/// @param ctxt the abixml read context to consider.
///
/// @param the type_id of the type which usage to analyse.
static bool
show_how_type_is_used(abigail::abixml::reader &ctxt,
		      const string& type_id)
{
  if (type_id.empty())
    return false;

  unique_ptr<artifact_use_relation_tree> use_tree =
    build_type_use_tree(ctxt, type_id);
  if (!use_tree)
    return false;

  // Now walk the use_tree to emit the type use trace
  if (use_tree->artifact)
    {
      std::cout << "Type ID '"
		<< type_id << "' is for type '"
		<< use_tree->artifact->get_pretty_representation()
		<< "'\n"
		<< "The usage graph for that type is:\n";
      emit_artifact_use_trace(*use_tree, std::cout);
    }
  return true;
}
#endif // WITH_SHOW_TYPE_USE_IN_ABILINT

static void
display_usage(const string& prog_name, ostream& out)
{
  emit_prefix(prog_name, out)
    << "usage: " << prog_name << " [options] [<abi-file1>]\n"
    << " where options can be:\n"
    << "  --annotate  annotate the ABI artifacts emitted in the output\n"
    << "  --verbose  show verbose messages about internal stuff\n"
#ifdef WITH_CTF
    << "  --ctf use CTF instead of DWARF in ELF files\n"
#endif
    << "  --debug-info-dir <path> the path under which to look for "
    "debug info for the elf <abi-file>\n"
    << "  --diff  for xml inputs, perform a text diff between "
    "the input and the memory model saved back to disk\n"
    << "  --header-file|--hf <path> the path to one header of the elf file\n"
    << "  --headers-dir|--hd <path> the path to headers of the elf file\n"
    << "  --help  display this message\n"
    << "  --noout  do not display anything on stdout\n"
#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
    << "  --show-type-use <type-id>  show how a type is used from the abixml file\n"
#endif
    << "  --stdin  read abi-file content from stdin\n"
    << "  --suppressions|--suppr <path> specify a suppression file\n"
    << "  --tu  expect a single translation unit file\n"
    << "  --version|-v  display program version information and exit\n"
    ;
}

bool
parse_command_line(int argc, char* argv[], options& opts)
{
  if (argc < 2)
    {
      opts.read_from_stdin = true;
      return true;
    }

    for (int i = 1; i < argc; ++i)
      {
	if (argv[i][0] != '-')
	  {
	    if (opts.file_path.empty())
	      opts.file_path = argv[i];
	    else
	      return false;
	  }
	else if (!strcmp(argv[i], "--help"))
	  return false;
	else if (!strcmp(argv[i], "--version")
		 || !strcmp(argv[i], "-v"))
	  {
	    opts.display_version = true;
	    return true;
	  }
	else if (!strcmp(argv[i], "--debug-info-dir"))
	  {
	    if (argc <= i + 1
		|| argv[i + 1][0] == '-')
	      return false;
	    // elfutils wants the root path to the debug info to be
	    // absolute.
	    opts.di_root_path =
	      abigail::tools_utils::make_path_absolute(string(argv[i + 1]));
	    ++i;
	  }
      else if (!strcmp(argv[i], "--headers-dir")
	       || !strcmp(argv[i], "--hd"))
	{
	  int j = i + 1;
	  if (j >= argc)
	    return false;
	  opts.headers_dir = argv[j];
	  ++i;
	}
      else if (!strcmp(argv[i], "--header-file")
	       || !strcmp(argv[i], "--hf"))
	{
	  int j = i + 1;
	  if (j >= argc)
	    return false;
	  opts.header_files.push_back(argv[j]);
	  ++i;
	}
      else if (!strcmp(argv[i], "--suppressions")
	       || !strcmp(argv[i], "--suppr"))
	{
	  int j = i + 1;
	  if (j >= argc)
	    {
	      opts.wrong_option = argv[i];
	      return true;
	    }
	  opts.suppression_paths.push_back(argv[j]);
	  ++i;
	}
	else if (!strcmp(argv[i], "--stdin"))
	  opts.read_from_stdin = true;
	else if (!strcmp(argv[i], "--tu"))
	  opts.read_tu = true;
#ifdef WITH_CTF
        else if (!strcmp(argv[i], "--ctf"))
          opts.use_ctf = true;
#endif
	else if (!strcmp(argv[i], "--diff"))
	  opts.diff = true;
	else if (!strcmp(argv[i], "--noout"))
	  opts.noout = true;
	else if (!strcmp(argv[i], "--annotate"))
	  opts.annotate = true;
	else if (!strcmp(argv[i], "--verbose"))
	  opts.do_log = true;
#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
      else if (!strcmp(argv[i], "--show-type-use"))
	{
	  ++i;
	  if (i >= argc || argv[i][0] == '-')
	    return false;
	  opts.type_id_to_show = argv[i];
	}
#endif
	else
	  {
	    if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
	      opts.wrong_option = argv[i];
	    return false;
	  }
      }

#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
    if (!opts.type_id_to_show.empty()
	&& opts.file_path.empty())
      emit_prefix(argv[0], cout)
	<< "WARNING: --show-type-use <type-id> "
	"must be accompanied with an abixml file\n";

    if (opts.file_path.empty()
	&& opts.type_id_to_show.empty())
      opts.read_from_stdin = true;
#endif

    if (opts.read_from_stdin && !opts.file_path.empty())
    {
      emit_prefix(argv[0], cout)
        << "WARNING: The \'--stdin\' option is used. The "
        << opts.file_path << " will be ignored automatically\n";
    }

    return true;
}

/// Check that the suppression specification files supplied are
/// present.  If not, emit an error on stderr.
///
/// @param opts the options instance to use.
///
/// @return true if all suppression specification files are present,
/// false otherwise.
static bool
maybe_check_suppression_files(const options& opts)
{
  for (vector<string>::const_iterator i = opts.suppression_paths.begin();
       i != opts.suppression_paths.end();
       ++i)
    if (!check_file(*i, cerr, "abidiff"))
      return false;

  return true;
}

/// Set suppression specifications to the @p reader used to load
/// the ABI corpus from the ELF/DWARF file.
///
/// These suppression specifications are going to be applied to drop
/// some ABI artifacts on the floor (while reading the ELF/DWARF file
/// or the native XML ABI file) and thus minimize the size of the
/// resulting ABI corpus.
///
/// @param read_ctxt the read context to apply the suppression
/// specifications to.  Note that the type of this parameter is
/// generic (class template) because in practise, it can be either an
/// abigail::dwarf_reader::reader type or an
/// abigail::abixml::reader type.
///
/// @param opts the options where to get the suppression
/// specifications from.
static void
set_suppressions(abigail::fe_iface& reader, const options& opts)
{
  suppressions_type supprs;
  for (vector<string>::const_iterator i = opts.suppression_paths.begin();
       i != opts.suppression_paths.end();
       ++i)
    read_suppressions(*i, supprs);

  suppression_sptr suppr =
    abigail::tools_utils::gen_suppr_spec_from_headers(opts.headers_dir,
						      opts.header_files);
  if (suppr)
    supprs.push_back(suppr);

  reader.add_suppressions(supprs);
}

/// Set the options of the reader.
///
/// @param reader the reader to consider.
///
/// @param opts the options to use.
static void
set_reader_options(abigail::fe_iface& reader, const options& opts)
{
  set_suppressions(reader, opts);
  reader.options().do_log = opts.do_log;
}

/// Reads a bi (binary instrumentation) file, saves it back to a
/// temporary file and run a diff on the two versions.
int
main(int argc, char* argv[])
{
  abigail::tools_utils::initialize();

  options opts;
  if (!parse_command_line(argc, argv, opts))
    {
      if (!opts.wrong_option.empty())
	emit_prefix(argv[0], cerr)
	  << "unrecognized option: " << opts.wrong_option << "\n";
      display_usage(argv[0], cerr);
      return 1;
    }

  if (opts.display_version)
    {
      emit_prefix(argv[0], cout)
	<< abigail::tools_utils::get_library_version_string()
	<< "\n";
      return 0;
    }

  if (!maybe_check_suppression_files(opts))
    return 1;

  abigail::ir::environment env;
  if (opts.read_from_stdin)
    {
      if (!cin.good())
	return 1;

      if (opts.read_tu)
	{
	  abigail::translation_unit_sptr tu =
	    read_translation_unit_from_istream(&cin, env);

	  if (!tu)
	    {
	      emit_prefix(argv[0], cerr)
		<< "failed to read the ABI instrumentation from stdin\n";
	      return 1;
	    }

	  if (!opts.noout)
	    {
	      const write_context_sptr& ctxt
		  = create_write_context(env, cout);
	      set_annotate(*ctxt, opts.annotate);
	      write_translation_unit(*ctxt, *tu, 0);
	    }
	  return 0;
	}
      else
	{
	  abigail::fe_iface_sptr rdr =
	    abigail::abixml::create_reader(&cin, env);
	  assert(rdr);
	  set_reader_options(*rdr, opts);
	  abigail::fe_iface::status sts;
	  corpus_sptr corp = rdr->read_corpus(sts);
	  if (!opts.noout)
	    {
	      const write_context_sptr& ctxt
		  = create_write_context(env, cout);
	      set_annotate(*ctxt, opts.annotate);
	      write_corpus(*ctxt, corp, /*indent=*/0);
	    }
	  return 0;
	}
    }
  else if (!opts.file_path.empty())
    {
      if (!check_file(opts.file_path, cerr, argv[0]))
	return 1;
      abigail::translation_unit_sptr tu;
      abigail::corpus_sptr corp;
      abigail::corpus_group_sptr group;
      abigail::fe_iface::status s = abigail::fe_iface::STATUS_OK;
      string di_root_path;
      file_type type = guess_file_type(opts.file_path);

      switch (type)
	{
	case abigail::tools_utils::FILE_TYPE_UNKNOWN:
	  emit_prefix(argv[0], cerr)
	    << "Unknown file type given in input: " << opts.file_path
	    << "\n";
	  return 1;
	case abigail::tools_utils::FILE_TYPE_NATIVE_BI:
	  {
	    abigail::fe_iface_sptr rdr =
	      abigail::abixml::create_reader(opts.file_path,
					     env);
	    set_reader_options(*rdr, opts);
	    tu = abigail::abixml::read_translation_unit(*rdr);
	  }
	  break;
	case abigail::tools_utils::FILE_TYPE_ELF:
	case abigail::tools_utils::FILE_TYPE_AR:
	  {
	    di_root_path = opts.di_root_path;
	    vector<string> di_roots;
	    di_roots.push_back(di_root_path);
	    abigail::elf_based_reader_sptr rdr;
#ifdef WITH_CTF
            if (opts.use_ctf)
	      rdr =
		abigail::ctf::create_reader(opts.file_path,
					    di_roots, env);
            else
#endif
	      rdr =
		abigail::dwarf::create_reader(opts.file_path,
					      di_roots, env,
					      /*load_all_types=*/false);
	    set_reader_options(*rdr, opts);
	    corp = rdr->read_corpus(s);
	  }
	  break;
	case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
	  {
	    abigail::fe_iface_sptr rdr =
	      abigail::abixml::create_reader(opts.file_path, env);
	    assert(rdr);
	    set_reader_options(*rdr, opts);
	    corp = rdr->read_corpus(s);
	    break;
	  }
	case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
	  {
	    abigail::fe_iface_sptr rdr =
	      abigail::abixml::create_reader(opts.file_path, env);
	    assert(rdr);
	    set_reader_options(*rdr, opts);
	    group = read_corpus_group_from_input(*rdr);
	  }
	  break;
	case abigail::tools_utils::FILE_TYPE_RPM:
	case abigail::tools_utils::FILE_TYPE_SRPM:
	case abigail::tools_utils::FILE_TYPE_DEB:
	case abigail::tools_utils::FILE_TYPE_DIR:
	case abigail::tools_utils::FILE_TYPE_TAR:
	case abigail::tools_utils::FILE_TYPE_XZ:
	  break;
	}

      if (!tu && !corp && !group)
	{
	  emit_prefix(argv[0], cerr)
	    << "failed to read " << opts.file_path << "\n";
	  if (!(s & abigail::fe_iface::STATUS_OK))
	    {
	      if (s & abigail::fe_iface::STATUS_DEBUG_INFO_NOT_FOUND)
		{
		  cerr << "could not find the debug info";
		  if(di_root_path.empty())
		    emit_prefix(argv[0], cerr)
		      << " Maybe you should consider using the "
		      "--debug-info-dir1 option to tell me about the "
		      "root directory of the debuginfo? "
		      "(e.g, --debug-info-dir1 /usr/lib/debug)\n";
		  else
		    emit_prefix(argv[0], cerr)
		      << "Maybe the root path to the debug "
		      "information is wrong?\n";
		}
	      if (s & abigail::fe_iface::STATUS_NO_SYMBOLS_FOUND)
		emit_prefix(argv[0], cerr)
		  << "could not find the ELF symbols in the file "
		  << opts.file_path
		  << "\n";
	    }
	  return 1;
	}

      using abigail::tools_utils::temp_file;
      using abigail::tools_utils::temp_file_sptr;

      temp_file_sptr tmp_file = temp_file::create();
      if (!tmp_file)
	{
	  emit_prefix(argv[0], cerr) << "failed to create temporary file\n";
	  return 1;
	}

      std::ostream& of = opts.diff ? tmp_file->get_stream() : cout;
      const write_context_sptr ctxt = create_write_context(env, of);

      bool is_ok = true;

      if (tu)
	{
	  if (!opts.noout)
	    is_ok = write_translation_unit(*ctxt, *tu, 0);
	}
      else
	{
	  if (type == abigail::tools_utils::FILE_TYPE_XML_CORPUS
	      || type == abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP
	      || type == abigail::tools_utils::FILE_TYPE_ELF)
	    {
	      if (!opts.noout)
		{
		  set_annotate(*ctxt, opts.annotate);
		  if (corp)
		    is_ok = write_corpus(*ctxt, corp, 0);
		  else if (group)
		    is_ok = write_corpus_group(*ctxt, group, 0);
		}
	    }
	}

      if (!is_ok)
	{
	  string output =
	    (type == abigail::tools_utils::FILE_TYPE_NATIVE_BI)
	    ? "translation unit"
	    : "ABI corpus";
	  emit_prefix(argv[0], cerr)
	    << "failed to write the translation unit "
	    << opts.file_path << " back\n";
	}

      if (is_ok
	  && opts.diff
	  && ((type == abigail::tools_utils::FILE_TYPE_XML_CORPUS)
	      ||type == abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP
	      || type == abigail::tools_utils::FILE_TYPE_NATIVE_BI))
	{
	  string cmd = "diff -u " + opts.file_path + " " + tmp_file->get_path();
	  if (system(cmd.c_str()))
	    is_ok = false;
	}

#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
      if (is_ok
	  && !opts.type_id_to_show.empty())
	{
	  ABG_ASSERT(abixml_read_ctxt);
	  show_how_type_is_used(*abixml_read_ctxt, opts.type_id_to_show);
	}
#endif
      return is_ok ? 0 : 1;
    }

  return 1;
}