1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2020 Red Hat, Inc.
5 //
6 // Author: Dodji Seketeli
7 
8 /// @file
9 ///
10 /// This program reads an elf file, try to load its debug info (in
11 /// DWARF format) and emit it back in a set of "text sections" in native
12 /// libabigail XML format.
13 
14 #include <unistd.h>
15 #include <cassert>
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cstring>
19 #include <fstream>
20 #include <iostream>
21 #include <memory>
22 #include <string>
23 #include <vector>
24 #include "abg-config.h"
25 #include "abg-tools-utils.h"
26 #include "abg-corpus.h"
27 #include "abg-dwarf-reader.h"
28 #include "abg-writer.h"
29 #include "abg-reader.h"
30 #include "abg-comparison.h"
31 
32 using std::string;
33 using std::cerr;
34 using std::cout;
35 using std::ostream;
36 using std::ofstream;
37 using std::vector;
38 using std::shared_ptr;
39 using abigail::tools_utils::emit_prefix;
40 using abigail::tools_utils::temp_file;
41 using abigail::tools_utils::temp_file_sptr;
42 using abigail::tools_utils::check_file;
43 using abigail::tools_utils::build_corpus_group_from_kernel_dist_under;
44 using abigail::tools_utils::timer;
45 using abigail::ir::environment_sptr;
46 using abigail::ir::environment;
47 using abigail::corpus;
48 using abigail::corpus_sptr;
49 using abigail::translation_units;
50 using abigail::suppr::suppression_sptr;
51 using abigail::suppr::suppressions_type;
52 using abigail::suppr::read_suppressions;
53 using abigail::comparison::corpus_diff;
54 using abigail::comparison::corpus_diff_sptr;
55 using abigail::comparison::compute_diff;
56 using abigail::comparison::diff_context_sptr;
57 using abigail::comparison::diff_context;
58 using abigail::xml_writer::SEQUENCE_TYPE_ID_STYLE;
59 using abigail::xml_writer::HASH_TYPE_ID_STYLE;
60 using abigail::xml_writer::create_write_context;
61 using abigail::xml_writer::type_id_style_kind;
62 using abigail::xml_writer::write_context_sptr;
63 using abigail::xml_writer::write_corpus;
64 using abigail::xml_reader::read_corpus_from_native_xml_file;
65 using abigail::dwarf_reader::read_context;
66 using abigail::dwarf_reader::read_context_sptr;
67 using abigail::dwarf_reader::read_corpus_from_elf;
68 using abigail::dwarf_reader::create_read_context;
69 using namespace abigail;
70 
71 struct options
72 {
73   string		wrong_option;
74   string		in_file_path;
75   string		out_file_path;
76   vector<char*>	di_root_paths;
77   vector<char**>	prepared_di_root_paths;
78   vector<string>	headers_dirs;
79   vector<string>	header_files;
80   string		vmlinux;
81   vector<string>	suppression_paths;
82   vector<string>	kabi_whitelist_paths;
83   suppressions_type	kabi_whitelist_supprs;
84   bool			display_version;
85   bool			check_alt_debug_info_path;
86   bool			show_base_name_alt_debug_info_path;
87   bool			write_architecture;
88   bool			write_corpus_path;
89   bool			write_comp_dir;
90   bool			write_elf_needed;
91   bool			write_parameter_names;
92   bool			short_locs;
93   bool			default_sizes;
94   bool			load_all_types;
95   bool			linux_kernel_mode;
96   bool			corpus_group_for_linux;
97   bool			show_stats;
98   bool			noout;
99   bool			show_locs;
100   bool			abidiff;
101   bool			annotate;
102   bool			do_log;
103   bool			drop_private_types;
104   bool			drop_undefined_syms;
105   bool			merge_translation_units;
106   type_id_style_kind	type_id_style;
107 
optionsoptions108   options()
109     : display_version(),
110       check_alt_debug_info_path(),
111       show_base_name_alt_debug_info_path(),
112       write_architecture(true),
113       write_corpus_path(true),
114       write_comp_dir(true),
115       write_elf_needed(true),
116       write_parameter_names(true),
117       short_locs(false),
118       default_sizes(true),
119       load_all_types(),
120       linux_kernel_mode(true),
121       corpus_group_for_linux(false),
122       show_stats(),
123       noout(),
124       show_locs(true),
125       abidiff(),
126       annotate(),
127       do_log(),
128       drop_private_types(false),
129       drop_undefined_syms(false),
130       merge_translation_units(false),
131       type_id_style(SEQUENCE_TYPE_ID_STYLE)
132   {}
133 
~optionsoptions134   ~options()
135   {
136     for (vector<char*>::iterator i = di_root_paths.begin();
137 	 i != di_root_paths.end();
138 	 ++i)
139       free(*i);
140 
141     prepared_di_root_paths.clear();
142   }
143 };
144 
145 static void
display_usage(const string & prog_name,ostream & out)146 display_usage(const string& prog_name, ostream& out)
147 {
148   emit_prefix(prog_name, out)
149     << "usage: " << prog_name << " [options] [<path-to-elf-file>]\n"
150     << " where options can be: \n"
151     << "  --help|-h  display this message\n"
152     << "  --version|-v  display program version information and exit\n"
153     << "  --debug-info-dir|-d <dir-path>  look for debug info under 'dir-path'\n"
154     << "  --headers-dir|--hd <path> the path to headers of the elf file\n"
155     << "  --header-file|--hf <path> the path one header of the elf file\n"
156     << "  --out-file <file-path>  write the output to 'file-path'\n"
157     << "  --noout  do not emit anything after reading the binary\n"
158     << "  --suppressions|--suppr <path> specify a suppression file\n"
159     << "  --no-architecture  do not emit architecture info in the output\n"
160     << "  --no-corpus-path  do not take the path to the corpora into account\n"
161     << "  --no-show-locs  do not show location information\n"
162     << "  --short-locs  only print filenames rather than paths\n"
163     << "  --drop-private-types  drop private types from representation\n"
164     << "  --drop-undefined-syms  drop undefined symbols from representation\n"
165     << "  --merge-translation-units  merge translation units for same language\n"
166     << "  --no-comp-dir-path  do not show compilation path information\n"
167     << "  --no-elf-needed  do not show the DT_NEEDED information\n"
168     << "  --no-write-default-sizes  do not emit pointer size when it equals"
169     " the default address size of the translation unit\n"
170     << "  --no-parameter-names  do not show names of function parameters\n"
171     << "  --type-id-style <sequence|hash>  type id style (sequence(default): "
172        "\"type-id-\" + number; hash: hex-digits)\n"
173     << "  --check-alternate-debug-info <elf-path>  check alternate debug info "
174     "of <elf-path>\n"
175     << "  --check-alternate-debug-info-base-name <elf-path>  check alternate "
176     "debug info of <elf-path>, and show its base name\n"
177     << "  --load-all-types  read all types including those not reachable from "
178     "exported declarations\n"
179     << "  --no-linux-kernel-mode  don't consider the input binary as "
180        "a Linux Kernel binary\n"
181     << "  --kmi-whitelist|-w  path to a linux kernel "
182     "abi whitelist\n"
183     << "  --linux-tree|--lt  emit the ABI for the union of a "
184     "vmlinux and its modules\n"
185     << "  --vmlinux <path>  the path to the vmlinux binary to consider to emit "
186        "the ABI of the union of vmlinux and its modules\n"
187     << "  --abidiff  compare the loaded ABI against itself\n"
188     << "  --annotate  annotate the ABI artifacts emitted in the output\n"
189     << "  --stats  show statistics about various internal stuff\n"
190     << "  --verbose show verbose messages about internal stuff\n";
191   ;
192 }
193 
194 static bool
parse_command_line(int argc,char * argv[],options & opts)195 parse_command_line(int argc, char* argv[], options& opts)
196 {
197   if (argc < 2)
198     return false;
199 
200   for (int i = 1; i < argc; ++i)
201     {
202       if (argv[i][0] != '-')
203 	{
204 	  if (opts.in_file_path.empty())
205 	    opts.in_file_path = argv[i];
206 	  else
207 	    return false;
208 	}
209       else if (!strcmp(argv[i], "--version")
210 	       || !strcmp(argv[i], "-v"))
211 	opts.display_version = true;
212       else if (!strcmp(argv[i], "--debug-info-dir")
213 	       || !strcmp(argv[i], "-d"))
214 	{
215 	  if (argc <= i + 1
216 	      || argv[i + 1][0] == '-')
217 	    return false;
218 	  // elfutils wants the root path to the debug info to be
219 	  // absolute.
220 	  opts.di_root_paths.push_back
221 	    (abigail::tools_utils::make_path_absolute_to_be_freed(argv[i + 1]));
222 	  ++i;
223 	}
224       else if (!strcmp(argv[i], "--headers-dir")
225 	       || !strcmp(argv[i], "--hd"))
226 	{
227 	  int j = i + 1;
228 	  if (j >= argc)
229 	    return false;
230 	  opts.headers_dirs.push_back(argv[j]);
231 	  ++i;
232 	}
233       else if (!strcmp(argv[i], "--header-file")
234 	       || !strcmp(argv[i], "--hf"))
235 	{
236 	  int j = i + 1;
237 	  if (j >= argc)
238 	    return false;
239 	  opts.header_files.push_back(argv[j]);
240 	  ++i;
241 	}
242       else if (!strcmp(argv[i], "--out-file"))
243 	{
244 	  if (argc <= i + 1
245 	      || argv[i + 1][0] == '-'
246 	      || !opts.out_file_path.empty())
247 	    return false;
248 
249 	  opts.out_file_path = argv[i + 1];
250 	  ++i;
251 	}
252       else if (!strcmp(argv[i], "--suppressions")
253 	       || !strcmp(argv[i], "--suppr"))
254 	{
255 	  int j = i + 1;
256 	  if (j >= argc)
257 	    return false;
258 	  opts.suppression_paths.push_back(argv[j]);
259 	  ++i;
260 	}
261       else if (!strcmp(argv[i], "--kmi-whitelist")
262 	       || !strcmp(argv[i], "-w"))
263 	{
264 	  int j = i + 1;
265 	  if (j >= argc)
266 	    return false;
267 	  opts.kabi_whitelist_paths.push_back(argv[j]);
268 	  ++i;
269 	}
270       else if (!strcmp(argv[i], "--linux-tree")
271 	       || !strcmp(argv[i], "--lt"))
272 	opts.corpus_group_for_linux = true;
273       else if (!strcmp(argv[i], "--vmlinux"))
274 	{
275 	  int j = i + 1;
276 	  if (j >= argc)
277 	    return false;
278 	  opts.vmlinux = argv[j];
279 	  ++i;
280 	}
281       else if (!strcmp(argv[i], "--noout"))
282 	opts.noout = true;
283       else if (!strcmp(argv[i], "--no-architecture"))
284 	opts.write_architecture = false;
285       else if (!strcmp(argv[i], "--no-corpus-path"))
286 	opts.write_corpus_path = false;
287       else if (!strcmp(argv[i], "--no-show-locs"))
288 	opts.show_locs = false;
289       else if (!strcmp(argv[i], "--short-locs"))
290 	opts.short_locs = true;
291       else if (!strcmp(argv[i], "--no-comp-dir-path"))
292 	opts.write_comp_dir = false;
293       else if (!strcmp(argv[i], "--no-elf-needed"))
294 	opts.write_elf_needed = false;
295       else if (!strcmp(argv[i], "--no-write-default-sizes"))
296 	opts.default_sizes = false;
297       else if (!strcmp(argv[i], "--no-parameter-names"))
298 	opts.write_parameter_names = false;
299       else if (!strcmp(argv[i], "--type-id-style"))
300         {
301           ++i;
302           if (i >= argc)
303             return false;
304           if (!strcmp(argv[i], "sequence"))
305             opts.type_id_style = SEQUENCE_TYPE_ID_STYLE;
306           else if (!strcmp(argv[i], "hash"))
307             opts.type_id_style = HASH_TYPE_ID_STYLE;
308           else
309             return false;
310         }
311       else if (!strcmp(argv[i], "--check-alternate-debug-info")
312 	       || !strcmp(argv[i], "--check-alternate-debug-info-base-name"))
313 	{
314 	  if (argc <= i + 1
315 	      || argv[i + 1][0] == '-'
316 	      || !opts.in_file_path.empty())
317 	    return false;
318 	  if (!strcmp(argv[i], "--check-alternate-debug-info-base-name"))
319 	    opts.show_base_name_alt_debug_info_path = true;
320 	  opts.check_alt_debug_info_path = true;
321 	  opts.in_file_path = argv[i + 1];
322 	  ++i;
323 	}
324       else if (!strcmp(argv[i], "--load-all-types"))
325 	opts.load_all_types = true;
326       else if (!strcmp(argv[i], "--drop-private-types"))
327 	opts.drop_private_types = true;
328       else if (!strcmp(argv[i], "--drop-undefined-syms"))
329 	opts.drop_undefined_syms = true;
330       else if (!strcmp(argv[i], "--merge-translation-units"))
331 	opts.merge_translation_units = true;
332       else if (!strcmp(argv[i], "--no-linux-kernel-mode"))
333 	opts.linux_kernel_mode = false;
334       else if (!strcmp(argv[i], "--abidiff"))
335 	opts.abidiff = true;
336       else if (!strcmp(argv[i], "--annotate"))
337 	opts.annotate = true;
338       else if (!strcmp(argv[i], "--stats"))
339 	opts.show_stats = true;
340       else if (!strcmp(argv[i], "--verbose"))
341 	opts.do_log = true;
342       else if (!strcmp(argv[i], "--help")
343 	       || !strcmp(argv[i], "--h"))
344 	return false;
345       else
346 	{
347 	  if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
348 	    opts.wrong_option = argv[i];
349 	  return false;
350 	}
351     }
352 
353   return true;
354 }
355 
356 /// Initialize the context use for driving ABI comparison.
357 ///
358 /// @param ctxt the context to initialize.
359 static void
set_diff_context(diff_context_sptr & ctxt)360 set_diff_context(diff_context_sptr& ctxt)
361 {
362   ctxt->default_output_stream(&cerr);
363   ctxt->error_output_stream(&cerr);
364   // Filter out changes that are not meaningful from an ABI
365   // standpoint, from the diff output.
366   ctxt->switch_categories_off
367     (abigail::comparison::ACCESS_CHANGE_CATEGORY
368      | abigail::comparison::COMPATIBLE_TYPE_CHANGE_CATEGORY
369      | abigail::comparison::HARMLESS_DECL_NAME_CHANGE_CATEGORY);
370 }
371 
372 /// Check that the suppression specification files supplied are
373 /// present.  If not, emit an error on stderr.
374 ///
375 /// @param opts the options instance to use.
376 ///
377 /// @return true if all suppression specification files are present,
378 /// false otherwise.
379 static bool
maybe_check_suppression_files(const options & opts)380 maybe_check_suppression_files(const options& opts)
381 {
382   for (vector<string>::const_iterator i = opts.suppression_paths.begin();
383        i != opts.suppression_paths.end();
384        ++i)
385     if (!check_file(*i, cerr, "abidw"))
386       return false;
387 
388   for (vector<string>::const_iterator i =
389 	 opts.kabi_whitelist_paths.begin();
390        i != opts.kabi_whitelist_paths.end();
391        ++i)
392     if (!check_file(*i, cerr, "abidw"))
393       return false;
394 
395   return true;
396 }
397 
398 /// Check that the header files supplied are present.
399 /// If not, emit an error on stderr.
400 ///
401 /// @param opts the options instance to use.
402 ///
403 /// @return true if all header files are present, false otherwise.
404 static bool
maybe_check_header_files(const options & opts)405 maybe_check_header_files(const options& opts)
406 {
407   for (vector<string>::const_iterator file = opts.header_files.begin();
408        file != opts.header_files.end();
409        ++file)
410     if (!check_file(*file, cerr, "abidw"))
411       return false;
412 
413   return true;
414 }
415 
416 /// Set suppression specifications to the @p read_context used to load
417 /// the ABI corpus from the ELF/DWARF file.
418 ///
419 /// These suppression specifications are going to be applied to drop
420 /// some ABI artifacts on the floor (while reading the ELF/DWARF file)
421 /// and thus minimize the size of the resulting ABI corpus.
422 ///
423 /// @param read_ctxt the read context to apply the suppression
424 /// specifications to.
425 ///
426 /// @param opts the options where to get the suppression
427 /// specifications from.
428 static void
set_suppressions(read_context & read_ctxt,options & opts)429 set_suppressions(read_context& read_ctxt, options& opts)
430 {
431   suppressions_type supprs;
432   for (vector<string>::const_iterator i = opts.suppression_paths.begin();
433        i != opts.suppression_paths.end();
434        ++i)
435     read_suppressions(*i, supprs);
436 
437   suppression_sptr suppr =
438     abigail::tools_utils::gen_suppr_spec_from_headers(opts.headers_dirs,
439 						      opts.header_files);
440   if (suppr)
441     {
442       if (opts.drop_private_types)
443 	suppr->set_drops_artifact_from_ir(true);
444       supprs.push_back(suppr);
445     }
446 
447   using abigail::tools_utils::gen_suppr_spec_from_kernel_abi_whitelists;
448   const suppressions_type& wl_suppr =
449       gen_suppr_spec_from_kernel_abi_whitelists(opts.kabi_whitelist_paths);
450 
451   opts.kabi_whitelist_supprs.insert(opts.kabi_whitelist_supprs.end(),
452 				    wl_suppr.begin(), wl_suppr.end());
453 
454   add_read_context_suppressions(read_ctxt, supprs);
455   add_read_context_suppressions(read_ctxt, opts.kabi_whitelist_supprs);
456 }
457 
458 /// Load an ABI @ref corpus (the internal representation of the ABI of
459 /// a binary) and write it out as an abixml.
460 ///
461 /// @param argv the arguments the program was called with.
462 ///
463 /// @param env the environment the ABI artifacts are being created in.
464 ///
465 /// @param context the context of the ELF reading.
466 ///
467 /// @param opts the options of the program.
468 ///
469 /// @return the exit code: 0 if everything went fine, non-zero
470 /// otherwise.
471 static int
load_corpus_and_write_abixml(char * argv[],environment_sptr & env,read_context_sptr & context,const options & opts)472 load_corpus_and_write_abixml(char* argv[],
473 			     environment_sptr& env,
474 			     read_context_sptr& context,
475 			     const options& opts)
476 {
477   int exit_code = 0;
478   timer t;
479 
480   read_context& ctxt = *context;
481   corpus_sptr corp;
482   dwarf_reader::status s = dwarf_reader::STATUS_UNKNOWN;
483   t.start();
484   corp = read_corpus_from_elf(ctxt, s);
485   t.stop();
486   if (opts.do_log)
487     emit_prefix(argv[0], cerr)
488       << "read corpus from elf file in: " << t << "\n";
489 
490   t.start();
491   context.reset();
492   t.stop();
493 
494   if (opts.do_log)
495     emit_prefix(argv[0], cerr)
496       << "reset read context in: " << t << "\n";
497 
498   if (!corp)
499     {
500       if (s == dwarf_reader::STATUS_DEBUG_INFO_NOT_FOUND)
501 	{
502 	  if (opts.di_root_paths.empty())
503 	    {
504 	      emit_prefix(argv[0], cerr)
505 		<< "Could not read debug info from "
506 		<< opts.in_file_path << "\n";
507 
508 	      emit_prefix(argv[0], cerr)
509 		<< "You might want to supply the root directory where "
510 		"to search debug info from, using the "
511 		"--debug-info-dir option "
512 		"(e.g --debug-info-dir /usr/lib/debug)\n";
513 	    }
514 	  else
515 	    {
516 	      emit_prefix(argv[0], cerr)
517 		<< "Could not read debug info for '" << opts.in_file_path
518 		<< "' from debug info root directory '";
519 	      for (vector<char*>::const_iterator i =
520 		     opts.di_root_paths.begin();
521 		   i != opts.di_root_paths.end();
522 		   ++i)
523 		{
524 		  if (i != opts.di_root_paths.begin())
525 		    cerr << ", ";
526 		  cerr << *i;
527 		}
528 	    }
529 	}
530       else if (s == dwarf_reader::STATUS_NO_SYMBOLS_FOUND)
531 	emit_prefix(argv[0], cerr)
532 	  << "Could not read ELF symbol information from "
533 	  << opts.in_file_path << "\n";
534 
535       return 1;
536     }
537   else
538     {
539       t.start();
540       const write_context_sptr& write_ctxt
541 	  = create_write_context(corp->get_environment(), cout);
542       set_common_options(*write_ctxt, opts);
543       t.stop();
544 
545       if (opts.do_log)
546 	emit_prefix(argv[0], cerr)
547 	  << "created & initialized write context in: "
548 	  << t << "\n";
549 
550       if (opts.abidiff)
551 	{
552 	  // Save the abi in abixml format in a temporary file, read
553 	  // it back, and compare the ABI of what we've read back
554 	  // against the ABI of the input ELF file.
555 	  temp_file_sptr tmp_file = temp_file::create();
556 	  set_ostream(*write_ctxt, tmp_file->get_stream());
557 	  write_corpus(*write_ctxt, corp, 0);
558 	  tmp_file->get_stream().flush();
559 	  t.start();
560 	  corpus_sptr corp2 =
561 	    read_corpus_from_native_xml_file(tmp_file->get_path(),
562 					     env.get());
563 	  t.stop();
564 	  if (opts.do_log)
565 	    emit_prefix(argv[0], cerr)
566 	      << "Read corpus in: " << t << "\n";
567 
568 	  if (!corp2)
569 	    {
570 	      emit_prefix(argv[0], cerr)
571 		<< "Could not read temporary XML representation of "
572 		"elf file back\n";
573 	      return 1;
574 	    }
575 
576 	  diff_context_sptr ctxt(new diff_context);
577 	  set_diff_context(ctxt);
578 	  ctxt->show_locs(opts.show_locs);
579 	  t.start();
580 	  corpus_diff_sptr diff = compute_diff(corp, corp2, ctxt);
581 	  t.stop();
582 	  if (opts.do_log)
583 	    emit_prefix(argv[0], cerr)
584 	      << "computed diff in: " << t << "\n";
585 
586 	  bool has_error = diff->has_changes();
587 	  if (has_error)
588 	    {
589 	      t.start();
590 	      diff->report(cerr);
591 	      t.stop();
592 	      if (opts.do_log)
593 		emit_prefix(argv[0], cerr)
594 		  << "emitted report in: " << t << "\n";
595 	      return 1;
596 	    }
597 	  return 0;
598 	}
599 
600       if (opts.noout)
601 	return 0;
602 
603       if (!opts.out_file_path.empty())
604 	{
605 	  ofstream of(opts.out_file_path.c_str(), std::ios_base::trunc);
606 	  if (!of.is_open())
607 	    {
608 	      emit_prefix(argv[0], cerr)
609 		<< "could not open output file '"
610 		<< opts.out_file_path << "'\n";
611 	      return 1;
612 	    }
613 	  set_ostream(*write_ctxt, of);
614 	  t.start();
615 	  write_corpus(*write_ctxt, corp, 0);
616 	  t.stop();
617 	  if (opts.do_log)
618 	    emit_prefix(argv[0], cerr)
619 	      << "emitted abixml output in: " << t << "\n";
620 	  of.close();
621 	  return 0;
622 	}
623       else
624 	{
625 	  t.start();
626 	  exit_code = !write_corpus(*write_ctxt, corp, 0);
627 	  t.stop();
628 	  if (opts.do_log)
629 	    emit_prefix(argv[0], cerr)
630 	      << "emitted abixml out in: " << t << "\n";
631 	}
632     }
633 
634   return exit_code;
635 }
636 
637 /// Load a corpus group representing the union of a Linux Kernel
638 /// vmlinux binary and its modules, and emit an abixml representation
639 /// for it.
640 ///
641 /// @param argv the arguments this program was called with.
642 ///
643 /// @param env the environment the ABI artifacts are created in.
644 ///
645 /// @param opts the options this program was created with.
646 ///
647 /// @return the exit code.  Zero if everything went well, non-zero
648 /// otherwise.
649 static int
load_kernel_corpus_group_and_write_abixml(char * argv[],environment_sptr & env,options & opts)650 load_kernel_corpus_group_and_write_abixml(char* argv[],
651 					  environment_sptr& env,
652 					  options& opts)
653 {
654   if (!(tools_utils::is_dir(opts.in_file_path) && opts.corpus_group_for_linux))
655     return 1;
656 
657   int exit_code = 0;
658 
659   if (!opts.vmlinux.empty())
660     if (!abigail::tools_utils::check_file(opts.vmlinux, cerr, argv[0]))
661       return 1;
662 
663   timer t, global_timer;
664   suppressions_type supprs;
665 
666   if (opts.do_log)
667     emit_prefix(argv[0], cerr)
668       << "going to build ABI representation of the Linux Kernel ...\n";
669 
670   global_timer.start();
671   t.start();
672   corpus_group_sptr group =
673     build_corpus_group_from_kernel_dist_under(opts.in_file_path,
674 					      /*debug_info_root=*/"",
675 					      opts.vmlinux,
676 					      opts.suppression_paths,
677 					      opts.kabi_whitelist_paths,
678 					      supprs, opts.do_log, env);
679   t.stop();
680 
681   if (opts.do_log)
682     {
683       emit_prefix(argv[0], cerr)
684 	<< "built ABI representation of the Linux Kernel in: "
685 	<< t << "\n";
686     }
687 
688   if (!group)
689     return 1;
690 
691   if (!opts.noout)
692     {
693       const xml_writer::write_context_sptr& ctxt
694 	  = xml_writer::create_write_context(group->get_environment(), cout);
695       set_common_options(*ctxt, opts);
696 
697       if (!opts.out_file_path.empty())
698 	{
699 	  ofstream of(opts.out_file_path.c_str(), std::ios_base::trunc);
700 	  if (!of.is_open())
701 	    {
702 	      emit_prefix(argv[0], cerr)
703 		<< "could not open output file '"
704 		<< opts.out_file_path << "'\n";
705 	      return 1;
706 	    }
707 
708 	  if (opts.do_log)
709 	    emit_prefix(argv[0], cerr)
710 	      << "emitting the abixml output ...\n";
711 	  set_ostream(*ctxt, of);
712 	  t.start();
713 	  exit_code = !write_corpus_group(*ctxt, group, 0);
714 	  t.stop();
715 	  if (opts.do_log)
716 	    emit_prefix(argv[0], cerr)
717 	      << "emitted abixml output in: " << t << "\n";
718 	}
719       else
720 	{
721 	  if (opts.do_log)
722 	    emit_prefix(argv[0], cerr)
723 	      << "emitting the abixml output ...\n";
724 	  t.start();
725 	  exit_code = !write_corpus_group(*ctxt, group, 0);
726 	  t.stop();
727 	  if (opts.do_log)
728 	    emit_prefix(argv[0], cerr)
729 	      << "emitted abixml output in: " << t << "\n";
730 	}
731     }
732 
733   global_timer.stop();
734   if (opts.do_log)
735     emit_prefix(argv[0], cerr)
736       << "total processing done in " << global_timer << "\n";
737   return exit_code;
738 }
739 
740 /// Convert options::di_root_paths into
741 /// options::prepared_di_root_paths which is the suitable type format
742 /// that the dwarf_reader expects.
743 ///
744 /// @param o the options to consider.
745 static void
prepare_di_root_paths(options & o)746 prepare_di_root_paths(options& o)
747 {
748   tools_utils::convert_char_stars_to_char_star_stars(o.di_root_paths,
749 						     o.prepared_di_root_paths);
750 }
751 
752 int
main(int argc,char * argv[])753 main(int argc, char* argv[])
754 {
755   options opts;
756 
757   if (!parse_command_line(argc, argv, opts)
758       || (opts.in_file_path.empty()
759 	  && !opts.display_version))
760     {
761       if (!opts.wrong_option.empty())
762 	emit_prefix(argv[0], cerr)
763 	  << "unrecognized option: " << opts.wrong_option << "\n";
764       display_usage(argv[0], cerr);
765       return 1;
766     }
767 
768   if (opts.display_version)
769     {
770       emit_prefix(argv[0], cout)
771 	<< abigail::tools_utils::get_library_version_string()
772 	<< "\n";
773       return 0;
774     }
775 
776   ABG_ASSERT(!opts.in_file_path.empty());
777   if (opts.corpus_group_for_linux)
778     {
779       if (!abigail::tools_utils::check_dir(opts.in_file_path, cerr, argv[0]))
780 	return 1;
781     }
782   else
783     {
784       if (!abigail::tools_utils::check_file(opts.in_file_path, cerr, argv[0]))
785 	return 1;
786     }
787 
788   prepare_di_root_paths(opts);
789 
790   if (!maybe_check_suppression_files(opts))
791     return 1;
792 
793   if (!maybe_check_header_files(opts))
794     return 1;
795 
796   abigail::tools_utils::file_type type =
797     abigail::tools_utils::guess_file_type(opts.in_file_path);
798   if (type != abigail::tools_utils::FILE_TYPE_ELF
799       && type != abigail::tools_utils::FILE_TYPE_AR
800       && type != abigail::tools_utils::FILE_TYPE_DIR)
801     {
802       emit_prefix(argv[0], cerr)
803 	<< "files of the kind of "<< opts.in_file_path << " are not handled\n";
804       return 1;
805     }
806 
807   environment_sptr env(new environment);
808   int exit_code = 0;
809 
810   if (tools_utils::is_regular_file(opts.in_file_path))
811     {
812       read_context_sptr c = create_read_context(opts.in_file_path,
813 						opts.prepared_di_root_paths,
814 						env.get(),
815 						opts.load_all_types,
816 						opts.linux_kernel_mode);
817       read_context& ctxt = *c;
818       set_drop_undefined_syms(ctxt, opts.drop_undefined_syms);
819       set_merge_translation_units(ctxt, opts.merge_translation_units);
820       set_show_stats(ctxt, opts.show_stats);
821       set_suppressions(ctxt, opts);
822       abigail::dwarf_reader::set_do_log(ctxt, opts.do_log);
823 
824       if (opts.check_alt_debug_info_path)
825 	{
826 	  bool has_alt_di = false;
827 	  string alt_di_path;
828 	  abigail::dwarf_reader::status status =
829 	    abigail::dwarf_reader::has_alt_debug_info(ctxt,
830 						      has_alt_di,
831 						      alt_di_path);
832 	  if (status & abigail::dwarf_reader::STATUS_OK)
833 	    {
834 	      if (alt_di_path.empty())
835 		;
836 	      else
837 		{
838 		  cout << "found the alternate debug info file";
839 		  if (opts.show_base_name_alt_debug_info_path)
840 		    {
841 		      tools_utils::base_name(alt_di_path, alt_di_path);
842 		      cout << " '" << alt_di_path << "'";
843 		    }
844 		  cout << "\n";
845 		}
846 	      return 0;
847 	    }
848 	  else
849 	    {
850 	      emit_prefix(argv[0], cerr)
851 		<< "could not find alternate debug info file\n";
852 	      return 1;
853 	    }
854 	}
855       exit_code = load_corpus_and_write_abixml(argv, env, c, opts);
856     }
857   else
858     exit_code = load_kernel_corpus_group_and_write_abixml(argv, env, opts);
859 
860   return exit_code;
861 }
862