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