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