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