Squashed 'third_party/elfutils/' content from commit 555e15e
Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/src/ChangeLog b/src/ChangeLog
new file mode 100644
index 0000000..a490705
--- /dev/null
+++ b/src/ChangeLog
@@ -0,0 +1,4058 @@
+2018-02-09 Joshua Watt <JPEWhacker@gmail.com>
+
+ * addr2line.c (handle_address): Use FALLTHROUGH macro instead of
+ comment.
+ * elfcompress.c (parse_opt): Likewise.
+ * elflint.c (check_dynamic): Likewise.
+ (check_sections): Likewise.
+ (check_note_data): Likewise.
+ * objdump.c (parse_opt): Likewise.
+ * readelf.c (parse_opt): Likewise.
+ (attr_callback): Likewise.
+ (handle_auxv_note): Likewise.
+ * strings.c (parse_opt): Likewise.
+ * backtrace.c (callback_verify): Likewise.
+
+2018-01-25 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (print_debug_ranges_section): Initialize cu to last_cu.
+ (print_debug_loc_section): Likewise.
+
+2018-01-01 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (attr_callback): Use dwarf_form_name for unknown forms.
+ (print_debug_macro_section): Print form using dwarf_form_name.
+
+2017-12-28 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (print_debug_units): Print DIE offset in error message
+ as hex.
+
+2017-12-18 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (handle_notes_data): Don't use EXIT_FAILURE in error.
+ Adjust error message depending on whether or not we got data.
+
+2017-12-07 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (print_ops): Update data pointer and print arguments
+ to DW_OP_call2 and DW_OP_call4 as DIE offsets.
+
+2017-11-29 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (argp_options): Add "section-groups", 'g'.
+
+2017-11-29 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (print_debug_loc_section): Print CU base and unresolved
+ addresses. Adjust formatting.
+
+2017-11-29 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (print_debug_ranges_section): Print CU base and unresolved
+ addresses. Adjust formatting.
+
+2017-11-29 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (attr_callback): Set valuestr to resolved file name
+ for DW_AT_decl_file and DW_AT_call_file.
+
+2017-11-29 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (print_debug_units): Print abbrev code after DIE tag.
+
+2017-11-29 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (print_ops): Use only2 space for index. re-indent +5
+ for DW_OP_GNU_entry_value.
+
+2017-11-21 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (attr_callback): Print attribute name and form in error
+ message. If only the value cannot be retrieved/resolved don't abort.
+
+2017-10-03 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (attr_callback): Print DIE offset in error messages.
+
+2017-11-03 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (print_ops): Handle DW_OP_GNU_variable_value. Print
+ referenced DIE as offset.
+
+2017-09-10 Mark Wielaard <mark@klomp.org>
+
+ * ar.c (do_oper_delete): Remove DEBUG conditional check.
+ (no0print): Return bool. Check snprintf return value.
+ (do_oper_insert): Initialize elf. Remove DEBUG conditional check.
+ Check no0print calls succeed. Explicitly elf_end found elfs.
+ (do_oper_extract): Make sure we don't create an empty variable
+ length array.
+
+2017-09-01 Mark Wielaard <mark@klomp.org>
+
+ * stack.c (main): Replace \v in doc string with \n\n.
+ * unstrip.c (main): Likewise.
+
+2017-05-04 Ulf Hermann <ulf.hermann@qt.io>
+
+ * stack.c: Print pid_t using %lld.
+
+2017-08-18 Ulf Hermann <ulf.hermann@qt.io>
+
+ * readelf.c: Hardcode the signal numbers for non-linux systems.
+
+2017-07-26 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (print_debug_macro_section): Accept either version 4 or
+ version 5. Use DW_MACRO names instead of DW_MACRO_GNU names. Add
+ minimal support for DW_MACRO_define_sup, DW_MACRO_undef_sup,
+ DW_MACRO_import_sup, DW_MACRO_define_strx and DW_MACRO_undef_strx.
+
+2017-07-26 Mark Wielaard <mark@klomp.org>
+
+ * readelf.c (dwarf_defaulted_string): New function.
+ (dwarf_defaulted_name): Likewise.
+ (attr_callback): Use dwarf_defaulted_name to get value of
+ DW_AT_defaulted.
+
+2017-07-20 Mark Wielaard <mark@klomp.org>
+
+ * strip.c (handle_elf): Deal with data marker symbols pointing to
+ debug sections (they can be removed).
+
+2017-07-14 Mark Wielaard <mark@klomp.org>
+
+ * strip (OPT_KEEP_SECTION): New define.
+ (options): Add documentation for remove-section. Add keep-section.
+ (struct section_pattern): New data type.
+ (keep_secs, remove_secs): New globals.
+ (add_pattern, free_sec_patterns, free_patterns, section_name_matches):
+ New functions.
+ (main): Call free_patterns.
+ (parse_opt): Handle 'R' and OPT_KEEP_SECTION. Check remove_comment
+ on ARGP_KEY_SUCCESS.
+ (handle_elf): Handle and sanity check keep_secs and remove_secs.
+
+2017-06-07 Mark Wielaard <mark@klomp.org>
+
+ * strip.c (handle_elf): Introduce new handle_elf boolean. Use it to
+ determine whether to create an output and/or debug file. Remove new
+ output file on error.
+
+2017-06-06 Mark Wielaard <mark@klomp.org>
+
+ * strip.c (handle_elf): Assume e_shstrndx section can be removed.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
+ * readelf.c: Include strings.h.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
+ * unstrip.c: Check shnum for 0 before subtracting from it.
+
+2017-04-20 Ulf Hermann <ulf.hermann@qt.io>
+
+ * readelf.c: Replace YESSTR and NOSTR with gettext ("yes") and
+ gettext ("no"), respectively.
+
+2017-04-05 Mark Wielaard <mark@klomp.org>
+
+ * elflint.c (check_elf_header): Decompress all sections.
+
+2017-03-28 Mark Wielaard <mark@klomp.org>
+
+ * elflint (check_group): Don't check if there is no flag word.
+
+2017-03-27 Mark Wielaard <mark@klomp.org>
+
+ * elflint.c (check_elf_header): Sanity check phnum and shnum.
+
+2017-03-27 Mark Wielaard <mark@klomp.org>
+
+ * elflint.c (check_sysv_hash): Return early if section size is
+ too small.
+ (check_sysv_hash64): Likewise.
+ (check_hash): Calculate expect_entsize to check section size.
+
+2017-03-27 Mark Wielaard <mark@klomp.org>
+
+ * elflint.c (check_symtab_shndx): Check data->d_size.
+
+2017-03-24 Mark Wielaard <mjw@redhat.com>
+
+ * elfcmp.c (main): If n_namesz == 0 then the note name data is the
+ empty string.
+ * readelf.c (handle_notes_data): Likewise.
+
+2017-03-24 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_gnu_hash): Check inner < max_nsyms before
+ indexing into chain array.
+
+2017-02-16 Ulf Hermann <ulf.hermann@qt.io>
+
+ * addr2line.c: Include printversion.h
+ * ar.c: Likewise.
+ * elflint.c: Likewise.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * stack.c: Likewise.
+ * strings.c: Likewise.
+ * strip.c: Likewise.
+ * elfcmp.c: Include printversion.h, remove system.h include.
+ * elfcompress.c: Likewise.
+ * findtextrel.c: Likewise.
+ * unstrip.c: Likewise.
+
+2017-02-14 Ulf Hermann <ulf.hermann@qt.io>
+
+ * nm.c: Include color.h.
+ * objdump.c: Likewise.
+
+2016-12-24 Mark Wielaard <mark@klomp.org>
+
+ * Makefile.am (findtextrel_LDADD): Add $(libeu).
+ (addr2line_LDADD): Likewise.
+ (elfcmp_LDADD): Likewise.
+ * addr2line.c (print_version): Removed.
+ * ar.c (print_version): Likewise.
+ * elfcmp.c (print_version): Likewise.
+ * elfcompress.c (print_version): Likewise.
+ * elflint.c (print_version): Likewise.
+ * findtextrel.c (print_version): Likewise.
+ * nm.c (print_version): Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * stack.c: Likewise.
+ * strings.c: Likewise.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise.
+
+2016-11-17 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (options): Add optional arg SECTION for symbols.
+ (symbol_table_section): New static variable.
+ (parse_opt): Set symbol_table_section on 's'.
+ (print_symtab): Filter on symbol_table_section name is set.
+
+2016-11-10 Mark Wielaard <mjw@redhat.com>
+
+ * ar.c (write_member): Make sure tmpbuf is large enough to contain
+ a starting '/' and ending '\0' char.
+ (do_oper_insert): Likewise.
+ * arlib.c (arlib_finalize): Format tmpbuf as PRId32 decimal.
+
+2016-11-02 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (handle_address): Add fallthrough comment.
+ * elfcompress.c (parse_opt): Adjust fallthrough comment.
+ * elflint.c (parse_opt): Add missing break after 'd' case.
+ (check_sections): Add fallthrough comments.
+ * objdump.c (parse_opt): Add explantion for fallthrough comment.
+
+2016-10-22 Kevin Cernekee <cernekee@chromium.org>
+
+ * unstrip.c: Fix "invalid string offset" error caused by using the
+ unstripped .symtab with the stripped .strtab.
+
+2016-10-11 Akihiko Odaki <akihiko.odaki.4i@stu.hosei.ac.jp>
+
+ * arlib.c: Remove system.h include, add libeu.h include.
+ * arlib2.c: Remove sys/param.h include.
+ * elfcompress.c: Add libeu.h include.
+ * elflint.c: Remove sys/param.h include, add libeu.h include.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Remove sys/param.h include.
+ * strings.c: Likewise, add libeu.h include.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise.
+
+2016-10-06 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (handle_elf): Don't remove real symbols from allocated
+ symbol tables.
+
+2016-08-25 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (handle_elf): Recompress with ELF_CHF_FORCE.
+
+2016-08-06 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (handle_elf): Uncompress and recompress relocation target
+ section if necessary.
+
+2016-07-08 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (strip_LDADD): Add libdw.
+ * elfcompress.c (process_file): Use dwelf_strtab functions instead of
+ ebl_strtab.
+ * strip.c (handle_elf): Likewise.
+ * unstrip.c (new_shstrtab): Likewise.
+
+2016-07-06 Mark Wielaard <mjw@redhat.com>
+
+ * elf32-i386.script, i386_ld.c, ld.c, ld.h, ldgeneric.c, ldlex.l,
+ ldscript.y, libld_elf_i386.map, none_ld.c, sectionhash.c,
+ sectionhash.h, symbolhash.c, symbolhash.h, unaligned.h,
+ versionhash.c, versionhash.h, xelf.h: Removed.
+ * Makefile.am (YACC): Removed.
+ (AM_YFLAGS): Removed.
+ (AM_LFLAGS): Removed.
+ (native_ld): Removed.
+ (base_cpu): Removed.
+ (bin_PROGRAMS): Removed ld.
+ (ld_dsos): Removed.
+ (ld_SOURCES): Removed.
+ (noinst_LIBRARIES): Only libar.a.
+ (EXTRA_DIST): Just arlib.h and debugpred.h.
+ (ld_LDADD): Removed.
+ (ld_LDFLAGS): Removed.
+ (ldlex.o): Removed.
+ (ldscript.h): Removed.
+ (libld*): Removed.
+ (CLEANFILES): Just *.gconv.
+ (MAINTAINERCLEANFILES): Removed.
+
+2016-07-06 Mark Wielaard <mjw@redhat.com>
+
+ * unstrip.c (copy_elided_sections): Use unstripped_strent[] from
+ index zero, instead of one.
+
+2016-06-28 Richard Henderson <rth@redhat.com>
+
+ * elflint.c (valid_e_machine): Add EM_BPF.
+
+2016-04-11 David Abdurachmanov <davidlt@cern.ch>
+
+ * elfcmp.c (main): Fix self-comparison error with GCC 6.
+
+2016-03-21 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (show_symbols): Check for malloc size argument overflow.
+
+2016-02-13 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_scngrp): Call error when gelf_getshdr fails.
+ (print_symtab): Likewise.
+ (handle_hash): Likewise.
+ (dump_data_section): Print a warning if decompressing fails.
+ (print_string_section): Likewise.
+
+2016-02-13 Mark Wielaard <mjw@redhat.com>
+
+ * elfcompress.c (parse_opt): Don't fallthrough after processing -q.
+
+2016-02-12 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (handle_elf): Correct elf_assert shndxdata check.
+
+2016-02-09 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (read_encoded): Move up.
+ (print_cfa_program): Add encoding argument. Use it for read_encoded
+ when reading DW_CFA_set_loc op.
+ (print_debug_frame_section): Pass fde_encoding to print_cfa_program.
+
+2016-02-09 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (compare_hash_gnu_hash): Check hash sh_entsize against
+ sizeof (Elf64_Xword). Correct invalid sh_entsize error message
+ section idx and name.
+
+2016-01-13 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_elf_header): Recognize ELFOSABI_FREEBSD.
+
+2016-01-08 Mark Wielaard <mjw@redhat.com>
+
+ * elfcompress.c (compress_section): Use %zu to print size_t.
+ * readelf.c (print_shdr): Use %zx to print size_t.
+
+2015-12-16 Mark Wielaard <mjw@redhat.com>
+
+ * elfcompress.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add elfcompress.
+ (elfcompress_LDADD): New variable.
+
+2015-12-18 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (section_flags_string): Add NEWFLAG COMPRESSED.
+ (check_sections): SHF_COMPRESSED can be on any special section.
+ SHF_COMPRESSED is a valid section flag. SHF_COMPRESSED cannot
+ be used together with SHF_ALLOC or with SHT_NOBITS. Should have
+ a valid Chdr.
+
+2015-10-20 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (options): Expand -z help text.
+ (dump_data_section): Check whether we need and can decompress section
+ data and call elf_rawzdata if so,
+ (print_string_section): Likewise.
+ (elf_contains_chdrs): New function.
+ (process_elf_file): Rename print_unrelocated to print_unchanged,
+ use elf_contains_chdrs.
+ (print_scngrp): Check whether section is compressed before use.
+ (print_symtab): Likewise.
+ (handle_hash): Likewise.
+
+2015-10-16 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (argp_option): Describe --decompress,-z.
+ (print_decompress): New bool.
+ (parse_opt): Handle -z.
+ (elf_ch_type_name): New function.
+ (print_shdr): Print section compress information.
+
+2015-12-31 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_symtab): Add _edata and _end (plus extra underscore
+ variants) to to the list of possibly dangling symbols.
+
+2015-12-02 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (process_file): Accept fd and pass it on.
+ (handle_elf): Likewise.
+ (find_no_debuginfo): New.
+ (struct getdbg): Likewise.
+ (getdbg_dwflmod): Likewise.
+ (show_symbols): Take fd. If the file is ET_REL use libdwfl to get
+ the relocated Dwarf.
+
+2015-12-02 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (get_local_names): Check for duplicates in local_root tree.
+
+2015-12-02 Mark Wielaard <mjw@redhat.com>
+
+ * unstrip.c (struct data_list): New.
+ (new_data_list): Likewise.
+ (record_new_data): Likewise.
+ (free_new_data): Likewise.
+ (adjust_relocs): Call record_new_data.
+ (add_new_section_symbols): Likewise.
+ (copy_elided_sections): Call free_new_data.
+
+2015-12-01 Mark Wielaard <mjw@redhat.com>
+
+ * elfcmp.c (main): Close ebl1 and ebl2 backends.
+
+2015-10-16 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am [BUILD_STATIC](libdw): Add -lz.
+ [BUILD_STATIC](libelf): Likewise.
+
+2015-10-16 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_symtab): Don't check TLS symbol value against TLS
+ phdr offset in debuginfo files.
+ (check_sections): Don't try to match section offsets to phdrs offsets
+ in debuginfo files.
+
+2015-10-16 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_reloc_shdr): Reject only desthdrs if they have both
+ SHF_MERGE and SHF_STRINGS set.
+
+2015-10-13 Jose E. Marchesi <jose.marchesi@oracle.com>
+
+ * elflint.c (check_sections): Do not rely on
+ ebl_check_special_section when checking debuginfo files. Also
+ check that the type of WE sections in debuginfo files is NOBITS.
+
+2015-10-13 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_program_header): Check relro flags are a subset
+ of the load segment if they don't fully overlap.
+
+2015-10-07 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (ldlex_no_Wstack_usage): New.
+ * ldlex.l ([RWX]): Make cnt unsigned.
+
+2015-10-09 Josh Stone <jistone@redhat.com>
+
+ * elflint.c (main): Replace stat64 and fstat64 with stat and fstat.
+ * readelf.c (process_file): Likewise.
+ (process_elf_file): Replace off64_t with off_t.
+ * findtextrel.c (process_file): Replace open64 with open.
+ * ld.c (main): Replace sizeof (off64_t) with 8.
+ * strings.c: Replace off64_t with off_t throughout.
+ (main): Replace stat64 and fstat64 with stat and fstat.
+ (map_file): Replace mmap64 with mmap.
+ (read_block): Likewise, and replace lseek64 with lseek.
+ * strip.c (handle_elf): Replace ftruncate64 with ftruncate.
+ (process_file): Replace stat64 and fstat64 with stat and fstat.
+ * unstrip.c (parse_opt): Replace stat64 with stat.
+ (handle_file): Replace open64 with open.
+ (open_file): Likewise.
+
+2015-10-08 Chih-Hung Hsieh <chh@google.com>
+
+ * ld.c (determine_output_format): Move recursive nested
+ function "try" to file scope.
+
+2015-10-04 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (handle_elf): Only sanity check section symbols to not
+ kept discarded sections if we are creating a debug file.
+
+2015-10-07 Mark Wielaard <mjw@redhat.com>
+
+ * unstrip.c (MAX): Removed.
+ (find_alloc_sections_prelink): Allocate exact amount of bytes for
+ shdrs.
+
+2015-10-05 Chih-Hung Hsieh <chh@google.com>
+
+ * unstrip.c (find_alloc_sections_prelink): Do not allocate
+ on stack union types with variable length arrays; use xmalloc
+ for such dynamic sized objects.
+ * readelf.c (handle_core_item): Likewise, but use alloca
+ for small variable size data object and add assert(count < 128).
+
+2015-10-05 Josh Stone <jistone@redhat.com>
+
+ * Makefile.am (libld_elf_i386.so): Add AM_V_CCLD silencer.
+ (.deps/none_ld.Po): Always silence the dummy command.
+ (make-debug-archive): Add AM_V_GEN and AM_V_at silencers.
+
+2015-10-02 Mark Wielaard <mjw@redhat.com>
+
+ * unstrip.c (copy_elided_sections): Use SH_INFO_LINK_P, not just
+ SHF_INFO_LINK.
+
+2015-10-02 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (handle_elf): Don't move around allocated NOBITS sections.
+ Don't just mark the section header string table as unused.
+ * unstrip.c (copy_elided_sections): Add header names to strtab if
+ shstrndx equals the symtab strtabndx.
+
+2015-09-22 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (cleanup_debug): Remove old-style function definitions.
+
+2015-09-09 Chih-Hung Hsieh <chh@google.com>
+
+ * readelf.c (print_debug_exception_table): Initialize variable before
+ it is used, because compiler does not know that error never returns.
+ (dump_arhive_index): Likewise.
+
+2015-09-04 Chih-Hung Hsieh <chh@google.com>
+
+ * elflint.c (check_group): Replace %Z length modifier with %z.
+ (check_note_data): Likewise.
+ * findtextrel.c (process_file): Likewise.
+ * readelf.c (handle_dynamic): Likewise.
+ (handle_symtab): Likewise.
+ (handle_verneed): Likewise.
+ (handle_verdef): Likewise.
+ (handle_versym): Likewise.
+ (print_hash_info): Likewise.
+ (print_decoded_aranges_section): Likewise.
+ (print_debug_aranges_section): Likewise.
+ (print_debug_line_section): Likewise.
+ (hex_dump): Likewise.
+ (dump_data_section): Likewise.
+ (print_string_section): Likewise.
+ (dump_archive_index): Likewise.
+ * unstrip.c (adjust_relocs): Likewise.
+ (collect_symbols): likewise.
+ (get_section_name): Likewise.
+ (find_alloc_sections_prelink): Likewise.
+ (copy_elided_sections): Likewise.
+ * ldscript.y (add_id_list): Add missing '%s'.
+
+2015-09-03 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_core_item): Handle right shift >= 32 bits.
+
+2015-08-11 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_sections): When gnuld and a NOBITS section falls
+ inside a segment make sure any ELF file contents is zero.
+
+2015-07-29 Mark Wielaard <mjw@redhat.com>
+
+ * unstrip.c (sections_flags_match): New function.
+ (sections_match): Use sections_flags_match.
+ (find_alloc_sections_prelink): Only clear matched sections if there
+ is an undo section.
+ (copy_elided_sections): Add SHF_INFO_LINK to shdr_mem.sh_flags if
+ necessary.
+
+2015-06-27 Pino Toscano <toscano.pino@tiscali.it>
+
+ * src/strings.c: Define MAP_POPULATE if not defined already.
+
+2015-06-27 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (show_symbols): First call elf_getdata, then allocate memory.
+
+2015-06-18 Mark Wielaard <mjw@redhat.com>
+
+ * findtextrel.c (process_file): Free segments after use.
+
+2015-06-18 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_phdr): Make sure phdr2_mem lifetime/scope equals
+ phdr2 pointer.
+
+2015-06-18 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_gnu_hash): Free lengths on invalid_data.
+
+2015-06-18 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_symtab): Only check the PT_TLS phdr if it actually
+ exists. Warn otherwise.
+
+2015-06-18 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (show_symbols): Check sizeof (sym_mem[0]), not GElf_Sym to
+ known whether or not we stack allocated memory.
+
+2015-06-18 Mark Wielaard <mjw@redhat.com>
+
+ * strings.c (readelf): Use "<unknown>" if we cannot retrieve section
+ name.
+
+2015-06-09 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (print_dwarf_function): Always free scopes before
+ returning.
+
+2015-06-09 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (handle_ar): Mark as unused.
+ (process_file): Produce an error when trying to handle an ar.
+
+2015-05-30 Mark Wielaard <mjw@redhat.com>
+
+ * elfcmp.c (main): Only call memcmp when d_size != 0.
+
+2015-05-23 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am: Define ldgeneric, readelf, nm, size, strip, elflint,
+ findtextrel, elfcmp objdump, ranlib, ar and unstrip no_Wstack_usage.
+
+2015-05-21 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (handle_address): Set scopes to NULL after free.
+
+2015-05-20 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (OPT_PRETTY): New constant define.
+ (argp_option): Add "pretty-print".
+ (pretty): New static bool.
+ (parse_opt): Set pretty.
+ (print_dwarf_function): Adjust output when pretty is set.
+ (print_addrsym): Likewise.
+ (handle_address): Likewise.
+
+2015-05-20 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (addr2line_LDADD): Add demanglelib.
+ * addr2line.c (argp_option): Move demangle under output format.
+ (demangle): New static bool.
+ (demangle_buffer_len): New static size_t.
+ (demangle_buffer): New static char *.
+ (main): free demangle_buffer.
+ (parse_opt): Set demangle.
+ (symname): New static function.
+ (get_diename): Use symname.
+ (print_dwarf_function): Likewise.
+ (print_addrsym): Likewise.
+ (handle_address): Likewise.
+
+2015-05-20 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (argp_option): Add "addresses", 'a'.
+ (print_addresses): New static bool.
+ (parse_opt): Set print_addresses.
+ (get_addr_width): New static function.
+ (handle_address): Print address if print_addresses is true.
+
+2015-05-20 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (handle_address): Call strtoumax with base 16. Make
+ sure all input has been processed.
+
+2015-05-20 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line (argp_option): Group 'section' under "Input format
+ options".
+
+2015-05-12 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (debug_fd): New static variable.
+ (tmp_debug_fname): Likewise.
+ (cleanup_debug): New function using the above to clean up temporary
+ debug file.
+ (INTERNAL_ERROR): Call cleanup_debug.
+ (handle_elf): Use debug_fd and tmp_debug_fname statics and call
+ cleanup_debug before any error. Use output_fname instead of fname in
+ error messages when it exists (-o was given). SHT_NOBITS sections
+ can also be moved freely even if SHF_ALLOC is set. Add various
+ sanity checks. Use elf_assert instead of plain assert.
+
+2015-05-08 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (show_symbols): Call gelf_fsize with EV_CURRENT.
+ * strip.c (handle_elf): Likewise.
+
+2015-05-06 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_gnu_hash): Return early when 2nd hash function
+ shift too big. Check there is enough data available. Make sure
+ bitmask_words is not zero.
+ (check_verdef): Use Elf64_Word for shdr->sh_info cnt.
+ (check_verneed): Likewise.
+ (check_attributes): Break when vendor name isn't terminated.
+ Add overflow check for subsection_len.
+
+2015-05-05 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (show_symbols): Handle dwarf_linesrc returning NULL.
+
+2015-05-04 Max Filippov <jcmvbkbc@gmail.com>
+
+ * ar.c (do_oper_extract): Replace struct timeval with struct timespec
+ and futimes with futimens.
+ * strip.c (process_file): Replace struct timeval with struct timespec.
+ (handle_elf, handle_ar): Replace struct timeval with struct timespec
+ in prototype. Replace futimes with futimens.
+
+2015-05-04 Max Filippov <jcmvbkbc@gmail.com>
+
+ * addr2line.c (main): Drop mtrace() call and #include <mcheck.h>.
+ * ar.c: Likewise.
+ * ld.c: Likewise.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * size.c: Likewise.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise.
+
+2015-05-04 Anthony G. Basile <blueness@gentoo.org>
+
+ * Makefile.am (readelf_LDADD, nm_LDADD, size_LDADD, strip_LDADD)
+ (ld_LDADD, elflint_LDADD, findtextrel_LDADD, addr2line_LDADD)
+ (elfcmp_LDADD, objdump_LDADD, ranlib_LDADD, strings_LDADD)
+ (ar_LDADD, unstrip_LDADD, stack_LDADD): Append $(argp_LDADD).
+
+2015-03-22 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_frame_section): Cast start to Dwarf_Off
+ before subtracting cie_id. And cast cie_offset to Dwarf_Off before
+ comparison.
+
+2015-03-22 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_gdb_index_section): Check all offsets used
+ against section d_size.
+
+2015-03-17 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug): Don't return, but always use dummy_dbg.
+
+2015-03-17 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_gdb_index_section): Add overflow checking for
+ dataend checks.
+
+2015-03-14 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (INTERNAL_ERROR): Remove __DATE__.
+ * objdump.c (INTERNAL_ERROR): Likewise.
+ * size.c (INTERNAL_ERROR): Likewise.
+ * strip.c (INTERNAL_ERROR): Likewise.
+
+2015-03-18 Petr Machata <pmachata@redhat.com>
+
+ * readelf.c (dwarf_tag_string, dwarf_attr_string)
+ (dwarf_form_string, dwarf_lang_string, dwarf_inline_string)
+ (dwarf_encoding_string, dwarf_access_string)
+ (dwarf_visibility_string, dwarf_virtuality_string)
+ (dwarf_identifier_case_string, dwarf_calling_convention_string)
+ (dwarf_ordering_string, dwarf_discr_list_string)
+ (dwarf_locexpr_opcode_string): Adjust uses of know-dwarf.h macros
+ to match the API changes.
+
+2015-03-09 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (compare_hash_gnu_hash): Correct gnu_symbias usage.
+
+2015-01-03 Mark Wielaard <mjw@redhat.com>
+
+ * elfcmp (main): Check section name is not NULL. Check sh_entsize
+ is not zero for symbol tables. Check phdrs are not NULL.
+ (search_for_copy_reloc): Check sh_entsize is not zero.
+
+2014-12-30 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_scn_group): Check d_buf and name are not NULL.
+ (is_rel_dyn): Check d is not NULL. Check shdr->sh_entsize is not
+ zero.
+ (check_dynamic): Check strshdr is not NULL. Check d_tag is positive.
+ (check_symtab_shndx): Check symshdr and data->d_buf are not NULL.
+ Check shdr and symshdr sh_entsize are not zero.
+ (check_gnu_hash): Make sure maskidx is smaller than bitmask_words.
+ Check symshdr->sh_entsize is not zero. Check data->d_buf is not
+ NULL.
+ (compare_hash_gnu_hash): Check sections d_buf are not NULL.
+ Check section data is large enough. Use gnu_symbias.
+ (check_group): Check section val is valid.
+ (has_copy_reloc): Check sh_entsize is not zero.
+ (check_versym): Likewise.
+ (unknown_dependency_p): Likewise.
+ (check_verneed): Break on invalid ref or offset. Don't assert.
+ Report error when next offset is zero, but more vers expected.
+ (check_verdef): Likewise.
+ (check_attributes): Make sure d_buf is not NULL.
+ (check_note): Likewise.
+ (check_note_section): Likewise.
+ (check_program_header): Make sure section name is not NULL.
+
+2014-12-26 Mark Wielaard <mjw@redhat.com>
+
+ * strings.c (read_elf): Produce error when section data falls outside
+ file.
+
+2014-12-26 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (show_symbols): Guard against divide by zero in error check.
+ Add section index number in error message.
+
+2014-12-26 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (handle_ar): Skip over /SYM64/ entries.
+
+2014-12-26 Mark Wielaard <mjw@redhat.com>
+
+ * nm.c (handle_ar): Break on arsym with invalid offset.
+
+2014-12-20 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_macinfo_section): Mark cus sentinel files
+ as -1 non-existent. Check macoff against sentinel cus.
+
+2014-12-20 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_exception_table): Add max_action overflow
+ check. Check action_table_end before reading slib128. Check
+ max_ar_filter underflow.
+
+2014-12-18 Ulrich Drepper <drepper@gmail.com>
+
+ * Makefile.am: Suppress output of textrel_check command.
+
+2014-12-17 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_cfa_program): Add bounds check before each op that
+ takes at least one argument.
+
+2014-12-16 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_decoded_line_section): Print dwarf_errmsg if
+ dwarf_onesrcline or dwarf_linesrc fails.
+
+2014-12-16 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_line_section): Correct overflow check for
+ unit_length.
+ (print_debug_aranges_section): Correct overflow check for length.
+
+2014-12-15 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (notice_listptr): Return false if offset doesn't fit
+ in 61-bits.
+ (attr_callback): Warn if loclist or rangelist offset doesn't fit.
+
+2014-12-15 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_ops): Don't assert when addr_size or ref_size
+ is not 4 or 8, just report invalid data.
+
+2014-12-15 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_gdb_index_section): Add more bounds checks.
+
+2014-12-15 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_line_section): Check there is enough room
+ for DW_LNE_set_address argument. Make sure there is enough room
+ for the the initial unit_length.
+
+2014-12-14 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_attributes): Call get_uleb128 with end pointer.
+ * readelf.c (print_attributes): Likewise.
+ (print_ops): Likewise and also for get_sleb128.
+ (print_cfa_program): Likewise and add more readp bounds checks.
+ (read_encoded): Likewise.
+ (print_debug_frame_section): Likewise.
+ (print_debug_line_section): Likewise.
+ (print_debug_macinfo_section): Likewise.
+ (print_debug_macro_section): Likewise.
+ (print_debug_exception_table): Likewise.
+
+2014-12-16 Mark Wielaard <mjw@redhat.com>
+
+ * elfcmp.c (compare_Elf32_Word): Make sure (unsigned) Elf32_Word
+ difference doesn't wrap around before returning as int.
+
+2014-12-11 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_exception_table): Check TType base offset
+ and Action table are sane.
+
+2014-12-11 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_frame_section): Check number of augmentation
+ chars to print.
+
+2014-12-09 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_file_note): Check count fits data section and
+ doesn't overflow fptr.
+
+2014-12-08 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_exception_table): Report invalid data if
+ action table doesn't immediately follow call site table.
+
+2014-12-10 Josh Stone <jistone@redhat.com>
+
+ * addr2line.c (get_diename): New, get linkage_name or name.
+ * addr2line.c (print_dwarf_function): Use get_diename.
+ * addr2line.c (handle_address): Likewise.
+ * addr2line.c (print_diesym): Removed.
+
+2014-12-10 Josh Stone <jistone@redhat.com>
+
+ * addr2line.c (handle_address): Find the proper inline parents.
+
+2014-12-07 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_line_section): max_ops_per_instr cannot
+ be zero.
+
+2014-12-07 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_ops): Handle zero ref_size for DW_OP_call_ref
+ and DW_OP_GNU_implicit_pointer.
+
+2014-12-04 Mark Wielaard <mjw@redhat.com>
+
+ * objdump.c (show_relocs_x): Make sure destshdr exists.
+ (show_relocs_rel): Don't rely on shdr->sh_entsize, use gelf_fsize.
+ (show_relocs_rela): Likewise.
+ (show_relocs): Make sure destshdr, symshdr and symdata exists.
+
+2014-11-30 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_sysv_hash64): Fix overflow check.
+
+2014-11-28 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_relocs_rel): Don't reuse destshdr to store
+ section header of a relocation against a STT_SECTION symbol. Use
+ a new local variable secshdr.
+ (handle_relocs_rela): Likewise.
+
+2014-11-26 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_aranges_section): Cast Dwarf_Word length
+ to ptrdiff_t for comparison.
+
+2014-11-24 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_line_section): Check line_range is not zero
+ before usage.
+
+2014-11-23 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_aranges_section): Check length to catch
+ nexthdr overflow.
+
+2014-11-21 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_attributes): Guard against empty section.
+ Document attribute format. Break when vendor name isn't terminated.
+ Add overflow check for subsection_len. Handle both gnu and non-gnu
+ attribute tags.
+
+2014-11-22 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_sections): Call ebl_bss_plt_p without ehdr.
+ * findtextrel.c (process_file): Use elf_getphdrnum.
+ * readelf.c (process_elf_file): Remove redundant ehdr->e_phoff check.
+ (print_phdr): Check phnum.
+ * size.c (show_segments): Use elf_getphdrnum.
+ * strip.c (handle_elf): Likewise.
+ * unstrip.c (copy_elf): Likewise.
+ (copy_elided_sections): Likewise.
+ (handle_file): Likewise.
+
+2014-11-18 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_cfa_program): Fix sanity check of DW_FORM_block
+ length.
+
+2014-11-17 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_verneed): Check vna_next and vn_next exist.
+ (handle_verdef): Check vda_next and vd_next exist.
+ (handle_versym): Check vd_next, vna_next and vn_next exist.
+ Check vername and filename are not NULL before use.
+
+2014-11-17 Mark Wielaard <mjw@redhat.com>
+
+ * elfcmp.c (main): Check section names are NULL before use.
+ * objdump.c (section_match): Likewise.
+ * size.c (show_sysv): Likewise.
+
+2014-11-17 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_frame_section): Warn if ptr_size is not 4
+ or 8 instead of just calling print_cfa_program.
+
+2014-11-16 Mark Wielaard <mjw@redhat.com>
+
+ * readelf (process_elf_file): Set phnum to zero if there aren't
+ actually any pheaders.
+ (print_phdr): Check there actually is a phdr.
+
+2014-11-16 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_cfa_program): Check block len before calling
+ print_ops.
+
+2014-11-14 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_frame_section): Sanity Check CIE
+ unit_length and augmentationlen.
+
+2014-11-14 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_versym): Check def == NULL before use.
+
+2014-11-08 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_versym): Initialize vername and filename array
+ elements.
+
+2014-11-07 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_sysv_hash): Sanity check section contents.
+ (handle_sysv_hash64): Likewise.
+ (handle_gnu_hash): Likewise.
+
+2014-09-14 Petr Machata <pmachata@redhat.com>
+
+ * readelf.c (handle_relocs_rela): Typo fix, test DESTSHDR properly.
+
+2014-09-12 Petr Machata <pmachata@redhat.com>
+
+ * readelf.c (encoded_ptr_size): In the switch statement, change
+ magic constants 3 and 4 to DW_EH_PE_* counterparts. Still accept
+ 0. Print diagnostic for anything else.
+
+2014-08-25 Josh Stone <jistone@redhat.com>
+
+ * Makefile.am: Prevent premature @AR@ replacement in a sed expression.
+
+2014-07-04 Menanteau Guy <menantea@linux.vnet.ibm.com>
+ Mark Wielaard <mjw@redhat.com>
+
+ * elflint (check_symtab): Add ".TOC." to the list of possibly
+ dangling symbols because of sourceware PR13621.
+
+2014-06-14 Mark Wielaard <mjw@redhat.com>
+
+ * elflint (check_symtab): Use ebl_func_addr_mask on st_value.
+
+2014-05-27 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug): Skip section if name is NULL.
+
+2014-05-26 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_relocs_rela): Print header like handle_relocs_rel
+ does, when sh_info == 0.
+
+2014-05-26 Mark Wielaard <mjw@redhat.com>
+
+ * unstrip.c (find_alloc_sections_prelink): Allow non-split .bss
+ section when sh_size of the original and undo .bss section are equal.
+
+2014-05-26 Mark Wielaard <mjw@redhat.com>
+
+ * unstrip.c (options): Add --force, -F.
+ (struct arg_info): Add bool force.
+ (parse_opt): Handle 'F', set force.
+ (handle_explicit_files): Add force argument, add warn function,
+ separate check ehdr field checks, use warn.
+ (handle_dwfl_module): Add force argument, pass on to
+ handle_explicit_files.
+ (handle_output_dir_module): Add force argument, pass on to
+ handle_dwfl_module.
+ (handle_implicit_modules): Pass info->force to handle_dwfl_module and
+ handle_output_dir_module.
+ (main): Pass info.force to handle_explicit_files.
+
+2014-05-19 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_reloc_shdr): Check ebl_check_reloc_target_type.
+
+2014-05-01 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (find_no_debuginfo): Call dwfl_standard_find_debuginfo
+ if looking for alternate debug file.
+
+2014-04-11 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (AM_CPPFLAGS): Add -I libdwelf.
+
+2014-04-22 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_core_item): Make sure variable length array
+ contains at least enough space for terminating zero char.
+
+2014-04-22 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_gdb_index_section): Use unsigned int for 31 bits
+ left shift.
+
+2014-03-13 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am: Remove no_mudflap.os. Remove libmudflap from all
+ LDADD lines.
+ * strings.c (process_chunk): Remove _MUDFLAP condition.
+
+2014-04-09 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_aranges_section): Don't get the raw section
+ data, use the possibly decompressed .[z]debug sectiondata.
+ (print_debug_ranges_section): Likewise.
+ (print_debug_frame_section): Likewise.
+ (print_debug_line_section): Likewise.
+ (print_debug_loc_section): Likewise.
+ (print_debug_macinfo_section): Likewise.
+ (print_debug_macro_section): Likewise.
+
+2014-04-10 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (buf_read_ulong): Pass actual long size to convert.
+
+2014-03-05 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (attr_callback): Print DW_FORM_sdata values as signed
+ numbers.
+
+2014-02-24 Mark Wielaard <mjw@redhat.com>
+
+ * readelf (print_phdr): Check there is a SHT_PROGBITS section at the
+ offset given by p_offsets for a PT_INTERP segment before trying to
+ display the interpreter string.
+
+2014-02-07 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_phdr): Check phdr->p_filesz and make sure
+ interpreter string is zero terminated before calling printf.
+
+2014-01-22 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (nm_no_Wformat): Removed.
+ (size_no_Wformat): Likewise.
+ (strings_no_Wformat): Likewise.
+ (addr2line_no_Wformat): Likewise.
+ * size.c (show_sysv): Use fmtstr directly as literal in printf.
+ (show_sysv_one_line): Likewise.
+ * strings.c (locfmt): Removed.
+ (radix): New static enum.
+ (parse_opt): Set radix, not locfmt.
+ (process_chunk_mb): Use fmtstr directly as literal in printf based
+ on radix.
+ (process_chunk): Likewise.
+ * nm.c (show_symbols_sysv): Use fmtstr directly as literal in printf.
+ (show_symbols_bsd): Likewise.
+ (show_symbols_posix): Likewise.
+
+2014-01-21 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (show_inlines): New static boolean.
+ (print_frame): New function split out from...
+ (print_frames): ..here. If show_inlines is true and we found a
+ DIE for the frame address, call print_inline_frames otherwise
+ call print_frame. Keep track of and track frame_nr.
+ (print_inline_frames): New function.
+ (parse_opt): Handle '-i'.
+ (main): Add 'i' to options.
+
+2014-01-27 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (maxframes): Initialize to 256.
+ (main): Document new default in options. Document magic number
+ used in frames.allocated initialization.
+
+2014-01-20 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (show_debugname): New static boolean.
+ (die_name): New function.
+ (print_frames): If show_debugname is true set symname to the
+ first function-like DIE with a name in scope for the address in
+ the debuginfo.
+ (parse_opt): Handle '-d'.
+ (main): Add 'd' to options.
+
+2014-01-20 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (handle_address): Initialize scopes to NULL.
+
+2014-01-17 Roland McGrath <roland@redhat.com>
+
+ * strip.c (handle_elf): Check for bogus values in sh_link, sh_info,
+ st_shndx, e_shstrndx, and SHT_GROUP or SHT_SYMTAB_SHNDX data.
+ Don't use assert on input values, instead bail with "illformed" error.
+
+2014-01-17 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (handle_dynamic, handle_symtab): Check for bogus sh_link.
+ (handle_verneed, handle_verdef, handle_versym, handle_hash): Likewise.
+ (handle_scngrp): Check for bogus sh_info.
+
+2014-01-17 Jakub Jelinek <jakub@redhat.com>
+
+ * elflint.c (section_name): Return "<invalid>" instead of
+ crashing on invalid section name.
+ (check_symtab, is_rel_dyn, check_rela, check_rel, check_dynamic,
+ check_symtab_shndx, check_hash, check_versym): Robustify.
+ (check_hash): Don't check entries beyond end of section.
+ (check_note): Don't crash if gelf_rawchunk fails.
+
+2014-01-17 Petr Machata <pmachata@redhat.com>
+
+ * readelf.c (handle_dynamic, handle_relocs_rel)
+ (handle_relocs_rela, handle_versym, print_liblist):
+ Use gelf_fsize instead of relying on shdr->sh_entsize.
+
+2014-01-14 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_macro_section): Clear vendor array before
+ use.
+
+2014-01-15 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Fix corruption of non-C++ symbols by the demangler.
+ * nm.c (show_symbols_sysv, show_symbols_bsd, show_symbols_posix)
+ (show_symbols): Check for _Z.
+ * stack.c (print_frames) <USE_DEMANGLE>: Check for _Z.
+
+2014-01-02 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (show_raw): Declare unconditionally.
+ (parse_opt): Handle '-r' unconditionally.
+ (main): Show "raw" option even without USE_DEMANGLE.
+
+2014-01-02 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (print_frames): Print 0x before build-id hex-offset.
+
+2014-01-02 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (maxframes): Increase to 2048.
+ (struct frames): Add allocated field.
+ (frame_callback): If frames used is frames allocated, realloc.
+ (print_frames): Show an error if maxframes has been reached.
+ (parse_opt): Allow -n 0 for unlimited frames.
+ (main): Document -n 0 and new default 2048 frames. Allocate initial
+ number of frames with malloc.
+
+2013-12-30 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (parse_opt): Explicitly call dwfl_linux_proc_attach
+ or dwfl_core_file_attach and check for errors.
+
+2013-12-28 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (print_frames): Remove address width code and use...
+ (get_addr_width): ...this new function.
+ (show_modules): New static boolean.
+ (module_callback): New static function.
+ (parse_opt): Handle '-l'.
+ (main): Add 'l' to options. If show_modules then use dwfl_getmodules
+ with module_callback to show all detected modules and possible
+ build_id, elf and dwarf files.
+
+2013-12-27 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (frames_shown): New static boolean.
+ (EXIT_OK,EXIT_ERROR,EXIT_BAD,EXIT_USAGES): New defines.
+ (frame_callback): Return -1 on error. Don't print error.
+ (print_frames): Add arguments, tid, dwflerr and what. Print tid.
+ If there was an error report it with address and module if possible.
+ Record whether any frames were actually printed.
+ (thread_callback): Collect tid and err, pass it to print_frames.
+ (parse_opt): Use EXIT_BAD for errors. On ARGP_KEY_END print errno
+ if dwfl_linux_proc_report returned it. Check whether we are properly
+ attached with dwfl_pid.
+ (main): Document exit status. Don't report DWARF_CB_ABORT from
+ callbacks as error. Pass real errors to print_frames. Return
+ EXIT_BAD if no frames could be shown. Return EXIT_ERROR if there
+ were any non-fatal errors.
+
+2013-12-23 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (stack_LDADD): Add demanglelib.
+ * stack.c (show_quiet): New static boolean, default false.
+ (show_raw): Likewise.
+ (demangle_buffer_len): New static size_t.
+ (demangle_buffer): New static char *.
+ (print_frames): Don't resolve pc name if show_quiet. Demangle name
+ unless show_raw.
+ (parse_opt): Handle '-q' and '-r'.
+ (main): Add 'q' and 'r' to options. Free demangle_buffer.
+
+2013-12-23 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (OPT_DEBUGINFO): New define.
+ (OPT_COREFILE): Likewise.
+ (pid): New static.
+ (core_fd): Likewise.
+ (core): Likewise.
+ (exec): Likewise.
+ (debuginfo_path): Likewise.
+ (parse_opt): Handle '-p', '--core', '-e' and '--debuginfo-path'.
+ Do argument sanity checking. Setup Dwfl.
+ (main): Add 'p', 'core', 'e' and 'debuginfo-path' to options.
+ Remove argp_child children, simplify argp doc, remove custom
+ usage message and construction of dwfl with dwfl_standard_argp.
+ Use pid directly as tid. close core and core_fd if opened. Print
+ pid of process or core.
+
+2013-12-23 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (show_build_id): New static boolean.
+ (print_frames): Print module build-id, load address and pc offset
+ if show_build_id is true.
+ (parse_opt): Handle '-b'.
+ (main): Add -b to options.
+
+2013-12-22 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (maxframes): New static unsigned. Initialize to 64.
+ (struct frame): New struct.
+ (struct frames): Likewise.
+ (dwfl): New static Dwfl pointer.
+ (frame_callback): Use arg as struct frames and fill it next frame.
+ Return DWARF_CB_ABORT when maxframes has been reached. Move
+ printing of frame to...
+ (print_frames): ...here. New function.
+ (thread_callback): Use arg as struct frames and set frames to zero.
+ Call print_frames.
+ (parse_opt): Handle '-n'.
+ (main): Add -n to options. Allocate frames using maxframes. Pass
+ frames to frame_callback and thread_callback.
+
+2013-12-20 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (show_one_tid): New static boolean.
+ (parse_opt): Handle '-1'.
+ (main): Add -1 to options. Call dwfl_getthread_frames when
+ show_one_tid is true.
+
+2013-12-18 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (options): Add symbol-sections, 'x'.
+ (show_symbol_sections): New static bool.
+ (parse_opt): Handle 'x'.
+ (print_addrsym): Use dwfl_module_addrinfo value.r
+ Also show section of address with show_symbol_sections.
+ (find_symbol): Use dwfl_module_getsym_info and set value.
+ (handle_address): Request value and use it instead of sym.st_value.
+ * readelf.c (format_dwarf_addr): Use dwfl_module_addrinfo to get
+ name and offset.
+
+2013-12-17 Masatake YAMATO <yamato@redhat.com>
+ Mark Wielaard <mjw@redhat.com>
+
+ * stack.c (show_activation, show_module, show_source): New variables.
+ (parse_opt): Set show_activation if -a option is given.
+ Set show_module if -m option is given. Set show_source if -s option
+ is given. Set all show booleans when -v option is given.
+ (main): Added `-a', `-m', `-s', and `-v' to the help message.
+ (frame_callback): Print module and source file information.
+
+2013-11-25 Petr Machata <pmachata@redhat.com>
+
+ * elflint.c (valid_e_machine): Add EM_AARCH64.
+
+2013-11-14 Petr Machata <pmachata@redhat.com>
+
+ * readelf.c (handle_core_item) <'h'>: New branch for handling
+ fields that shouldn't be displayed.
+
+2013-11-10 Mark Wielaard <mjw@redhat.com>
+
+ * stack.c: Use ARGP_PROGRAM_VERSION_HOOK_DEF and
+ ARGP_PROGRAM_BUG_ADDRESS_DEF.
+ (print_version): New function.
+
+2013-11-09 Mark Wielaard <mjw@redhat.com>
+
+ * arlib.c (arlib_init): Call snprintf before using the result
+ with memcpy.
+ (arlib_finalize): Likewise.
+ * nm.c (show_symbols_sysv): Don't modify cnt inside assert.
+
+2013-11-07 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * Makefile.am (bin_PROGRAMS): Add stack.
+ (stack_LDADD): New.
+ * stack.c: New file.
+
+2013-11-05 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_ranges_section): Cast address to size_t
+ before comparison.
+ (print_debug_loc_section): Likewise.
+
+2013-10-18 Mark Wielaard <mjw@redhat.com>
+
+ * ar.c (main): Correct operation check when instance_specifed is set.
+
+2013-09-26 Petr Machata <pmachata@redhat.com>
+
+ * readelf.c (handle_file_note): New function.
+ (handle_notes_data): Call it to handle NT_FILE notes.
+
+2013-09-26 Petr Machata <pmachata@redhat.com>
+
+ * readelf.c (handle_siginfo_note): New function.
+ (handle_notes_data): Call it to handle NT_SIGINFO notes.
+ (buf_read_int, buf_read_ulong, buf_has_data): New functions.
+
+2013-08-13 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (options): Add "inlines", 'i'.
+ (show_inlines): New bool.
+ (parse_opt): Handle 'i'.
+ (print_diesym): New static function.
+ (print_src): New function taking code from...
+ (handle_address): here. Call print_src. Print inlines.
+
+2013-08-12 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (main): If there is a newline char at end of buf,
+ then remove it.
+
+2013-07-05 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_ops): Take CU as argument, use it to print
+ parameter_ref DIE offset.
+ (struct listptr): Replace base field with cu.
+ (listptr_base): New function.
+ (compare_listptr): Use listptr_base.
+ (notice_listptr): Take CU as argument.
+ (skip_listptr_hole): Likewise.
+ (print_debug_ranges_section): Pass NULL as CU to skip_listptr_hole.
+ (print_cfa_program): Pass NULL as CU to print_ops.
+ (struct attrcb_args): Replace cu_base field with cu.
+ (attr_callback): Pass cu not cu_base to notice_listptr.
+ (print_debug_units): Don't calculate base, just set cu.
+ (print_debug_loc_section): Pass cu to skip_listptr_hole and
+ print_ops.
+
+2013-05-06 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_ops): Format first DW_OP_GNU_implicit_pointer
+ argument as DIE offset.
+
+2013-04-24 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am: Use AM_CPPFLAGS instead of INCLUDES.
+
+2013-03-25 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (argp_options): Add decodedline.
+ (decodedline): New boolean initialized to false.
+ (parse_opt): Set decodedline when arg is decodedline.
+ (print_decoded_line_section): New function.
+ (print_debug_line_section): Call print_decoded_line_section when
+ decodedline is true.
+
+2013-03-25 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (argp_option): Add decodedaranges.
+ (decodedaranges): New boolean initialized to false.
+ (parse_opt): Set decodedaranges when arg is decodedaranges.
+ (print_debug_aranges_section): Reimplemented and original
+ implementation renamed to...
+ (print_decoded_aranges_section): this.
+
+2013-03-25 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (attrcb_args): Add Dwarf_Die.
+ (attr_callback): When highpc is in constant form also print as
+ address.
+ (print_debug_units): Set args.die.
+
+2013-03-19 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_gdb_index_section): Free format_dwarf_addr results.
+
+2013-03-18 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_gdb_index_section): Accept version 8.
+
+2013-03-01 Mark Wielaard <mjw@redhat.com>
+
+ * findtextrel.c (process_file): Release ELF and close file when not
+ text relocations are found.
+ * strip.c (handle_elf): Track memory used for .debuglink section data
+ and free when done.
+
+2013-02-24 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_symtab): Add __bss_start__ to the list of symbols
+ allowed to have out of section values because of GNU ld bugs.
+
+2013-02-06 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_symtab): Add __bss_start and __TMC_END__ to the
+ list of symbols allowed to have out of section values because of
+ GNU ld bugs in either .symtab or .dynsym, but only when they are
+ zero sized.
+
+2013-01-24 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (argp_option): Add unresolved-address-offsets, U.
+ (print_unresolved_addresses): New static.
+ (parse_opt): Handle 'U', set print_unprocessed_values.
+ (format_dwarf_addr): Take and handle new raw argument.
+ (print_ops): Call format_dwarf_addr with raw offset values.
+ (print_debug_ranges_section): Likewise.
+ (print_debug_frame_section): Likewise.
+ (attr_callback): Likewise.
+ (print_debug_line_section): Likewise.
+ (print_debug_loc_section): Likewise.
+ (print_gdb_index_section): Likewise.
+
+2013-01-18 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (struct listptr): Add base Dwarf_Addr field.
+ (compare_listptr): Warn for same offset with different base.
+ (notice_listptr): Take base argument and set it.
+ (skip_listptr_hole): Likewise.
+ (struct attrcb_args): Removed unused cu_offset field.
+ Add cu_base Dwarf_Addr field.
+ (attr_callback): Call notice_listptr with cbargs->cu_base.
+ (print_debug_units): Set args.cu_base.
+ (print_debug_ranges_section): Get base and use for format_dwarf_addr.
+ (print_debug_loc_section): Likewise.
+
+2013-01-29 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * readelf.c (handle_core_items): Limit special repeated items handling
+ to single-item formats '\n', 'b' and 'B', assert OFFSET 0 there.
+
+2012-12-18 Mark Wielaard <mark@bordewijk.wildebeest.org>
+
+ * readelf.c (ELF_INPUT_SECTION): New argp key value.
+ (argp_option): Add elf-section.
+ (elf_input_section): New static.
+ (parse_opt): Handle ELF_INPUT_SECTION and set elf_input_section.
+ (open_input_section): New function.
+ (process_file): Call open_input_section if elf_input_section set.
+
+2013-01-13 David Abdurachmanov <David.Abdurachmanov@cern.ch>
+
+ ar.c (do_oper_delete): Fix num passed to memset.
+
+2012-12-21 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_frame_section): Adjust FDE start address
+ if pcrel before feeding it to format_dwarf_addr.
+
+2012-12-21 Mark Wielaard <mjw@redhat.com>
+
+ * addr2line.c (main): Call dwfl_end.
+
+2012-12-11 Roland McGrath <roland@hack.frob.com>
+
+ * nm.c (show_symbols_sysv): Fix size passed to snprintf for invalid
+ sh_name case.
+ Reported by David Abdurachmanov <David.Abdurachmanov@cern.ch>.
+
+2012-10-16 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_ops): DW_OP_skip and DW_OP_bra targets are
+ calculated beginning after the operand and 2-byte constant.
+
+2012-10-12 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * readelf.c (ITEM_WRAP_COLUMN, REGISTER_WRAP_COLUMN): Merge to ...
+ (WRAP_COLUMN): ... here.
+ (print_core_item): Remove parameter format_max. Update function
+ comment. Replace FORMAT_MAX by the real output width.
+ (handle_core_item): Remove the FORMAT_MAX values in TYPES, DO_TYPE,
+ calls of print_core_item, remove variable maxfmt, change
+ ITEM_WRAP_COLUMN to WRAP_COLUMN.
+ (handle_core_register): Remove the FORMAT_MAX values in TYPES, BITS,
+ calls of print_core_item, change REGISTER_WRAP_COLUMN to WRAP_COLUMN.
+
+2012-10-11 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * readelf.c (handle_core_item) <b>: Make run an outer block variable.
+ Increase run only if LASTBIT != 0. Print last element only if RUN > 0.
+
+2012-08-27 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_macro_section): Print offset as PRIx64.
+
+2012-08-27 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (register_info): Handle loc == NULL.
+
+2012-08-22 Jeff Kenton <jkenton@tilera.com>
+
+ * elflint.c (valid_e_machine): Add EM_TILEGX and EM_TILEPRO.
+
+2012-08-16 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (dwarf_tag_name): Renamed from dwarf_tag_string.
+ Uses new dwarf_tag_string or adds ??? or lo_user+%#x when
+ appropriate.
+ (dwarf_attr_name): Likewise.
+ (dwarf_form_name): Likewise.
+ (dwarf_lang_name): Likewise.
+ (dwarf_inline_name): Likewise.
+ (dwarf_encoding_name): Likewise.
+ (dwarf_access_name): Likewise.
+ (dwarf_visibility_name): Likewise.
+ (dwarf_virtuality_name): Likewise.
+ (dwarf_identifier_case_name): Likewise.
+ (dwarf_calling_convention_name): Likewise.
+ (dwarf_ordering_name): Likewise.
+ (dwarf_discr_list_name): Likewise.
+ (print_ops): Remove KNOWN. Use dwarf_locexpr_opcode_string.
+ (attr_callback): Call new dwarf_foobar_name instead of old
+ dwarf_foobar_string functions.
+ (dwarf_tag_string): New function using known-dwarf.h macros.
+ (dwarf_attr_string): Likewise.
+ (dwarf_form_string): Likewise.
+ (dwarf_lang_string): Likewise.
+ (dwarf_inline_string): Likewise.
+ (dwarf_encoding_string): Likewise.
+ (dwarf_access_string): Likewise.
+ (dwarf_visibility_string): Likewise.
+ (dwarf_virtuality_string): Likewise.
+ (dwarf_identifier_case_string): Likewise.
+ (dwarf_calling_convention_string): Likewise.
+ (dwarf_ordering_string): Likewise.
+ (dwarf_discr_list_string): Likewise.
+ (dwarf_locexpr_opcode_string): Likewise.
+
+2012-06-27 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (dwarf_form_string): Handle DW_FORM_GNU_ref_alt and
+ DW_FORM_GNU_strp_alt.
+ (attr_callback): Likewise.
+
+2012-07-30 Petr Machata <pmachata@redhat.com>
+
+ * nm.c (show_symbols_bsd): Reorder arguments in {S,}FMTSTRS (and
+ corresponding printf) so that those that are referenced by only
+ one of the formatting strings are at the end.
+
+2012-07-29 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (dwarf_lang_string): Use DW_LANG_ObjC, not DW_LANG_Objc.
+ (print_ops): Use known[op], not op_name, for DW_OP_GNU_parameter_ref.
+
+2012-07-19 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_ops): Handle DW_OP_GNU_parameter_ref.
+
+2012-07-11 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (options): Add macro to help of debug-dump.
+ (section_e): Add section_macro.
+ (section_all): Add section_macro.
+ (parse_opt): Handle macro.
+ (print_debug_macro_section): New function.
+ (print_debug): Add NEW_SECTION (macro).
+
+2012-07-10 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_gdb_index_section): Add version 7 support.
+ Keep track of cu_nr. Print kind and static/global flag for each
+ symbol. When a symbol is in the TU list add 'T'.
+
+2012-06-26 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (dwarf_attr_string): Add DW_AT_GNU_macros.
+
+2012-06-22 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_ops): Cast printf PRIu/x64 arguments to uint64_t
+ for gcc 4.7 -Wformat.
+
+2012-05-09 Roland McGrath <roland@hack.frob.com>
+
+ * elflint (check_sections): Allow zero sized sections at (filesz) end
+ of segment. And make check overflow-proofed.
+
+2012-04-24 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_ops): Add DW_OP_GNU_push_tls_address,
+ DW_OP_GNU_uinit and DW_OP_GNU_encoded_addr.
+
+2012-03-28 Roland McGrath <roland@hack.frob.com>
+
+ * elflint.c (special_sections): Accept SHF_INFO_LINK for reloc sections.
+
+2012-03-28 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_debug_abbrev_section): Check there is Dwarf
+ section data.
+ (print_debug_str_section): Likewise.
+
+2012-03-21 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_gdb_index_section): Accept version 6.
+
+2012-01-31 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (attr_callback): Don't special case DW_FORM_sec_offset.
+
+2012-01-21 Ulrich Drepper <drepper@gmail.com>
+
+ * addr2line.c: Update copyright year.
+ * ar.c: Likewise.
+ * elfcmp.c: Likewise.
+ * elflint.c: Likewise.
+ * findtextrel.c: Likewise.
+ * ld.c: Likewise.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * strings.c: Likewise.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise.
+
+ * nm.c (argp_children): Define.
+ (argp): Hook up argp_children.
+ (handle_ar): Optimize puts call.
+ (show_symbols_bsd): Use positional parameters to also print color
+ codes. Don't print STT_FILE symbols.
+ * objdump.c (options): Improve help text.
+ (argp_children): Define.
+ (argp): Hook up argp_children.
+ (disasm_info): Add elements for color codes.
+ (disasm_output): Print color codes as well.
+ (show_disasm): Set up disasm_info data for callback.
+
+2012-01-20 Roland McGrath <roland@hack.frob.com>
+
+ * arlib-argp.c (arlib_deterministic_output): Initialize from
+ configured value.
+ (help_filter): New function.
+ (argp): Use it.
+
+ * ar.c (main): Handle oper_none as usage error.
+
+ * arlib-argp.c (options, parse_opt): Grok -U as inverse of -D.
+
+ * ranlib.c (argp): Use arlib_argp_children.
+
+ * arlib.c (arlib_init): Obey arlib_deterministic_output.
+
+ * arlib-argp.c: New file.
+ * Makefile.am (libar_a_SOURCES): Add it.
+ * arlib.h (arlib_deterministic_output, arlib_argp_children):
+ Declare new variables.
+ * ar.c (deterministic_output): Variable removed.
+ (do_oper_insert): Use arlib_deterministic_output instead.
+ (options, parse_opt): Don't handle -D here. Add group numbers.
+ (argp): Use arlib_argp_children.
+
+2011-12-20 Roland McGrath <roland@hack.frob.com>
+
+ * readelf.c (print_debug): Initialize DUMMY_DBG.elf.
+ Reported by Karel Klic <kklic@redhat.com>.
+
+2011-11-05 Roland McGrath <roland@hack.frob.com>
+
+ * ar.c (deterministic_output): New flag variable.
+ (options, parse_opt): Grok -D to set it.
+ (do_oper_insert): When set, use zero from mtime, uid, and gid.
+
+ * ar.c (do_oper_insert): Fix check on elf_rawfile return value.
+
+2011-10-04 Marek Polacek <mpolacek@redhat.com>
+
+ * readelf.c (register_info): Assume the right size of an array.
+
+2011-10-03 Ulrich Drepper <drepper@gmail.com>
+
+ * nm.c: Recognize option --mark-special. Still recognize --mark-weak
+ but don't show it in help anymore.
+ (mark_special): Renamed from mark_weak.
+ (parse_opt): Adjust.
+ (class_type_char): Take additional parameters for ELF file and ELF
+ header. Treat TLS symbols like objects.
+ In case of D symbols, show u for unique symbols, R for symbols in
+ read-only sections, B for symbols in BSS sections.
+ (show_symbols_bsd): Take additional parameters for ELF file and ELF
+ header. Adjust for class_type_char change. Show TLS symbols with
+ @ after them in case --mark-special is selected.
+ (show_symbols_posix): Likewise.
+ (show_symbols): Adjust calls to show_symbols_bsd and
+ show_symbols_posix.
+ (show_symbols_sysv): Avoid printing adress and size for undefined
+ symbols. Don't print initial special entry and section entries.
+
+2011-10-02 Ulrich Drepper <drepper@gmail.com>
+
+ * Makefile.am (demanglelib): Define.
+ (nm_LDADD): Add demanglelib.
+ * nm.c (options): Add -C option.
+ (demangle): Define as global variable.
+ (parse_opt): Recognize -C.
+ (show_symbols_sysv): Handle demangling.
+ (show_symbols_bad): Likewise.
+ (show_symbols_posix): Likewise.
+ (show_symbols): Likewise.
+
+2011-07-09 Roland McGrath <roland@hack.frob.com>
+
+ * readelf.c (options, parse_opt): Grok -W/--wide and ignore it.
+
+ * ar.c (parse_opt): Grok -u.
+
+2011-05-30 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (relocate): Make offset check overflow-proof.
+
+2011-05-23 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (relocate): Take new arguments is_rela to indicate
+ whether the relocation is from a SHT_REL or SHT_RELA section.
+ Relocate against any debug section symbol, not just STT_SECTION
+ symbols. For SHT_REL relocations, fetch addend from offset and
+ add it to symbol value if not zero.
+
+2011-05-23 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (OPT_RELOC_DEBUG): New option.
+ (argp_option): Add new --reloc-debug-sections option.
+ (main): Check new option.
+ (parse_opt): Likewise.
+ (handle_elf): Remove any relocations between debug sections
+ in ET_REL for the debug file when requested.
+
+2011-05-18 Mark Wielaard <mjw@redhat.com>
+
+ * strip.c (handle_elf): Make sure all sections of a removed group
+ section are removed too. Don't discard SHT_GROUP sections, copy
+ section table before it gets modified. Section group signature
+ symbols don't have to be retained.
+
+2011-05-16 Jakub Jelinek <jakub@redhat.com>
+
+ * readelf.c (print_ops): Handle DW_OP_GNU_const_type,
+ DW_OP_GNU_regval_type, DW_OP_GNU_deref_type, DW_OP_GNU_convert
+ and DW_OP_GNU_reinterpret.
+
+2011-05-17 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (dwarf_tag_string): Fixup DW_TAG_GNU_call_site and
+ DW_TAG_GNU_call_site_parameter return strings.
+
+2011-05-11 Marek Polacek <mpolacek@redhat.com>
+
+ * nm.c (show_symbols_sysv): Remove unused if/else, remove
+ unused `prefix' and `fname' parameters.
+
+2011-05-07 Marek Polacek <mpolacek@redhat.com>
+
+ * unstrip.c (compare_sections_nonrel): Mark this function as static.
+
+2011-04-26 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (handle_notes_data): Call ebl_object_note_type_name
+ with note name.
+
+2011-04-14 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (options): Add gdb_index.
+ (section_e): Define section_gdb_index.
+ (parse_opt): Recognize gdb_index debug-dump argument.
+ (print_gdb_index_section): New function.
+ (print_debug): Add gdb_index to debug_sections.
+
+2011-03-24 Petr Machata <pmachata@redhat.com>
+
+ * readelf.c (print_debug_line_section): Emit initial space for all
+ opcode lines. Print offset in front of each opcode.
+
+2011-03-22 Marek Polacek <mpolacek@redhat.com>
+
+ * readelf.c (handle_dynamic): Don't segfault at DT_PLTREL case.
+
+2011-03-22 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (dwarf_tag_string): Support DW_TAG_GNU_call_site
+ and DW_TAG_GNU_call_site_parameter.
+ (dwarf_attr_string): Support DW_AT_GNU_call_site_value,
+ DW_AT_GNU_call_site_data_value,
+ DW_AT_GNU_call_site_target,
+ DW_AT_GNU_call_site_target_clobbered,
+ DW_AT_GNU_tail_call,
+ DW_AT_GNU_all_tail_call_sites,
+ DW_AT_GNU_all_call_sites,
+ and DW_AT_GNU_all_source_call_sites.
+ (print_ops): Handle DW_OP_GNU_entry_value.
+ (attr_callback): Handle DW_AT_GNU_call_site_value,
+ DW_AT_GNU_call_site_data_value,
+ DW_AT_GNU_call_site_target,
+ and DW_AT_GNU_call_site_target_clobbered.
+
+2011-03-10 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_symtab): Use ebl_check_st_other_bits.
+
+2011-02-27 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * readelf.c (reset_listptr): Clear TABLE->TABLE.
+
+2011-02-25 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (dwarf_attr_string): Add DW_AT_GNU_* handling.
+ (dwarf_form_string): Properly format and return unknown form.
+
+2011-02-23 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (section_name): New function.
+ (print_debug_abbrev_section): Use it instead of constant.
+ (print_debug_aranges_section): Likewise.
+ (print_debug_ranges_section): Likewise.
+ (print_debug_units): Likewise.
+ (print_debug_line_section): Likewise.
+ (print_debug_loc_section): Likewise.
+ (print_debug_macinfo_section): Likewise.
+ (print_debug_pubnames_section): Likewise.
+ (print_debug_str_section): Likewise.
+ (print_debug) [USE_ZLIB]: Match .zdebug_* sections too.
+ (print_debug_abbrev_section): Use decoded d_size, not sh_size.
+ (print_debug_str_section): Likewise.
+
+ * readelf.c (dwarf_attr_string): Grok DW_AT_GNU_odr_signature.
+
+2011-02-11 Roland McGrath <roland@redhat.com>
+
+ * elfcmp.c (verbose): New variable.
+ (options, parse_opt): Grok -l/--verbose to set it.
+ (main): Under -l, keep going after first difference.
+
+ * elfcmp.c (ignore_build_id): New variable.
+ (options, parse_opt): Grok --ignore-build-id to set it.
+ (main): For SHT_NOTE sections, compare note details rather than raw
+ bytes. Under --ignore-build-id, don't complain about differing build
+ ID contents if lengths match.
+
+2011-02-08 Roland McGrath <roland@redhat.com>
+
+ * ldscript.y (filename_id_star): Remove unused variable.
+
+ * unstrip.c (copy_elided_sections): Remove unused variable.
+
+ * elflint.c (check_dynamic): Remove unused variables.
+
+ * elflint.c (check_symtab): Warn about missing xndx section only once.
+
+ * ldgeneric.c (check_for_duplicate2): Remove unused variable.
+
+2011-01-06 Roland McGrath <roland@redhat.com>
+
+ * strip.c (handle_elf): Under --strip-sections, remove all
+ non-allocated sections and never generate .gnu_debuglink.
+
+2011-01-04 Roland McGrath <roland@redhat.com>
+
+ * strip.c (remove_shdrs): New variable.
+ (options, parse_opt): Grok --strip-sections to set it.
+ (handle_elf): When that's set, truncate off .shstrtab and shdrs.
+
+2010-11-10 Roland McGrath <roland@redhat.com>
+
+ * findtextrel.c (process_file): Don't assume order of sections.
+ Reported by Mike Hommey <mh@glandium.org>.
+
+2010-07-26 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_ops): Handle DW_OP_GNU_implicit_pointer.
+
+2010-08-30 Roland McGrath <roland@redhat.com>
+
+ Print .debug_loc/.debug_ranges with cognizance of actual DIE uses.
+ * readelf.c (parse_opt): Add section_info to implicit_debug_sections
+ for ranges, loc.
+ (struct listptr, struct listptr_table): New types.
+ (compare_listptr, reset_listptr, sort_listptr): New functions.
+ (notice_listptr, skip_listptr_hole): New functions.
+ (struct attrcb_args): Add silent member.
+ (attr_callback): Call notice_listptr for loclistptr and rangelistptr.
+ Suppress output if silent, but still call notice_listptr.
+ (print_debug_units): Suppress output if section_info not requested.
+ (print_debug_loc_section): Call sort_listptr, skip_listptr_hole.
+ (print_debug_ranges_section): Likewise.
+ (print_debug): Call reset_listptr on both tables.
+
+ * readelf.c (print_debug_ranges_section): Print empty list.
+ (print_debug_loc_section): Likewise.
+
+ * readelf.c (print_debug_loc_section): Check for bogus length
+ before calling print_ops.
+ (print_ops): Check harder for bogus data that would read off end.
+
+2010-08-11 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (for_each_section_argument): Process all sections with
+ matching name, not just the first.
+
+2010-07-26 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_ops): Take new argument for CU version.
+ Fix DW_OP_call_ref decoding to depend on it.
+ (print_debug_loc_section): Update caller.
+ (print_cfa_program): Take new argument, pass it down.
+ (print_debug_frame_section): Update caller.
+ (struct attrcb_args): New member version.
+ (print_debug_units): Initialize it.
+
+2010-07-02 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_debug_frame_section): Use format_dwarf_addr for
+ initial_location.
+
+2010-06-30 Roland McGrath <roland@redhat.com>
+
+ * strings.c (main): Use STDIN_FILENO, not STDOUT_FILENO.
+ Ignore st_size for a non-S_ISREG file descriptor.
+ (read_block): Move assert after no-mmap bail-out.
+ (read_block_no_mmap): Fix size calculations for moving buffer remnant.
+
+2010-06-22 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_debug_line_section): Fix braino in DW_LNS_set_isa.
+
+2010-06-21 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (dwarf_tag_string): Handle new v4 tags.
+ (dwarf_attr_string): Add new attributes.
+ (dwarf_tag_string): Handle DW_TAG_GNU_*.
+
+ * readelf.c (print_ops): Use 64-bit types for LEB128 operands.
+ (print_cfa_program): Likewise.
+
+2010-06-20 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_debug_units): New function, broken out of ...
+ (print_debug_info_section): ... here. Call it.
+ (print_debug_types_section): New function.
+ (enum section_e): Add section_types alias for section_info.
+ (print_debug): Add types to the sections table.
+
+ * readelf.c (print_debug_frame_section): Handle version 4 format.
+
+ * readelf.c (print_debug_line_section): Handle version 4 format.
+
+2010-06-14 Roland McGrath <roland@redhat.com>
+
+ * unstrip.c (copy_elided_sections): Make sure all sections' data have
+ been read in before we write anything out.
+
+2010-06-04 Roland McGrath <roland@redhat.com>
+
+ * unstrip.c (update_shdr): New function.
+ (update_sh_size): Call it instead of gelf_update_shdr.
+ (adjust_relocs, add_new_section_symbols): Likewise.
+ (new_shstrtab, copy_elided_sections): Likewise.
+
+ * unstrip.c (copy_elided_sections): Bail if stripped file has more
+ sections than unstripped file, rather than getting confused later.
+
+2010-06-01 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (dwarf_form_string): Handle DWARF 4 forms.
+ (attr_callback): Handle DW_FORM_flag_present, DW_FORM_exprloc,
+ DW_FORM_sec_offset, DW_FORM_ref_sig8.
+
+ * readelf.c (print_debug): Don't bail if libdw setup fails.
+ Suppress complaint if we only want .eh_frame anyway.
+
+2010-05-28 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (attr_callback): Also print form information.
+
+2010-05-19 Roland McGrath <roland@redhat.com>
+
+ * addr2line.c (find_symbol): Short-circuit on empty name.
+ (handle_address): Handle SYMBOL with no +OFFSET.
+
+2010-05-08 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_ops): Take new arg OFFSET_SIZE.
+ Use that for DW_OP_call_ref, not ADDRSIZE.
+ (print_cfa_program): Update caller.
+ (struct attrcb_args): Add offset_size field.
+ (attr_callback): Use it for print_ops call.
+ (print_debug_info_section): Initialize it.
+ (print_ops): Likewise.
+
+2010-04-14 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (handle_core_item): Fix bitmask printing.
+
+2010-04-06 Roland McGrath <roland@redhat.com>
+
+ * ld.c (options): Fix some typos in messages.
+ * elflint.c (check_scn_group, check_group): Likewise.
+ * ldscript.y (add_id_list): Likewise.
+ * readelf.c (print_hash_info): Add xgettext:no-c-format magic comment
+ before translated string containing a literal %.
+
+2010-02-26 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (process_file): Don't leak an fd in failure case.
+
+2010-02-15 Roland McGrath <roland@redhat.com>
+
+ * Makefile.am: Use config/eu.am for common stuff.
+
+ * readelf.c (print_debug_frame_section): Add a cast to avoid sign
+ mismatch in comparison.
+
+2010-02-02 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_encoding_base): Handle DW_EH_PE_absptr (zero).
+ (read_encoded): Likewise.
+ (print_debug_frame_section): Check for bogus augmentation length.
+ For P augmentation, use read_encoded, print the encoding description,
+ and use hex for unsigned values.
+
+2010-01-15 Roland McGrath <roland@redhat.com>
+
+ * ar.c: Include <sys/stat.h>.
+ * elflint.c: Likewise.
+ * readelf.c: Likewise.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise
+
+2010-01-07 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_ehdr): Handle PN_XNUM.
+ (phnum): New static variable.
+ (process_elf_file): Set it with elf_getphdrnum.
+ (print_phdr): Use phnum instead of EHDR->e_phnum.
+ (print_dynamic, handle_notes): Likewise.
+ (handle_relocs_rel, handle_relocs_rela): Likewise.
+
+ * elfcmp.c (main): Use elf_getshdrnum and elf_getphdrnum.
+
+ * elflint.c (phnum): New static variable.
+ (check_elf_header): Set it, handling PN_XNUM.
+ Use that in place of EHDR->e_phnum throughout.
+ (check_symtab, check_reloc_shdr, check_dynamic): Likewise.
+ (unknown_dependency_p, check_sections, check_program_header): Likewise.
+
+2010-01-05 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (dwarf_attr_string): Match DW_AT_GNU_vector and
+ DW_AT_GNU_template_name.
+
+2010-01-04 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (handle_notes_data): Grab NT_AUXV only for name "CORE".
+ (handle_core_note): Pass NHDR and NAME to ebl_core_note.
+ (handle_core_item): Handle .format of '\n' as \n-separated strings.
+
+ * readelf.c (implicit_debug_sections): New variable.
+ (parse_opt): Set it instead of print_debug_sections for -a.
+ OR them together for print_debug check.
+ (print_debug): OR them together for section check.
+
+ * readelf.c (options): Repartition into set implied by -a and others.
+ Correct -a text to match reality.
+
+ * readelf.c (struct section_argument): Add bool member 'implicit'.
+ (parse_opt): Set it for -a cases, clear it for -x args.
+ (for_each_section_argument): Don't complain about a missing section by
+ name if it's implicit.
+
+2009-11-16 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_string_section): Punt SHT_NOBITS like empty
+ sections, just as dump_data_section already does.
+
+2009-09-21 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (special_sections): Allow MERGE and STRINGS flags to be
+ set for .comment section.
+ Patch by Mark Wielaard <mjw@redhat.com>.
+
+2009-09-08 Roland McGrath <roland@redhat.com>
+
+ * ar.c (main): Fix typo in message format.
+
+2009-08-21 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (attr_callback): Use print_block only when we don't use
+ print_ops.
+
+2009-08-14 Roland McGrath <roland@redhat.com>
+
+ * ar.c (do_oper_extract): Use pathconf instead of statfs.
+
+2009-08-01 Ulrich Drepper <drepper@redhat.com>
+
+ * debugpred.h: Add two most const.
+
+2009-07-26 Mark Wielaard <mjw@redhat.com>
+
+ * elflint.c (check_note_data): Recognize NT_GNU_GOLD_VERSION.
+
+2009-07-25 Mark Wielaard <mjw@redhat.com>
+
+ * Makefile.am (addr2line_LDADD): Add $(libelf).
+
+2009-07-24 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_block): New function.
+ (print_ops): Use it.
+ (attr_callback): Use it for DW_FORM_block* forms.
+
+2009-07-20 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (print_ops): Add handling of DW_OP_implicit_value
+ and DW_OP_stack_value.
+
+2009-07-14 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_elf_header): Allow Linux ABI.
+ (check_symtab): Handle STB_GNU_UNIQUE.
+
+2009-07-08 Mark Wielaard <mjw@redhat.com>
+
+ * readelf.c (attr_callback): Handle DW_Form constants for
+ DW_AT_data_member_location.
+
+2009-07-06 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (register_info): New function. Handle unknown register #s.
+ (print_cfa_program): Use it.
+ (handle_core_register, handle_core_registers): Likewise.
+
+2009-06-28 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_address_names): New static variable.
+ (options, parse_opt): Grok -N/--numeric-addresses to clear it.
+ (format_dwarf_addr): Don't look up name if !print_address_names.
+
+2009-06-13 Ulrich Drepper <drepper@redhat.com>
+
+ * ldgeneric.c: Don't use deprecated libelf functions.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise.
+ * ld.h: Fix up comment.
+
+2009-06-01 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_relocs): Expect ELF header argument and pass on
+ to handle_relocs_rel* functions. Adjust caller.
+ (handle_relocs_rel): Add ELF header argument. Add special case for
+ the IRELATIVE relocations in statically linked executables.
+ (handle_relocs_rela): Likewise.
+
+2009-04-29 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_symtab): Add tests of st_other field.
+
+2009-04-23 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile [BUILD_STATIC] (libdw): Add $(zip_LIBS).
+
+2009-04-20 Roland McGrath <roland@redhat.com>
+
+ * addr2line.c (print_dwarf_function): Honor -s and -A for file names
+ of inline call sites.
+
+ * addr2line.c (just_section): New variable.
+ (adjust_to_section): New function, broken out of ...
+ (handle_address): ... here.
+ (options, parse_opt): Add -j/--section=NAME to set it.
+
+2009-04-15 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_debug_frame_section): Check for DW_CIE_ID_64 in
+ 64-bit format header, DW_CIE_ID_32 in 32-bit format header.
+
+2009-04-14 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_attributes): Treat SHT_ARM_ATTRIBUTES on EM_ARM
+ like SHT_GNU_ATTRIBUTES.
+
+ * readelf.c (handle_core_registers): Fix error message.
+
+ * strip.c (handle_elf: check_preserved): Don't note any change when
+ .debug_data is already filled from a previous pass.
+
+2009-02-05 Ulrich Drepper <drepper@redhat.com>
+
+ * objdump.c (show_relocs_x): Minor cleanups.
+
+ * readelf.c (print_cfa_program): Correct a few labels.
+ Print first DW_CFA_expression and DW_CFA_val_expression parameter
+ as register.
+
+2009-02-01 Ulrich Drepper <drepper@redhat.com>
+
+ * objdump.c (show_relocs_rel, show_relocs_rela): Split common parts
+ into ...
+ (show_relocs_x): ...here. New function.
+ (show_relocs): Better spacing in output.
+
+ * objdump.c (show_relocs_rela): Show offsets as signed values.
+
+ * ar.c (main): Fix recognition of invalid modes for a, b, i modifiers.
+ Improve some error messages.
+ Use program_invocation_short_name instead of AR macro.
+ * Makefile.am (CFLAGS_ar): Remove.
+ * elflint.c (parse_opt): ARGP_HELP_EXIT_ERR does nothing for argp_help.
+ * objdump.c (parse_opt): Likewise.
+ * readelf.c (parse_opt): Likewise.
+
+2009-01-27 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_ops): Notice short length, don't overrun buffer
+ (still need to fix LEB128).
+
+ * readelf.c (print_ops): Fix DW_OP_call[24] decoding.
+
+ * readelf.c (print_ops): Print (empty)\n when LEN == 0.
+
+2009-01-24 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_debug_frame_section): Fix computation of vma_base
+ for PC-relative mode.
+
+2009-01-23 Ulrich Drepper <drepper@redhat.com>
+
+ * size.c (process_file): When handling archive, close file descriptor
+ here. For unknown file format also close file descriptor.
+ (handle_ar): Don't close file descriptor here.
+
+ * readelf.c (parse_opt): Move code to add to dump_data_sections and
+ string_sections list in local function add_dump_section. Adjust 'x'
+ key handling. For 'a' key add .strtab, .dynstr, and .comment section
+ to string_sections list.
+
+2009-01-22 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_phdr): Don't print section mapping when no sections.
+
+ * Makefile.am (AM_CFLAGS): Pass -Wno-format for *_no_Wformat.
+
+ * readelf.c (print_debug_frame_section): Initialize IS_SIGNED to false
+ and reset it only for the 'true' cases.
+
+ * Makefile.am (addr2line_no_Wformat): New variable.
+
+ * readelf.c (print_debug_frame_section): Use t instead of j formats
+ for ptrdiff_t OFFSET.
+
+2009-01-21 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_program_header): Fix typo in .eh_frame_hdr section
+ test. Handle debuginfo files.
+ (check_exception_data): First sanity test.
+
+2009-01-17 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_debug_exception_table): Show target of ar_disp
+ field.
+
+ * elflint.c (check_program_header): Add most consistency checks for
+ PT_GNU_EH_FRAME entry.
+
+ * addr2line.c: Use ARGP_PROGRAM_VERSION_HOOK_DEF and
+ ARGP_PROGRAM_BUG_ADDRESS_DEF.
+ * ar.c: Likewise.
+ * elfcmp.c: Likewise.
+ * elflint.c: Likewise.
+ * findtextrel.c: Likewise.
+ * ld.c: Likewise.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+
+ * size.c: Likewise.
+ * strings.c: Likewise.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise.
+
+2009-01-16 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_program_header): Check that PT_GNU_EH_FRAME entry
+ matches .eh_frame_hdr section, if it is available. Also check that
+ the segment is allocated, not writable, not executable.
+
+ * readelf.c: Add -e option. Dump exception and unwind related
+ sections. Add -e to -a.
+ (print_encoding_base): Handle DW_EH_PE_omit.
+ (print_debug_exception_table): Beginning of support.
+ (print_debug): Hook up print_debug_exception_table for
+ .gcc_except_table sections.
+
+ * readelf.c (print_debug_frame_section): Some fixes for last change.
+
+2009-01-15 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_encoding): Now a toplevel function.
+ (print_relinfo): Likewise.
+ (print_encoding_base): Broken out of print_debug_frame_section.
+ (print_debug_frame_section): Print different header for .eh_frame
+ sections. Fix recognition of matching CIEs in .debug_frame sections.
+ Print absolute offset for PC-relative FDE locations. Don't print
+ table header for FDEs if the table is empty.
+ (read_encoded): New function.
+ (print_debug_frame_hdr_section): New function.
+ (print_debug): Hook up print_debug_frame_hdr_section for .eh_frame_hdr
+ sections.
+
+ * readelf.c (handle_relocs_rel): Print section number.
+ (print_debug_abbrev_section): Likewise.
+ (print_debug_aranges_section): Likewise.
+ (print_debug_ranges_section): Likewise.
+ (print_debug_info_section): Likewise.
+ (print_debug_line_section): Likewise.
+ (print_debug_loc_section): Likewise.
+ (print_debug_macinfo_section): Likewise.
+ (print_debug_pubnames_section): Likewise.
+ (print_debug_str_section): Likewise.
+
+2009-01-10 Ulrich Drepper <drepper@redhat.com>
+
+ * strings.c (read_block): Fix typo in error message string.
+
+2009-01-07 Ulrich Drepper <drepper@redhat.com>
+
+ * ld.c (ld_new_searchdir): Fix adding to search path list.
+
+2009-01-06 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c: Implement call frame debug section dumping.
+
+2009-01-05 Roland McGrath <roland@redhat.com>
+
+ * elfcmp.c: Exit with status 2 for errors (like cmp, diff, grep).
+ Status 1 (aka EXIT_FAILURE) is only for completed OK but not equal.
+
+2009-01-01 Ulrich Drepper <drepper@redhat.com>
+
+ * addr2line.c: Update copyright year.
+ * ar.c: Likewise.
+ * elfcmp.c: Likewise.
+ * elflint.c: Likewise.
+ * findtextrel.c: Likewise.
+ * ld.c: Likewise.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * strings.c: Likewise.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise.
+
+2008-12-11 Roland McGrath <roland@redhat.com>
+
+ * nm.c (sym_name): New function.
+ (show_symbols_sysv): Use it in place of elf_strptr.
+ (show_symbols_bsd, show_symbols_posix): Likewise.
+ Fixes RHBZ#476136.
+
+ * nm.c (show_symbols_sysv): Use an alloca'd backup section name when
+ elf_strptr fails.
+
+2008-12-02 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (count_dwflmod, process_file): Don't presume encoding of
+ nonzero OFFSET argument to dwfl_getmodules.
+
+2008-08-07 Roland McGrath <roland@redhat.com>
+
+ * addr2line.c (main): Pass string to handle_address.
+ (see_one_module): New function, subroutine of handle_address.
+ (find_symbol): Likewise.
+ (handle_address): Take string argument rather than address.
+ Convert plain number, or handle strings like "(section)+offset"
+ or "symbol+offset".
+
+2008-08-01 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (handle_core_item): Handle 'B' type for 1-origin bitset.
+ For 'b' and 'B', print <x-y,z> or ~<x,y-z> rather than 1/0 string.
+
+ * readelf.c (convert): Take new argument SIZE.
+ (handle_core_register, handle_core_item): Update callers.
+ (handle_core_item): Take new arg REPEATED_SIZE.
+ (handle_core_items): Special case for a singleton item,
+ let handle_core_item handle repeats if it wants to.
+
+ * readelf.c (handle_core_items): Give abridged output
+ for identical groups repeated more than twice.
+
+2008-07-04 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (handle_core_items): Handle ELF_T_ADDR.
+
+2008-04-10 Roland McGrath <roland@redhat.com>
+
+ * strip.c (handle_elf): Don't keep sections that kept symbol tables
+ refer to. Instead, just be sure to preserve the original symbol
+ table in the debug file so those symbols go with their sections and
+ can be elided from the stripped version of the symbol table.
+
+ * strip.c (handle_elf): When a discarded section kept in the debug
+ file refers to a nondiscard section via sh_link/sh_info, preserve
+ that nondiscarded section unmodified in the debug file as well.
+ Skip adjustment of discarded sections symbol table references when
+ that symbol table is copied in this way.
+
+ * elflint.c (check_symtab): Don't crash from missing symbol names
+ after diagnosing bogus strtab.
+
+ * strip.c (handle_elf): Cosmetic cleanup in special section contents
+ adjustment for symtab changes.
+
+2008-03-31 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_sections): Add checks on SHF_EXECINSTR sections:
+ must be SHT_PROGBITS, must not be SHF_WRITE. Let backend hook
+ excuse a special section.
+
+2008-03-27 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_sections): Check that executability and writability
+ of sections is reflected in segment p_flags.
+
+2008-03-26 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_program_header): Accept PT_GNU_RELRO p_flags
+ that matches its PT_LOAD's p_flags &~ PF_W. On sparc, PF_X really
+ is valid in RELRO.
+
+2008-02-29 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_attributes): Add a cast.
+ * elflint.c (check_attributes): Likewise.
+
+ * unaligned.h (add_8ubyte_unaligned): Cast PTR argument for parity
+ with [UNALIGNED_ACCESS_CLASS == BYTE_ORDER] definition.
+ (add_4ubyte_unaligned, add_2ubyte_unaligned): Likewise.
+
+2008-02-03 Ulrich Drepper <drepper@redhat.com>
+
+ * i386_ld.c (elf_i386_count_relocations): Implement R_386_TLS_GD
+ when linked into executable.
+ (elf_i386_create_relocations): Likewise.
+
+2008-02-20 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_attributes): New function.
+ (process_elf_file): Call it under -A.
+
+ * elflint.c (check_attributes): Implement it for real.
+
+2008-02-19 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (special_sections): Handle .gnu.attributes section.
+ (check_sections): Likewise.
+ (check_attributes): New function.
+
+2008-02-10 Roland McGrath <roland@redhat.com>
+
+ * elfcmp.c (main): Ignore sh_offset differences in non-SHF_ALLOC
+ sections and ET_REL files.
+
+2008-02-02 Ulrich Drepper <drepper@redhat.com>
+
+ * elf32-i386.script: Add .eh_frame_hdr, .tdata, and .tbss sections.
+ * i386_ld.c (elf_i386_count_relocations): Handle R_386_TLS_LDO_32
+ and R_386_TLS_LE.
+ (elf_i386_create_relocations): Likewise.
+ * ld.h (struct ld_state): Add need_tls, tls_start, and tls_tcb
+ elements.
+ * ldgeneric.c (add_section): If TLS section is used, set need_tls flag.
+ (ld_generic_create_outfile): Add PT_TLS entry to program header.
+ Fix generation of PT_GNU_STACK entry.
+
+2008-02-01 Ulrich Drepper <drepper@redhat.com>
+
+ * ld.c (replace_args): Prevent loop over replacements if the parameter
+ is only two characters long.
+
+ * ld.c: Recognize sha1 argument for --build-id parameter.
+ * ldgeneric.c (create_build_id_section): Handle sha1.
+ (compute_hash_sum): New function. Broken out of compute_build_id.
+ Take hash function and context as parameters.
+ (compute_build_id): Use compute_hash_sum for md5 and the new sha1
+ implementation.
+
+2008-01-31 Ulrich Drepper <drepper@redhat.com>
+
+ * elf32-i386.script: Add .note.ABI-tag and .note.gnu.build-id sections.
+ * ld.c: Recognize --build-id command line parameter.
+ * ld.h: Define scn_dot_note_gnu_build_id.
+ (struct ld_state): Add build_id and buildidscnidx elements.
+ * ldgeneric.c: Implement --build-id command line parameter.
+ * ldlex.l (ID): Recognize - as valid character after the first one.
+
+2008-01-29 Ulrich Drepper <drepper@redhat.com>
+
+ * ld.c (replace_args): New function.
+ (main): Use it to rewrite old-style parameters.
+
+ * elf32-i386.script: Add .gnu.hash section.
+ * ldgeneric.c (optimal_bucket_size): A tiny bit more efficient.
+ (fillin_special_symbol): Initialize st_size.
+ (sortfct_hashval): New function.
+ (create_gnu_hash): New function.
+ (create_hash): New function.
+ (ld_generic_create_outfile): Use the new functions to create the
+ hash tables.
+
+ * elflint.c (check_gnu_hash): Fix index value printed in error message.
+
+2008-01-24 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_group): Check that signature symbol for section
+ group is not an empty string.
+ * ldgeneric.c: Remove magic assignment of indeces in the dynsym
+ section. Start implementation of --hash-style.
+ * i386_ld.c: Likewise.
+ * ld.c: Recognize --hash-style.
+ * ld.h (struct scninfo): Add comdat_group.
+ Add additional parameter to finalize_plt callback.
+
+2008-01-22 Ulrich Drepper <drepper@redhat.com>
+
+ * ld.h (struct callbacks): Add initialize_gotplt.
+ (struct scnhead): Add scn_dot_gotplt.
+ (struct ld_state): Add gotpltscnidx.
+ * i386_ld.c (elf_i386_initialize_plt): Minor optimization.
+ (elf_i386_initialize_pltrel): Likewise.
+ (elf_i386_initialize_got): There is now a separate .got.plt, so
+ don't do the PLT-related work here. Initialize d_type.
+ (elf_i386_initialize_gotplt): New function.
+ (elf_i386_plt0): Use ud2a after indirect jump.
+ (elf_i386_pic_plt0_entry): Likewise.
+ (elf_i386_finalize_plt): Reference now .got.plt.
+ (elf_i386_count_relocations): For GOT entries which need no relocation
+ don't bump nrel_got.
+ (elf_i386_create_relocations): Also get .got.plt. Rewrite R-386_GOT32
+ handling for split .got/.got.plt.
+ (elf_i386_ld_init): Initialize callbacks.initialize_gotplt.
+ * elf32-i386.script: Sort sections for security. There are no .got
+ input sections. Add .got.plt.
+ * ldgeneric.c (ld_generic_generate_sections): Add .got.plt section.
+ (ld_generic_create_outfile): Initialize .got.plt section.
+ Use .got.plt address for _GLOBAL_OFFSET_TABLE_ symbol and DT_PLTGOT.
+
+2008-01-19 Ulrich Drepper <drepper@redhat.com>
+
+ * i386_ld.c (elf_i386_count_relocations): PLT relocations for undefined
+ symbols are not carried over into statically linked output files.
+ Add dummy entries for more TLS relocations.
+
+ * ld.c (options): Add long names for -( and -).
+
+ * ldgeneric.c (check_definition): For newly found definitions don't
+ mark section as used if symbol is absolute.
+ (extract_from_archive): Only assign archive sequence number the first
+ time the archive is handled. Update ld_state.last_archive_used
+ if any symbol was used. Remove nround variable.
+ (file_process2): When using symbol from an archive, update
+ ld_state.group_start_archive, ld_state.archives, and
+ ld_state.tailarchives.
+ (ld_generic_file_process): If group is not handled anymore, after
+ freeing ELF handles for the archives, clear ld_state.archives and
+ *nextp. Fix wrong logic in recognizing first iteration of group
+ loop. When clearing flags, also clear ld_state.group_start_archive.
+
+2008-01-11 Ulrich Drepper <drepper@redhat.com>
+
+ * objdump.c (show_disasm): Adjust disassembler format string for
+ removal of %e.
+
+2008-01-04 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (handle_core_items): Take new arg DESCSZ; if nonzero,
+ a size greater than the items cover means multiple sets of items.
+ (handle_core_note): Update caller.
+
+2008-01-04 Roland McGrath <roland@redhat.com>
+
+ * strip.c (handle_elf): Move SHDRIDX defn to silence gcc warning.
+
+2008-01-03 Roland McGrath <roland@redhat.com>
+
+ * ld.h (linked_from_dso_p): Use __attribute__ ((__gnu_inline__)).
+
+ * elflint.c (check_dynamic): Remove duplicate initializer.
+
+2008-01-02 Ulrich Drepper <drepper@redhat.com>
+
+ * addr2line.c: Update copyright year.
+ * ar.c: Likewise.
+ * elfcmp.c: Likewise.
+ * elflint.c: Likewise.
+ * findtextrel.c: Likewise.
+ * ld.c: Likewise.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * strings.c: Likewise.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise.
+
+2007-12-30 Ulrich Drepper <drepper@redhat.com>
+
+ * objdump (show_disasm): Use %e after third parameter.
+
+2007-12-21 Ulrich Drepper <drepper@redhat.com>
+
+ * strip.c: Fix wrong parenthesis in a few branch predictions.
+ * strings.c: Likewise.
+
+2007-12-20 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.am (DEFS): Add DEBUGPRED.
+ * addr2line.c: Include debugpred.h.
+ * ar.c: Likewise.
+ * elfcmp.c: Likewise.
+ * elflint.c: Likewise.
+ * findtextrel.c: Likewise.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * strings.c: Likewise.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise.
+ * debugpred.h: New file.
+
+ * readelf.c (handle_relocs_rel): Use elf_scnshndx.
+ (handle_relocs_rela): Likewise.
+
+ * readelf.c: Add lots of likely/unlikely.
+
+ * elflint.c: Minor cleanups.
+
+2007-11-19 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_ops): Handle all bad op codes gracefully.
+ Print their numbers instead of just ???.
+
+2007-11-09 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (attr_callback): Handle DW_AT_data_location.
+ Handle block forms to mean a DWARF expression for DW_AT_allocated,
+ DW_AT_associated, DW_AT_bit_size, DW_AT_bit_offset, DW_AT_bit_stride,
+ DW_AT_byte_size, DW_AT_byte_stride, DW_AT_count, DW_AT_lower_bound,
+ DW_AT_upper_bound.
+
+2007-10-20 Roland McGrath <roland@redhat.com>
+
+ * unstrip.c (options): Update -R description.
+ (struct symbol): Put symbol details a union with a size_t pointer
+ `duplicate'.
+ (compare_symbols_output): Use null ->name as marker for discard
+ symbols, not zero *->map.
+ (copy_elided_sections): Record forwarding pointers for discarded
+ duplicates and fill SYMNDX_MAP elements through them.
+
+ * readelf.c (process_file): Set offline_next_address to 0 at start.
+ (struct process_dwflmod_args): New type.
+ (process_dwflmod): Take args in it, pass fd to process_elf_file.
+ (process_file): Update caller; dup FD for passing to libdwfl.
+ (process_elf_file): Take new arg FD. For ET_REL file when
+ displaying data affected by libdwfl relocation, open a new Elf handle.
+
+2007-10-17 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_debug_line_section): For invalid data inside a
+ unit with plausible length, keep printing at the next unit boundary.
+
+ * readelf.c (attr_callback): Use dwarf_formref_die, not dwarf_formref.
+
+2007-10-16 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (hex_dump): Fix rounding error in whitespace calculation.
+
+2007-10-15 Roland McGrath <roland@redhat.com>
+
+ * make-debug-archive.in: New file.
+ * Makefile.am (EXTRA_DIST): Add it.
+ (make-debug-archive): New target.
+ (bin_SCRIPTS, CLEANFILES): Add it.
+
+2007-10-10 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (special_sections): Add new attrflag value exact_or_gnuld.
+ Use it to check MERGE|STRINGS for .debug_str.
+ (check_sections): Handle exact_or_gnuld.
+
+2007-10-08 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (handle_core_item): Handle 'T'|0x80 to indicate
+ 64-bit struct timeval with 32-bit tv_usec.
+
+2007-10-07 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (check_archive_index): New function.
+ (process_file): Call it. Change signature to take only fd and name.
+ Use libdwfl to open the file, then iterate on its modules (multiple
+ for an archive) to print file name and call process_elf_file.
+ (main): Update caller. Let process_file do elf_begin.
+ (count_dwflmod, process_dwflmod, find_no_debuginfo): New functions.
+ (process_elf_file): Take only Dwfl_Module * argument.
+ Don't print the file name here.
+ (print_debug_*_section): Take Dwfl_Module * argument.
+ (print_debug): Likewise. Update caller.
+ (format_dwarf_addr): New function.
+ (print_debug_ranges_section): Use it.
+ (attr_callback): Likewise.
+ (print_debug_line_section, print_debug_loc_section): Likewise.
+
+ * readelf.c (print_debug_ranges_section): Translate all strings.
+ (print_debug_loc_section): Likewise.
+
+ * unstrip.c (copy_elided_sections): Initialize SEC.
+
+ * ar.c (do_oper_insert): Put trailing / on short names.
+
+ * arlib.h (MAX_AR_NAME_LEN): Decrease by one.
+
+ * arlib2.c (arlib_add_long_name): Adjust for header size.
+
+ * arlib.c (arlib_finalize): Pad long name table to keep size even.
+
+ * ar.c (do_oper_insert): Use write_retry for padding write.
+
+ * ar.c (do_oper_insert): Initialize CUR_OFF in no_old case.
+ Unconditionally set FOUND[CNT]->elf when setting ->mem.
+ (remember_long_name): New function.
+ (do_oper_insert): Call it. Correctly use length of basename,
+ not original name. Don't store long name twice for new member.
+
+2007-10-06 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_note): Skip empty segment.
+ (check_note_section): Skip empty section.
+
+ * unstrip.c (options, parse_opt, struct arg_info): Grok -R/--relocate.
+ (handle_output_dir_module, handle_implicit_modules): Pass it down.
+ (handle_dwfl_module): When set, use ET_REL already loaded by Dwfl.
+ (compare_alloc_sections): Take new arg REL, ignore address if true.
+ (compare_sections): Likewise, pass it down.
+ (compare_sections_rel, compare_sections_nonrel): New functions.
+ (find_alloc_sections_prelink, copy_elided_sections): Use them
+ instead of compare_sections.
+ (sections_match): New function, broken out of ...
+ (find_alloc_section): ... here.
+ (copy_elided_sections): Reorganize section match-up logic.
+ Use sections_match for SHF_ALLOC in ET_REL.
+ For ET_REL, let the nonzero sh_addr from the debug file dominate.
+
+ * unstrip.c (add_new_section_symbols): Take new arg REL.
+ When true, do not update section symbol values.
+ (collect_symbols): Likewise. Update section symbols with address
+ of chosen output section, not original section.
+ (check_symtab_section_symbols, copy_elided_sections): Update callers.
+
+ * unstrip.c (compare_alloc_sections): At the same address, preserve
+ original section order.
+
+ * elflint.c (special_sections): Don't require MERGE|STRINGS for
+ .debug_str, it didn't always have them with older tools.
+
+ * elflint.c (check_symtab, check_one_reloc): Ignore sh_addr in ET_REL.
+
+2007-10-05 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_symtab): Allow SHN_UNDEF _GLOBAL_OFFSET_TABLE_ in
+ ET_REL file.
+
+ * elflint.c (check_symtab): For _GLOBAL_OFFSET_TABLE_, diagnose
+ SHN_UNDEF as "bad section". Use shndx value in messages.
+
+ * elflint.c (special_sections): Add ".debug_str". Decrement namelen
+ for ".debug" so it matches as a prefix.
+ (IS_KNOWN_SPECIAL): New macro.
+ (check_sections): Use it for ".plt" match. Cite wrong SHT_NOBITS
+ type even under -d, for a .debug* or .shstrtab section.
+
+ * readelf.c (print_ops): Use hex for address operand.
+
+2007-10-04 Roland McGrath <roland@redhat.com>
+
+ * unstrip.c (copy_elided_sections): Initialize NDX_SECTION element for
+ .gnu_debuglink section to SHN_UNDEF. Drop STT_SECTION symbols for
+ sections mapped to SHN_UNDEF.
+
+2007-10-04 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (dump_archive_index): Avoid warning about uninitialized
+ variable with older glibc versions.
+ Add some branch prediction.
+
+2007-10-04 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_archive_index): New variable.
+ (options, parse_opt): Accept -c/--archive-index to set it.
+ (dump_archive_index): New function.
+ (process_file): Take new arg WILL_PRINT_ARCHIVE_INDEX.
+ Call dump_archive_index on archives if set.
+ (main): Update caller.
+ (any_control_option): Give it file scope, moved out of ...
+ (parse_opt): ... here.
+
+2007-10-03 Roland McGrath <roland@redhat.com>
+
+ * unstrip.c (struct arg_info): Add `list' flag.
+ (options, parse_opt): Grok -n/--list to set it.
+ (list_module): New function.
+ (handle_implicit_modules): Call it under -n.
+
+ * elflint.c (check_note_section): New function.
+ (check_sections): Call it for SHT_NOTE.
+
+ * readelf.c (handle_notes): Use sections when available.
+
+ * elflint.c (check_note_data): New function, broken out of ...
+ (check_note): ... here. Call it and elf_getdata_rawchunk.
+
+ * readelf.c (handle_auxv_note): Take offset as argument, not buffer.
+ Use elf_getdata_rawchunk and gelf_getauxv.
+ (handle_notes_data): New function, broken out of ...
+ (handle_notes): ... here. Call it and elf_getdata_rawchunk.
+
+2007-10-01 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (hex_dump): Fix transposed subtraction generating spaces.
+
+ * readelf.c (hex_dump): Fix line header to be hex instead of decimal.
+
+2007-09-10 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (options): Give -p optional argument, alias --string-dump.
+ (string_sections, string_sections_tail): New static variables.
+ (parse_opt): Set them when -p has an argument.
+ (print_string_section): New function, broken out of ...
+ (print_strings): ... here. Call it.
+ (dump_data_section): New function, broken out of ...
+ (dump_data): ... here. Call it.
+ (for_each_section_argument): New function, broken out of ...
+ (dump_data): ... here. Call it.
+ (dump_strings): New function.
+
+2007-08-31 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_strings): Typo fix.
+
+2007-08-23 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (printf_with_wrap): Function removed.
+ (REGISTER_WRAP_COLUMN): New macro.
+ (handle_core_register): Use print_core_item instead.
+ (struct register_info): New type.
+ (compare_registers, compare_register_sets): New functions.
+ (register_bitpos, compare_sets_by_info): New functions.
+ (handle_core_registers): Use those to segregate and sort registers
+ for display.
+
+ * readelf.c (ITEM_WRAP_COLUMN): New macro.
+ (print_core_item): New function.
+ (handle_core_item): Use it instead of printf_with_wrap.
+ (compare_core_items, compare_core_item_groups): New functions.
+ (handle_core_items): Use them. Sort by group and force line breaks
+ between groups.
+
+ * readelf.c (handle_core_registers, handle_core_items): New functions,
+ broken out of ...
+ (handle_core_note): ... here. Call them.
+
+2007-08-22 Roland McGrath <roland@redhat.com>
+
+ * unstrip.c (new_shstrtab): New function, broken out of ...
+ (copy_elided_sections): ... here.
+
+2007-08-20 Roland McGrath <roland@redhat.com>
+
+ Avoid local function trampolines in nm binary.
+ * nm.c (sort_by_address): Move to a static function instead of local
+ inside show_symbols.
+ (sort_by_name_strtab): New static variable.
+ (sort_by_name): Use it. Move to a static function instead of local
+ inside show_symbols.
+ (show_symbols): Set sort_by_name_strtab.
+
+2007-08-19 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (handle_auxv_note): New function.
+ (handle_notes): Call it.
+
+ * readelf.c (printf_with_wrap, convert): New functions.
+ (handle_core_item, (handle_core_register): New functions.
+ (handle_notes): Call those with details from ebl_core_note.
+
+2007-08-12 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_note): Accept type 0 with name "Linux".
+
+ * elflint.c (special_sections): Accept SHF_ALLOC for ".note".
+
+ * elflint.c (section_flags_string): Return "none" for 0, not "".
+
+2007-08-11 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_note): Accept NT_GNU_HWCAP, NT_GNU_BUILD_ID.
+
+2007-08-04 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (hex_dump): Use isprint to determine whether to print
+ character itself or full stop character.
+ (dump_data): No need to check endp for NULL after strtol call.
+
+2007-08-03 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_string_sections): New variable.
+ (options, parse_opt): Handle --strings/-p to set it.
+ (print_strings): New function.
+ (process_elf_file): Call it under -p.
+
+ * readelf.c (options): Add hidden aliases --segments, --sections,
+ as taken by binutils readelf.
+
+2007-08-01 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (dump_data_sections, dump_data_sections_tail):
+ New variables.
+ (options, parse_opt): Handle --hex-dump/-x, set them.
+ (hex_dump): New function.
+ (dump_data): New function, call it.
+ (process_elf_file): Call it.
+
+2007-07-25 Roland McGrath <roland@redhat.com>
+
+ * addr2line.c (show_symbols): New variable.
+ (print_addrsym): New function.
+ (handle_address): Call it.
+ (options, parse_opt): Handle -S/--symbols.
+
+2007-06-05 Ulrich Drepper <drepper@redhat.com>
+
+ * addr2line.c: Update for latest autoconf header.
+ * ar.c: Likewise.
+ * elfcmp.c: Likewise.
+ * elflint.c: Likewise.
+ * findtextrel.c: Likewise.
+ * ld.c: Likewise.
+ * ldgeneric.c: Likewise.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * strings.c: Likewise.
+ * strip.c: Likewise.
+ * unstrip.c: Likewise.
+
+2007-05-18 Roland McGrath <roland@redhat.com>
+
+ * unstrip.c (copy_elided_sections): Match up non-NOBITS sections with
+ stripped file, so as not to duplicate a section copied in both.
+
+ * strip.c (handle_elf): Keep SHT_NOTE section copies in the debug file.
+
+2007-05-17 Roland McGrath <roland@redhat.com>
+
+ * unstrip.c (copy_elided_sections): Don't call gelf_newphdr for 0.
+
+ * unstrip.c (handle_file): Tweak BIAS != 0 warning.
+
+ * unstrip.c (handle_file): Take new arg CREATE_DIRS. If set,
+ call make_directories here.
+ (handle_explicit_files): Take new arg CREATE_DIRS, pass it down.
+ (handle_dwfl_module): Likewise.
+ (handle_implicit_modules): Update callers.
+ (handle_output_dir_module): Likewise. Don't do make_directories here.
+
+ * unstrip.c (get_section_name): New function, broken out of ...
+ (copy_elided_sections): here. Update callers.
+ (find_alloc_section): Broken out of ...
+ (copy_elided_sections): ... here. Update caller.
+ (symtab_count_leading_section_symbols): Take new arg NEWSYMDATA,
+ update STT_SECTION symbols' st_value fields as a side effect.
+ (check_symtab_section_symbols): Update caller.
+ (add_new_section_symbols): Set st_value in symbols added.
+ (collect_symbols): Reset S->value for STT_SECTION symbols recorded.
+ Take new arg SPLIT_BSS. Adjust S->shndx recorded for symbols moved
+ from .bss to .dynbss.
+ (find_alloc_sections_prelink): New function. Associate debug file
+ allocated SHT_NOBITS shdrs with stripped moved by prelink via
+ .gnu.prelink_undo information.
+ (copy_elided_sections): Call it when we couldn't find every allocated
+ section. Don't use a debug file non-NOBITS section if SHF_ALLOC.
+ Take STRIPPED_EHDR arg instead of E_TYPE and PHNUM.
+ (handle_file): Update callers.
+
+ * unstrip.c (copy_elided_sections): Ignore unfound unallocated section
+ named ".comment".
+
+ * elflint.c (check_sections): Fix association of segments with
+ sections when p_memsz > p_filesz.
+
+2007-04-29 Roland McGrath <roland@redhat.com>
+
+ * addr2line.c (options, main): Tweak argp group settings to fix
+ usage output.
+
+2007-04-28 Roland McGrath <roland@redhat.com>
+
+ * strip.c (handle_elf): Update debug file's SHT_NOBITS sections'
+ sizes to match sections adjusted in the stripped file.
+
+2007-04-24 Roland McGrath <roland@redhat.com>
+
+ * elfcmp.c (OPT_HASH_INEXACT): New macro.
+ (hash_inexact): New variable.
+ (options, parse_opt): Add --hash-inexact option to set it.
+ (hash_content_equivalent): New function.
+ (main): Call it for differing SHT_HASH sections under --hash-inexact.
+
+2007-04-23 Roland McGrath <roland@redhat.com>
+
+ * unstrip.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add it.
+ (unstrip_LDADD): New variable.
+
+ * strip.c (options): Allow --output for -o.
+
+2007-02-15 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c: Remove unused code. Add a few consts.
+
+2007-02-15 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_debug): Fix brainos in SHDR test.
+
+2007-02-05 Roland McGrath <roland@redhat.com>
+
+ * ar.c: Include <limits.h>, since we use LONG_MAX.
+
+2007-02-05 Ulrich Drepper <drepper@redhat.com>
+
+ * ar.c: Add ugly hack to work around gcc complaining that we
+ ignore fchown's return value.
+ (do_oper_insert): Handle error when writing padding.
+ * ranlib.c: Add fchown complain work around.
+
+ * arlib.c: Make symtab a global variable. Change all users.
+ * arlib2.c: Likewise.
+ * ranlib.c: Likewise.
+ * ar.c: Likewise.
+ * arlib.h: Declare it.
+
+2007-01-11 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_sections): Use ebl_machine_section_flag_check on
+ SHF_MASKPROC bits separately from generic sh_flags validation.
+
+2007-02-04 Ulrich Drepper <drepper@redhat.com>
+
+ * ar.c: New file.
+ * arlib.c: New file.
+ * arlib2.c: New file.
+ * arlib.h: New file.
+ * Makefile (noinst_LIBRARIES): Add libar.
+ (libar_a_SOURCES): Define.
+ (ar_LDADD): Define.
+ (CFLAGS_ar): Define.
+ * ranlib.c: Change to use arlib.
+
+ * elflint.c (check_symtab): Work around GNU ld bug which omits
+ sections but not symbols in those sections.
+
+2007-01-10 Ulrich Drepper <drepper@redhat.com>
+
+ * addr2line.c: Update copyright year.
+ * elfcmp.c: Likewise.
+ * elflint.c: Likewise.
+ * findtextrel.c: Likewise.
+ * ld.c: Likewise.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * strings.c: Likewise.
+ * strip.c: Likewise.
+
+2006-12-09 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (compare_hash_gnu_hash): New function. Report if the
+ two hash tables have different content (module expected omission
+ of undefined symbols).
+
+2006-10-31 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_program_header): Don't complain about
+ p_filesz > p_memsz if p_memsz is zero and p_type is PT_NOTE.
+
+2006-09-19 Jakub Jelinek <jakub@redhat.com>
+
+ * strip.c (process_file): Disallow -f on archives.
+
+2006-10-09 Roland McGrath <roland@redhat.com>
+
+ * Makefile.am (libld_elf_i386.so): Use $(LINK), not $(CC).
+
+2006-08-29 Roland McGrath <roland@redhat.com>
+
+ * Makefile.am (MAINTAINERCLEANFILES): New variable.
+
+ * readelf.c (handle_relocs_rel): Typo fix, test DESTSHDR properly.
+ Reported by Christian Aichinger <Greek0@gmx.net>.
+
+ * elflint.c (valid_e_machine): Add EM_ALPHA.
+ Reported by Christian Aichinger <Greek0@gmx.net>.
+
+2006-08-08 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_dynamic): Don't require DT_HASH for DT_SYMTAB.
+ Keep track of which "high DT" entries are present.
+ Check that either old or GNU-style hash table is present.
+ If GNU-style hash table is used a symbol table is mandatory.
+ Check that if any prelink entry is present all of them are.
+ (check_gnu_hash): Only fail for undefined symbols in GNU-style hash
+ table if they don't refer to functions.
+
+2006-07-17 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (struct version_namelist): Use GElf_Versym for `ndx' field.
+ (add_version): Likewise for argument.
+ (check_versym): Cast constant to GElf_Versym for comparison.
+
+2006-07-12 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (handle_gnu_hash): Add casts for machines where
+ Elf32_Word != unsigned int.
+
+2006-07-12 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_sysv_hash64): Fix printf format.
+
+2006-07-11 Roland McGrath <roland@redhat.com>
+
+ * addr2line.c (options): English fix in -f doc string.
+
+ * addr2line.c (use_comp_dir): New variable.
+ (options, parse_opt): Grok -A/--absolute to set it.
+ (handle_address): If set, prepend dwfl_line_comp_dir results to
+ relative file names.
+
+2006-07-06 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c: Adjust for latest new hash table format.
+ * readelf.c: Likewise.
+
+ * elflint.c (check_versym): Ignore hidden bit when comparing version
+ numbers.
+
+2006-07-05 Ulrich Drepper <drepper@redhat.com>
+
+ * ldgeneric.c (ld_generic_create_outfile): Correctly recognize
+ discarded COMDAT symbols.
+
+ * i386_ld.c (elf_i386_count_relocations): Lot of corrections.
+ (elf_i386_create_relocations): Likewise.
+ * ld.h (struct symbol): Add local and hidden bits.
+ * ld.c (create_special_section_symbol): These synthsized symbols
+ are local and hidden.
+ * ldgeneric.c (file_process2): Check whether input file matches
+ the emulation.
+ (fillin_special_symbol): Create symbols as local and/or hidden
+ if requested.
+ (ld_generic_create_outfile): Make local copy of symbol.
+ Don't hide global, defined symbols in dynamic symbol table unless
+ requested. Synthetic symbols have no version information.
+
+ * elflint.c: Add support for checking 64-bit SysV-style hash tables.
+ * readelf.c: Add support for printing 64-bit SysV-style hash tables.
+
+2006-07-04 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (is_rel_dyn): Fix and extend DT_RELCOUNT/DT_RELACOUNT
+ testing.
+
+2006-07-03 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c: Add testing of DT_GNU_HASH.
+ * readelf.c: Implement showing histogram for DT_GNU_HASH section.
+
+ * Makefile.am: Add hacks to create dependency files for non-generic
+ linker.
+
+2006-06-12 Ulrich Drepper <drepper@redhat.com>
+
+ * ldgeneric.c (ld_generic_generate_sections): Don't create .interp
+ section if creating a DSO and no interpreter is given.
+ (ld_generic_create_outfile): Don't store reference to symbols in
+ discarded COMDAT groups. Don't create PHDR and INTERP program header
+ for DSO if no interpreter is specified.
+ (create_verneed_data): Pretty printing.
+
+ * ldscript.y (content): If a DSO is created don't set default
+ interpreter from linker script.
+
+ * i386_ld.c (elf_i386_count_relocations): Do not add relocations
+ for symbols in discarded COMDAT groups.
+ (elf_i386_create_relocations): Likewise.
+ * ld.h (struct scninfo): Add unused_comdat.
+ * ldgeneric.c (add_section): Also check group signature when
+ matching COMDAT sections.
+ (add_relocatable_file): Ignore symbols in COMDAT group which are
+ discarded.
+
+ * elflint.c (check_one_reloc): For *_NONE relocs only check type
+ and symbol reference.
+
+2006-06-11 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_dynamic): Fix checking value of tags which are
+ offsets in the string section. Make sure DT_STRTAB points to the
+ section referenced in sh_link.
+
+ * ld.c (options): Add headers. Add short option 'R' for '--rpath'.
+
+ * ld.c: Recognize --eh-frame-hdr option.
+ * ld.h (struct ld_state): Add eh_frame_hdr field.
+ * ldgeneric.c (struct unw_eh_frame_hdr): Define.
+
+ * ldgeneric.c (add_section): Use ebl_sh_flags_combine instead of
+ SH_FLAGS_COMBINE.
+ (add_relocatable_file): Minor optimization of last change.
+ (match_section): Don't preserve SHF_GROUP flag any longer.
+
+2006-06-10 Ulrich Drepper <drepper@redhat.com>
+
+ * ld.c (parse_z_option): Recognize execstack and noexecstack.
+ Handle record and ignore as position dependent options.
+ (parse_z_option_2): Handle ignore and record here.
+ * ld.h (struct ld_state): Add execstack field.
+ * ldgeneric.c (add_relocatable_file): Recognize .note.GNU-stack
+ sections.
+ (ld_generic_create_outfile): Fix program header creation in native
+ linker. Add PT_GNU_STACK program header.
+
+2006-06-09 Ulrich Drepper <drepper@redhat.com>
+
+ * i386_ld.c (elf_i386_finalize_plt): Don't change symbol table entries
+ for PLT entries if there is no local definition.
+
+ * ld.c (parse_option): Handle -z ignore like --as-needed and
+ -z record like --no-as-needed.
+ * ld.h (struct ld_state): Remove ignore_unused_dsos field.
+ * ldgeneric.c (new_generated_scn): Always compute ndt_needed by
+ looping over DSOs. When deciding about adding DT_NEEDED entries
+ use ->as_needed instead of ignore_unused_dsos.
+
+2006-05-31 Ulrich Drepper <drepper@redhat.com>
+
+ * ld.c: Recognize --as-needed and --no-as-needed options.
+ * ld.h (struct usedfile): Add as_needed field.
+ (struct ld_state): Likewise.
+ * ldgeneric.c (ld_handle_filename_list): Copy as_needed flag from
+ the list.
+ * ldscript.y (filename_id_list): Split to correctly parse all
+ combinations.
+ (mark_as_needed): Fix loop.
+
+2006-05-28 Ulrich Drepper <drepper@redhat.com>
+
+ * addr2line.c (print_dwarf_function): Use unsigned type for lineno
+ and colno.
+
+2006-05-27 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (handle_relocs_rela): Better notations for addon value.
+ (print_ehdr): Distinguish e_ident[EI_VERSION] from e_version.
+
+2006-04-04 Ulrich Drepper <drepper@redhat.com>
+
+ * addr2line.c: Update copyright year.
+ * elfcmp.c: Likewise.
+ * elflint.c: Likewise.
+ * findtextrel.c: Likewise.
+ * ld.c: Likewise.
+ * nm.c: Likewise.
+ * objdump.c: Likewise.
+ * ranlib.c: Likewise.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * strings.c: Likewise.
+ * strip.c: Likewise.
+
+2006-03-09 Roland McGrath <roland@redhat.com>
+
+ * Makefile.am (AM_LDFLAGS): New variable.
+
+2006-03-01 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (dwarf_tag_string, dwarf_attr_string): Update name tables
+ for dwarf.h changes matching 3.0 spec.
+ (dwarf_encoding_string, dwarf_lang_string, print_ops): Likewise.
+
+2005-12-04 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_one_reloc): If relocation section is not loaded,
+ don't check whether the relocations modify read-only sections or
+ loaded and unloaded sections.
+
+2005-11-28 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_one_reloc): Take additional parameters. Use
+ them to determine whether relocation is valid in this type of
+ file. DSOs and executables can contain relocation sections in
+ unloaded sections which just show the relocations the linker
+ applied. Adjust all callers.
+ (check_program_header): Check that PT_PHDR is loaded and that offset
+ matches the one in the ELF header.
+
+2005-10-26 Roland McGrath <roland@redhat.com>
+
+ * nm.c (get_var_range): dwarf_getloclist -> dwarf_getlocation.
+
+2005-09-03 Ulrich Drepper <drepper@redhat.com>
+
+ * strip.c (handle_elf): Unify some error messages.
+ * ld.c (main): Likewise.
+ * ldgeneric.c (open_elf): Likewise.
+ * elfcmp.c (main): Likewise.
+ * elflint.c (check_elf_header): Likewise.
+
+ * size.c (process_file): Fix typo in error message.
+
+ * readelf.c: Lots of little cleanups. Use _unlocked functions.
+
+2005-09-02 Ulrich Drepper <drepper@redhat.com>
+
+ * strings.c (main): Reset elfmap variable after munmap call.
+ [_MUDFLAP] (map_file): Simplify mudflap debugging by not using mmap.
+
+2005-08-28 Ulrich Drepper <drepper@redhat.com>
+
+ * ranlib.c: Don't define pread_retry and write_retry here.
+
+ * Makefile.an [BUILD_STATIC] (libdw): Add -ldl.
+ (CLEANFILES): Add *.gcno *.gcda *.gconv.
+
+ * strings.c (process_chunk): Reorder expressions in conditional
+ (process_chunk_mb): Likewise.
+
+ * strings.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add strings.
+ (strings_no_Wstring): Define.
+ (strings_LDADD): Define.
+
+2005-08-27 Roland McGrath <roland@redhat.com>
+
+ * addr2line.c (dwarf_diename_integrate): Function removed.
+ (print_dwarf_function): Use plain dwarf_diename.
+
+2005-08-24 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_versym): Versioned symbols should not have
+ local binding.
+
+2005-08-15 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_versym): Allow VER_NDX_LOCAL symbols to be
+ undefined.
+
+ * Makefile.am: Add rules to build ranlib.
+ * ranlib.c: New file.
+
+2005-08-14 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_sections): Use ebl_section_type_name and allow any
+ sh_type it recognizes.
+
+ * elflint.c (check_sections): Print unknown flags in hex, don't
+ truncate high bits. Print section number and name for unknown type.
+
+2005-08-13 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_program_header): Use ebl_segment_type_name and
+ allow any p_type it recognizes. Include p_type value in error
+ message for unknown type.
+
+2005-08-13 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_symtab): Simplify last change a bit. Pass ehdr
+ to ebl_check_special_symbol.
+ (check_sections): Pass ehdr to ebl_bss_plt_p.
+
+2005-08-12 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (check_symtab): Check that _GLOBAL_OFFSET_TABLE_ st_shndx
+ refers to the right section if it's not SHN_ABS.
+ Let ebl_check_special_symbol override _G_O_T_ value and size checks.
+
+ * elflint.c (check_sections): Don't complain about a non-NOBITS
+ section taking no segment space, if it's sh_size is 0.
+
+ * elflint.c (check_sections): Use ebl_bss_plt_p to see if .plt should
+ be PROGBITS or NOBITS.
+
+ * elflint.c (check_symtab): Use ebl_check_special_symbol to override
+ standard st_value and st_size checks.
+
+2005-07-28 Roland McGrath <roland@redhat.com>
+
+ * addr2line.c (options, parse_opt): Don't handle -e here.
+ (executable): Variable removed.
+ (argp_children): New static variable.
+ (argp): Use it. Make const.
+ (main): Fill in argp_children from dwfl_standard_argp ().
+ Let libdwfl handle file selection, pass Dwfl handle to handle_address.
+ (print_dwarf_function): New function. Try to figure out inline chain.
+ (elf_getname): Function removed, libdwfl does it for us.
+ (handle_address): Take Dwfl handle instead of Elf, Dwarf handles.
+ Use dwfl_module_addrname instead of elf_getname.
+ Use dwfl_module_getsrc and dwfl_lineinfo instead of libdw calls.
+ * Makefile.am (INCLUDES): Add libdwfl directory to path.
+
+2005-08-10 Ulrich Drepper <drepper@redhat.com>
+
+ * strip.c (parse_opt): STATE parameter is now used.
+ Various little cleanups.
+
+ * readelf.c (print_debug_line_section): Correct fallout of renaming
+ of DW_LNS_set_epilog_begin.
+
+2005-08-08 Roland McGrath <roland@redhat.com>
+
+ * strip.c (options, parse_opt): Grok -R .comment for compatibility
+ with binutils strip. Likewise -d, -S, as aliases for -g.
+ Likewise ignore -s/--strip-all.
+
+2005-08-07 Roland McGrath <roland@redhat.com>
+
+ * strip.c (process_file): Open read-only when using a different output
+ file.
+
+2005-08-06 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (in_nobits_scn): New function.
+ (check_versym): Allow references for defined symbols against versions
+ of other DSOs also for symbols in nobits sections.
+ Move a few variables around.
+
+ * Makefile.am (AM_CFLAGS): Avoid duplication.
+ Link with statis libs if BUILD_STATIC.
+
+2005-08-05 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c: Many, many more tests. Mostly related to symbol
+ versioning. Those sections should now be completely checked.
+
+ * readelf.c (print_dynamic): Use gelf_offscn.
+
+2005-08-04 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c: Add lots more tests: more extension symbol table sanity,
+ versioning section tests, hash table tests. General cleanup.
+
+2005-08-02 Ulrich Drepper <drepper@redhat.com>
+
+ * objdump.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add objdump.
+ (objdump_LDADD): Define.
+
+ * elflint.c (check_reloc_shdr): New function split out from check_rela
+ and check_rel.
+ (check_one_reloc): New function. Likewise.
+ (check_rela): Use check_reloc_shdr and check_one_reloc.
+ (check_rel): Likewise.
+ (check_program_header): Check that PT_DYNAMIC entry matches .dynamic
+ section.
+ Add checks that relocations against read-only segments are flagged,
+ that the text relocation flag is not set unnecessarily, and that
+ relocations in one section are either against loaded or not-loaded
+ segments.
+
+2005-08-01 Ulrich Drepper <drepper@redhat.com>
+
+ * elfcmp.c (main): Ignore section count and section name string table
+ section index.
+
+2005-07-27 Roland McGrath <roland@redhat.com>
+
+ * elfcmp.c: Include <locale.h>.
+
+2005-07-27 Ulrich Drepper <drepper@redhat.com>
+
+ * elfcmp.c: Print name and index of differing section.
+
+2005-07-24 Ulrich Drepper <drepper@redhat.com>
+
+ * elfcmp.c: Implement comparing gaps between sections.
+
+2005-07-23 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c: Include libeblP.h instead of libebl.h.
+ * nm.c: Likewise.
+ * readelf.c: Likewise.
+ * elfcmp.c: Likewise.
+
+ * elfcmp.c (main): Compare individual ELF header fields, excluding
+ e_shoff instead of the whole struct at once.
+ Use ebl_section_strip_p instead of SECTION_STRIP_P.
+ * strip.c: Use ebl_section_strip_p instead of SECTION_STRIP_P.
+
+2005-07-22 Ulrich Drepper <drepper@redhat.com>
+
+ * elfcmp.c (main): Take empty section into account when comparing
+ section content.
+
+ * elflint.c (check_dynamic): Check that d_tag value is >= 0 before
+ using it.
+
+2005-07-21 Ulrich Drepper <drepper@redhat.com>
+
+ * elfcmp.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add elfcmp.
+ (elfcmp_LDADD): Define.
+
+ * elflint.c (check_rela): Check that copy relocations only reference
+ object symbols or symbols with unknown type.
+ (check_rel): Likewise.
+
+2005-06-08 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_ops): Add consts.
+
+2005-05-31 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_debug_abbrev_section): Don't bail after first CU's
+ abbreviations. Print a header line before each CU section.
+
+ * readelf.c (print_debug_loc_section): Fix indentation for larger
+ address size.
+
+2005-05-30 Roland McGrath <roland@redhat.com>
+
+ * readelf.c (print_debug_line_section): Print section offset of each
+ CU's table, so they are easy to find from seeing the stmt_list value.
+
+ * readelf.c (dwarf_attr_string): Add all attributes in <dwarf.h>.
+ (attr_callback): Grok DW_AT_ranges and print offset in hex.
+
+ * readelf.c (attr_callback): Add 2 to addrsize * 2 for %#0* format.
+ (print_debug_ranges_section, print_debug_loc_section): Likewise.
+
+ * readelf.c (print_ops): Take different args for indentation control.
+ (attr_callback): Caller updated.
+ Grok several more block-form attributes as being location expressions.
+ For those same attributes with udata forms, format output differently
+ for location list offset.
+ (print_debug_loc_section): Implement it for real.
+
+ * readelf.c (options): Mention ranges for --debug-dump.
+ (enum section_e): Add section_ranges.
+ (parse_opt): Grok "ranges" for -w/--debug-dump.
+ (print_debug_ranges_section): New function.
+ (print_debug): Handle .debug_ranges section.
+
+2005-05-30 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (handle_notes): At least x86-64 need not have the note
+ section values aligned to 8 bytes.
+
+2005-05-18 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (dwarf_tag_string): Add new tags.
+
+2005-05-08 Roland McGrath <roland@redhat.com>
+
+ * strip.c (handle_elf): Don't translate hash and versym data formats,
+ elf_getdata already did it for us.
+
+2005-05-07 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.am (findtextrel_LDADD): Add $(libmudflap).
+ (addr2line_LDADD): Likewise.
+
+2005-05-03 Roland McGrath <roland@redhat.com>
+
+ * strip.c (handle_elf): Apply symbol table fixups to discarded
+ relocation sections when they are being saved in the debug file.
+
+ * strip.c (handle_elf): Pass EHDR->e_ident[EI_DATA] to gelf_xlatetom
+ and gelf_xlatetof, not the native byte order.
+
+ * strip.c (parse_opt): Give error if -f or -o is repeated.
+ (main): Exit if argp_parse returns nonzero.
+
+ * strip.c (debug_fname_embed): New variable.
+ (options, parse_opt): New option -F to set it.
+
+2005-05-07 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (parse_opt): Make any_control_option variable
+ local. Simplify some tests.
+
+2005-05-03 Roland McGrath <roland@redhat.com>
+
+ * strip.c (crc32_file): Function removed (now in ../lib).
+
+2005-05-03 Roland McGrath <roland@redhat.com>
+
+ * elflint.c (is_debuginfo): New variable.
+ (options, parse_opt): New option --debuginfo/-d to set it.
+ (check_sections): If is_debuginfo, don't complain about SHT_NOBITS.
+ (check_note): If is_debuginfo, don't try to get note contents.
+
+2005-04-24 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_debug_abbrev_section): Don't print error when end of
+ section reached.
+
+2005-04-14 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (dwarf_encoding_string): New function.
+ (dwarf_inline_string): New function.
+ (dwarf_access_string): New function.
+ (dwarf_visibility_string): New function.
+ (dwarf_virtuality_string): New function.
+ (dwarf_identifier_case_string): New function.
+ (dwarf_calling_convention_string): New function.
+ (dwarf_ordering_string): New function.
+ (dwarf_discr_list_string): New function.
+ (attr_callback): Decode man more attribute values.
+
+2005-04-01 Ulrich Drepper <drepper@redhat.com>
+
+ * addr2line.c: Finish implementation of -f option.
+
+2005-03-29 Ulrich Drepper <drepper@redhat.com>
+
+ * addr2line.c: New file.
+ * Makefile.am (bin_PROGRAMS): Add addr2line.
+ Define addr2line_LDADD.
+
+ * findtextrel.c: Use new dwarf_addrdie function.
+
+ * findtextrel.c: Fix usage message and re-add accidentally removed
+ line.
+
+2005-03-28 Ulrich Drepper <drepper@redhat.com>
+
+ * findtextrel.c: New file.
+ * Makefile: Add rules to build findtextrel.
+
+2005-02-15 Ulrich Drepper <drepper@redhat.com>
+
+ * ldlex.l: Provide ECHO definition to avoid warning.
+
+ * elflint.c (check_program_header): Fix typo in RELRO test.
+
+ * Makefile.am (AM_CFLAGS): Add more warning options.
+ * elflint.c: Fix warnings introduced by the new warning options.
+ * i386_ld.c: Likewise.
+ * ld.c: Likewise.
+ * ld.h: Likewise.
+ * ldgeneric.c: Likewise.
+ * nm.c: Likewise.
+ * readelf.c: Likewise.
+ * sectionhash.c: Likewise.
+ * size.c: Likewise.
+ * string.c: Likewise.
+
+2005-02-05 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.am: Check for text relocations in constructed DSOs.
+
+ * Makefile.am [MUDFLAP] (AM_CFLAGS): Add -fmudflap. Link all apps
+ with -lmudflap.
+
+ * ldscript.y: Add as_needed handling.
+ * ldlex.l: Recognize AS_NEEDED token.
+ * ld.h (struct filename_list): Add as_needed flag.
+
+2005-02-04 Ulrich Drepper <drepper@redhat.com>
+
+ * elflint.c (check_symtab): Correctly determine size of GOT section.
+
+2005-01-19 Ulrich Drepper <drepper@redhat.com>
+
+ * ld.c: Remove unnecessary more_help function. Print bug report
+ address using argp.
+ * strip.c: Likewise.
+ * size.c: Likewise.
+ * nm.c: Likewise.
+ * readelf.c: Likewise.
+ * elflint.c: Likewise.
+
+ * elflint.c (main): Don't check for parameter problems here.
+ (parse_opt): Do it here, where we get informed about some of them
+ anyway.
+
+ * readelf.c (main): Don't check for parameter problems here.
+ (parse_opt): Do it here, where we get informed about some of them
+ anyway.
+
+2005-01-11 Ulrich Drepper <drepper@redhat.com>
+
+ * strip.c: Update copyright year.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * nm.c: Likewise.
+ * ld.c: Likewise.
+ * elflint.c: Likewise.
+
+ * elflint.c (check_symtab): Don't warn about wrong size for
+ _DYNAMIC and __GLOBAL_OFFSET_TABLE__ for --gnu-ld.
+
+2004-10-05 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_phdr): In section mapping, also indicate
+ sections in read-only segments.
+
+2004-09-25 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c: Make compile with gcc 4.0.
+ * strip.c: Likewise.
+
+2004-08-16 Ulrich Drepper <drepper@redhat.com>
+
+ * strip.c (handle_elf): Rewrite dynamic memory handling to use of
+ allocate to work around gcc 3.4 bug.
+
+2004-01-25 Ulrich Drepper <drepper@redhat.com>
+
+ * ldlex.l (invalid_char): Better error message.
+
+2004-01-23 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c: Print SHT_GNU_LIBLIST sections.
+
+ * none_ld.c: New file.
+
+2004-01-21 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.am: Enable building of machine specific linker.
+
+2004-01-20 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.am: Support building with mudflap.
+
+ * i386_ld.c: Fix warnings gcc 3.4 spits out.
+ * ldgeneric.c: Likewise.
+ * ldscript.y: Likewise.
+ * readelf.c: Likewise.
+ * strip.c: Likewise.
+
+ * readelf.c (print_debug_line_section): Determine address size
+ correctly.
+
+2004-01-19 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_phdr): Show which sections are covered by the
+ PT_GNU_RELRO entry.
+
+ * elflint.c (check_program_header): Check PT_GNU_RELRO entry.
+
+ * readelf.c (print_debug_macinfo_section): Implement.
+
+2004-01-18 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_debug_line_section): Implement.
+
+2004-01-17 Ulrich Drepper <drepper@redhat.com>
+
+ * src/elflint.c: Use PACKAGE_NAME instead of PACKAGE.
+ * src/ld.c: Likewise.
+ * src/nm.c: Likewise.
+ * src/readelf.c: Likewise.
+ * src/size.c: Likewise.
+ * src/strip.c: Likewise.
+
+ * strip.c: Add a few more unlikely. Reduce scope of some variables.
+
+ * Makefile.am: Support building with mudflap.
+
+2004-01-16 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_debug_info_section): Free dies memory.
+
+ * readelf.c: Print .debug_info section content.
+
+2004-01-13 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_shdr): Add support for SHF_ORDERED and SHF_EXCLUDE.
+
+2004-01-12 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (print_debug_aranges): Implement using libdw.
+
+2004-01-11 Ulrich Drepper <drepper@redhat.com>
+
+ * nm.c: Adjust for Dwarf_Files type and dwarf_lineno interface change.
+
+ * readelf.c: Use libdw instead of libdwarf. Not all of the old
+ behavior is available yet.
+ * Makefile.am: Link readelf with libdw. Remove libdwarf include path.
+
+2004-01-09 Ulrich Drepper <drepper@redhat.com>
+
+ * nm.c (get_local_names): Adjust call to dwarf_nextcu.
+
+ * nm.c: Implement getting information about local variables.
+
+2004-01-07 Ulrich Drepper <drepper@redhat.com>
+
+ * nm.c: Read also debug information for local symbols.
+
+2004-01-05 Ulrich Drepper <drepper@redhat.com>
+
+ * nm.c: Shuffle dwarf handling code around so the maximum column
+ width can be computed ahead of printing. Avoid collection symbols
+ which are not printed anyway.
+
+ * nm.c: Rewrite dwarf handling to use libdw.
+ * Makefile.am (AM_CFLAGS): Add -std parameter.
+ (INCLUDES): Find header in libdw subdir.
+ (nm_LDADD): Replace libdwarf with libdw.
+
+ * elflint.c: Update copyright year.
+ * readelf.c: Likewise.
+ * size.c: Likewise.
+ * strip.c: Likewise.
+ * nm.c: Likewise.
+
+2003-12-31 Ulrich Drepper <drepper@redhat.com>
+
+ * strip.c (process_file): Close file before returning.
+
+2003-11-19 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (handle_dynamic): Make column for tag name wider.
+
+2003-09-29 Ulrich Drepper <drepper@redhat.com>
+
+ * readelf.c (handle_dynamic): Always terminate tag name with a space.
+
+2003-09-25 Ulrich Drepper <drepper@redhat.com>
+
+ * strip.c (process_file): Don't mmap the input file, we modify the
+ data structures and don't want the change end up on disk.
+
+2003-09-23 Jakub Jelinek <jakub@redhat.com>
+
+ * unaligned.h (union u_2ubyte_unaligned,
+ union u_4ubyte_unaligned, union u_8ubyte_unaligned): Add
+ packed attribute.
+ (add_2ubyte_unaligned, add_4ubyte_unaligned,
+ add_8ubyte_unaligned): Avoid nesting bswap_NN macros.
+ Read/store value through _ptr->u instead of *_ptr.
+
+2003-09-22 Ulrich Drepper <drepper@redhat.com>
+
+ * size.c (show_sysv): Change type of maxlen to int.
+
+ * strip.c (handle_elf): Handle the 64-bit archs which is 64-bit
+ buckets.
+
+ * i386_ld.c: Many many fixes and extensions.
+ * ld.c: Likewise.
+ * ldgeneric.c: Likewise.
+
+2003-08-16 Ulrich Drepper <drepper@redhat.com>
+
+ * ldgeneric.c (check_definition): Don't add symbol on dso_list if
+ the reference is from another DSO.
+
+2003-08-15 Ulrich Drepper <drepper@redhat.com>
+
+ * ldgeneric.c (find_entry_point): It is no fatal error if no entry
+ point is found when creating a DSO.
+
+2003-08-14 Ulrich Drepper <drepper@redhat.com>
+
+ * ld.c (main): Always call FLAG_UNRESOLVED.
+ * ldgeneric.c (ld_generic_flag_unresolved): Only complain about
+ undefined symbols if not creating DSO or ld_state.nodefs is not set.
+
+2003-08-13 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.in: Depend on libebl.a, not libebl.so.
+
+ * ld.c (main): Mark stream for linker script as locked by caller.
+ (read_version_script): Likewise.
+ * ldlex.c: Define fread and fwrite to _unlocked variant.
+
+ * i386_ld.c (elf_i386_finalize_plt): Replace #ifdefs with uses of
+ target_bswap_32.
+ * unaligned.h: Define target_bswap_16, target_bswap_32, and
+ target_bswap_64.
+ (store_2ubyte_unaligned, store_4ubyte_unaligned,
+ store_8ubyte_unaligned): Define using new macros.
+
+2003-08-12 Ulrich Drepper <drepper@redhat.com>
+
+ * i386_ld.c (elf_i386_finalize_plt): Use packed structs to access
+ possibly unaligned memory. Support use of big endian machines.
+
+2003-08-11 Ulrich Drepper <drepper@redhat.com>
+
+ * Moved to CVS archive.
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..2b1c0dc
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,113 @@
+## Process this file with automake to create Makefile.in
+##
+## Copyright (C) 1996-2014, 2016 Red Hat, Inc.
+## This file is part of elfutils.
+##
+## This file is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or
+## (at your option) any later version.
+##
+## elfutils is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+include $(top_srcdir)/config/eu.am
+DEFS += $(YYDEBUG) -DDEBUGPRED=@DEBUGPRED@ \
+ -DSRCDIR=\"$(shell cd $(srcdir);pwd)\" -DOBJDIR=\"$(shell pwd)\"
+AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
+ -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf \
+ -I$(srcdir)/../libdwfl -I$(srcdir)/../libasm
+
+AM_LDFLAGS = -Wl,-rpath-link,../libelf:../libdw
+
+bin_PROGRAMS = readelf nm size strip elflint findtextrel addr2line \
+ elfcmp objdump ranlib strings ar unstrip stack elfcompress
+
+noinst_LIBRARIES = libar.a
+
+libar_a_SOURCES = arlib.c arlib2.c arlib-argp.c
+
+EXTRA_DIST = arlib.h debugpred.h
+
+bin_SCRIPTS = make-debug-archive
+EXTRA_DIST += make-debug-archive.in
+CLEANFILES += make-debug-archive
+
+if BUILD_STATIC
+libasm = ../libasm/libasm.a
+libdw = ../libdw/libdw.a -lz $(zip_LIBS) $(libelf) $(libebl) -ldl
+libelf = ../libelf/libelf.a -lz
+else
+libasm = ../libasm/libasm.so
+libdw = ../libdw/libdw.so
+libelf = ../libelf/libelf.so
+endif
+libebl = ../libebl/libebl.a
+libeu = ../lib/libeu.a
+
+if DEMANGLE
+demanglelib = -lstdc++
+endif
+
+# Bad, bad stack usage...
+readelf_no_Wstack_usage = yes
+nm_no_Wstack_usage = yes
+size_no_Wstack_usage = yes
+strip_no_Wstack_usage = yes
+elflint_no_Wstack_usage = yes
+findtextrel_no_Wstack_usage = yes
+elfcmp_no_Wstack_usage = yes
+objdump_no_Wstack_usage = yes
+ranlib_no_Wstack_usage = yes
+ar_no_Wstack_usage = yes
+unstrip_no_Wstack_usage = yes
+
+readelf_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(argp_LDADD) -ldl
+nm_LDADD = $(libdw) $(libebl) $(libelf) $(libeu) $(argp_LDADD) -ldl \
+ $(demanglelib)
+size_LDADD = $(libelf) $(libeu) $(argp_LDADD)
+strip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) -ldl
+elflint_LDADD = $(libebl) $(libelf) $(libeu) $(argp_LDADD) -ldl
+findtextrel_LDADD = $(libdw) $(libelf) $(libeu) $(argp_LDADD)
+addr2line_LDADD = $(libdw) $(libelf) $(libeu) $(argp_LDADD) $(demanglelib)
+elfcmp_LDADD = $(libebl) $(libelf) $(libeu) $(argp_LDADD) -ldl
+objdump_LDADD = $(libasm) $(libebl) $(libelf) $(libeu) $(argp_LDADD) -ldl
+ranlib_LDADD = libar.a $(libelf) $(libeu) $(argp_LDADD)
+strings_LDADD = $(libelf) $(libeu) $(argp_LDADD)
+ar_LDADD = libar.a $(libelf) $(libeu) $(argp_LDADD)
+unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) -ldl
+stack_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) -ldl $(demanglelib)
+elfcompress_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD)
+
+installcheck-binPROGRAMS: $(bin_PROGRAMS)
+ bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \
+ case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \
+ *" $$p "* | *" $(srcdir)/$$p "*) continue;; \
+ esac; \
+ f=`echo "$$p" | \
+ sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ for opt in --help --version; do \
+ if LD_LIBRARY_PATH=$(DESTDIR)$(libdir) \
+ $(DESTDIR)$(bindir)/$$f $$opt > c$${pid}_.out 2> c$${pid}_.err \
+ && test -n "`cat c$${pid}_.out`" \
+ && test -z "`cat c$${pid}_.err`"; then :; \
+ else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \
+ done; \
+ done; rm -f c$${pid}_.???; exit $$bad
+
+CLEANFILES += *.gconv
+
+make-debug-archive: $(srcdir)/make-debug-archive.in
+ $(AM_V_GEN)UNSTRIP=$(bindir)/`echo unstrip | sed '$(transform)'`; \
+ AR=$(bindir)/`echo ar | sed '$(transform)'`; \
+ sed -e "s,[@]UNSTRIP[@],$$UNSTRIP,g" -e "s,[@]AR[@],$$AR,g" \
+ -e "s%[@]PACKAGE_NAME[@]%$(PACKAGE_NAME)%g" \
+ -e "s%[@]PACKAGE_VERSION[@]%$(PACKAGE_VERSION)%g" \
+ $(srcdir)/make-debug-archive.in > $@.new
+ $(AM_V_at)chmod +x $@.new
+ $(AM_V_at)mv -f $@.new $@
diff --git a/src/addr2line.c b/src/addr2line.c
new file mode 100644
index 0000000..444ee52
--- /dev/null
+++ b/src/addr2line.c
@@ -0,0 +1,814 @@
+/* Locate source files and line information for given addresses
+ Copyright (C) 2005-2010, 2012, 2013, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2005.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libdwfl.h>
+#include <dwarf.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <system.h>
+#include <printversion.h>
+
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+
+/* Values for the parameters which have no short form. */
+#define OPT_DEMANGLER 0x100
+#define OPT_PRETTY 0x101 /* 'p' is already used to select the process. */
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Input format options:"), 2 },
+ { "section", 'j', "NAME", 0,
+ N_("Treat addresses as offsets relative to NAME section."), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output format options:"), 3 },
+ { "addresses", 'a', NULL, 0, N_("Print address before each entry"), 0 },
+ { "basenames", 's', NULL, 0, N_("Show only base names of source files"), 0 },
+ { "absolute", 'A', NULL, 0,
+ N_("Show absolute file names using compilation directory"), 0 },
+ { "functions", 'f', NULL, 0, N_("Also show function names"), 0 },
+ { "symbols", 'S', NULL, 0, N_("Also show symbol or section names"), 0 },
+ { "symbols-sections", 'x', NULL, 0, N_("Also show symbol and the section names"), 0 },
+ { "flags", 'F', NULL, 0, N_("Also show line table flags"), 0 },
+ { "inlines", 'i', NULL, 0,
+ N_("Show all source locations that caused inline expansion of subroutines at the address."),
+ 0 },
+ { "demangle", 'C', "ARG", OPTION_ARG_OPTIONAL,
+ N_("Show demangled symbols (ARG is always ignored)"), 0 },
+ { "pretty-print", OPT_PRETTY, NULL, 0,
+ N_("Print all information on one line, and indent inlines"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
+ /* Unsupported options. */
+ { "target", 'b', "ARG", OPTION_HIDDEN, NULL, 0 },
+ { "demangler", OPT_DEMANGLER, "ARG", OPTION_HIDDEN, NULL, 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("\
+Locate source files and line information for ADDRs (in a.out by default).");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("[ADDR...]");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+static struct argp_child argp_children[2]; /* [0] is set in main. */
+
+/* Data structure to communicate with argp functions. */
+static const struct argp argp =
+{
+ options, parse_opt, args_doc, doc, argp_children, NULL, NULL
+};
+
+
+/* Handle ADDR. */
+static int handle_address (const char *addr, Dwfl *dwfl);
+
+/* True when we should print the address for each entry. */
+static bool print_addresses;
+
+/* True if only base names of files should be shown. */
+static bool only_basenames;
+
+/* True if absolute file names based on DW_AT_comp_dir should be shown. */
+static bool use_comp_dir;
+
+/* True if line flags should be shown. */
+static bool show_flags;
+
+/* True if function names should be shown. */
+static bool show_functions;
+
+/* True if ELF symbol or section info should be shown. */
+static bool show_symbols;
+
+/* True if section associated with a symbol address should be shown. */
+static bool show_symbol_sections;
+
+/* If non-null, take address parameters as relative to named section. */
+static const char *just_section;
+
+/* True if all inlined subroutines of the current address should be shown. */
+static bool show_inlines;
+
+/* True if all names need to be demangled. */
+static bool demangle;
+
+/* True if all information should be printed on one line. */
+static bool pretty;
+
+#ifdef USE_DEMANGLE
+static size_t demangle_buffer_len = 0;
+static char *demangle_buffer = NULL;
+#endif
+
+int
+main (int argc, char *argv[])
+{
+ int remaining;
+ int result = 0;
+
+ /* We use no threads here which can interfere with handling a stream. */
+ (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ (void) textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. This includes opening the modules. */
+ argp_children[0].argp = dwfl_standard_argp ();
+ argp_children[0].group = 1;
+ Dwfl *dwfl = NULL;
+ (void) argp_parse (&argp, argc, argv, 0, &remaining, &dwfl);
+ assert (dwfl != NULL);
+
+ /* Now handle the addresses. In case none are given on the command
+ line, read from stdin. */
+ if (remaining == argc)
+ {
+ /* We use no threads here which can interfere with handling a stream. */
+ (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+
+ char *buf = NULL;
+ size_t len = 0;
+ ssize_t chars;
+ while (!feof_unlocked (stdin))
+ {
+ if ((chars = getline (&buf, &len, stdin)) < 0)
+ break;
+
+ if (buf[chars - 1] == '\n')
+ buf[chars - 1] = '\0';
+
+ result = handle_address (buf, dwfl);
+ }
+
+ free (buf);
+ }
+ else
+ {
+ do
+ result = handle_address (argv[remaining], dwfl);
+ while (++remaining < argc);
+ }
+
+ dwfl_end (dwfl);
+
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
+
+ return result;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = state->input;
+ break;
+
+ case 'a':
+ print_addresses = true;
+ break;
+
+ case 'b':
+ case 'C':
+ case OPT_DEMANGLER:
+ demangle = true;
+ break;
+
+ case 's':
+ only_basenames = true;
+ break;
+
+ case 'A':
+ use_comp_dir = true;
+ break;
+
+ case 'f':
+ show_functions = true;
+ break;
+
+ case 'F':
+ show_flags = true;
+ break;
+
+ case 'S':
+ show_symbols = true;
+ break;
+
+ case 'x':
+ show_symbols = true;
+ show_symbol_sections = true;
+ break;
+
+ case 'j':
+ just_section = arg;
+ break;
+
+ case 'i':
+ show_inlines = true;
+ break;
+
+ case OPT_PRETTY:
+ pretty = true;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const char *
+symname (const char *name)
+{
+#ifdef USE_DEMANGLE
+ // Require GNU v3 ABI by the "_Z" prefix.
+ if (demangle && name[0] == '_' && name[1] == 'Z')
+ {
+ int status = -1;
+ char *dsymname = __cxa_demangle (name, demangle_buffer,
+ &demangle_buffer_len, &status);
+ if (status == 0)
+ name = demangle_buffer = dsymname;
+ }
+#endif
+ return name;
+}
+
+static const char *
+get_diename (Dwarf_Die *die)
+{
+ Dwarf_Attribute attr;
+ const char *name;
+
+ name = dwarf_formstring (dwarf_attr_integrate (die, DW_AT_MIPS_linkage_name,
+ &attr)
+ ?: dwarf_attr_integrate (die, DW_AT_linkage_name,
+ &attr));
+
+ if (name == NULL)
+ name = dwarf_diename (die) ?: "??";
+
+ return name;
+}
+
+static bool
+print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr)
+{
+ Dwarf_Addr bias = 0;
+ Dwarf_Die *cudie = dwfl_module_addrdie (mod, addr, &bias);
+
+ Dwarf_Die *scopes;
+ int nscopes = dwarf_getscopes (cudie, addr - bias, &scopes);
+ if (nscopes <= 0)
+ return false;
+
+ bool res = false;
+ for (int i = 0; i < nscopes; ++i)
+ switch (dwarf_tag (&scopes[i]))
+ {
+ case DW_TAG_subprogram:
+ {
+ const char *name = get_diename (&scopes[i]);
+ if (name == NULL)
+ goto done;
+ printf ("%s%c", symname (name), pretty ? ' ' : '\n');
+ res = true;
+ goto done;
+ }
+
+ case DW_TAG_inlined_subroutine:
+ {
+ const char *name = get_diename (&scopes[i]);
+ if (name == NULL)
+ goto done;
+
+ /* When using --pretty-print we only show inlines on their
+ own line. Just print the first subroutine name. */
+ if (pretty)
+ {
+ printf ("%s ", symname (name));
+ res = true;
+ goto done;
+ }
+ else
+ printf ("%s inlined", symname (name));
+
+ Dwarf_Files *files;
+ if (dwarf_getsrcfiles (cudie, &files, NULL) == 0)
+ {
+ Dwarf_Attribute attr_mem;
+ Dwarf_Word val;
+ if (dwarf_formudata (dwarf_attr (&scopes[i],
+ DW_AT_call_file,
+ &attr_mem), &val) == 0)
+ {
+ const char *file = dwarf_filesrc (files, val, NULL, NULL);
+ unsigned int lineno = 0;
+ unsigned int colno = 0;
+ if (dwarf_formudata (dwarf_attr (&scopes[i],
+ DW_AT_call_line,
+ &attr_mem), &val) == 0)
+ lineno = val;
+ if (dwarf_formudata (dwarf_attr (&scopes[i],
+ DW_AT_call_column,
+ &attr_mem), &val) == 0)
+ colno = val;
+
+ const char *comp_dir = "";
+ const char *comp_dir_sep = "";
+
+ if (file == NULL)
+ file = "???";
+ else if (only_basenames)
+ file = basename (file);
+ else if (use_comp_dir && file[0] != '/')
+ {
+ const char *const *dirs;
+ size_t ndirs;
+ if (dwarf_getsrcdirs (files, &dirs, &ndirs) == 0
+ && dirs[0] != NULL)
+ {
+ comp_dir = dirs[0];
+ comp_dir_sep = "/";
+ }
+ }
+
+ if (lineno == 0)
+ printf (" from %s%s%s",
+ comp_dir, comp_dir_sep, file);
+ else if (colno == 0)
+ printf (" at %s%s%s:%u",
+ comp_dir, comp_dir_sep, file, lineno);
+ else
+ printf (" at %s%s%s:%u:%u",
+ comp_dir, comp_dir_sep, file, lineno, colno);
+ }
+ }
+ printf (" in ");
+ continue;
+ }
+ }
+
+done:
+ free (scopes);
+ return res;
+}
+
+static void
+print_addrsym (Dwfl_Module *mod, GElf_Addr addr)
+{
+ GElf_Sym s;
+ GElf_Off off;
+ const char *name = dwfl_module_addrinfo (mod, addr, &off, &s,
+ NULL, NULL, NULL);
+ if (name == NULL)
+ {
+ /* No symbol name. Get a section name instead. */
+ int i = dwfl_module_relocate_address (mod, &addr);
+ if (i >= 0)
+ name = dwfl_module_relocation_info (mod, i, NULL);
+ if (name == NULL)
+ printf ("??%c", pretty ? ' ': '\n');
+ else
+ printf ("(%s)+%#" PRIx64 "%c", name, addr, pretty ? ' ' : '\n');
+ }
+ else
+ {
+ name = symname (name);
+ if (off == 0)
+ printf ("%s", name);
+ else
+ printf ("%s+%#" PRIx64 "", name, off);
+
+ // Also show section name for address.
+ if (show_symbol_sections)
+ {
+ Dwarf_Addr ebias;
+ Elf_Scn *scn = dwfl_module_address_section (mod, &addr, &ebias);
+ if (scn != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL)
+ {
+ Elf *elf = dwfl_module_getelf (mod, &ebias);
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr (elf, &ehdr) != NULL)
+ printf (" (%s)", elf_strptr (elf, ehdr.e_shstrndx,
+ shdr->sh_name));
+ }
+ }
+ }
+ printf ("%c", pretty ? ' ' : '\n');
+ }
+}
+
+static int
+see_one_module (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *name __attribute__ ((unused)),
+ Dwarf_Addr start __attribute__ ((unused)),
+ void *arg)
+{
+ Dwfl_Module **result = arg;
+ if (*result != NULL)
+ return DWARF_CB_ABORT;
+ *result = mod;
+ return DWARF_CB_OK;
+}
+
+static int
+find_symbol (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *name __attribute__ ((unused)),
+ Dwarf_Addr start __attribute__ ((unused)),
+ void *arg)
+{
+ const char *looking_for = ((void **) arg)[0];
+ GElf_Sym *symbol = ((void **) arg)[1];
+ GElf_Addr *value = ((void **) arg)[2];
+
+ int n = dwfl_module_getsymtab (mod);
+ for (int i = 1; i < n; ++i)
+ {
+ const char *symbol_name = dwfl_module_getsym_info (mod, i, symbol,
+ value, NULL, NULL,
+ NULL);
+ if (symbol_name == NULL || symbol_name[0] == '\0')
+ continue;
+ switch (GELF_ST_TYPE (symbol->st_info))
+ {
+ case STT_SECTION:
+ case STT_FILE:
+ case STT_TLS:
+ break;
+ default:
+ if (!strcmp (symbol_name, looking_for))
+ {
+ ((void **) arg)[0] = NULL;
+ return DWARF_CB_ABORT;
+ }
+ }
+ }
+
+ return DWARF_CB_OK;
+}
+
+static bool
+adjust_to_section (const char *name, uintmax_t *addr, Dwfl *dwfl)
+{
+ /* It was (section)+offset. This makes sense if there is
+ only one module to look in for a section. */
+ Dwfl_Module *mod = NULL;
+ if (dwfl_getmodules (dwfl, &see_one_module, &mod, 0) != 0
+ || mod == NULL)
+ error (EXIT_FAILURE, 0, gettext ("Section syntax requires"
+ " exactly one module"));
+
+ int nscn = dwfl_module_relocations (mod);
+ for (int i = 0; i < nscn; ++i)
+ {
+ GElf_Word shndx;
+ const char *scn = dwfl_module_relocation_info (mod, i, &shndx);
+ if (unlikely (scn == NULL))
+ break;
+ if (!strcmp (scn, name))
+ {
+ /* Found the section. */
+ GElf_Shdr shdr_mem;
+ GElf_Addr shdr_bias;
+ GElf_Shdr *shdr = gelf_getshdr
+ (elf_getscn (dwfl_module_getelf (mod, &shdr_bias), shndx),
+ &shdr_mem);
+ if (unlikely (shdr == NULL))
+ break;
+
+ if (*addr >= shdr->sh_size)
+ error (0, 0,
+ gettext ("offset %#" PRIxMAX " lies outside"
+ " section '%s'"),
+ *addr, scn);
+
+ *addr += shdr->sh_addr + shdr_bias;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void
+print_src (const char *src, int lineno, int linecol, Dwarf_Die *cu)
+{
+ const char *comp_dir = "";
+ const char *comp_dir_sep = "";
+
+ if (only_basenames)
+ src = basename (src);
+ else if (use_comp_dir && src[0] != '/')
+ {
+ Dwarf_Attribute attr;
+ comp_dir = dwarf_formstring (dwarf_attr (cu, DW_AT_comp_dir, &attr));
+ if (comp_dir != NULL)
+ comp_dir_sep = "/";
+ }
+
+ if (linecol != 0)
+ printf ("%s%s%s:%d:%d",
+ comp_dir, comp_dir_sep, src, lineno, linecol);
+ else
+ printf ("%s%s%s:%d",
+ comp_dir, comp_dir_sep, src, lineno);
+}
+
+static int
+get_addr_width (Dwfl_Module *mod)
+{
+ // Try to find the address width if possible.
+ static int width = 0;
+ if (width == 0 && mod != NULL)
+ {
+ Dwarf_Addr bias;
+ Elf *elf = dwfl_module_getelf (mod, &bias);
+ if (elf != NULL)
+ {
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr != NULL)
+ width = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16;
+ }
+ }
+ if (width == 0)
+ width = 16;
+
+ return width;
+}
+
+static int
+handle_address (const char *string, Dwfl *dwfl)
+{
+ char *endp;
+ uintmax_t addr = strtoumax (string, &endp, 16);
+ if (endp == string || *endp != '\0')
+ {
+ bool parsed = false;
+ int i, j;
+ char *name = NULL;
+ if (sscanf (string, "(%m[^)])%" PRIiMAX "%n", &name, &addr, &i) == 2
+ && string[i] == '\0')
+ parsed = adjust_to_section (name, &addr, dwfl);
+ switch (sscanf (string, "%m[^-+]%n%" PRIiMAX "%n", &name, &i, &addr, &j))
+ {
+ default:
+ break;
+ case 1:
+ addr = 0;
+ j = i;
+ FALLTHROUGH;
+ case 2:
+ if (string[j] != '\0')
+ break;
+
+ /* It was symbol[+offset]. */
+ GElf_Sym sym;
+ GElf_Addr value = 0;
+ void *arg[3] = { name, &sym, &value };
+ (void) dwfl_getmodules (dwfl, &find_symbol, arg, 0);
+ if (arg[0] != NULL)
+ error (0, 0, gettext ("cannot find symbol '%s'"), name);
+ else
+ {
+ if (sym.st_size != 0 && addr >= sym.st_size)
+ error (0, 0,
+ gettext ("offset %#" PRIxMAX " lies outside"
+ " contents of '%s'"),
+ addr, name);
+ addr += value;
+ parsed = true;
+ }
+ break;
+ }
+
+ free (name);
+ if (!parsed)
+ return 1;
+ }
+ else if (just_section != NULL
+ && !adjust_to_section (just_section, &addr, dwfl))
+ return 1;
+
+ Dwfl_Module *mod = dwfl_addrmodule (dwfl, addr);
+
+ if (print_addresses)
+ {
+ int width = get_addr_width (mod);
+ printf ("0x%.*" PRIx64 "%s", width, addr, pretty ? ": " : "\n");
+ }
+
+ if (show_functions)
+ {
+ /* First determine the function name. Use the DWARF information if
+ possible. */
+ if (! print_dwarf_function (mod, addr) && !show_symbols)
+ {
+ const char *name = dwfl_module_addrname (mod, addr);
+ name = name != NULL ? symname (name) : "??";
+ printf ("%s%c", name, pretty ? ' ' : '\n');
+ }
+ }
+
+ if (show_symbols)
+ print_addrsym (mod, addr);
+
+ if ((show_functions || show_symbols) && pretty)
+ printf ("at ");
+
+ Dwfl_Line *line = dwfl_module_getsrc (mod, addr);
+
+ const char *src;
+ int lineno, linecol;
+
+ if (line != NULL && (src = dwfl_lineinfo (line, &addr, &lineno, &linecol,
+ NULL, NULL)) != NULL)
+ {
+ print_src (src, lineno, linecol, dwfl_linecu (line));
+ if (show_flags)
+ {
+ Dwarf_Addr bias;
+ Dwarf_Line *info = dwfl_dwarf_line (line, &bias);
+ assert (info != NULL);
+
+ inline void show (int (*get) (Dwarf_Line *, bool *),
+ const char *note)
+ {
+ bool flag;
+ if ((*get) (info, &flag) == 0 && flag)
+ fputs (note, stdout);
+ }
+ inline void show_int (int (*get) (Dwarf_Line *, unsigned int *),
+ const char *name)
+ {
+ unsigned int val;
+ if ((*get) (info, &val) == 0 && val != 0)
+ printf (" (%s %u)", name, val);
+ }
+
+ show (&dwarf_linebeginstatement, " (is_stmt)");
+ show (&dwarf_lineblock, " (basic_block)");
+ show (&dwarf_lineprologueend, " (prologue_end)");
+ show (&dwarf_lineepiloguebegin, " (epilogue_begin)");
+ show_int (&dwarf_lineisa, "isa");
+ show_int (&dwarf_linediscriminator, "discriminator");
+ }
+ putchar ('\n');
+ }
+ else
+ puts ("??:0");
+
+ if (show_inlines)
+ {
+ Dwarf_Addr bias = 0;
+ Dwarf_Die *cudie = dwfl_module_addrdie (mod, addr, &bias);
+
+ Dwarf_Die *scopes = NULL;
+ int nscopes = dwarf_getscopes (cudie, addr - bias, &scopes);
+ if (nscopes < 0)
+ return 1;
+
+ if (nscopes > 0)
+ {
+ Dwarf_Die subroutine;
+ Dwarf_Off dieoff = dwarf_dieoffset (&scopes[0]);
+ dwarf_offdie (dwfl_module_getdwarf (mod, &bias),
+ dieoff, &subroutine);
+ free (scopes);
+ scopes = NULL;
+
+ nscopes = dwarf_getscopes_die (&subroutine, &scopes);
+ if (nscopes > 1)
+ {
+ Dwarf_Die cu;
+ Dwarf_Files *files;
+ if (dwarf_diecu (&scopes[0], &cu, NULL, NULL) != NULL
+ && dwarf_getsrcfiles (cudie, &files, NULL) == 0)
+ {
+ for (int i = 0; i < nscopes - 1; i++)
+ {
+ Dwarf_Word val;
+ Dwarf_Attribute attr;
+ Dwarf_Die *die = &scopes[i];
+ if (dwarf_tag (die) != DW_TAG_inlined_subroutine)
+ continue;
+
+ if (pretty)
+ printf (" (inlined by) ");
+
+ if (show_functions)
+ {
+ /* Search for the parent inline or function. It
+ might not be directly above this inline -- e.g.
+ there could be a lexical_block in between. */
+ for (int j = i + 1; j < nscopes; j++)
+ {
+ Dwarf_Die *parent = &scopes[j];
+ int tag = dwarf_tag (parent);
+ if (tag == DW_TAG_inlined_subroutine
+ || tag == DW_TAG_entry_point
+ || tag == DW_TAG_subprogram)
+ {
+ printf ("%s%s",
+ symname (get_diename (parent)),
+ pretty ? " at " : "\n");
+ break;
+ }
+ }
+ }
+
+ src = NULL;
+ lineno = 0;
+ linecol = 0;
+ if (dwarf_formudata (dwarf_attr (die, DW_AT_call_file,
+ &attr), &val) == 0)
+ src = dwarf_filesrc (files, val, NULL, NULL);
+
+ if (dwarf_formudata (dwarf_attr (die, DW_AT_call_line,
+ &attr), &val) == 0)
+ lineno = val;
+
+ if (dwarf_formudata (dwarf_attr (die, DW_AT_call_column,
+ &attr), &val) == 0)
+ linecol = val;
+
+ if (src != NULL)
+ {
+ print_src (src, lineno, linecol, &cu);
+ putchar ('\n');
+ }
+ else
+ puts ("??:0");
+ }
+ }
+ }
+ }
+ free (scopes);
+ }
+
+ return 0;
+}
+
+
+#include "debugpred.h"
diff --git a/src/ar.c b/src/ar.c
new file mode 100644
index 0000000..818115b
--- /dev/null
+++ b/src/ar.c
@@ -0,0 +1,1555 @@
+/* Create, modify, and extract from archives.
+ Copyright (C) 2005-2012, 2016, 2017 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2005.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libintl.h>
+#include <limits.h>
+#include <locale.h>
+#include <search.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <system.h>
+#include <printversion.h>
+
+#include "arlib.h"
+
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Prototypes for local functions. */
+static int do_oper_extract (int oper, const char *arfname, char **argv,
+ int argc, long int instance);
+static int do_oper_delete (const char *arfname, char **argv, int argc,
+ long int instance);
+static int do_oper_insert (int oper, const char *arfname, char **argv,
+ int argc, const char *member);
+
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Commands:"), 1 },
+ { NULL, 'd', NULL, 0, N_("Delete files from archive."), 0 },
+ { NULL, 'm', NULL, 0, N_("Move files in archive."), 0 },
+ { NULL, 'p', NULL, 0, N_("Print files in archive."), 0 },
+ { NULL, 'q', NULL, 0, N_("Quick append files to archive."), 0 },
+ { NULL, 'r', NULL, 0,
+ N_("Replace existing or insert new file into archive."), 0 },
+ { NULL, 't', NULL, 0, N_("Display content of archive."), 0 },
+ { NULL, 'x', NULL, 0, N_("Extract files from archive."), 0 },
+
+ { NULL, 0, NULL, 0, N_("Command Modifiers:"), 2 },
+ { NULL, 'o', NULL, 0, N_("Preserve original dates."), 0 },
+ { NULL, 'N', NULL, 0, N_("Use instance [COUNT] of name."), 0 },
+ { NULL, 'C', NULL, 0,
+ N_("Do not replace existing files with extracted files."), 0 },
+ { NULL, 'T', NULL, 0, N_("Allow filename to be truncated if necessary."),
+ 0 },
+ { NULL, 'v', NULL, 0, N_("Provide verbose output."), 0 },
+ { NULL, 's', NULL, 0, N_("Force regeneration of symbol table."), 0 },
+ { NULL, 'a', NULL, 0, N_("Insert file after [MEMBER]."), 0 },
+ { NULL, 'b', NULL, 0, N_("Insert file before [MEMBER]."), 0 },
+ { NULL, 'i', NULL, 0, N_("Same as -b."), 0 },
+ { NULL, 'c', NULL, 0, N_("Suppress message when library has to be created."),
+ 0 },
+ { NULL, 'P', NULL, 0, N_("Use full path for file matching."), 0 },
+ { NULL, 'u', NULL, 0, N_("Update only older files in archive."), 0 },
+
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("Create, modify, and extract from archives.");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("[MEMBER] [COUNT] ARCHIVE [FILE...]");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, arlib_argp_children, NULL, NULL
+};
+
+
+/* What operation to perform. */
+static enum
+ {
+ oper_none,
+ oper_delete,
+ oper_move,
+ oper_print,
+ oper_qappend,
+ oper_replace,
+ oper_list,
+ oper_extract
+ } operation;
+
+/* Modifiers. */
+static bool verbose;
+static bool preserve_dates;
+static bool instance_specifed;
+static bool dont_replace_existing;
+static bool allow_truncate_fname;
+static bool force_symtab;
+static bool suppress_create_msg;
+static bool full_path;
+static bool update_newer;
+static enum { ipos_none, ipos_before, ipos_after } ipos;
+
+
+int
+main (int argc, char *argv[])
+{
+ /* We use no threads here which can interfere with handling a stream. */
+ (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ (void) textdomain (PACKAGE_TARNAME);
+
+ /* For historical reasons the options in the first parameter need
+ not be preceded by a dash. Add it now if necessary. */
+ if (argc > 1 && argv[1][0] != '-')
+ {
+ size_t len = strlen (argv[1]) + 1;
+ char *newp = alloca (len + 1);
+ newp[0] = '-';
+ memcpy (&newp[1], argv[1], len);
+ argv[1] = newp;
+ }
+
+ /* Parse and process arguments. */
+ int remaining;
+ (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &remaining, NULL);
+
+ /* Tell the library which version we are expecting. */
+ (void) elf_version (EV_CURRENT);
+
+ /* Handle the [MEMBER] parameter. */
+ const char *member = NULL;
+ if (ipos != ipos_none)
+ {
+ /* Only valid for certain operations. */
+ if (operation != oper_move && operation != oper_replace)
+ error (1, 0, gettext ("\
+'a', 'b', and 'i' are only allowed with the 'm' and 'r' options"));
+
+ if (remaining == argc)
+ {
+ error (0, 0, gettext ("\
+MEMBER parameter required for 'a', 'b', and 'i' modifiers"));
+ argp_help (&argp, stderr, ARGP_HELP_USAGE | ARGP_HELP_SEE,
+ program_invocation_short_name);
+ exit (EXIT_FAILURE);
+ }
+
+ member = argv[remaining++];
+ }
+
+ /* Handle the [COUNT] parameter. */
+ long int instance = -1;
+ if (instance_specifed)
+ {
+ /* Only valid for certain operations. */
+ if (operation != oper_extract && operation != oper_delete)
+ error (1, 0, gettext ("\
+'N' is only meaningful with the 'x' and 'd' options"));
+
+ if (remaining == argc)
+ {
+ error (0, 0, gettext ("COUNT parameter required"));
+ argp_help (&argp, stderr, ARGP_HELP_SEE,
+ program_invocation_short_name);
+ exit (EXIT_FAILURE);
+ }
+
+ char *endp;
+ errno = 0;
+ if (((instance = strtol (argv[remaining], &endp, 10)) == LONG_MAX
+ && errno == ERANGE)
+ || instance <= 0
+ || *endp != '\0')
+ error (1, 0, gettext ("invalid COUNT parameter %s"), argv[remaining]);
+
+ ++remaining;
+ }
+
+ if ((dont_replace_existing || allow_truncate_fname)
+ && unlikely (operation != oper_extract))
+ error (1, 0, gettext ("'%c' is only meaningful with the 'x' option"),
+ dont_replace_existing ? 'C' : 'T');
+
+ /* There must at least be one more parameter specifying the archive. */
+ if (remaining == argc)
+ {
+ error (0, 0, gettext ("archive name required"));
+ argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
+ exit (EXIT_FAILURE);
+ }
+
+ const char *arfname = argv[remaining++];
+ argv += remaining;
+ argc -= remaining;
+
+ int status;
+ switch (operation)
+ {
+ case oper_none:
+ error (0, 0, gettext ("command option required"));
+ argp_help (&argp, stderr, ARGP_HELP_STD_ERR,
+ program_invocation_short_name);
+ status = 1;
+ break;
+
+ case oper_list:
+ case oper_print:
+ status = do_oper_extract (operation, arfname, argv, argc, -1);
+ break;
+
+ case oper_extract:
+ status = do_oper_extract (operation, arfname, argv, argc, instance);
+ break;
+
+ case oper_delete:
+ status = do_oper_delete (arfname, argv, argc, instance);
+ break;
+
+ case oper_move:
+ case oper_qappend:
+ case oper_replace:
+ status = do_oper_insert (operation, arfname, argv, argc, member);
+ break;
+
+ default:
+ assert (! "should not happen");
+ status = 1;
+ break;
+ }
+
+ return status;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg __attribute__ ((unused)),
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case 'd':
+ case 'm':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 't':
+ case 'x':
+ if (operation != oper_none)
+ {
+ error (0, 0, gettext ("More than one operation specified"));
+ argp_help (&argp, stderr, ARGP_HELP_SEE,
+ program_invocation_short_name);
+ exit (EXIT_FAILURE);
+ }
+
+ switch (key)
+ {
+ case 'd':
+ operation = oper_delete;
+ break;
+ case 'm':
+ operation = oper_move;
+ break;
+ case 'p':
+ operation = oper_print;
+ break;
+ case 'q':
+ operation = oper_qappend;
+ break;
+ case 'r':
+ operation = oper_replace;
+ break;
+ case 't':
+ operation = oper_list;
+ break;
+ case 'x':
+ operation = oper_extract;
+ break;
+ }
+ break;
+
+ case 'a':
+ ipos = ipos_after;
+ break;
+
+ case 'b':
+ case 'i':
+ ipos = ipos_before;
+ break;
+
+ case 'c':
+ suppress_create_msg = true;
+ break;
+
+ case 'C':
+ dont_replace_existing = true;
+ break;
+
+ case 'N':
+ instance_specifed = true;
+ break;
+
+ case 'o':
+ preserve_dates = true;
+ break;
+
+ case 'P':
+ full_path = true;
+ break;
+
+ case 's':
+ force_symtab = true;
+ break;
+
+ case 'T':
+ allow_truncate_fname = true;
+ break;
+
+ case 'u':
+ update_newer = true;
+ break;
+
+ case 'v':
+ verbose = true;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+static int
+open_archive (const char *arfname, int flags, int mode, Elf **elf,
+ struct stat *st, bool miss_allowed)
+{
+ int fd = open (arfname, flags, mode);
+ if (fd == -1)
+ {
+ if (miss_allowed)
+ return -1;
+
+ error (EXIT_FAILURE, errno, gettext ("cannot open archive '%s'"),
+ arfname);
+ }
+
+ if (elf != NULL)
+ {
+ Elf_Cmd cmd = flags == O_RDONLY ? ELF_C_READ_MMAP : ELF_C_RDWR_MMAP;
+
+ *elf = elf_begin (fd, cmd, NULL);
+ if (*elf == NULL)
+ error (EXIT_FAILURE, 0, gettext ("cannot open archive '%s': %s"),
+ arfname, elf_errmsg (-1));
+
+ if (flags == O_RDONLY && elf_kind (*elf) != ELF_K_AR)
+ error (EXIT_FAILURE, 0, gettext ("%s: not an archive file"), arfname);
+ }
+
+ if (st != NULL && fstat (fd, st) != 0)
+ error (EXIT_FAILURE, errno, gettext ("cannot stat archive '%s'"),
+ arfname);
+
+ return fd;
+}
+
+
+static void
+not_found (int argc, char *argv[argc], bool found[argc])
+{
+ for (int i = 0; i < argc; ++i)
+ if (!found[i])
+ printf (gettext ("no entry %s in archive\n"), argv[i]);
+}
+
+
+static int
+copy_content (Elf *elf, int newfd, off_t off, size_t n)
+{
+ size_t len;
+ char *rawfile = elf_rawfile (elf, &len);
+
+ assert (off + n <= len);
+
+ /* Tell the kernel we will read all the pages sequentially. */
+ size_t ps = sysconf (_SC_PAGESIZE);
+ if (n > 2 * ps)
+ posix_madvise (rawfile + (off & ~(ps - 1)), n, POSIX_MADV_SEQUENTIAL);
+
+ return write_retry (newfd, rawfile + off, n) != (ssize_t) n;
+}
+
+
+static int
+do_oper_extract (int oper, const char *arfname, char **argv, int argc,
+ long int instance)
+{
+ bool found[argc > 0 ? argc : 1];
+ memset (found, '\0', sizeof (found));
+
+ size_t name_max = 0;
+ inline bool should_truncate_fname (void)
+ {
+ if (errno == ENAMETOOLONG && allow_truncate_fname)
+ {
+ if (name_max == 0)
+ {
+ long int len = pathconf (".", _PC_NAME_MAX);
+ if (len > 0)
+ name_max = len;
+ }
+ return name_max != 0;
+ }
+ return false;
+ }
+
+ off_t index_off = -1;
+ size_t index_size = 0;
+ off_t cur_off = SARMAG;
+
+ int status = 0;
+ Elf *elf;
+ int fd = open_archive (arfname, O_RDONLY, 0, &elf, NULL, false);
+
+ if (hcreate (2 * argc) == 0)
+ error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
+
+ for (int cnt = 0; cnt < argc; ++cnt)
+ {
+ ENTRY entry = { .key = argv[cnt], .data = &argv[cnt] };
+ if (hsearch (entry, ENTER) == NULL)
+ error (EXIT_FAILURE, errno,
+ gettext ("cannot insert into hash table"));
+ }
+
+ struct stat st;
+ if (force_symtab)
+ {
+ if (fstat (fd, &st) != 0)
+ {
+ error (0, errno, gettext ("cannot stat '%s'"), arfname);
+ close (fd);
+ return 1;
+ }
+ arlib_init ();
+ }
+
+ Elf_Cmd cmd = ELF_C_READ_MMAP;
+ Elf *subelf;
+ while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
+ {
+ Elf_Arhdr *arhdr = elf_getarhdr (subelf);
+
+ if (strcmp (arhdr->ar_name, "/") == 0)
+ {
+ index_off = elf_getaroff (subelf);
+ index_size = arhdr->ar_size;
+ goto next;
+ }
+ if (strcmp (arhdr->ar_name, "//") == 0)
+ goto next;
+
+ if (force_symtab)
+ {
+ arlib_add_symbols (elf, arfname, arhdr->ar_name, cur_off);
+ cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1))
+ + sizeof (struct ar_hdr));
+ }
+
+ bool do_extract = argc <= 0;
+ if (!do_extract)
+ {
+ ENTRY entry;
+ entry.key = arhdr->ar_name;
+ ENTRY *res = hsearch (entry, FIND);
+ if (res != NULL && (instance < 0 || instance-- == 0)
+ && !found[(char **) res->data - argv])
+ found[(char **) res->data - argv] = do_extract = true;
+ }
+
+ if (do_extract)
+ {
+ if (verbose)
+ {
+ if (oper == oper_print)
+ {
+ printf ("\n<%s>\n\n", arhdr->ar_name);
+
+ /* We have to flush now because now we use the descriptor
+ directly. */
+ fflush (stdout);
+ }
+ else if (oper == oper_list)
+ {
+ char datestr[100];
+ strftime (datestr, sizeof (datestr), "%b %e %H:%M %Y",
+ localtime (&arhdr->ar_date));
+
+ printf ("%c%c%c%c%c%c%c%c%c %u/%u %6ju %s %s\n",
+ (arhdr->ar_mode & S_IRUSR) ? 'r' : '-',
+ (arhdr->ar_mode & S_IWUSR) ? 'w' : '-',
+ (arhdr->ar_mode & S_IXUSR)
+ ? ((arhdr->ar_mode & S_ISUID) ? 's' : 'x')
+ : ((arhdr->ar_mode & S_ISUID) ? 'S' : '-'),
+ (arhdr->ar_mode & S_IRGRP) ? 'r' : '-',
+ (arhdr->ar_mode & S_IWGRP) ? 'w' : '-',
+ (arhdr->ar_mode & S_IXGRP)
+ ? ((arhdr->ar_mode & S_ISGID) ? 's' : 'x')
+ : ((arhdr->ar_mode & S_ISGID) ? 'S' : '-'),
+ (arhdr->ar_mode & S_IROTH) ? 'r' : '-',
+ (arhdr->ar_mode & S_IWOTH) ? 'w' : '-',
+ (arhdr->ar_mode & S_IXOTH)
+ ? ((arhdr->ar_mode & S_ISVTX) ? 't' : 'x')
+ : ((arhdr->ar_mode & S_ISVTX) ? 'T' : '-'),
+ arhdr->ar_uid,
+ arhdr->ar_gid,
+ (uintmax_t) arhdr->ar_size,
+ datestr,
+ arhdr->ar_name);
+ }
+ else
+ printf ("x - %s\n", arhdr->ar_name);
+ }
+
+ if (oper == oper_list)
+ {
+ if (!verbose)
+ puts (arhdr->ar_name);
+
+ goto next;
+ }
+
+ size_t nleft;
+ char *data = elf_rawfile (subelf, &nleft);
+ if (data == NULL)
+ {
+ error (0, 0, gettext ("cannot read content of %s: %s"),
+ arhdr->ar_name, elf_errmsg (-1));
+ status = 1;
+ goto next;
+ }
+
+ int xfd;
+ char tempfname[] = "XXXXXX";
+ bool use_mkstemp = true;
+
+ if (oper == oper_print)
+ xfd = STDOUT_FILENO;
+ else
+ {
+ xfd = mkstemp (tempfname);
+ if (unlikely (xfd == -1))
+ {
+ /* We cannot create a temporary file. Try to overwrite
+ the file or create it if it does not exist. */
+ int flags = O_WRONLY | O_CREAT;
+ if (dont_replace_existing)
+ flags |= O_EXCL;
+ else
+ flags |= O_TRUNC;
+ xfd = open (arhdr->ar_name, flags, 0600);
+ if (unlikely (xfd == -1))
+ {
+ int printlen = INT_MAX;
+
+ if (should_truncate_fname ())
+ {
+ /* Try to truncate the name. First find out by how
+ much. */
+ printlen = name_max;
+ char truncfname[name_max + 1];
+ *((char *) mempcpy (truncfname, arhdr->ar_name,
+ name_max)) = '\0';
+
+ xfd = open (truncfname, flags, 0600);
+ }
+
+ if (xfd == -1)
+ {
+ error (0, errno, gettext ("cannot open %.*s"),
+ (int) printlen, arhdr->ar_name);
+ status = 1;
+ goto next;
+ }
+ }
+
+ use_mkstemp = false;
+ }
+ }
+
+ ssize_t n;
+ while ((n = TEMP_FAILURE_RETRY (write (xfd, data, nleft))) != -1)
+ {
+ nleft -= n;
+ if (nleft == 0)
+ break;
+ data += n;
+ }
+
+ if (unlikely (n == -1))
+ {
+ error (0, errno, gettext ("failed to write %s"), arhdr->ar_name);
+ status = 1;
+ unlink (tempfname);
+ close (xfd);
+ goto next;
+ }
+
+ if (oper != oper_print)
+ {
+ /* Fix up the mode. */
+ if (unlikely (fchmod (xfd, arhdr->ar_mode) != 0))
+ {
+ error (0, errno, gettext ("cannot change mode of %s"),
+ arhdr->ar_name);
+ status = 0;
+ }
+
+ if (preserve_dates)
+ {
+ struct timespec tv[2];
+ tv[0].tv_sec = arhdr->ar_date;
+ tv[0].tv_nsec = 0;
+ tv[1].tv_sec = arhdr->ar_date;
+ tv[1].tv_nsec = 0;
+
+ if (unlikely (futimens (xfd, tv) != 0))
+ {
+ error (0, errno,
+ gettext ("cannot change modification time of %s"),
+ arhdr->ar_name);
+ status = 1;
+ }
+ }
+
+ /* If we used a temporary file, move it do the right
+ name now. */
+ if (use_mkstemp)
+ {
+ int r;
+
+ if (dont_replace_existing)
+ {
+ r = link (tempfname, arhdr->ar_name);
+ if (likely (r == 0))
+ unlink (tempfname);
+ }
+ else
+ r = rename (tempfname, arhdr->ar_name);
+
+ if (unlikely (r) != 0)
+ {
+ int printlen = INT_MAX;
+
+ if (should_truncate_fname ())
+ {
+ /* Try to truncate the name. First find out by how
+ much. */
+ printlen = name_max;
+ char truncfname[name_max + 1];
+ *((char *) mempcpy (truncfname, arhdr->ar_name,
+ name_max)) = '\0';
+
+ if (dont_replace_existing)
+ {
+ r = link (tempfname, truncfname);
+ if (likely (r == 0))
+ unlink (tempfname);
+ }
+ else
+ r = rename (tempfname, truncfname);
+ }
+
+ if (r != 0)
+ {
+ error (0, errno, gettext ("\
+cannot rename temporary file to %.*s"),
+ printlen, arhdr->ar_name);
+ unlink (tempfname);
+ status = 1;
+ }
+ }
+ }
+
+ close (xfd);
+ }
+ }
+
+ next:
+ cmd = elf_next (subelf);
+ if (elf_end (subelf) != 0)
+ error (1, 0, "%s: %s", arfname, elf_errmsg (-1));
+ }
+
+ hdestroy ();
+
+ if (force_symtab)
+ {
+ arlib_finalize ();
+
+ if (symtab.symsnamelen != 0
+ /* We have to rewrite the file also if it initially had an index
+ but now does not need one anymore. */
+ || (symtab.symsnamelen == 0 && index_size != 0))
+ {
+ char tmpfname[strlen (arfname) + 7];
+ strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
+ int newfd = mkstemp (tmpfname);
+ if (unlikely (newfd == -1))
+ {
+ nonew:
+ error (0, errno, gettext ("cannot create new file"));
+ status = 1;
+ }
+ else
+ {
+ /* Create the header. */
+ if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
+ {
+ // XXX Use /prof/self/fd/%d ???
+ nonew_unlink:
+ unlink (tmpfname);
+ if (newfd != -1)
+ close (newfd);
+ goto nonew;
+ }
+
+ /* Create the new file. There are three parts as far we are
+ concerned: 1. original context before the index, 2. the
+ new index, 3. everything after the new index. */
+ off_t rest_off;
+ if (index_off != -1)
+ rest_off = (index_off + sizeof (struct ar_hdr)
+ + ((index_size + 1) & ~1ul));
+ else
+ rest_off = SARMAG;
+
+ if ((symtab.symsnamelen != 0
+ && ((write_retry (newfd, symtab.symsoff,
+ symtab.symsofflen)
+ != (ssize_t) symtab.symsofflen)
+ || (write_retry (newfd, symtab.symsname,
+ symtab.symsnamelen)
+ != (ssize_t) symtab.symsnamelen)))
+ /* Even if the original file had content before the
+ symbol table, we write it in the correct order. */
+ || (index_off != SARMAG
+ && copy_content (elf, newfd, SARMAG, index_off - SARMAG))
+ || copy_content (elf, newfd, rest_off, st.st_size - rest_off)
+ /* Set the mode of the new file to the same values the
+ original file has. */
+ || fchmod (newfd, st.st_mode & ALLPERMS) != 0
+ /* Never complain about fchown failing. */
+ || (({asm ("" :: "r" (fchown (newfd, st.st_uid,
+ st.st_gid))); }),
+ close (newfd) != 0)
+ || (newfd = -1, rename (tmpfname, arfname) != 0))
+ goto nonew_unlink;
+ }
+ }
+ }
+
+ elf_end (elf);
+
+ close (fd);
+
+ not_found (argc, argv, found);
+
+ return status;
+}
+
+
+struct armem
+{
+ off_t off;
+ off_t old_off;
+ size_t size;
+ long int long_name_off;
+ struct armem *next;
+ void *mem;
+ time_t sec;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ const char *name;
+ Elf *elf;
+};
+
+
+static int
+write_member (struct armem *memb, off_t *startp, off_t *lenp, Elf *elf,
+ off_t end_off, int newfd)
+{
+ struct ar_hdr arhdr;
+ /* The ar_name is not actually zero teminated, but we need that for
+ snprintf. Also if the name is too long, then the string starts
+ with '/' plus an index off number (decimal). */
+ char tmpbuf[sizeof (arhdr.ar_name) + 2];
+
+ bool changed_header = memb->long_name_off != -1;
+ if (changed_header)
+ {
+ /* In case of a long file name we assume the archive header
+ changed and we write it here. */
+ memcpy (&arhdr, elf_rawfile (elf, NULL) + *startp, sizeof (arhdr));
+
+ snprintf (tmpbuf, sizeof (tmpbuf), "/%-*ld",
+ (int) sizeof (arhdr.ar_name), memb->long_name_off);
+ changed_header = memcmp (arhdr.ar_name, tmpbuf,
+ sizeof (arhdr.ar_name)) != 0;
+ }
+
+ /* If the files are adjacent in the old file extend the range. */
+ if (*startp != -1 && !changed_header && *startp + *lenp == memb->old_off)
+ {
+ /* Extend the current range. */
+ *lenp += (memb->next != NULL
+ ? memb->next->off : end_off) - memb->off;
+ return 0;
+ }
+
+ /* Write out the old range. */
+ if (*startp != -1 && copy_content (elf, newfd, *startp, *lenp))
+ return -1;
+
+ *startp = memb->old_off;
+ *lenp = (memb->next != NULL ? memb->next->off : end_off) - memb->off;
+
+ if (changed_header)
+ {
+ memcpy (arhdr.ar_name, tmpbuf, sizeof (arhdr.ar_name));
+
+ if (unlikely (write_retry (newfd, &arhdr, sizeof (arhdr))
+ != sizeof (arhdr)))
+ return -1;
+
+ *startp += sizeof (struct ar_hdr);
+ assert ((size_t) *lenp >= sizeof (struct ar_hdr));
+ *lenp -= sizeof (struct ar_hdr);
+ }
+
+ return 0;
+}
+
+/* Store the name in the long name table if necessary.
+ Record its offset or -1 if we did not need to use the table. */
+static void
+remember_long_name (struct armem *mem, const char *name, size_t namelen)
+{
+ mem->long_name_off = (namelen > MAX_AR_NAME_LEN
+ ? arlib_add_long_name (name, namelen)
+ : -1l);
+}
+
+static int
+do_oper_delete (const char *arfname, char **argv, int argc,
+ long int instance)
+{
+ bool *found = alloca (sizeof (bool) * argc);
+ memset (found, '\0', sizeof (bool) * argc);
+
+ /* List of the files we keep. */
+ struct armem *to_copy = NULL;
+
+ int status = 0;
+ Elf *elf;
+ struct stat st;
+ int fd = open_archive (arfname, O_RDONLY, 0, &elf, &st, false);
+
+ if (hcreate (2 * argc) == 0)
+ error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
+
+ for (int cnt = 0; cnt < argc; ++cnt)
+ {
+ ENTRY entry = { .key = argv[cnt], .data = &argv[cnt] };
+ if (hsearch (entry, ENTER) == NULL)
+ error (EXIT_FAILURE, errno,
+ gettext ("cannot insert into hash table"));
+ }
+
+ arlib_init ();
+
+ off_t cur_off = SARMAG;
+ Elf_Cmd cmd = ELF_C_READ_MMAP;
+ Elf *subelf;
+ while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
+ {
+ Elf_Arhdr *arhdr = elf_getarhdr (subelf);
+
+ /* Ignore the symbol table and the long file name table here. */
+ if (strcmp (arhdr->ar_name, "/") == 0
+ || strcmp (arhdr->ar_name, "//") == 0)
+ goto next;
+
+ bool do_delete = argc <= 0;
+ if (!do_delete)
+ {
+ ENTRY entry;
+ entry.key = arhdr->ar_name;
+ ENTRY *res = hsearch (entry, FIND);
+ if (res != NULL && (instance < 0 || instance-- == 0)
+ && !found[(char **) res->data - argv])
+ found[(char **) res->data - argv] = do_delete = true;
+ }
+
+ if (do_delete)
+ {
+ if (verbose)
+ printf ("d - %s\n", arhdr->ar_name);
+ }
+ else
+ {
+ struct armem *newp = alloca (sizeof (struct armem));
+ newp->old_off = elf_getaroff (subelf);
+ newp->off = cur_off;
+
+ cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1))
+ + sizeof (struct ar_hdr));
+
+ if (to_copy == NULL)
+ to_copy = newp->next = newp;
+ else
+ {
+ newp->next = to_copy->next;
+ to_copy = to_copy->next = newp;
+ }
+
+ /* If we recreate the symbol table read the file's symbol
+ table now. */
+ arlib_add_symbols (subelf, arfname, arhdr->ar_name, newp->off);
+
+ /* Remember long file names. */
+ remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name));
+ }
+
+ next:
+ cmd = elf_next (subelf);
+ if (elf_end (subelf) != 0)
+ error (1, 0, "%s: %s", arfname, elf_errmsg (-1));
+ }
+
+ arlib_finalize ();
+
+ hdestroy ();
+
+ /* Create a new, temporary file in the same directory as the
+ original file. */
+ char tmpfname[strlen (arfname) + 7];
+ strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
+ int newfd = mkstemp (tmpfname);
+ if (unlikely (newfd == -1))
+ goto nonew;
+
+ /* Create the header. */
+ if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
+ {
+ // XXX Use /prof/self/fd/%d ???
+ nonew_unlink:
+ unlink (tmpfname);
+ if (newfd != -1)
+ close (newfd);
+ nonew:
+ error (0, errno, gettext ("cannot create new file"));
+ status = 1;
+ goto errout;
+ }
+
+ /* If the archive is empty that is all we have to do. */
+ if (likely (to_copy != NULL))
+ {
+ /* Write the symbol table or the long file name table or both. */
+ if (symtab.symsnamelen != 0
+ && ((write_retry (newfd, symtab.symsoff, symtab.symsofflen)
+ != (ssize_t) symtab.symsofflen)
+ || (write_retry (newfd, symtab.symsname, symtab.symsnamelen)
+ != (ssize_t) symtab.symsnamelen)))
+ goto nonew_unlink;
+
+ if (symtab.longnameslen > sizeof (struct ar_hdr)
+ && (write_retry (newfd, symtab.longnames, symtab.longnameslen)
+ != (ssize_t) symtab.longnameslen))
+ goto nonew_unlink;
+
+ /* NULL-terminate the list of files to copy. */
+ struct armem *last = to_copy;
+ to_copy = to_copy->next;
+ last->next = NULL;
+
+ off_t start = -1;
+ off_t len = -1;
+
+ do
+ if (write_member (to_copy, &start, &len, elf, cur_off, newfd) != 0)
+ goto nonew_unlink;
+ while ((to_copy = to_copy->next) != NULL);
+
+ /* Write the last part. */
+ if (copy_content (elf, newfd, start, len))
+ goto nonew_unlink;
+ }
+
+ /* Set the mode of the new file to the same values the original file
+ has. */
+ if (fchmod (newfd, st.st_mode & ALLPERMS) != 0
+ /* Never complain about fchown failing. */
+ || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }),
+ close (newfd) != 0)
+ || (newfd = -1, rename (tmpfname, arfname) != 0))
+ goto nonew_unlink;
+
+ errout:
+ elf_end (elf);
+
+ arlib_fini ();
+
+ close (fd);
+
+ not_found (argc, argv, found);
+
+ return status;
+}
+
+
+/* Prints the given value in the given buffer without a trailing zero char.
+ Returns false if the given value doesn't fit in the given buffer. */
+static bool
+no0print (bool ofmt, char *buf, int bufsize, long int val)
+{
+ char tmpbuf[bufsize + 1];
+ int ret = snprintf (tmpbuf, sizeof (tmpbuf), ofmt ? "%-*lo" : "%-*ld",
+ bufsize, val);
+ if (ret >= (int) sizeof (tmpbuf))
+ return false;
+ memcpy (buf, tmpbuf, bufsize);
+ return true;
+}
+
+
+static int
+do_oper_insert (int oper, const char *arfname, char **argv, int argc,
+ const char *member)
+{
+ int status = 0;
+ Elf *elf = NULL;
+ struct stat st;
+ int fd = open_archive (arfname, O_RDONLY, 0, &elf, &st, oper != oper_move);
+
+ /* List of the files we keep. */
+ struct armem *all = NULL;
+ struct armem *after_memberelem = NULL;
+ struct armem **found = alloca (sizeof (*found) * argc);
+ memset (found, '\0', sizeof (*found) * argc);
+
+ arlib_init ();
+
+ /* Initialize early for no_old case. */
+ off_t cur_off = SARMAG;
+
+ if (fd == -1)
+ {
+ if (!suppress_create_msg)
+ fprintf (stderr, "%s: creating %s\n",
+ program_invocation_short_name, arfname);
+
+ goto no_old;
+ }
+
+ /* Store the names of all files from the command line in a hash
+ table so that we can match it. Note that when no file name is
+ given we are basically doing nothing except recreating the
+ index. */
+ if (oper != oper_qappend)
+ {
+ if (hcreate (2 * argc) == 0)
+ error (EXIT_FAILURE, errno, gettext ("cannot create hash table"));
+
+ for (int cnt = 0; cnt < argc; ++cnt)
+ {
+ ENTRY entry;
+ entry.key = full_path ? argv[cnt] : basename (argv[cnt]);
+ entry.data = &argv[cnt];
+ if (hsearch (entry, ENTER) == NULL)
+ error (EXIT_FAILURE, errno,
+ gettext ("cannot insert into hash table"));
+ }
+ }
+
+ /* While iterating over the current content of the archive we must
+ determine a number of things: which archive members to keep,
+ which are replaced, and where to insert the new members. */
+ Elf_Cmd cmd = ELF_C_READ_MMAP;
+ Elf *subelf;
+ while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
+ {
+ Elf_Arhdr *arhdr = elf_getarhdr (subelf);
+
+ /* Ignore the symbol table and the long file name table here. */
+ if (strcmp (arhdr->ar_name, "/") == 0
+ || strcmp (arhdr->ar_name, "//") == 0)
+ goto next;
+
+ struct armem *newp = alloca (sizeof (struct armem));
+ newp->old_off = elf_getaroff (subelf);
+ newp->size = arhdr->ar_size;
+ newp->sec = arhdr->ar_date;
+ newp->mem = NULL;
+
+ /* Remember long file names. */
+ remember_long_name (newp, arhdr->ar_name, strlen (arhdr->ar_name));
+
+ /* Check whether this is a file we are looking for. */
+ if (oper != oper_qappend)
+ {
+ /* Check whether this is the member used as the insert point. */
+ if (member != NULL && strcmp (arhdr->ar_name, member) == 0)
+ {
+ /* Note that all == NULL means insert at the beginning. */
+ if (ipos == ipos_before)
+ after_memberelem = all;
+ else
+ after_memberelem = newp;
+ member = NULL;
+ }
+
+ ENTRY entry;
+ entry.key = arhdr->ar_name;
+ ENTRY *res = hsearch (entry, FIND);
+ if (res != NULL && found[(char **) res->data - argv] == NULL)
+ {
+ found[(char **) res->data - argv] = newp;
+
+ /* If we insert before or after a certain element move
+ all files to a special list. */
+ if (unlikely (ipos != ipos_none || oper == oper_move))
+ {
+ if (after_memberelem == newp)
+ /* Since we remove this element even though we should
+ insert everything after it, we in fact insert
+ everything after the previous element. */
+ after_memberelem = all;
+
+ goto next;
+ }
+ }
+ }
+
+ if (all == NULL)
+ all = newp->next = newp;
+ else
+ {
+ newp->next = all->next;
+ all = all->next = newp;
+ }
+
+ next:
+ cmd = elf_next (subelf);
+ if (elf_end (subelf) != 0)
+ error (EXIT_FAILURE, 0, "%s: %s", arfname, elf_errmsg (-1));
+ }
+
+ if (oper != oper_qappend)
+ hdestroy ();
+
+ no_old:
+ if (member != NULL)
+ error (EXIT_FAILURE, 0, gettext ("position member %s not found"),
+ member);
+
+ if (oper == oper_move)
+ {
+ /* Make sure all requested elements are found in the archive. */
+ for (int cnt = 0; cnt < argc; ++cnt)
+ {
+ if (found[cnt] == NULL)
+ {
+ fprintf (stderr, gettext ("%s: no entry %s in archive!\n"),
+ program_invocation_short_name, argv[cnt]);
+ status = 1;
+ }
+
+ if (verbose)
+ printf ("m - %s\n", argv[cnt]);
+ }
+ }
+ else
+ {
+ /* Open all the new files, get their sizes and add all symbols. */
+ for (int cnt = 0; cnt < argc; ++cnt)
+ {
+ const char *bname = basename (argv[cnt]);
+ size_t bnamelen = strlen (bname);
+ if (found[cnt] == NULL)
+ {
+ found[cnt] = alloca (sizeof (struct armem));
+ found[cnt]->old_off = -1;
+
+ remember_long_name (found[cnt], bname, bnamelen);
+ }
+
+ struct stat newst;
+ Elf *newelf;
+ int newfd = open (argv[cnt], O_RDONLY);
+ if (newfd == -1)
+ {
+ error (0, errno, gettext ("cannot open %s"), argv[cnt]);
+ status = 1;
+ }
+ else if (fstat (newfd, &newst) == -1)
+ {
+ error (0, errno, gettext ("cannot stat %s"), argv[cnt]);
+ close (newfd);
+ status = 1;
+ }
+ else if (!S_ISREG (newst.st_mode))
+ {
+ error (0, errno, gettext ("%s is no regular file"), argv[cnt]);
+ close (newfd);
+ status = 1;
+ }
+ else if (update_newer
+ && found[cnt]->old_off != -1l
+ && found[cnt]->sec > st.st_mtime)
+ /* Do nothing, the file in the archive is younger. */
+ close (newfd);
+ else if ((newelf = elf_begin (newfd, ELF_C_READ_MMAP, NULL))
+ == NULL)
+ {
+ fprintf (stderr,
+ gettext ("cannot get ELF descriptor for %s: %s\n"),
+ argv[cnt], elf_errmsg (-1));
+ status = 1;
+ }
+ else
+ {
+ if (verbose)
+ printf ("%c - %s\n",
+ found[cnt]->old_off == -1l ? 'a' : 'r', argv[cnt]);
+
+ found[cnt]->elf = newelf;
+ found[cnt]->sec = arlib_deterministic_output ? 0 : newst.st_mtime;
+ found[cnt]->uid = arlib_deterministic_output ? 0 : newst.st_uid;
+ found[cnt]->gid = arlib_deterministic_output ? 0 : newst.st_gid;
+ found[cnt]->mode = newst.st_mode;
+ found[cnt]->name = bname;
+
+ found[cnt]->mem = elf_rawfile (newelf, &found[cnt]->size);
+ if (found[cnt]->mem == NULL
+ || elf_cntl (newelf, ELF_C_FDDONE) != 0)
+ error (EXIT_FAILURE, 0, gettext ("cannot read %s: %s"),
+ argv[cnt], elf_errmsg (-1));
+
+ close (newfd);
+
+ if (found[cnt]->old_off != -1l)
+ /* Remember long file names. */
+ remember_long_name (found[cnt], bname, bnamelen);
+ }
+ }
+ }
+
+ if (status != 0)
+ {
+ elf_end (elf);
+
+ arlib_fini ();
+
+ close (fd);
+
+ return status;
+ }
+
+ /* If we have no entry point so far add at the end. AFTER_MEMBERELEM
+ being NULL when adding before an entry means add at the beginning. */
+ if (ipos != ipos_before && after_memberelem == NULL)
+ after_memberelem = all;
+
+ /* Convert the circular list into a normal list first. */
+ if (all != NULL)
+ {
+ struct armem *tmp = all;
+ all = all->next;
+ tmp->next = NULL;
+ }
+
+ struct armem *last_added = after_memberelem;
+ for (int cnt = 0; cnt < argc; ++cnt)
+ if (oper != oper_replace || found[cnt]->old_off == -1)
+ {
+ if (last_added == NULL)
+ {
+ found[cnt]->next = all;
+ last_added = all = found[cnt];
+ }
+ else
+ {
+ found[cnt]->next = last_added->next;
+ last_added = last_added->next = found[cnt];
+ }
+ }
+
+ /* Finally compute the offset and add the symbols for the files
+ after the insert point. */
+ if (likely (all != NULL))
+ for (struct armem *memp = all; memp != NULL; memp = memp->next)
+ {
+ memp->off = cur_off;
+
+ if (memp->mem == NULL)
+ {
+ Elf_Arhdr *arhdr;
+ /* Fake initializing arhdr and subelf to keep gcc calm. */
+ asm ("" : "=m" (arhdr), "=m" (subelf));
+ if (elf_rand (elf, memp->old_off) == 0
+ || (subelf = elf_begin (fd, ELF_C_READ_MMAP, elf)) == NULL
+ || (arhdr = elf_getarhdr (subelf)) == NULL)
+ /* This should never happen since we already looked at the
+ archive content. But who knows... */
+ error (EXIT_FAILURE, 0, "%s: %s", arfname, elf_errmsg (-1));
+
+ arlib_add_symbols (subelf, arfname, arhdr->ar_name, cur_off);
+
+ elf_end (subelf);
+ }
+ else
+ arlib_add_symbols (memp->elf, arfname, memp->name, cur_off);
+
+ cur_off += (((memp->size + 1) & ~((off_t) 1))
+ + sizeof (struct ar_hdr));
+ }
+
+ /* Now we have all the information for the symbol table and long
+ file name table. Construct the final layout. */
+ arlib_finalize ();
+
+ /* Create a new, temporary file in the same directory as the
+ original file. */
+ char tmpfname[strlen (arfname) + 7];
+ strcpy (stpcpy (tmpfname, arfname), "XXXXXX");
+ int newfd;
+ if (fd != -1)
+ newfd = mkstemp (tmpfname);
+ else
+ {
+ newfd = open (arfname, O_RDWR | O_CREAT | O_EXCL, DEFFILEMODE);
+ if (newfd == -1 && errno == EEXIST)
+ /* Bah, first the file did not exist, now it does. Restart. */
+ return do_oper_insert (oper, arfname, argv, argc, member);
+ }
+ if (unlikely (newfd == -1))
+ goto nonew;
+
+ /* Create the header. */
+ if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
+ {
+ nonew_unlink:
+ if (fd != -1)
+ {
+ // XXX Use /prof/self/fd/%d ???
+ unlink (tmpfname);
+ if (newfd != -1)
+ close (newfd);
+ }
+ nonew:
+ error (0, errno, gettext ("cannot create new file"));
+ status = 1;
+ goto errout;
+ }
+
+ /* If the new archive is not empty we actually have something to do. */
+ if (likely (all != NULL))
+ {
+ /* Write the symbol table or the long file name table or both. */
+ if (symtab.symsnamelen != 0
+ && ((write_retry (newfd, symtab.symsoff, symtab.symsofflen)
+ != (ssize_t) symtab.symsofflen)
+ || (write_retry (newfd, symtab.symsname, symtab.symsnamelen)
+ != (ssize_t) symtab.symsnamelen)))
+ goto nonew_unlink;
+
+ if (symtab.longnameslen > sizeof (struct ar_hdr)
+ && (write_retry (newfd, symtab.longnames, symtab.longnameslen)
+ != (ssize_t) symtab.longnameslen))
+ goto nonew_unlink;
+
+ off_t start = -1;
+ off_t len = -1;
+
+ while (all != NULL)
+ {
+ if (all->mem != NULL)
+ {
+ /* This is a new file. If there is anything from the
+ archive left to be written do it now. */
+ if (start != -1 && copy_content (elf, newfd, start, len))
+ goto nonew_unlink;
+
+ start = -1;
+ len = -1;
+
+ /* Create the header. */
+ struct ar_hdr arhdr;
+ /* The ar_name is not actually zero teminated, but we
+ need that for snprintf. Also if the name is too
+ long, then the string starts with '/' plus an index
+ off number (decimal). */
+ char tmpbuf[sizeof (arhdr.ar_name) + 2];
+ if (all->long_name_off == -1)
+ {
+ size_t namelen = strlen (all->name);
+ char *p = mempcpy (arhdr.ar_name, all->name, namelen);
+ *p++ = '/';
+ memset (p, ' ', sizeof (arhdr.ar_name) - namelen - 1);
+ }
+ else
+ {
+ snprintf (tmpbuf, sizeof (tmpbuf), "/%-*ld",
+ (int) sizeof (arhdr.ar_name), all->long_name_off);
+ memcpy (arhdr.ar_name, tmpbuf, sizeof (arhdr.ar_name));
+ }
+
+ if (! no0print (false, arhdr.ar_date, sizeof (arhdr.ar_date),
+ all->sec))
+ {
+ error (0, errno, gettext ("cannot represent ar_date"));
+ goto nonew_unlink;
+ }
+ if (! no0print (false, arhdr.ar_uid, sizeof (arhdr.ar_uid),
+ all->uid))
+ {
+ error (0, errno, gettext ("cannot represent ar_uid"));
+ goto nonew_unlink;
+ }
+ if (! no0print (false, arhdr.ar_gid, sizeof (arhdr.ar_gid),
+ all->gid))
+ {
+ error (0, errno, gettext ("cannot represent ar_gid"));
+ goto nonew_unlink;
+ }
+ if (! no0print (true, arhdr.ar_mode, sizeof (arhdr.ar_mode),
+ all->mode))
+ {
+ error (0, errno, gettext ("cannot represent ar_mode"));
+ goto nonew_unlink;
+ }
+ if (! no0print (false, arhdr.ar_size, sizeof (arhdr.ar_size),
+ all->size))
+ {
+ error (0, errno, gettext ("cannot represent ar_size"));
+ goto nonew_unlink;
+ }
+ memcpy (arhdr.ar_fmag, ARFMAG, sizeof (arhdr.ar_fmag));
+
+ if (unlikely (write_retry (newfd, &arhdr, sizeof (arhdr))
+ != sizeof (arhdr)))
+ goto nonew_unlink;
+
+ /* Now the file itself. */
+ if (unlikely (write_retry (newfd, all->mem, all->size)
+ != (off_t) all->size))
+ goto nonew_unlink;
+
+ /* Pad the file if its size is odd. */
+ if ((all->size & 1) != 0)
+ if (unlikely (write_retry (newfd, "\n", 1) != 1))
+ goto nonew_unlink;
+ }
+ else
+ {
+ /* This is a member from the archive. */
+ if (write_member (all, &start, &len, elf, cur_off, newfd)
+ != 0)
+ goto nonew_unlink;
+ }
+
+ all = all->next;
+ }
+
+ /* Write the last part. */
+ if (start != -1 && copy_content (elf, newfd, start, len))
+ goto nonew_unlink;
+ }
+
+ /* Set the mode of the new file to the same values the original file
+ has. */
+ if (fd != -1
+ && (fchmod (newfd, st.st_mode & ALLPERMS) != 0
+ /* Never complain about fchown failing. */
+ || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }),
+ close (newfd) != 0)
+ || (newfd = -1, rename (tmpfname, arfname) != 0)))
+ goto nonew_unlink;
+
+ errout:
+ for (int cnt = 0; cnt < argc; ++cnt)
+ elf_end (found[cnt]->elf);
+
+ elf_end (elf);
+
+ arlib_fini ();
+
+ if (fd != -1)
+ close (fd);
+
+ return status;
+}
+
+
+#include "debugpred.h"
diff --git a/src/arlib-argp.c b/src/arlib-argp.c
new file mode 100644
index 0000000..1bdd8d0
--- /dev/null
+++ b/src/arlib-argp.c
@@ -0,0 +1,94 @@
+/* Options common to ar and ranlib.
+ Copyright (C) 2012 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <libintl.h>
+
+#include "arlib.h"
+
+bool arlib_deterministic_output = DEFAULT_AR_DETERMINISTIC;
+
+static const struct argp_option options[] =
+ {
+ { NULL, 'D', NULL, 0,
+ N_("Use zero for uid, gid, and date in archive members."), 0 },
+ { NULL, 'U', NULL, 0,
+ N_("Use actual uid, gid, and date in archive members."), 0 },
+
+ { NULL, 0, NULL, 0, NULL, 0 }
+ };
+
+static error_t
+parse_opt (int key, char *arg __attribute__ ((unused)),
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case 'D':
+ arlib_deterministic_output = true;
+ break;
+
+ case 'U':
+ arlib_deterministic_output = false;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static char *
+help_filter (int key, const char *text, void *input __attribute__ ((unused)))
+{
+ inline char *text_for_default (void)
+ {
+ char *new_text;
+ if (unlikely (asprintf (&new_text, gettext ("%s (default)"), text) < 0))
+ return (char *) text;
+ return new_text;
+ }
+
+ switch (key)
+ {
+ case 'D':
+ if (DEFAULT_AR_DETERMINISTIC)
+ return text_for_default ();
+ break;
+ case 'U':
+ if (! DEFAULT_AR_DETERMINISTIC)
+ return text_for_default ();
+ break;
+ }
+
+ return (char *) text;
+}
+
+static const struct argp argp =
+ {
+ options, parse_opt, NULL, NULL, NULL, help_filter, NULL
+ };
+
+const struct argp_child arlib_argp_children[] =
+ {
+ { &argp, 0, "", 2 },
+ { NULL, 0, NULL, 0 }
+ };
diff --git a/src/arlib.c b/src/arlib.c
new file mode 100644
index 0000000..e0839aa
--- /dev/null
+++ b/src/arlib.c
@@ -0,0 +1,277 @@
+/* Functions to handle creation of Linux archives.
+ Copyright (C) 2007-2012, 2016 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2007.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <error.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <libeu.h>
+
+#include "arlib.h"
+
+
+/* The one symbol table we hanble. */
+struct arlib_symtab symtab;
+
+
+/* Initialize ARLIB_SYMTAB structure. */
+void
+arlib_init (void)
+{
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+ obstack_init (&symtab.symsoffob);
+ obstack_init (&symtab.symsnameob);
+ obstack_init (&symtab.longnamesob);
+
+ /* We add the archive header here as well, that avoids allocating
+ another memory block. */
+ struct ar_hdr ar_hdr;
+ memcpy (ar_hdr.ar_name, "/ ", sizeof (ar_hdr.ar_name));
+ /* Using snprintf here has a problem: the call always wants to add a
+ NUL byte. We could use a trick whereby we specify the target
+ buffer size longer than it is and this would not actually fail,
+ since all the fields are consecutive and we fill them in
+ sequence (i.e., the NUL byte gets overwritten). But
+ _FORTIFY_SOURCE=2 would not let us play these games. Therefore
+ we play it safe. */
+ char tmpbuf[sizeof (ar_hdr.ar_date) + 1];
+ int s = snprintf (tmpbuf, sizeof (tmpbuf), "%-*lld",
+ (int) sizeof (ar_hdr.ar_date),
+ (arlib_deterministic_output ? 0
+ : (long long int) time (NULL)));
+ memcpy (ar_hdr.ar_date, tmpbuf, s);
+ assert ((sizeof (struct ar_hdr) % sizeof (uint32_t)) == 0);
+
+ /* Note the string for the ar_uid and ar_gid cases is longer than
+ necessary. This does not matter since we copy only as much as
+ necessary but it helps the compiler to use the same string for
+ the ar_mode case. */
+ memcpy (ar_hdr.ar_uid, "0 ", sizeof (ar_hdr.ar_uid));
+ memcpy (ar_hdr.ar_gid, "0 ", sizeof (ar_hdr.ar_gid));
+ memcpy (ar_hdr.ar_mode, "0 ", sizeof (ar_hdr.ar_mode));
+ memcpy (ar_hdr.ar_fmag, ARFMAG, sizeof (ar_hdr.ar_fmag));
+
+ /* Add the archive header to the file content. */
+ obstack_grow (&symtab.symsoffob, &ar_hdr, sizeof (ar_hdr));
+
+ /* The first word in the offset table specifies the size. Create
+ such an entry now. The real value will be filled-in later. For
+ all supported platforms the following is true. */
+ assert (sizeof (uint32_t) == sizeof (int));
+ obstack_int_grow (&symtab.symsoffob, 0);
+
+ /* The long name obstack also gets its archive header. As above,
+ some of the input strings are longer than required but we only
+ copy the necessary part. */
+ memcpy (ar_hdr.ar_name, "// ", sizeof (ar_hdr.ar_name));
+ memcpy (ar_hdr.ar_date, " ", sizeof (ar_hdr.ar_date));
+ memcpy (ar_hdr.ar_uid, " ", sizeof (ar_hdr.ar_uid));
+ memcpy (ar_hdr.ar_gid, " ", sizeof (ar_hdr.ar_gid));
+ memcpy (ar_hdr.ar_mode, " ", sizeof (ar_hdr.ar_mode));
+ /* The ar_size field will be filled in later and ar_fmag is already OK. */
+ obstack_grow (&symtab.longnamesob, &ar_hdr, sizeof (ar_hdr));
+
+ /* All other members are zero. */
+ symtab.symsofflen = 0;
+ symtab.symsoff = NULL;
+ symtab.symsnamelen = 0;
+ symtab.symsname = NULL;
+}
+
+
+/* Finalize ARLIB_SYMTAB content. */
+void
+arlib_finalize (void)
+{
+ /* Note that the size is stored as decimal string in 10 chars,
+ without zero terminator (we add + 1 here only so snprintf can
+ put it at the end, we then don't use it when we memcpy it). */
+ char tmpbuf[sizeof (((struct ar_hdr *) NULL)->ar_size) + 1];
+
+ symtab.longnameslen = obstack_object_size (&symtab.longnamesob);
+ if (symtab.longnameslen != sizeof (struct ar_hdr))
+ {
+ if ((symtab.longnameslen & 1) != 0)
+ {
+ /* Add one more byte to make length even. */
+ obstack_grow (&symtab.longnamesob, "\n", 1);
+ ++symtab.longnameslen;
+ }
+
+ symtab.longnames = obstack_finish (&symtab.longnamesob);
+
+ int s = snprintf (tmpbuf, sizeof (tmpbuf), "%-*" PRIu32 "",
+ (int) sizeof (((struct ar_hdr *) NULL)->ar_size),
+ (uint32_t) (symtab.longnameslen - sizeof (struct ar_hdr)));
+ memcpy (&((struct ar_hdr *) symtab.longnames)->ar_size, tmpbuf, s);
+ }
+
+ symtab.symsofflen = obstack_object_size (&symtab.symsoffob);
+ assert (symtab.symsofflen % sizeof (uint32_t) == 0);
+ if (symtab.symsofflen != 0)
+ {
+ symtab.symsoff = (uint32_t *) obstack_finish (&symtab.symsoffob);
+
+ /* Fill in the number of offsets now. */
+ symtab.symsoff[AR_HDR_WORDS] = le_bswap_32 ((symtab.symsofflen
+ - sizeof (struct ar_hdr))
+ / sizeof (uint32_t) - 1);
+ }
+
+ symtab.symsnamelen = obstack_object_size (&symtab.symsnameob);
+ if ((symtab.symsnamelen & 1) != 0)
+ {
+ /* Add one more NUL byte to make length even. */
+ obstack_grow (&symtab.symsnameob, "", 1);
+ ++symtab.symsnamelen;
+ }
+ symtab.symsname = obstack_finish (&symtab.symsnameob);
+
+ /* Determine correction for the offsets in the symbol table. */
+ off_t disp = 0;
+ if (symtab.symsnamelen > 0)
+ disp = symtab.symsofflen + symtab.symsnamelen;
+ if (symtab.longnameslen > sizeof (struct ar_hdr))
+ disp += symtab.longnameslen;
+
+ if (disp != 0 && symtab.symsoff != NULL)
+ {
+ uint32_t nsyms = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS]);
+
+ for (uint32_t cnt = 1; cnt <= nsyms; ++cnt)
+ {
+ uint32_t val = le_bswap_32 (symtab.symsoff[AR_HDR_WORDS + cnt]);
+ val += disp;
+ symtab.symsoff[AR_HDR_WORDS + cnt] = le_bswap_32 (val);
+ }
+ }
+
+ /* See comment for ar_date above. */
+ memcpy (&((struct ar_hdr *) symtab.symsoff)->ar_size, tmpbuf,
+ snprintf (tmpbuf, sizeof (tmpbuf), "%-*" PRIu32 "",
+ (int) sizeof (((struct ar_hdr *) NULL)->ar_size),
+ (uint32_t) (symtab.symsofflen + symtab.symsnamelen
+ - sizeof (struct ar_hdr))));
+}
+
+
+/* Free resources for ARLIB_SYMTAB. */
+void
+arlib_fini (void)
+{
+ obstack_free (&symtab.symsoffob, NULL);
+ obstack_free (&symtab.symsnameob, NULL);
+ obstack_free (&symtab.longnamesob, NULL);
+}
+
+
+/* Add name a file offset of a symbol. */
+void
+arlib_add_symref (const char *symname, off_t symoff)
+{
+ /* For all supported platforms the following is true. */
+ assert (sizeof (uint32_t) == sizeof (int));
+ obstack_int_grow (&symtab.symsoffob, (int) le_bswap_32 (symoff));
+
+ size_t symname_len = strlen (symname) + 1;
+ obstack_grow (&symtab.symsnameob, symname, symname_len);
+}
+
+
+/* Add symbols from ELF with value OFFSET to the symbol table SYMTAB. */
+void
+arlib_add_symbols (Elf *elf, const char *arfname, const char *membername,
+ off_t off)
+{
+ if (sizeof (off) > sizeof (uint32_t) && off > ~((uint32_t) 0))
+ /* The archive is too big. */
+ error (EXIT_FAILURE, 0, gettext ("the archive '%s' is too large"),
+ arfname);
+
+ /* We only add symbol tables for ELF files. It makes not much sense
+ to add symbols from executables but we do so for compatibility.
+ For DSOs and executables we use the dynamic symbol table, for
+ relocatable files all the DT_SYMTAB tables. */
+ if (elf_kind (elf) != ELF_K_ELF)
+ return;
+
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ error (EXIT_FAILURE, 0, gettext ("cannot read ELF header of %s(%s): %s"),
+ arfname, membername, elf_errmsg (-1));
+
+ GElf_Word symtype;
+ if (ehdr->e_type == ET_REL)
+ symtype = SHT_SYMTAB;
+ else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN)
+ symtype = SHT_DYNSYM;
+ else
+ /* We do not handle that type. */
+ return;
+
+ /* Iterate over all sections. */
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ /* Get the section header. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ continue;
+
+ if (shdr->sh_type != symtype)
+ continue;
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ continue;
+
+ int nsyms = shdr->sh_size / shdr->sh_entsize;
+ for (int ndx = shdr->sh_info; ndx < nsyms; ++ndx)
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (data, ndx, &sym_mem);
+ if (sym == NULL)
+ continue;
+
+ /* Ignore undefined symbols. */
+ if (sym->st_shndx == SHN_UNDEF)
+ continue;
+
+ /* Use this symbol. */
+ const char *symname = elf_strptr (elf, shdr->sh_link, sym->st_name);
+ if (symname != NULL)
+ arlib_add_symref (symname, off);
+ }
+
+ /* Only relocatable files can have more than one symbol table. */
+ if (ehdr->e_type != ET_REL)
+ break;
+ }
+}
diff --git a/src/arlib.h b/src/arlib.h
new file mode 100644
index 0000000..e117166
--- /dev/null
+++ b/src/arlib.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 2007-2012 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2007.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _ARLIB_H
+#define _ARLIB_H 1
+
+#include <ar.h>
+#include <argp.h>
+#include <byteswap.h>
+#include <endian.h>
+#include <libelf.h>
+#include <obstack.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+
+/* State of -D/-U flags. */
+extern bool arlib_deterministic_output;
+
+/* For options common to ar and ranlib. */
+extern const struct argp_child arlib_argp_children[];
+
+
+/* Maximum length of a file name that fits directly into the ar header.
+ We cannot use the final byte since a / goes there. */
+#define MAX_AR_NAME_LEN (sizeof (((struct ar_hdr *) NULL)->ar_name) - 1)
+
+
+/* Words matching in size to archive header. */
+#define AR_HDR_WORDS (sizeof (struct ar_hdr) / sizeof (uint32_t))
+
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define le_bswap_32(val) bswap_32 (val)
+#else
+# define le_bswap_32(val) (val)
+#endif
+
+
+/* Symbol table type. */
+struct arlib_symtab
+{
+ /* Symbol table handling. */
+ struct obstack symsoffob;
+ struct obstack symsnameob;
+ size_t symsofflen;
+ uint32_t *symsoff;
+ size_t symsnamelen;
+ char *symsname;
+
+ /* Long filename handling. */
+ struct obstack longnamesob;
+ size_t longnameslen;
+ char *longnames;
+};
+
+
+/* Global variable with symbol table. */
+extern struct arlib_symtab symtab;
+
+
+/* Initialize ARLIB_SYMTAB structure. */
+extern void arlib_init (void);
+
+/* Finalize ARLIB_SYMTAB content. */
+extern void arlib_finalize (void);
+
+/* Free resources for ARLIB_SYMTAB. */
+extern void arlib_fini (void);
+
+/* Add symbols from ELF with value OFFSET to the symbol table SYMTAB. */
+extern void arlib_add_symbols (Elf *elf, const char *arfname,
+ const char *membername, off_t off);
+
+/* Add name a file offset of a symbol. */
+extern void arlib_add_symref (const char *symname, off_t symoff);
+
+/* Add long file name FILENAME of length FILENAMELEN to the symbol table
+ SYMTAB. Return the offset into the long file name table. */
+extern long int arlib_add_long_name (const char *filename, size_t filenamelen);
+
+#endif /* arlib.h */
diff --git a/src/arlib2.c b/src/arlib2.c
new file mode 100644
index 0000000..553fc57
--- /dev/null
+++ b/src/arlib2.c
@@ -0,0 +1,42 @@
+/* Functions to handle creation of Linux archives.
+ Copyright (C) 2007 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2007.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <error.h>
+#include <libintl.h>
+#include <limits.h>
+#include <string.h>
+
+#include "arlib.h"
+
+
+/* Add long file name FILENAME of length FILENAMELEN to the symbol table
+ SYMTAB. Return the offset into the long file name table. */
+long int
+arlib_add_long_name (const char *filename, size_t filenamelen)
+{
+ size_t size = obstack_object_size (&symtab.longnamesob);
+
+ obstack_grow (&symtab.longnamesob, filename, filenamelen);
+ obstack_grow (&symtab.longnamesob, "/\n", 2);
+
+ return size - sizeof (struct ar_hdr);
+}
diff --git a/src/debugpred.h b/src/debugpred.h
new file mode 100644
index 0000000..4845a6e
--- /dev/null
+++ b/src/debugpred.h
@@ -0,0 +1,45 @@
+/* Support to debug branch prediction.
+ Copyright (C) 2007 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2007.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+
+#if DEBUGPRED
+extern const unsigned long int __start_predict_data;
+extern const unsigned long int __stop_predict_data;
+extern const unsigned long int __start_predict_line;
+extern const char *const __start_predict_file;
+
+static void
+__attribute__ ((destructor))
+predprint (void)
+{
+ const unsigned long int *s = &__start_predict_data;
+ const unsigned long int *e = &__stop_predict_data;
+ const unsigned long int *sl = &__start_predict_line;
+ const char *const *sf = &__start_predict_file;
+ while (s < e)
+ {
+ if (s[0] != 0 || s[1] != 0)
+ printf ("%s:%lu: wrong=%lu, correct=%lu%s\n", *sf, *sl, s[0], s[1],
+ s[0] > s[1] ? " <==== WARNING" : "");
+ ++sl;
+ ++sf;
+ s += 2;
+ }
+}
+#endif
diff --git a/src/elfcmp.c b/src/elfcmp.c
new file mode 100644
index 0000000..5046420
--- /dev/null
+++ b/src/elfcmp.c
@@ -0,0 +1,896 @@
+/* Compare relevant content of two ELF files.
+ Copyright (C) 2005-2012, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2005.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <printversion.h>
+#include "../libelf/elf-knowledge.h"
+#include "../libebl/libeblP.h"
+
+
+/* Prototypes of local functions. */
+static Elf *open_file (const char *fname, int *fdp, Ebl **eblp);
+static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx);
+static int regioncompare (const void *p1, const void *p2);
+
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+/* Values for the parameters which have no short form. */
+#define OPT_GAPS 0x100
+#define OPT_HASH_INEXACT 0x101
+#define OPT_IGNORE_BUILD_ID 0x102
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Control options:"), 0 },
+ { "verbose", 'l', NULL, 0,
+ N_("Output all differences, not just the first"), 0 },
+ { "gaps", OPT_GAPS, "ACTION", 0, N_("Control treatment of gaps in loadable segments [ignore|match] (default: ignore)"), 0 },
+ { "hash-inexact", OPT_HASH_INEXACT, NULL, 0,
+ N_("Ignore permutation of buckets in SHT_HASH section"), 0 },
+ { "ignore-build-id", OPT_IGNORE_BUILD_ID, NULL, 0,
+ N_("Ignore differences in build ID"), 0 },
+ { "quiet", 'q', NULL, 0, N_("Output nothing; yield exit status only"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("\
+Compare relevant parts of two ELF files for equality.");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("FILE1 FILE2");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+
+/* How to treat gaps in loadable segments. */
+static enum
+ {
+ gaps_ignore = 0,
+ gaps_match
+ }
+ gaps;
+
+/* Structure to hold information about used regions. */
+struct region
+{
+ GElf_Addr from;
+ GElf_Addr to;
+ struct region *next;
+};
+
+/* Nonzero if only exit status is wanted. */
+static bool quiet;
+
+/* True iff multiple differences should be output. */
+static bool verbose;
+
+/* True iff SHT_HASH treatment should be generous. */
+static bool hash_inexact;
+
+/* True iff build ID notes should be ignored. */
+static bool ignore_build_id;
+
+static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
+
+
+int
+main (int argc, char *argv[])
+{
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ (void) textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ int remaining;
+ (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* We expect exactly two non-option parameters. */
+ if (unlikely (remaining + 2 != argc))
+ {
+ fputs (gettext ("Invalid number of parameters.\n"), stderr);
+ argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
+ exit (1);
+ }
+
+ if (quiet)
+ verbose = false;
+
+ /* Comparing the files is done in two phases:
+ 1. compare all sections. Sections which are irrelevant (i.e., if
+ strip would remove them) are ignored. Some section types are
+ handled special.
+ 2. all parts of the loadable segments which are not parts of any
+ section is compared according to the rules of the --gaps option.
+ */
+ int result = 0;
+ elf_version (EV_CURRENT);
+
+ const char *const fname1 = argv[remaining];
+ int fd1;
+ Ebl *ebl1;
+ Elf *elf1 = open_file (fname1, &fd1, &ebl1);
+
+ const char *const fname2 = argv[remaining + 1];
+ int fd2;
+ Ebl *ebl2;
+ Elf *elf2 = open_file (fname2, &fd2, &ebl2);
+
+ GElf_Ehdr ehdr1_mem;
+ GElf_Ehdr *ehdr1 = gelf_getehdr (elf1, &ehdr1_mem);
+ if (ehdr1 == NULL)
+ error (2, 0, gettext ("cannot get ELF header of '%s': %s"),
+ fname1, elf_errmsg (-1));
+ GElf_Ehdr ehdr2_mem;
+ GElf_Ehdr *ehdr2 = gelf_getehdr (elf2, &ehdr2_mem);
+ if (ehdr2 == NULL)
+ error (2, 0, gettext ("cannot get ELF header of '%s': %s"),
+ fname2, elf_errmsg (-1));
+
+#define DIFFERENCE \
+ do \
+ { \
+ result = 1; \
+ if (! verbose) \
+ goto out; \
+ } \
+ while (0)
+
+ /* Compare the ELF headers. */
+ if (unlikely (memcmp (ehdr1->e_ident, ehdr2->e_ident, EI_NIDENT) != 0
+ || ehdr1->e_type != ehdr2->e_type
+ || ehdr1->e_machine != ehdr2->e_machine
+ || ehdr1->e_version != ehdr2->e_version
+ || ehdr1->e_entry != ehdr2->e_entry
+ || ehdr1->e_phoff != ehdr2->e_phoff
+ || ehdr1->e_flags != ehdr2->e_flags
+ || ehdr1->e_ehsize != ehdr2->e_ehsize
+ || ehdr1->e_phentsize != ehdr2->e_phentsize
+ || ehdr1->e_phnum != ehdr2->e_phnum
+ || ehdr1->e_shentsize != ehdr2->e_shentsize))
+ {
+ if (! quiet)
+ error (0, 0, gettext ("%s %s diff: ELF header"), fname1, fname2);
+ DIFFERENCE;
+ }
+
+ size_t shnum1;
+ size_t shnum2;
+ if (unlikely (elf_getshdrnum (elf1, &shnum1) != 0))
+ error (2, 0, gettext ("cannot get section count of '%s': %s"),
+ fname1, elf_errmsg (-1));
+ if (unlikely (elf_getshdrnum (elf2, &shnum2) != 0))
+ error (2, 0, gettext ("cannot get section count of '%s': %s"),
+ fname2, elf_errmsg (-1));
+ if (unlikely (shnum1 != shnum2))
+ {
+ if (! quiet)
+ error (0, 0, gettext ("%s %s diff: section count"), fname1, fname2);
+ DIFFERENCE;
+ }
+
+ size_t phnum1;
+ size_t phnum2;
+ if (unlikely (elf_getphdrnum (elf1, &phnum1) != 0))
+ error (2, 0, gettext ("cannot get program header count of '%s': %s"),
+ fname1, elf_errmsg (-1));
+ if (unlikely (elf_getphdrnum (elf2, &phnum2) != 0))
+ error (2, 0, gettext ("cannot get program header count of '%s': %s"),
+ fname2, elf_errmsg (-1));
+ if (unlikely (phnum1 != phnum2))
+ {
+ if (! quiet)
+ error (0, 0, gettext ("%s %s diff: program header count"),
+ fname1, fname2);
+ DIFFERENCE;
+ }
+
+ /* Iterate over all sections. We expect the sections in the two
+ files to match exactly. */
+ Elf_Scn *scn1 = NULL;
+ Elf_Scn *scn2 = NULL;
+ struct region *regions = NULL;
+ size_t nregions = 0;
+ while (1)
+ {
+ GElf_Shdr shdr1_mem;
+ GElf_Shdr *shdr1;
+ const char *sname1 = NULL;
+ do
+ {
+ scn1 = elf_nextscn (elf1, scn1);
+ shdr1 = gelf_getshdr (scn1, &shdr1_mem);
+ if (shdr1 != NULL)
+ sname1 = elf_strptr (elf1, ehdr1->e_shstrndx, shdr1->sh_name);
+ }
+ while (scn1 != NULL
+ && ebl_section_strip_p (ebl1, ehdr1, shdr1, sname1, true, false));
+
+ GElf_Shdr shdr2_mem;
+ GElf_Shdr *shdr2;
+ const char *sname2 = NULL;
+ do
+ {
+ scn2 = elf_nextscn (elf2, scn2);
+ shdr2 = gelf_getshdr (scn2, &shdr2_mem);
+ if (shdr2 != NULL)
+ sname2 = elf_strptr (elf2, ehdr2->e_shstrndx, shdr2->sh_name);
+ }
+ while (scn2 != NULL
+ && ebl_section_strip_p (ebl2, ehdr2, shdr2, sname2, true, false));
+
+ if (scn1 == NULL || scn2 == NULL)
+ break;
+
+ if (gaps != gaps_ignore && (shdr1->sh_flags & SHF_ALLOC) != 0)
+ {
+ struct region *newp = (struct region *) alloca (sizeof (*newp));
+ newp->from = shdr1->sh_offset;
+ newp->to = shdr1->sh_offset + shdr1->sh_size;
+ newp->next = regions;
+ regions = newp;
+
+ ++nregions;
+ }
+
+ /* Compare the headers. We allow the name to be at a different
+ location. */
+ if (unlikely (sname1 == NULL || sname2 == NULL
+ || strcmp (sname1, sname2) != 0))
+ {
+ error (0, 0, gettext ("%s %s differ: section [%zu], [%zu] name"),
+ fname1, fname2, elf_ndxscn (scn1), elf_ndxscn (scn2));
+ DIFFERENCE;
+ }
+
+ /* We ignore certain sections. */
+ if ((sname1 != NULL && strcmp (sname1, ".gnu_debuglink") == 0)
+ || (sname1 != NULL && strcmp (sname1, ".gnu.prelink_undo") == 0))
+ continue;
+
+ if (shdr1->sh_type != shdr2->sh_type
+ // XXX Any flags which should be ignored?
+ || shdr1->sh_flags != shdr2->sh_flags
+ || shdr1->sh_addr != shdr2->sh_addr
+ || (shdr1->sh_offset != shdr2->sh_offset
+ && (shdr1->sh_flags & SHF_ALLOC)
+ && ehdr1->e_type != ET_REL)
+ || shdr1->sh_size != shdr2->sh_size
+ || shdr1->sh_link != shdr2->sh_link
+ || shdr1->sh_info != shdr2->sh_info
+ || shdr1->sh_addralign != shdr2->sh_addralign
+ || shdr1->sh_entsize != shdr2->sh_entsize)
+ {
+ error (0, 0, gettext ("%s %s differ: section [%zu] '%s' header"),
+ fname1, fname2, elf_ndxscn (scn1), sname1);
+ DIFFERENCE;
+ }
+
+ Elf_Data *data1 = elf_getdata (scn1, NULL);
+ if (data1 == NULL)
+ error (2, 0,
+ gettext ("cannot get content of section %zu in '%s': %s"),
+ elf_ndxscn (scn1), fname1, elf_errmsg (-1));
+
+ Elf_Data *data2 = elf_getdata (scn2, NULL);
+ if (data2 == NULL)
+ error (2, 0,
+ gettext ("cannot get content of section %zu in '%s': %s"),
+ elf_ndxscn (scn2), fname2, elf_errmsg (-1));
+
+ switch (shdr1->sh_type)
+ {
+ case SHT_DYNSYM:
+ case SHT_SYMTAB:
+ if (shdr1->sh_entsize == 0)
+ error (2, 0,
+ gettext ("symbol table [%zu] in '%s' has zero sh_entsize"),
+ elf_ndxscn (scn1), fname1);
+
+ /* Iterate over the symbol table. We ignore the st_size
+ value of undefined symbols. */
+ for (int ndx = 0; ndx < (int) (shdr1->sh_size / shdr1->sh_entsize);
+ ++ndx)
+ {
+ GElf_Sym sym1_mem;
+ GElf_Sym *sym1 = gelf_getsym (data1, ndx, &sym1_mem);
+ if (sym1 == NULL)
+ error (2, 0,
+ gettext ("cannot get symbol in '%s': %s"),
+ fname1, elf_errmsg (-1));
+ GElf_Sym sym2_mem;
+ GElf_Sym *sym2 = gelf_getsym (data2, ndx, &sym2_mem);
+ if (sym2 == NULL)
+ error (2, 0,
+ gettext ("cannot get symbol in '%s': %s"),
+ fname2, elf_errmsg (-1));
+
+ const char *name1 = elf_strptr (elf1, shdr1->sh_link,
+ sym1->st_name);
+ const char *name2 = elf_strptr (elf2, shdr2->sh_link,
+ sym2->st_name);
+ if (unlikely (name1 == NULL || name2 == NULL
+ || strcmp (name1, name2) != 0
+ || sym1->st_value != sym2->st_value
+ || (sym1->st_size != sym2->st_size
+ && sym1->st_shndx != SHN_UNDEF)
+ || sym1->st_info != sym2->st_info
+ || sym1->st_other != sym2->st_other
+ || sym1->st_shndx != sym2->st_shndx))
+ {
+ // XXX Do we want to allow reordered symbol tables?
+ symtab_mismatch:
+ if (! quiet)
+ {
+ if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
+ error (0, 0,
+ gettext ("%s %s differ: symbol table [%zu]"),
+ fname1, fname2, elf_ndxscn (scn1));
+ else
+ error (0, 0, gettext ("\
+%s %s differ: symbol table [%zu,%zu]"),
+ fname1, fname2, elf_ndxscn (scn1),
+ elf_ndxscn (scn2));
+ }
+ DIFFERENCE;
+ break;
+ }
+
+ if (sym1->st_shndx == SHN_UNDEF
+ && sym1->st_size != sym2->st_size)
+ {
+ /* The size of the symbol in the object defining it
+ might have changed. That is OK unless the symbol
+ is used in a copy relocation. Look over the
+ sections in both files and determine which
+ relocation section uses this symbol table
+ section. Then look through the relocations to
+ see whether any copy relocation references this
+ symbol. */
+ if (search_for_copy_reloc (ebl1, elf_ndxscn (scn1), ndx)
+ || search_for_copy_reloc (ebl2, elf_ndxscn (scn2), ndx))
+ goto symtab_mismatch;
+ }
+ }
+ break;
+
+ case SHT_NOTE:
+ /* Parse the note format and compare the notes themselves. */
+ {
+ GElf_Nhdr note1;
+ GElf_Nhdr note2;
+
+ size_t off1 = 0;
+ size_t off2 = 0;
+ size_t name_offset;
+ size_t desc_offset;
+ while (off1 < data1->d_size
+ && (off1 = gelf_getnote (data1, off1, ¬e1,
+ &name_offset, &desc_offset)) > 0)
+ {
+ const char *name1 = (note1.n_namesz == 0
+ ? "" : data1->d_buf + name_offset);
+ const void *desc1 = data1->d_buf + desc_offset;
+ if (off2 >= data2->d_size)
+ {
+ if (! quiet)
+ error (0, 0, gettext ("\
+%s %s differ: section [%zu] '%s' number of notes"),
+ fname1, fname2, elf_ndxscn (scn1), sname1);
+ DIFFERENCE;
+ }
+ off2 = gelf_getnote (data2, off2, ¬e2,
+ &name_offset, &desc_offset);
+ if (off2 == 0)
+ error (2, 0, gettext ("\
+cannot read note section [%zu] '%s' in '%s': %s"),
+ elf_ndxscn (scn2), sname2, fname2, elf_errmsg (-1));
+ const char *name2 = (note2.n_namesz == 0
+ ? "" : data2->d_buf + name_offset);
+ const void *desc2 = data2->d_buf + desc_offset;
+
+ if (note1.n_namesz != note2.n_namesz
+ || memcmp (name1, name2, note1.n_namesz))
+ {
+ if (! quiet)
+ error (0, 0, gettext ("\
+%s %s differ: section [%zu] '%s' note name"),
+ fname1, fname2, elf_ndxscn (scn1), sname1);
+ DIFFERENCE;
+ }
+ if (note1.n_type != note2.n_type)
+ {
+ if (! quiet)
+ error (0, 0, gettext ("\
+%s %s differ: section [%zu] '%s' note '%s' type"),
+ fname1, fname2, elf_ndxscn (scn1), sname1, name1);
+ DIFFERENCE;
+ }
+ if (note1.n_descsz != note2.n_descsz
+ || memcmp (desc1, desc2, note1.n_descsz))
+ {
+ if (note1.n_type == NT_GNU_BUILD_ID
+ && note1.n_namesz == sizeof "GNU"
+ && !memcmp (name1, "GNU", sizeof "GNU"))
+ {
+ if (note1.n_descsz != note2.n_descsz)
+ {
+ if (! quiet)
+ error (0, 0, gettext ("\
+%s %s differ: build ID length"),
+ fname1, fname2);
+ DIFFERENCE;
+ }
+ else if (! ignore_build_id)
+ {
+ if (! quiet)
+ error (0, 0, gettext ("\
+%s %s differ: build ID content"),
+ fname1, fname2);
+ DIFFERENCE;
+ }
+ }
+ else
+ {
+ if (! quiet)
+ error (0, 0, gettext ("\
+%s %s differ: section [%zu] '%s' note '%s' content"),
+ fname1, fname2, elf_ndxscn (scn1), sname1,
+ name1);
+ DIFFERENCE;
+ }
+ }
+ }
+ if (off2 < data2->d_size)
+ {
+ if (! quiet)
+ error (0, 0, gettext ("\
+%s %s differ: section [%zu] '%s' number of notes"),
+ fname1, fname2, elf_ndxscn (scn1), sname1);
+ DIFFERENCE;
+ }
+ }
+ break;
+
+ default:
+ /* Compare the section content byte for byte. */
+ assert (shdr1->sh_type == SHT_NOBITS
+ || (data1->d_buf != NULL || data1->d_size == 0));
+ assert (shdr2->sh_type == SHT_NOBITS
+ || (data2->d_buf != NULL || data1->d_size == 0));
+
+ if (unlikely (data1->d_size != data2->d_size
+ || (shdr1->sh_type != SHT_NOBITS
+ && data1->d_size != 0
+ && memcmp (data1->d_buf, data2->d_buf,
+ data1->d_size) != 0)))
+ {
+ if (hash_inexact
+ && shdr1->sh_type == SHT_HASH
+ && data1->d_size == data2->d_size
+ && hash_content_equivalent (shdr1->sh_entsize, data1, data2))
+ break;
+
+ if (! quiet)
+ {
+ if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
+ error (0, 0, gettext ("\
+%s %s differ: section [%zu] '%s' content"),
+ fname1, fname2, elf_ndxscn (scn1), sname1);
+ else
+ error (0, 0, gettext ("\
+%s %s differ: section [%zu,%zu] '%s' content"),
+ fname1, fname2, elf_ndxscn (scn1),
+ elf_ndxscn (scn2), sname1);
+ }
+ DIFFERENCE;
+ }
+ break;
+ }
+ }
+
+ if (unlikely (scn1 != scn2))
+ {
+ if (! quiet)
+ error (0, 0,
+ gettext ("%s %s differ: unequal amount of important sections"),
+ fname1, fname2);
+ DIFFERENCE;
+ }
+
+ /* We we look at gaps, create artificial ones for the parts of the
+ program which we are not in sections. */
+ struct region ehdr_region;
+ struct region phdr_region;
+ if (gaps != gaps_ignore)
+ {
+ ehdr_region.from = 0;
+ ehdr_region.to = ehdr1->e_ehsize;
+ ehdr_region.next = &phdr_region;
+
+ phdr_region.from = ehdr1->e_phoff;
+ phdr_region.to = ehdr1->e_phoff + phnum1 * ehdr1->e_phentsize;
+ phdr_region.next = regions;
+
+ regions = &ehdr_region;
+ nregions += 2;
+ }
+
+ /* If we need to look at the gaps we need access to the file data. */
+ char *raw1 = NULL;
+ size_t size1 = 0;
+ char *raw2 = NULL;
+ size_t size2 = 0;
+ struct region *regionsarr = alloca (nregions * sizeof (struct region));
+ if (gaps != gaps_ignore)
+ {
+ raw1 = elf_rawfile (elf1, &size1);
+ if (raw1 == NULL )
+ error (2, 0, gettext ("cannot load data of '%s': %s"),
+ fname1, elf_errmsg (-1));
+
+ raw2 = elf_rawfile (elf2, &size2);
+ if (raw2 == NULL )
+ error (2, 0, gettext ("cannot load data of '%s': %s"),
+ fname2, elf_errmsg (-1));
+
+ for (size_t cnt = 0; cnt < nregions; ++cnt)
+ {
+ regionsarr[cnt] = *regions;
+ regions = regions->next;
+ }
+
+ qsort (regionsarr, nregions, sizeof (regionsarr[0]), regioncompare);
+ }
+
+ /* Compare the program header tables. */
+ for (unsigned int ndx = 0; ndx < phnum1; ++ndx)
+ {
+ GElf_Phdr phdr1_mem;
+ GElf_Phdr *phdr1 = gelf_getphdr (elf1, ndx, &phdr1_mem);
+ if (phdr1 == NULL)
+ error (2, 0,
+ gettext ("cannot get program header entry %d of '%s': %s"),
+ ndx, fname1, elf_errmsg (-1));
+ GElf_Phdr phdr2_mem;
+ GElf_Phdr *phdr2 = gelf_getphdr (elf2, ndx, &phdr2_mem);
+ if (phdr2 == NULL)
+ error (2, 0,
+ gettext ("cannot get program header entry %d of '%s': %s"),
+ ndx, fname2, elf_errmsg (-1));
+
+ if (unlikely (memcmp (phdr1, phdr2, sizeof (GElf_Phdr)) != 0))
+ {
+ if (! quiet)
+ error (0, 0, gettext ("%s %s differ: program header %d"),
+ fname1, fname2, ndx);
+ DIFFERENCE;
+ }
+
+ if (gaps != gaps_ignore && phdr1->p_type == PT_LOAD)
+ {
+ size_t cnt = 0;
+ while (cnt < nregions && regionsarr[cnt].to < phdr1->p_offset)
+ ++cnt;
+
+ GElf_Off last = phdr1->p_offset;
+ GElf_Off end = phdr1->p_offset + phdr1->p_filesz;
+ while (cnt < nregions && regionsarr[cnt].from < end)
+ {
+ if (last < regionsarr[cnt].from)
+ {
+ /* Compare the [LAST,FROM) region. */
+ assert (gaps == gaps_match);
+ if (unlikely (memcmp (raw1 + last, raw2 + last,
+ regionsarr[cnt].from - last) != 0))
+ {
+ gapmismatch:
+ if (!quiet)
+ error (0, 0, gettext ("%s %s differ: gap"),
+ fname1, fname2);
+ DIFFERENCE;
+ break;
+ }
+
+ }
+ last = regionsarr[cnt].to;
+ ++cnt;
+ }
+
+ if (cnt == nregions && last < end)
+ goto gapmismatch;
+ }
+ }
+
+ out:
+ elf_end (elf1);
+ elf_end (elf2);
+ ebl_closebackend (ebl1);
+ ebl_closebackend (ebl2);
+ close (fd1);
+ close (fd2);
+
+ return result;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg,
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case 'q':
+ quiet = true;
+ break;
+
+ case 'l':
+ verbose = true;
+ break;
+
+ case OPT_GAPS:
+ if (strcasecmp (arg, "ignore") == 0)
+ gaps = gaps_ignore;
+ else if (likely (strcasecmp (arg, "match") == 0))
+ gaps = gaps_match;
+ else
+ {
+ fprintf (stderr,
+ gettext ("Invalid value '%s' for --gaps parameter."),
+ arg);
+ argp_help (&argp, stderr, ARGP_HELP_SEE,
+ program_invocation_short_name);
+ exit (1);
+ }
+ break;
+
+ case OPT_HASH_INEXACT:
+ hash_inexact = true;
+ break;
+
+ case OPT_IGNORE_BUILD_ID:
+ ignore_build_id = true;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+static Elf *
+open_file (const char *fname, int *fdp, Ebl **eblp)
+{
+ int fd = open (fname, O_RDONLY);
+ if (unlikely (fd == -1))
+ error (2, errno, gettext ("cannot open '%s'"), fname);
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ error (2, 0,
+ gettext ("cannot create ELF descriptor for '%s': %s"),
+ fname, elf_errmsg (-1));
+ Ebl *ebl = ebl_openbackend (elf);
+ if (ebl == NULL)
+ error (2, 0,
+ gettext ("cannot create EBL descriptor for '%s'"), fname);
+
+ *fdp = fd;
+ *eblp = ebl;
+ return elf;
+}
+
+
+static bool
+search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx)
+{
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ error (2, 0,
+ gettext ("cannot get section header of section %zu: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+
+ if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA)
+ || shdr->sh_link != scnndx)
+ continue;
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ error (2, 0,
+ gettext ("cannot get content of section %zu: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+
+ if (shdr->sh_type == SHT_REL && shdr->sh_entsize != 0)
+ for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
+ ++ndx)
+ {
+ GElf_Rel rel_mem;
+ GElf_Rel *rel = gelf_getrel (data, ndx, &rel_mem);
+ if (rel == NULL)
+ error (2, 0, gettext ("cannot get relocation: %s"),
+ elf_errmsg (-1));
+
+ if ((int) GELF_R_SYM (rel->r_info) == symndx
+ && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info)))
+ return true;
+ }
+ else if (shdr->sh_entsize != 0)
+ for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
+ ++ndx)
+ {
+ GElf_Rela rela_mem;
+ GElf_Rela *rela = gelf_getrela (data, ndx, &rela_mem);
+ if (rela == NULL)
+ error (2, 0, gettext ("cannot get relocation: %s"),
+ elf_errmsg (-1));
+
+ if ((int) GELF_R_SYM (rela->r_info) == symndx
+ && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info)))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+static int
+regioncompare (const void *p1, const void *p2)
+{
+ const struct region *r1 = (const struct region *) p1;
+ const struct region *r2 = (const struct region *) p2;
+
+ if (r1->from < r2->from)
+ return -1;
+ return 1;
+}
+
+
+static int
+compare_Elf32_Word (const void *p1, const void *p2)
+{
+ const Elf32_Word *w1 = p1;
+ const Elf32_Word *w2 = p2;
+ return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
+}
+
+static int
+compare_Elf64_Xword (const void *p1, const void *p2)
+{
+ const Elf64_Xword *w1 = p1;
+ const Elf64_Xword *w2 = p2;
+ return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
+}
+
+static bool
+hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2)
+{
+#define CHECK_HASH(Hash_Word) \
+ { \
+ const Hash_Word *const hash1 = data1->d_buf; \
+ const Hash_Word *const hash2 = data2->d_buf; \
+ const size_t nbucket = hash1[0]; \
+ const size_t nchain = hash1[1]; \
+ if (data1->d_size != (2 + nbucket + nchain) * sizeof hash1[0] \
+ || hash2[0] != nbucket || hash2[1] != nchain) \
+ return false; \
+ \
+ const Hash_Word *const bucket1 = &hash1[2]; \
+ const Hash_Word *const chain1 = &bucket1[nbucket]; \
+ const Hash_Word *const bucket2 = &hash2[2]; \
+ const Hash_Word *const chain2 = &bucket2[nbucket]; \
+ \
+ bool chain_ok[nchain]; \
+ Hash_Word temp1[nchain - 1]; \
+ Hash_Word temp2[nchain - 1]; \
+ memset (chain_ok, 0, sizeof chain_ok); \
+ for (size_t i = 0; i < nbucket; ++i) \
+ { \
+ if (bucket1[i] >= nchain || bucket2[i] >= nchain) \
+ return false; \
+ \
+ size_t b1 = 0; \
+ for (size_t p = bucket1[i]; p != STN_UNDEF; p = chain1[p]) \
+ if (p >= nchain || b1 >= nchain - 1) \
+ return false; \
+ else \
+ temp1[b1++] = p; \
+ \
+ size_t b2 = 0; \
+ for (size_t p = bucket2[i]; p != STN_UNDEF; p = chain2[p]) \
+ if (p >= nchain || b2 >= nchain - 1) \
+ return false; \
+ else \
+ temp2[b2++] = p; \
+ \
+ if (b1 != b2) \
+ return false; \
+ \
+ qsort (temp1, b1, sizeof temp1[0], compare_##Hash_Word); \
+ qsort (temp2, b2, sizeof temp2[0], compare_##Hash_Word); \
+ \
+ for (b1 = 0; b1 < b2; ++b1) \
+ if (temp1[b1] != temp2[b1]) \
+ return false; \
+ else \
+ chain_ok[temp1[b1]] = true; \
+ } \
+ \
+ for (size_t i = 0; i < nchain; ++i) \
+ if (!chain_ok[i] && chain1[i] != chain2[i]) \
+ return false; \
+ \
+ return true; \
+ }
+
+ switch (entsize)
+ {
+ case 4:
+ CHECK_HASH (Elf32_Word);
+ break;
+ case 8:
+ CHECK_HASH (Elf64_Xword);
+ break;
+ }
+
+ return false;
+}
+
+
+#include "debugpred.h"
diff --git a/src/elfcompress.c b/src/elfcompress.c
new file mode 100644
index 0000000..25378a4
--- /dev/null
+++ b/src/elfcompress.c
@@ -0,0 +1,1322 @@
+/* Compress or decompress an ELF file.
+ Copyright (C) 2015, 2016 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <assert.h>
+#include <argp.h>
+#include <error.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <locale.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include ELFUTILS_HEADER(elf)
+#include ELFUTILS_HEADER(ebl)
+#include ELFUTILS_HEADER(dwelf)
+#include <gelf.h>
+#include "libeu.h"
+#include "printversion.h"
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+static int verbose = 0; /* < 0, no warnings, > 0 extra verbosity. */
+static bool force = false;
+static bool permissive = false;
+static const char *foutput = NULL;
+
+#define T_UNSET 0
+#define T_DECOMPRESS 1 /* none */
+#define T_COMPRESS_ZLIB 2 /* zlib */
+#define T_COMPRESS_GNU 3 /* zlib-gnu */
+static int type = T_UNSET;
+
+struct section_pattern
+{
+ char *pattern;
+ struct section_pattern *next;
+};
+
+static struct section_pattern *patterns = NULL;
+
+static void
+add_pattern (const char *pattern)
+{
+ struct section_pattern *p = xmalloc (sizeof *p);
+ p->pattern = xstrdup (pattern);
+ p->next = patterns;
+ patterns = p;
+}
+
+static void
+free_patterns (void)
+{
+ struct section_pattern *pattern = patterns;
+ while (pattern != NULL)
+ {
+ struct section_pattern *p = pattern;
+ pattern = p->next;
+ free (p->pattern);
+ free (p);
+ }
+}
+
+static error_t
+parse_opt (int key, char *arg __attribute__ ((unused)),
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case 'v':
+ verbose++;
+ break;
+
+ case 'q':
+ verbose--;
+ break;
+
+ case 'f':
+ force = true;
+ break;
+
+ case 'p':
+ permissive = true;
+ break;
+
+ case 'n':
+ add_pattern (arg);
+ break;
+
+ case 'o':
+ if (foutput != NULL)
+ argp_error (state, N_("-o option specified twice"));
+ else
+ foutput = arg;
+ break;
+
+ case 't':
+ if (type != T_UNSET)
+ argp_error (state, N_("-t option specified twice"));
+
+ if (strcmp ("none", arg) == 0)
+ type = T_DECOMPRESS;
+ else if (strcmp ("zlib", arg) == 0 || strcmp ("zlib-gabi", arg) == 0)
+ type = T_COMPRESS_ZLIB;
+ else if (strcmp ("zlib-gnu", arg) == 0 || strcmp ("gnu", arg) == 0)
+ type = T_COMPRESS_GNU;
+ else
+ argp_error (state, N_("unknown compression type '%s'"), arg);
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ if (type == T_UNSET)
+ type = T_COMPRESS_ZLIB;
+ if (patterns == NULL)
+ add_pattern (".?(z)debug*");
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ /* We need at least one input file. */
+ argp_error (state, N_("No input file given"));
+ break;
+
+ case ARGP_KEY_ARGS:
+ if (foutput != NULL && state->argc - state->next > 1)
+ argp_error (state,
+ N_("Only one input file allowed together with '-o'"));
+ /* We only use this for checking the number of arguments, we don't
+ actually want to consume them. */
+ FALLTHROUGH;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static bool
+section_name_matches (const char *name)
+{
+ struct section_pattern *pattern = patterns;
+ while (pattern != NULL)
+ {
+ if (fnmatch (pattern->pattern, name, FNM_EXTMATCH) == 0)
+ return true;
+ pattern = pattern->next;
+ }
+ return false;
+}
+
+static int
+setshdrstrndx (Elf *elf, GElf_Ehdr *ehdr, size_t ndx)
+{
+ if (ndx < SHN_LORESERVE)
+ ehdr->e_shstrndx = ndx;
+ else
+ {
+ ehdr->e_shstrndx = SHN_XINDEX;
+ Elf_Scn *zscn = elf_getscn (elf, 0);
+ GElf_Shdr zshdr_mem;
+ GElf_Shdr *zshdr = gelf_getshdr (zscn, &zshdr_mem);
+ if (zshdr == NULL)
+ return -1;
+ zshdr->sh_link = ndx;
+ if (gelf_update_shdr (zscn, zshdr) == 0)
+ return -1;
+ }
+
+ if (gelf_update_ehdr (elf, ehdr) == 0)
+ return -1;
+
+ return 0;
+}
+
+static int
+compress_section (Elf_Scn *scn, size_t orig_size, const char *name,
+ const char *newname, size_t ndx,
+ bool gnu, bool compress, bool report_verbose)
+{
+ int res;
+ unsigned int flags = compress && force ? ELF_CHF_FORCE : 0;
+ if (gnu)
+ res = elf_compress_gnu (scn, compress ? 1 : 0, flags);
+ else
+ res = elf_compress (scn, compress ? ELFCOMPRESS_ZLIB : 0, flags);
+
+ if (res < 0)
+ error (0, 0, "Couldn't decompress section [%zd] %s: %s",
+ ndx, name, elf_errmsg (-1));
+ else
+ {
+ if (compress && res == 0)
+ {
+ if (verbose >= 0)
+ printf ("[%zd] %s NOT compressed, wouldn't be smaller\n",
+ ndx, name);
+ }
+
+ if (report_verbose && res > 0)
+ {
+ printf ("[%zd] %s %s", ndx, name,
+ compress ? "compressed" : "decompressed");
+ if (newname != NULL)
+ printf (" -> %s", newname);
+
+ /* Reload shdr, it has changed. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for section [%zd]", ndx);
+ return -1;
+ }
+ float new = shdr->sh_size;
+ float orig = orig_size ?: 1;
+ printf (" (%zu => %" PRIu64 " %.2f%%)\n",
+ orig_size, shdr->sh_size, (new / orig) * 100);
+ }
+ }
+
+ return res;
+}
+
+static int
+process_file (const char *fname)
+{
+ if (verbose > 0)
+ printf ("processing: %s\n", fname);
+
+ /* The input ELF. */
+ int fd = -1;
+ Elf *elf = NULL;
+
+ /* The output ELF. */
+ char *fnew = NULL;
+ int fdnew = -1;
+ Elf *elfnew = NULL;
+
+ /* Buffer for (one) new section name if necessary. */
+ char *snamebuf = NULL;
+
+ /* String table (and symbol table), if section names need adjusting. */
+ Dwelf_Strtab *names = NULL;
+ Dwelf_Strent **scnstrents = NULL;
+ Dwelf_Strent **symstrents = NULL;
+ char **scnnames = NULL;
+
+ /* Section data from names. */
+ void *namesbuf = NULL;
+
+ /* Which sections match and need to be (un)compressed. */
+ unsigned int *sections = NULL;
+
+ /* How many sections are we talking about? */
+ size_t shnum = 0;
+
+#define WORD_BITS (8U * sizeof (unsigned int))
+ void set_section (size_t ndx)
+ {
+ sections[ndx / WORD_BITS] |= (1U << (ndx % WORD_BITS));
+ }
+
+ bool get_section (size_t ndx)
+ {
+ return (sections[ndx / WORD_BITS] & (1U << (ndx % WORD_BITS))) != 0;
+ }
+
+ int cleanup (int res)
+ {
+ elf_end (elf);
+ close (fd);
+
+ elf_end (elfnew);
+ close (fdnew);
+
+ if (fnew != NULL)
+ {
+ unlink (fnew);
+ free (fnew);
+ fnew = NULL;
+ }
+
+ free (snamebuf);
+ if (names != NULL)
+ {
+ dwelf_strtab_free (names);
+ free (scnstrents);
+ free (symstrents);
+ free (namesbuf);
+ if (scnnames != NULL)
+ {
+ for (size_t n = 0; n < shnum; n++)
+ free (scnnames[n]);
+ free (scnnames);
+ }
+ }
+
+ free (sections);
+
+ return res;
+ }
+
+ fd = open (fname, O_RDONLY);
+ if (fd < 0)
+ {
+ error (0, errno, "Couldn't open %s\n", fname);
+ return cleanup (-1);
+ }
+
+ elf = elf_begin (fd, ELF_C_READ, NULL);
+ if (elf == NULL)
+ {
+ error (0, 0, "Couldn't open ELF file %s for reading: %s",
+ fname, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* We dont' handle ar files (or anything else), we probably should. */
+ Elf_Kind kind = elf_kind (elf);
+ if (kind != ELF_K_ELF)
+ {
+ if (kind == ELF_K_AR)
+ error (0, 0, "Cannot handle ar files: %s", fname);
+ else
+ error (0, 0, "Unknown file type: %s", fname);
+ return cleanup (-1);
+ }
+
+ struct stat st;
+ if (fstat (fd, &st) != 0)
+ {
+ error (0, errno, "Couldn't fstat %s", fname);
+ return cleanup (-1);
+ }
+
+ GElf_Ehdr ehdr;
+ if (gelf_getehdr (elf, &ehdr) == NULL)
+ {
+ error (0, 0, "Couldn't get ehdr for %s: %s", fname, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Get the section header string table. */
+ size_t shdrstrndx;
+ if (elf_getshdrstrndx (elf, &shdrstrndx) != 0)
+ {
+ error (0, 0, "Couldn't get section header string table index in %s: %s",
+ fname, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* How many sections are we talking about? */
+ if (elf_getshdrnum (elf, &shnum) != 0)
+ {
+ error (0, 0, "Couldn't get number of sections in %s: %s",
+ fname, elf_errmsg (1));
+ return cleanup (-1);
+ }
+
+ if (shnum == 0)
+ {
+ error (0, 0, "ELF file %s has no sections", fname);
+ return cleanup (-1);
+ }
+
+ sections = xcalloc (shnum / 8 + 1, sizeof (unsigned int));
+
+ size_t phnum;
+ if (elf_getphdrnum (elf, &phnum) != 0)
+ {
+ error (0, 0, "Couldn't get phdrnum: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Whether we need to adjust any section names (going to/from GNU
+ naming). If so we'll need to build a new section header string
+ table. */
+ bool adjust_names = false;
+
+ /* If there are phdrs we want to maintain the layout of the
+ allocated sections in the file. */
+ bool layout = phnum != 0;
+
+ /* While going through all sections keep track of last section data
+ offset if needed to keep the layout. We are responsible for
+ adding the section offsets and headers (e_shoff) in that case
+ (which we will place after the last section). */
+ GElf_Off last_offset = 0;
+ if (layout)
+ last_offset = (ehdr.e_phoff
+ + gelf_fsize (elf, ELF_T_PHDR, phnum, EV_CURRENT));
+
+ /* Which section, if any, is a symbol table that shares a string
+ table with the section header string table? */
+ size_t symtabndx = 0;
+
+ /* We do three passes over all sections.
+
+ First an inspection pass over the old Elf to see which section
+ data needs to be copied and/or transformed, which sections need a
+ names change and whether there is a symbol table that might need
+ to be adjusted be if the section header name table is changed.
+
+ Second a collection pass that creates the Elf sections and copies
+ the data. This pass will compress/decompress section data when
+ needed. And it will collect all data needed if we'll need to
+ construct a new string table. Afterwards the new string table is
+ constructed.
+
+ Third a fixup/adjustment pass over the new Elf that will adjust
+ any section references (names) and adjust the layout based on the
+ new sizes of the sections if necessary. This pass is optional if
+ we aren't responsible for the layout and the section header
+ string table hasn't been changed. */
+
+ /* Inspection pass. */
+ size_t maxnamelen = 0;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ size_t ndx = elf_ndxscn (scn);
+ if (ndx > shnum)
+ {
+ error (0, 0, "Unexpected section number %zd, expected only %zd",
+ ndx, shnum);
+ cleanup (-1);
+ }
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ const char *sname = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+ if (sname == NULL)
+ {
+ error (0, 0, "Couldn't get name for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ if (section_name_matches (sname))
+ {
+ if (shdr->sh_type != SHT_NOBITS
+ && (shdr->sh_flags & SHF_ALLOC) == 0)
+ {
+ set_section (ndx);
+ /* Check if we might want to change this section name. */
+ if (! adjust_names
+ && ((type != T_COMPRESS_GNU
+ && strncmp (sname, ".zdebug",
+ strlen (".zdebug")) == 0)
+ || (type == T_COMPRESS_GNU
+ && strncmp (sname, ".debug",
+ strlen (".debug")) == 0)))
+ adjust_names = true;
+
+ /* We need a buffer this large if we change the names. */
+ if (adjust_names)
+ {
+ size_t slen = strlen (sname);
+ if (slen > maxnamelen)
+ maxnamelen = slen;
+ }
+ }
+ else
+ if (verbose >= 0)
+ printf ("[%zd] %s ignoring %s section\n", ndx, sname,
+ (shdr->sh_type == SHT_NOBITS ? "no bits" : "allocated"));
+ }
+
+ if (shdr->sh_type == SHT_SYMTAB)
+ {
+ /* Check if we might have to adjust the symbol name indexes. */
+ if (shdr->sh_link == shdrstrndx)
+ {
+ if (symtabndx != 0)
+ {
+ error (0, 0,
+ "Multiple symbol tables (%zd, %zd) using the same string table unsupported", symtabndx, ndx);
+ return cleanup (-1);
+ }
+ symtabndx = ndx;
+ }
+ }
+
+ /* Keep track of last allocated data offset. */
+ if (layout)
+ if ((shdr->sh_flags & SHF_ALLOC) != 0)
+ {
+ GElf_Off off = shdr->sh_offset + (shdr->sh_type != SHT_NOBITS
+ ? shdr->sh_size : 0);
+ if (last_offset < off)
+ last_offset = off;
+ }
+ }
+
+ if (adjust_names)
+ {
+ names = dwelf_strtab_init (true);
+ if (names == NULL)
+ {
+ error (0, 0, "Not enough memory for new strtab");
+ return cleanup (-1);
+ }
+ scnstrents = xmalloc (shnum
+ * sizeof (Dwelf_Strent *));
+ scnnames = xcalloc (shnum, sizeof (char *));
+ }
+
+ /* Create a new (temporary) ELF file for the result. */
+ if (foutput == NULL)
+ {
+ size_t fname_len = strlen (fname);
+ fnew = xmalloc (fname_len + sizeof (".XXXXXX"));
+ strcpy (mempcpy (fnew, fname, fname_len), ".XXXXXX");
+ fdnew = mkstemp (fnew);
+ }
+ else
+ {
+ fnew = xstrdup (foutput);
+ fdnew = open (fnew, O_WRONLY | O_CREAT, st.st_mode & ALLPERMS);
+ }
+
+ if (fdnew < 0)
+ {
+ error (0, errno, "Couldn't create output file %s", fnew);
+ /* Since we didn't create it we don't want to try to unlink it. */
+ free (fnew);
+ fnew = NULL;
+ return cleanup (-1);
+ }
+
+ elfnew = elf_begin (fdnew, ELF_C_WRITE, NULL);
+ if (elfnew == NULL)
+ {
+ error (0, 0, "Couldn't open new ELF %s for writing: %s",
+ fnew, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Create the new ELF header and copy over all the data. */
+ if (gelf_newehdr (elfnew, gelf_getclass (elf)) == 0)
+ {
+ error (0, 0, "Couldn't create new ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ GElf_Ehdr newehdr;
+ if (gelf_getehdr (elfnew, &newehdr) == NULL)
+ {
+ error (0, 0, "Couldn't get new ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ newehdr.e_ident[EI_DATA] = ehdr.e_ident[EI_DATA];
+ newehdr.e_type = ehdr.e_type;
+ newehdr.e_machine = ehdr.e_machine;
+ newehdr.e_version = ehdr.e_version;
+ newehdr.e_entry = ehdr.e_entry;
+ newehdr.e_flags = ehdr.e_flags;
+
+ if (gelf_update_ehdr (elfnew, &newehdr) == 0)
+ {
+ error (0, 0, "Couldn't update ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Copy over the phdrs as is. */
+ if (phnum != 0)
+ {
+ if (gelf_newphdr (elfnew, phnum) == 0)
+ {
+ error (0, 0, "Couldn't create phdrs: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
+ if (phdr == NULL)
+ {
+ error (0, 0, "Couldn't get phdr %zd: %s", cnt, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+ if (gelf_update_phdr (elfnew, cnt, phdr) == 0)
+ {
+ error (0, 0, "Couldn't create phdr %zd: %s", cnt,
+ elf_errmsg (-1));
+ return cleanup (-1);
+ }
+ }
+ }
+
+ /* Possibly add a 'z' and zero terminator. */
+ if (maxnamelen > 0)
+ snamebuf = xmalloc (maxnamelen + 2);
+
+ /* We might want to read/adjust the section header strings and
+ symbol tables. If so, and those sections are to be compressed
+ then we will have to decompress it during the collection pass and
+ compress it again in the fixup pass. Don't compress unnecessary
+ and keep track of whether or not to compress them (later in the
+ fixup pass). Also record the original size, so we can report the
+ difference later when we do compress. */
+ int shstrtab_compressed = T_UNSET;
+ size_t shstrtab_size = 0;
+ char *shstrtab_name = NULL;
+ char *shstrtab_newname = NULL;
+ int symtab_compressed = T_UNSET;
+ size_t symtab_size = 0;
+ char *symtab_name = NULL;
+ char *symtab_newname = NULL;
+
+ /* Collection pass. Copy over the sections, (de)compresses matching
+ sections, collect names of sections and symbol table if
+ necessary. */
+ scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ size_t ndx = elf_ndxscn (scn);
+ assert (ndx < shnum);
+
+ /* (de)compress if section matched. */
+ char *sname = NULL;
+ char *newname = NULL;
+ if (get_section (ndx))
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ uint64_t size = shdr->sh_size;
+ sname = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+ if (sname == NULL)
+ {
+ error (0, 0, "Couldn't get name for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ /* strdup sname, the shdrstrndx section itself might be
+ (de)compressed, invalidating the string pointers. */
+ sname = xstrdup (sname);
+
+ /* We might want to decompress (and rename), but not
+ compress during this pass since we might need the section
+ data in later passes. Skip those sections for now and
+ compress them in the fixup pass. */
+ bool skip_compress_section = (adjust_names
+ && (ndx == shdrstrndx
+ || ndx == symtabndx));
+
+ switch (type)
+ {
+ case T_DECOMPRESS:
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ if (compress_section (scn, size, sname, NULL, ndx,
+ false, false, verbose > 0) < 0)
+ return cleanup (-1);
+ }
+ else if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+ {
+ snamebuf[0] = '.';
+ strcpy (&snamebuf[1], &sname[2]);
+ newname = snamebuf;
+ if (compress_section (scn, size, sname, newname, ndx,
+ true, false, verbose > 0) < 0)
+ return cleanup (-1);
+ }
+ else if (verbose > 0)
+ printf ("[%zd] %s already decompressed\n", ndx, sname);
+ break;
+
+ case T_COMPRESS_GNU:
+ if (strncmp (sname, ".debug", strlen (".debug")) == 0)
+ {
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ /* First decompress to recompress GNU style.
+ Don't report even when verbose. */
+ if (compress_section (scn, size, sname, NULL, ndx,
+ false, false, false) < 0)
+ return cleanup (-1);
+ }
+
+ snamebuf[0] = '.';
+ snamebuf[1] = 'z';
+ strcpy (&snamebuf[2], &sname[1]);
+ newname = snamebuf;
+
+ if (skip_compress_section)
+ {
+ if (ndx == shdrstrndx)
+ {
+ shstrtab_size = size;
+ shstrtab_compressed = T_COMPRESS_GNU;
+ shstrtab_name = xstrdup (sname);
+ shstrtab_newname = xstrdup (newname);
+ }
+ else
+ {
+ symtab_size = size;
+ symtab_compressed = T_COMPRESS_GNU;
+ symtab_name = xstrdup (sname);
+ symtab_newname = xstrdup (newname);
+ }
+ }
+ else
+ {
+ int res = compress_section (scn, size, sname, newname,
+ ndx, true, true,
+ verbose > 0);
+ if (res < 0)
+ return cleanup (-1);
+
+ if (res == 0)
+ newname = NULL;
+ }
+ }
+ else if (verbose >= 0)
+ {
+ if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+ printf ("[%zd] %s unchanged, already GNU compressed",
+ ndx, sname);
+ else
+ printf ("[%zd] %s cannot GNU compress section not starting with .debug\n",
+ ndx, sname);
+ }
+ break;
+
+ case T_COMPRESS_ZLIB:
+ if ((shdr->sh_flags & SHF_COMPRESSED) == 0)
+ {
+ if (strncmp (sname, ".zdebug", strlen (".zdebug")) == 0)
+ {
+ /* First decompress to recompress zlib style.
+ Don't report even when verbose. */
+ if (compress_section (scn, size, sname, NULL, ndx,
+ true, false, false) < 0)
+ return cleanup (-1);
+
+ snamebuf[0] = '.';
+ strcpy (&snamebuf[1], &sname[2]);
+ newname = snamebuf;
+ }
+
+ if (skip_compress_section)
+ {
+ if (ndx == shdrstrndx)
+ {
+ shstrtab_size = size;
+ shstrtab_compressed = T_COMPRESS_ZLIB;
+ shstrtab_name = xstrdup (sname);
+ shstrtab_newname = (newname == NULL
+ ? NULL : xstrdup (newname));
+ }
+ else
+ {
+ symtab_size = size;
+ symtab_compressed = T_COMPRESS_ZLIB;
+ symtab_name = xstrdup (sname);
+ symtab_newname = (newname == NULL
+ ? NULL : xstrdup (newname));
+ }
+ }
+ else if (compress_section (scn, size, sname, newname, ndx,
+ false, true, verbose > 0) < 0)
+ return cleanup (-1);
+ }
+ else if (verbose > 0)
+ printf ("[%zd] %s already compressed\n", ndx, sname);
+ break;
+ }
+
+ free (sname);
+ }
+
+ Elf_Scn *newscn = elf_newscn (elfnew);
+ if (newscn == NULL)
+ {
+ error (0, 0, "Couldn't create new section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ if (gelf_update_shdr (newscn, shdr) == 0)
+ {
+ error (0, 0, "Couldn't update section header %zd", ndx);
+ return cleanup (-1);
+ }
+
+ /* Except for the section header string table all data can be
+ copied as is. The section header string table will be
+ created later and the symbol table might be fixed up if
+ necessary. */
+ if (! adjust_names || ndx != shdrstrndx)
+ {
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ {
+ error (0, 0, "Couldn't get data from section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ Elf_Data *newdata = elf_newdata (newscn);
+ if (newdata == NULL)
+ {
+ error (0, 0, "Couldn't create new data for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ *newdata = *data;
+ }
+
+ /* Keep track of the (new) section names. */
+ if (adjust_names)
+ {
+ char *name;
+ if (newname != NULL)
+ name = newname;
+ else
+ {
+ name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+ if (name == NULL)
+ {
+ error (0, 0, "Couldn't get name for section [%zd]", ndx);
+ return cleanup (-1);
+ }
+ }
+
+ /* We need to keep a copy of the name till the strtab is done. */
+ name = scnnames[ndx] = xstrdup (name);
+ if ((scnstrents[ndx] = dwelf_strtab_add (names, name)) == NULL)
+ {
+ error (0, 0, "No memory to add section name string table");
+ return cleanup (-1);
+ }
+
+ /* If the symtab shares strings then add those too. */
+ if (ndx == symtabndx)
+ {
+ /* If the section is (still) compressed we'll need to
+ uncompress it first to adjust the data, then
+ recompress it in the fixup pass. */
+ if (symtab_compressed == T_UNSET)
+ {
+ size_t size = shdr->sh_size;
+ if ((shdr->sh_flags == SHF_COMPRESSED) != 0)
+ {
+ /* Don't report the (internal) uncompression. */
+ if (compress_section (newscn, size, sname, NULL, ndx,
+ false, false, false) < 0)
+ return cleanup (-1);
+
+ symtab_size = size;
+ symtab_compressed = T_COMPRESS_ZLIB;
+ }
+ else if (strncmp (name, ".zdebug", strlen (".zdebug")) == 0)
+ {
+ /* Don't report the (internal) uncompression. */
+ if (compress_section (newscn, size, sname, NULL, ndx,
+ true, false, false) < 0)
+ return cleanup (-1);
+
+ symtab_size = size;
+ symtab_compressed = T_COMPRESS_GNU;
+ }
+ }
+
+ Elf_Data *symd = elf_getdata (newscn, NULL);
+ if (symd == NULL)
+ {
+ error (0, 0, "Couldn't get symtab data for section [%zd] %s",
+ ndx, name);
+ return cleanup (-1);
+ }
+ size_t elsize = gelf_fsize (elfnew, ELF_T_SYM, 1, EV_CURRENT);
+ size_t syms = symd->d_size / elsize;
+ symstrents = xmalloc (syms * sizeof (Dwelf_Strent *));
+ for (size_t i = 0; i < syms; i++)
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem);
+ if (sym == NULL)
+ {
+ error (0, 0, "Couldn't get symbol %zd", i);
+ return cleanup (-1);
+ }
+ if (sym->st_name != 0)
+ {
+ /* Note we take the name from the original ELF,
+ since the new one will not have setup the
+ strtab yet. */
+ const char *symname = elf_strptr (elf, shdrstrndx,
+ sym->st_name);
+ if (symname == NULL)
+ {
+ error (0, 0, "Couldn't get symbol %zd name", i);
+ return cleanup (-1);
+ }
+ symstrents[i] = dwelf_strtab_add (names, symname);
+ if (symstrents[i] == NULL)
+ {
+ error (0, 0, "No memory to add to symbol name");
+ return cleanup (-1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (adjust_names)
+ {
+ /* We got all needed strings, put the new data in the shstrtab. */
+ if (verbose > 0)
+ printf ("[%zd] Updating section string table\n", shdrstrndx);
+
+ scn = elf_getscn (elfnew, shdrstrndx);
+ if (scn == NULL)
+ {
+ error (0, 0, "Couldn't get new section header string table [%zd]",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ Elf_Data *data = elf_newdata (scn);
+ if (data == NULL)
+ {
+ error (0, 0, "Couldn't create new section header string table data");
+ return cleanup (-1);
+ }
+ if (dwelf_strtab_finalize (names, data) == NULL)
+ {
+ error (0, 0, "Not enough memory to create string table");
+ return cleanup (-1);
+ }
+ namesbuf = data->d_buf;
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for new section strings %zd",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ /* Note that we also might have to compress and possibly set
+ sh_off below */
+ shdr->sh_name = dwelf_strent_off (scnstrents[shdrstrndx]);
+ shdr->sh_type = SHT_STRTAB;
+ shdr->sh_flags = 0;
+ shdr->sh_addr = 0;
+ shdr->sh_offset = 0;
+ shdr->sh_size = data->d_size;
+ shdr->sh_link = SHN_UNDEF;
+ shdr->sh_info = SHN_UNDEF;
+ shdr->sh_addralign = 1;
+ shdr->sh_entsize = 0;
+
+ if (gelf_update_shdr (scn, shdr) == 0)
+ {
+ error (0, 0, "Couldn't update new section strings [%zd]",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ /* We might have to compress the data if the user asked us to,
+ or if the section was already compressed (and the user didn't
+ ask for decompression). Note somewhat identical code for
+ symtab below. */
+ if (shstrtab_compressed == T_UNSET)
+ {
+ /* The user didn't ask for compression, but maybe it was
+ compressed in the original ELF file. */
+ Elf_Scn *oldscn = elf_getscn (elf, shdrstrndx);
+ if (oldscn == NULL)
+ {
+ error (0, 0, "Couldn't get section header string table [%zd]",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ shdr = gelf_getshdr (oldscn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for old section strings [%zd]",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ shstrtab_name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+ if (shstrtab_name == NULL)
+ {
+ error (0, 0, "Couldn't get name for old section strings [%zd]",
+ shdrstrndx);
+ return cleanup (-1);
+ }
+
+ shstrtab_size = shdr->sh_size;
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ shstrtab_compressed = T_COMPRESS_ZLIB;
+ else if (strncmp (shstrtab_name, ".zdebug", strlen (".zdebug")) == 0)
+ shstrtab_compressed = T_COMPRESS_GNU;
+ }
+
+ /* Should we (re)compress? */
+ if (shstrtab_compressed != T_UNSET)
+ {
+ if (compress_section (scn, shstrtab_size, shstrtab_name,
+ shstrtab_newname, shdrstrndx,
+ shstrtab_compressed == T_COMPRESS_GNU,
+ true, verbose > 0) < 0)
+ return cleanup (-1);
+ }
+ }
+
+ /* Make sure to re-get the new ehdr. Adding phdrs and shdrs will
+ have changed it. */
+ if (gelf_getehdr (elfnew, &newehdr) == NULL)
+ {
+ error (0, 0, "Couldn't re-get new ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Set this after the sections have been created, otherwise section
+ zero might not exist yet. */
+ if (setshdrstrndx (elfnew, &newehdr, shdrstrndx) != 0)
+ {
+ error (0, 0, "Couldn't set new shdrstrndx: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Fixup pass. Adjust string table references, symbol table and
+ layout if necessary. */
+ if (layout || adjust_names)
+ {
+ scn = NULL;
+ while ((scn = elf_nextscn (elfnew, scn)) != NULL)
+ {
+ size_t ndx = elf_ndxscn (scn);
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get shdr for section %zd", ndx);
+ return cleanup (-1);
+ }
+
+ /* Keep the offset of allocated sections so they are at the
+ same place in the file. Add (possibly changed)
+ unallocated ones after the allocated ones. */
+ if ((shdr->sh_flags & SHF_ALLOC) == 0)
+ {
+ /* Zero means one. No alignment constraints. */
+ size_t addralign = shdr->sh_addralign ?: 1;
+ last_offset = (last_offset + addralign - 1) & ~(addralign - 1);
+ shdr->sh_offset = last_offset;
+ if (shdr->sh_type != SHT_NOBITS)
+ last_offset += shdr->sh_size;
+ }
+
+ if (adjust_names)
+ shdr->sh_name = dwelf_strent_off (scnstrents[ndx]);
+
+ if (gelf_update_shdr (scn, shdr) == 0)
+ {
+ error (0, 0, "Couldn't update section header %zd", ndx);
+ return cleanup (-1);
+ }
+
+ if (adjust_names && ndx == symtabndx)
+ {
+ if (verbose > 0)
+ printf ("[%zd] Updating symbol table\n", symtabndx);
+
+ Elf_Data *symd = elf_getdata (scn, NULL);
+ if (symd == NULL)
+ {
+ error (0, 0, "Couldn't get new symtab data section [%zd]",
+ ndx);
+ return cleanup (-1);
+ }
+ size_t elsize = gelf_fsize (elfnew, ELF_T_SYM, 1, EV_CURRENT);
+ size_t syms = symd->d_size / elsize;
+ for (size_t i = 0; i < syms; i++)
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symd, i, &sym_mem);
+ if (sym == NULL)
+ {
+ error (0, 0, "2 Couldn't get symbol %zd", i);
+ return cleanup (-1);
+ }
+
+ if (sym->st_name != 0)
+ {
+ sym->st_name = dwelf_strent_off (symstrents[i]);
+
+ if (gelf_update_sym (symd, i, sym) == 0)
+ {
+ error (0, 0, "Couldn't update symbol %zd", i);
+ return cleanup (-1);
+ }
+ }
+ }
+
+ /* We might have to compress the data if the user asked
+ us to, or if the section was already compressed (and
+ the user didn't ask for decompression). Note
+ somewhat identical code for shstrtab above. */
+ if (symtab_compressed == T_UNSET)
+ {
+ /* The user didn't ask for compression, but maybe it was
+ compressed in the original ELF file. */
+ Elf_Scn *oldscn = elf_getscn (elf, symtabndx);
+ if (oldscn == NULL)
+ {
+ error (0, 0, "Couldn't get symbol table [%zd]",
+ symtabndx);
+ return cleanup (-1);
+ }
+
+ shdr = gelf_getshdr (oldscn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ error (0, 0, "Couldn't get old symbol table shdr [%zd]",
+ symtabndx);
+ return cleanup (-1);
+ }
+
+ symtab_name = elf_strptr (elf, shdrstrndx, shdr->sh_name);
+ if (symtab_name == NULL)
+ {
+ error (0, 0, "Couldn't get old symbol table name [%zd]",
+ symtabndx);
+ return cleanup (-1);
+ }
+
+ symtab_size = shdr->sh_size;
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ symtab_compressed = T_COMPRESS_ZLIB;
+ else if (strncmp (symtab_name, ".zdebug",
+ strlen (".zdebug")) == 0)
+ symtab_compressed = T_COMPRESS_GNU;
+ }
+
+ /* Should we (re)compress? */
+ if (symtab_compressed != T_UNSET)
+ {
+ if (compress_section (scn, symtab_size, symtab_name,
+ symtab_newname, symtabndx,
+ symtab_compressed == T_COMPRESS_GNU,
+ true, verbose > 0) < 0)
+ return cleanup (-1);
+ }
+ }
+ }
+ }
+
+ /* If we have phdrs we want elf_update to layout the SHF_ALLOC
+ sections precisely as in the original file. In that case we are
+ also responsible for setting phoff and shoff */
+ if (layout)
+ {
+ if (gelf_getehdr (elfnew, &newehdr) == NULL)
+ {
+ error (0, 0, "Couldn't get ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ /* Position the shdrs after the last (unallocated) section. */
+ const size_t offsize = gelf_fsize (elfnew, ELF_T_OFF, 1, EV_CURRENT);
+ newehdr.e_shoff = ((last_offset + offsize - 1)
+ & ~((GElf_Off) (offsize - 1)));
+
+ /* The phdrs go in the same place as in the original file.
+ Normally right after the ELF header. */
+ newehdr.e_phoff = ehdr.e_phoff;
+
+ if (gelf_update_ehdr (elfnew, &newehdr) == 0)
+ {
+ error (0, 0, "Couldn't update ehdr: %s", elf_errmsg (-1));
+ return cleanup (-1);
+ }
+ }
+
+ elf_flagelf (elfnew, ELF_C_SET, ((layout ? ELF_F_LAYOUT : 0)
+ | (permissive ? ELF_F_PERMISSIVE : 0)));
+
+ if (elf_update (elfnew, ELF_C_WRITE) < 0)
+ {
+ error (0, 0, "Couldn't write %s: %s", fnew, elf_errmsg (-1));
+ return cleanup (-1);
+ }
+
+ elf_end (elfnew);
+ elfnew = NULL;
+
+ /* Try to match mode and owner.group of the original file. */
+ if (fchmod (fdnew, st.st_mode & ALLPERMS) != 0)
+ if (verbose >= 0)
+ error (0, errno, "Couldn't fchmod %s", fnew);
+ if (fchown (fdnew, st.st_uid, st.st_gid) != 0)
+ if (verbose >= 0)
+ error (0, errno, "Couldn't fchown %s", fnew);
+
+ /* Finally replace the old file with the new file. */
+ if (foutput == NULL)
+ if (rename (fnew, fname) != 0)
+ {
+ error (0, errno, "Couldn't rename %s to %s", fnew, fname);
+ return cleanup (-1);
+ }
+
+ /* We are finally done with the new file, don't unlink it now. */
+ free (fnew);
+ fnew = NULL;
+
+ return cleanup (0);
+}
+
+int
+main (int argc, char **argv)
+{
+ const struct argp_option options[] =
+ {
+ { "output", 'o', "FILE", 0,
+ N_("Place (de)compressed output into FILE"),
+ 0 },
+ { "type", 't', "TYPE", 0,
+ N_("What type of compression to apply. TYPE can be 'none' (decompress), 'zlib' (ELF ZLIB compression, the default, 'zlib-gabi' is an alias) or 'zlib-gnu' (.zdebug GNU style compression, 'gnu' is an alias)"),
+ 0 },
+ { "name", 'n', "SECTION", 0,
+ N_("SECTION name to (de)compress, SECTION is an extended wildcard pattern (defaults to '.?(z)debug*')"),
+ 0 },
+ { "verbose", 'v', NULL, 0,
+ N_("Print a message for each section being (de)compressed"),
+ 0 },
+ { "force", 'f', NULL, 0,
+ N_("Force compression of section even if it would become larger"),
+ 0 },
+ { "permissive", 'p', NULL, 0,
+ N_("Relax a few rules to handle slightly broken ELF files"),
+ 0 },
+ { "quiet", 'q', NULL, 0,
+ N_("Be silent when a section cannot be compressed"),
+ 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+ };
+
+ const struct argp argp =
+ {
+ .options = options,
+ .parser = parse_opt,
+ .args_doc = N_("FILE..."),
+ .doc = N_("Compress or decompress sections in an ELF file.")
+ };
+
+ int remaining;
+ if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0)
+ return EXIT_FAILURE;
+
+ /* Should already be handled by ARGP_KEY_NO_ARGS case above,
+ just sanity check. */
+ if (remaining >= argc)
+ error (EXIT_FAILURE, 0, N_("No input file given"));
+
+ /* Likewise for the ARGP_KEY_ARGS case above, an extra sanity check. */
+ if (foutput != NULL && remaining + 1 < argc)
+ error (EXIT_FAILURE, 0,
+ N_("Only one input file allowed together with '-o'"));
+
+ elf_version (EV_CURRENT);
+
+ /* Process all the remaining files. */
+ int result = 0;
+ do
+ result |= process_file (argv[remaining]);
+ while (++remaining < argc);
+
+ free_patterns ();
+ return result;
+}
diff --git a/src/elflint.c b/src/elflint.c
new file mode 100644
index 0000000..df1b3a0
--- /dev/null
+++ b/src/elflint.c
@@ -0,0 +1,4756 @@
+/* Pedantic checking of ELF files compliance with gABI/psABI spec.
+ Copyright (C) 2001-2015, 2017 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2001.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <endian.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <elf-knowledge.h>
+#include <libeu.h>
+#include <system.h>
+#include <printversion.h>
+#include "../libelf/libelfP.h"
+#include "../libelf/common.h"
+#include "../libebl/libeblP.h"
+#include "../libdw/libdwP.h"
+#include "../libdwfl/libdwflP.h"
+#include "../libdw/memory-access.h"
+
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+#define ARGP_strict 300
+#define ARGP_gnuld 301
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { "strict", ARGP_strict, NULL, 0,
+ N_("Be extremely strict, flag level 2 features."), 0 },
+ { "quiet", 'q', NULL, 0, N_("Do not print anything if successful"), 0 },
+ { "debuginfo", 'd', NULL, 0, N_("Binary is a separate debuginfo file"), 0 },
+ { "gnu-ld", ARGP_gnuld, NULL, 0,
+ N_("Binary has been created with GNU ld and is therefore known to be \
+broken in certain ways"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("\
+Pedantic checking of ELF files compliance with gABI/psABI spec.");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("FILE...");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+
+/* Declarations of local functions. */
+static void process_file (int fd, Elf *elf, const char *prefix,
+ const char *suffix, const char *fname, size_t size,
+ bool only_one);
+static void process_elf_file (Elf *elf, const char *prefix, const char *suffix,
+ const char *fname, size_t size, bool only_one);
+static void check_note_section (Ebl *ebl, GElf_Ehdr *ehdr,
+ GElf_Shdr *shdr, int idx);
+
+
+/* Report an error. */
+#define ERROR(str, args...) \
+ do { \
+ printf (str, ##args); \
+ ++error_count; \
+ } while (0)
+static unsigned int error_count;
+
+/* True if we should perform very strict testing. */
+static bool be_strict;
+
+/* True if no message is to be printed if the run is succesful. */
+static bool be_quiet;
+
+/* True if binary is from strip -f, not a normal ELF file. */
+static bool is_debuginfo;
+
+/* True if binary is assumed to be generated with GNU ld. */
+static bool gnuld;
+
+/* Index of section header string table. */
+static uint32_t shstrndx;
+
+/* Array to count references in section groups. */
+static int *scnref;
+
+/* Numbers of sections and program headers. */
+static unsigned int shnum;
+static unsigned int phnum;
+
+
+int
+main (int argc, char *argv[])
+{
+ /* Set locale. */
+ setlocale (LC_ALL, "");
+
+ /* Initialize the message catalog. */
+ textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ int remaining;
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Before we start tell the ELF library which version we are using. */
+ elf_version (EV_CURRENT);
+
+ /* Now process all the files given at the command line. */
+ bool only_one = remaining + 1 == argc;
+ do
+ {
+ /* Open the file. */
+ int fd = open (argv[remaining], O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, gettext ("cannot open input file"));
+ continue;
+ }
+
+ /* Create an `Elf' descriptor. */
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ ERROR (gettext ("cannot generate Elf descriptor: %s\n"),
+ elf_errmsg (-1));
+ else
+ {
+ unsigned int prev_error_count = error_count;
+ struct stat st;
+
+ if (fstat (fd, &st) != 0)
+ {
+ printf ("cannot stat '%s': %m\n", argv[remaining]);
+ close (fd);
+ continue;
+ }
+
+ process_file (fd, elf, NULL, NULL, argv[remaining], st.st_size,
+ only_one);
+
+ /* Now we can close the descriptor. */
+ if (elf_end (elf) != 0)
+ ERROR (gettext ("error while closing Elf descriptor: %s\n"),
+ elf_errmsg (-1));
+
+ if (prev_error_count == error_count && !be_quiet)
+ puts (gettext ("No errors"));
+ }
+
+ close (fd);
+ }
+ while (++remaining < argc);
+
+ return error_count != 0;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg __attribute__ ((unused)),
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case ARGP_strict:
+ be_strict = true;
+ break;
+
+ case 'q':
+ be_quiet = true;
+ break;
+
+ case 'd':
+ is_debuginfo = true;
+ break;
+
+ case ARGP_gnuld:
+ gnuld = true;
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ fputs (gettext ("Missing file name.\n"), stderr);
+ argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
+ exit (EXIT_FAILURE);
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+/* Process one file. */
+static void
+process_file (int fd, Elf *elf, const char *prefix, const char *suffix,
+ const char *fname, size_t size, bool only_one)
+{
+ /* We can handle two types of files: ELF files and archives. */
+ Elf_Kind kind = elf_kind (elf);
+
+ switch (kind)
+ {
+ case ELF_K_ELF:
+ /* Yes! It's an ELF file. */
+ process_elf_file (elf, prefix, suffix, fname, size, only_one);
+ break;
+
+ case ELF_K_AR:
+ {
+ Elf *subelf;
+ Elf_Cmd cmd = ELF_C_READ_MMAP;
+ size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
+ size_t fname_len = strlen (fname) + 1;
+ char new_prefix[prefix_len + 1 + fname_len];
+ char new_suffix[(suffix == NULL ? 0 : strlen (suffix)) + 2];
+ char *cp = new_prefix;
+
+ /* Create the full name of the file. */
+ if (prefix != NULL)
+ {
+ cp = mempcpy (cp, prefix, prefix_len);
+ *cp++ = '(';
+ strcpy (stpcpy (new_suffix, suffix), ")");
+ }
+ else
+ new_suffix[0] = '\0';
+ memcpy (cp, fname, fname_len);
+
+ /* It's an archive. We process each file in it. */
+ while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
+ {
+ kind = elf_kind (subelf);
+
+ /* Call this function recursively. */
+ if (kind == ELF_K_ELF || kind == ELF_K_AR)
+ {
+ Elf_Arhdr *arhdr = elf_getarhdr (subelf);
+ assert (arhdr != NULL);
+
+ process_file (fd, subelf, new_prefix, new_suffix,
+ arhdr->ar_name, arhdr->ar_size, false);
+ }
+
+ /* Get next archive element. */
+ cmd = elf_next (subelf);
+ if (elf_end (subelf) != 0)
+ ERROR (gettext (" error while freeing sub-ELF descriptor: %s\n"),
+ elf_errmsg (-1));
+ }
+ }
+ break;
+
+ default:
+ /* We cannot do anything. */
+ ERROR (gettext ("\
+Not an ELF file - it has the wrong magic bytes at the start\n"));
+ break;
+ }
+}
+
+
+static const char *
+section_name (Ebl *ebl, int idx)
+{
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr;
+ const char *ret;
+
+ if ((unsigned int) idx > shnum)
+ return "<invalid>";
+
+ shdr = gelf_getshdr (elf_getscn (ebl->elf, idx), &shdr_mem);
+ if (shdr == NULL)
+ return "<invalid>";
+
+ ret = elf_strptr (ebl->elf, shstrndx, shdr->sh_name);
+ if (ret == NULL)
+ return "<invalid>";
+ return ret;
+}
+
+
+static const int valid_e_machine[] =
+ {
+ EM_M32, EM_SPARC, EM_386, EM_68K, EM_88K, EM_860, EM_MIPS, EM_S370,
+ EM_MIPS_RS3_LE, EM_PARISC, EM_VPP500, EM_SPARC32PLUS, EM_960, EM_PPC,
+ EM_PPC64, EM_S390, EM_V800, EM_FR20, EM_RH32, EM_RCE, EM_ARM,
+ EM_FAKE_ALPHA, EM_SH, EM_SPARCV9, EM_TRICORE, EM_ARC, EM_H8_300,
+ EM_H8_300H, EM_H8S, EM_H8_500, EM_IA_64, EM_MIPS_X, EM_COLDFIRE,
+ EM_68HC12, EM_MMA, EM_PCP, EM_NCPU, EM_NDR1, EM_STARCORE, EM_ME16,
+ EM_ST100, EM_TINYJ, EM_X86_64, EM_PDSP, EM_FX66, EM_ST9PLUS, EM_ST7,
+ EM_68HC16, EM_68HC11, EM_68HC08, EM_68HC05, EM_SVX, EM_ST19, EM_VAX,
+ EM_CRIS, EM_JAVELIN, EM_FIREPATH, EM_ZSP, EM_MMIX, EM_HUANY, EM_PRISM,
+ EM_AVR, EM_FR30, EM_D10V, EM_D30V, EM_V850, EM_M32R, EM_MN10300,
+ EM_MN10200, EM_PJ, EM_OPENRISC, EM_ARC_A5, EM_XTENSA, EM_ALPHA,
+ EM_TILEGX, EM_TILEPRO, EM_AARCH64, EM_BPF
+ };
+#define nvalid_e_machine \
+ (sizeof (valid_e_machine) / sizeof (valid_e_machine[0]))
+
+
+static void
+check_elf_header (Ebl *ebl, GElf_Ehdr *ehdr, size_t size)
+{
+ char buf[512];
+ size_t cnt;
+
+ /* Check e_ident field. */
+ if (ehdr->e_ident[EI_MAG0] != ELFMAG0)
+ ERROR ("e_ident[%d] != '%c'\n", EI_MAG0, ELFMAG0);
+ if (ehdr->e_ident[EI_MAG1] != ELFMAG1)
+ ERROR ("e_ident[%d] != '%c'\n", EI_MAG1, ELFMAG1);
+ if (ehdr->e_ident[EI_MAG2] != ELFMAG2)
+ ERROR ("e_ident[%d] != '%c'\n", EI_MAG2, ELFMAG2);
+ if (ehdr->e_ident[EI_MAG3] != ELFMAG3)
+ ERROR ("e_ident[%d] != '%c'\n", EI_MAG3, ELFMAG3);
+
+ if (ehdr->e_ident[EI_CLASS] != ELFCLASS32
+ && ehdr->e_ident[EI_CLASS] != ELFCLASS64)
+ ERROR (gettext ("e_ident[%d] == %d is no known class\n"),
+ EI_CLASS, ehdr->e_ident[EI_CLASS]);
+
+ if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB
+ && ehdr->e_ident[EI_DATA] != ELFDATA2MSB)
+ ERROR (gettext ("e_ident[%d] == %d is no known data encoding\n"),
+ EI_DATA, ehdr->e_ident[EI_DATA]);
+
+ if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
+ ERROR (gettext ("unknown ELF header version number e_ident[%d] == %d\n"),
+ EI_VERSION, ehdr->e_ident[EI_VERSION]);
+
+ /* We currently don't handle any OS ABIs other than Linux and the
+ kFreeBSD variant of Debian. */
+ if (ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE
+ && ehdr->e_ident[EI_OSABI] != ELFOSABI_LINUX
+ && ehdr->e_ident[EI_OSABI] != ELFOSABI_FREEBSD)
+ ERROR (gettext ("unsupported OS ABI e_ident[%d] == '%s'\n"),
+ EI_OSABI,
+ ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf)));
+
+ /* No ABI versions other than zero supported either. */
+ if (ehdr->e_ident[EI_ABIVERSION] != 0)
+ ERROR (gettext ("unsupport ABI version e_ident[%d] == %d\n"),
+ EI_ABIVERSION, ehdr->e_ident[EI_ABIVERSION]);
+
+ for (cnt = EI_PAD; cnt < EI_NIDENT; ++cnt)
+ if (ehdr->e_ident[cnt] != 0)
+ ERROR (gettext ("e_ident[%zu] is not zero\n"), cnt);
+
+ /* Check the e_type field. */
+ if (ehdr->e_type != ET_REL && ehdr->e_type != ET_EXEC
+ && ehdr->e_type != ET_DYN && ehdr->e_type != ET_CORE)
+ ERROR (gettext ("unknown object file type %d\n"), ehdr->e_type);
+
+ /* Check the e_machine field. */
+ for (cnt = 0; cnt < nvalid_e_machine; ++cnt)
+ if (valid_e_machine[cnt] == ehdr->e_machine)
+ break;
+ if (cnt == nvalid_e_machine)
+ ERROR (gettext ("unknown machine type %d\n"), ehdr->e_machine);
+
+ /* Check the e_version field. */
+ if (ehdr->e_version != EV_CURRENT)
+ ERROR (gettext ("unknown object file version\n"));
+
+ /* Check the e_phoff and e_phnum fields. */
+ if (ehdr->e_phoff == 0)
+ {
+ if (ehdr->e_phnum != 0)
+ ERROR (gettext ("invalid program header offset\n"));
+ else if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN)
+ ERROR (gettext ("\
+executables and DSOs cannot have zero program header offset\n"));
+ }
+ else if (ehdr->e_phnum == 0)
+ ERROR (gettext ("invalid number of program header entries\n"));
+
+ /* Check the e_shoff field. */
+ shnum = ehdr->e_shnum;
+ shstrndx = ehdr->e_shstrndx;
+ if (ehdr->e_shoff == 0)
+ {
+ if (ehdr->e_shnum != 0)
+ ERROR (gettext ("invalid section header table offset\n"));
+ else if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN
+ && ehdr->e_type != ET_CORE)
+ ERROR (gettext ("section header table must be present\n"));
+ }
+ else
+ {
+ if (ehdr->e_shnum == 0)
+ {
+ /* Get the header of the zeroth section. The sh_size field
+ might contain the section number. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
+ if (shdr != NULL)
+ {
+ /* The error will be reported later. */
+ if (shdr->sh_size == 0)
+ ERROR (gettext ("\
+invalid number of section header table entries\n"));
+ else
+ shnum = shdr->sh_size;
+ }
+ }
+
+ if (ehdr->e_shstrndx == SHN_XINDEX)
+ {
+ /* Get the header of the zeroth section. The sh_size field
+ might contain the section number. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
+ if (shdr != NULL && shdr->sh_link < shnum)
+ shstrndx = shdr->sh_link;
+ }
+ else if (shstrndx >= shnum)
+ ERROR (gettext ("invalid section header index\n"));
+ }
+
+ /* Check the shdrs actually exist. And uncompress them before
+ further checking. Indexes between sections reference the
+ uncompressed data. */
+ unsigned int scnt;
+ Elf_Scn *scn = NULL;
+ for (scnt = 1; scnt < shnum; ++scnt)
+ {
+ scn = elf_nextscn (ebl->elf, scn);
+ if (scn == NULL)
+ break;
+ /* If the section wasn't compressed this does nothing, but
+ returns an error. We don't care. */
+ elf_compress (scn, 0, 0);
+ }
+ if (scnt < shnum)
+ ERROR (gettext ("Can only check %u headers, shnum was %u\n"), scnt, shnum);
+ shnum = scnt;
+
+ phnum = ehdr->e_phnum;
+ if (ehdr->e_phnum == PN_XNUM)
+ {
+ /* Get the header of the zeroth section. The sh_info field
+ might contain the phnum count. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
+ if (shdr != NULL)
+ {
+ /* The error will be reported later. */
+ if (shdr->sh_info < PN_XNUM)
+ ERROR (gettext ("\
+invalid number of program header table entries\n"));
+ else
+ phnum = shdr->sh_info;
+ }
+ }
+
+ /* Check the phdrs actually exist. */
+ unsigned int pcnt;
+ for (pcnt = 0; pcnt < phnum; ++pcnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem);
+ if (phdr == NULL)
+ break;
+ }
+ if (pcnt < phnum)
+ ERROR (gettext ("Can only check %u headers, phnum was %u\n"), pcnt, phnum);
+ phnum = pcnt;
+
+ /* Check the e_flags field. */
+ if (!ebl_machine_flag_check (ebl, ehdr->e_flags))
+ ERROR (gettext ("invalid machine flags: %s\n"),
+ ebl_machine_flag_name (ebl, ehdr->e_flags, buf, sizeof (buf)));
+
+ /* Check e_ehsize, e_phentsize, and e_shentsize fields. */
+ if (gelf_getclass (ebl->elf) == ELFCLASS32)
+ {
+ if (ehdr->e_ehsize != 0 && ehdr->e_ehsize != sizeof (Elf32_Ehdr))
+ ERROR (gettext ("invalid ELF header size: %hd\n"), ehdr->e_ehsize);
+
+ if (ehdr->e_phentsize != 0 && ehdr->e_phentsize != sizeof (Elf32_Phdr))
+ ERROR (gettext ("invalid program header size: %hd\n"),
+ ehdr->e_phentsize);
+ else if (ehdr->e_phoff + phnum * ehdr->e_phentsize > size)
+ ERROR (gettext ("invalid program header position or size\n"));
+
+ if (ehdr->e_shentsize != 0 && ehdr->e_shentsize != sizeof (Elf32_Shdr))
+ ERROR (gettext ("invalid section header size: %hd\n"),
+ ehdr->e_shentsize);
+ else if (ehdr->e_shoff + shnum * ehdr->e_shentsize > size)
+ ERROR (gettext ("invalid section header position or size\n"));
+ }
+ else if (gelf_getclass (ebl->elf) == ELFCLASS64)
+ {
+ if (ehdr->e_ehsize != 0 && ehdr->e_ehsize != sizeof (Elf64_Ehdr))
+ ERROR (gettext ("invalid ELF header size: %hd\n"), ehdr->e_ehsize);
+
+ if (ehdr->e_phentsize != 0 && ehdr->e_phentsize != sizeof (Elf64_Phdr))
+ ERROR (gettext ("invalid program header size: %hd\n"),
+ ehdr->e_phentsize);
+ else if (ehdr->e_phoff + phnum * ehdr->e_phentsize > size)
+ ERROR (gettext ("invalid program header position or size\n"));
+
+ if (ehdr->e_shentsize != 0 && ehdr->e_shentsize != sizeof (Elf64_Shdr))
+ ERROR (gettext ("invalid section header size: %hd\n"),
+ ehdr->e_shentsize);
+ else if (ehdr->e_shoff + ehdr->e_shnum * ehdr->e_shentsize > size)
+ ERROR (gettext ("invalid section header position or size\n"));
+ }
+}
+
+
+/* Check that there is a section group section with index < IDX which
+ contains section IDX and that there is exactly one. */
+static void
+check_scn_group (Ebl *ebl, int idx)
+{
+ if (scnref[idx] == 0)
+ {
+ /* No reference so far. Search following sections, maybe the
+ order is wrong. */
+ size_t cnt;
+
+ for (cnt = idx + 1; cnt < shnum; ++cnt)
+ {
+ Elf_Scn *scn = elf_getscn (ebl->elf, cnt);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ /* We cannot get the section header so we cannot check it.
+ The error to get the section header will be shown
+ somewhere else. */
+ continue;
+
+ if (shdr->sh_type != SHT_GROUP)
+ continue;
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL || data->d_buf == NULL
+ || data->d_size < sizeof (Elf32_Word))
+ /* Cannot check the section. */
+ continue;
+
+ Elf32_Word *grpdata = (Elf32_Word *) data->d_buf;
+ for (size_t inner = 1; inner < data->d_size / sizeof (Elf32_Word);
+ ++inner)
+ if (grpdata[inner] == (Elf32_Word) idx)
+ goto out;
+ }
+
+ out:
+ if (cnt == shnum)
+ ERROR (gettext ("\
+section [%2d] '%s': section with SHF_GROUP flag set not part of a section group\n"),
+ idx, section_name (ebl, idx));
+ else
+ ERROR (gettext ("\
+section [%2d] '%s': section group [%2zu] '%s' does not precede group member\n"),
+ idx, section_name (ebl, idx),
+ cnt, section_name (ebl, cnt));
+ }
+}
+
+
+static void
+check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ bool no_xndx_warned = false;
+ int no_pt_tls = 0;
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ GElf_Shdr strshdr_mem;
+ GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &strshdr_mem);
+ if (strshdr == NULL)
+ return;
+
+ if (strshdr->sh_type != SHT_STRTAB)
+ {
+ ERROR (gettext ("section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"),
+ shdr->sh_link, section_name (ebl, shdr->sh_link),
+ idx, section_name (ebl, idx));
+ strshdr = NULL;
+ }
+
+ /* Search for an extended section index table section. */
+ Elf_Data *xndxdata = NULL;
+ Elf32_Word xndxscnidx = 0;
+ bool found_xndx = false;
+ for (size_t cnt = 1; cnt < shnum; ++cnt)
+ if (cnt != (size_t) idx)
+ {
+ Elf_Scn *xndxscn = elf_getscn (ebl->elf, cnt);
+ GElf_Shdr xndxshdr_mem;
+ GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem);
+ if (xndxshdr == NULL)
+ continue;
+
+ if (xndxshdr->sh_type == SHT_SYMTAB_SHNDX
+ && xndxshdr->sh_link == (GElf_Word) idx)
+ {
+ if (found_xndx)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol table cannot have more than one extended index section\n"),
+ idx, section_name (ebl, idx));
+
+ xndxdata = elf_getdata (xndxscn, NULL);
+ xndxscnidx = elf_ndxscn (xndxscn);
+ found_xndx = true;
+ }
+ }
+
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT);
+ if (shdr->sh_entsize != sh_entsize)
+ ERROR (gettext ("\
+section [%2u] '%s': entry size is does not match ElfXX_Sym\n"),
+ idx, section_name (ebl, idx));
+
+ /* Test the zeroth entry. */
+ GElf_Sym sym_mem;
+ Elf32_Word xndx;
+ GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, 0, &sym_mem, &xndx);
+ if (sym == NULL)
+ ERROR (gettext ("section [%2d] '%s': cannot get symbol %d: %s\n"),
+ idx, section_name (ebl, idx), 0, elf_errmsg (-1));
+ else
+ {
+ if (sym->st_name != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, idx), "st_name");
+ if (sym->st_value != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, idx), "st_value");
+ if (sym->st_size != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, idx), "st_size");
+ if (sym->st_info != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, idx), "st_info");
+ if (sym->st_other != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, idx), "st_other");
+ if (sym->st_shndx != 0)
+ ERROR (gettext ("section [%2d] '%s': '%s' in zeroth entry not zero\n"),
+ idx, section_name (ebl, idx), "st_shndx");
+ if (xndxdata != NULL && xndx != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': XINDEX for zeroth entry not zero\n"),
+ xndxscnidx, section_name (ebl, xndxscnidx));
+ }
+
+ for (size_t cnt = 1; cnt < shdr->sh_size / sh_entsize; ++cnt)
+ {
+ sym = gelf_getsymshndx (data, xndxdata, cnt, &sym_mem, &xndx);
+ if (sym == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get symbol %zu: %s\n"),
+ idx, section_name (ebl, idx), cnt, elf_errmsg (-1));
+ continue;
+ }
+
+ const char *name = NULL;
+ if (strshdr == NULL)
+ name = "";
+ else if (sym->st_name >= strshdr->sh_size)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: invalid name value\n"),
+ idx, section_name (ebl, idx), cnt);
+ else
+ {
+ name = elf_strptr (ebl->elf, shdr->sh_link, sym->st_name);
+ if (name == NULL)
+ name = "";
+ }
+
+ if (sym->st_shndx == SHN_XINDEX)
+ {
+ if (xndxdata == NULL)
+ {
+ if (!no_xndx_warned)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: too large section index but no extended section index section\n"),
+ idx, section_name (ebl, idx), cnt);
+ no_xndx_warned = true;
+ }
+ else if (xndx < SHN_LORESERVE)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: XINDEX used for index which would fit in st_shndx (%" PRIu32 ")\n"),
+ xndxscnidx, section_name (ebl, xndxscnidx), cnt,
+ xndx);
+ }
+ else if ((sym->st_shndx >= SHN_LORESERVE
+ // && sym->st_shndx <= SHN_HIRESERVE always true
+ && sym->st_shndx != SHN_ABS
+ && sym->st_shndx != SHN_COMMON)
+ || (sym->st_shndx >= shnum
+ && (sym->st_shndx < SHN_LORESERVE
+ /* || sym->st_shndx > SHN_HIRESERVE always false */)))
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: invalid section index\n"),
+ idx, section_name (ebl, idx), cnt);
+ else
+ xndx = sym->st_shndx;
+
+ if (GELF_ST_TYPE (sym->st_info) >= STT_NUM
+ && !ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info), NULL, 0))
+ ERROR (gettext ("section [%2d] '%s': symbol %zu: unknown type\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ if (GELF_ST_BIND (sym->st_info) >= STB_NUM
+ && !ebl_symbol_binding_name (ebl, GELF_ST_BIND (sym->st_info), NULL,
+ 0))
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: unknown symbol binding\n"),
+ idx, section_name (ebl, idx), cnt);
+ if (GELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE
+ && GELF_ST_TYPE (sym->st_info) != STT_OBJECT)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: unique symbol not of object type\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ if (xndx == SHN_COMMON)
+ {
+ /* Common symbols can only appear in relocatable files. */
+ if (ehdr->e_type != ET_REL)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: COMMON only allowed in relocatable files\n"),
+ idx, section_name (ebl, idx), cnt);
+ if (cnt < shdr->sh_info)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: local COMMON symbols are nonsense\n"),
+ idx, section_name (ebl, idx), cnt);
+ if (GELF_R_TYPE (sym->st_info) == STT_FUNC)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: function in COMMON section is nonsense\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+ else if (xndx > 0 && xndx < shnum)
+ {
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr;
+
+ destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx), &destshdr_mem);
+ if (destshdr != NULL)
+ {
+ GElf_Addr sh_addr = (ehdr->e_type == ET_REL ? 0
+ : destshdr->sh_addr);
+ GElf_Addr st_value;
+ if (GELF_ST_TYPE (sym->st_info) == STT_FUNC
+ || (GELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))
+ st_value = sym->st_value & ebl_func_addr_mask (ebl);
+ else
+ st_value = sym->st_value;
+ if (GELF_ST_TYPE (sym->st_info) != STT_TLS)
+ {
+ if (! ebl_check_special_symbol (ebl, ehdr, sym, name,
+ destshdr))
+ {
+ if (st_value - sh_addr > destshdr->sh_size)
+ {
+ /* GNU ld has severe bugs. When it decides to remove
+ empty sections it leaves symbols referencing them
+ behind. These are symbols in .symtab or .dynsym
+ and for the named symbols have zero size. See
+ sourceware PR13621. */
+ if (!gnuld
+ || (strcmp (section_name (ebl, idx), ".symtab")
+ && strcmp (section_name (ebl, idx),
+ ".dynsym"))
+ || sym->st_size != 0
+ || (strcmp (name, "__preinit_array_start") != 0
+ && strcmp (name, "__preinit_array_end") != 0
+ && strcmp (name, "__init_array_start") != 0
+ && strcmp (name, "__init_array_end") != 0
+ && strcmp (name, "__fini_array_start") != 0
+ && strcmp (name, "__fini_array_end") != 0
+ && strcmp (name, "__bss_start") != 0
+ && strcmp (name, "__bss_start__") != 0
+ && strcmp (name, "__TMC_END__") != 0
+ && strcmp (name, ".TOC.") != 0
+ && strcmp (name, "_edata") != 0
+ && strcmp (name, "__edata") != 0
+ && strcmp (name, "_end") != 0
+ && strcmp (name, "__end") != 0))
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: st_value out of bounds\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+ else if ((st_value - sh_addr
+ + sym->st_size) > destshdr->sh_size)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"),
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
+ }
+ }
+ else
+ {
+ if ((destshdr->sh_flags & SHF_TLS) == 0)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: referenced section [%2d] '%s' does not have SHF_TLS flag set\n"),
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
+
+ if (ehdr->e_type == ET_REL)
+ {
+ /* For object files the symbol value must fall
+ into the section. */
+ if (st_value > destshdr->sh_size)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: st_value out of bounds of referenced section [%2d] '%s'\n"),
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
+ else if (st_value + sym->st_size
+ > destshdr->sh_size)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"),
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
+ }
+ else
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = NULL;
+ unsigned int pcnt;
+
+ for (pcnt = 0; pcnt < phnum; ++pcnt)
+ {
+ phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem);
+ if (phdr != NULL && phdr->p_type == PT_TLS)
+ break;
+ }
+
+ if (pcnt == phnum)
+ {
+ if (no_pt_tls++ == 0)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: TLS symbol but no TLS program header entry\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+ else if (phdr == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: TLS symbol but couldn't get TLS program header entry\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+ else if (!is_debuginfo)
+ {
+ if (st_value
+ < destshdr->sh_offset - phdr->p_offset)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: st_value short of referenced section [%2d] '%s'\n"),
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
+ else if (st_value
+ > (destshdr->sh_offset - phdr->p_offset
+ + destshdr->sh_size))
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: st_value out of bounds of referenced section [%2d] '%s'\n"),
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
+ else if (st_value + sym->st_size
+ > (destshdr->sh_offset - phdr->p_offset
+ + destshdr->sh_size))
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu does not fit completely in referenced section [%2d] '%s'\n"),
+ idx, section_name (ebl, idx), cnt,
+ (int) xndx, section_name (ebl, xndx));
+ }
+ }
+ }
+ }
+ }
+
+ if (GELF_ST_BIND (sym->st_info) == STB_LOCAL)
+ {
+ if (cnt >= shdr->sh_info)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: local symbol outside range described in sh_info\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+ else
+ {
+ if (cnt < shdr->sh_info)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: non-local symbol outside range described in sh_info\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+
+ if (GELF_ST_TYPE (sym->st_info) == STT_SECTION
+ && GELF_ST_BIND (sym->st_info) != STB_LOCAL)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: non-local section symbol\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ if (name != NULL)
+ {
+ if (strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
+ {
+ /* Check that address and size match the global offset table. */
+
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, xndx),
+ &destshdr_mem);
+
+ if (destshdr == NULL && xndx == SHN_ABS)
+ {
+ /* In a DSO, we have to find the GOT section by name. */
+ Elf_Scn *gotscn = NULL;
+ Elf_Scn *gscn = NULL;
+ while ((gscn = elf_nextscn (ebl->elf, gscn)) != NULL)
+ {
+ destshdr = gelf_getshdr (gscn, &destshdr_mem);
+ assert (destshdr != NULL);
+ const char *sname = elf_strptr (ebl->elf,
+ ehdr->e_shstrndx,
+ destshdr->sh_name);
+ if (sname != NULL)
+ {
+ if (strcmp (sname, ".got.plt") == 0)
+ break;
+ if (strcmp (sname, ".got") == 0)
+ /* Do not stop looking.
+ There might be a .got.plt section. */
+ gotscn = gscn;
+ }
+
+ destshdr = NULL;
+ }
+
+ if (destshdr == NULL && gotscn != NULL)
+ destshdr = gelf_getshdr (gotscn, &destshdr_mem);
+ }
+
+ const char *sname = ((destshdr == NULL || xndx == SHN_UNDEF)
+ ? NULL
+ : elf_strptr (ebl->elf, ehdr->e_shstrndx,
+ destshdr->sh_name));
+ if (sname == NULL)
+ {
+ if (xndx != SHN_UNDEF || ehdr->e_type != ET_REL)
+ ERROR (gettext ("\
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \
+bad section [%2d]\n"),
+ idx, section_name (ebl, idx), xndx);
+ }
+ else if (strcmp (sname, ".got.plt") != 0
+ && strcmp (sname, ".got") != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol refers to \
+section [%2d] '%s'\n"),
+ idx, section_name (ebl, idx), xndx, sname);
+
+ if (destshdr != NULL)
+ {
+ /* Found it. */
+ if (!ebl_check_special_symbol (ebl, ehdr, sym, name,
+ destshdr))
+ {
+ if (ehdr->e_type != ET_REL
+ && sym->st_value != destshdr->sh_addr)
+ /* This test is more strict than the psABIs which
+ usually allow the symbol to be in the middle of
+ the .got section, allowing negative offsets. */
+ ERROR (gettext ("\
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol value %#" PRIx64 " does not match %s section address %#" PRIx64 "\n"),
+ idx, section_name (ebl, idx),
+ (uint64_t) sym->st_value,
+ sname, (uint64_t) destshdr->sh_addr);
+
+ if (!gnuld && sym->st_size != destshdr->sh_size)
+ ERROR (gettext ("\
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol size %" PRIu64 " does not match %s section size %" PRIu64 "\n"),
+ idx, section_name (ebl, idx),
+ (uint64_t) sym->st_size,
+ sname, (uint64_t) destshdr->sh_size);
+ }
+ }
+ else
+ ERROR (gettext ("\
+section [%2d] '%s': _GLOBAL_OFFSET_TABLE_ symbol present, but no .got section\n"),
+ idx, section_name (ebl, idx));
+ }
+ else if (strcmp (name, "_DYNAMIC") == 0)
+ /* Check that address and size match the dynamic section.
+ We locate the dynamic section via the program header
+ entry. */
+ for (unsigned int pcnt = 0; pcnt < phnum; ++pcnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem);
+
+ if (phdr != NULL && phdr->p_type == PT_DYNAMIC)
+ {
+ if (sym->st_value != phdr->p_vaddr)
+ ERROR (gettext ("\
+section [%2d] '%s': _DYNAMIC_ symbol value %#" PRIx64 " does not match dynamic segment address %#" PRIx64 "\n"),
+ idx, section_name (ebl, idx),
+ (uint64_t) sym->st_value,
+ (uint64_t) phdr->p_vaddr);
+
+ if (!gnuld && sym->st_size != phdr->p_memsz)
+ ERROR (gettext ("\
+section [%2d] '%s': _DYNAMIC symbol size %" PRIu64 " does not match dynamic segment size %" PRIu64 "\n"),
+ idx, section_name (ebl, idx),
+ (uint64_t) sym->st_size,
+ (uint64_t) phdr->p_memsz);
+
+ break;
+ }
+ }
+ }
+
+ if (GELF_ST_VISIBILITY (sym->st_other) != STV_DEFAULT
+ && shdr->sh_type == SHT_DYNSYM)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: symbol in dynamic symbol table with non-default visibility\n"),
+ idx, section_name (ebl, idx), cnt);
+ if (! ebl_check_st_other_bits (ebl, sym->st_other))
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %zu: unknown bit set in st_other\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ }
+}
+
+
+static bool
+is_rel_dyn (Ebl *ebl, const GElf_Ehdr *ehdr, int idx, const GElf_Shdr *shdr,
+ bool is_rela)
+{
+ /* If this is no executable or DSO it cannot be a .rel.dyn section. */
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ return false;
+
+ /* Check the section name. Unfortunately necessary. */
+ if (strcmp (section_name (ebl, idx), is_rela ? ".rela.dyn" : ".rel.dyn"))
+ return false;
+
+ /* When a .rel.dyn section is used a DT_RELCOUNT dynamic section
+ entry can be present as well. */
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr rcshdr_mem;
+ const GElf_Shdr *rcshdr = gelf_getshdr (scn, &rcshdr_mem);
+
+ if (rcshdr == NULL)
+ break;
+
+ if (rcshdr->sh_type == SHT_DYNAMIC && rcshdr->sh_entsize != 0)
+ {
+ /* Found the dynamic section. Look through it. */
+ Elf_Data *d = elf_getdata (scn, NULL);
+ size_t cnt;
+
+ if (d == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get section data.\n"),
+ idx, section_name (ebl, idx));
+
+ for (cnt = 1; cnt < rcshdr->sh_size / rcshdr->sh_entsize; ++cnt)
+ {
+ GElf_Dyn dyn_mem;
+ GElf_Dyn *dyn = gelf_getdyn (d, cnt, &dyn_mem);
+
+ if (dyn == NULL)
+ break;
+
+ if (dyn->d_tag == DT_RELCOUNT)
+ {
+ /* Found it. Does the type match. */
+ if (is_rela)
+ ERROR (gettext ("\
+section [%2d] '%s': DT_RELCOUNT used for this RELA section\n"),
+ idx, section_name (ebl, idx));
+ else
+ {
+ /* Does the number specified number of relative
+ relocations exceed the total number of
+ relocations? */
+ if (shdr->sh_entsize != 0
+ && dyn->d_un.d_val > (shdr->sh_size
+ / shdr->sh_entsize))
+ ERROR (gettext ("\
+section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"),
+ idx, section_name (ebl, idx),
+ (int) dyn->d_un.d_val);
+
+ /* Make sure the specified number of relocations are
+ relative. */
+ Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf,
+ idx), NULL);
+ if (reldata != NULL && shdr->sh_entsize != 0)
+ for (size_t inner = 0;
+ inner < shdr->sh_size / shdr->sh_entsize;
+ ++inner)
+ {
+ GElf_Rel rel_mem;
+ GElf_Rel *rel = gelf_getrel (reldata, inner,
+ &rel_mem);
+ if (rel == NULL)
+ /* The problem will be reported elsewhere. */
+ break;
+
+ if (ebl_relative_reloc_p (ebl,
+ GELF_R_TYPE (rel->r_info)))
+ {
+ if (inner >= dyn->d_un.d_val)
+ ERROR (gettext ("\
+section [%2d] '%s': relative relocations after index %d as specified by DT_RELCOUNT\n"),
+ idx, section_name (ebl, idx),
+ (int) dyn->d_un.d_val);
+ }
+ else if (inner < dyn->d_un.d_val)
+ ERROR (gettext ("\
+section [%2d] '%s': non-relative relocation at index %zu; DT_RELCOUNT specified %d relative relocations\n"),
+ idx, section_name (ebl, idx),
+ inner, (int) dyn->d_un.d_val);
+ }
+ }
+ }
+
+ if (dyn->d_tag == DT_RELACOUNT)
+ {
+ /* Found it. Does the type match. */
+ if (!is_rela)
+ ERROR (gettext ("\
+section [%2d] '%s': DT_RELACOUNT used for this REL section\n"),
+ idx, section_name (ebl, idx));
+ else
+ {
+ /* Does the number specified number of relative
+ relocations exceed the total number of
+ relocations? */
+ if (shdr->sh_entsize != 0
+ && dyn->d_un.d_val > shdr->sh_size / shdr->sh_entsize)
+ ERROR (gettext ("\
+section [%2d] '%s': DT_RELCOUNT value %d too high for this section\n"),
+ idx, section_name (ebl, idx),
+ (int) dyn->d_un.d_val);
+
+ /* Make sure the specified number of relocations are
+ relative. */
+ Elf_Data *reldata = elf_getdata (elf_getscn (ebl->elf,
+ idx), NULL);
+ if (reldata != NULL && shdr->sh_entsize != 0)
+ for (size_t inner = 0;
+ inner < shdr->sh_size / shdr->sh_entsize;
+ ++inner)
+ {
+ GElf_Rela rela_mem;
+ GElf_Rela *rela = gelf_getrela (reldata, inner,
+ &rela_mem);
+ if (rela == NULL)
+ /* The problem will be reported elsewhere. */
+ break;
+
+ if (ebl_relative_reloc_p (ebl,
+ GELF_R_TYPE (rela->r_info)))
+ {
+ if (inner >= dyn->d_un.d_val)
+ ERROR (gettext ("\
+section [%2d] '%s': relative relocations after index %d as specified by DT_RELCOUNT\n"),
+ idx, section_name (ebl, idx),
+ (int) dyn->d_un.d_val);
+ }
+ else if (inner < dyn->d_un.d_val)
+ ERROR (gettext ("\
+section [%2d] '%s': non-relative relocation at index %zu; DT_RELCOUNT specified %d relative relocations\n"),
+ idx, section_name (ebl, idx),
+ inner, (int) dyn->d_un.d_val);
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+struct loaded_segment
+{
+ GElf_Addr from;
+ GElf_Addr to;
+ bool read_only;
+ struct loaded_segment *next;
+};
+
+
+/* Check whether binary has text relocation flag set. */
+static bool textrel;
+
+/* Keep track of whether text relocation flag is needed. */
+static bool needed_textrel;
+
+
+static bool
+check_reloc_shdr (Ebl *ebl, const GElf_Ehdr *ehdr, const GElf_Shdr *shdr,
+ int idx, int reltype, GElf_Shdr **destshdrp,
+ GElf_Shdr *destshdr_memp, struct loaded_segment **loadedp)
+{
+ bool reldyn = false;
+
+ /* Check whether the link to the section we relocate is reasonable. */
+ if (shdr->sh_info >= shnum)
+ ERROR (gettext ("section [%2d] '%s': invalid destination section index\n"),
+ idx, section_name (ebl, idx));
+ else if (shdr->sh_info != 0)
+ {
+ *destshdrp = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info),
+ destshdr_memp);
+ if (*destshdrp != NULL)
+ {
+ if(! ebl_check_reloc_target_type (ebl, (*destshdrp)->sh_type))
+ {
+ reldyn = is_rel_dyn (ebl, ehdr, idx, shdr, true);
+ if (!reldyn)
+ ERROR (gettext ("\
+section [%2d] '%s': invalid destination section type\n"),
+ idx, section_name (ebl, idx));
+ else
+ {
+ /* There is no standard, but we require that .rel{,a}.dyn
+ sections have a sh_info value of zero. */
+ if (shdr->sh_info != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': sh_info should be zero\n"),
+ idx, section_name (ebl, idx));
+ }
+ }
+
+ if ((((*destshdrp)->sh_flags & SHF_MERGE) != 0)
+ && ((*destshdrp)->sh_flags & SHF_STRINGS) != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': no relocations for merge-able string sections possible\n"),
+ idx, section_name (ebl, idx));
+ }
+ }
+
+ size_t sh_entsize = gelf_fsize (ebl->elf, reltype, 1, EV_CURRENT);
+ if (shdr->sh_entsize != sh_entsize)
+ ERROR (gettext (reltype == ELF_T_RELA ? "\
+section [%2d] '%s': section entry size does not match ElfXX_Rela\n" : "\
+section [%2d] '%s': section entry size does not match ElfXX_Rel\n"),
+ idx, section_name (ebl, idx));
+
+ /* In preparation of checking whether relocations are text
+ relocations or not we need to determine whether the file is
+ flagged to have text relocation and we need to determine a) what
+ the loaded segments are and b) which are read-only. This will
+ also allow us to determine whether the same reloc section is
+ modifying loaded and not loaded segments. */
+ for (unsigned int i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem);
+ if (phdr == NULL)
+ continue;
+
+ if (phdr->p_type == PT_LOAD)
+ {
+ struct loaded_segment *newp = xmalloc (sizeof (*newp));
+ newp->from = phdr->p_vaddr;
+ newp->to = phdr->p_vaddr + phdr->p_memsz;
+ newp->read_only = (phdr->p_flags & PF_W) == 0;
+ newp->next = *loadedp;
+ *loadedp = newp;
+ }
+ else if (phdr->p_type == PT_DYNAMIC)
+ {
+ Elf_Scn *dynscn = gelf_offscn (ebl->elf, phdr->p_offset);
+ GElf_Shdr dynshdr_mem;
+ GElf_Shdr *dynshdr = gelf_getshdr (dynscn, &dynshdr_mem);
+ Elf_Data *dyndata = elf_getdata (dynscn, NULL);
+ if (dynshdr != NULL && dynshdr->sh_type == SHT_DYNAMIC
+ && dyndata != NULL && dynshdr->sh_entsize != 0)
+ for (size_t j = 0; j < dynshdr->sh_size / dynshdr->sh_entsize; ++j)
+ {
+ GElf_Dyn dyn_mem;
+ GElf_Dyn *dyn = gelf_getdyn (dyndata, j, &dyn_mem);
+ if (dyn != NULL
+ && (dyn->d_tag == DT_TEXTREL
+ || (dyn->d_tag == DT_FLAGS
+ && (dyn->d_un.d_val & DF_TEXTREL) != 0)))
+ {
+ textrel = true;
+ break;
+ }
+ }
+ }
+ }
+
+ /* A quick test which can be easily done here (although it is a bit
+ out of place): the text relocation flag makes only sense if there
+ is a segment which is not writable. */
+ if (textrel)
+ {
+ struct loaded_segment *seg = *loadedp;
+ while (seg != NULL && !seg->read_only)
+ seg = seg->next;
+ if (seg == NULL)
+ ERROR (gettext ("\
+text relocation flag set but there is no read-only segment\n"));
+ }
+
+ return reldyn;
+}
+
+
+enum load_state
+ {
+ state_undecided,
+ state_loaded,
+ state_unloaded,
+ state_error
+ };
+
+
+static void
+check_one_reloc (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *relshdr, int idx,
+ size_t cnt, const GElf_Shdr *symshdr, Elf_Data *symdata,
+ GElf_Addr r_offset, GElf_Xword r_info,
+ const GElf_Shdr *destshdr, bool reldyn,
+ struct loaded_segment *loaded, enum load_state *statep)
+{
+ bool known_broken = gnuld;
+
+ if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (r_info)))
+ ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"),
+ idx, section_name (ebl, idx), cnt);
+ else if (((ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ /* The executable/DSO can contain relocation sections with
+ all the relocations the linker has applied. Those sections
+ are marked non-loaded, though. */
+ || (relshdr->sh_flags & SHF_ALLOC) != 0)
+ && !ebl_reloc_valid_use (ebl, GELF_R_TYPE (r_info)))
+ ERROR (gettext ("\
+section [%2d] '%s': relocation %zu: relocation type invalid for the file type\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ if (symshdr != NULL
+ && ((GELF_R_SYM (r_info) + 1)
+ * gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT)
+ > symshdr->sh_size))
+ ERROR (gettext ("\
+section [%2d] '%s': relocation %zu: invalid symbol index\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ /* No more tests if this is a no-op relocation. */
+ if (ebl_none_reloc_p (ebl, GELF_R_TYPE (r_info)))
+ return;
+
+ if (ebl_gotpc_reloc_check (ebl, GELF_R_TYPE (r_info)))
+ {
+ const char *name;
+ char buf[64];
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (r_info), &sym_mem);
+ if (sym != NULL
+ /* Get the name for the symbol. */
+ && (name = elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name))
+ && strcmp (name, "_GLOBAL_OFFSET_TABLE_") !=0 )
+ ERROR (gettext ("\
+section [%2d] '%s': relocation %zu: only symbol '_GLOBAL_OFFSET_TABLE_' can be used with %s\n"),
+ idx, section_name (ebl, idx), cnt,
+ ebl_reloc_type_name (ebl, GELF_R_SYM (r_info),
+ buf, sizeof (buf)));
+ }
+
+ if (reldyn)
+ {
+ // XXX TODO Check .rel.dyn section addresses.
+ }
+ else if (!known_broken)
+ {
+ if (destshdr != NULL
+ && GELF_R_TYPE (r_info) != 0
+ && (r_offset - (ehdr->e_type == ET_REL ? 0
+ : destshdr->sh_addr)) >= destshdr->sh_size)
+ ERROR (gettext ("\
+section [%2d] '%s': relocation %zu: offset out of bounds\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symdata, GELF_R_SYM (r_info), &sym_mem);
+
+ if (ebl_copy_reloc_p (ebl, GELF_R_TYPE (r_info))
+ /* Make sure the referenced symbol is an object or unspecified. */
+ && sym != NULL
+ && GELF_ST_TYPE (sym->st_info) != STT_NOTYPE
+ && GELF_ST_TYPE (sym->st_info) != STT_OBJECT)
+ {
+ char buf[64];
+ ERROR (gettext ("section [%2d] '%s': relocation %zu: copy relocation against symbol of type %s\n"),
+ idx, section_name (ebl, idx), cnt,
+ ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info),
+ buf, sizeof (buf)));
+ }
+
+ if ((ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ || (relshdr->sh_flags & SHF_ALLOC) != 0)
+ {
+ bool in_loaded_seg = false;
+ while (loaded != NULL)
+ {
+ if (r_offset < loaded->to
+ && r_offset + (sym == NULL ? 0 : sym->st_size) >= loaded->from)
+ {
+ /* The symbol is in this segment. */
+ if (loaded->read_only)
+ {
+ if (textrel)
+ needed_textrel = true;
+ else
+ ERROR (gettext ("section [%2d] '%s': relocation %zu: read-only section modified but text relocation flag not set\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+
+ in_loaded_seg = true;
+ }
+
+ loaded = loaded->next;
+ }
+
+ if (*statep == state_undecided)
+ *statep = in_loaded_seg ? state_loaded : state_unloaded;
+ else if ((*statep == state_unloaded && in_loaded_seg)
+ || (*statep == state_loaded && !in_loaded_seg))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': relocations are against loaded and unloaded data\n"),
+ idx, section_name (ebl, idx));
+ *statep = state_error;
+ }
+ }
+}
+
+
+static void
+check_rela (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ /* Check the fields of the section header. */
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr = NULL;
+ struct loaded_segment *loaded = NULL;
+ bool reldyn = check_reloc_shdr (ebl, ehdr, shdr, idx, ELF_T_RELA, &destshdr,
+ &destshdr_mem, &loaded);
+
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ enum load_state state = state_undecided;
+
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_RELA, 1, EV_CURRENT);
+ for (size_t cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt)
+ {
+ GElf_Rela rela_mem;
+ GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
+ if (rela == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get relocation %zu: %s\n"),
+ idx, section_name (ebl, idx), cnt, elf_errmsg (-1));
+ continue;
+ }
+
+ check_one_reloc (ebl, ehdr, shdr, idx, cnt, symshdr, symdata,
+ rela->r_offset, rela->r_info, destshdr, reldyn, loaded,
+ &state);
+ }
+
+ while (loaded != NULL)
+ {
+ struct loaded_segment *old = loaded;
+ loaded = loaded->next;
+ free (old);
+ }
+}
+
+
+static void
+check_rel (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ /* Check the fields of the section header. */
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr = NULL;
+ struct loaded_segment *loaded = NULL;
+ bool reldyn = check_reloc_shdr (ebl, ehdr, shdr, idx, ELF_T_REL, &destshdr,
+ &destshdr_mem, &loaded);
+
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ enum load_state state = state_undecided;
+
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_REL, 1, EV_CURRENT);
+ for (size_t cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt)
+ {
+ GElf_Rel rel_mem;
+ GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
+ if (rel == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get relocation %zu: %s\n"),
+ idx, section_name (ebl, idx), cnt, elf_errmsg (-1));
+ continue;
+ }
+
+ check_one_reloc (ebl, ehdr, shdr, idx, cnt, symshdr, symdata,
+ rel->r_offset, rel->r_info, destshdr, reldyn, loaded,
+ &state);
+ }
+
+ while (loaded != NULL)
+ {
+ struct loaded_segment *old = loaded;
+ loaded = loaded->next;
+ free (old);
+ }
+}
+
+
+/* Number of dynamic sections. */
+static int ndynamic;
+
+
+static void
+check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ Elf_Data *data;
+ GElf_Shdr strshdr_mem;
+ GElf_Shdr *strshdr;
+ size_t cnt;
+ static const bool dependencies[DT_NUM][DT_NUM] =
+ {
+ [DT_NEEDED] = { [DT_STRTAB] = true },
+ [DT_PLTRELSZ] = { [DT_JMPREL] = true },
+ [DT_HASH] = { [DT_SYMTAB] = true },
+ [DT_STRTAB] = { [DT_STRSZ] = true },
+ [DT_SYMTAB] = { [DT_STRTAB] = true, [DT_SYMENT] = true },
+ [DT_RELA] = { [DT_RELASZ] = true, [DT_RELAENT] = true },
+ [DT_RELASZ] = { [DT_RELA] = true },
+ [DT_RELAENT] = { [DT_RELA] = true },
+ [DT_STRSZ] = { [DT_STRTAB] = true },
+ [DT_SYMENT] = { [DT_SYMTAB] = true },
+ [DT_SONAME] = { [DT_STRTAB] = true },
+ [DT_RPATH] = { [DT_STRTAB] = true },
+ [DT_REL] = { [DT_RELSZ] = true, [DT_RELENT] = true },
+ [DT_RELSZ] = { [DT_REL] = true },
+ [DT_RELENT] = { [DT_REL] = true },
+ [DT_JMPREL] = { [DT_PLTRELSZ] = true, [DT_PLTREL] = true },
+ [DT_RUNPATH] = { [DT_STRTAB] = true },
+ [DT_PLTREL] = { [DT_JMPREL] = true },
+ };
+ bool has_dt[DT_NUM];
+ bool has_val_dt[DT_VALNUM];
+ bool has_addr_dt[DT_ADDRNUM];
+ static const bool level2[DT_NUM] =
+ {
+ [DT_RPATH] = true,
+ [DT_SYMBOLIC] = true,
+ [DT_TEXTREL] = true,
+ [DT_BIND_NOW] = true
+ };
+ static const bool mandatory[DT_NUM] =
+ {
+ [DT_NULL] = true,
+ [DT_STRTAB] = true,
+ [DT_SYMTAB] = true,
+ [DT_STRSZ] = true,
+ [DT_SYMENT] = true
+ };
+
+ memset (has_dt, '\0', sizeof (has_dt));
+ memset (has_val_dt, '\0', sizeof (has_val_dt));
+ memset (has_addr_dt, '\0', sizeof (has_addr_dt));
+
+ if (++ndynamic == 2)
+ ERROR (gettext ("more than one dynamic section present\n"));
+
+ data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &strshdr_mem);
+ if (strshdr != NULL && strshdr->sh_type != SHT_STRTAB)
+ ERROR (gettext ("\
+section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"),
+ shdr->sh_link, section_name (ebl, shdr->sh_link),
+ idx, section_name (ebl, idx));
+ else if (strshdr == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d]: referenced as string table for section [%2d] '%s' but section link value is invalid\n"),
+ shdr->sh_link, idx, section_name (ebl, idx));
+ return;
+ }
+
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_DYN, 1, EV_CURRENT);
+ if (shdr->sh_entsize != sh_entsize)
+ ERROR (gettext ("\
+section [%2d] '%s': section entry size does not match ElfXX_Dyn\n"),
+ idx, section_name (ebl, idx));
+
+ if (shdr->sh_info != 0)
+ ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"),
+ idx, section_name (ebl, idx));
+
+ bool non_null_warned = false;
+ for (cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt)
+ {
+ GElf_Dyn dyn_mem;
+ GElf_Dyn *dyn = gelf_getdyn (data, cnt, &dyn_mem);
+ if (dyn == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get dynamic section entry %zu: %s\n"),
+ idx, section_name (ebl, idx), cnt, elf_errmsg (-1));
+ continue;
+ }
+
+ if (has_dt[DT_NULL] && dyn->d_tag != DT_NULL && ! non_null_warned)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': non-DT_NULL entries follow DT_NULL entry\n"),
+ idx, section_name (ebl, idx));
+ non_null_warned = true;
+ }
+
+ if (!ebl_dynamic_tag_check (ebl, dyn->d_tag))
+ ERROR (gettext ("section [%2d] '%s': entry %zu: unknown tag\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ if (dyn->d_tag >= 0 && dyn->d_tag < DT_NUM)
+ {
+ if (has_dt[dyn->d_tag]
+ && dyn->d_tag != DT_NEEDED
+ && dyn->d_tag != DT_NULL
+ && dyn->d_tag != DT_POSFLAG_1)
+ {
+ char buf[50];
+ ERROR (gettext ("\
+section [%2d] '%s': entry %zu: more than one entry with tag %s\n"),
+ idx, section_name (ebl, idx), cnt,
+ ebl_dynamic_tag_name (ebl, dyn->d_tag,
+ buf, sizeof (buf)));
+ }
+
+ if (be_strict && level2[dyn->d_tag])
+ {
+ char buf[50];
+ ERROR (gettext ("\
+section [%2d] '%s': entry %zu: level 2 tag %s used\n"),
+ idx, section_name (ebl, idx), cnt,
+ ebl_dynamic_tag_name (ebl, dyn->d_tag,
+ buf, sizeof (buf)));
+ }
+
+ has_dt[dyn->d_tag] = true;
+ }
+ else if (dyn->d_tag >= 0 && dyn->d_tag <= DT_VALRNGHI
+ && DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM)
+ has_val_dt[DT_VALTAGIDX (dyn->d_tag)] = true;
+ else if (dyn->d_tag >= 0 && dyn->d_tag <= DT_ADDRRNGHI
+ && DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM)
+ has_addr_dt[DT_ADDRTAGIDX (dyn->d_tag)] = true;
+
+ if (dyn->d_tag == DT_PLTREL && dyn->d_un.d_val != DT_REL
+ && dyn->d_un.d_val != DT_RELA)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %zu: DT_PLTREL value must be DT_REL or DT_RELA\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ /* Check that addresses for entries are in loaded segments. */
+ switch (dyn->d_tag)
+ {
+ size_t n;
+ case DT_STRTAB:
+ /* We require the referenced section is the same as the one
+ specified in sh_link. */
+ if (strshdr->sh_addr != dyn->d_un.d_val)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %zu: pointer does not match address of section [%2d] '%s' referenced by sh_link\n"),
+ idx, section_name (ebl, idx), cnt,
+ shdr->sh_link, section_name (ebl, shdr->sh_link));
+ break;
+ }
+ goto check_addr;
+
+ default:
+ if (dyn->d_tag < DT_ADDRRNGLO || dyn->d_tag > DT_ADDRRNGHI)
+ /* Value is no pointer. */
+ break;
+ FALLTHROUGH;
+
+ case DT_AUXILIARY:
+ case DT_FILTER:
+ case DT_FINI:
+ case DT_FINI_ARRAY:
+ case DT_HASH:
+ case DT_INIT:
+ case DT_INIT_ARRAY:
+ case DT_JMPREL:
+ case DT_PLTGOT:
+ case DT_REL:
+ case DT_RELA:
+ case DT_SYMBOLIC:
+ case DT_SYMTAB:
+ case DT_VERDEF:
+ case DT_VERNEED:
+ case DT_VERSYM:
+ check_addr:
+ for (n = 0; n < phnum; ++n)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, n, &phdr_mem);
+ if (phdr != NULL && phdr->p_type == PT_LOAD
+ && phdr->p_vaddr <= dyn->d_un.d_ptr
+ && phdr->p_vaddr + phdr->p_memsz > dyn->d_un.d_ptr)
+ break;
+ }
+ if (unlikely (n >= phnum))
+ {
+ char buf[50];
+ ERROR (gettext ("\
+section [%2d] '%s': entry %zu: %s value must point into loaded segment\n"),
+ idx, section_name (ebl, idx), cnt,
+ ebl_dynamic_tag_name (ebl, dyn->d_tag, buf,
+ sizeof (buf)));
+ }
+ break;
+
+ case DT_NEEDED:
+ case DT_RPATH:
+ case DT_RUNPATH:
+ case DT_SONAME:
+ if (dyn->d_un.d_ptr >= strshdr->sh_size)
+ {
+ char buf[50];
+ ERROR (gettext ("\
+section [%2d] '%s': entry %zu: %s value must be valid offset in section [%2d] '%s'\n"),
+ idx, section_name (ebl, idx), cnt,
+ ebl_dynamic_tag_name (ebl, dyn->d_tag, buf,
+ sizeof (buf)),
+ shdr->sh_link, section_name (ebl, shdr->sh_link));
+ }
+ break;
+ }
+ }
+
+ for (cnt = 1; cnt < DT_NUM; ++cnt)
+ if (has_dt[cnt])
+ {
+ for (int inner = 0; inner < DT_NUM; ++inner)
+ if (dependencies[cnt][inner] && ! has_dt[inner])
+ {
+ char buf1[50];
+ char buf2[50];
+
+ ERROR (gettext ("\
+section [%2d] '%s': contains %s entry but not %s\n"),
+ idx, section_name (ebl, idx),
+ ebl_dynamic_tag_name (ebl, cnt, buf1, sizeof (buf1)),
+ ebl_dynamic_tag_name (ebl, inner, buf2, sizeof (buf2)));
+ }
+ }
+ else
+ {
+ if (mandatory[cnt])
+ {
+ char buf[50];
+ ERROR (gettext ("\
+section [%2d] '%s': mandatory tag %s not present\n"),
+ idx, section_name (ebl, idx),
+ ebl_dynamic_tag_name (ebl, cnt, buf, sizeof (buf)));
+ }
+ }
+
+ /* Make sure we have an hash table. */
+ if (!has_dt[DT_HASH] && !has_addr_dt[DT_ADDRTAGIDX (DT_GNU_HASH)])
+ ERROR (gettext ("\
+section [%2d] '%s': no hash section present\n"),
+ idx, section_name (ebl, idx));
+
+ /* The GNU-style hash table also needs a symbol table. */
+ if (!has_dt[DT_HASH] && has_addr_dt[DT_ADDRTAGIDX (DT_GNU_HASH)]
+ && !has_dt[DT_SYMTAB])
+ ERROR (gettext ("\
+section [%2d] '%s': contains %s entry but not %s\n"),
+ idx, section_name (ebl, idx),
+ "DT_GNU_HASH", "DT_SYMTAB");
+
+ /* Check the rel/rela tags. At least one group must be available. */
+ if ((has_dt[DT_RELA] || has_dt[DT_RELASZ] || has_dt[DT_RELAENT])
+ && (!has_dt[DT_RELA] || !has_dt[DT_RELASZ] || !has_dt[DT_RELAENT]))
+ ERROR (gettext ("\
+section [%2d] '%s': not all of %s, %s, and %s are present\n"),
+ idx, section_name (ebl, idx),
+ "DT_RELA", "DT_RELASZ", "DT_RELAENT");
+
+ if ((has_dt[DT_REL] || has_dt[DT_RELSZ] || has_dt[DT_RELENT])
+ && (!has_dt[DT_REL] || !has_dt[DT_RELSZ] || !has_dt[DT_RELENT]))
+ ERROR (gettext ("\
+section [%2d] '%s': not all of %s, %s, and %s are present\n"),
+ idx, section_name (ebl, idx),
+ "DT_REL", "DT_RELSZ", "DT_RELENT");
+
+ /* Check that all prelink sections are present if any of them is. */
+ if (has_val_dt[DT_VALTAGIDX (DT_GNU_PRELINKED)]
+ || has_val_dt[DT_VALTAGIDX (DT_CHECKSUM)])
+ {
+ if (!has_val_dt[DT_VALTAGIDX (DT_GNU_PRELINKED)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in DSO marked during prelinking\n"),
+ idx, section_name (ebl, idx), "DT_GNU_PRELINKED");
+ if (!has_val_dt[DT_VALTAGIDX (DT_CHECKSUM)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in DSO marked during prelinking\n"),
+ idx, section_name (ebl, idx), "DT_CHECKSUM");
+
+ /* Only DSOs can be marked like this. */
+ if (ehdr->e_type != ET_DYN)
+ ERROR (gettext ("\
+section [%2d] '%s': non-DSO file marked as dependency during prelink\n"),
+ idx, section_name (ebl, idx));
+ }
+
+ if (has_val_dt[DT_VALTAGIDX (DT_GNU_CONFLICTSZ)]
+ || has_val_dt[DT_VALTAGIDX (DT_GNU_LIBLISTSZ)]
+ || has_addr_dt[DT_ADDRTAGIDX (DT_GNU_CONFLICT)]
+ || has_addr_dt[DT_ADDRTAGIDX (DT_GNU_LIBLIST)])
+ {
+ if (!has_val_dt[DT_VALTAGIDX (DT_GNU_CONFLICTSZ)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in prelinked executable\n"),
+ idx, section_name (ebl, idx), "DT_GNU_CONFLICTSZ");
+ if (!has_val_dt[DT_VALTAGIDX (DT_GNU_LIBLISTSZ)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in prelinked executable\n"),
+ idx, section_name (ebl, idx), "DT_GNU_LIBLISTSZ");
+ if (!has_addr_dt[DT_ADDRTAGIDX (DT_GNU_CONFLICT)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in prelinked executable\n"),
+ idx, section_name (ebl, idx), "DT_GNU_CONFLICT");
+ if (!has_addr_dt[DT_ADDRTAGIDX (DT_GNU_LIBLIST)])
+ ERROR (gettext ("\
+section [%2d] '%s': %s tag missing in prelinked executable\n"),
+ idx, section_name (ebl, idx), "DT_GNU_LIBLIST");
+ }
+}
+
+
+static void
+check_symtab_shndx (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ if (ehdr->e_type != ET_REL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': only relocatable files can have extended section index\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ if (symshdr != NULL && symshdr->sh_type != SHT_SYMTAB)
+ ERROR (gettext ("\
+section [%2d] '%s': extended section index section not for symbol table\n"),
+ idx, section_name (ebl, idx));
+ else if (symshdr == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': sh_link extended section index [%2d] is invalid\n"),
+ idx, section_name (ebl, idx), shdr->sh_link);
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ if (symdata == NULL)
+ ERROR (gettext ("cannot get data for symbol section\n"));
+
+ if (shdr->sh_entsize != sizeof (Elf32_Word))
+ ERROR (gettext ("\
+section [%2d] '%s': entry size does not match Elf32_Word\n"),
+ idx, section_name (ebl, idx));
+
+ if (symshdr != NULL
+ && shdr->sh_entsize != 0
+ && symshdr->sh_entsize != 0
+ && (shdr->sh_size / shdr->sh_entsize
+ < symshdr->sh_size / symshdr->sh_entsize))
+ ERROR (gettext ("\
+section [%2d] '%s': extended index table too small for symbol table\n"),
+ idx, section_name (ebl, idx));
+
+ if (shdr->sh_info != 0)
+ ERROR (gettext ("section [%2d] '%s': sh_info not zero\n"),
+ idx, section_name (ebl, idx));
+
+ for (size_t cnt = idx + 1; cnt < shnum; ++cnt)
+ {
+ GElf_Shdr rshdr_mem;
+ GElf_Shdr *rshdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &rshdr_mem);
+ if (rshdr != NULL && rshdr->sh_type == SHT_SYMTAB_SHNDX
+ && rshdr->sh_link == shdr->sh_link)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': extended section index in section [%2zu] '%s' refers to same symbol table\n"),
+ idx, section_name (ebl, idx),
+ cnt, section_name (ebl, cnt));
+ break;
+ }
+ }
+
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL || data->d_buf == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ if (data->d_size < sizeof (Elf32_Word)
+ || *((Elf32_Word *) data->d_buf) != 0)
+ ERROR (gettext ("symbol 0 should have zero extended section index\n"));
+
+ for (size_t cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt)
+ {
+ Elf32_Word xndx = ((Elf32_Word *) data->d_buf)[cnt];
+
+ if (xndx != 0)
+ {
+ GElf_Sym sym_data;
+ GElf_Sym *sym = gelf_getsym (symdata, cnt, &sym_data);
+ if (sym == NULL)
+ {
+ ERROR (gettext ("cannot get data for symbol %zu\n"), cnt);
+ continue;
+ }
+
+ if (sym->st_shndx != SHN_XINDEX)
+ ERROR (gettext ("\
+extended section index is %" PRIu32 " but symbol index is not XINDEX\n"),
+ (uint32_t) xndx);
+ }
+ }
+}
+
+
+static void
+check_sysv_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx,
+ GElf_Shdr *symshdr)
+{
+ Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
+ Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1];
+
+ if (shdr->sh_size < (2 + nbucket + nchain) * sizeof (Elf32_Word))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"),
+ idx, section_name (ebl, idx), (long int) shdr->sh_size,
+ (long int) ((2 + nbucket + nchain) * sizeof (Elf32_Word)));
+ return;
+ }
+
+ size_t maxidx = nchain;
+
+ if (symshdr != NULL && symshdr->sh_entsize != 0)
+ {
+ size_t symsize = symshdr->sh_size / symshdr->sh_entsize;
+
+ if (nchain > symshdr->sh_size / symshdr->sh_entsize)
+ ERROR (gettext ("section [%2d] '%s': chain array too large\n"),
+ idx, section_name (ebl, idx));
+
+ maxidx = symsize;
+ }
+
+ Elf32_Word *buf = (Elf32_Word *) data->d_buf;
+ Elf32_Word *end = (Elf32_Word *) ((char *) data->d_buf + shdr->sh_size);
+ size_t cnt;
+ for (cnt = 2; cnt < 2 + nbucket; ++cnt)
+ {
+ if (buf + cnt >= end)
+ break;
+ else if (buf[cnt] >= maxidx)
+ ERROR (gettext ("\
+section [%2d] '%s': hash bucket reference %zu out of bounds\n"),
+ idx, section_name (ebl, idx), cnt - 2);
+ }
+
+ for (; cnt < 2 + nbucket + nchain; ++cnt)
+ {
+ if (buf + cnt >= end)
+ break;
+ else if (buf[cnt] >= maxidx)
+ ERROR (gettext ("\
+section [%2d] '%s': hash chain reference %zu out of bounds\n"),
+ idx, section_name (ebl, idx), cnt - 2 - nbucket);
+ }
+}
+
+
+static void
+check_sysv_hash64 (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx,
+ GElf_Shdr *symshdr)
+{
+ Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0];
+ Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1];
+
+ if (shdr->sh_size < (2 + nbucket + nchain) * sizeof (Elf64_Xword))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': hash table section is too small (is %ld, expected %ld)\n"),
+ idx, section_name (ebl, idx), (long int) shdr->sh_size,
+ (long int) ((2 + nbucket + nchain) * sizeof (Elf64_Xword)));
+ return;
+ }
+
+ size_t maxidx = nchain;
+
+ if (symshdr != NULL && symshdr->sh_entsize != 0)
+ {
+ size_t symsize = symshdr->sh_size / symshdr->sh_entsize;
+
+ if (nchain > symshdr->sh_size / symshdr->sh_entsize)
+ ERROR (gettext ("section [%2d] '%s': chain array too large\n"),
+ idx, section_name (ebl, idx));
+
+ maxidx = symsize;
+ }
+
+ Elf64_Xword *buf = (Elf64_Xword *) data->d_buf;
+ Elf64_Xword *end = (Elf64_Xword *) ((char *) data->d_buf + shdr->sh_size);
+ size_t cnt;
+ for (cnt = 2; cnt < 2 + nbucket; ++cnt)
+ {
+ if (buf + cnt >= end)
+ break;
+ else if (buf[cnt] >= maxidx)
+ ERROR (gettext ("\
+section [%2d] '%s': hash bucket reference %zu out of bounds\n"),
+ idx, section_name (ebl, idx), cnt - 2);
+ }
+
+ for (; cnt < 2 + nbucket + nchain; ++cnt)
+ {
+ if (buf + cnt >= end)
+ break;
+ else if (buf[cnt] >= maxidx)
+ ERROR (gettext ("\
+section [%2d] '%s': hash chain reference %" PRIu64 " out of bounds\n"),
+ idx, section_name (ebl, idx), (uint64_t) cnt - 2 - nbucket);
+ }
+}
+
+
+static void
+check_gnu_hash (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data, int idx,
+ GElf_Shdr *symshdr)
+{
+ if (data->d_size < 4 * sizeof (Elf32_Word))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': not enough data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ Elf32_Word nbuckets = ((Elf32_Word *) data->d_buf)[0];
+ Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1];
+ Elf32_Word bitmask_words = ((Elf32_Word *) data->d_buf)[2];
+
+ if (bitmask_words == 0 || !powerof2 (bitmask_words))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': bitmask size zero or not power of 2: %u\n"),
+ idx, section_name (ebl, idx), bitmask_words);
+ return;
+ }
+
+ size_t bitmask_idxmask = bitmask_words - 1;
+ if (gelf_getclass (ebl->elf) == ELFCLASS64)
+ bitmask_words *= 2;
+ Elf32_Word shift = ((Elf32_Word *) data->d_buf)[3];
+
+ /* Is there still room for the sym chain?
+ Use uint64_t calculation to prevent 32bit overlow. */
+ uint64_t used_buf = (4ULL + bitmask_words + nbuckets) * sizeof (Elf32_Word);
+ if (used_buf > data->d_size)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': hash table section is too small (is %ld, expected at least %ld)\n"),
+ idx, section_name (ebl, idx), (long int) shdr->sh_size,
+ (long int) used_buf);
+ return;
+ }
+
+ if (shift > 31)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': 2nd hash function shift too big: %u\n"),
+ idx, section_name (ebl, idx), shift);
+ return;
+ }
+
+ size_t maxidx = shdr->sh_size / sizeof (Elf32_Word) - (4 + bitmask_words
+ + nbuckets);
+
+ if (symshdr != NULL && symshdr->sh_entsize != 0)
+ maxidx = MIN (maxidx, symshdr->sh_size / symshdr->sh_entsize);
+
+ /* We need the symbol section data. */
+ Elf_Data *symdata = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link), NULL);
+
+ union
+ {
+ Elf32_Word *p32;
+ Elf64_Xword *p64;
+ } bitmask = { .p32 = &((Elf32_Word *) data->d_buf)[4] },
+ collected = { .p32 = xcalloc (bitmask_words, sizeof (Elf32_Word)) };
+
+ size_t classbits = gelf_getclass (ebl->elf) == ELFCLASS32 ? 32 : 64;
+
+ size_t cnt;
+ for (cnt = 4 + bitmask_words; cnt < 4 + bitmask_words + nbuckets; ++cnt)
+ {
+ Elf32_Word symidx = ((Elf32_Word *) data->d_buf)[cnt];
+
+ if (symidx == 0)
+ continue;
+
+ if (symidx < symbias)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': hash chain for bucket %zu lower than symbol index bias\n"),
+ idx, section_name (ebl, idx), cnt - (4 + bitmask_words));
+ continue;
+ }
+
+ while (symidx - symbias < maxidx)
+ {
+ Elf32_Word chainhash = ((Elf32_Word *) data->d_buf)[4
+ + bitmask_words
+ + nbuckets
+ + symidx
+ - symbias];
+
+ if (symdata != NULL)
+ {
+ /* Check that the referenced symbol is not undefined. */
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symdata, symidx, &sym_mem);
+ if (sym != NULL && sym->st_shndx == SHN_UNDEF
+ && GELF_ST_TYPE (sym->st_info) != STT_FUNC)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %u referenced in chain for bucket %zu is undefined\n"),
+ idx, section_name (ebl, idx), symidx,
+ cnt - (4 + bitmask_words));
+
+ const char *symname = (sym != NULL
+ ? elf_strptr (ebl->elf, symshdr->sh_link,
+ sym->st_name)
+ : NULL);
+ if (symname != NULL)
+ {
+ Elf32_Word hval = elf_gnu_hash (symname);
+ if ((hval & ~1u) != (chainhash & ~1u))
+ ERROR (gettext ("\
+section [%2d] '%s': hash value for symbol %u in chain for bucket %zu wrong\n"),
+ idx, section_name (ebl, idx), symidx,
+ cnt - (4 + bitmask_words));
+
+ /* Set the bits in the bitmask. */
+ size_t maskidx = (hval / classbits) & bitmask_idxmask;
+ if (maskidx >= bitmask_words)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': mask index for symbol %u in chain for bucket %zu wrong\n"),
+ idx, section_name (ebl, idx), symidx,
+ cnt - (4 + bitmask_words));
+ return;
+ }
+ if (classbits == 32)
+ {
+ collected.p32[maskidx]
+ |= UINT32_C (1) << (hval & (classbits - 1));
+ collected.p32[maskidx]
+ |= UINT32_C (1) << ((hval >> shift) & (classbits - 1));
+ }
+ else
+ {
+ collected.p64[maskidx]
+ |= UINT64_C (1) << (hval & (classbits - 1));
+ collected.p64[maskidx]
+ |= UINT64_C (1) << ((hval >> shift) & (classbits - 1));
+ }
+ }
+ }
+
+ if ((chainhash & 1) != 0)
+ break;
+
+ ++symidx;
+ }
+
+ if (symidx - symbias >= maxidx)
+ ERROR (gettext ("\
+section [%2d] '%s': hash chain for bucket %zu out of bounds\n"),
+ idx, section_name (ebl, idx), cnt - (4 + bitmask_words));
+ else if (symshdr != NULL && symshdr->sh_entsize != 0
+ && symidx > symshdr->sh_size / symshdr->sh_entsize)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol reference in chain for bucket %zu out of bounds\n"),
+ idx, section_name (ebl, idx), cnt - (4 + bitmask_words));
+ }
+
+ if (memcmp (collected.p32, bitmask.p32, bitmask_words * sizeof (Elf32_Word)))
+ ERROR (gettext ("\
+section [%2d] '%s': bitmask does not match names in the hash table\n"),
+ idx, section_name (ebl, idx));
+
+ free (collected.p32);
+}
+
+
+static void
+check_hash (int tag, Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ if (ehdr->e_type == ET_REL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': relocatable files cannot have hash tables\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL || data->d_buf == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &symshdr_mem);
+ if (symshdr != NULL && symshdr->sh_type != SHT_DYNSYM)
+ ERROR (gettext ("\
+section [%2d] '%s': hash table not for dynamic symbol table\n"),
+ idx, section_name (ebl, idx));
+ else if (symshdr == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': invalid sh_link symbol table section index [%2d]\n"),
+ idx, section_name (ebl, idx), shdr->sh_link);
+
+ size_t expect_entsize = (tag == SHT_GNU_HASH
+ ? (gelf_getclass (ebl->elf) == ELFCLASS32
+ ? sizeof (Elf32_Word) : 0)
+ : (size_t) ebl_sysvhash_entrysize (ebl));
+
+ if (shdr->sh_entsize != expect_entsize)
+ ERROR (gettext ("\
+section [%2d] '%s': hash table entry size incorrect\n"),
+ idx, section_name (ebl, idx));
+
+ if ((shdr->sh_flags & SHF_ALLOC) == 0)
+ ERROR (gettext ("section [%2d] '%s': not marked to be allocated\n"),
+ idx, section_name (ebl, idx));
+
+ if (shdr->sh_size < (tag == SHT_GNU_HASH ? 4 : 2) * (expect_entsize ?: 4))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': hash table has not even room for initial administrative entries\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ switch (tag)
+ {
+ case SHT_HASH:
+ if (ebl_sysvhash_entrysize (ebl) == sizeof (Elf64_Xword))
+ check_sysv_hash64 (ebl, shdr, data, idx, symshdr);
+ else
+ check_sysv_hash (ebl, shdr, data, idx, symshdr);
+ break;
+
+ case SHT_GNU_HASH:
+ check_gnu_hash (ebl, shdr, data, idx, symshdr);
+ break;
+
+ default:
+ assert (! "should not happen");
+ }
+}
+
+
+/* Compare content of both hash tables, it must be identical. */
+static void
+compare_hash_gnu_hash (Ebl *ebl, GElf_Ehdr *ehdr, size_t hash_idx,
+ size_t gnu_hash_idx)
+{
+ Elf_Scn *hash_scn = elf_getscn (ebl->elf, hash_idx);
+ Elf_Data *hash_data = elf_getdata (hash_scn, NULL);
+ GElf_Shdr hash_shdr_mem;
+ GElf_Shdr *hash_shdr = gelf_getshdr (hash_scn, &hash_shdr_mem);
+ Elf_Scn *gnu_hash_scn = elf_getscn (ebl->elf, gnu_hash_idx);
+ Elf_Data *gnu_hash_data = elf_getdata (gnu_hash_scn, NULL);
+ GElf_Shdr gnu_hash_shdr_mem;
+ GElf_Shdr *gnu_hash_shdr = gelf_getshdr (gnu_hash_scn, &gnu_hash_shdr_mem);
+
+ if (hash_shdr == NULL || gnu_hash_shdr == NULL
+ || hash_data == NULL || hash_data->d_buf == NULL
+ || gnu_hash_data == NULL || gnu_hash_data->d_buf == NULL)
+ /* None of these pointers should be NULL since we used the
+ sections already. We are careful nonetheless. */
+ return;
+
+ /* The link must point to the same symbol table. */
+ if (hash_shdr->sh_link != gnu_hash_shdr->sh_link)
+ {
+ ERROR (gettext ("\
+sh_link in hash sections [%2zu] '%s' and [%2zu] '%s' not identical\n"),
+ hash_idx, elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name),
+ gnu_hash_idx,
+ elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name));
+ return;
+ }
+
+ Elf_Scn *sym_scn = elf_getscn (ebl->elf, hash_shdr->sh_link);
+ Elf_Data *sym_data = elf_getdata (sym_scn, NULL);
+ GElf_Shdr sym_shdr_mem;
+ GElf_Shdr *sym_shdr = gelf_getshdr (sym_scn, &sym_shdr_mem);
+
+ if (sym_data == NULL || sym_data->d_buf == NULL
+ || sym_shdr == NULL || sym_shdr->sh_entsize == 0)
+ return;
+
+ const char *hash_name;
+ const char *gnu_hash_name;
+ hash_name = elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name);
+ gnu_hash_name = elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name);
+
+ if (gnu_hash_data->d_size < 4 * sizeof (Elf32_Word))
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' does not contain enough data\n"),
+ gnu_hash_idx, gnu_hash_name);
+ return;
+ }
+
+ uint32_t nentries = sym_shdr->sh_size / sym_shdr->sh_entsize;
+ char *used = alloca (nentries);
+ memset (used, '\0', nentries);
+
+ /* First go over the GNU_HASH table and mark the entries as used. */
+ const Elf32_Word *gnu_hasharr = (Elf32_Word *) gnu_hash_data->d_buf;
+ Elf32_Word gnu_nbucket = gnu_hasharr[0];
+ Elf32_Word gnu_symbias = gnu_hasharr[1];
+ const int bitmap_factor = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 1 : 2;
+ const Elf32_Word *gnu_bucket = (gnu_hasharr
+ + (4 + gnu_hasharr[2] * bitmap_factor));
+ const Elf32_Word *gnu_chain = gnu_bucket + gnu_hasharr[0];
+
+ if (gnu_hasharr[2] == 0)
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' has zero bit mask words\n"),
+ gnu_hash_idx, gnu_hash_name);
+ return;
+ }
+
+ uint64_t used_buf = ((4ULL + gnu_hasharr[2] * bitmap_factor + gnu_nbucket)
+ * sizeof (Elf32_Word));
+ uint32_t max_nsyms = (gnu_hash_data->d_size - used_buf) / sizeof (Elf32_Word);
+ if (used_buf > gnu_hash_data->d_size)
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' uses too much data\n"),
+ gnu_hash_idx, gnu_hash_name);
+ return;
+ }
+
+ for (Elf32_Word cnt = 0; cnt < gnu_nbucket; ++cnt)
+ {
+ if (gnu_bucket[cnt] != STN_UNDEF)
+ {
+ Elf32_Word symidx = gnu_bucket[cnt] - gnu_symbias;
+ do
+ {
+ if (symidx >= max_nsyms || symidx + gnu_symbias >= nentries)
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' invalid symbol index %" PRIu32 " (max_nsyms: %" PRIu32 ", nentries: %" PRIu32 "\n"),
+ gnu_hash_idx, gnu_hash_name, symidx, max_nsyms, nentries);
+ return;
+ }
+ used[symidx + gnu_symbias] |= 1;
+ }
+ while ((gnu_chain[symidx++] & 1u) == 0);
+ }
+ }
+
+ /* Now go over the old hash table and check that we cover the same
+ entries. */
+ if (hash_shdr->sh_entsize == sizeof (Elf32_Word))
+ {
+ const Elf32_Word *hasharr = (Elf32_Word *) hash_data->d_buf;
+ if (hash_data->d_size < 2 * sizeof (Elf32_Word))
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' does not contain enough data\n"),
+ hash_idx, hash_name);
+ return;
+ }
+
+ Elf32_Word nbucket = hasharr[0];
+ Elf32_Word nchain = hasharr[1];
+ uint64_t hash_used = (2ULL + nchain + nbucket) * sizeof (Elf32_Word);
+ if (hash_used > hash_data->d_size)
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' uses too much data\n"),
+ hash_idx, hash_name);
+ return;
+ }
+
+ const Elf32_Word *bucket = &hasharr[2];
+ const Elf32_Word *chain = &hasharr[2 + nbucket];
+
+ for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
+ {
+ Elf32_Word symidx = bucket[cnt];
+ while (symidx != STN_UNDEF && symidx < nentries && symidx < nchain)
+ {
+ used[symidx] |= 2;
+ symidx = chain[symidx];
+ }
+ }
+ }
+ else if (hash_shdr->sh_entsize == sizeof (Elf64_Xword))
+ {
+ const Elf64_Xword *hasharr = (Elf64_Xword *) hash_data->d_buf;
+ if (hash_data->d_size < 2 * sizeof (Elf32_Word))
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' does not contain enough data\n"),
+ hash_idx, hash_name);
+ return;
+ }
+
+ Elf64_Xword nbucket = hasharr[0];
+ Elf64_Xword nchain = hasharr[1];
+ uint64_t maxwords = hash_data->d_size / sizeof (Elf64_Xword);
+ if (maxwords < 2
+ || maxwords - 2 < nbucket
+ || maxwords - 2 - nbucket < nchain)
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' uses too much data\n"),
+ hash_idx, hash_name);
+ return;
+ }
+
+ const Elf64_Xword *bucket = &hasharr[2];
+ const Elf64_Xword *chain = &hasharr[2 + nbucket];
+
+ for (Elf64_Xword cnt = 0; cnt < nbucket; ++cnt)
+ {
+ Elf64_Xword symidx = bucket[cnt];
+ while (symidx != STN_UNDEF && symidx < nentries && symidx < nchain)
+ {
+ used[symidx] |= 2;
+ symidx = chain[symidx];
+ }
+ }
+ }
+ else
+ {
+ ERROR (gettext ("\
+hash section [%2zu] '%s' invalid sh_entsize\n"),
+ hash_idx, hash_name);
+ return;
+ }
+
+ /* Now see which entries are not set in one or both hash tables
+ (unless the symbol is undefined in which case it can be omitted
+ in the new table format). */
+ if ((used[0] & 1) != 0)
+ ERROR (gettext ("section [%2zu] '%s': reference to symbol index 0\n"),
+ gnu_hash_idx,
+ elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name));
+ if ((used[0] & 2) != 0)
+ ERROR (gettext ("section [%2zu] '%s': reference to symbol index 0\n"),
+ hash_idx, elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name));
+
+ for (uint32_t cnt = 1; cnt < nentries; ++cnt)
+ if (used[cnt] != 0 && used[cnt] != 3)
+ {
+ if (used[cnt] == 1)
+ ERROR (gettext ("\
+symbol %d referenced in new hash table in [%2zu] '%s' but not in old hash table in [%2zu] '%s'\n"),
+ cnt, gnu_hash_idx,
+ elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name),
+ hash_idx,
+ elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name));
+ else
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (sym_data, cnt, &sym_mem);
+
+ if (sym != NULL && sym->st_shndx != STN_UNDEF)
+ ERROR (gettext ("\
+symbol %d referenced in old hash table in [%2zu] '%s' but not in new hash table in [%2zu] '%s'\n"),
+ cnt, hash_idx,
+ elf_strptr (ebl->elf, shstrndx, hash_shdr->sh_name),
+ gnu_hash_idx,
+ elf_strptr (ebl->elf, shstrndx, gnu_hash_shdr->sh_name));
+ }
+ }
+}
+
+
+static void
+check_null (Ebl *ebl, GElf_Shdr *shdr, int idx)
+{
+#define TEST(name, extra) \
+ if (extra && shdr->sh_##name != 0) \
+ ERROR (gettext ("section [%2d] '%s': nonzero sh_%s for NULL section\n"), \
+ idx, section_name (ebl, idx), #name)
+
+ TEST (name, 1);
+ TEST (flags, 1);
+ TEST (addr, 1);
+ TEST (offset, 1);
+ TEST (size, idx != 0);
+ TEST (link, idx != 0);
+ TEST (info, 1);
+ TEST (addralign, 1);
+ TEST (entsize, 1);
+}
+
+
+static void
+check_group (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ if (ehdr->e_type != ET_REL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': section groups only allowed in relocatable object files\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ /* Check that sh_link is an index of a symbol table. */
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ if (symshdr == NULL)
+ ERROR (gettext ("section [%2d] '%s': cannot get symbol table: %s\n"),
+ idx, section_name (ebl, idx), elf_errmsg (-1));
+ else
+ {
+ if (symshdr->sh_type != SHT_SYMTAB)
+ ERROR (gettext ("\
+section [%2d] '%s': section reference in sh_link is no symbol table\n"),
+ idx, section_name (ebl, idx));
+
+ if (shdr->sh_info >= symshdr->sh_size / gelf_fsize (ebl->elf, ELF_T_SYM,
+ 1, EV_CURRENT))
+ ERROR (gettext ("\
+section [%2d] '%s': invalid symbol index in sh_info\n"),
+ idx, section_name (ebl, idx));
+
+ if (shdr->sh_flags != 0)
+ ERROR (gettext ("section [%2d] '%s': sh_flags not zero\n"),
+ idx, section_name (ebl, idx));
+
+ GElf_Sym sym_data;
+ GElf_Sym *sym = gelf_getsym (elf_getdata (symscn, NULL), shdr->sh_info,
+ &sym_data);
+ if (sym == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get symbol for signature\n"),
+ idx, section_name (ebl, idx));
+ else if (elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name) == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get symbol name for signature\n"),
+ idx, section_name (ebl, idx));
+ else if (strcmp (elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name),
+ "") == 0)
+ ERROR (gettext ("\
+section [%2d] '%s': signature symbol cannot be empty string\n"),
+ idx, section_name (ebl, idx));
+
+ if (be_strict
+ && shdr->sh_entsize != elf32_fsize (ELF_T_WORD, 1, EV_CURRENT))
+ ERROR (gettext ("section [%2d] '%s': sh_flags not set correctly\n"),
+ idx, section_name (ebl, idx));
+ }
+
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL || data->d_buf == NULL)
+ ERROR (gettext ("section [%2d] '%s': cannot get data: %s\n"),
+ idx, section_name (ebl, idx), elf_errmsg (-1));
+ else
+ {
+ size_t elsize = elf32_fsize (ELF_T_WORD, 1, EV_CURRENT);
+ size_t cnt;
+ Elf32_Word val;
+
+ if (data->d_size % elsize != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': section size not multiple of sizeof(Elf32_Word)\n"),
+ idx, section_name (ebl, idx));
+
+ if (data->d_size < elsize)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': section group without flags word\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+ else if (be_strict)
+ {
+ if (data->d_size < 2 * elsize)
+ ERROR (gettext ("\
+section [%2d] '%s': section group without member\n"),
+ idx, section_name (ebl, idx));
+ else if (data->d_size < 3 * elsize)
+ ERROR (gettext ("\
+section [%2d] '%s': section group with only one member\n"),
+ idx, section_name (ebl, idx));
+ }
+
+#if ALLOW_UNALIGNED
+ val = *((Elf32_Word *) data->d_buf);
+#else
+ memcpy (&val, data->d_buf, elsize);
+#endif
+ if ((val & ~GRP_COMDAT) != 0)
+ ERROR (gettext ("section [%2d] '%s': unknown section group flags\n"),
+ idx, section_name (ebl, idx));
+
+ for (cnt = elsize; cnt < data->d_size; cnt += elsize)
+ {
+#if ALLOW_UNALIGNED
+ val = *((Elf32_Word *) ((char *) data->d_buf + cnt));
+#else
+ memcpy (&val, (char *) data->d_buf + cnt, elsize);
+#endif
+
+ if (val > shnum)
+ ERROR (gettext ("\
+section [%2d] '%s': section index %zu out of range\n"),
+ idx, section_name (ebl, idx), cnt / elsize);
+ else
+ {
+ GElf_Shdr refshdr_mem;
+ GElf_Shdr *refshdr = gelf_getshdr (elf_getscn (ebl->elf, val),
+ &refshdr_mem);
+ if (refshdr == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': cannot get section header for element %zu: %s\n"),
+ idx, section_name (ebl, idx), cnt / elsize,
+ elf_errmsg (-1));
+ else
+ {
+ if (refshdr->sh_type == SHT_GROUP)
+ ERROR (gettext ("\
+section [%2d] '%s': section group contains another group [%2d] '%s'\n"),
+ idx, section_name (ebl, idx),
+ val, section_name (ebl, val));
+
+ if ((refshdr->sh_flags & SHF_GROUP) == 0)
+ ERROR (gettext ("\
+section [%2d] '%s': element %zu references section [%2d] '%s' without SHF_GROUP flag set\n"),
+ idx, section_name (ebl, idx), cnt / elsize,
+ val, section_name (ebl, val));
+ }
+
+ if (val < shnum && ++scnref[val] == 2)
+ ERROR (gettext ("\
+section [%2d] '%s' is contained in more than one section group\n"),
+ val, section_name (ebl, val));
+ }
+ }
+ }
+}
+
+
+static const char *
+section_flags_string (GElf_Word flags, char *buf, size_t len)
+{
+ if (flags == 0)
+ return "none";
+
+ static const struct
+ {
+ GElf_Word flag;
+ const char *name;
+ } known_flags[] =
+ {
+#define NEWFLAG(name) { SHF_##name, #name }
+ NEWFLAG (WRITE),
+ NEWFLAG (ALLOC),
+ NEWFLAG (EXECINSTR),
+ NEWFLAG (MERGE),
+ NEWFLAG (STRINGS),
+ NEWFLAG (INFO_LINK),
+ NEWFLAG (LINK_ORDER),
+ NEWFLAG (OS_NONCONFORMING),
+ NEWFLAG (GROUP),
+ NEWFLAG (TLS),
+ NEWFLAG (COMPRESSED)
+ };
+#undef NEWFLAG
+ const size_t nknown_flags = sizeof (known_flags) / sizeof (known_flags[0]);
+
+ char *cp = buf;
+
+ for (size_t cnt = 0; cnt < nknown_flags; ++cnt)
+ if (flags & known_flags[cnt].flag)
+ {
+ if (cp != buf && len > 1)
+ {
+ *cp++ = '|';
+ --len;
+ }
+
+ size_t ncopy = MIN (len - 1, strlen (known_flags[cnt].name));
+ cp = mempcpy (cp, known_flags[cnt].name, ncopy);
+ len -= ncopy;
+
+ flags ^= known_flags[cnt].flag;
+ }
+
+ if (flags != 0 || cp == buf)
+ snprintf (cp, len - 1, "%" PRIx64, (uint64_t) flags);
+
+ *cp = '\0';
+
+ return buf;
+}
+
+
+static int
+has_copy_reloc (Ebl *ebl, unsigned int symscnndx, unsigned int symndx)
+{
+ /* First find the relocation section for the symbol table. */
+ Elf_Scn *scn = NULL;
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL
+ && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+ && shdr->sh_link == symscnndx)
+ /* Found the section. */
+ break;
+ }
+
+ if (scn == NULL)
+ return 0;
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL || shdr->sh_entsize == 0)
+ return 0;
+
+ if (shdr->sh_type == SHT_REL)
+ for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i)
+ {
+ GElf_Rel rel_mem;
+ GElf_Rel *rel = gelf_getrel (data, i, &rel_mem);
+ if (rel == NULL)
+ continue;
+
+ if (GELF_R_SYM (rel->r_info) == symndx
+ && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info)))
+ return 1;
+ }
+ else
+ for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i)
+ {
+ GElf_Rela rela_mem;
+ GElf_Rela *rela = gelf_getrela (data, i, &rela_mem);
+ if (rela == NULL)
+ continue;
+
+ if (GELF_R_SYM (rela->r_info) == symndx
+ && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info)))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int
+in_nobits_scn (Ebl *ebl, unsigned int shndx)
+{
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, shndx), &shdr_mem);
+ return shdr != NULL && shdr->sh_type == SHT_NOBITS;
+}
+
+
+static struct version_namelist
+{
+ const char *objname;
+ const char *name;
+ GElf_Versym ndx;
+ enum { ver_def, ver_need } type;
+ struct version_namelist *next;
+} *version_namelist;
+
+
+static int
+add_version (const char *objname, const char *name, GElf_Versym ndx, int type)
+{
+ /* Check that there are no duplications. */
+ struct version_namelist *nlp = version_namelist;
+ while (nlp != NULL)
+ {
+ if (((nlp->objname == NULL && objname == NULL)
+ || (nlp->objname != NULL && objname != NULL
+ && strcmp (nlp->objname, objname) == 0))
+ && strcmp (nlp->name, name) == 0)
+ return nlp->type == ver_def ? 1 : -1;
+ nlp = nlp->next;
+ }
+
+ nlp = xmalloc (sizeof (*nlp));
+ nlp->objname = objname;
+ nlp->name = name;
+ nlp->ndx = ndx;
+ nlp->type = type;
+ nlp->next = version_namelist;
+ version_namelist = nlp;
+
+ return 0;
+}
+
+
+static void
+check_versym (Ebl *ebl, int idx)
+{
+ Elf_Scn *scn = elf_getscn (ebl->elf, idx);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ /* The error has already been reported. */
+ return;
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ if (symshdr == NULL)
+ /* The error has already been reported. */
+ return;
+
+ if (symshdr->sh_type != SHT_DYNSYM)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s' refers in sh_link to section [%2d] '%s' which is no dynamic symbol table\n"),
+ idx, section_name (ebl, idx),
+ shdr->sh_link, section_name (ebl, shdr->sh_link));
+ return;
+ }
+
+ /* The number of elements in the version symbol table must be the
+ same as the number of symbols. */
+ if (shdr->sh_entsize != 0 && symshdr->sh_entsize != 0
+ && (shdr->sh_size / shdr->sh_entsize
+ != symshdr->sh_size / symshdr->sh_entsize))
+ ERROR (gettext ("\
+section [%2d] '%s' has different number of entries than symbol table [%2d] '%s'\n"),
+ idx, section_name (ebl, idx),
+ shdr->sh_link, section_name (ebl, shdr->sh_link));
+
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ if (symdata == NULL || shdr->sh_entsize == 0)
+ /* The error has already been reported. */
+ return;
+
+ for (int cnt = 1; (size_t) cnt < shdr->sh_size / shdr->sh_entsize; ++cnt)
+ {
+ GElf_Versym versym_mem;
+ GElf_Versym *versym = gelf_getversym (data, cnt, &versym_mem);
+ if (versym == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: cannot read version data\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symdata, cnt, &sym_mem);
+ if (sym == NULL)
+ /* Already reported elsewhere. */
+ continue;
+
+ if (*versym == VER_NDX_GLOBAL)
+ {
+ /* Global symbol. Make sure it is not defined as local. */
+ if (GELF_ST_BIND (sym->st_info) == STB_LOCAL)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: local symbol with global scope\n"),
+ idx, section_name (ebl, idx), cnt);
+ }
+ else if (*versym != VER_NDX_LOCAL)
+ {
+ /* Versioned symbol. Make sure it is not defined as local. */
+ if (!gnuld && GELF_ST_BIND (sym->st_info) == STB_LOCAL)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: local symbol with version\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ /* Look through the list of defined versions and locate the
+ index we need for this symbol. */
+ struct version_namelist *runp = version_namelist;
+ while (runp != NULL)
+ if (runp->ndx == (*versym & (GElf_Versym) 0x7fff))
+ break;
+ else
+ runp = runp->next;
+
+ if (runp == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: invalid version index %d\n"),
+ idx, section_name (ebl, idx), cnt, (int) *versym);
+ else if (sym->st_shndx == SHN_UNDEF
+ && runp->type == ver_def)
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: version index %d is for defined version\n"),
+ idx, section_name (ebl, idx), cnt, (int) *versym);
+ else if (sym->st_shndx != SHN_UNDEF
+ && runp->type == ver_need)
+ {
+ /* Unless this symbol has a copy relocation associated
+ this must not happen. */
+ if (!has_copy_reloc (ebl, shdr->sh_link, cnt)
+ && !in_nobits_scn (ebl, sym->st_shndx))
+ ERROR (gettext ("\
+section [%2d] '%s': symbol %d: version index %d is for requested version\n"),
+ idx, section_name (ebl, idx), cnt, (int) *versym);
+ }
+ }
+ }
+}
+
+
+static int
+unknown_dependency_p (Elf *elf, const char *fname)
+{
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = NULL;
+
+ unsigned int i;
+ for (i = 0; i < phnum; ++i)
+ if ((phdr = gelf_getphdr (elf, i, &phdr_mem)) != NULL
+ && phdr->p_type == PT_DYNAMIC)
+ break;
+
+ if (i == phnum)
+ return 1;
+ assert (phdr != NULL);
+ Elf_Scn *scn = gelf_offscn (elf, phdr->p_offset);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC
+ && data != NULL && shdr->sh_entsize != 0)
+ for (size_t j = 0; j < shdr->sh_size / shdr->sh_entsize; ++j)
+ {
+ GElf_Dyn dyn_mem;
+ GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem);
+ if (dyn != NULL && dyn->d_tag == DT_NEEDED)
+ {
+ const char *str = elf_strptr (elf, shdr->sh_link, dyn->d_un.d_val);
+ if (str != NULL && strcmp (str, fname) == 0)
+ /* Found it. */
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+static unsigned int nverneed;
+
+static void
+check_verneed (Ebl *ebl, GElf_Shdr *shdr, int idx)
+{
+ if (++nverneed == 2)
+ ERROR (gettext ("more than one version reference section present\n"));
+
+ GElf_Shdr strshdr_mem;
+ GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &strshdr_mem);
+ if (strshdr == NULL)
+ return;
+ if (strshdr->sh_type != SHT_STRTAB)
+ ERROR (gettext ("\
+section [%2d] '%s': sh_link does not link to string table\n"),
+ idx, section_name (ebl, idx));
+
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+ unsigned int offset = 0;
+ for (Elf64_Word cnt = shdr->sh_info; cnt > 0; )
+ {
+ cnt--;
+
+ /* Get the data at the next offset. */
+ GElf_Verneed needmem;
+ GElf_Verneed *need = gelf_getverneed (data, offset, &needmem);
+ if (need == NULL)
+ break;
+
+ unsigned int auxoffset = offset + need->vn_aux;
+
+ if (need->vn_version != EV_CURRENT)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong version %d\n"),
+ idx, section_name (ebl, idx), cnt, (int) need->vn_version);
+
+ if (need->vn_cnt > 0 && need->vn_aux < gelf_fsize (ebl->elf, ELF_T_VNEED,
+ 1, EV_CURRENT))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+
+ const char *libname = elf_strptr (ebl->elf, shdr->sh_link,
+ need->vn_file);
+ if (libname == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid file reference\n"),
+ idx, section_name (ebl, idx), cnt);
+ goto next_need;
+ }
+
+ /* Check that there is a DT_NEEDED entry for the referenced library. */
+ if (unknown_dependency_p (ebl->elf, libname))
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d references unknown dependency\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ for (int cnt2 = need->vn_cnt; --cnt2 >= 0; )
+ {
+ GElf_Vernaux auxmem;
+ GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem);
+ if (aux == NULL)
+ break;
+
+ if ((aux->vna_flags & ~VER_FLG_WEAK) != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has unknown flag\n"),
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+
+ const char *verstr = elf_strptr (ebl->elf, shdr->sh_link,
+ aux->vna_name);
+ if (verstr == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has invalid name reference\n"),
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+ break;
+ }
+ else
+ {
+ GElf_Word hashval = elf_hash (verstr);
+ if (hashval != aux->vna_hash)
+ ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has wrong hash value: %#x, expected %#x\n"),
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2,
+ cnt, (int) hashval, (int) aux->vna_hash);
+
+ int res = add_version (libname, verstr, aux->vna_other,
+ ver_need);
+ if (unlikely (res !=0))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has duplicate version name '%s'\n"),
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2,
+ cnt, verstr);
+ }
+ }
+
+ if ((aux->vna_next != 0 || cnt2 > 0)
+ && aux->vna_next < gelf_fsize (ebl->elf, ELF_T_VNAUX, 1,
+ EV_CURRENT))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': auxiliary entry %d of entry %d has wrong next field\n"),
+ idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt);
+ break;
+ }
+
+ auxoffset += MAX (aux->vna_next,
+ gelf_fsize (ebl->elf, ELF_T_VNAUX, 1, EV_CURRENT));
+ }
+
+ /* Find the next offset. */
+ next_need:
+ offset += need->vn_next;
+
+ if ((need->vn_next != 0 || cnt > 0)
+ && offset < auxoffset)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid offset to next entry\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+
+ if (need->vn_next == 0 && cnt > 0)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has zero offset to next entry, but sh_info says there are more entries\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+ }
+}
+
+
+static unsigned int nverdef;
+
+static void
+check_verdef (Ebl *ebl, GElf_Shdr *shdr, int idx)
+{
+ if (++nverdef == 2)
+ ERROR (gettext ("more than one version definition section present\n"));
+
+ GElf_Shdr strshdr_mem;
+ GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &strshdr_mem);
+ if (strshdr == NULL)
+ return;
+ if (strshdr->sh_type != SHT_STRTAB)
+ ERROR (gettext ("\
+section [%2d] '%s': sh_link does not link to string table\n"),
+ idx, section_name (ebl, idx));
+
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL)
+ {
+ no_data:
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ /* Iterate over all version definition entries. We check that there
+ is a BASE entry and that each index is unique. To do the later
+ we collection the information in a list which is later
+ examined. */
+ struct namelist
+ {
+ const char *name;
+ struct namelist *next;
+ } *namelist = NULL;
+ struct namelist *refnamelist = NULL;
+
+ bool has_base = false;
+ unsigned int offset = 0;
+ for (Elf64_Word cnt = shdr->sh_info; cnt > 0; )
+ {
+ cnt--;
+
+ /* Get the data at the next offset. */
+ GElf_Verdef defmem;
+ GElf_Verdef *def = gelf_getverdef (data, offset, &defmem);
+ if (def == NULL)
+ goto no_data;
+
+ if ((def->vd_flags & VER_FLG_BASE) != 0)
+ {
+ if (has_base)
+ ERROR (gettext ("\
+section [%2d] '%s': more than one BASE definition\n"),
+ idx, section_name (ebl, idx));
+ if (def->vd_ndx != VER_NDX_GLOBAL)
+ ERROR (gettext ("\
+section [%2d] '%s': BASE definition must have index VER_NDX_GLOBAL\n"),
+ idx, section_name (ebl, idx));
+ has_base = true;
+ }
+ if ((def->vd_flags & ~(VER_FLG_BASE|VER_FLG_WEAK)) != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has unknown flag\n"),
+ idx, section_name (ebl, idx), cnt);
+
+ if (def->vd_version != EV_CURRENT)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong version %d\n"),
+ idx, section_name (ebl, idx), cnt, (int) def->vd_version);
+
+ if (def->vd_cnt > 0 && def->vd_aux < gelf_fsize (ebl->elf, ELF_T_VDEF,
+ 1, EV_CURRENT))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+
+ unsigned int auxoffset = offset + def->vd_aux;
+ GElf_Verdaux auxmem;
+ GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem);
+ if (aux == NULL)
+ goto no_data;
+
+ const char *name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name);
+ if (name == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid name reference\n"),
+ idx, section_name (ebl, idx), cnt);
+ goto next_def;
+ }
+ GElf_Word hashval = elf_hash (name);
+ if (def->vd_hash != hashval)
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong hash value: %#x, expected %#x\n"),
+ idx, section_name (ebl, idx), cnt, (int) hashval,
+ (int) def->vd_hash);
+
+ int res = add_version (NULL, name, def->vd_ndx, ver_def);
+ if (unlikely (res !=0))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has duplicate version name '%s'\n"),
+ idx, section_name (ebl, idx), cnt, name);
+ }
+
+ struct namelist *newname = alloca (sizeof (*newname));
+ newname->name = name;
+ newname->next = namelist;
+ namelist = newname;
+
+ auxoffset += aux->vda_next;
+ for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2)
+ {
+ aux = gelf_getverdaux (data, auxoffset, &auxmem);
+ if (aux == NULL)
+ goto no_data;
+
+ name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name);
+ if (name == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid name reference in auxiliary data\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+ else
+ {
+ newname = alloca (sizeof (*newname));
+ newname->name = name;
+ newname->next = refnamelist;
+ refnamelist = newname;
+ }
+
+ if ((aux->vda_next != 0 || cnt2 + 1 < def->vd_cnt)
+ && aux->vda_next < gelf_fsize (ebl->elf, ELF_T_VDAUX, 1,
+ EV_CURRENT))
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has wrong next field in auxiliary data\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+
+ auxoffset += MAX (aux->vda_next,
+ gelf_fsize (ebl->elf, ELF_T_VDAUX, 1, EV_CURRENT));
+ }
+
+ /* Find the next offset. */
+ next_def:
+ offset += def->vd_next;
+
+ if ((def->vd_next != 0 || cnt > 0)
+ && offset < auxoffset)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has invalid offset to next entry\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+
+ if (def->vd_next == 0 && cnt > 0)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': entry %d has zero offset to next entry, but sh_info says there are more entries\n"),
+ idx, section_name (ebl, idx), cnt);
+ break;
+ }
+ }
+
+ if (!has_base)
+ ERROR (gettext ("section [%2d] '%s': no BASE definition\n"),
+ idx, section_name (ebl, idx));
+
+ /* Check whether the referenced names are available. */
+ while (namelist != NULL)
+ {
+ struct version_namelist *runp = version_namelist;
+ while (runp != NULL)
+ {
+ if (runp->type == ver_def
+ && strcmp (runp->name, namelist->name) == 0)
+ break;
+ runp = runp->next;
+ }
+
+ if (runp == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': unknown parent version '%s'\n"),
+ idx, section_name (ebl, idx), namelist->name);
+
+ namelist = namelist->next;
+ }
+}
+
+static void
+check_attributes (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ if (shdr->sh_size == 0)
+ {
+ ERROR (gettext ("section [%2d] '%s': empty object attributes section\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ Elf_Data *data = elf_rawdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL || data->d_size == 0 || data->d_buf == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ inline size_t pos (const unsigned char *p)
+ {
+ return p - (const unsigned char *) data->d_buf;
+ }
+
+ const unsigned char *p = data->d_buf;
+ if (*p++ != 'A')
+ {
+ ERROR (gettext ("section [%2d] '%s': unrecognized attribute format\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ inline size_t left (void)
+ {
+ return (const unsigned char *) data->d_buf + data->d_size - p;
+ }
+
+ while (left () >= 4)
+ {
+ uint32_t len;
+ memcpy (&len, p, sizeof len);
+
+ if (len == 0)
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: zero length field in attribute section\n"),
+ idx, section_name (ebl, idx), pos (p));
+
+ if (MY_ELFDATA != ehdr->e_ident[EI_DATA])
+ CONVERT (len);
+
+ if (len > left ())
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: invalid length in attribute section\n"),
+ idx, section_name (ebl, idx), pos (p));
+ break;
+ }
+
+ const unsigned char *name = p + sizeof len;
+ p += len;
+
+ unsigned const char *q = memchr (name, '\0', len);
+ if (q == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: unterminated vendor name string\n"),
+ idx, section_name (ebl, idx), pos (p));
+ break;
+ }
+ ++q;
+
+ if (q - name == sizeof "gnu" && !memcmp (name, "gnu", sizeof "gnu"))
+ while (q < p)
+ {
+ unsigned const char *chunk = q;
+
+ unsigned int subsection_tag;
+ get_uleb128 (subsection_tag, q, p);
+
+ if (q >= p)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: endless ULEB128 in attribute subsection tag\n"),
+ idx, section_name (ebl, idx), pos (chunk));
+ break;
+ }
+
+ uint32_t subsection_len;
+ if (p - q < (ptrdiff_t) sizeof subsection_len)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: truncated attribute section\n"),
+ idx, section_name (ebl, idx), pos (q));
+ break;
+ }
+
+ memcpy (&subsection_len, q, sizeof subsection_len);
+ if (subsection_len == 0)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: zero length field in attribute subsection\n"),
+ idx, section_name (ebl, idx), pos (q));
+
+ q += sizeof subsection_len;
+ continue;
+ }
+
+ if (MY_ELFDATA != ehdr->e_ident[EI_DATA])
+ CONVERT (subsection_len);
+
+ /* Don't overflow, ptrdiff_t might be 32bits, but signed. */
+ if (p - chunk < (ptrdiff_t) subsection_len
+ || subsection_len >= (uint32_t) PTRDIFF_MAX)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: invalid length in attribute subsection\n"),
+ idx, section_name (ebl, idx), pos (q));
+ break;
+ }
+
+ const unsigned char *subsection_end = chunk + subsection_len;
+ chunk = q;
+ q = subsection_end;
+
+ if (subsection_tag != 1) /* Tag_File */
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: attribute subsection has unexpected tag %u\n"),
+ idx, section_name (ebl, idx), pos (chunk), subsection_tag);
+ else
+ {
+ chunk += sizeof subsection_len;
+ while (chunk < q)
+ {
+ unsigned int tag;
+ get_uleb128 (tag, chunk, q);
+
+ uint64_t value = 0;
+ const unsigned char *r = chunk;
+ if (tag == 32 || (tag & 1) == 0)
+ {
+ get_uleb128 (value, r, q);
+ if (r > q)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: endless ULEB128 in attribute tag\n"),
+ idx, section_name (ebl, idx), pos (chunk));
+ break;
+ }
+ }
+ if (tag == 32 || (tag & 1) != 0)
+ {
+ r = memchr (r, '\0', q - r);
+ if (r == NULL)
+ {
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: unterminated string in attribute\n"),
+ idx, section_name (ebl, idx), pos (chunk));
+ break;
+ }
+ ++r;
+ }
+
+ const char *tag_name = NULL;
+ const char *value_name = NULL;
+ if (!ebl_check_object_attribute (ebl, (const char *) name,
+ tag, value,
+ &tag_name, &value_name))
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: unrecognized attribute tag %u\n"),
+ idx, section_name (ebl, idx), pos (chunk), tag);
+ else if ((tag & 1) == 0 && value_name == NULL)
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: unrecognized %s attribute value %" PRIu64 "\n"),
+ idx, section_name (ebl, idx), pos (chunk),
+ tag_name, value);
+
+ chunk = r;
+ }
+ }
+ }
+ else
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: vendor '%s' unknown\n"),
+ idx, section_name (ebl, idx), pos (p), name);
+ }
+
+ if (left () != 0)
+ ERROR (gettext ("\
+section [%2d] '%s': offset %zu: extra bytes after last attribute section\n"),
+ idx, section_name (ebl, idx), pos (p));
+}
+
+static bool has_loadable_segment;
+static bool has_interp_segment;
+
+static const struct
+{
+ const char *name;
+ size_t namelen;
+ GElf_Word type;
+ enum { unused, exact, atleast, exact_or_gnuld } attrflag;
+ GElf_Word attr;
+ GElf_Word attr2;
+} special_sections[] =
+ {
+ /* See figure 4-14 in the gABI. */
+ { ".bss", 5, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".comment", 8, SHT_PROGBITS, atleast, 0, SHF_MERGE | SHF_STRINGS },
+ { ".data", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".data1", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".debug_str", 11, SHT_PROGBITS, exact_or_gnuld, SHF_MERGE | SHF_STRINGS, 0 },
+ { ".debug", 6, SHT_PROGBITS, exact, 0, 0 },
+ { ".dynamic", 9, SHT_DYNAMIC, atleast, SHF_ALLOC, SHF_WRITE },
+ { ".dynstr", 8, SHT_STRTAB, exact, SHF_ALLOC, 0 },
+ { ".dynsym", 8, SHT_DYNSYM, exact, SHF_ALLOC, 0 },
+ { ".fini", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 },
+ { ".fini_array", 12, SHT_FINI_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".got", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more info?
+ { ".hash", 6, SHT_HASH, exact, SHF_ALLOC, 0 },
+ { ".init", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 },
+ { ".init_array", 12, SHT_INIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".interp", 8, SHT_PROGBITS, atleast, 0, SHF_ALLOC }, // XXX more tests?
+ { ".line", 6, SHT_PROGBITS, exact, 0, 0 },
+ { ".note", 6, SHT_NOTE, atleast, 0, SHF_ALLOC },
+ { ".plt", 5, SHT_PROGBITS, unused, 0, 0 }, // XXX more tests
+ { ".preinit_array", 15, SHT_PREINIT_ARRAY, exact, SHF_ALLOC | SHF_WRITE, 0 },
+ { ".rela", 5, SHT_RELA, atleast, 0, SHF_ALLOC | SHF_INFO_LINK }, // XXX more tests
+ { ".rel", 4, SHT_REL, atleast, 0, SHF_ALLOC | SHF_INFO_LINK }, // XXX more tests
+ { ".rodata", 8, SHT_PROGBITS, atleast, SHF_ALLOC, SHF_MERGE | SHF_STRINGS },
+ { ".rodata1", 9, SHT_PROGBITS, atleast, SHF_ALLOC, SHF_MERGE | SHF_STRINGS },
+ { ".shstrtab", 10, SHT_STRTAB, exact, 0, 0 },
+ { ".strtab", 8, SHT_STRTAB, atleast, 0, SHF_ALLOC }, // XXX more tests
+ { ".symtab", 8, SHT_SYMTAB, atleast, 0, SHF_ALLOC }, // XXX more tests
+ { ".symtab_shndx", 14, SHT_SYMTAB_SHNDX, atleast, 0, SHF_ALLOC }, // XXX more tests
+ { ".tbss", 6, SHT_NOBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 },
+ { ".tdata", 7, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 },
+ { ".tdata1", 8, SHT_PROGBITS, exact, SHF_ALLOC | SHF_WRITE | SHF_TLS, 0 },
+ { ".text", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 },
+
+ /* The following are GNU extensions. */
+ { ".gnu.version", 13, SHT_GNU_versym, exact, SHF_ALLOC, 0 },
+ { ".gnu.version_d", 15, SHT_GNU_verdef, exact, SHF_ALLOC, 0 },
+ { ".gnu.version_r", 15, SHT_GNU_verneed, exact, SHF_ALLOC, 0 },
+ { ".gnu.attributes", 16, SHT_GNU_ATTRIBUTES, exact, 0, 0 },
+ };
+#define nspecial_sections \
+ (sizeof (special_sections) / sizeof (special_sections[0]))
+
+#define IS_KNOWN_SPECIAL(idx, string, prefix) \
+ (special_sections[idx].namelen == sizeof string - (prefix ? 1 : 0) \
+ && !memcmp (special_sections[idx].name, string, \
+ sizeof string - (prefix ? 1 : 0)))
+
+
+/* Indeces of some sections we need later. */
+static size_t eh_frame_hdr_scnndx;
+static size_t eh_frame_scnndx;
+static size_t gcc_except_table_scnndx;
+
+
+static void
+check_sections (Ebl *ebl, GElf_Ehdr *ehdr)
+{
+ if (ehdr->e_shoff == 0)
+ /* No section header. */
+ return;
+
+ /* Allocate array to count references in section groups. */
+ scnref = (int *) xcalloc (shnum, sizeof (int));
+
+ /* Check the zeroth section first. It must not have any contents
+ and the section header must contain nonzero value at most in the
+ sh_size and sh_link fields. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
+ if (shdr == NULL)
+ ERROR (gettext ("cannot get section header of zeroth section\n"));
+ else
+ {
+ if (shdr->sh_name != 0)
+ ERROR (gettext ("zeroth section has nonzero name\n"));
+ if (shdr->sh_type != 0)
+ ERROR (gettext ("zeroth section has nonzero type\n"));
+ if (shdr->sh_flags != 0)
+ ERROR (gettext ("zeroth section has nonzero flags\n"));
+ if (shdr->sh_addr != 0)
+ ERROR (gettext ("zeroth section has nonzero address\n"));
+ if (shdr->sh_offset != 0)
+ ERROR (gettext ("zeroth section has nonzero offset\n"));
+ if (shdr->sh_addralign != 0)
+ ERROR (gettext ("zeroth section has nonzero align value\n"));
+ if (shdr->sh_entsize != 0)
+ ERROR (gettext ("zeroth section has nonzero entry size value\n"));
+
+ if (shdr->sh_size != 0 && ehdr->e_shnum != 0)
+ ERROR (gettext ("\
+zeroth section has nonzero size value while ELF header has nonzero shnum value\n"));
+
+ if (shdr->sh_link != 0 && ehdr->e_shstrndx != SHN_XINDEX)
+ ERROR (gettext ("\
+zeroth section has nonzero link value while ELF header does not signal overflow in shstrndx\n"));
+
+ if (shdr->sh_info != 0 && ehdr->e_phnum != PN_XNUM)
+ ERROR (gettext ("\
+zeroth section has nonzero link value while ELF header does not signal overflow in phnum\n"));
+ }
+
+ int *segment_flags = xcalloc (phnum, sizeof segment_flags[0]);
+
+ bool dot_interp_section = false;
+
+ size_t hash_idx = 0;
+ size_t gnu_hash_idx = 0;
+
+ size_t versym_scnndx = 0;
+ for (size_t cnt = 1; cnt < shnum; ++cnt)
+ {
+ Elf_Scn *scn = elf_getscn (ebl->elf, cnt);
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ {
+ ERROR (gettext ("\
+cannot get section header for section [%2zu] '%s': %s\n"),
+ cnt, section_name (ebl, cnt), elf_errmsg (-1));
+ continue;
+ }
+
+ const char *scnname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name);
+
+ if (scnname == NULL)
+ ERROR (gettext ("section [%2zu]: invalid name\n"), cnt);
+ else
+ {
+ /* Check whether it is one of the special sections defined in
+ the gABI. */
+ size_t s;
+ for (s = 0; s < nspecial_sections; ++s)
+ if (strncmp (scnname, special_sections[s].name,
+ special_sections[s].namelen) == 0)
+ {
+ char stbuf1[100];
+ char stbuf2[100];
+ char stbuf3[100];
+
+ GElf_Word good_type = special_sections[s].type;
+ if (IS_KNOWN_SPECIAL (s, ".plt", false)
+ && ebl_bss_plt_p (ebl))
+ good_type = SHT_NOBITS;
+
+ /* In a debuginfo file, any normal section can be SHT_NOBITS.
+ This is only invalid for DWARF sections and .shstrtab. */
+ if (shdr->sh_type != good_type
+ && (shdr->sh_type != SHT_NOBITS
+ || !is_debuginfo
+ || IS_KNOWN_SPECIAL (s, ".debug_str", false)
+ || IS_KNOWN_SPECIAL (s, ".debug", true)
+ || IS_KNOWN_SPECIAL (s, ".shstrtab", false)))
+ ERROR (gettext ("\
+section [%2d] '%s' has wrong type: expected %s, is %s\n"),
+ (int) cnt, scnname,
+ ebl_section_type_name (ebl, special_sections[s].type,
+ stbuf1, sizeof (stbuf1)),
+ ebl_section_type_name (ebl, shdr->sh_type,
+ stbuf2, sizeof (stbuf2)));
+
+ if (special_sections[s].attrflag == exact
+ || special_sections[s].attrflag == exact_or_gnuld)
+ {
+ /* Except for the link order, group bit and
+ compression flag all the other bits should
+ match exactly. */
+ if ((shdr->sh_flags
+ & ~(SHF_LINK_ORDER | SHF_GROUP | SHF_COMPRESSED))
+ != special_sections[s].attr
+ && (special_sections[s].attrflag == exact || !gnuld))
+ ERROR (gettext ("\
+section [%2zu] '%s' has wrong flags: expected %s, is %s\n"),
+ cnt, scnname,
+ section_flags_string (special_sections[s].attr,
+ stbuf1, sizeof (stbuf1)),
+ section_flags_string (shdr->sh_flags
+ & ~SHF_LINK_ORDER,
+ stbuf2, sizeof (stbuf2)));
+ }
+ else if (special_sections[s].attrflag == atleast)
+ {
+ if ((shdr->sh_flags & special_sections[s].attr)
+ != special_sections[s].attr
+ || ((shdr->sh_flags
+ & ~(SHF_LINK_ORDER | SHF_GROUP | SHF_COMPRESSED
+ | special_sections[s].attr
+ | special_sections[s].attr2))
+ != 0))
+ ERROR (gettext ("\
+section [%2zu] '%s' has wrong flags: expected %s and possibly %s, is %s\n"),
+ cnt, scnname,
+ section_flags_string (special_sections[s].attr,
+ stbuf1, sizeof (stbuf1)),
+ section_flags_string (special_sections[s].attr2,
+ stbuf2, sizeof (stbuf2)),
+ section_flags_string (shdr->sh_flags
+ & ~(SHF_LINK_ORDER
+ | SHF_GROUP),
+ stbuf3, sizeof (stbuf3)));
+ }
+
+ if (strcmp (scnname, ".interp") == 0)
+ {
+ dot_interp_section = true;
+
+ if (ehdr->e_type == ET_REL)
+ ERROR (gettext ("\
+section [%2zu] '%s' present in object file\n"),
+ cnt, scnname);
+
+ if ((shdr->sh_flags & SHF_ALLOC) != 0
+ && !has_loadable_segment)
+ ERROR (gettext ("\
+section [%2zu] '%s' has SHF_ALLOC flag set but there is no loadable segment\n"),
+ cnt, scnname);
+ else if ((shdr->sh_flags & SHF_ALLOC) == 0
+ && has_loadable_segment)
+ ERROR (gettext ("\
+section [%2zu] '%s' has SHF_ALLOC flag not set but there are loadable segments\n"),
+ cnt, scnname);
+ }
+ else
+ {
+ if (strcmp (scnname, ".symtab_shndx") == 0
+ && ehdr->e_type != ET_REL)
+ ERROR (gettext ("\
+section [%2zu] '%s' is extension section index table in non-object file\n"),
+ cnt, scnname);
+
+ /* These sections must have the SHF_ALLOC flag set iff
+ a loadable segment is available.
+
+ .relxxx
+ .strtab
+ .symtab
+ .symtab_shndx
+
+ Check that if there is a reference from the
+ loaded section these sections also have the
+ ALLOC flag set. */
+#if 0
+ // XXX TODO
+ if ((shdr->sh_flags & SHF_ALLOC) != 0
+ && !has_loadable_segment)
+ ERROR (gettext ("\
+section [%2zu] '%s' has SHF_ALLOC flag set but there is no loadable segment\n"),
+ cnt, scnname);
+ else if ((shdr->sh_flags & SHF_ALLOC) == 0
+ && has_loadable_segment)
+ ERROR (gettext ("\
+section [%2zu] '%s' has SHF_ALLOC flag not set but there are loadable segments\n"),
+ cnt, scnname);
+#endif
+ }
+
+ break;
+ }
+
+ /* Remember a few special sections for later. */
+ if (strcmp (scnname, ".eh_frame_hdr") == 0)
+ eh_frame_hdr_scnndx = cnt;
+ else if (strcmp (scnname, ".eh_frame") == 0)
+ eh_frame_scnndx = cnt;
+ else if (strcmp (scnname, ".gcc_except_table") == 0)
+ gcc_except_table_scnndx = cnt;
+ }
+
+ if (shdr->sh_entsize != 0 && shdr->sh_size % shdr->sh_entsize)
+ ERROR (gettext ("\
+section [%2zu] '%s': size not multiple of entry size\n"),
+ cnt, section_name (ebl, cnt));
+
+ if (elf_strptr (ebl->elf, shstrndx, shdr->sh_name) == NULL)
+ ERROR (gettext ("cannot get section header\n"));
+
+ if (shdr->sh_type >= SHT_NUM
+ && shdr->sh_type != SHT_GNU_ATTRIBUTES
+ && shdr->sh_type != SHT_GNU_LIBLIST
+ && shdr->sh_type != SHT_CHECKSUM
+ && shdr->sh_type != SHT_GNU_verdef
+ && shdr->sh_type != SHT_GNU_verneed
+ && shdr->sh_type != SHT_GNU_versym
+ && ebl_section_type_name (ebl, shdr->sh_type, NULL, 0) == NULL)
+ ERROR (gettext ("section [%2zu] '%s' has unsupported type %d\n"),
+ cnt, section_name (ebl, cnt),
+ (int) shdr->sh_type);
+
+#define ALL_SH_FLAGS (SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR | SHF_MERGE \
+ | SHF_STRINGS | SHF_INFO_LINK | SHF_LINK_ORDER \
+ | SHF_OS_NONCONFORMING | SHF_GROUP | SHF_TLS \
+ | SHF_COMPRESSED)
+ if (shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS)
+ {
+ GElf_Xword sh_flags = shdr->sh_flags & ~(GElf_Xword) ALL_SH_FLAGS;
+ if (sh_flags & SHF_MASKPROC)
+ {
+ if (!ebl_machine_section_flag_check (ebl,
+ sh_flags & SHF_MASKPROC))
+ ERROR (gettext ("section [%2zu] '%s'"
+ " contains invalid processor-specific flag(s)"
+ " %#" PRIx64 "\n"),
+ cnt, section_name (ebl, cnt), sh_flags & SHF_MASKPROC);
+ sh_flags &= ~(GElf_Xword) SHF_MASKPROC;
+ }
+ if (sh_flags != 0)
+ ERROR (gettext ("section [%2zu] '%s' contains unknown flag(s)"
+ " %#" PRIx64 "\n"),
+ cnt, section_name (ebl, cnt), sh_flags);
+ }
+ if (shdr->sh_flags & SHF_TLS)
+ {
+ // XXX Correct?
+ if (shdr->sh_addr != 0 && !gnuld)
+ ERROR (gettext ("\
+section [%2zu] '%s': thread-local data sections address not zero\n"),
+ cnt, section_name (ebl, cnt));
+
+ // XXX TODO more tests!?
+ }
+
+ if (shdr->sh_flags & SHF_COMPRESSED)
+ {
+ if (shdr->sh_flags & SHF_ALLOC)
+ ERROR (gettext ("\
+section [%2zu] '%s': allocated section cannot be compressed\n"),
+ cnt, section_name (ebl, cnt));
+
+ if (shdr->sh_type == SHT_NOBITS)
+ ERROR (gettext ("\
+section [%2zu] '%s': nobits section cannot be compressed\n"),
+ cnt, section_name (ebl, cnt));
+
+ GElf_Chdr chdr;
+ if (gelf_getchdr (scn, &chdr) == NULL)
+ ERROR (gettext ("\
+section [%2zu] '%s': compressed section with no compression header: %s\n"),
+ cnt, section_name (ebl, cnt), elf_errmsg (-1));
+ }
+
+ if (shdr->sh_link >= shnum)
+ ERROR (gettext ("\
+section [%2zu] '%s': invalid section reference in link value\n"),
+ cnt, section_name (ebl, cnt));
+
+ if (SH_INFO_LINK_P (shdr) && shdr->sh_info >= shnum)
+ ERROR (gettext ("\
+section [%2zu] '%s': invalid section reference in info value\n"),
+ cnt, section_name (ebl, cnt));
+
+ if ((shdr->sh_flags & SHF_MERGE) == 0
+ && (shdr->sh_flags & SHF_STRINGS) != 0
+ && be_strict)
+ ERROR (gettext ("\
+section [%2zu] '%s': strings flag set without merge flag\n"),
+ cnt, section_name (ebl, cnt));
+
+ if ((shdr->sh_flags & SHF_MERGE) != 0 && shdr->sh_entsize == 0)
+ ERROR (gettext ("\
+section [%2zu] '%s': merge flag set but entry size is zero\n"),
+ cnt, section_name (ebl, cnt));
+
+ if (shdr->sh_flags & SHF_GROUP)
+ check_scn_group (ebl, cnt);
+
+ if (shdr->sh_flags & SHF_EXECINSTR)
+ {
+ switch (shdr->sh_type)
+ {
+ case SHT_PROGBITS:
+ break;
+
+ case SHT_NOBITS:
+ if (is_debuginfo)
+ break;
+ FALLTHROUGH;
+ default:
+ ERROR (gettext ("\
+section [%2zu] '%s' has unexpected type %d for an executable section\n"),
+ cnt, section_name (ebl, cnt), shdr->sh_type);
+ break;
+ }
+
+ if (shdr->sh_flags & SHF_WRITE)
+ {
+ if (is_debuginfo && shdr->sh_type != SHT_NOBITS)
+ ERROR (gettext ("\
+section [%2zu] '%s' must be of type NOBITS in debuginfo files\n"),
+ cnt, section_name (ebl, cnt));
+
+ if (!is_debuginfo
+ && !ebl_check_special_section (ebl, cnt, shdr,
+ section_name (ebl, cnt)))
+ ERROR (gettext ("\
+section [%2zu] '%s' is both executable and writable\n"),
+ cnt, section_name (ebl, cnt));
+ }
+ }
+
+ if (ehdr->e_type != ET_REL && (shdr->sh_flags & SHF_ALLOC) != 0
+ && !is_debuginfo)
+ {
+ /* Make sure the section is contained in a loaded segment
+ and that the initialization part matches NOBITS sections. */
+ unsigned int pcnt;
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr;
+
+ for (pcnt = 0; pcnt < phnum; ++pcnt)
+ if ((phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem)) != NULL
+ && ((phdr->p_type == PT_LOAD
+ && (shdr->sh_flags & SHF_TLS) == 0)
+ || (phdr->p_type == PT_TLS
+ && (shdr->sh_flags & SHF_TLS) != 0))
+ && phdr->p_offset <= shdr->sh_offset
+ && ((shdr->sh_offset - phdr->p_offset <= phdr->p_filesz
+ && (shdr->sh_offset - phdr->p_offset < phdr->p_filesz
+ || shdr->sh_size == 0))
+ || (shdr->sh_offset - phdr->p_offset < phdr->p_memsz
+ && shdr->sh_type == SHT_NOBITS)))
+ {
+ /* Found the segment. */
+ if (phdr->p_offset + phdr->p_memsz
+ < shdr->sh_offset + shdr->sh_size)
+ ERROR (gettext ("\
+section [%2zu] '%s' not fully contained in segment of program header entry %d\n"),
+ cnt, section_name (ebl, cnt), pcnt);
+
+ if (shdr->sh_type == SHT_NOBITS)
+ {
+ if (shdr->sh_offset < phdr->p_offset + phdr->p_filesz
+ && !is_debuginfo)
+ {
+ if (!gnuld)
+ ERROR (gettext ("\
+section [%2zu] '%s' has type NOBITS but is read from the file in segment of program header entry %d\n"),
+ cnt, section_name (ebl, cnt), pcnt);
+ else
+ {
+ /* This is truly horrible. GNU ld might put a
+ NOBITS section in the middle of a PT_LOAD
+ segment, assuming the next gap in the file
+ actually consists of zero bits...
+ So it really is like a PROGBITS section
+ where the data is all zeros. Check those
+ zero bytes are really there. */
+ bool bad;
+ Elf_Data *databits;
+ databits = elf_getdata_rawchunk (ebl->elf,
+ shdr->sh_offset,
+ shdr->sh_size,
+ ELF_T_BYTE);
+ bad = (databits == NULL
+ || databits->d_size != shdr->sh_size);
+ for (size_t idx = 0;
+ idx < databits->d_size && ! bad;
+ idx++)
+ bad = ((char *) databits->d_buf)[idx] != 0;
+
+ if (bad)
+ ERROR (gettext ("\
+section [%2zu] '%s' has type NOBITS but is read from the file in segment of program header entry %d and file contents is non-zero\n"),
+ cnt, section_name (ebl, cnt), pcnt);
+ }
+ }
+ }
+ else
+ {
+ const GElf_Off end = phdr->p_offset + phdr->p_filesz;
+ if (shdr->sh_offset > end ||
+ (shdr->sh_offset == end && shdr->sh_size != 0))
+ ERROR (gettext ("\
+section [%2zu] '%s' has not type NOBITS but is not read from the file in segment of program header entry %d\n"),
+ cnt, section_name (ebl, cnt), pcnt);
+ }
+
+ if (shdr->sh_type != SHT_NOBITS)
+ {
+ if ((shdr->sh_flags & SHF_EXECINSTR) != 0)
+ {
+ segment_flags[pcnt] |= PF_X;
+ if ((phdr->p_flags & PF_X) == 0)
+ ERROR (gettext ("\
+section [%2zu] '%s' is executable in nonexecutable segment %d\n"),
+ cnt, section_name (ebl, cnt), pcnt);
+ }
+
+ if ((shdr->sh_flags & SHF_WRITE) != 0)
+ {
+ segment_flags[pcnt] |= PF_W;
+ if (0 /* XXX vdso images have this */
+ && (phdr->p_flags & PF_W) == 0)
+ ERROR (gettext ("\
+section [%2zu] '%s' is writable in unwritable segment %d\n"),
+ cnt, section_name (ebl, cnt), pcnt);
+ }
+ }
+
+ break;
+ }
+
+ if (pcnt == phnum)
+ ERROR (gettext ("\
+section [%2zu] '%s': alloc flag set but section not in any loaded segment\n"),
+ cnt, section_name (ebl, cnt));
+ }
+
+ if (cnt == shstrndx && shdr->sh_type != SHT_STRTAB)
+ ERROR (gettext ("\
+section [%2zu] '%s': ELF header says this is the section header string table but type is not SHT_TYPE\n"),
+ cnt, section_name (ebl, cnt));
+
+ switch (shdr->sh_type)
+ {
+ case SHT_DYNSYM:
+ if (ehdr->e_type == ET_REL)
+ ERROR (gettext ("\
+section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"),
+ cnt, section_name (ebl, cnt));
+ FALLTHROUGH;
+ case SHT_SYMTAB:
+ check_symtab (ebl, ehdr, shdr, cnt);
+ break;
+
+ case SHT_RELA:
+ check_rela (ebl, ehdr, shdr, cnt);
+ break;
+
+ case SHT_REL:
+ check_rel (ebl, ehdr, shdr, cnt);
+ break;
+
+ case SHT_DYNAMIC:
+ check_dynamic (ebl, ehdr, shdr, cnt);
+ break;
+
+ case SHT_SYMTAB_SHNDX:
+ check_symtab_shndx (ebl, ehdr, shdr, cnt);
+ break;
+
+ case SHT_HASH:
+ check_hash (shdr->sh_type, ebl, ehdr, shdr, cnt);
+ hash_idx = cnt;
+ break;
+
+ case SHT_GNU_HASH:
+ check_hash (shdr->sh_type, ebl, ehdr, shdr, cnt);
+ gnu_hash_idx = cnt;
+ break;
+
+ case SHT_NULL:
+ check_null (ebl, shdr, cnt);
+ break;
+
+ case SHT_GROUP:
+ check_group (ebl, ehdr, shdr, cnt);
+ break;
+
+ case SHT_NOTE:
+ check_note_section (ebl, ehdr, shdr, cnt);
+ break;
+
+ case SHT_GNU_versym:
+ /* We cannot process this section now since we have no guarantee
+ that the verneed and verdef sections have already been read.
+ Just remember the section index. */
+ if (versym_scnndx != 0)
+ ERROR (gettext ("more than one version symbol table present\n"));
+ versym_scnndx = cnt;
+ break;
+
+ case SHT_GNU_verneed:
+ check_verneed (ebl, shdr, cnt);
+ break;
+
+ case SHT_GNU_verdef:
+ check_verdef (ebl, shdr, cnt);
+ break;
+
+ case SHT_GNU_ATTRIBUTES:
+ check_attributes (ebl, ehdr, shdr, cnt);
+ break;
+
+ default:
+ /* Nothing. */
+ break;
+ }
+ }
+
+ if (has_interp_segment && !dot_interp_section)
+ ERROR (gettext ("INTERP program header entry but no .interp section\n"));
+
+ if (!is_debuginfo)
+ for (unsigned int pcnt = 0; pcnt < phnum; ++pcnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, pcnt, &phdr_mem);
+ if (phdr != NULL && (phdr->p_type == PT_LOAD || phdr->p_type == PT_TLS))
+ {
+ if ((phdr->p_flags & PF_X) != 0
+ && (segment_flags[pcnt] & PF_X) == 0)
+ ERROR (gettext ("\
+loadable segment [%u] is executable but contains no executable sections\n"),
+ pcnt);
+
+ if ((phdr->p_flags & PF_W) != 0
+ && (segment_flags[pcnt] & PF_W) == 0)
+ ERROR (gettext ("\
+loadable segment [%u] is writable but contains no writable sections\n"),
+ pcnt);
+ }
+ }
+
+ free (segment_flags);
+
+ if (version_namelist != NULL)
+ {
+ if (versym_scnndx == 0)
+ ERROR (gettext ("\
+no .gnu.versym section present but .gnu.versym_d or .gnu.versym_r section exist\n"));
+ else
+ check_versym (ebl, versym_scnndx);
+
+ /* Check for duplicate index numbers. */
+ do
+ {
+ struct version_namelist *runp = version_namelist->next;
+ while (runp != NULL)
+ {
+ if (version_namelist->ndx == runp->ndx)
+ {
+ ERROR (gettext ("duplicate version index %d\n"),
+ (int) version_namelist->ndx);
+ break;
+ }
+ runp = runp->next;
+ }
+
+ struct version_namelist *old = version_namelist;
+ version_namelist = version_namelist->next;
+ free (old);
+ }
+ while (version_namelist != NULL);
+ }
+ else if (versym_scnndx != 0)
+ ERROR (gettext ("\
+.gnu.versym section present without .gnu.versym_d or .gnu.versym_r\n"));
+
+ if (hash_idx != 0 && gnu_hash_idx != 0)
+ compare_hash_gnu_hash (ebl, ehdr, hash_idx, gnu_hash_idx);
+
+ free (scnref);
+}
+
+
+static GElf_Off
+check_note_data (Ebl *ebl, const GElf_Ehdr *ehdr,
+ Elf_Data *data, int shndx, int phndx, GElf_Off start)
+{
+ size_t offset = 0;
+ size_t last_offset = 0;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ while (offset < data->d_size
+ && (offset = gelf_getnote (data, offset,
+ &nhdr, &name_offset, &desc_offset)) > 0)
+ {
+ last_offset = offset;
+
+ /* Make sure it is one of the note types we know about. */
+ if (ehdr->e_type == ET_CORE)
+ switch (nhdr.n_type)
+ {
+ case NT_PRSTATUS:
+ case NT_FPREGSET:
+ case NT_PRPSINFO:
+ case NT_TASKSTRUCT: /* NT_PRXREG on Solaris. */
+ case NT_PLATFORM:
+ case NT_AUXV:
+ case NT_GWINDOWS:
+ case NT_ASRS:
+ case NT_PSTATUS:
+ case NT_PSINFO:
+ case NT_PRCRED:
+ case NT_UTSNAME:
+ case NT_LWPSTATUS:
+ case NT_LWPSINFO:
+ case NT_PRFPXREG:
+ /* Known type. */
+ break;
+
+ default:
+ if (shndx == 0)
+ ERROR (gettext ("\
+phdr[%d]: unknown core file note type %" PRIu32 " at offset %" PRIu64 "\n"),
+ phndx, (uint32_t) nhdr.n_type, start + offset);
+ else
+ ERROR (gettext ("\
+section [%2d] '%s': unknown core file note type %" PRIu32
+ " at offset %zu\n"),
+ shndx, section_name (ebl, shndx),
+ (uint32_t) nhdr.n_type, offset);
+ }
+ else
+ switch (nhdr.n_type)
+ {
+ case NT_GNU_ABI_TAG:
+ case NT_GNU_HWCAP:
+ case NT_GNU_BUILD_ID:
+ case NT_GNU_GOLD_VERSION:
+ break;
+
+ case 0:
+ /* Linux vDSOs use a type 0 note for the kernel version word. */
+ if (nhdr.n_namesz == sizeof "Linux"
+ && !memcmp (data->d_buf + name_offset, "Linux", sizeof "Linux"))
+ break;
+ FALLTHROUGH;
+ default:
+ if (shndx == 0)
+ ERROR (gettext ("\
+phdr[%d]: unknown object file note type %" PRIu32 " at offset %zu\n"),
+ phndx, (uint32_t) nhdr.n_type, offset);
+ else
+ ERROR (gettext ("\
+section [%2d] '%s': unknown object file note type %" PRIu32
+ " at offset %zu\n"),
+ shndx, section_name (ebl, shndx),
+ (uint32_t) nhdr.n_type, offset);
+ }
+ }
+
+ return last_offset;
+}
+
+
+static void
+check_note (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Phdr *phdr, int cnt)
+{
+ if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL
+ && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ ERROR (gettext ("\
+phdr[%d]: no note entries defined for the type of file\n"),
+ cnt);
+
+ if (is_debuginfo)
+ /* The p_offset values in a separate debug file are bogus. */
+ return;
+
+ if (phdr->p_filesz == 0)
+ return;
+
+ GElf_Off notes_size = 0;
+ Elf_Data *data = elf_getdata_rawchunk (ebl->elf,
+ phdr->p_offset, phdr->p_filesz,
+ ELF_T_NHDR);
+ if (data != NULL && data->d_buf != NULL)
+ notes_size = check_note_data (ebl, ehdr, data, 0, cnt, phdr->p_offset);
+
+ if (notes_size == 0)
+ ERROR (gettext ("phdr[%d]: cannot get content of note section: %s\n"),
+ cnt, elf_errmsg (-1));
+ else if (notes_size != phdr->p_filesz)
+ ERROR (gettext ("phdr[%d]: extra %" PRIu64 " bytes after last note\n"),
+ cnt, phdr->p_filesz - notes_size);
+}
+
+
+static void
+check_note_section (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx)
+{
+ if (shdr->sh_size == 0)
+ return;
+
+ Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL);
+ if (data == NULL || data->d_buf == NULL)
+ {
+ ERROR (gettext ("section [%2d] '%s': cannot get section data\n"),
+ idx, section_name (ebl, idx));
+ return;
+ }
+
+ if (ehdr->e_type != ET_CORE && ehdr->e_type != ET_REL
+ && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ ERROR (gettext ("\
+section [%2d] '%s': no note entries defined for the type of file\n"),
+ idx, section_name (ebl, idx));
+
+ GElf_Off notes_size = check_note_data (ebl, ehdr, data, idx, 0, 0);
+
+ if (notes_size == 0)
+ ERROR (gettext ("section [%2d] '%s': cannot get content of note section\n"),
+ idx, section_name (ebl, idx));
+ else if (notes_size != shdr->sh_size)
+ ERROR (gettext ("section [%2d] '%s': extra %" PRIu64
+ " bytes after last note\n"),
+ idx, section_name (ebl, idx), shdr->sh_size - notes_size);
+}
+
+
+/* Index of the PT_GNU_EH_FRAME program eader entry. */
+static int pt_gnu_eh_frame_pndx;
+
+
+static void
+check_program_header (Ebl *ebl, GElf_Ehdr *ehdr)
+{
+ if (ehdr->e_phoff == 0)
+ return;
+
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN
+ && ehdr->e_type != ET_CORE)
+ ERROR (gettext ("\
+only executables, shared objects, and core files can have program headers\n"));
+
+ int num_pt_interp = 0;
+ int num_pt_tls = 0;
+ int num_pt_relro = 0;
+
+ for (unsigned int cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr;
+
+ phdr = gelf_getphdr (ebl->elf, cnt, &phdr_mem);
+ if (phdr == NULL)
+ {
+ ERROR (gettext ("cannot get program header entry %d: %s\n"),
+ cnt, elf_errmsg (-1));
+ continue;
+ }
+
+ if (phdr->p_type >= PT_NUM && phdr->p_type != PT_GNU_EH_FRAME
+ && phdr->p_type != PT_GNU_STACK && phdr->p_type != PT_GNU_RELRO
+ /* Check for a known machine-specific type. */
+ && ebl_segment_type_name (ebl, phdr->p_type, NULL, 0) == NULL)
+ ERROR (gettext ("\
+program header entry %d: unknown program header entry type %#" PRIx64 "\n"),
+ cnt, (uint64_t) phdr->p_type);
+
+ if (phdr->p_type == PT_LOAD)
+ has_loadable_segment = true;
+ else if (phdr->p_type == PT_INTERP)
+ {
+ if (++num_pt_interp != 1)
+ {
+ if (num_pt_interp == 2)
+ ERROR (gettext ("\
+more than one INTERP entry in program header\n"));
+ }
+ has_interp_segment = true;
+ }
+ else if (phdr->p_type == PT_TLS)
+ {
+ if (++num_pt_tls == 2)
+ ERROR (gettext ("more than one TLS entry in program header\n"));
+ }
+ else if (phdr->p_type == PT_NOTE)
+ check_note (ebl, ehdr, phdr, cnt);
+ else if (phdr->p_type == PT_DYNAMIC)
+ {
+ if (ehdr->e_type == ET_EXEC && ! has_interp_segment)
+ ERROR (gettext ("\
+static executable cannot have dynamic sections\n"));
+ else
+ {
+ /* Check that the .dynamic section, if it exists, has
+ the same address. */
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC)
+ {
+ if (phdr->p_offset != shdr->sh_offset)
+ ERROR (gettext ("\
+dynamic section reference in program header has wrong offset\n"));
+ if (phdr->p_memsz != shdr->sh_size)
+ ERROR (gettext ("\
+dynamic section size mismatch in program and section header\n"));
+ break;
+ }
+ }
+ }
+ }
+ else if (phdr->p_type == PT_GNU_RELRO)
+ {
+ if (++num_pt_relro == 2)
+ ERROR (gettext ("\
+more than one GNU_RELRO entry in program header\n"));
+ else
+ {
+ /* Check that the region is in a writable segment. */
+ unsigned int inner;
+ for (inner = 0; inner < phnum; ++inner)
+ {
+ GElf_Phdr phdr2_mem;
+ GElf_Phdr *phdr2;
+
+ phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem);
+ if (phdr2 == NULL)
+ continue;
+
+ if (phdr2->p_type == PT_LOAD
+ && phdr->p_vaddr >= phdr2->p_vaddr
+ && (phdr->p_vaddr + phdr->p_memsz
+ <= phdr2->p_vaddr + phdr2->p_memsz))
+ {
+ if ((phdr2->p_flags & PF_W) == 0)
+ ERROR (gettext ("\
+loadable segment GNU_RELRO applies to is not writable\n"));
+ /* Unless fully covered, relro flags could be a
+ subset of the phdrs2 flags. For example the load
+ segment could also have PF_X set. */
+ if (phdr->p_vaddr == phdr2->p_vaddr
+ && (phdr->p_vaddr + phdr->p_memsz
+ == phdr2->p_vaddr + phdr2->p_memsz))
+ {
+ if ((phdr2->p_flags & ~PF_W)
+ != (phdr->p_flags & ~PF_W))
+ ERROR (gettext ("\
+loadable segment [%u] flags do not match GNU_RELRO [%u] flags\n"),
+ cnt, inner);
+ }
+ else
+ {
+ if ((phdr->p_flags & ~phdr2->p_flags) != 0)
+ ERROR (gettext ("\
+GNU_RELRO [%u] flags are not a subset of the loadable segment [%u] flags\n"),
+ inner, cnt);
+ }
+ break;
+ }
+ }
+
+ if (inner >= phnum)
+ ERROR (gettext ("\
+%s segment not contained in a loaded segment\n"), "GNU_RELRO");
+ }
+ }
+ else if (phdr->p_type == PT_PHDR)
+ {
+ /* Check that the region is in a writable segment. */
+ unsigned int inner;
+ for (inner = 0; inner < phnum; ++inner)
+ {
+ GElf_Phdr phdr2_mem;
+ GElf_Phdr *phdr2;
+
+ phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem);
+ if (phdr2 != NULL
+ && phdr2->p_type == PT_LOAD
+ && phdr->p_vaddr >= phdr2->p_vaddr
+ && (phdr->p_vaddr + phdr->p_memsz
+ <= phdr2->p_vaddr + phdr2->p_memsz))
+ break;
+ }
+
+ if (inner >= phnum)
+ ERROR (gettext ("\
+%s segment not contained in a loaded segment\n"), "PHDR");
+
+ /* Check that offset in segment corresponds to offset in ELF
+ header. */
+ if (phdr->p_offset != ehdr->e_phoff)
+ ERROR (gettext ("\
+program header offset in ELF header and PHDR entry do not match"));
+ }
+ else if (phdr->p_type == PT_GNU_EH_FRAME)
+ {
+ /* If there is an .eh_frame_hdr section it must be
+ referenced by this program header entry. */
+ Elf_Scn *scn = NULL;
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = NULL;
+ bool any = false;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ any = true;
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL
+ && shdr->sh_type == (is_debuginfo
+ ? SHT_NOBITS : SHT_PROGBITS)
+ && elf_strptr (ebl->elf, shstrndx, shdr->sh_name) != NULL
+ && ! strcmp (".eh_frame_hdr",
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name)))
+ {
+ if (! is_debuginfo)
+ {
+ if (phdr->p_offset != shdr->sh_offset)
+ ERROR (gettext ("\
+call frame search table reference in program header has wrong offset\n"));
+ if (phdr->p_memsz != shdr->sh_size)
+ ERROR (gettext ("\
+call frame search table size mismatch in program and section header\n"));
+ }
+ break;
+ }
+ }
+
+ if (scn == NULL)
+ {
+ /* If there is no section header table we don't
+ complain. But if there is one there should be an
+ entry for .eh_frame_hdr. */
+ if (any)
+ ERROR (gettext ("\
+PT_GNU_EH_FRAME present but no .eh_frame_hdr section\n"));
+ }
+ else
+ {
+ /* The section must be allocated and not be writable and
+ executable. */
+ if ((phdr->p_flags & PF_R) == 0)
+ ERROR (gettext ("\
+call frame search table must be allocated\n"));
+ else if (shdr != NULL && (shdr->sh_flags & SHF_ALLOC) == 0)
+ ERROR (gettext ("\
+section [%2zu] '%s' must be allocated\n"), elf_ndxscn (scn), ".eh_frame_hdr");
+
+ if ((phdr->p_flags & PF_W) != 0)
+ ERROR (gettext ("\
+call frame search table must not be writable\n"));
+ else if (shdr != NULL && (shdr->sh_flags & SHF_WRITE) != 0)
+ ERROR (gettext ("\
+section [%2zu] '%s' must not be writable\n"),
+ elf_ndxscn (scn), ".eh_frame_hdr");
+
+ if ((phdr->p_flags & PF_X) != 0)
+ ERROR (gettext ("\
+call frame search table must not be executable\n"));
+ else if (shdr != NULL && (shdr->sh_flags & SHF_EXECINSTR) != 0)
+ ERROR (gettext ("\
+section [%2zu] '%s' must not be executable\n"),
+ elf_ndxscn (scn), ".eh_frame_hdr");
+ }
+
+ /* Remember which entry this is. */
+ pt_gnu_eh_frame_pndx = cnt;
+ }
+
+ if (phdr->p_filesz > phdr->p_memsz
+ && (phdr->p_memsz != 0 || phdr->p_type != PT_NOTE))
+ ERROR (gettext ("\
+program header entry %d: file size greater than memory size\n"),
+ cnt);
+
+ if (phdr->p_align > 1)
+ {
+ if (!powerof2 (phdr->p_align))
+ ERROR (gettext ("\
+program header entry %d: alignment not a power of 2\n"), cnt);
+ else if ((phdr->p_vaddr - phdr->p_offset) % phdr->p_align != 0)
+ ERROR (gettext ("\
+program header entry %d: file offset and virtual address not module of alignment\n"), cnt);
+ }
+ }
+}
+
+
+static void
+check_exception_data (Ebl *ebl __attribute__ ((unused)),
+ GElf_Ehdr *ehdr __attribute__ ((unused)))
+{
+ if ((ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN)
+ && pt_gnu_eh_frame_pndx == 0 && eh_frame_hdr_scnndx != 0)
+ ERROR (gettext ("executable/DSO with .eh_frame_hdr section does not have "
+ "a PT_GNU_EH_FRAME program header entry"));
+}
+
+
+/* Process one file. */
+static void
+process_elf_file (Elf *elf, const char *prefix, const char *suffix,
+ const char *fname, size_t size, bool only_one)
+{
+ /* Reset variables. */
+ ndynamic = 0;
+ nverneed = 0;
+ nverdef = 0;
+ textrel = false;
+ needed_textrel = false;
+ has_loadable_segment = false;
+ has_interp_segment = false;
+
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ Ebl *ebl;
+
+ /* Print the file name. */
+ if (!only_one)
+ {
+ if (prefix != NULL)
+ printf ("\n%s(%s)%s:\n", prefix, fname, suffix);
+ else
+ printf ("\n%s:\n", fname);
+ }
+
+ if (ehdr == NULL)
+ {
+ ERROR (gettext ("cannot read ELF header: %s\n"), elf_errmsg (-1));
+ return;
+ }
+
+ ebl = ebl_openbackend (elf);
+ /* If there is no appropriate backend library we cannot test
+ architecture and OS specific features. Any encountered extension
+ is an error. */
+
+ /* Go straight by the gABI, check all the parts in turn. */
+ check_elf_header (ebl, ehdr, size);
+
+ /* Check the program header. */
+ check_program_header (ebl, ehdr);
+
+ /* Next the section headers. It is OK if there are no section
+ headers at all. */
+ check_sections (ebl, ehdr);
+
+ /* Check the exception handling data, if it exists. */
+ if (pt_gnu_eh_frame_pndx != 0 || eh_frame_hdr_scnndx != 0
+ || eh_frame_scnndx != 0 || gcc_except_table_scnndx != 0)
+ check_exception_data (ebl, ehdr);
+
+ /* Report if no relocation section needed the text relocation flag. */
+ if (textrel && !needed_textrel)
+ ERROR (gettext ("text relocation flag set but not needed\n"));
+
+ /* Free the resources. */
+ ebl_closebackend (ebl);
+}
+
+
+#include "debugpred.h"
diff --git a/src/findtextrel.c b/src/findtextrel.c
new file mode 100644
index 0000000..8f1e239
--- /dev/null
+++ b/src/findtextrel.c
@@ -0,0 +1,611 @@
+/* Locate source files or functions which caused text relocations.
+ Copyright (C) 2005-2010, 2012, 2014 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2005.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libdw.h>
+#include <libintl.h>
+#include <locale.h>
+#include <search.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <printversion.h>
+
+
+struct segments
+{
+ GElf_Addr from;
+ GElf_Addr to;
+};
+
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+/* Values for the parameters which have no short form. */
+#define OPT_DEBUGINFO 0x100
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Input Selection:"), 0 },
+ { "root", 'r', "PATH", 0, N_("Prepend PATH to all file names"), 0 },
+ { "debuginfo", OPT_DEBUGINFO, "PATH", 0,
+ N_("Use PATH as root of debuginfo hierarchy"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("\
+Locate source of text relocations in FILEs (a.out by default).");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("[FILE...]");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+
+/* Print symbols in file named FNAME. */
+static int process_file (const char *fname, bool more_than_one);
+
+/* Check for text relocations in the given file. The segment
+ information is known. */
+static void check_rel (size_t nsegments, struct segments segments[nsegments],
+ GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
+ const char *fname, bool more_than_one,
+ void **knownsrcs);
+
+
+
+/* User-provided root directory. */
+static const char *rootdir = "/";
+
+/* Root of debuginfo directory hierarchy. */
+static const char *debuginfo_root;
+
+
+int
+main (int argc, char *argv[])
+{
+ int remaining;
+ int result = 0;
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ (void) textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Tell the library which version we are expecting. */
+ elf_version (EV_CURRENT);
+
+ /* If the user has not specified the root directory for the
+ debuginfo hierarchy, we have to determine it ourselves. */
+ if (debuginfo_root == NULL)
+ {
+ // XXX The runtime should provide this information.
+#if defined __ia64__ || defined __alpha__
+ debuginfo_root = "/usr/lib/debug";
+#else
+ debuginfo_root = (sizeof (long int) == 4
+ ? "/usr/lib/debug" : "/usr/lib64/debug");
+#endif
+ }
+
+ if (remaining == argc)
+ result = process_file ("a.out", false);
+ else
+ {
+ /* Process all the remaining files. */
+ const bool more_than_one = remaining + 1 < argc;
+
+ do
+ result |= process_file (argv[remaining], more_than_one);
+ while (++remaining < argc);
+ }
+
+ return result;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg,
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case 'r':
+ rootdir = arg;
+ break;
+
+ case OPT_DEBUGINFO:
+ debuginfo_root = arg;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+static void
+noop (void *arg __attribute__ ((unused)))
+{
+}
+
+
+static int
+process_file (const char *fname, bool more_than_one)
+{
+ int result = 0;
+ void *knownsrcs = NULL;
+
+ size_t fname_len = strlen (fname);
+ size_t rootdir_len = strlen (rootdir);
+ const char *real_fname = fname;
+ if (fname[0] == '/' && (rootdir[0] != '/' || rootdir[1] != '\0'))
+ {
+ /* Prepend the user-provided root directory. */
+ char *new_fname = alloca (rootdir_len + fname_len + 2);
+ *((char *) mempcpy (stpcpy (mempcpy (new_fname, rootdir, rootdir_len),
+ "/"),
+ fname, fname_len)) = '\0';
+ real_fname = new_fname;
+ }
+
+ int fd = open (real_fname, O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, gettext ("cannot open '%s'"), fname);
+ return 1;
+ }
+
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ {
+ error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"),
+ fname, elf_errmsg (-1));
+ goto err_close;
+ }
+
+ /* Make sure the file is a DSO. */
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ error (0, 0, gettext ("cannot get ELF header '%s': %s"),
+ fname, elf_errmsg (-1));
+ err_elf_close:
+ elf_end (elf);
+ err_close:
+ close (fd);
+ return 1;
+ }
+
+ if (ehdr->e_type != ET_DYN)
+ {
+ error (0, 0, gettext ("'%s' is not a DSO or PIE"), fname);
+ goto err_elf_close;
+ }
+
+ /* Determine whether the DSO has text relocations at all and locate
+ the symbol table. */
+ Elf_Scn *symscn = NULL;
+ Elf_Scn *scn = NULL;
+ bool seen_dynamic = false;
+ bool have_textrel = false;
+ while ((scn = elf_nextscn (elf, scn)) != NULL
+ && (!seen_dynamic || symscn == NULL))
+ {
+ /* Handle the section if it is a symbol table. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr == NULL)
+ {
+ error (0, 0,
+ gettext ("getting get section header of section %zu: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+ goto err_elf_close;
+ }
+
+ switch (shdr->sh_type)
+ {
+ case SHT_DYNAMIC:
+ if (!seen_dynamic)
+ {
+ seen_dynamic = true;
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+
+ for (size_t cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize;
+ ++cnt)
+ {
+ GElf_Dyn dynmem;
+ GElf_Dyn *dyn;
+
+ dyn = gelf_getdyn (data, cnt, &dynmem);
+ if (dyn == NULL)
+ {
+ error (0, 0, gettext ("cannot read dynamic section: %s"),
+ elf_errmsg (-1));
+ goto err_elf_close;
+ }
+
+ if (dyn->d_tag == DT_TEXTREL
+ || (dyn->d_tag == DT_FLAGS
+ && (dyn->d_un.d_val & DF_TEXTREL) != 0))
+ have_textrel = true;
+ }
+ }
+ break;
+
+ case SHT_SYMTAB:
+ symscn = scn;
+ break;
+ }
+ }
+
+ if (!have_textrel)
+ {
+ error (0, 0, gettext ("no text relocations reported in '%s'"), fname);
+ goto err_elf_close;
+ }
+
+ int fd2 = -1;
+ Elf *elf2 = NULL;
+ /* Get the address ranges for the loaded segments. */
+ size_t nsegments_max = 10;
+ size_t nsegments = 0;
+ struct segments *segments
+ = (struct segments *) malloc (nsegments_max * sizeof (segments[0]));
+ if (segments == NULL)
+ error (1, errno, gettext ("while reading ELF file"));
+
+ size_t phnum;
+ if (elf_getphdrnum (elf, &phnum) != 0)
+ error (1, 0, gettext ("cannot get program header count: %s"),
+ elf_errmsg (-1));
+
+
+ for (size_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
+ if (phdr == NULL)
+ {
+ error (0, 0,
+ gettext ("cannot get program header index at offset %zd: %s"),
+ i, elf_errmsg (-1));
+ result = 1;
+ goto next;
+ }
+
+ if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0)
+ {
+ if (nsegments == nsegments_max)
+ {
+ nsegments_max *= 2;
+ segments
+ = (struct segments *) realloc (segments,
+ nsegments_max
+ * sizeof (segments[0]));
+ if (segments == NULL)
+ {
+ error (0, 0, gettext ("\
+cannot get program header index at offset %zd: %s"),
+ i, elf_errmsg (-1));
+ result = 1;
+ goto next;
+ }
+ }
+
+ segments[nsegments].from = phdr->p_vaddr;
+ segments[nsegments].to = phdr->p_vaddr + phdr->p_memsz;
+ ++nsegments;
+ }
+ }
+
+ if (nsegments > 0)
+ {
+
+ Dwarf *dw = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
+ /* Look for debuginfo files if the information is not the in
+ opened file itself. This makes only sense if the input file
+ is specified with an absolute path. */
+ if (dw == NULL && fname[0] == '/')
+ {
+ size_t debuginfo_rootlen = strlen (debuginfo_root);
+ char *difname = (char *) alloca (rootdir_len + debuginfo_rootlen
+ + fname_len + 8);
+ strcpy (mempcpy (stpcpy (mempcpy (mempcpy (difname, rootdir,
+ rootdir_len),
+ debuginfo_root,
+ debuginfo_rootlen),
+ "/"),
+ fname, fname_len),
+ ".debug");
+
+ fd2 = open (difname, O_RDONLY);
+ if (fd2 != -1
+ && (elf2 = elf_begin (fd2, ELF_C_READ_MMAP, NULL)) != NULL)
+ dw = dwarf_begin_elf (elf2, DWARF_C_READ, NULL);
+ }
+
+ /* Look at all relocations and determine which modify
+ write-protected segments. */
+ scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ /* Handle the section if it is a symbol table. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr == NULL)
+ {
+ error (0, 0,
+ gettext ("cannot get section header of section %zu: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+ result = 1;
+ goto next;
+ }
+
+ if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+ && symscn == NULL)
+ {
+ symscn = elf_getscn (elf, shdr->sh_link);
+ if (symscn == NULL)
+ {
+ error (0, 0, gettext ("\
+cannot get symbol table section %zu in '%s': %s"),
+ (size_t) shdr->sh_link, fname, elf_errmsg (-1));
+ result = 1;
+ goto next;
+ }
+ }
+
+ if (shdr->sh_type == SHT_REL)
+ {
+ Elf_Data *data = elf_getdata (scn, NULL);
+
+ for (int cnt = 0;
+ (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
+ ++cnt)
+ {
+ GElf_Rel rel_mem;
+ GElf_Rel *rel = gelf_getrel (data, cnt, &rel_mem);
+ if (rel == NULL)
+ {
+ error (0, 0, gettext ("\
+cannot get relocation at index %d in section %zu in '%s': %s"),
+ cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
+ result = 1;
+ goto next;
+ }
+
+ check_rel (nsegments, segments, rel->r_offset, elf,
+ symscn, dw, fname, more_than_one, &knownsrcs);
+ }
+ }
+ else if (shdr->sh_type == SHT_RELA)
+ {
+ Elf_Data *data = elf_getdata (scn, NULL);
+
+ for (int cnt = 0;
+ (size_t) cnt < shdr->sh_size / shdr->sh_entsize;
+ ++cnt)
+ {
+ GElf_Rela rela_mem;
+ GElf_Rela *rela = gelf_getrela (data, cnt, &rela_mem);
+ if (rela == NULL)
+ {
+ error (0, 0, gettext ("\
+cannot get relocation at index %d in section %zu in '%s': %s"),
+ cnt, elf_ndxscn (scn), fname, elf_errmsg (-1));
+ result = 1;
+ goto next;
+ }
+
+ check_rel (nsegments, segments, rela->r_offset, elf,
+ symscn, dw, fname, more_than_one, &knownsrcs);
+ }
+ }
+ }
+
+ dwarf_end (dw);
+ }
+
+ next:
+ elf_end (elf);
+ elf_end (elf2);
+ close (fd);
+ if (fd2 != -1)
+ close (fd2);
+
+ free (segments);
+ tdestroy (knownsrcs, noop);
+
+ return result;
+}
+
+
+static int
+ptrcompare (const void *p1, const void *p2)
+{
+ if ((uintptr_t) p1 < (uintptr_t) p2)
+ return -1;
+ if ((uintptr_t) p1 > (uintptr_t) p2)
+ return 1;
+ return 0;
+}
+
+
+static void
+check_rel (size_t nsegments, struct segments segments[nsegments],
+ GElf_Addr addr, Elf *elf, Elf_Scn *symscn, Dwarf *dw,
+ const char *fname, bool more_than_one, void **knownsrcs)
+{
+ for (size_t cnt = 0; cnt < nsegments; ++cnt)
+ if (segments[cnt].from <= addr && segments[cnt].to > addr)
+ {
+ Dwarf_Die die_mem;
+ Dwarf_Die *die;
+ Dwarf_Line *line;
+ const char *src;
+
+ if (more_than_one)
+ printf ("%s: ", fname);
+
+ if ((die = dwarf_addrdie (dw, addr, &die_mem)) != NULL
+ && (line = dwarf_getsrc_die (die, addr)) != NULL
+ && (src = dwarf_linesrc (line, NULL, NULL)) != NULL)
+ {
+ /* There can be more than one relocation against one file.
+ Try to avoid multiple messages. And yes, the code uses
+ pointer comparison. */
+ if (tfind (src, knownsrcs, ptrcompare) == NULL)
+ {
+ printf (gettext ("%s not compiled with -fpic/-fPIC\n"), src);
+ tsearch (src, knownsrcs, ptrcompare);
+ }
+ return;
+ }
+ else
+ {
+ /* At least look at the symbol table to see which function
+ the modified address is in. */
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
+ if (shdr != NULL)
+ {
+ GElf_Addr lowaddr = 0;
+ int lowidx = -1;
+ GElf_Addr highaddr = ~0ul;
+ int highidx = -1;
+ GElf_Sym sym_mem;
+ GElf_Sym *sym;
+
+ for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize;
+ ++i)
+ {
+ sym = gelf_getsym (symdata, i, &sym_mem);
+ if (sym == NULL)
+ continue;
+
+ if (sym->st_value < addr && sym->st_value > lowaddr)
+ {
+ lowaddr = sym->st_value;
+ lowidx = i;
+ }
+ if (sym->st_value > addr && sym->st_value < highaddr)
+ {
+ highaddr = sym->st_value;
+ highidx = i;
+ }
+ }
+
+ if (lowidx != -1)
+ {
+ sym = gelf_getsym (symdata, lowidx, &sym_mem);
+ assert (sym != NULL);
+
+ const char *lowstr = elf_strptr (elf, shdr->sh_link,
+ sym->st_name);
+
+ if (sym->st_value + sym->st_size > addr)
+ {
+ /* It is this function. */
+ if (tfind (lowstr, knownsrcs, ptrcompare) == NULL)
+ {
+ printf (gettext ("\
+the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
+ lowstr);
+ tsearch (lowstr, knownsrcs, ptrcompare);
+ }
+ }
+ else if (highidx == -1)
+ printf (gettext ("\
+the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
+ lowstr);
+ else
+ {
+ sym = gelf_getsym (symdata, highidx, &sym_mem);
+ assert (sym != NULL);
+
+ printf (gettext ("\
+either the file containing the function '%s' or the file containing the function '%s' is not compiled with -fpic/-fPIC\n"),
+ lowstr, elf_strptr (elf, shdr->sh_link,
+ sym->st_name));
+ }
+ return;
+ }
+ else if (highidx != -1)
+ {
+ sym = gelf_getsym (symdata, highidx, &sym_mem);
+ assert (sym != NULL);
+
+ printf (gettext ("\
+the file containing the function '%s' might not be compiled with -fpic/-fPIC\n"),
+ elf_strptr (elf, shdr->sh_link, sym->st_name));
+ return;
+ }
+ }
+ }
+
+ printf (gettext ("\
+a relocation modifies memory at offset %llu in a write-protected segment\n"),
+ (unsigned long long int) addr);
+ break;
+ }
+}
+
+
+#include "debugpred.h"
diff --git a/src/make-debug-archive.in b/src/make-debug-archive.in
new file mode 100644
index 0000000..c3fcbce
--- /dev/null
+++ b/src/make-debug-archive.in
@@ -0,0 +1,132 @@
+#!/bin/sh
+#
+# Script to make an offline archive for debugging with libdwfl-based tools.
+#
+# make-debug-archive ARCHIVE {options}
+# make-debug-archive --kernel [--force] [RELEASE]
+#
+# Valid options are those listed under 'Input selection options'
+# by running @UNSTRIP@ --help.
+#
+# The archive installed by --kernel be used automatically by -K.
+# An offline archive can be used via -e in any tool that accepts those options.
+#
+
+UNSTRIP=${UNSTRIP:-@UNSTRIP@}
+AR=${AR:-@AR@}
+SUDO=${SUDO:-/usr/bin/sudo}
+
+LS=/bin/ls
+RM=/bin/rm
+MV=/bin/mv
+MKDIR=/bin/mkdir
+XARGS=/usr/bin/xargs
+
+outdir=${TMPDIR:-/tmp}/debugar$$
+
+usage()
+{
+ echo "Usage: $0 ARCHIVE {options}"
+ echo " or: $0 --kernel [--sudo] [--force] [RELEASE]"
+ echo
+ echo "Valid options are listed under 'Input selection options'"
+ echo "when running: $UNSTRIP --help"
+ echo
+ echo "The --kernel form updates the file used by -K if the"
+ echo "kernel installation has changed, or always with --force."
+ echo "With --sudo, touches the installed file via $SUDO."
+}
+
+fatal_usage()
+{
+ usage >&2
+ exit 2
+}
+
+script_version()
+{
+ echo "`basename $0` (@PACKAGE_NAME@) @PACKAGE_VERSION@"
+ echo "Copyright (C) 2007 Red Hat, Inc."
+ echo "This is free software; see the source for copying conditions."
+ echo "There is NO warranty; not even for MERCHANTABILITY or"
+ echo "FITNESS FOR A PARTICULAR PURPOSE."
+ echo "Written by Roland McGrath."
+}
+
+sudo=
+kernel=no
+force_kernel=no
+while [ $# -gt 0 ]; do
+ case "x$1" in
+ x--help) usage; exit 0 ;;
+ x--version) script_version; exit 0 ;;
+ x--kernel) kernel=yes ;;
+ x--force) force_kernel=yes ;;
+ x--sudo) sudo=$SUDO ;;
+ *) break ;;
+ esac
+ shift
+done
+
+if [ $kernel = no ] && [ $force_kernel = yes -o -n "$sudo" ]; then
+ usage
+fi
+
+if [ $kernel = yes ]; then
+ if [ $# -eq 0 ]; then
+ release=`uname -r`
+ elif [ $# -eq 1 ]; then
+ release=$1
+ else
+ fatal_usage
+ fi
+
+ dir=/usr/lib/debug/lib/modules/$release
+ archive=$dir/debug.a
+ dep=/lib/modules/$release/modules.dep
+
+ if [ ! -d $dir ]; then
+ echo >&2 "$0: $dir not installed"
+ exit 1
+ fi
+
+ # Without --force, bail if the kernel installation is not newer.
+ # This file is normally touched by installing new kernels or modules.
+ if [ $force_kernel = no -a "$archive" -nt "$dep" ]; then
+ exit 0
+ fi
+
+ # We have to kill the old one first, because our own -K would use it.
+ [ ! -e "$archive" ] || $sudo $RM -f "$archive" || exit
+
+ set "$archive" "-K$release"
+fi
+
+if [ $# -lt 2 ]; then
+ fatal_usage
+fi
+
+archive="$1"
+shift
+
+case "$archive" in
+/*) ;;
+*) archive="`/bin/pwd`/$archive" ;;
+esac
+
+if [ -z "$sudo" ]; then
+ new_archive="$archive.new"
+else
+ new_archive="$outdir.a"
+fi
+
+$RM -f "$new_archive" || exit
+
+trap '$RM -rf "$outdir" "$new_archive"' 0 1 2 15
+
+$MKDIR "$outdir" &&
+$UNSTRIP -d "$outdir" -m -a -R "$@" &&
+(cd "$outdir" && $LS | $XARGS $AR cq "$new_archive") &&
+$sudo $MV -f "$new_archive" "$archive"
+
+exit
diff --git a/src/nm.c b/src/nm.c
new file mode 100644
index 0000000..969c6d3
--- /dev/null
+++ b/src/nm.c
@@ -0,0 +1,1599 @@
+/* Print symbol information from ELF file in human-readable form.
+ Copyright (C) 2000-2008, 2009, 2011, 2012, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2000.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ar.h>
+#include <argp.h>
+#include <assert.h>
+#include <ctype.h>
+#include <dwarf.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <libdw.h>
+#include <libintl.h>
+#include <locale.h>
+#include <obstack.h>
+#include <search.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libeu.h>
+#include <system.h>
+#include <color.h>
+#include <printversion.h>
+#include "../libebl/libeblP.h"
+#include "../libdwfl/libdwflP.h"
+
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+
+/* Values for the parameters which have no short form. */
+#define OPT_DEFINED 0x100
+#define OPT_MARK_SPECIAL 0x101
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Output selection:"), 0 },
+ { "debug-syms", 'a', NULL, 0, N_("Display debugger-only symbols"), 0 },
+ { "defined-only", OPT_DEFINED, NULL, 0, N_("Display only defined symbols"),
+ 0 },
+ { "dynamic", 'D', NULL, 0,
+ N_("Display dynamic symbols instead of normal symbols"), 0 },
+ { "extern-only", 'g', NULL, 0, N_("Display only external symbols"), 0 },
+ { "undefined-only", 'u', NULL, 0, N_("Display only undefined symbols"), 0 },
+ { "print-armap", 's', NULL, 0,
+ N_("Include index for symbols from archive members"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output format:"), 0 },
+ { "print-file-name", 'A', NULL, 0,
+ N_("Print name of the input file before every symbol"), 0 },
+ { NULL, 'o', NULL, OPTION_HIDDEN, "Same as -A", 0 },
+ { "format", 'f', "FORMAT", 0,
+ N_("Use the output format FORMAT. FORMAT can be `bsd', `sysv' or `posix'. The default is `sysv'"),
+ 0 },
+ { NULL, 'B', NULL, 0, N_("Same as --format=bsd"), 0 },
+ { "portability", 'P', NULL, 0, N_("Same as --format=posix"), 0 },
+ { "radix", 't', "RADIX", 0, N_("Use RADIX for printing symbol values"), 0 },
+ { "mark-special", OPT_MARK_SPECIAL, NULL, 0, N_("Mark special symbols"), 0 },
+ { "mark-weak", OPT_MARK_SPECIAL, NULL, OPTION_HIDDEN, "", 0 },
+ { "print-size", 'S', NULL, 0, N_("Print size of defined symbols"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output options:"), 0 },
+ { "numeric-sort", 'n', NULL, 0, N_("Sort symbols numerically by address"),
+ 0 },
+ { "no-sort", 'p', NULL, 0, N_("Do not sort the symbols"), 0 },
+ { "reverse-sort", 'r', NULL, 0, N_("Reverse the sense of the sort"), 0 },
+#ifdef USE_DEMANGLE
+ { "demangle", 'C', NULL, 0,
+ N_("Decode low-level symbol names into source code names"), 0 },
+#endif
+ { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("List symbols from FILEs (a.out by default).");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("[FILE...]");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Parser children. */
+static struct argp_child argp_children[] =
+ {
+ { &color_argp, 0, N_("Output formatting"), 2 },
+ { NULL, 0, NULL, 0}
+ };
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, argp_children, NULL, NULL
+};
+
+
+/* Print symbols in file named FNAME. */
+static int process_file (const char *fname, bool more_than_one);
+
+/* Handle content of archive. */
+static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
+ const char *suffix);
+
+/* Handle ELF file. */
+static int handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
+ const char *suffix);
+
+
+#define INTERNAL_ERROR(fname) \
+ error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s): %s"), \
+ fname, __LINE__, PACKAGE_VERSION, elf_errmsg (-1))
+
+
+/* Internal representation of symbols. */
+typedef struct GElf_SymX
+{
+ GElf_Sym sym;
+ Elf32_Word xndx;
+ char *where;
+} GElf_SymX;
+
+
+/* User-selectable options. */
+
+/* The selected output format. */
+static enum
+{
+ format_sysv = 0,
+ format_bsd,
+ format_posix
+} format;
+
+/* Print defined, undefined, or both? */
+static bool hide_undefined;
+static bool hide_defined;
+
+/* Print local symbols also? */
+static bool hide_local;
+
+/* Nonzero if full filename should precede every symbol. */
+static bool print_file_name;
+
+/* If true print size of defined symbols in BSD format. */
+static bool print_size;
+
+/* If true print archive index. */
+static bool print_armap;
+
+/* If true reverse sorting. */
+static bool reverse_sort;
+
+#ifdef USE_DEMANGLE
+/* If true demangle symbols. */
+static bool demangle;
+#endif
+
+/* Type of the section we are printing. */
+static GElf_Word symsec_type = SHT_SYMTAB;
+
+/* Sorting selection. */
+static enum
+{
+ sort_name = 0,
+ sort_numeric,
+ sort_nosort
+} sort;
+
+/* Radix for printed numbers. */
+static enum
+{
+ radix_hex = 0,
+ radix_decimal,
+ radix_octal
+} radix;
+
+/* If nonzero mark special symbols:
+ - weak symbols are distinguished from global symbols by adding
+ a `*' after the identifying letter for the symbol class and type.
+ - TLS symbols are distinguished from normal symbols by adding
+ a '@' after the identifying letter for the symbol class and type. */
+static bool mark_special;
+
+
+int
+main (int argc, char *argv[])
+{
+ int remaining;
+ int result = 0;
+
+ /* We use no threads here which can interfere with handling a stream. */
+ (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ (void) textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Tell the library which version we are expecting. */
+ (void) elf_version (EV_CURRENT);
+
+ if (remaining == argc)
+ /* The user didn't specify a name so we use a.out. */
+ result = process_file ("a.out", false);
+ else
+ {
+ /* Process all the remaining files. */
+ const bool more_than_one = remaining + 1 < argc;
+
+ do
+ result |= process_file (argv[remaining], more_than_one);
+ while (++remaining < argc);
+ }
+
+ return result;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg,
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case 'a':
+ /* XXX */
+ break;
+
+#ifdef USE_DEMANGLE
+ case 'C':
+ demangle = true;
+ break;
+#endif
+
+ case 'f':
+ if (strcmp (arg, "bsd") == 0)
+ format = format_bsd;
+ else if (strcmp (arg, "posix") == 0)
+ format = format_posix;
+ else
+ /* Be bug compatible. The BFD implementation also defaulted to
+ using the SysV format if nothing else matches. */
+ format = format_sysv;
+ break;
+
+ case 'g':
+ hide_local = true;
+ break;
+
+ case 'n':
+ sort = sort_numeric;
+ break;
+
+ case 'p':
+ sort = sort_nosort;
+ break;
+
+ case 't':
+ if (strcmp (arg, "10") == 0 || strcmp (arg, "d") == 0)
+ radix = radix_decimal;
+ else if (strcmp (arg, "8") == 0 || strcmp (arg, "o") == 0)
+ radix = radix_octal;
+ else
+ radix = radix_hex;
+ break;
+
+ case 'u':
+ hide_undefined = false;
+ hide_defined = true;
+ break;
+
+ case 'A':
+ case 'o':
+ print_file_name = true;
+ break;
+
+ case 'B':
+ format = format_bsd;
+ break;
+
+ case 'D':
+ symsec_type = SHT_DYNSYM;
+ break;
+
+ case 'P':
+ format = format_posix;
+ break;
+
+ case OPT_DEFINED:
+ hide_undefined = true;
+ hide_defined = false;
+ break;
+
+ case OPT_MARK_SPECIAL:
+ mark_special = true;
+ break;
+
+ case 'S':
+ print_size = true;
+ break;
+
+ case 's':
+ print_armap = true;
+ break;
+
+ case 'r':
+ reverse_sort = true;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+/* Open the file and determine the type. */
+static int
+process_file (const char *fname, bool more_than_one)
+{
+ /* Open the file. */
+ int fd = open (fname, O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, gettext ("cannot open '%s'"), fname);
+ return 1;
+ }
+
+ /* Now get the ELF descriptor. */
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (elf != NULL)
+ {
+ if (elf_kind (elf) == ELF_K_ELF)
+ {
+ int result = handle_elf (fd, elf, more_than_one ? "" : NULL,
+ fname, NULL);
+
+ if (elf_end (elf) != 0)
+ INTERNAL_ERROR (fname);
+
+ if (close (fd) != 0)
+ error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
+
+ return result;
+ }
+ else if (elf_kind (elf) == ELF_K_AR)
+ {
+ int result = handle_ar (fd, elf, NULL, fname, NULL);
+
+ if (elf_end (elf) != 0)
+ INTERNAL_ERROR (fname);
+
+ if (close (fd) != 0)
+ error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
+
+ return result;
+ }
+
+ /* We cannot handle this type. Close the descriptor anyway. */
+ if (elf_end (elf) != 0)
+ INTERNAL_ERROR (fname);
+ }
+
+ error (0, 0, gettext ("%s: File format not recognized"), fname);
+
+ return 1;
+}
+
+
+static int
+handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
+ const char *suffix)
+{
+ size_t fname_len = strlen (fname) + 1;
+ size_t prefix_len = prefix != NULL ? strlen (prefix) : 0;
+ char new_prefix[prefix_len + fname_len + 2];
+ size_t suffix_len = suffix != NULL ? strlen (suffix) : 0;
+ char new_suffix[suffix_len + 2];
+ Elf *subelf;
+ Elf_Cmd cmd = ELF_C_READ_MMAP;
+ int result = 0;
+
+ char *cp = new_prefix;
+ if (prefix != NULL)
+ cp = stpcpy (cp, prefix);
+ cp = stpcpy (cp, fname);
+ stpcpy (cp, "[");
+
+ cp = new_suffix;
+ if (suffix != NULL)
+ cp = stpcpy (cp, suffix);
+ stpcpy (cp, "]");
+
+ /* First print the archive index if this is wanted. */
+ if (print_armap)
+ {
+ Elf_Arsym *arsym = elf_getarsym (elf, NULL);
+
+ if (arsym != NULL)
+ {
+ Elf_Arhdr *arhdr = NULL;
+ size_t arhdr_off = 0; /* Note: 0 is no valid offset. */
+
+ fputs_unlocked (gettext("\nArchive index:\n"), stdout);
+
+ while (arsym->as_off != 0)
+ {
+ if (arhdr_off != arsym->as_off
+ && (elf_rand (elf, arsym->as_off) != arsym->as_off
+ || (subelf = elf_begin (fd, cmd, elf)) == NULL
+ || (arhdr = elf_getarhdr (subelf)) == NULL))
+ {
+ error (0, 0, gettext ("invalid offset %zu for symbol %s"),
+ arsym->as_off, arsym->as_name);
+ break;
+ }
+
+ printf (gettext ("%s in %s\n"), arsym->as_name, arhdr->ar_name);
+
+ ++arsym;
+ }
+
+ if (elf_rand (elf, SARMAG) != SARMAG)
+ {
+ error (0, 0,
+ gettext ("cannot reset archive offset to beginning"));
+ return 1;
+ }
+ }
+ }
+
+ /* Process all the files contained in the archive. */
+ while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
+ {
+ /* The the header for this element. */
+ Elf_Arhdr *arhdr = elf_getarhdr (subelf);
+
+ /* Skip over the index entries. */
+ if (strcmp (arhdr->ar_name, "/") != 0
+ && strcmp (arhdr->ar_name, "//") != 0
+ && strcmp (arhdr->ar_name, "/SYM64/") != 0)
+ {
+ if (elf_kind (subelf) == ELF_K_ELF)
+ result |= handle_elf (fd, subelf, new_prefix, arhdr->ar_name,
+ new_suffix);
+ else if (elf_kind (subelf) == ELF_K_AR)
+ result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name,
+ new_suffix);
+ else
+ {
+ error (0, 0, gettext ("%s%s%s: file format not recognized"),
+ new_prefix, arhdr->ar_name, new_suffix);
+ result = 1;
+ }
+ }
+
+ /* Get next archive element. */
+ cmd = elf_next (subelf);
+ if (elf_end (subelf) != 0)
+ INTERNAL_ERROR (fname);
+ }
+
+ return result;
+}
+
+
+/* Mapping of radix and binary class to length. */
+static const int length_map[2][3] =
+{
+ [ELFCLASS32 - 1] =
+ {
+ [radix_hex] = 8,
+ [radix_decimal] = 10,
+ [radix_octal] = 11
+ },
+ [ELFCLASS64 - 1] =
+ {
+ [radix_hex] = 16,
+ [radix_decimal] = 20,
+ [radix_octal] = 22
+ }
+};
+
+
+static int
+global_compare (const void *p1, const void *p2)
+{
+ const Dwarf_Global *g1 = (const Dwarf_Global *) p1;
+ const Dwarf_Global *g2 = (const Dwarf_Global *) p2;
+
+ return strcmp (g1->name, g2->name);
+}
+
+
+static void *global_root;
+
+
+static int
+get_global (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global,
+ void *arg __attribute__ ((unused)))
+{
+ tsearch (memcpy (xmalloc (sizeof (Dwarf_Global)), global,
+ sizeof (Dwarf_Global)),
+ &global_root, global_compare);
+
+ return DWARF_CB_OK;
+}
+
+
+struct local_name
+{
+ const char *name;
+ const char *file;
+ Dwarf_Word lineno;
+ Dwarf_Addr lowpc;
+ Dwarf_Addr highpc;
+};
+
+
+static int
+local_compare (const void *p1, const void *p2)
+{
+ struct local_name *g1 = (struct local_name *) p1;
+ struct local_name *g2 = (struct local_name *) p2;
+ int result;
+
+ result = strcmp (g1->name, g2->name);
+ if (result == 0)
+ {
+ if (g1->lowpc <= g2->lowpc && g1->highpc >= g2->highpc)
+ {
+ /* g2 is contained in g1. Update the data. */
+ g2->lowpc = g1->lowpc;
+ g2->highpc = g1->highpc;
+ result = 0;
+ }
+ else if (g2->lowpc <= g1->lowpc && g2->highpc >= g1->highpc)
+ {
+ /* g1 is contained in g2. Update the data. */
+ g1->lowpc = g2->lowpc;
+ g1->highpc = g2->highpc;
+ result = 0;
+ }
+ else
+ result = g1->lowpc < g2->lowpc ? -1 : 1;
+ }
+
+ return result;
+}
+
+
+static int
+get_var_range (Dwarf_Die *die, Dwarf_Word *lowpc, Dwarf_Word *highpc)
+{
+ Dwarf_Attribute locattr_mem;
+ Dwarf_Attribute *locattr = dwarf_attr (die, DW_AT_location, &locattr_mem);
+ if (locattr == NULL)
+ return 1;
+
+ Dwarf_Op *loc;
+ size_t nloc;
+ if (dwarf_getlocation (locattr, &loc, &nloc) != 0)
+ return 1;
+
+ /* Interpret the location expressions. */
+ // XXX For now just the simple one:
+ if (nloc == 1 && loc[0].atom == DW_OP_addr)
+ {
+ *lowpc = *highpc = loc[0].number;
+ return 0;
+ }
+
+ return 1;
+}
+
+
+
+static void *local_root;
+
+
+static void
+get_local_names (Dwarf *dbg)
+{
+ Dwarf_Off offset = 0;
+ Dwarf_Off old_offset;
+ size_t hsize;
+
+ while (dwarf_nextcu (dbg, old_offset = offset, &offset, &hsize, NULL, NULL,
+ NULL) == 0)
+ {
+ Dwarf_Die cudie_mem;
+ Dwarf_Die *cudie = dwarf_offdie (dbg, old_offset + hsize, &cudie_mem);
+
+ /* If we cannot get the CU DIE there is no need to go on with
+ this CU. */
+ if (cudie == NULL)
+ continue;
+ /* This better be a CU DIE. */
+ if (dwarf_tag (cudie) != DW_TAG_compile_unit)
+ continue;
+
+ /* Get the line information. */
+ Dwarf_Files *files;
+ size_t nfiles;
+ if (dwarf_getsrcfiles (cudie, &files, &nfiles) != 0)
+ continue;
+
+ Dwarf_Die die_mem;
+ Dwarf_Die *die = &die_mem;
+ if (dwarf_child (cudie, die) == 0)
+ /* Iterate over all immediate children of the CU DIE. */
+ do
+ {
+ int tag = dwarf_tag (die);
+ if (tag != DW_TAG_subprogram && tag != DW_TAG_variable)
+ continue;
+
+ /* We are interested in five attributes: name, decl_file,
+ decl_line, low_pc, and high_pc. */
+ Dwarf_Attribute attr_mem;
+ Dwarf_Attribute *attr = dwarf_attr (die, DW_AT_name, &attr_mem);
+ const char *name = dwarf_formstring (attr);
+ if (name == NULL)
+ continue;
+
+ Dwarf_Word fileidx;
+ attr = dwarf_attr (die, DW_AT_decl_file, &attr_mem);
+ if (dwarf_formudata (attr, &fileidx) != 0 || fileidx >= nfiles)
+ continue;
+
+ Dwarf_Word lineno;
+ attr = dwarf_attr (die, DW_AT_decl_line, &attr_mem);
+ if (dwarf_formudata (attr, &lineno) != 0 || lineno == 0)
+ continue;
+
+ Dwarf_Addr lowpc;
+ Dwarf_Addr highpc;
+ if (tag == DW_TAG_subprogram)
+ {
+ if (dwarf_lowpc (die, &lowpc) != 0
+ || dwarf_highpc (die, &highpc) != 0)
+ continue;
+ }
+ else
+ {
+ if (get_var_range (die, &lowpc, &highpc) != 0)
+ continue;
+ }
+
+ /* We have all the information. Create a record. */
+ struct local_name *newp
+ = (struct local_name *) xmalloc (sizeof (*newp));
+ newp->name = name;
+ newp->file = dwarf_filesrc (files, fileidx, NULL, NULL);
+ newp->lineno = lineno;
+ newp->lowpc = lowpc;
+ newp->highpc = highpc;
+
+ /* Check whether a similar local_name is already in the
+ cache. That should not happen. But if it does, we
+ don't want to leak memory. */
+ struct local_name **tres = tsearch (newp, &local_root,
+ local_compare);
+ if (tres == NULL)
+ error (EXIT_FAILURE, errno,
+ gettext ("cannot create search tree"));
+ else if (*tres != newp)
+ free (newp);
+ }
+ while (dwarf_siblingof (die, die) == 0);
+ }
+}
+
+/* Do elf_strptr, but return a backup string and never NULL. */
+static const char *
+sym_name (Elf *elf, GElf_Word strndx, GElf_Word st_name, char buf[], size_t n)
+{
+ const char *symstr = elf_strptr (elf, strndx, st_name);
+ if (symstr == NULL)
+ {
+ snprintf (buf, n, "[invalid st_name %#" PRIx32 "]", st_name);
+ symstr = buf;
+ }
+ return symstr;
+}
+
+/* Show symbols in SysV format. */
+static void
+show_symbols_sysv (Ebl *ebl, GElf_Word strndx, const char *fullname,
+ GElf_SymX *syms, size_t nsyms, int longest_name,
+ int longest_where)
+{
+ size_t shnum;
+ if (elf_getshdrnum (ebl->elf, &shnum) < 0)
+ INTERNAL_ERROR (fullname);
+
+ bool scnnames_malloced = shnum * sizeof (const char *) > 128 * 1024;
+ const char **scnnames;
+ if (scnnames_malloced)
+ scnnames = (const char **) xmalloc (sizeof (const char *) * shnum);
+ else
+ scnnames = (const char **) alloca (sizeof (const char *) * shnum);
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ /* Cache the section names. */
+ Elf_Scn *scn = NULL;
+ size_t cnt = 1;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+
+ assert (elf_ndxscn (scn) == cnt);
+ cnt++;
+
+ char *name = elf_strptr (ebl->elf, shstrndx,
+ gelf_getshdr (scn, &shdr_mem)->sh_name);
+ if (unlikely (name == NULL))
+ {
+ const size_t bufsz = sizeof "[invalid sh_name 0x12345678]";
+ name = alloca (bufsz);
+ snprintf (name, bufsz, "[invalid sh_name %#" PRIx32 "]",
+ gelf_getshdr (scn, &shdr_mem)->sh_name);
+ }
+ scnnames[elf_ndxscn (scn)] = name;
+ }
+
+ int digits = length_map[gelf_getclass (ebl->elf) - 1][radix];
+
+ /* We always print this prolog. */
+ printf (gettext ("\n\nSymbols from %s:\n\n"), fullname);
+
+ /* The header line. */
+ printf (gettext ("%*s%-*s %-*s Class Type %-*s %*s Section\n\n"),
+ print_file_name ? (int) strlen (fullname) + 1: 0, "",
+ longest_name, sgettext ("sysv|Name"),
+ /* TRANS: the "sysv|" parts makes the string unique. */
+ digits, sgettext ("sysv|Value"),
+ /* TRANS: the "sysv|" parts makes the string unique. */
+ digits, sgettext ("sysv|Size"),
+ /* TRANS: the "sysv|" parts makes the string unique. */
+ longest_where, sgettext ("sysv|Line"));
+
+#ifdef USE_DEMANGLE
+ size_t demangle_buffer_len = 0;
+ char *demangle_buffer = NULL;
+#endif
+
+ /* Iterate over all symbols. */
+ for (cnt = 1; cnt < nsyms; ++cnt)
+ {
+ /* In this format SECTION entries are not printed. */
+ if (GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_SECTION)
+ continue;
+
+ char symstrbuf[50];
+ const char *symstr = sym_name (ebl->elf, strndx, syms[cnt].sym.st_name,
+ symstrbuf, sizeof symstrbuf);
+
+#ifdef USE_DEMANGLE
+ /* Demangle if necessary. Require GNU v3 ABI by the "_Z" prefix. */
+ if (demangle && symstr[0] == '_' && symstr[1] == 'Z')
+ {
+ int status = -1;
+ char *dmsymstr = __cxa_demangle (symstr, demangle_buffer,
+ &demangle_buffer_len, &status);
+
+ if (status == 0)
+ symstr = dmsymstr;
+ }
+#endif
+
+ char symbindbuf[50];
+ char symtypebuf[50];
+ char secnamebuf[1024];
+ char addressbuf[(64 + 2) / 3 + 1];
+ char sizebuf[(64 + 2) / 3 + 1];
+
+ /* If we have to precede the line with the file name. */
+ if (print_file_name)
+ {
+ fputs_unlocked (fullname, stdout);
+ putchar_unlocked (':');
+ }
+
+ /* Covert the address. */
+ if (syms[cnt].sym.st_shndx == SHN_UNDEF)
+ addressbuf[0] = sizebuf[0] = '\0';
+ else
+ {
+ snprintf (addressbuf, sizeof (addressbuf),
+ (radix == radix_hex ? "%0*" PRIx64
+ : (radix == radix_decimal ? "%0*" PRId64
+ : "%0*" PRIo64)),
+ digits, syms[cnt].sym.st_value);
+ snprintf (sizebuf, sizeof (sizebuf),
+ (radix == radix_hex ? "%0*" PRIx64
+ : (radix == radix_decimal ? "%0*" PRId64
+ : "%0*" PRIo64)),
+ digits, syms[cnt].sym.st_size);
+ }
+
+ /* Print the actual string. */
+ printf ("%-*s|%s|%-6s|%-8s|%s|%*s|%s\n",
+ longest_name, symstr, addressbuf,
+ ebl_symbol_binding_name (ebl,
+ GELF_ST_BIND (syms[cnt].sym.st_info),
+ symbindbuf, sizeof (symbindbuf)),
+ ebl_symbol_type_name (ebl, GELF_ST_TYPE (syms[cnt].sym.st_info),
+ symtypebuf, sizeof (symtypebuf)),
+ sizebuf, longest_where, syms[cnt].where,
+ ebl_section_name (ebl, syms[cnt].sym.st_shndx, syms[cnt].xndx,
+ secnamebuf, sizeof (secnamebuf), scnnames,
+ shnum));
+ }
+
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
+
+ if (scnnames_malloced)
+ free (scnnames);
+}
+
+
+static char
+class_type_char (Elf *elf, const GElf_Ehdr *ehdr, GElf_Sym *sym)
+{
+ int local_p = GELF_ST_BIND (sym->st_info) == STB_LOCAL;
+
+ /* XXX Add support for architecture specific types and classes. */
+ if (sym->st_shndx == SHN_ABS)
+ return local_p ? 'a' : 'A';
+
+ if (sym->st_shndx == SHN_UNDEF)
+ /* Undefined symbols must be global. */
+ return 'U';
+
+ char result = "NDTSFBD "[GELF_ST_TYPE (sym->st_info)];
+
+ if (result == 'D')
+ {
+ /* Special handling: unique data symbols. */
+ if (ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX
+ && GELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE)
+ result = 'u';
+ else
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, sym->st_shndx),
+ &shdr_mem);
+ if (shdr != NULL)
+ {
+ if ((shdr->sh_flags & SHF_WRITE) == 0)
+ result = 'R';
+ else if (shdr->sh_type == SHT_NOBITS)
+ result = 'B';
+ }
+ }
+ }
+
+ return local_p ? tolower (result) : result;
+}
+
+
+static void
+show_symbols_bsd (Elf *elf, const GElf_Ehdr *ehdr, GElf_Word strndx,
+ const char *prefix, const char *fname, const char *fullname,
+ GElf_SymX *syms, size_t nsyms)
+{
+ int digits = length_map[gelf_getclass (elf) - 1][radix];
+
+ if (prefix != NULL && ! print_file_name)
+ printf ("\n%s:\n", fname);
+
+#ifdef USE_DEMANGLE
+ size_t demangle_buffer_len = 0;
+ char *demangle_buffer = NULL;
+#endif
+
+ /* Iterate over all symbols. */
+ for (size_t cnt = 0; cnt < nsyms; ++cnt)
+ {
+ char symstrbuf[50];
+ const char *symstr = sym_name (elf, strndx, syms[cnt].sym.st_name,
+ symstrbuf, sizeof symstrbuf);
+
+ /* Printing entries with a zero-length name makes the output
+ not very well parseable. Since these entries don't carry
+ much information we leave them out. */
+ if (symstr[0] == '\0')
+ continue;
+
+ /* We do not print the entries for files. */
+ if (GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_FILE)
+ continue;
+
+#ifdef USE_DEMANGLE
+ /* Demangle if necessary. Require GNU v3 ABI by the "_Z" prefix. */
+ if (demangle && symstr[0] == '_' && symstr[1] == 'Z')
+ {
+ int status = -1;
+ char *dmsymstr = __cxa_demangle (symstr, demangle_buffer,
+ &demangle_buffer_len, &status);
+
+ if (status == 0)
+ symstr = dmsymstr;
+ }
+#endif
+
+ /* If we have to precede the line with the file name. */
+ if (print_file_name)
+ {
+ fputs_unlocked (fullname, stdout);
+ putchar_unlocked (':');
+ }
+
+ bool is_tls = GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_TLS;
+ bool is_weak = GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK;
+ const char *marker = (mark_special
+ ? (is_tls ? "@" : (is_weak ? "*" : " ")) : "");
+
+ if (syms[cnt].sym.st_shndx == SHN_UNDEF)
+ {
+ const char *color = "";
+ if (color_mode)
+ {
+ if (is_tls)
+ color = color_undef_tls;
+ else if (is_weak)
+ color = color_undef_weak;
+ else
+ color = color_undef;
+ }
+
+ printf ("%*s %sU%s %s", digits, "", color, marker, symstr);
+ }
+ else
+ {
+ const char *color = "";
+ if (color_mode)
+ {
+ if (is_tls)
+ color = color_tls;
+ else if (is_weak)
+ color = color_weak;
+ else
+ color = color_symbol;
+ }
+ if (print_size && syms[cnt].sym.st_size != 0)
+ {
+#define HEXFMT "%6$s%2$0*1$" PRIx64 "%8$s %10$0*9$" PRIx64 " %7$s%3$c%4$s %5$s"
+#define DECFMT "%6$s%2$*1$" PRId64 "%8$s %10$*9$" PRId64 " %7$s%3$c%4$s %5$s"
+#define OCTFMT "%6$s%2$0*1$" PRIo64 "%8$s %10$0*9$" PRIo64 " %7$s%3$c%4$s %5$s"
+ printf ((radix == radix_hex ? HEXFMT
+ : (radix == radix_decimal ? DECFMT : OCTFMT)),
+ digits, syms[cnt].sym.st_value,
+ class_type_char (elf, ehdr, &syms[cnt].sym), marker,
+ symstr,
+ color_mode ? color_address : "",
+ color,
+ color_mode ? color_off : "",
+ digits, (uint64_t) syms[cnt].sym.st_size);
+#undef HEXFMT
+#undef DECFMT
+#undef OCTFMT
+ }
+ else
+ {
+#define HEXFMT "%6$s%2$0*1$" PRIx64 "%8$s %7$s%3$c%4$s %5$s"
+#define DECFMT "%6$s%2$*1$" PRId64 "%8$s %7$s%3$c%4$s %5$s"
+#define OCTFMT "%6$s%2$0*1$" PRIo64 "%8$s %7$s%3$c%4$s %5$s"
+ printf ((radix == radix_hex ? HEXFMT
+ : (radix == radix_decimal ? DECFMT : OCTFMT)),
+ digits, syms[cnt].sym.st_value,
+ class_type_char (elf, ehdr, &syms[cnt].sym), marker,
+ symstr,
+ color_mode ? color_address : "",
+ color,
+ color_mode ? color_off : "");
+#undef HEXFMT
+#undef DECFMT
+#undef OCTFMT
+ }
+ }
+
+ if (color_mode)
+ fputs_unlocked (color_off, stdout);
+ putchar_unlocked ('\n');
+ }
+
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
+}
+
+
+static void
+show_symbols_posix (Elf *elf, const GElf_Ehdr *ehdr, GElf_Word strndx,
+ const char *prefix, const char *fullname, GElf_SymX *syms,
+ size_t nsyms)
+{
+ if (prefix != NULL && ! print_file_name)
+ printf ("%s:\n", fullname);
+
+ int digits = length_map[gelf_getclass (elf) - 1][radix];
+
+#ifdef USE_DEMANGLE
+ size_t demangle_buffer_len = 0;
+ char *demangle_buffer = NULL;
+#endif
+
+ /* Iterate over all symbols. */
+ for (size_t cnt = 0; cnt < nsyms; ++cnt)
+ {
+ char symstrbuf[50];
+ const char *symstr = sym_name (elf, strndx, syms[cnt].sym.st_name,
+ symstrbuf, sizeof symstrbuf);
+
+ /* Printing entries with a zero-length name makes the output
+ not very well parseable. Since these entries don't carry
+ much information we leave them out. */
+ if (symstr[0] == '\0')
+ continue;
+
+#ifdef USE_DEMANGLE
+ /* Demangle if necessary. Require GNU v3 ABI by the "_Z" prefix. */
+ if (demangle && symstr[0] == '_' && symstr[1] == 'Z')
+ {
+ int status = -1;
+ char *dmsymstr = __cxa_demangle (symstr, demangle_buffer,
+ &demangle_buffer_len, &status);
+
+ if (status == 0)
+ symstr = dmsymstr;
+ }
+#endif
+
+ /* If we have to precede the line with the file name. */
+ if (print_file_name)
+ {
+ fputs_unlocked (fullname, stdout);
+ putchar_unlocked (':');
+ putchar_unlocked (' ');
+ }
+
+ printf ((radix == radix_hex
+ ? "%s %c%s %0*" PRIx64 " %0*" PRIx64 "\n"
+ : (radix == radix_decimal
+ ? "%s %c%s %*" PRId64 " %*" PRId64 "\n"
+ : "%s %c%s %0*" PRIo64 " %0*" PRIo64 "\n")),
+ symstr,
+ class_type_char (elf, ehdr, &syms[cnt].sym),
+ mark_special
+ ? (GELF_ST_TYPE (syms[cnt].sym.st_info) == STT_TLS
+ ? "@"
+ : (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK
+ ? "*" : " "))
+ : "",
+ digits, syms[cnt].sym.st_value,
+ digits, syms[cnt].sym.st_size);
+ }
+
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
+}
+
+
+/* Maximum size of memory we allocate on the stack. */
+#define MAX_STACK_ALLOC 65536
+
+static int
+sort_by_address (const void *p1, const void *p2)
+{
+ GElf_SymX *s1 = (GElf_SymX *) p1;
+ GElf_SymX *s2 = (GElf_SymX *) p2;
+
+ int result = (s1->sym.st_value < s2->sym.st_value
+ ? -1 : (s1->sym.st_value == s2->sym.st_value ? 0 : 1));
+
+ return reverse_sort ? -result : result;
+}
+
+static Elf_Data *sort_by_name_strtab;
+
+static int
+sort_by_name (const void *p1, const void *p2)
+{
+ GElf_SymX *s1 = (GElf_SymX *) p1;
+ GElf_SymX *s2 = (GElf_SymX *) p2;
+
+ const char *n1 = sort_by_name_strtab->d_buf + s1->sym.st_name;
+ const char *n2 = sort_by_name_strtab->d_buf + s2->sym.st_name;
+
+ int result = strcmp (n1, n2);
+
+ return reverse_sort ? -result : result;
+}
+
+/* Stub libdwfl callback, only the ELF handle already open is ever
+ used. Only used for finding the alternate debug file if the Dwarf
+ comes from the main file. We are not interested in separate
+ debuginfo. */
+static int
+find_no_debuginfo (Dwfl_Module *mod,
+ void **userdata,
+ const char *modname,
+ Dwarf_Addr base,
+ const char *file_name,
+ const char *debuglink_file,
+ GElf_Word debuglink_crc,
+ char **debuginfo_file_name)
+{
+ Dwarf_Addr dwbias;
+ dwfl_module_info (mod, NULL, NULL, NULL, &dwbias, NULL, NULL, NULL);
+
+ /* We are only interested if the Dwarf has been setup on the main
+ elf file but is only missing the alternate debug link. If dwbias
+ hasn't even been setup, this is searching for separate debuginfo
+ for the main elf. We don't care in that case. */
+ if (dwbias == (Dwarf_Addr) -1)
+ return -1;
+
+ return dwfl_standard_find_debuginfo (mod, userdata, modname, base,
+ file_name, debuglink_file,
+ debuglink_crc, debuginfo_file_name);
+}
+
+/* Get the Dwarf for the module/file we want. */
+struct getdbg
+{
+ const char *name;
+ Dwarf **dbg;
+};
+
+static int
+getdbg_dwflmod (Dwfl_Module *dwflmod,
+ void **userdata __attribute__ ((unused)),
+ const char *name,
+ Dwarf_Addr base __attribute__ ((unused)),
+ void *arg)
+{
+ struct getdbg *get = (struct getdbg *) arg;
+ if (get != NULL && get->name != NULL && strcmp (get->name, name) == 0)
+ {
+ Dwarf_Addr bias;
+ *get->dbg = dwfl_module_getdwarf (dwflmod, &bias);
+ return DWARF_CB_ABORT;
+ }
+
+ return DWARF_CB_OK;
+}
+
+static void
+show_symbols (int fd, Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, Elf_Scn *xndxscn,
+ GElf_Shdr *shdr, const char *prefix, const char *fname,
+ const char *fullname)
+{
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ /* The section is that large. */
+ size_t size = shdr->sh_size;
+ /* One entry is this large. */
+ size_t entsize = shdr->sh_entsize;
+
+ /* Consistency checks. */
+ if (entsize == 0
+ || entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT))
+ error (0, 0,
+ gettext ("%s: entry size in section %zd `%s' is not what we expect"),
+ fullname, elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
+ else if (size % entsize != 0)
+ error (0, 0,
+ gettext ("%s: size of section %zd `%s' is not multiple of entry size"),
+ fullname, elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
+
+ /* Compute number of entries. Handle buggy entsize values. */
+ size_t nentries = size / (entsize ?: 1);
+
+
+#define obstack_chunk_alloc xmalloc
+#define obstack_chunk_free free
+ struct obstack whereob;
+ obstack_init (&whereob);
+
+ /* Get a DWARF debugging descriptor. It's no problem if this isn't
+ possible. We just won't print any line number information. */
+ Dwarf *dbg = NULL;
+ Dwfl *dwfl = NULL;
+ if (format == format_sysv)
+ {
+ if (ehdr->e_type != ET_REL)
+ dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL);
+ else
+ {
+ /* Abuse libdwfl to do the relocations for us. This is just
+ for the ET_REL file containing Dwarf, so no need for
+ fancy lookups. */
+
+ /* Duplicate an fd for dwfl_report_offline to swallow. */
+ int dwfl_fd = dup (fd);
+ if (likely (dwfl_fd >= 0))
+ {
+ static const Dwfl_Callbacks callbacks =
+ {
+ .section_address = dwfl_offline_section_address,
+ .find_debuginfo = find_no_debuginfo
+ };
+ dwfl = dwfl_begin (&callbacks);
+ if (likely (dwfl != NULL))
+ {
+ /* Let 0 be the logical address of the file (or
+ first in archive). */
+ dwfl->offline_next_address = 0;
+ if (dwfl_report_offline (dwfl, fname, fname, dwfl_fd)
+ == NULL)
+ {
+ /* Consumed on success, not on failure. */
+ close (dwfl_fd);
+ }
+ else
+ {
+ dwfl_report_end (dwfl, NULL, NULL);
+
+ struct getdbg get = { .name = fname, .dbg = &dbg };
+ dwfl_getmodules (dwfl, &getdbg_dwflmod, &get, 0);
+ }
+ }
+ }
+ }
+ if (dbg != NULL)
+ {
+ (void) dwarf_getpubnames (dbg, get_global, NULL, 0);
+
+ get_local_names (dbg);
+ }
+ }
+
+ /* Get the data of the section. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+ Elf_Data *xndxdata = elf_getdata (xndxscn, NULL);
+ if (data == NULL || (xndxscn != NULL && xndxdata == NULL))
+ INTERNAL_ERROR (fullname);
+
+ /* Allocate the memory.
+
+ XXX We can use a dirty trick here. Since GElf_Sym == Elf64_Sym we
+ can use the data memory instead of copying again if what we read
+ is a 64 bit file. */
+ if (nentries > SIZE_MAX / sizeof (GElf_SymX))
+ error (EXIT_FAILURE, 0,
+ gettext ("%s: entries (%zd) in section %zd `%s' is too large"),
+ fullname, nentries, elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
+ GElf_SymX *sym_mem;
+ if (nentries * sizeof (GElf_SymX) < MAX_STACK_ALLOC)
+ sym_mem = (GElf_SymX *) alloca (nentries * sizeof (GElf_SymX));
+ else
+ sym_mem = (GElf_SymX *) xmalloc (nentries * sizeof (GElf_SymX));
+
+ /* Iterate over all symbols. */
+#ifdef USE_DEMANGLE
+ size_t demangle_buffer_len = 0;
+ char *demangle_buffer = NULL;
+#endif
+ int longest_name = 4;
+ int longest_where = 4;
+ size_t nentries_used = 0;
+ for (size_t cnt = 0; cnt < nentries; ++cnt)
+ {
+ GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, cnt,
+ &sym_mem[nentries_used].sym,
+ &sym_mem[nentries_used].xndx);
+ if (sym == NULL)
+ INTERNAL_ERROR (fullname);
+
+ /* Filter out administrative symbols without a name and those
+ deselected by the user with command line options. */
+ if ((hide_undefined && sym->st_shndx == SHN_UNDEF)
+ || (hide_defined && sym->st_shndx != SHN_UNDEF)
+ || (hide_local && GELF_ST_BIND (sym->st_info) == STB_LOCAL))
+ continue;
+
+ sym_mem[nentries_used].where = "";
+ if (format == format_sysv)
+ {
+ const char *symstr = elf_strptr (ebl->elf, shdr->sh_link,
+ sym->st_name);
+ if (symstr == NULL)
+ continue;
+
+#ifdef USE_DEMANGLE
+ /* Demangle if necessary. Require GNU v3 ABI by the "_Z" prefix. */
+ if (demangle && symstr[0] == '_' && symstr[1] == 'Z')
+ {
+ int status = -1;
+ char *dmsymstr = __cxa_demangle (symstr, demangle_buffer,
+ &demangle_buffer_len, &status);
+
+ if (status == 0)
+ symstr = dmsymstr;
+ }
+#endif
+
+ longest_name = MAX ((size_t) longest_name, strlen (symstr));
+
+ if (sym->st_shndx != SHN_UNDEF
+ && GELF_ST_BIND (sym->st_info) != STB_LOCAL
+ && global_root != NULL)
+ {
+ Dwarf_Global fake = { .name = symstr };
+ Dwarf_Global **found = tfind (&fake, &global_root,
+ global_compare);
+ if (found != NULL)
+ {
+ Dwarf_Die die_mem;
+ Dwarf_Die *die = dwarf_offdie (dbg, (*found)->die_offset,
+ &die_mem);
+
+ Dwarf_Die cudie_mem;
+ Dwarf_Die *cudie = NULL;
+
+ Dwarf_Addr lowpc;
+ Dwarf_Addr highpc;
+ if (die != NULL
+ && dwarf_lowpc (die, &lowpc) == 0
+ && lowpc <= sym->st_value
+ && dwarf_highpc (die, &highpc) == 0
+ && highpc > sym->st_value)
+ cudie = dwarf_offdie (dbg, (*found)->cu_offset,
+ &cudie_mem);
+ if (cudie != NULL)
+ {
+ Dwarf_Line *line = dwarf_getsrc_die (cudie,
+ sym->st_value);
+ if (line != NULL)
+ {
+ /* We found the line. */
+ int lineno;
+ (void) dwarf_lineno (line, &lineno);
+ const char *file = dwarf_linesrc (line, NULL, NULL);
+ file = (file != NULL) ? basename (file) : "???";
+ int n;
+ n = obstack_printf (&whereob, "%s:%d%c", file,
+ lineno, '\0');
+ sym_mem[nentries_used].where
+ = obstack_finish (&whereob);
+
+ /* The return value of obstack_print included the
+ NUL byte, so subtract one. */
+ if (--n > (int) longest_where)
+ longest_where = (size_t) n;
+ }
+ }
+ }
+ }
+
+ /* Try to find the symbol among the local symbols. */
+ if (sym_mem[nentries_used].where[0] == '\0')
+ {
+ struct local_name fake =
+ {
+ .name = symstr,
+ .lowpc = sym->st_value,
+ .highpc = sym->st_value,
+ };
+ struct local_name **found = tfind (&fake, &local_root,
+ local_compare);
+ if (found != NULL)
+ {
+ /* We found the line. */
+ int n = obstack_printf (&whereob, "%s:%" PRIu64 "%c",
+ basename ((*found)->file),
+ (*found)->lineno,
+ '\0');
+ sym_mem[nentries_used].where = obstack_finish (&whereob);
+
+ /* The return value of obstack_print included the
+ NUL byte, so subtract one. */
+ if (--n > (int) longest_where)
+ longest_where = (size_t) n;
+ }
+ }
+ }
+
+ /* We use this entry. */
+ ++nentries_used;
+ }
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
+ /* Now we know the exact number. */
+ nentries = nentries_used;
+
+ /* Sort the entries according to the users wishes. */
+ if (sort == sort_name)
+ {
+ sort_by_name_strtab = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link),
+ NULL);
+ qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_name);
+ }
+ else if (sort == sort_numeric)
+ qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_address);
+
+ /* Finally print according to the users selection. */
+ switch (format)
+ {
+ case format_sysv:
+ show_symbols_sysv (ebl, shdr->sh_link, fullname, sym_mem, nentries,
+ longest_name, longest_where);
+ break;
+
+ case format_bsd:
+ show_symbols_bsd (ebl->elf, ehdr, shdr->sh_link, prefix, fname, fullname,
+ sym_mem, nentries);
+ break;
+
+ case format_posix:
+ default:
+ assert (format == format_posix);
+ show_symbols_posix (ebl->elf, ehdr, shdr->sh_link, prefix, fullname,
+ sym_mem, nentries);
+ break;
+ }
+
+ /* Free all memory. */
+ if (nentries * sizeof (sym_mem[0]) >= MAX_STACK_ALLOC)
+ free (sym_mem);
+
+ obstack_free (&whereob, NULL);
+
+ if (dbg != NULL)
+ {
+ tdestroy (global_root, free);
+ global_root = NULL;
+
+ tdestroy (local_root, free);
+ local_root = NULL;
+
+ if (dwfl == NULL)
+ (void) dwarf_end (dbg);
+ }
+ if (dwfl != NULL)
+ dwfl_end (dwfl);
+}
+
+
+static int
+handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
+ const char *suffix)
+{
+ size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
+ size_t suffix_len = suffix == NULL ? 0 : strlen (suffix);
+ size_t fname_len = strlen (fname) + 1;
+ char fullname[prefix_len + 1 + fname_len + suffix_len];
+ char *cp = fullname;
+ Elf_Scn *scn = NULL;
+ int any = 0;
+ int result = 0;
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr;
+ Ebl *ebl;
+
+ /* Get the backend for this object file type. */
+ ebl = ebl_openbackend (elf);
+
+ /* We need the ELF header in a few places. */
+ ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ INTERNAL_ERROR (fullname);
+
+ /* If we are asked to print the dynamic symbol table and this is
+ executable or dynamic executable, fail. */
+ if (symsec_type == SHT_DYNSYM
+ && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+ {
+ /* XXX Add machine specific object file types. */
+ error (0, 0, gettext ("%s%s%s%s: Invalid operation"),
+ prefix ?: "", prefix ? "(" : "", fname, prefix ? ")" : "");
+ result = 1;
+ goto out;
+ }
+
+ /* Create the full name of the file. */
+ if (prefix != NULL)
+ cp = mempcpy (cp, prefix, prefix_len);
+ cp = mempcpy (cp, fname, fname_len);
+ if (suffix != NULL)
+ memcpy (cp - 1, suffix, suffix_len + 1);
+
+ /* Find the symbol table.
+
+ XXX Can there be more than one? Do we print all? Currently we do. */
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr == NULL)
+ INTERNAL_ERROR (fullname);
+
+ if (shdr->sh_type == symsec_type)
+ {
+ Elf_Scn *xndxscn = NULL;
+
+ /* We have a symbol table. First make sure we remember this. */
+ any = 1;
+
+ /* Look for an extended section index table for this section. */
+ if (symsec_type == SHT_SYMTAB)
+ {
+ size_t scnndx = elf_ndxscn (scn);
+
+ while ((xndxscn = elf_nextscn (elf, xndxscn)) != NULL)
+ {
+ GElf_Shdr xndxshdr_mem;
+ GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem);
+
+ if (xndxshdr == NULL)
+ INTERNAL_ERROR (fullname);
+
+ if (xndxshdr->sh_type == SHT_SYMTAB_SHNDX
+ && xndxshdr->sh_link == scnndx)
+ break;
+ }
+ }
+
+ show_symbols (fd, ebl, ehdr, scn, xndxscn, shdr, prefix, fname,
+ fullname);
+ }
+ }
+
+ if (! any)
+ {
+ error (0, 0, gettext ("%s%s%s: no symbols"),
+ prefix ?: "", prefix ? ":" : "", fname);
+ result = 1;
+ }
+
+ out:
+ /* Close the ELF backend library descriptor. */
+ ebl_closebackend (ebl);
+
+ return result;
+}
+
+
+#include "debugpred.h"
diff --git a/src/objdump.c b/src/objdump.c
new file mode 100644
index 0000000..0dd9a6a
--- /dev/null
+++ b/src/objdump.c
@@ -0,0 +1,795 @@
+/* Print information from ELF file in human-readable form.
+ Copyright (C) 2005, 2006, 2007, 2009, 2011, 2012, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2005.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <error.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libeu.h>
+#include <system.h>
+#include <color.h>
+#include <printversion.h>
+#include "../libebl/libeblP.h"
+
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Mode selection:"), 0 },
+ { "reloc", 'r', NULL, 0, N_("Display relocation information."), 0 },
+ { "full-contents", 's', NULL, 0,
+ N_("Display the full contents of all sections requested"), 0 },
+ { "disassemble", 'd', NULL, 0,
+ N_("Display assembler code of executable sections"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output content selection:"), 0 },
+ { "section", 'j', "NAME", 0,
+ N_("Only display information for section NAME."), 0 },
+
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("\
+Show information from FILEs (a.out by default).");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("[FILE...]");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Parser children. */
+static struct argp_child argp_children[] =
+ {
+ { &color_argp, 0, N_("Output formatting"), 2 },
+ { NULL, 0, NULL, 0}
+ };
+
+/* Data structure to communicate with argp functions. */
+static const struct argp argp =
+{
+ options, parse_opt, args_doc, doc, argp_children, NULL, NULL
+};
+
+
+/* Print symbols in file named FNAME. */
+static int process_file (const char *fname, bool more_than_one);
+
+/* Handle content of archive. */
+static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
+ const char *suffix);
+
+/* Handle ELF file. */
+static int handle_elf (Elf *elf, const char *prefix, const char *fname,
+ const char *suffix);
+
+
+#define INTERNAL_ERROR(fname) \
+ error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s): %s"), \
+ fname, __LINE__, PACKAGE_VERSION, elf_errmsg (-1))
+
+
+/* List of sections which should be used. */
+static struct section_list
+{
+ bool is_name;
+ union
+ {
+ const char *name;
+ uint32_t scnndx;
+ };
+ struct section_list *next;
+} *section_list;
+
+
+/* If true print archive index. */
+static bool print_relocs;
+
+/* If true print full contents of requested sections. */
+static bool print_full_content;
+
+/* If true print disassembled output.. */
+static bool print_disasm;
+
+
+int
+main (int argc, char *argv[])
+{
+ /* We use no threads here which can interfere with handling a stream. */
+ (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ (void) textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ int remaining;
+ (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Tell the library which version we are expecting. */
+ (void) elf_version (EV_CURRENT);
+
+ int result = 0;
+ if (remaining == argc)
+ /* The user didn't specify a name so we use a.out. */
+ result = process_file ("a.out", false);
+ else
+ {
+ /* Process all the remaining files. */
+ const bool more_than_one = remaining + 1 < argc;
+
+ do
+ result |= process_file (argv[remaining], more_than_one);
+ while (++remaining < argc);
+ }
+
+ return result;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg,
+ struct argp_state *state __attribute__ ((unused)))
+{
+ /* True if any of the control options is set. */
+ static bool any_control_option;
+
+ switch (key)
+ {
+ case 'j':
+ {
+ struct section_list *newp = xmalloc (sizeof (*newp));
+ char *endp;
+ newp->scnndx = strtoul (arg, &endp, 0);
+ if (*endp == 0)
+ newp->is_name = false;
+ else
+ {
+ newp->name = arg;
+ newp->is_name = true;
+ }
+ newp->next = section_list;
+ section_list = newp;
+ }
+ any_control_option = true;
+ break;
+
+ case 'd':
+ print_disasm = true;
+ any_control_option = true;
+ break;
+
+ case 'r':
+ print_relocs = true;
+ any_control_option = true;
+ break;
+
+ case 's':
+ print_full_content = true;
+ any_control_option = true;
+ break;
+
+ case ARGP_KEY_FINI:
+ if (! any_control_option)
+ {
+ fputs (gettext ("No operation specified.\n"), stderr);
+ argp_help (&argp, stderr, ARGP_HELP_SEE,
+ program_invocation_short_name);
+ exit (EXIT_FAILURE);
+ }
+ /* We only use this for checking the number of arguments, we don't
+ actually want to consume them. */
+ FALLTHROUGH;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+/* Open the file and determine the type. */
+static int
+process_file (const char *fname, bool more_than_one)
+{
+ /* Open the file. */
+ int fd = open (fname, O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, gettext ("cannot open %s"), fname);
+ return 1;
+ }
+
+ /* Now get the ELF descriptor. */
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (elf != NULL)
+ {
+ if (elf_kind (elf) == ELF_K_ELF)
+ {
+ int result = handle_elf (elf, more_than_one ? "" : NULL,
+ fname, NULL);
+
+ if (elf_end (elf) != 0)
+ INTERNAL_ERROR (fname);
+
+ if (close (fd) != 0)
+ error (EXIT_FAILURE, errno, gettext ("while close `%s'"), fname);
+
+ return result;
+ }
+ else if (elf_kind (elf) == ELF_K_AR)
+ {
+ int result = handle_ar (fd, elf, NULL, fname, NULL);
+
+ if (elf_end (elf) != 0)
+ INTERNAL_ERROR (fname);
+
+ if (close (fd) != 0)
+ error (EXIT_FAILURE, errno, gettext ("while close `%s'"), fname);
+
+ return result;
+ }
+
+ /* We cannot handle this type. Close the descriptor anyway. */
+ if (elf_end (elf) != 0)
+ INTERNAL_ERROR (fname);
+ }
+
+ error (0, 0, gettext ("%s: File format not recognized"), fname);
+
+ return 1;
+}
+
+
+static int
+handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
+ const char *suffix)
+{
+ size_t fname_len = strlen (fname) + 1;
+ size_t prefix_len = prefix != NULL ? strlen (prefix) : 0;
+ char new_prefix[prefix_len + fname_len + 2];
+ size_t suffix_len = suffix != NULL ? strlen (suffix) : 0;
+ char new_suffix[suffix_len + 2];
+ Elf *subelf;
+ Elf_Cmd cmd = ELF_C_READ_MMAP;
+ int result = 0;
+
+ char *cp = new_prefix;
+ if (prefix != NULL)
+ cp = stpcpy (cp, prefix);
+ cp = stpcpy (cp, fname);
+ stpcpy (cp, "[");
+
+ cp = new_suffix;
+ if (suffix != NULL)
+ cp = stpcpy (cp, suffix);
+ stpcpy (cp, "]");
+
+ /* Process all the files contained in the archive. */
+ while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
+ {
+ /* The the header for this element. */
+ Elf_Arhdr *arhdr = elf_getarhdr (subelf);
+
+ /* Skip over the index entries. */
+ if (strcmp (arhdr->ar_name, "/") != 0
+ && strcmp (arhdr->ar_name, "//") != 0)
+ {
+ if (elf_kind (subelf) == ELF_K_ELF)
+ result |= handle_elf (subelf, new_prefix, arhdr->ar_name,
+ new_suffix);
+ else if (elf_kind (subelf) == ELF_K_AR)
+ result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name,
+ new_suffix);
+ else
+ {
+ error (0, 0, gettext ("%s%s%s: file format not recognized"),
+ new_prefix, arhdr->ar_name, new_suffix);
+ result = 1;
+ }
+ }
+
+ /* Get next archive element. */
+ cmd = elf_next (subelf);
+ if (elf_end (subelf) != 0)
+ INTERNAL_ERROR (fname);
+ }
+
+ return result;
+}
+
+
+static void
+show_relocs_x (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *symdata,
+ Elf_Data *xndxdata, size_t symstrndx, size_t shstrndx,
+ GElf_Addr r_offset, GElf_Xword r_info, GElf_Sxword r_addend)
+{
+ int elfclass = gelf_getclass (ebl->elf);
+ char buf[128];
+
+ printf ("%0*" PRIx64 " %-20s ",
+ elfclass == ELFCLASS32 ? 8 : 16, r_offset,
+ ebl_reloc_type_name (ebl, GELF_R_TYPE (r_info), buf, sizeof (buf)));
+
+ Elf32_Word xndx;
+ GElf_Sym symmem;
+ GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, GELF_R_SYM (r_info),
+ &symmem, &xndx);
+
+ if (sym == NULL)
+ printf ("<%s %ld>",
+ gettext ("INVALID SYMBOL"), (long int) GELF_R_SYM (r_info));
+ else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION)
+ printf ("%s",
+ elf_strptr (ebl->elf, symstrndx, sym->st_name));
+ else
+ {
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr;
+ destshdr = gelf_getshdr (elf_getscn (ebl->elf,
+ sym->st_shndx == SHN_XINDEX
+ ? xndx : sym->st_shndx),
+ &destshdr_mem);
+
+ if (shdr == NULL || destshdr == NULL)
+ printf ("<%s %ld>",
+ gettext ("INVALID SECTION"),
+ (long int) (sym->st_shndx == SHN_XINDEX
+ ? xndx : sym->st_shndx));
+ else
+ printf ("%s",
+ elf_strptr (ebl->elf, shstrndx, destshdr->sh_name));
+ }
+
+ if (r_addend != 0)
+ {
+ char sign = '+';
+ if (r_addend < 0)
+ {
+ sign = '-';
+ r_addend = -r_addend;
+ }
+ printf ("%c%#" PRIx64, sign, r_addend);
+ }
+ putchar ('\n');
+}
+
+
+static void
+show_relocs_rel (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data,
+ Elf_Data *symdata, Elf_Data *xndxdata, size_t symstrndx,
+ size_t shstrndx)
+{
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_REL, 1, EV_CURRENT);
+ int nentries = shdr->sh_size / sh_entsize;
+
+ for (int cnt = 0; cnt < nentries; ++cnt)
+ {
+ GElf_Rel relmem;
+ GElf_Rel *rel;
+
+ rel = gelf_getrel (data, cnt, &relmem);
+ if (rel != NULL)
+ show_relocs_x (ebl, shdr, symdata, xndxdata, symstrndx, shstrndx,
+ rel->r_offset, rel->r_info, 0);
+ }
+}
+
+
+static void
+show_relocs_rela (Ebl *ebl, GElf_Shdr *shdr, Elf_Data *data,
+ Elf_Data *symdata, Elf_Data *xndxdata, size_t symstrndx,
+ size_t shstrndx)
+{
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_RELA, 1, EV_CURRENT);
+ int nentries = shdr->sh_size / sh_entsize;
+
+ for (int cnt = 0; cnt < nentries; ++cnt)
+ {
+ GElf_Rela relmem;
+ GElf_Rela *rel;
+
+ rel = gelf_getrela (data, cnt, &relmem);
+ if (rel != NULL)
+ show_relocs_x (ebl, shdr, symdata, xndxdata, symstrndx, shstrndx,
+ rel->r_offset, rel->r_info, rel->r_addend);
+ }
+}
+
+
+static bool
+section_match (Elf *elf, uint32_t scnndx, GElf_Shdr *shdr, size_t shstrndx)
+{
+ if (section_list == NULL)
+ return true;
+
+ struct section_list *runp = section_list;
+ const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
+
+ do
+ {
+ if (runp->is_name)
+ {
+ if (name && strcmp (runp->name, name) == 0)
+ return true;
+ }
+ else
+ {
+ if (runp->scnndx == scnndx)
+ return true;
+ }
+
+ runp = runp->next;
+ }
+ while (runp != NULL);
+
+ return false;
+}
+
+
+static int
+show_relocs (Ebl *ebl, const char *fname, uint32_t shstrndx)
+{
+ int elfclass = gelf_getclass (ebl->elf);
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr == NULL)
+ INTERNAL_ERROR (fname);
+
+ if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+ {
+ if (! section_match (ebl->elf, elf_ndxscn (scn), shdr, shstrndx))
+ continue;
+
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf,
+ shdr->sh_info),
+ &destshdr_mem);
+ if (unlikely (destshdr == NULL))
+ continue;
+
+ printf (gettext ("\nRELOCATION RECORDS FOR [%s]:\n"
+ "%-*s TYPE VALUE\n"),
+ elf_strptr (ebl->elf, shstrndx, destshdr->sh_name),
+ elfclass == ELFCLASS32 ? 8 : 16, gettext ("OFFSET"));
+
+ /* Get the data of the section. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ continue;
+
+ /* Get the symbol table information. */
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ if (unlikely (symshdr == NULL || symdata == NULL))
+ continue;
+
+ /* Search for the optional extended section index table. */
+ Elf_Data *xndxdata = NULL;
+ Elf_Scn *xndxscn = NULL;
+ while ((xndxscn = elf_nextscn (ebl->elf, xndxscn)) != NULL)
+ {
+ GElf_Shdr xndxshdr_mem;
+ GElf_Shdr *xndxshdr;
+
+ xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem);
+ if (xndxshdr != NULL && xndxshdr->sh_type == SHT_SYMTAB_SHNDX
+ && xndxshdr->sh_link == elf_ndxscn (symscn))
+ {
+ /* Found it. */
+ xndxdata = elf_getdata (xndxscn, NULL);
+ break;
+ }
+ }
+
+ if (shdr->sh_type == SHT_REL)
+ show_relocs_rel (ebl, shdr, data, symdata, xndxdata,
+ symshdr->sh_link, shstrndx);
+ else
+ show_relocs_rela (ebl, shdr, data, symdata, xndxdata,
+ symshdr->sh_link, shstrndx);
+
+ putchar ('\n');
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+show_full_content (Ebl *ebl, const char *fname, uint32_t shstrndx)
+{
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr == NULL)
+ INTERNAL_ERROR (fname);
+
+ if (shdr->sh_type == SHT_PROGBITS && shdr->sh_size > 0)
+ {
+ if (! section_match (ebl->elf, elf_ndxscn (scn), shdr, shstrndx))
+ continue;
+
+ printf (gettext ("Contents of section %s:\n"),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
+
+ /* Get the data of the section. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ continue;
+
+ unsigned char *cp = data->d_buf;
+ size_t cnt;
+ for (cnt = 0; cnt + 16 < data->d_size; cp += 16, cnt += 16)
+ {
+ printf (" %04zx ", cnt);
+
+ for (size_t inner = 0; inner < 16; inner += 4)
+ printf ("%02hhx%02hhx%02hhx%02hhx ",
+ cp[inner], cp[inner + 1], cp[inner + 2],
+ cp[inner + 3]);
+ fputc_unlocked (' ', stdout);
+
+ for (size_t inner = 0; inner < 16; ++inner)
+ fputc_unlocked (isascii (cp[inner]) && isprint (cp[inner])
+ ? cp[inner] : '.', stdout);
+ fputc_unlocked ('\n', stdout);
+ }
+
+ printf (" %04zx ", cnt);
+
+ size_t remaining = data->d_size - cnt;
+ size_t inner;
+ for (inner = 0; inner + 4 <= remaining; inner += 4)
+ printf ("%02hhx%02hhx%02hhx%02hhx ",
+ cp[inner], cp[inner + 1], cp[inner + 2], cp[inner + 3]);
+
+ for (; inner < remaining; ++inner)
+ printf ("%02hhx", cp[inner]);
+
+ for (inner = 2 * (16 - inner) + (16 - inner + 3) / 4 + 1; inner > 0;
+ --inner)
+ fputc_unlocked (' ', stdout);
+
+ for (inner = 0; inner < remaining; ++inner)
+ fputc_unlocked (isascii (cp[inner]) && isprint (cp[inner])
+ ? cp[inner] : '.', stdout);
+ fputc_unlocked ('\n', stdout);
+
+ fputc_unlocked ('\n', stdout);
+ }
+ }
+
+ return 0;
+}
+
+
+struct disasm_info
+{
+ GElf_Addr addr;
+ const uint8_t *cur;
+ const uint8_t *last_end;
+ const char *address_color;
+ const char *bytes_color;
+};
+
+
+// XXX This is not the preferred output for all architectures. Needs
+// XXX customization, too.
+static int
+disasm_output (char *buf, size_t buflen, void *arg)
+{
+ struct disasm_info *info = (struct disasm_info *) arg;
+
+ if (info->address_color != NULL)
+ printf ("%s%8" PRIx64 "%s: ",
+ info->address_color, (uint64_t) info->addr, color_off);
+ else
+ printf ("%8" PRIx64 ": ", (uint64_t) info->addr);
+
+ if (info->bytes_color != NULL)
+ fputs_unlocked (info->bytes_color, stdout);
+ size_t cnt;
+ for (cnt = 0; cnt < (size_t) MIN (info->cur - info->last_end, 8); ++cnt)
+ printf (" %02" PRIx8, info->last_end[cnt]);
+ if (info->bytes_color != NULL)
+ fputs_unlocked (color_off, stdout);
+
+ printf ("%*s %.*s\n",
+ (int) (8 - cnt) * 3 + 1, "", (int) buflen, buf);
+
+ info->addr += cnt;
+
+ /* We limit the number of bytes printed before the mnemonic to 8.
+ Print the rest on a separate, following line. */
+ if (info->cur - info->last_end > 8)
+ {
+ if (info->address_color != NULL)
+ printf ("%s%8" PRIx64 "%s: ",
+ info->address_color, (uint64_t) info->addr, color_off);
+ else
+ printf ("%8" PRIx64 ": ", (uint64_t) info->addr);
+
+ if (info->bytes_color != NULL)
+ fputs_unlocked (info->bytes_color, stdout);
+ for (; cnt < (size_t) (info->cur - info->last_end); ++cnt)
+ printf (" %02" PRIx8, info->last_end[cnt]);
+ if (info->bytes_color != NULL)
+ fputs_unlocked (color_off, stdout);
+ putchar_unlocked ('\n');
+ info->addr += info->cur - info->last_end - 8;
+ }
+
+ info->last_end = info->cur;
+
+ return 0;
+}
+
+
+static int
+show_disasm (Ebl *ebl, const char *fname, uint32_t shstrndx)
+{
+ DisasmCtx_t *ctx = disasm_begin (ebl, ebl->elf, NULL /* XXX TODO */);
+ if (ctx == NULL)
+ error (EXIT_FAILURE, 0, gettext ("cannot disassemble"));
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr == NULL)
+ INTERNAL_ERROR (fname);
+
+ if (shdr->sh_type == SHT_PROGBITS && shdr->sh_size > 0
+ && (shdr->sh_flags & SHF_EXECINSTR) != 0)
+ {
+ if (! section_match (ebl->elf, elf_ndxscn (scn), shdr, shstrndx))
+ continue;
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ continue;
+
+ printf ("Disassembly of section %s:\n\n",
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
+
+ struct disasm_info info;
+ info.addr = shdr->sh_addr;
+ info.last_end = info.cur = data->d_buf;
+ char *fmt;
+ if (color_mode)
+ {
+ info.address_color = color_address;
+ info.bytes_color = color_bytes;
+
+ if (asprintf (&fmt, "%s%%7m %s%%.1o,%s%%.2o,%s%%.3o%%34a %s%%l",
+ color_mnemonic ?: "",
+ color_operand1 ?: "",
+ color_operand2 ?: "",
+ color_operand3 ?: "",
+ color_label ?: "") < 0)
+ error (EXIT_FAILURE, errno, _("cannot allocate memory"));
+ }
+ else
+ {
+ info.address_color = info.bytes_color = NULL;
+
+ fmt = "%7m %.1o,%.2o,%.3o%34a %l";
+ }
+
+ disasm_cb (ctx, &info.cur, info.cur + data->d_size, info.addr,
+ fmt, disasm_output, &info, NULL /* XXX */);
+
+ if (color_mode)
+ free (fmt);
+ }
+ }
+
+ (void) disasm_end (ctx);
+
+ return 0;
+}
+
+
+static int
+handle_elf (Elf *elf, const char *prefix, const char *fname,
+ const char *suffix)
+{
+
+ /* Get the backend for this object file type. */
+ Ebl *ebl = ebl_openbackend (elf);
+
+ printf ("%s: elf%d-%s\n\n",
+ fname, gelf_getclass (elf) == ELFCLASS32 ? 32 : 64,
+ ebl_backend_name (ebl));
+
+ /* Create the full name of the file. */
+ size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
+ size_t suffix_len = suffix == NULL ? 0 : strlen (suffix);
+ size_t fname_len = strlen (fname) + 1;
+ char fullname[prefix_len + 1 + fname_len + suffix_len];
+ char *cp = fullname;
+ if (prefix != NULL)
+ cp = mempcpy (cp, prefix, prefix_len);
+ cp = mempcpy (cp, fname, fname_len);
+ if (suffix != NULL)
+ memcpy (cp - 1, suffix, suffix_len + 1);
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ int result = 0;
+ if (print_disasm)
+ result = show_disasm (ebl, fullname, shstrndx);
+ if (print_relocs && !print_disasm)
+ result = show_relocs (ebl, fullname, shstrndx);
+ if (print_full_content)
+ result = show_full_content (ebl, fullname, shstrndx);
+
+ /* Close the ELF backend library descriptor. */
+ ebl_closebackend (ebl);
+
+ return result;
+}
+
+
+#include "debugpred.h"
diff --git a/src/ranlib.c b/src/ranlib.c
new file mode 100644
index 0000000..cc0ee23
--- /dev/null
+++ b/src/ranlib.c
@@ -0,0 +1,282 @@
+/* Generate an index to speed access to archives.
+ Copyright (C) 2005-2012 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2005.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <ar.h>
+#include <argp.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libintl.h>
+#include <locale.h>
+#include <obstack.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <system.h>
+#include <printversion.h>
+
+#include "arlib.h"
+
+
+/* Prototypes for local functions. */
+static int handle_file (const char *fname);
+
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("Generate an index to speed access to archives.");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("ARCHIVE");
+
+/* Data structure to communicate with argp functions. */
+static const struct argp argp =
+{
+ options, NULL, args_doc, doc, arlib_argp_children, NULL, NULL
+};
+
+
+int
+main (int argc, char *argv[])
+{
+ /* We use no threads here which can interfere with handling a stream. */
+ (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ (void) textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ int remaining;
+ (void) argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &remaining, NULL);
+
+ /* Tell the library which version we are expecting. */
+ (void) elf_version (EV_CURRENT);
+
+ /* There must at least be one more parameter specifying the archive. */
+ if (remaining == argc)
+ {
+ error (0, 0, gettext ("Archive name required"));
+ argp_help (&argp, stderr, ARGP_HELP_SEE, "ranlib");
+ exit (EXIT_FAILURE);
+ }
+
+ /* We accept the names of multiple archives. */
+ int status = 0;
+ do
+ status |= handle_file (argv[remaining]);
+ while (++remaining < argc);
+
+ return status;
+}
+
+
+static int
+copy_content (Elf *elf, int newfd, off_t off, size_t n)
+{
+ size_t len;
+ char *rawfile = elf_rawfile (elf, &len);
+
+ assert (off + n <= len);
+
+ /* Tell the kernel we will read all the pages sequentially. */
+ size_t ps = sysconf (_SC_PAGESIZE);
+ if (n > 2 * ps)
+ posix_madvise (rawfile + (off & ~(ps - 1)), n, POSIX_MADV_SEQUENTIAL);
+
+ return write_retry (newfd, rawfile + off, n) != (ssize_t) n;
+}
+
+
+/* Handle a file given on the command line. */
+static int
+handle_file (const char *fname)
+{
+ int fd = open (fname, O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, gettext ("cannot open '%s'"), fname);
+ return 1;
+ }
+
+ struct stat st;
+ if (fstat (fd, &st) != 0)
+ {
+ error (0, errno, gettext ("cannot stat '%s'"), fname);
+ close (fd);
+ return 1;
+ }
+
+ /* First we walk through the file, looking for all ELF files to
+ collect symbols from. */
+ Elf *arelf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (arelf == NULL)
+ {
+ error (0, 0, gettext ("cannot create ELF descriptor for '%s': %s"),
+ fname, elf_errmsg (-1));
+ close (fd);
+ return 1;
+ }
+
+ if (elf_kind (arelf) != ELF_K_AR)
+ {
+ error (0, 0, gettext ("'%s' is no archive"), fname);
+ elf_end (arelf);
+ close (fd);
+ return 1;
+ }
+
+ arlib_init ();
+
+ /* Iterate over the content of the archive. */
+ off_t index_off = -1;
+ size_t index_size = 0;
+ off_t cur_off = SARMAG;
+ Elf *elf;
+ Elf_Cmd cmd = ELF_C_READ_MMAP;
+ while ((elf = elf_begin (fd, cmd, arelf)) != NULL)
+ {
+ Elf_Arhdr *arhdr = elf_getarhdr (elf);
+ assert (arhdr != NULL);
+
+ /* If this is the index, remember the location. */
+ if (strcmp (arhdr->ar_name, "/") == 0)
+ {
+ index_off = elf_getaroff (elf);
+ index_size = arhdr->ar_size;
+ }
+ else
+ {
+ arlib_add_symbols (elf, fname, arhdr->ar_name, cur_off);
+ cur_off += (((arhdr->ar_size + 1) & ~((off_t) 1))
+ + sizeof (struct ar_hdr));
+ }
+
+ /* Get next archive element. */
+ cmd = elf_next (elf);
+ if (elf_end (elf) != 0)
+ error (0, 0, gettext ("error while freeing sub-ELF descriptor: %s"),
+ elf_errmsg (-1));
+ }
+
+ arlib_finalize ();
+
+ /* If the file contains no symbols we need not do anything. */
+ int status = 0;
+ if (symtab.symsnamelen != 0
+ /* We have to rewrite the file also if it initially had an index
+ but now does not need one anymore. */
+ || (symtab.symsnamelen == 0 && index_size != 0))
+ {
+ /* Create a new, temporary file in the same directory as the
+ original file. */
+ char tmpfname[strlen (fname) + 7];
+ strcpy (stpcpy (tmpfname, fname), "XXXXXX");
+ int newfd = mkstemp (tmpfname);
+ if (unlikely (newfd == -1))
+ {
+ nonew:
+ error (0, errno, gettext ("cannot create new file"));
+ status = 1;
+ }
+ else
+ {
+ /* Create the header. */
+ if (unlikely (write_retry (newfd, ARMAG, SARMAG) != SARMAG))
+ {
+ // XXX Use /prof/self/fd/%d ???
+ nonew_unlink:
+ unlink (tmpfname);
+ if (newfd != -1)
+ close (newfd);
+ goto nonew;
+ }
+
+ /* Create the new file. There are three parts as far we are
+ concerned: 1. original context before the index, 2. the
+ new index, 3. everything after the new index. */
+ off_t rest_off;
+ if (index_off != -1)
+ rest_off = (index_off + sizeof (struct ar_hdr)
+ + ((index_size + 1) & ~1ul));
+ else
+ rest_off = SARMAG;
+
+ if ((symtab.symsnamelen != 0
+ && ((write_retry (newfd, symtab.symsoff,
+ symtab.symsofflen)
+ != (ssize_t) symtab.symsofflen)
+ || (write_retry (newfd, symtab.symsname,
+ symtab.symsnamelen)
+ != (ssize_t) symtab.symsnamelen)))
+ /* Even if the original file had content before the
+ symbol table, we write it in the correct order. */
+ || (index_off > SARMAG
+ && copy_content (arelf, newfd, SARMAG, index_off - SARMAG))
+ || copy_content (arelf, newfd, rest_off, st.st_size - rest_off)
+ /* Set the mode of the new file to the same values the
+ original file has. */
+ || fchmod (newfd, st.st_mode & ALLPERMS) != 0
+ /* Never complain about fchown failing. */
+ || (({asm ("" :: "r" (fchown (newfd, st.st_uid, st.st_gid))); }),
+ close (newfd) != 0)
+ || (newfd = -1, rename (tmpfname, fname) != 0))
+ goto nonew_unlink;
+ }
+ }
+
+ elf_end (arelf);
+
+ arlib_fini ();
+
+ close (fd);
+
+ return status;
+}
+
+
+#include "debugpred.h"
diff --git a/src/readelf.c b/src/readelf.c
new file mode 100644
index 0000000..d606cf5
--- /dev/null
+++ b/src/readelf.c
@@ -0,0 +1,9973 @@
+/* Print information from ELF file in human-readable form.
+ Copyright (C) 1999-2017 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 1999.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <ctype.h>
+#include <dwarf.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <langinfo.h>
+#include <libdw.h>
+#include <libdwfl.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include <libeu.h>
+#include <system.h>
+#include <printversion.h>
+#include "../libelf/libelfP.h"
+#include "../libelf/common.h"
+#include "../libebl/libeblP.h"
+#include "../libdwelf/libdwelf.h"
+#include "../libdw/libdwP.h"
+#include "../libdwfl/libdwflP.h"
+#include "../libdw/memory-access.h"
+
+#include "../libdw/known-dwarf.h"
+
+#ifdef __linux__
+#define CORE_SIGILL SIGILL
+#define CORE_SIGBUS SIGBUS
+#define CORE_SIGFPE SIGFPE
+#define CORE_SIGSEGV SIGSEGV
+#define CORE_SI_USER SI_USER
+#else
+/* We want the linux version of those as that is what shows up in the core files. */
+#define CORE_SIGILL 4 /* Illegal instruction (ANSI). */
+#define CORE_SIGBUS 7 /* BUS error (4.2 BSD). */
+#define CORE_SIGFPE 8 /* Floating-point exception (ANSI). */
+#define CORE_SIGSEGV 11 /* Segmentation violation (ANSI). */
+#define CORE_SI_USER 0 /* Sent by kill, sigsend. */
+#endif
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+/* argp key value for --elf-section, non-ascii. */
+#define ELF_INPUT_SECTION 256
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("ELF input selection:"), 0 },
+ { "elf-section", ELF_INPUT_SECTION, "SECTION", OPTION_ARG_OPTIONAL,
+ N_("Use the named SECTION (default .gnu_debugdata) as (compressed) ELF "
+ "input data"), 0 },
+ { NULL, 0, NULL, 0, N_("ELF output selection:"), 0 },
+ { "all", 'a', NULL, 0,
+ N_("All these plus -p .strtab -p .dynstr -p .comment"), 0 },
+ { "dynamic", 'd', NULL, 0, N_("Display the dynamic segment"), 0 },
+ { "file-header", 'h', NULL, 0, N_("Display the ELF file header"), 0 },
+ { "histogram", 'I', NULL, 0,
+ N_("Display histogram of bucket list lengths"), 0 },
+ { "program-headers", 'l', NULL, 0, N_("Display the program headers"), 0 },
+ { "segments", 'l', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 },
+ { "relocs", 'r', NULL, 0, N_("Display relocations"), 0 },
+ { "section-groups", 'g', NULL, 0, N_("Display the section groups"), 0 },
+ { "section-headers", 'S', NULL, 0, N_("Display the sections' headers"), 0 },
+ { "sections", 'S', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 },
+ { "symbols", 's', "SECTION", OPTION_ARG_OPTIONAL,
+ N_("Display the symbol table sections"), 0 },
+ { "version-info", 'V', NULL, 0, N_("Display versioning information"), 0 },
+ { "notes", 'n', NULL, 0, N_("Display the ELF notes"), 0 },
+ { "arch-specific", 'A', NULL, 0,
+ N_("Display architecture specific information, if any"), 0 },
+ { "exception", 'e', NULL, 0,
+ N_("Display sections for exception handling"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Additional output selection:"), 0 },
+ { "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL,
+ N_("Display DWARF section content. SECTION can be one of abbrev, "
+ "aranges, decodedaranges, frame, gdb_index, info, loc, line, "
+ "decodedline, ranges, pubnames, str, macinfo, macro or exception"), 0 },
+ { "hex-dump", 'x', "SECTION", 0,
+ N_("Dump the uninterpreted contents of SECTION, by number or name"), 0 },
+ { "strings", 'p', "SECTION", OPTION_ARG_OPTIONAL,
+ N_("Print string contents of sections"), 0 },
+ { "string-dump", 'p', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 },
+ { "archive-index", 'c', NULL, 0,
+ N_("Display the symbol index of an archive"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output control:"), 0 },
+ { "numeric-addresses", 'N', NULL, 0,
+ N_("Do not find symbol names for addresses in DWARF data"), 0 },
+ { "unresolved-address-offsets", 'U', NULL, 0,
+ N_("Display just offsets instead of resolving values to addresses in DWARF data"), 0 },
+ { "wide", 'W', NULL, 0,
+ N_("Ignored for compatibility (lines always wide)"), 0 },
+ { "decompress", 'z', NULL, 0,
+ N_("Show compression information for compressed sections (when used with -S); decompress section before dumping data (when used with -p or -x)"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("\
+Print information from ELF file in human-readable form.");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("FILE...");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+/* If non-null, the section from which we should read to (compressed) ELF. */
+static const char *elf_input_section = NULL;
+
+/* Flags set by the option controlling the output. */
+
+/* True if dynamic segment should be printed. */
+static bool print_dynamic_table;
+
+/* True if the file header should be printed. */
+static bool print_file_header;
+
+/* True if the program headers should be printed. */
+static bool print_program_header;
+
+/* True if relocations should be printed. */
+static bool print_relocations;
+
+/* True if the section headers should be printed. */
+static bool print_section_header;
+
+/* True if the symbol table should be printed. */
+static bool print_symbol_table;
+
+/* A specific section name, or NULL to print all symbol tables. */
+static char *symbol_table_section;
+
+/* True if the version information should be printed. */
+static bool print_version_info;
+
+/* True if section groups should be printed. */
+static bool print_section_groups;
+
+/* True if bucket list length histogram should be printed. */
+static bool print_histogram;
+
+/* True if the architecture specific data should be printed. */
+static bool print_arch;
+
+/* True if note section content should be printed. */
+static bool print_notes;
+
+/* True if SHF_STRINGS section content should be printed. */
+static bool print_string_sections;
+
+/* True if archive index should be printed. */
+static bool print_archive_index;
+
+/* True if any of the control options except print_archive_index is set. */
+static bool any_control_option;
+
+/* True if we should print addresses from DWARF in symbolic form. */
+static bool print_address_names = true;
+
+/* True if we should print raw values instead of relativized addresses. */
+static bool print_unresolved_addresses = false;
+
+/* True if we should print the .debug_aranges section using libdw. */
+static bool decodedaranges = false;
+
+/* True if we should print the .debug_aranges section using libdw. */
+static bool decodedline = false;
+
+/* True if we want to show more information about compressed sections. */
+static bool print_decompress = false;
+
+/* Select printing of debugging sections. */
+static enum section_e
+{
+ section_abbrev = 1, /* .debug_abbrev */
+ section_aranges = 2, /* .debug_aranges */
+ section_frame = 4, /* .debug_frame or .eh_frame & al. */
+ section_info = 8, /* .debug_info, .debug_types */
+ section_types = section_info,
+ section_line = 16, /* .debug_line */
+ section_loc = 32, /* .debug_loc */
+ section_pubnames = 64, /* .debug_pubnames */
+ section_str = 128, /* .debug_str */
+ section_macinfo = 256, /* .debug_macinfo */
+ section_ranges = 512, /* .debug_ranges */
+ section_exception = 1024, /* .eh_frame & al. */
+ section_gdb_index = 2048, /* .gdb_index */
+ section_macro = 4096, /* .debug_macro */
+ section_all = (section_abbrev | section_aranges | section_frame
+ | section_info | section_line | section_loc
+ | section_pubnames | section_str | section_macinfo
+ | section_ranges | section_exception | section_gdb_index
+ | section_macro)
+} print_debug_sections, implicit_debug_sections;
+
+/* Select hex dumping of sections. */
+static struct section_argument *dump_data_sections;
+static struct section_argument **dump_data_sections_tail = &dump_data_sections;
+
+/* Select string dumping of sections. */
+static struct section_argument *string_sections;
+static struct section_argument **string_sections_tail = &string_sections;
+
+struct section_argument
+{
+ struct section_argument *next;
+ const char *arg;
+ bool implicit;
+};
+
+/* Numbers of sections and program headers in the file. */
+static size_t shnum;
+static size_t phnum;
+
+
+/* Declarations of local functions. */
+static void process_file (int fd, const char *fname, bool only_one);
+static void process_elf_file (Dwfl_Module *dwflmod, int fd);
+static void print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr);
+static void print_shdr (Ebl *ebl, GElf_Ehdr *ehdr);
+static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr);
+static void print_scngrp (Ebl *ebl);
+static void print_dynamic (Ebl *ebl);
+static void print_relocs (Ebl *ebl, GElf_Ehdr *ehdr);
+static void handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr);
+static void handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr);
+static void print_symtab (Ebl *ebl, int type);
+static void handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr);
+static void print_verinfo (Ebl *ebl);
+static void handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr);
+static void handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr);
+static void handle_versym (Ebl *ebl, Elf_Scn *scn,
+ GElf_Shdr *shdr);
+static void print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr);
+static void handle_hash (Ebl *ebl);
+static void handle_notes (Ebl *ebl, GElf_Ehdr *ehdr);
+static void print_liblist (Ebl *ebl);
+static void print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr);
+static void dump_data (Ebl *ebl);
+static void dump_strings (Ebl *ebl);
+static void print_strings (Ebl *ebl);
+static void dump_archive_index (Elf *, const char *);
+
+
+int
+main (int argc, char *argv[])
+{
+ /* Set locale. */
+ setlocale (LC_ALL, "");
+
+ /* Initialize the message catalog. */
+ textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ int remaining;
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Before we start tell the ELF library which version we are using. */
+ elf_version (EV_CURRENT);
+
+ /* Now process all the files given at the command line. */
+ bool only_one = remaining + 1 == argc;
+ do
+ {
+ /* Open the file. */
+ int fd = open (argv[remaining], O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, gettext ("cannot open input file"));
+ continue;
+ }
+
+ process_file (fd, argv[remaining], only_one);
+
+ close (fd);
+ }
+ while (++remaining < argc);
+
+ return error_message_count != 0;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg,
+ struct argp_state *state __attribute__ ((unused)))
+{
+ void add_dump_section (const char *name, bool implicit)
+ {
+ struct section_argument *a = xmalloc (sizeof *a);
+ a->arg = name;
+ a->next = NULL;
+ a->implicit = implicit;
+ struct section_argument ***tailp
+ = key == 'x' ? &dump_data_sections_tail : &string_sections_tail;
+ **tailp = a;
+ *tailp = &a->next;
+ }
+
+ switch (key)
+ {
+ case 'a':
+ print_file_header = true;
+ print_program_header = true;
+ print_relocations = true;
+ print_section_header = true;
+ print_symbol_table = true;
+ print_version_info = true;
+ print_dynamic_table = true;
+ print_section_groups = true;
+ print_histogram = true;
+ print_arch = true;
+ print_notes = true;
+ implicit_debug_sections |= section_exception;
+ add_dump_section (".strtab", true);
+ add_dump_section (".dynstr", true);
+ add_dump_section (".comment", true);
+ any_control_option = true;
+ break;
+ case 'A':
+ print_arch = true;
+ any_control_option = true;
+ break;
+ case 'd':
+ print_dynamic_table = true;
+ any_control_option = true;
+ break;
+ case 'e':
+ print_debug_sections |= section_exception;
+ any_control_option = true;
+ break;
+ case 'g':
+ print_section_groups = true;
+ any_control_option = true;
+ break;
+ case 'h':
+ print_file_header = true;
+ any_control_option = true;
+ break;
+ case 'I':
+ print_histogram = true;
+ any_control_option = true;
+ break;
+ case 'l':
+ print_program_header = true;
+ any_control_option = true;
+ break;
+ case 'n':
+ print_notes = true;
+ any_control_option = true;
+ break;
+ case 'r':
+ print_relocations = true;
+ any_control_option = true;
+ break;
+ case 'S':
+ print_section_header = true;
+ any_control_option = true;
+ break;
+ case 's':
+ print_symbol_table = true;
+ any_control_option = true;
+ symbol_table_section = arg;
+ break;
+ case 'V':
+ print_version_info = true;
+ any_control_option = true;
+ break;
+ case 'c':
+ print_archive_index = true;
+ break;
+ case 'w':
+ if (arg == NULL)
+ print_debug_sections = section_all;
+ else if (strcmp (arg, "abbrev") == 0)
+ print_debug_sections |= section_abbrev;
+ else if (strcmp (arg, "aranges") == 0)
+ print_debug_sections |= section_aranges;
+ else if (strcmp (arg, "decodedaranges") == 0)
+ {
+ print_debug_sections |= section_aranges;
+ decodedaranges = true;
+ }
+ else if (strcmp (arg, "ranges") == 0)
+ {
+ print_debug_sections |= section_ranges;
+ implicit_debug_sections |= section_info;
+ }
+ else if (strcmp (arg, "frame") == 0 || strcmp (arg, "frames") == 0)
+ print_debug_sections |= section_frame;
+ else if (strcmp (arg, "info") == 0)
+ print_debug_sections |= section_info;
+ else if (strcmp (arg, "loc") == 0)
+ {
+ print_debug_sections |= section_loc;
+ implicit_debug_sections |= section_info;
+ }
+ else if (strcmp (arg, "line") == 0)
+ print_debug_sections |= section_line;
+ else if (strcmp (arg, "decodedline") == 0)
+ {
+ print_debug_sections |= section_line;
+ decodedline = true;
+ }
+ else if (strcmp (arg, "pubnames") == 0)
+ print_debug_sections |= section_pubnames;
+ else if (strcmp (arg, "str") == 0)
+ print_debug_sections |= section_str;
+ else if (strcmp (arg, "macinfo") == 0)
+ print_debug_sections |= section_macinfo;
+ else if (strcmp (arg, "macro") == 0)
+ print_debug_sections |= section_macro;
+ else if (strcmp (arg, "exception") == 0)
+ print_debug_sections |= section_exception;
+ else if (strcmp (arg, "gdb_index") == 0)
+ print_debug_sections |= section_gdb_index;
+ else
+ {
+ fprintf (stderr, gettext ("Unknown DWARF debug section `%s'.\n"),
+ arg);
+ argp_help (&argp, stderr, ARGP_HELP_SEE,
+ program_invocation_short_name);
+ exit (1);
+ }
+ any_control_option = true;
+ break;
+ case 'p':
+ any_control_option = true;
+ if (arg == NULL)
+ {
+ print_string_sections = true;
+ break;
+ }
+ FALLTHROUGH;
+ case 'x':
+ add_dump_section (arg, false);
+ any_control_option = true;
+ break;
+ case 'N':
+ print_address_names = false;
+ break;
+ case 'U':
+ print_unresolved_addresses = true;
+ break;
+ case ARGP_KEY_NO_ARGS:
+ fputs (gettext ("Missing file name.\n"), stderr);
+ goto do_argp_help;
+ case ARGP_KEY_FINI:
+ if (! any_control_option && ! print_archive_index)
+ {
+ fputs (gettext ("No operation specified.\n"), stderr);
+ do_argp_help:
+ argp_help (&argp, stderr, ARGP_HELP_SEE,
+ program_invocation_short_name);
+ exit (EXIT_FAILURE);
+ }
+ break;
+ case 'W': /* Ignored. */
+ break;
+ case 'z':
+ print_decompress = true;
+ break;
+ case ELF_INPUT_SECTION:
+ if (arg == NULL)
+ elf_input_section = ".gnu_debugdata";
+ else
+ elf_input_section = arg;
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+/* Create a file descriptor to read the data from the
+ elf_input_section given a file descriptor to an ELF file. */
+static int
+open_input_section (int fd)
+{
+ size_t shnums;
+ size_t cnt;
+ size_t shstrndx;
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ {
+ error (0, 0, gettext ("cannot generate Elf descriptor: %s"),
+ elf_errmsg (-1));
+ return -1;
+ }
+
+ if (elf_getshdrnum (elf, &shnums) < 0)
+ {
+ error (0, 0, gettext ("cannot determine number of sections: %s"),
+ elf_errmsg (-1));
+ open_error:
+ elf_end (elf);
+ return -1;
+ }
+
+ if (elf_getshdrstrndx (elf, &shstrndx) < 0)
+ {
+ error (0, 0, gettext ("cannot get section header string table index"));
+ goto open_error;
+ }
+
+ for (cnt = 0; cnt < shnums; ++cnt)
+ {
+ Elf_Scn *scn = elf_getscn (elf, cnt);
+ if (scn == NULL)
+ {
+ error (0, 0, gettext ("cannot get section: %s"),
+ elf_errmsg (-1));
+ goto open_error;
+ }
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ {
+ error (0, 0, gettext ("cannot get section header: %s"),
+ elf_errmsg (-1));
+ goto open_error;
+ }
+
+ const char *sname = elf_strptr (elf, shstrndx, shdr->sh_name);
+ if (sname == NULL)
+ {
+ error (0, 0, gettext ("cannot get section name"));
+ goto open_error;
+ }
+
+ if (strcmp (sname, elf_input_section) == 0)
+ {
+ Elf_Data *data = elf_rawdata (scn, NULL);
+ if (data == NULL)
+ {
+ error (0, 0, gettext ("cannot get %s content: %s"),
+ sname, elf_errmsg (-1));
+ goto open_error;
+ }
+
+ /* Create (and immediately unlink) a temporary file to store
+ section data in to create a file descriptor for it. */
+ const char *tmpdir = getenv ("TMPDIR") ?: P_tmpdir;
+ static const char suffix[] = "/readelfXXXXXX";
+ int tmplen = strlen (tmpdir) + sizeof (suffix);
+ char *tempname = alloca (tmplen);
+ sprintf (tempname, "%s%s", tmpdir, suffix);
+
+ int sfd = mkstemp (tempname);
+ if (sfd == -1)
+ {
+ error (0, 0, gettext ("cannot create temp file '%s'"),
+ tempname);
+ goto open_error;
+ }
+ unlink (tempname);
+
+ ssize_t size = data->d_size;
+ if (write_retry (sfd, data->d_buf, size) != size)
+ {
+ error (0, 0, gettext ("cannot write section data"));
+ goto open_error;
+ }
+
+ if (elf_end (elf) != 0)
+ {
+ error (0, 0, gettext ("error while closing Elf descriptor: %s"),
+ elf_errmsg (-1));
+ return -1;
+ }
+
+ if (lseek (sfd, 0, SEEK_SET) == -1)
+ {
+ error (0, 0, gettext ("error while rewinding file descriptor"));
+ return -1;
+ }
+
+ return sfd;
+ }
+ }
+
+ /* Named section not found. */
+ if (elf_end (elf) != 0)
+ error (0, 0, gettext ("error while closing Elf descriptor: %s"),
+ elf_errmsg (-1));
+ return -1;
+}
+
+/* Check if the file is an archive, and if so dump its index. */
+static void
+check_archive_index (int fd, const char *fname, bool only_one)
+{
+ /* Create an `Elf' descriptor. */
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ error (0, 0, gettext ("cannot generate Elf descriptor: %s"),
+ elf_errmsg (-1));
+ else
+ {
+ if (elf_kind (elf) == ELF_K_AR)
+ {
+ if (!only_one)
+ printf ("\n%s:\n\n", fname);
+ dump_archive_index (elf, fname);
+ }
+ else
+ error (0, 0,
+ gettext ("'%s' is not an archive, cannot print archive index"),
+ fname);
+
+ /* Now we can close the descriptor. */
+ if (elf_end (elf) != 0)
+ error (0, 0, gettext ("error while closing Elf descriptor: %s"),
+ elf_errmsg (-1));
+ }
+}
+
+/* Trivial callback used for checking if we opened an archive. */
+static int
+count_dwflmod (Dwfl_Module *dwflmod __attribute__ ((unused)),
+ void **userdata __attribute__ ((unused)),
+ const char *name __attribute__ ((unused)),
+ Dwarf_Addr base __attribute__ ((unused)),
+ void *arg)
+{
+ if (*(bool *) arg)
+ return DWARF_CB_ABORT;
+ *(bool *) arg = true;
+ return DWARF_CB_OK;
+}
+
+struct process_dwflmod_args
+{
+ int fd;
+ bool only_one;
+};
+
+static int
+process_dwflmod (Dwfl_Module *dwflmod,
+ void **userdata __attribute__ ((unused)),
+ const char *name __attribute__ ((unused)),
+ Dwarf_Addr base __attribute__ ((unused)),
+ void *arg)
+{
+ const struct process_dwflmod_args *a = arg;
+
+ /* Print the file name. */
+ if (!a->only_one)
+ {
+ const char *fname;
+ dwfl_module_info (dwflmod, NULL, NULL, NULL, NULL, NULL, &fname, NULL);
+
+ printf ("\n%s:\n\n", fname);
+ }
+
+ process_elf_file (dwflmod, a->fd);
+
+ return DWARF_CB_OK;
+}
+
+/* Stub libdwfl callback, only the ELF handle already open is ever used.
+ Only used for finding the alternate debug file if the Dwarf comes from
+ the main file. We are not interested in separate debuginfo. */
+static int
+find_no_debuginfo (Dwfl_Module *mod,
+ void **userdata,
+ const char *modname,
+ Dwarf_Addr base,
+ const char *file_name,
+ const char *debuglink_file,
+ GElf_Word debuglink_crc,
+ char **debuginfo_file_name)
+{
+ Dwarf_Addr dwbias;
+ dwfl_module_info (mod, NULL, NULL, NULL, &dwbias, NULL, NULL, NULL);
+
+ /* We are only interested if the Dwarf has been setup on the main
+ elf file but is only missing the alternate debug link. If dwbias
+ hasn't even been setup, this is searching for separate debuginfo
+ for the main elf. We don't care in that case. */
+ if (dwbias == (Dwarf_Addr) -1)
+ return -1;
+
+ return dwfl_standard_find_debuginfo (mod, userdata, modname, base,
+ file_name, debuglink_file,
+ debuglink_crc, debuginfo_file_name);
+}
+
+/* Process one input file. */
+static void
+process_file (int fd, const char *fname, bool only_one)
+{
+ if (print_archive_index)
+ check_archive_index (fd, fname, only_one);
+
+ if (!any_control_option)
+ return;
+
+ if (elf_input_section != NULL)
+ {
+ /* Replace fname and fd with section content. */
+ char *fnname = alloca (strlen (fname) + strlen (elf_input_section) + 2);
+ sprintf (fnname, "%s:%s", fname, elf_input_section);
+ fd = open_input_section (fd);
+ if (fd == -1)
+ {
+ error (0, 0, gettext ("No such section '%s' in '%s'"),
+ elf_input_section, fname);
+ return;
+ }
+ fname = fnname;
+ }
+
+ /* Duplicate an fd for dwfl_report_offline to swallow. */
+ int dwfl_fd = dup (fd);
+ if (unlikely (dwfl_fd < 0))
+ error (EXIT_FAILURE, errno, "dup");
+
+ /* Use libdwfl in a trivial way to open the libdw handle for us.
+ This takes care of applying relocations to DWARF data in ET_REL files. */
+ static const Dwfl_Callbacks callbacks =
+ {
+ .section_address = dwfl_offline_section_address,
+ .find_debuginfo = find_no_debuginfo
+ };
+ Dwfl *dwfl = dwfl_begin (&callbacks);
+ if (likely (dwfl != NULL))
+ /* Let 0 be the logical address of the file (or first in archive). */
+ dwfl->offline_next_address = 0;
+ if (dwfl_report_offline (dwfl, fname, fname, dwfl_fd) == NULL)
+ {
+ struct stat st;
+ if (fstat (dwfl_fd, &st) != 0)
+ error (0, errno, gettext ("cannot stat input file"));
+ else if (unlikely (st.st_size == 0))
+ error (0, 0, gettext ("input file is empty"));
+ else
+ error (0, 0, gettext ("failed reading '%s': %s"),
+ fname, dwfl_errmsg (-1));
+ close (dwfl_fd); /* Consumed on success, not on failure. */
+ }
+ else
+ {
+ dwfl_report_end (dwfl, NULL, NULL);
+
+ if (only_one)
+ {
+ /* Clear ONLY_ONE if we have multiple modules, from an archive. */
+ bool seen = false;
+ only_one = dwfl_getmodules (dwfl, &count_dwflmod, &seen, 0) == 0;
+ }
+
+ /* Process the one or more modules gleaned from this file. */
+ struct process_dwflmod_args a = { .fd = fd, .only_one = only_one };
+ dwfl_getmodules (dwfl, &process_dwflmod, &a, 0);
+ }
+ dwfl_end (dwfl);
+
+ /* Need to close the replaced fd if we created it. Caller takes
+ care of original. */
+ if (elf_input_section != NULL)
+ close (fd);
+}
+
+/* Check whether there are any compressed sections in the ELF file. */
+static bool
+elf_contains_chdrs (Elf *elf)
+{
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL && (shdr->sh_flags & SHF_COMPRESSED) != 0)
+ return true;
+ }
+ return false;
+}
+
+/* Process one ELF file. */
+static void
+process_elf_file (Dwfl_Module *dwflmod, int fd)
+{
+ GElf_Addr dwflbias;
+ Elf *elf = dwfl_module_getelf (dwflmod, &dwflbias);
+
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+
+ if (ehdr == NULL)
+ {
+ elf_error:
+ error (0, 0, gettext ("cannot read ELF header: %s"), elf_errmsg (-1));
+ return;
+ }
+
+ Ebl *ebl = ebl_openbackend (elf);
+ if (unlikely (ebl == NULL))
+ {
+ ebl_error:
+ error (0, errno, gettext ("cannot create EBL handle"));
+ return;
+ }
+
+ /* Determine the number of sections. */
+ if (unlikely (elf_getshdrnum (ebl->elf, &shnum) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot determine number of sections: %s"),
+ elf_errmsg (-1));
+
+ /* Determine the number of phdrs. */
+ if (unlikely (elf_getphdrnum (ebl->elf, &phnum) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot determine number of program headers: %s"),
+ elf_errmsg (-1));
+
+ /* For an ET_REL file, libdwfl has adjusted the in-core shdrs and
+ may have applied relocation to some sections. If there are any
+ compressed sections, any pass (or libdw/libdwfl) might have
+ uncompressed them. So we need to get a fresh Elf handle on the
+ file to display those. */
+ bool print_unchanged = ((print_section_header
+ || print_relocations
+ || dump_data_sections != NULL
+ || print_notes)
+ && (ehdr->e_type == ET_REL
+ || elf_contains_chdrs (ebl->elf)));
+
+ Elf *pure_elf = NULL;
+ Ebl *pure_ebl = ebl;
+ if (print_unchanged)
+ {
+ /* Read the file afresh. */
+ off_t aroff = elf_getaroff (elf);
+ pure_elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (aroff > 0)
+ {
+ /* Archive member. */
+ (void) elf_rand (pure_elf, aroff);
+ Elf *armem = elf_begin (-1, ELF_C_READ_MMAP, pure_elf);
+ elf_end (pure_elf);
+ pure_elf = armem;
+ }
+ if (pure_elf == NULL)
+ goto elf_error;
+ pure_ebl = ebl_openbackend (pure_elf);
+ if (pure_ebl == NULL)
+ goto ebl_error;
+ }
+
+ if (print_file_header)
+ print_ehdr (ebl, ehdr);
+ if (print_section_header)
+ print_shdr (pure_ebl, ehdr);
+ if (print_program_header)
+ print_phdr (ebl, ehdr);
+ if (print_section_groups)
+ print_scngrp (ebl);
+ if (print_dynamic_table)
+ print_dynamic (ebl);
+ if (print_relocations)
+ print_relocs (pure_ebl, ehdr);
+ if (print_histogram)
+ handle_hash (ebl);
+ if (print_symbol_table)
+ print_symtab (ebl, SHT_DYNSYM);
+ if (print_version_info)
+ print_verinfo (ebl);
+ if (print_symbol_table)
+ print_symtab (ebl, SHT_SYMTAB);
+ if (print_arch)
+ print_liblist (ebl);
+ if (print_arch)
+ print_attributes (ebl, ehdr);
+ if (dump_data_sections != NULL)
+ dump_data (pure_ebl);
+ if (string_sections != NULL)
+ dump_strings (ebl);
+ if ((print_debug_sections | implicit_debug_sections) != 0)
+ print_debug (dwflmod, ebl, ehdr);
+ if (print_notes)
+ handle_notes (pure_ebl, ehdr);
+ if (print_string_sections)
+ print_strings (ebl);
+
+ ebl_closebackend (ebl);
+
+ if (pure_ebl != ebl)
+ {
+ ebl_closebackend (pure_ebl);
+ elf_end (pure_elf);
+ }
+}
+
+
+/* Print file type. */
+static void
+print_file_type (unsigned short int e_type)
+{
+ if (likely (e_type <= ET_CORE))
+ {
+ static const char *const knowntypes[] =
+ {
+ N_("NONE (None)"),
+ N_("REL (Relocatable file)"),
+ N_("EXEC (Executable file)"),
+ N_("DYN (Shared object file)"),
+ N_("CORE (Core file)")
+ };
+ puts (gettext (knowntypes[e_type]));
+ }
+ else if (e_type >= ET_LOOS && e_type <= ET_HIOS)
+ printf (gettext ("OS Specific: (%x)\n"), e_type);
+ else if (e_type >= ET_LOPROC /* && e_type <= ET_HIPROC always true */)
+ printf (gettext ("Processor Specific: (%x)\n"), e_type);
+ else
+ puts ("???");
+}
+
+
+/* Print ELF header. */
+static void
+print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr)
+{
+ fputs_unlocked (gettext ("ELF Header:\n Magic: "), stdout);
+ for (size_t cnt = 0; cnt < EI_NIDENT; ++cnt)
+ printf (" %02hhx", ehdr->e_ident[cnt]);
+
+ printf (gettext ("\n Class: %s\n"),
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? "ELF32"
+ : ehdr->e_ident[EI_CLASS] == ELFCLASS64 ? "ELF64"
+ : "\?\?\?");
+
+ printf (gettext (" Data: %s\n"),
+ ehdr->e_ident[EI_DATA] == ELFDATA2LSB
+ ? "2's complement, little endian"
+ : ehdr->e_ident[EI_DATA] == ELFDATA2MSB
+ ? "2's complement, big endian" : "\?\?\?");
+
+ printf (gettext (" Ident Version: %hhd %s\n"),
+ ehdr->e_ident[EI_VERSION],
+ ehdr->e_ident[EI_VERSION] == EV_CURRENT ? gettext ("(current)")
+ : "(\?\?\?)");
+
+ char buf[512];
+ printf (gettext (" OS/ABI: %s\n"),
+ ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf)));
+
+ printf (gettext (" ABI Version: %hhd\n"),
+ ehdr->e_ident[EI_ABIVERSION]);
+
+ fputs_unlocked (gettext (" Type: "), stdout);
+ print_file_type (ehdr->e_type);
+
+ printf (gettext (" Machine: %s\n"), ebl->name);
+
+ printf (gettext (" Version: %d %s\n"),
+ ehdr->e_version,
+ ehdr->e_version == EV_CURRENT ? gettext ("(current)") : "(\?\?\?)");
+
+ printf (gettext (" Entry point address: %#" PRIx64 "\n"),
+ ehdr->e_entry);
+
+ printf (gettext (" Start of program headers: %" PRId64 " %s\n"),
+ ehdr->e_phoff, gettext ("(bytes into file)"));
+
+ printf (gettext (" Start of section headers: %" PRId64 " %s\n"),
+ ehdr->e_shoff, gettext ("(bytes into file)"));
+
+ printf (gettext (" Flags: %s\n"),
+ ebl_machine_flag_name (ebl, ehdr->e_flags, buf, sizeof (buf)));
+
+ printf (gettext (" Size of this header: %" PRId16 " %s\n"),
+ ehdr->e_ehsize, gettext ("(bytes)"));
+
+ printf (gettext (" Size of program header entries: %" PRId16 " %s\n"),
+ ehdr->e_phentsize, gettext ("(bytes)"));
+
+ printf (gettext (" Number of program headers entries: %" PRId16),
+ ehdr->e_phnum);
+ if (ehdr->e_phnum == PN_XNUM)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
+ if (shdr != NULL)
+ printf (gettext (" (%" PRIu32 " in [0].sh_info)"),
+ (uint32_t) shdr->sh_info);
+ else
+ fputs_unlocked (gettext (" ([0] not available)"), stdout);
+ }
+ fputc_unlocked ('\n', stdout);
+
+ printf (gettext (" Size of section header entries: %" PRId16 " %s\n"),
+ ehdr->e_shentsize, gettext ("(bytes)"));
+
+ printf (gettext (" Number of section headers entries: %" PRId16),
+ ehdr->e_shnum);
+ if (ehdr->e_shnum == 0)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
+ if (shdr != NULL)
+ printf (gettext (" (%" PRIu32 " in [0].sh_size)"),
+ (uint32_t) shdr->sh_size);
+ else
+ fputs_unlocked (gettext (" ([0] not available)"), stdout);
+ }
+ fputc_unlocked ('\n', stdout);
+
+ if (unlikely (ehdr->e_shstrndx == SHN_XINDEX))
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
+ if (shdr != NULL)
+ /* We managed to get the zeroth section. */
+ snprintf (buf, sizeof (buf), gettext (" (%" PRIu32 " in [0].sh_link)"),
+ (uint32_t) shdr->sh_link);
+ else
+ {
+ strncpy (buf, gettext (" ([0] not available)"), sizeof (buf));
+ buf[sizeof (buf) - 1] = '\0';
+ }
+
+ printf (gettext (" Section header string table index: XINDEX%s\n\n"),
+ buf);
+ }
+ else
+ printf (gettext (" Section header string table index: %" PRId16 "\n\n"),
+ ehdr->e_shstrndx);
+}
+
+
+static const char *
+get_visibility_type (int value)
+{
+ switch (value)
+ {
+ case STV_DEFAULT:
+ return "DEFAULT";
+ case STV_INTERNAL:
+ return "INTERNAL";
+ case STV_HIDDEN:
+ return "HIDDEN";
+ case STV_PROTECTED:
+ return "PROTECTED";
+ default:
+ return "???";
+ }
+}
+
+static const char *
+elf_ch_type_name (unsigned int code)
+{
+ if (code == 0)
+ return "NONE";
+
+ if (code == ELFCOMPRESS_ZLIB)
+ return "ZLIB";
+
+ return "UNKNOWN";
+}
+
+/* Print the section headers. */
+static void
+print_shdr (Ebl *ebl, GElf_Ehdr *ehdr)
+{
+ size_t cnt;
+ size_t shstrndx;
+
+ if (! print_file_header)
+ printf (gettext ("\
+There are %d section headers, starting at offset %#" PRIx64 ":\n\
+\n"),
+ ehdr->e_shnum, ehdr->e_shoff);
+
+ /* Get the section header string table index. */
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ puts (gettext ("Section Headers:"));
+
+ if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
+ puts (gettext ("[Nr] Name Type Addr Off Size ES Flags Lk Inf Al"));
+ else
+ puts (gettext ("[Nr] Name Type Addr Off Size ES Flags Lk Inf Al"));
+
+ if (print_decompress)
+ {
+ if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
+ puts (gettext (" [Compression Size Al]"));
+ else
+ puts (gettext (" [Compression Size Al]"));
+ }
+
+ for (cnt = 0; cnt < shnum; ++cnt)
+ {
+ Elf_Scn *scn = elf_getscn (ebl->elf, cnt);
+
+ if (unlikely (scn == NULL))
+ error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"),
+ elf_errmsg (-1));
+
+ /* Get the section header. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ error (EXIT_FAILURE, 0, gettext ("cannot get section header: %s"),
+ elf_errmsg (-1));
+
+ char flagbuf[20];
+ char *cp = flagbuf;
+ if (shdr->sh_flags & SHF_WRITE)
+ *cp++ = 'W';
+ if (shdr->sh_flags & SHF_ALLOC)
+ *cp++ = 'A';
+ if (shdr->sh_flags & SHF_EXECINSTR)
+ *cp++ = 'X';
+ if (shdr->sh_flags & SHF_MERGE)
+ *cp++ = 'M';
+ if (shdr->sh_flags & SHF_STRINGS)
+ *cp++ = 'S';
+ if (shdr->sh_flags & SHF_INFO_LINK)
+ *cp++ = 'I';
+ if (shdr->sh_flags & SHF_LINK_ORDER)
+ *cp++ = 'L';
+ if (shdr->sh_flags & SHF_OS_NONCONFORMING)
+ *cp++ = 'N';
+ if (shdr->sh_flags & SHF_GROUP)
+ *cp++ = 'G';
+ if (shdr->sh_flags & SHF_TLS)
+ *cp++ = 'T';
+ if (shdr->sh_flags & SHF_COMPRESSED)
+ *cp++ = 'C';
+ if (shdr->sh_flags & SHF_ORDERED)
+ *cp++ = 'O';
+ if (shdr->sh_flags & SHF_EXCLUDE)
+ *cp++ = 'E';
+ *cp = '\0';
+
+ const char *sname;
+ char buf[128];
+ sname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name) ?: "<corrupt>";
+ printf ("[%2zu] %-20s %-12s %0*" PRIx64 " %0*" PRIx64 " %0*" PRIx64
+ " %2" PRId64 " %-5s %2" PRId32 " %3" PRId32
+ " %2" PRId64 "\n",
+ cnt, sname,
+ ebl_section_type_name (ebl, shdr->sh_type, buf, sizeof (buf)),
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, shdr->sh_addr,
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_offset,
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_size,
+ shdr->sh_entsize, flagbuf, shdr->sh_link, shdr->sh_info,
+ shdr->sh_addralign);
+
+ if (print_decompress)
+ {
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ GElf_Chdr chdr;
+ if (gelf_getchdr (scn, &chdr) != NULL)
+ printf (" [ELF %s (%" PRId32 ") %0*" PRIx64
+ " %2" PRId64 "]\n",
+ elf_ch_type_name (chdr.ch_type),
+ chdr.ch_type,
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8,
+ chdr.ch_size, chdr.ch_addralign);
+ else
+ error (0, 0,
+ gettext ("bad compression header for section %zd: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+ }
+ else if (strncmp(".zdebug", sname, strlen (".zdebug")) == 0)
+ {
+ ssize_t size;
+ if ((size = dwelf_scn_gnu_compressed_size (scn)) >= 0)
+ printf (" [GNU ZLIB %0*zx ]\n",
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, size);
+ else
+ error (0, 0,
+ gettext ("bad gnu compressed size for section %zd: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+ }
+ }
+ }
+
+ fputc_unlocked ('\n', stdout);
+}
+
+
+/* Print the program header. */
+static void
+print_phdr (Ebl *ebl, GElf_Ehdr *ehdr)
+{
+ if (phnum == 0)
+ /* No program header, this is OK in relocatable objects. */
+ return;
+
+ puts (gettext ("Program Headers:"));
+ if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
+ puts (gettext ("\
+ Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align"));
+ else
+ puts (gettext ("\
+ Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align"));
+
+ /* Process all program headers. */
+ bool has_relro = false;
+ GElf_Addr relro_from = 0;
+ GElf_Addr relro_to = 0;
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ char buf[128];
+ GElf_Phdr mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &mem);
+
+ /* If for some reason the header cannot be returned show this. */
+ if (unlikely (phdr == NULL))
+ {
+ puts (" ???");
+ continue;
+ }
+
+ printf (" %-14s 0x%06" PRIx64 " 0x%0*" PRIx64 " 0x%0*" PRIx64
+ " 0x%06" PRIx64 " 0x%06" PRIx64 " %c%c%c 0x%" PRIx64 "\n",
+ ebl_segment_type_name (ebl, phdr->p_type, buf, sizeof (buf)),
+ phdr->p_offset,
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, phdr->p_vaddr,
+ ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, phdr->p_paddr,
+ phdr->p_filesz,
+ phdr->p_memsz,
+ phdr->p_flags & PF_R ? 'R' : ' ',
+ phdr->p_flags & PF_W ? 'W' : ' ',
+ phdr->p_flags & PF_X ? 'E' : ' ',
+ phdr->p_align);
+
+ if (phdr->p_type == PT_INTERP)
+ {
+ /* If we are sure the file offset is valid then we can show
+ the user the name of the interpreter. We check whether
+ there is a section at the file offset. Normally there
+ would be a section called ".interp". But in separate
+ .debug files it is a NOBITS section (and so doesn't match
+ with gelf_offscn). Which probably means the offset is
+ not valid another reason could be because the ELF file
+ just doesn't contain any section headers, in that case
+ just play it safe and don't display anything. */
+
+ Elf_Scn *scn = gelf_offscn (ebl->elf, phdr->p_offset);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ size_t maxsize;
+ char *filedata = elf_rawfile (ebl->elf, &maxsize);
+
+ if (shdr != NULL && shdr->sh_type == SHT_PROGBITS
+ && filedata != NULL && phdr->p_offset < maxsize
+ && phdr->p_filesz <= maxsize - phdr->p_offset
+ && memchr (filedata + phdr->p_offset, '\0',
+ phdr->p_filesz) != NULL)
+ printf (gettext ("\t[Requesting program interpreter: %s]\n"),
+ filedata + phdr->p_offset);
+ }
+ else if (phdr->p_type == PT_GNU_RELRO)
+ {
+ has_relro = true;
+ relro_from = phdr->p_vaddr;
+ relro_to = relro_from + phdr->p_memsz;
+ }
+ }
+
+ if (ehdr->e_shnum == 0)
+ /* No sections in the file. Punt. */
+ return;
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ puts (gettext ("\n Section to Segment mapping:\n Segment Sections..."));
+
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ /* Print the segment number. */
+ printf (" %2.2zu ", cnt);
+
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &phdr_mem);
+ /* This must not happen. */
+ if (unlikely (phdr == NULL))
+ error (EXIT_FAILURE, 0, gettext ("cannot get program header: %s"),
+ elf_errmsg (-1));
+
+ /* Iterate over the sections. */
+ bool in_relro = false;
+ bool in_ro = false;
+ for (size_t inner = 1; inner < shnum; ++inner)
+ {
+ Elf_Scn *scn = elf_getscn (ebl->elf, inner);
+ /* This should not happen. */
+ if (unlikely (scn == NULL))
+ error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"),
+ elf_errmsg (-1));
+
+ /* Get the section header. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header: %s"),
+ elf_errmsg (-1));
+
+ if (shdr->sh_size > 0
+ /* Compare allocated sections by VMA, unallocated
+ sections by file offset. */
+ && (shdr->sh_flags & SHF_ALLOC
+ ? (shdr->sh_addr >= phdr->p_vaddr
+ && (shdr->sh_addr + shdr->sh_size
+ <= phdr->p_vaddr + phdr->p_memsz))
+ : (shdr->sh_offset >= phdr->p_offset
+ && (shdr->sh_offset + shdr->sh_size
+ <= phdr->p_offset + phdr->p_filesz))))
+ {
+ if (has_relro && !in_relro
+ && shdr->sh_addr >= relro_from
+ && shdr->sh_addr + shdr->sh_size <= relro_to)
+ {
+ fputs_unlocked (" [RELRO:", stdout);
+ in_relro = true;
+ }
+ else if (has_relro && in_relro && shdr->sh_addr >= relro_to)
+ {
+ fputs_unlocked ("]", stdout);
+ in_relro = false;
+ }
+ else if (has_relro && in_relro
+ && shdr->sh_addr + shdr->sh_size > relro_to)
+ fputs_unlocked ("] <RELRO:", stdout);
+ else if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0)
+ {
+ if (!in_ro)
+ {
+ fputs_unlocked (" [RO:", stdout);
+ in_ro = true;
+ }
+ }
+ else
+ {
+ /* Determine the segment this section is part of. */
+ size_t cnt2;
+ GElf_Phdr phdr2_mem;
+ GElf_Phdr *phdr2 = NULL;
+ for (cnt2 = 0; cnt2 < phnum; ++cnt2)
+ {
+ phdr2 = gelf_getphdr (ebl->elf, cnt2, &phdr2_mem);
+
+ if (phdr2 != NULL && phdr2->p_type == PT_LOAD
+ && shdr->sh_addr >= phdr2->p_vaddr
+ && (shdr->sh_addr + shdr->sh_size
+ <= phdr2->p_vaddr + phdr2->p_memsz))
+ break;
+ }
+
+ if (cnt2 < phnum)
+ {
+ if ((phdr2->p_flags & PF_W) == 0 && !in_ro)
+ {
+ fputs_unlocked (" [RO:", stdout);
+ in_ro = true;
+ }
+ else if ((phdr2->p_flags & PF_W) != 0 && in_ro)
+ {
+ fputs_unlocked ("]", stdout);
+ in_ro = false;
+ }
+ }
+ }
+
+ printf (" %s",
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
+
+ /* Signal that this sectin is only partially covered. */
+ if (has_relro && in_relro
+ && shdr->sh_addr + shdr->sh_size > relro_to)
+ {
+ fputs_unlocked (">", stdout);
+ in_relro = false;
+ }
+ }
+ }
+ if (in_relro || in_ro)
+ fputs_unlocked ("]", stdout);
+
+ /* Finish the line. */
+ fputc_unlocked ('\n', stdout);
+ }
+}
+
+
+static const char *
+section_name (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr)
+{
+ return elf_strptr (ebl->elf, ehdr->e_shstrndx, shdr->sh_name) ?: "???";
+}
+
+
+static void
+handle_scngrp (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
+{
+ /* Get the data of the section. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+
+ if (data == NULL || data->d_size < sizeof (Elf32_Word) || symshdr == NULL
+ || symdata == NULL)
+ return;
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ Elf32_Word *grpref = (Elf32_Word *) data->d_buf;
+
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symdata, shdr->sh_info, &sym_mem);
+
+ printf ((grpref[0] & GRP_COMDAT)
+ ? ngettext ("\
+\nCOMDAT section group [%2zu] '%s' with signature '%s' contains %zu entry:\n",
+ "\
+\nCOMDAT section group [%2zu] '%s' with signature '%s' contains %zu entries:\n",
+ data->d_size / sizeof (Elf32_Word) - 1)
+ : ngettext ("\
+\nSection group [%2zu] '%s' with signature '%s' contains %zu entry:\n", "\
+\nSection group [%2zu] '%s' with signature '%s' contains %zu entries:\n",
+ data->d_size / sizeof (Elf32_Word) - 1),
+ elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ (sym == NULL ? NULL
+ : elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name))
+ ?: gettext ("<INVALID SYMBOL>"),
+ data->d_size / sizeof (Elf32_Word) - 1);
+
+ for (size_t cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt)
+ {
+ GElf_Shdr grpshdr_mem;
+ GElf_Shdr *grpshdr = gelf_getshdr (elf_getscn (ebl->elf, grpref[cnt]),
+ &grpshdr_mem);
+
+ const char *str;
+ printf (" [%2u] %s\n",
+ grpref[cnt],
+ grpshdr != NULL
+ && (str = elf_strptr (ebl->elf, shstrndx, grpshdr->sh_name))
+ ? str : gettext ("<INVALID SECTION>"));
+ }
+}
+
+
+static void
+print_scngrp (Ebl *ebl)
+{
+ /* Find all relocation sections and handle them. */
+ Elf_Scn *scn = NULL;
+
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ /* Handle the section if it is a symbol table. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr != NULL && shdr->sh_type == SHT_GROUP)
+ {
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ if (elf_compress (scn, 0, 0) < 0)
+ printf ("WARNING: %s [%zd]\n",
+ gettext ("Couldn't uncompress section"),
+ elf_ndxscn (scn));
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section [%zd] header: %s"),
+ elf_ndxscn (scn),
+ elf_errmsg (-1));
+ }
+ handle_scngrp (ebl, scn, shdr);
+ }
+ }
+}
+
+
+static const struct flags
+{
+ int mask;
+ const char *str;
+} dt_flags[] =
+ {
+ { DF_ORIGIN, "ORIGIN" },
+ { DF_SYMBOLIC, "SYMBOLIC" },
+ { DF_TEXTREL, "TEXTREL" },
+ { DF_BIND_NOW, "BIND_NOW" },
+ { DF_STATIC_TLS, "STATIC_TLS" }
+ };
+static const int ndt_flags = sizeof (dt_flags) / sizeof (dt_flags[0]);
+
+static const struct flags dt_flags_1[] =
+ {
+ { DF_1_NOW, "NOW" },
+ { DF_1_GLOBAL, "GLOBAL" },
+ { DF_1_GROUP, "GROUP" },
+ { DF_1_NODELETE, "NODELETE" },
+ { DF_1_LOADFLTR, "LOADFLTR" },
+ { DF_1_INITFIRST, "INITFIRST" },
+ { DF_1_NOOPEN, "NOOPEN" },
+ { DF_1_ORIGIN, "ORIGIN" },
+ { DF_1_DIRECT, "DIRECT" },
+ { DF_1_TRANS, "TRANS" },
+ { DF_1_INTERPOSE, "INTERPOSE" },
+ { DF_1_NODEFLIB, "NODEFLIB" },
+ { DF_1_NODUMP, "NODUMP" },
+ { DF_1_CONFALT, "CONFALT" },
+ { DF_1_ENDFILTEE, "ENDFILTEE" },
+ { DF_1_DISPRELDNE, "DISPRELDNE" },
+ { DF_1_DISPRELPND, "DISPRELPND" },
+ };
+static const int ndt_flags_1 = sizeof (dt_flags_1) / sizeof (dt_flags_1[0]);
+
+static const struct flags dt_feature_1[] =
+ {
+ { DTF_1_PARINIT, "PARINIT" },
+ { DTF_1_CONFEXP, "CONFEXP" }
+ };
+static const int ndt_feature_1 = (sizeof (dt_feature_1)
+ / sizeof (dt_feature_1[0]));
+
+static const struct flags dt_posflag_1[] =
+ {
+ { DF_P1_LAZYLOAD, "LAZYLOAD" },
+ { DF_P1_GROUPPERM, "GROUPPERM" }
+ };
+static const int ndt_posflag_1 = (sizeof (dt_posflag_1)
+ / sizeof (dt_posflag_1[0]));
+
+
+static void
+print_flags (int class, GElf_Xword d_val, const struct flags *flags,
+ int nflags)
+{
+ bool first = true;
+ int cnt;
+
+ for (cnt = 0; cnt < nflags; ++cnt)
+ if (d_val & flags[cnt].mask)
+ {
+ if (!first)
+ putchar_unlocked (' ');
+ fputs_unlocked (flags[cnt].str, stdout);
+ d_val &= ~flags[cnt].mask;
+ first = false;
+ }
+
+ if (d_val != 0)
+ {
+ if (!first)
+ putchar_unlocked (' ');
+ printf ("%#0*" PRIx64, class == ELFCLASS32 ? 10 : 18, d_val);
+ }
+
+ putchar_unlocked ('\n');
+}
+
+
+static void
+print_dt_flags (int class, GElf_Xword d_val)
+{
+ print_flags (class, d_val, dt_flags, ndt_flags);
+}
+
+
+static void
+print_dt_flags_1 (int class, GElf_Xword d_val)
+{
+ print_flags (class, d_val, dt_flags_1, ndt_flags_1);
+}
+
+
+static void
+print_dt_feature_1 (int class, GElf_Xword d_val)
+{
+ print_flags (class, d_val, dt_feature_1, ndt_feature_1);
+}
+
+
+static void
+print_dt_posflag_1 (int class, GElf_Xword d_val)
+{
+ print_flags (class, d_val, dt_posflag_1, ndt_posflag_1);
+}
+
+
+static void
+handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
+{
+ int class = gelf_getclass (ebl->elf);
+ GElf_Shdr glink_mem;
+ GElf_Shdr *glink;
+ Elf_Data *data;
+ size_t cnt;
+ size_t shstrndx;
+ size_t sh_entsize;
+
+ /* Get the data of the section. */
+ data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ return;
+
+ /* Get the section header string table index. */
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ sh_entsize = gelf_fsize (ebl->elf, ELF_T_DYN, 1, EV_CURRENT);
+
+ glink = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &glink_mem);
+ if (glink == NULL)
+ error (EXIT_FAILURE, 0, gettext ("invalid sh_link value in section %zu"),
+ elf_ndxscn (scn));
+
+ printf (ngettext ("\
+\nDynamic segment contains %lu entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n",
+ "\
+\nDynamic segment contains %lu entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n",
+ shdr->sh_size / sh_entsize),
+ (unsigned long int) (shdr->sh_size / sh_entsize),
+ class == ELFCLASS32 ? 10 : 18, shdr->sh_addr,
+ shdr->sh_offset,
+ (int) shdr->sh_link,
+ elf_strptr (ebl->elf, shstrndx, glink->sh_name));
+ fputs_unlocked (gettext (" Type Value\n"), stdout);
+
+ for (cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt)
+ {
+ GElf_Dyn dynmem;
+ GElf_Dyn *dyn = gelf_getdyn (data, cnt, &dynmem);
+ if (dyn == NULL)
+ break;
+
+ char buf[64];
+ printf (" %-17s ",
+ ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, sizeof (buf)));
+
+ switch (dyn->d_tag)
+ {
+ case DT_NULL:
+ case DT_DEBUG:
+ case DT_BIND_NOW:
+ case DT_TEXTREL:
+ /* No further output. */
+ fputc_unlocked ('\n', stdout);
+ break;
+
+ case DT_NEEDED:
+ printf (gettext ("Shared library: [%s]\n"),
+ elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val));
+ break;
+
+ case DT_SONAME:
+ printf (gettext ("Library soname: [%s]\n"),
+ elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val));
+ break;
+
+ case DT_RPATH:
+ printf (gettext ("Library rpath: [%s]\n"),
+ elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val));
+ break;
+
+ case DT_RUNPATH:
+ printf (gettext ("Library runpath: [%s]\n"),
+ elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val));
+ break;
+
+ case DT_PLTRELSZ:
+ case DT_RELASZ:
+ case DT_STRSZ:
+ case DT_RELSZ:
+ case DT_RELAENT:
+ case DT_SYMENT:
+ case DT_RELENT:
+ case DT_PLTPADSZ:
+ case DT_MOVEENT:
+ case DT_MOVESZ:
+ case DT_INIT_ARRAYSZ:
+ case DT_FINI_ARRAYSZ:
+ case DT_SYMINSZ:
+ case DT_SYMINENT:
+ case DT_GNU_CONFLICTSZ:
+ case DT_GNU_LIBLISTSZ:
+ printf (gettext ("%" PRId64 " (bytes)\n"), dyn->d_un.d_val);
+ break;
+
+ case DT_VERDEFNUM:
+ case DT_VERNEEDNUM:
+ case DT_RELACOUNT:
+ case DT_RELCOUNT:
+ printf ("%" PRId64 "\n", dyn->d_un.d_val);
+ break;
+
+ case DT_PLTREL:;
+ const char *tagname = ebl_dynamic_tag_name (ebl, dyn->d_un.d_val,
+ NULL, 0);
+ puts (tagname ?: "???");
+ break;
+
+ case DT_FLAGS:
+ print_dt_flags (class, dyn->d_un.d_val);
+ break;
+
+ case DT_FLAGS_1:
+ print_dt_flags_1 (class, dyn->d_un.d_val);
+ break;
+
+ case DT_FEATURE_1:
+ print_dt_feature_1 (class, dyn->d_un.d_val);
+ break;
+
+ case DT_POSFLAG_1:
+ print_dt_posflag_1 (class, dyn->d_un.d_val);
+ break;
+
+ default:
+ printf ("%#0*" PRIx64 "\n",
+ class == ELFCLASS32 ? 10 : 18, dyn->d_un.d_val);
+ break;
+ }
+ }
+}
+
+
+/* Print the dynamic segment. */
+static void
+print_dynamic (Ebl *ebl)
+{
+ for (size_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem);
+
+ if (phdr != NULL && phdr->p_type == PT_DYNAMIC)
+ {
+ Elf_Scn *scn = gelf_offscn (ebl->elf, phdr->p_offset);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC)
+ handle_dynamic (ebl, scn, shdr);
+ break;
+ }
+ }
+}
+
+
+/* Print relocations. */
+static void
+print_relocs (Ebl *ebl, GElf_Ehdr *ehdr)
+{
+ /* Find all relocation sections and handle them. */
+ Elf_Scn *scn = NULL;
+
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ /* Handle the section if it is a symbol table. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (likely (shdr != NULL))
+ {
+ if (shdr->sh_type == SHT_REL)
+ handle_relocs_rel (ebl, ehdr, scn, shdr);
+ else if (shdr->sh_type == SHT_RELA)
+ handle_relocs_rela (ebl, ehdr, scn, shdr);
+ }
+ }
+}
+
+
+/* Handle a relocation section. */
+static void
+handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr)
+{
+ int class = gelf_getclass (ebl->elf);
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_REL, 1, EV_CURRENT);
+ int nentries = shdr->sh_size / sh_entsize;
+
+ /* Get the data of the section. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ return;
+
+ /* Get the symbol table information. */
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+
+ /* Get the section header of the section the relocations are for. */
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info),
+ &destshdr_mem);
+
+ if (unlikely (symshdr == NULL || symdata == NULL || destshdr == NULL))
+ {
+ printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"),
+ shdr->sh_offset);
+ return;
+ }
+
+ /* Search for the optional extended section index table. */
+ Elf_Data *xndxdata = NULL;
+ int xndxscnidx = elf_scnshndx (scn);
+ if (unlikely (xndxscnidx > 0))
+ xndxdata = elf_getdata (elf_getscn (ebl->elf, xndxscnidx), NULL);
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ if (shdr->sh_info != 0)
+ printf (ngettext ("\
+\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
+ "\
+\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n",
+ nentries),
+ elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ (unsigned int) shdr->sh_info,
+ elf_strptr (ebl->elf, shstrndx, destshdr->sh_name),
+ shdr->sh_offset,
+ nentries);
+ else
+ /* The .rel.dyn section does not refer to a specific section but
+ instead of section index zero. Do not try to print a section
+ name. */
+ printf (ngettext ("\
+\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
+ "\
+\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n",
+ nentries),
+ (unsigned int) elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ shdr->sh_offset,
+ nentries);
+ fputs_unlocked (class == ELFCLASS32
+ ? gettext ("\
+ Offset Type Value Name\n")
+ : gettext ("\
+ Offset Type Value Name\n"),
+ stdout);
+
+ int is_statically_linked = 0;
+ for (int cnt = 0; cnt < nentries; ++cnt)
+ {
+ GElf_Rel relmem;
+ GElf_Rel *rel = gelf_getrel (data, cnt, &relmem);
+ if (likely (rel != NULL))
+ {
+ char buf[128];
+ GElf_Sym symmem;
+ Elf32_Word xndx;
+ GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata,
+ GELF_R_SYM (rel->r_info),
+ &symmem, &xndx);
+ if (unlikely (sym == NULL))
+ {
+ /* As a special case we have to handle relocations in static
+ executables. This only happens for IRELATIVE relocations
+ (so far). There is no symbol table. */
+ if (is_statically_linked == 0)
+ {
+ /* Find the program header and look for a PT_INTERP entry. */
+ is_statically_linked = -1;
+ if (ehdr->e_type == ET_EXEC)
+ {
+ is_statically_linked = 1;
+
+ for (size_t inner = 0; inner < phnum; ++inner)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, inner,
+ &phdr_mem);
+ if (phdr != NULL && phdr->p_type == PT_INTERP)
+ {
+ is_statically_linked = -1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (is_statically_linked > 0 && shdr->sh_link == 0)
+ printf ("\
+ %#0*" PRIx64 " %-20s %*s %s\n",
+ class == ELFCLASS32 ? 10 : 18, rel->r_offset,
+ ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
+ /* Avoid the leading R_ which isn't carrying any
+ information. */
+ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
+ buf, sizeof (buf)) + 2
+ : gettext ("<INVALID RELOC>"),
+ class == ELFCLASS32 ? 10 : 18, "",
+ elf_strptr (ebl->elf, shstrndx, destshdr->sh_name));
+ else
+ printf (" %#0*" PRIx64 " %-20s <%s %ld>\n",
+ class == ELFCLASS32 ? 10 : 18, rel->r_offset,
+ ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
+ /* Avoid the leading R_ which isn't carrying any
+ information. */
+ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
+ buf, sizeof (buf)) + 2
+ : gettext ("<INVALID RELOC>"),
+ gettext ("INVALID SYMBOL"),
+ (long int) GELF_R_SYM (rel->r_info));
+ }
+ else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION)
+ printf (" %#0*" PRIx64 " %-20s %#0*" PRIx64 " %s\n",
+ class == ELFCLASS32 ? 10 : 18, rel->r_offset,
+ likely (ebl_reloc_type_check (ebl,
+ GELF_R_TYPE (rel->r_info)))
+ /* Avoid the leading R_ which isn't carrying any
+ information. */
+ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
+ buf, sizeof (buf)) + 2
+ : gettext ("<INVALID RELOC>"),
+ class == ELFCLASS32 ? 10 : 18, sym->st_value,
+ elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name));
+ else
+ {
+ /* This is a relocation against a STT_SECTION symbol. */
+ GElf_Shdr secshdr_mem;
+ GElf_Shdr *secshdr;
+ secshdr = gelf_getshdr (elf_getscn (ebl->elf,
+ sym->st_shndx == SHN_XINDEX
+ ? xndx : sym->st_shndx),
+ &secshdr_mem);
+
+ if (unlikely (secshdr == NULL))
+ printf (" %#0*" PRIx64 " %-20s <%s %ld>\n",
+ class == ELFCLASS32 ? 10 : 18, rel->r_offset,
+ ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
+ /* Avoid the leading R_ which isn't carrying any
+ information. */
+ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
+ buf, sizeof (buf)) + 2
+ : gettext ("<INVALID RELOC>"),
+ gettext ("INVALID SECTION"),
+ (long int) (sym->st_shndx == SHN_XINDEX
+ ? xndx : sym->st_shndx));
+ else
+ printf (" %#0*" PRIx64 " %-20s %#0*" PRIx64 " %s\n",
+ class == ELFCLASS32 ? 10 : 18, rel->r_offset,
+ ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
+ /* Avoid the leading R_ which isn't carrying any
+ information. */
+ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
+ buf, sizeof (buf)) + 2
+ : gettext ("<INVALID RELOC>"),
+ class == ELFCLASS32 ? 10 : 18, sym->st_value,
+ elf_strptr (ebl->elf, shstrndx, secshdr->sh_name));
+ }
+ }
+ }
+}
+
+
+/* Handle a relocation section. */
+static void
+handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr)
+{
+ int class = gelf_getclass (ebl->elf);
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_RELA, 1, EV_CURRENT);
+ int nentries = shdr->sh_size / sh_entsize;
+
+ /* Get the data of the section. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ return;
+
+ /* Get the symbol table information. */
+ Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+
+ /* Get the section header of the section the relocations are for. */
+ GElf_Shdr destshdr_mem;
+ GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info),
+ &destshdr_mem);
+
+ if (unlikely (symshdr == NULL || symdata == NULL || destshdr == NULL))
+ {
+ printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"),
+ shdr->sh_offset);
+ return;
+ }
+
+ /* Search for the optional extended section index table. */
+ Elf_Data *xndxdata = NULL;
+ int xndxscnidx = elf_scnshndx (scn);
+ if (unlikely (xndxscnidx > 0))
+ xndxdata = elf_getdata (elf_getscn (ebl->elf, xndxscnidx), NULL);
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ if (shdr->sh_info != 0)
+ printf (ngettext ("\
+\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
+ "\
+\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n",
+ nentries),
+ elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ (unsigned int) shdr->sh_info,
+ elf_strptr (ebl->elf, shstrndx, destshdr->sh_name),
+ shdr->sh_offset,
+ nentries);
+ else
+ /* The .rela.dyn section does not refer to a specific section but
+ instead of section index zero. Do not try to print a section
+ name. */
+ printf (ngettext ("\
+\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
+ "\
+\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n",
+ nentries),
+ (unsigned int) elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ shdr->sh_offset,
+ nentries);
+ fputs_unlocked (class == ELFCLASS32
+ ? gettext ("\
+ Offset Type Value Addend Name\n")
+ : gettext ("\
+ Offset Type Value Addend Name\n"),
+ stdout);
+
+ int is_statically_linked = 0;
+ for (int cnt = 0; cnt < nentries; ++cnt)
+ {
+ GElf_Rela relmem;
+ GElf_Rela *rel = gelf_getrela (data, cnt, &relmem);
+ if (likely (rel != NULL))
+ {
+ char buf[64];
+ GElf_Sym symmem;
+ Elf32_Word xndx;
+ GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata,
+ GELF_R_SYM (rel->r_info),
+ &symmem, &xndx);
+
+ if (unlikely (sym == NULL))
+ {
+ /* As a special case we have to handle relocations in static
+ executables. This only happens for IRELATIVE relocations
+ (so far). There is no symbol table. */
+ if (is_statically_linked == 0)
+ {
+ /* Find the program header and look for a PT_INTERP entry. */
+ is_statically_linked = -1;
+ if (ehdr->e_type == ET_EXEC)
+ {
+ is_statically_linked = 1;
+
+ for (size_t inner = 0; inner < phnum; ++inner)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, inner,
+ &phdr_mem);
+ if (phdr != NULL && phdr->p_type == PT_INTERP)
+ {
+ is_statically_linked = -1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (is_statically_linked > 0 && shdr->sh_link == 0)
+ printf ("\
+ %#0*" PRIx64 " %-15s %*s %#6" PRIx64 " %s\n",
+ class == ELFCLASS32 ? 10 : 18, rel->r_offset,
+ ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
+ /* Avoid the leading R_ which isn't carrying any
+ information. */
+ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
+ buf, sizeof (buf)) + 2
+ : gettext ("<INVALID RELOC>"),
+ class == ELFCLASS32 ? 10 : 18, "",
+ rel->r_addend,
+ elf_strptr (ebl->elf, shstrndx, destshdr->sh_name));
+ else
+ printf (" %#0*" PRIx64 " %-15s <%s %ld>\n",
+ class == ELFCLASS32 ? 10 : 18, rel->r_offset,
+ ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
+ /* Avoid the leading R_ which isn't carrying any
+ information. */
+ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
+ buf, sizeof (buf)) + 2
+ : gettext ("<INVALID RELOC>"),
+ gettext ("INVALID SYMBOL"),
+ (long int) GELF_R_SYM (rel->r_info));
+ }
+ else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION)
+ printf ("\
+ %#0*" PRIx64 " %-15s %#0*" PRIx64 " %+6" PRId64 " %s\n",
+ class == ELFCLASS32 ? 10 : 18, rel->r_offset,
+ likely (ebl_reloc_type_check (ebl,
+ GELF_R_TYPE (rel->r_info)))
+ /* Avoid the leading R_ which isn't carrying any
+ information. */
+ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
+ buf, sizeof (buf)) + 2
+ : gettext ("<INVALID RELOC>"),
+ class == ELFCLASS32 ? 10 : 18, sym->st_value,
+ rel->r_addend,
+ elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name));
+ else
+ {
+ /* This is a relocation against a STT_SECTION symbol. */
+ GElf_Shdr secshdr_mem;
+ GElf_Shdr *secshdr;
+ secshdr = gelf_getshdr (elf_getscn (ebl->elf,
+ sym->st_shndx == SHN_XINDEX
+ ? xndx : sym->st_shndx),
+ &secshdr_mem);
+
+ if (unlikely (secshdr == NULL))
+ printf (" %#0*" PRIx64 " %-15s <%s %ld>\n",
+ class == ELFCLASS32 ? 10 : 18, rel->r_offset,
+ ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
+ /* Avoid the leading R_ which isn't carrying any
+ information. */
+ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
+ buf, sizeof (buf)) + 2
+ : gettext ("<INVALID RELOC>"),
+ gettext ("INVALID SECTION"),
+ (long int) (sym->st_shndx == SHN_XINDEX
+ ? xndx : sym->st_shndx));
+ else
+ printf ("\
+ %#0*" PRIx64 " %-15s %#0*" PRIx64 " %+6" PRId64 " %s\n",
+ class == ELFCLASS32 ? 10 : 18, rel->r_offset,
+ ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
+ /* Avoid the leading R_ which isn't carrying any
+ information. */
+ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
+ buf, sizeof (buf)) + 2
+ : gettext ("<INVALID RELOC>"),
+ class == ELFCLASS32 ? 10 : 18, sym->st_value,
+ rel->r_addend,
+ elf_strptr (ebl->elf, shstrndx, secshdr->sh_name));
+ }
+ }
+ }
+}
+
+
+/* Print the program header. */
+static void
+print_symtab (Ebl *ebl, int type)
+{
+ /* Find the symbol table(s). For this we have to search through the
+ section table. */
+ Elf_Scn *scn = NULL;
+
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ /* Handle the section if it is a symbol table. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr != NULL && shdr->sh_type == (GElf_Word) type)
+ {
+ if (symbol_table_section != NULL)
+ {
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ const char *sname;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+ sname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name);
+ if (sname == NULL || strcmp (sname, symbol_table_section) != 0)
+ continue;
+ }
+
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ if (elf_compress (scn, 0, 0) < 0)
+ printf ("WARNING: %s [%zd]\n",
+ gettext ("Couldn't uncompress section"),
+ elf_ndxscn (scn));
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section [%zd] header: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+ }
+ handle_symtab (ebl, scn, shdr);
+ }
+ }
+}
+
+
+static void
+handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
+{
+ Elf_Data *versym_data = NULL;
+ Elf_Data *verneed_data = NULL;
+ Elf_Data *verdef_data = NULL;
+ Elf_Data *xndx_data = NULL;
+ int class = gelf_getclass (ebl->elf);
+ Elf32_Word verneed_stridx = 0;
+ Elf32_Word verdef_stridx = 0;
+
+ /* Get the data of the section. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ return;
+
+ /* Find out whether we have other sections we might need. */
+ Elf_Scn *runscn = NULL;
+ while ((runscn = elf_nextscn (ebl->elf, runscn)) != NULL)
+ {
+ GElf_Shdr runshdr_mem;
+ GElf_Shdr *runshdr = gelf_getshdr (runscn, &runshdr_mem);
+
+ if (likely (runshdr != NULL))
+ {
+ if (runshdr->sh_type == SHT_GNU_versym
+ && runshdr->sh_link == elf_ndxscn (scn))
+ /* Bingo, found the version information. Now get the data. */
+ versym_data = elf_getdata (runscn, NULL);
+ else if (runshdr->sh_type == SHT_GNU_verneed)
+ {
+ /* This is the information about the needed versions. */
+ verneed_data = elf_getdata (runscn, NULL);
+ verneed_stridx = runshdr->sh_link;
+ }
+ else if (runshdr->sh_type == SHT_GNU_verdef)
+ {
+ /* This is the information about the defined versions. */
+ verdef_data = elf_getdata (runscn, NULL);
+ verdef_stridx = runshdr->sh_link;
+ }
+ else if (runshdr->sh_type == SHT_SYMTAB_SHNDX
+ && runshdr->sh_link == elf_ndxscn (scn))
+ /* Extended section index. */
+ xndx_data = elf_getdata (runscn, NULL);
+ }
+ }
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ GElf_Shdr glink_mem;
+ GElf_Shdr *glink = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &glink_mem);
+ if (glink == NULL)
+ error (EXIT_FAILURE, 0, gettext ("invalid sh_link value in section %zu"),
+ elf_ndxscn (scn));
+
+ /* Now we can compute the number of entries in the section. */
+ unsigned int nsyms = data->d_size / (class == ELFCLASS32
+ ? sizeof (Elf32_Sym)
+ : sizeof (Elf64_Sym));
+
+ printf (ngettext ("\nSymbol table [%2u] '%s' contains %u entry:\n",
+ "\nSymbol table [%2u] '%s' contains %u entries:\n",
+ nsyms),
+ (unsigned int) elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name), nsyms);
+ printf (ngettext (" %lu local symbol String table: [%2u] '%s'\n",
+ " %lu local symbols String table: [%2u] '%s'\n",
+ shdr->sh_info),
+ (unsigned long int) shdr->sh_info,
+ (unsigned int) shdr->sh_link,
+ elf_strptr (ebl->elf, shstrndx, glink->sh_name));
+
+ fputs_unlocked (class == ELFCLASS32
+ ? gettext ("\
+ Num: Value Size Type Bind Vis Ndx Name\n")
+ : gettext ("\
+ Num: Value Size Type Bind Vis Ndx Name\n"),
+ stdout);
+
+ for (unsigned int cnt = 0; cnt < nsyms; ++cnt)
+ {
+ char typebuf[64];
+ char bindbuf[64];
+ char scnbuf[64];
+ Elf32_Word xndx;
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsymshndx (data, xndx_data, cnt, &sym_mem, &xndx);
+
+ if (unlikely (sym == NULL))
+ continue;
+
+ /* Determine the real section index. */
+ if (likely (sym->st_shndx != SHN_XINDEX))
+ xndx = sym->st_shndx;
+
+ printf (gettext ("\
+%5u: %0*" PRIx64 " %6" PRId64 " %-7s %-6s %-9s %6s %s"),
+ cnt,
+ class == ELFCLASS32 ? 8 : 16,
+ sym->st_value,
+ sym->st_size,
+ ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info),
+ typebuf, sizeof (typebuf)),
+ ebl_symbol_binding_name (ebl, GELF_ST_BIND (sym->st_info),
+ bindbuf, sizeof (bindbuf)),
+ get_visibility_type (GELF_ST_VISIBILITY (sym->st_other)),
+ ebl_section_name (ebl, sym->st_shndx, xndx, scnbuf,
+ sizeof (scnbuf), NULL, shnum),
+ elf_strptr (ebl->elf, shdr->sh_link, sym->st_name));
+
+ if (versym_data != NULL)
+ {
+ /* Get the version information. */
+ GElf_Versym versym_mem;
+ GElf_Versym *versym = gelf_getversym (versym_data, cnt, &versym_mem);
+
+ if (versym != NULL && ((*versym & 0x8000) != 0 || *versym > 1))
+ {
+ bool is_nobits = false;
+ bool check_def = xndx != SHN_UNDEF;
+
+ if (xndx < SHN_LORESERVE || sym->st_shndx == SHN_XINDEX)
+ {
+ GElf_Shdr symshdr_mem;
+ GElf_Shdr *symshdr =
+ gelf_getshdr (elf_getscn (ebl->elf, xndx), &symshdr_mem);
+
+ is_nobits = (symshdr != NULL
+ && symshdr->sh_type == SHT_NOBITS);
+ }
+
+ if (is_nobits || ! check_def)
+ {
+ /* We must test both. */
+ GElf_Vernaux vernaux_mem;
+ GElf_Vernaux *vernaux = NULL;
+ size_t vn_offset = 0;
+
+ GElf_Verneed verneed_mem;
+ GElf_Verneed *verneed = gelf_getverneed (verneed_data, 0,
+ &verneed_mem);
+ while (verneed != NULL)
+ {
+ size_t vna_offset = vn_offset;
+
+ vernaux = gelf_getvernaux (verneed_data,
+ vna_offset += verneed->vn_aux,
+ &vernaux_mem);
+ while (vernaux != NULL
+ && vernaux->vna_other != *versym
+ && vernaux->vna_next != 0)
+ {
+ /* Update the offset. */
+ vna_offset += vernaux->vna_next;
+
+ vernaux = (vernaux->vna_next == 0
+ ? NULL
+ : gelf_getvernaux (verneed_data,
+ vna_offset,
+ &vernaux_mem));
+ }
+
+ /* Check whether we found the version. */
+ if (vernaux != NULL && vernaux->vna_other == *versym)
+ /* Found it. */
+ break;
+
+ vn_offset += verneed->vn_next;
+ verneed = (verneed->vn_next == 0
+ ? NULL
+ : gelf_getverneed (verneed_data, vn_offset,
+ &verneed_mem));
+ }
+
+ if (vernaux != NULL && vernaux->vna_other == *versym)
+ {
+ printf ("@%s (%u)",
+ elf_strptr (ebl->elf, verneed_stridx,
+ vernaux->vna_name),
+ (unsigned int) vernaux->vna_other);
+ check_def = 0;
+ }
+ else if (unlikely (! is_nobits))
+ error (0, 0, gettext ("bad dynamic symbol"));
+ else
+ check_def = 1;
+ }
+
+ if (check_def && *versym != 0x8001)
+ {
+ /* We must test both. */
+ size_t vd_offset = 0;
+
+ GElf_Verdef verdef_mem;
+ GElf_Verdef *verdef = gelf_getverdef (verdef_data, 0,
+ &verdef_mem);
+ while (verdef != NULL)
+ {
+ if (verdef->vd_ndx == (*versym & 0x7fff))
+ /* Found the definition. */
+ break;
+
+ vd_offset += verdef->vd_next;
+ verdef = (verdef->vd_next == 0
+ ? NULL
+ : gelf_getverdef (verdef_data, vd_offset,
+ &verdef_mem));
+ }
+
+ if (verdef != NULL)
+ {
+ GElf_Verdaux verdaux_mem;
+ GElf_Verdaux *verdaux
+ = gelf_getverdaux (verdef_data,
+ vd_offset + verdef->vd_aux,
+ &verdaux_mem);
+
+ if (verdaux != NULL)
+ printf ((*versym & 0x8000) ? "@%s" : "@@%s",
+ elf_strptr (ebl->elf, verdef_stridx,
+ verdaux->vda_name));
+ }
+ }
+ }
+ }
+
+ putchar_unlocked ('\n');
+ }
+}
+
+
+/* Print version information. */
+static void
+print_verinfo (Ebl *ebl)
+{
+ /* Find the version information sections. For this we have to
+ search through the section table. */
+ Elf_Scn *scn = NULL;
+
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ /* Handle the section if it is part of the versioning handling. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (likely (shdr != NULL))
+ {
+ if (shdr->sh_type == SHT_GNU_verneed)
+ handle_verneed (ebl, scn, shdr);
+ else if (shdr->sh_type == SHT_GNU_verdef)
+ handle_verdef (ebl, scn, shdr);
+ else if (shdr->sh_type == SHT_GNU_versym)
+ handle_versym (ebl, scn, shdr);
+ }
+ }
+}
+
+
+static const char *
+get_ver_flags (unsigned int flags)
+{
+ static char buf[32];
+ char *endp;
+
+ if (flags == 0)
+ return gettext ("none");
+
+ if (flags & VER_FLG_BASE)
+ endp = stpcpy (buf, "BASE ");
+ else
+ endp = buf;
+
+ if (flags & VER_FLG_WEAK)
+ {
+ if (endp != buf)
+ endp = stpcpy (endp, "| ");
+
+ endp = stpcpy (endp, "WEAK ");
+ }
+
+ if (unlikely (flags & ~(VER_FLG_BASE | VER_FLG_WEAK)))
+ {
+ strncpy (endp, gettext ("| <unknown>"), buf + sizeof (buf) - endp);
+ buf[sizeof (buf) - 1] = '\0';
+ }
+
+ return buf;
+}
+
+
+static void
+handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
+{
+ int class = gelf_getclass (ebl->elf);
+
+ /* Get the data of the section. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ return;
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ GElf_Shdr glink_mem;
+ GElf_Shdr *glink = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &glink_mem);
+ if (glink == NULL)
+ error (EXIT_FAILURE, 0, gettext ("invalid sh_link value in section %zu"),
+ elf_ndxscn (scn));
+
+ printf (ngettext ("\
+\nVersion needs section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n",
+ "\
+\nVersion needs section [%2u] '%s' contains %d entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n",
+ shdr->sh_info),
+ (unsigned int) elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name), shdr->sh_info,
+ class == ELFCLASS32 ? 10 : 18, shdr->sh_addr,
+ shdr->sh_offset,
+ (unsigned int) shdr->sh_link,
+ elf_strptr (ebl->elf, shstrndx, glink->sh_name));
+
+ unsigned int offset = 0;
+ for (int cnt = shdr->sh_info; --cnt >= 0; )
+ {
+ /* Get the data at the next offset. */
+ GElf_Verneed needmem;
+ GElf_Verneed *need = gelf_getverneed (data, offset, &needmem);
+ if (unlikely (need == NULL))
+ break;
+
+ printf (gettext (" %#06x: Version: %hu File: %s Cnt: %hu\n"),
+ offset, (unsigned short int) need->vn_version,
+ elf_strptr (ebl->elf, shdr->sh_link, need->vn_file),
+ (unsigned short int) need->vn_cnt);
+
+ unsigned int auxoffset = offset + need->vn_aux;
+ for (int cnt2 = need->vn_cnt; --cnt2 >= 0; )
+ {
+ GElf_Vernaux auxmem;
+ GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem);
+ if (unlikely (aux == NULL))
+ break;
+
+ printf (gettext (" %#06x: Name: %s Flags: %s Version: %hu\n"),
+ auxoffset,
+ elf_strptr (ebl->elf, shdr->sh_link, aux->vna_name),
+ get_ver_flags (aux->vna_flags),
+ (unsigned short int) aux->vna_other);
+
+ if (aux->vna_next == 0)
+ break;
+
+ auxoffset += aux->vna_next;
+ }
+
+ /* Find the next offset. */
+ if (need->vn_next == 0)
+ break;
+
+ offset += need->vn_next;
+ }
+}
+
+
+static void
+handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
+{
+ /* Get the data of the section. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ return;
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ GElf_Shdr glink_mem;
+ GElf_Shdr *glink = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &glink_mem);
+ if (glink == NULL)
+ error (EXIT_FAILURE, 0, gettext ("invalid sh_link value in section %zu"),
+ elf_ndxscn (scn));
+
+ int class = gelf_getclass (ebl->elf);
+ printf (ngettext ("\
+\nVersion definition section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n",
+ "\
+\nVersion definition section [%2u] '%s' contains %d entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n",
+ shdr->sh_info),
+ (unsigned int) elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ shdr->sh_info,
+ class == ELFCLASS32 ? 10 : 18, shdr->sh_addr,
+ shdr->sh_offset,
+ (unsigned int) shdr->sh_link,
+ elf_strptr (ebl->elf, shstrndx, glink->sh_name));
+
+ unsigned int offset = 0;
+ for (int cnt = shdr->sh_info; --cnt >= 0; )
+ {
+ /* Get the data at the next offset. */
+ GElf_Verdef defmem;
+ GElf_Verdef *def = gelf_getverdef (data, offset, &defmem);
+ if (unlikely (def == NULL))
+ break;
+
+ unsigned int auxoffset = offset + def->vd_aux;
+ GElf_Verdaux auxmem;
+ GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem);
+ if (unlikely (aux == NULL))
+ break;
+
+ printf (gettext ("\
+ %#06x: Version: %hd Flags: %s Index: %hd Cnt: %hd Name: %s\n"),
+ offset, def->vd_version,
+ get_ver_flags (def->vd_flags),
+ def->vd_ndx,
+ def->vd_cnt,
+ elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name));
+
+ auxoffset += aux->vda_next;
+ for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2)
+ {
+ aux = gelf_getverdaux (data, auxoffset, &auxmem);
+ if (unlikely (aux == NULL))
+ break;
+
+ printf (gettext (" %#06x: Parent %d: %s\n"),
+ auxoffset, cnt2,
+ elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name));
+
+ if (aux->vda_next == 0)
+ break;
+
+ auxoffset += aux->vda_next;
+ }
+
+ /* Find the next offset. */
+ if (def->vd_next == 0)
+ break;
+ offset += def->vd_next;
+ }
+}
+
+
+static void
+handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
+{
+ int class = gelf_getclass (ebl->elf);
+ const char **vername;
+ const char **filename;
+
+ /* Get the data of the section. */
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ return;
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ /* We have to find the version definition section and extract the
+ version names. */
+ Elf_Scn *defscn = NULL;
+ Elf_Scn *needscn = NULL;
+
+ Elf_Scn *verscn = NULL;
+ while ((verscn = elf_nextscn (ebl->elf, verscn)) != NULL)
+ {
+ GElf_Shdr vershdr_mem;
+ GElf_Shdr *vershdr = gelf_getshdr (verscn, &vershdr_mem);
+
+ if (likely (vershdr != NULL))
+ {
+ if (vershdr->sh_type == SHT_GNU_verdef)
+ defscn = verscn;
+ else if (vershdr->sh_type == SHT_GNU_verneed)
+ needscn = verscn;
+ }
+ }
+
+ size_t nvername;
+ if (defscn != NULL || needscn != NULL)
+ {
+ /* We have a version information (better should have). Now get
+ the version names. First find the maximum version number. */
+ nvername = 0;
+ if (defscn != NULL)
+ {
+ /* Run through the version definitions and find the highest
+ index. */
+ unsigned int offset = 0;
+ Elf_Data *defdata;
+ GElf_Shdr defshdrmem;
+ GElf_Shdr *defshdr;
+
+ defdata = elf_getdata (defscn, NULL);
+ if (unlikely (defdata == NULL))
+ return;
+
+ defshdr = gelf_getshdr (defscn, &defshdrmem);
+ if (unlikely (defshdr == NULL))
+ return;
+
+ for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt)
+ {
+ GElf_Verdef defmem;
+ GElf_Verdef *def;
+
+ /* Get the data at the next offset. */
+ def = gelf_getverdef (defdata, offset, &defmem);
+ if (unlikely (def == NULL))
+ break;
+
+ nvername = MAX (nvername, (size_t) (def->vd_ndx & 0x7fff));
+
+ if (def->vd_next == 0)
+ break;
+ offset += def->vd_next;
+ }
+ }
+ if (needscn != NULL)
+ {
+ unsigned int offset = 0;
+ Elf_Data *needdata;
+ GElf_Shdr needshdrmem;
+ GElf_Shdr *needshdr;
+
+ needdata = elf_getdata (needscn, NULL);
+ if (unlikely (needdata == NULL))
+ return;
+
+ needshdr = gelf_getshdr (needscn, &needshdrmem);
+ if (unlikely (needshdr == NULL))
+ return;
+
+ for (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt)
+ {
+ GElf_Verneed needmem;
+ GElf_Verneed *need;
+ unsigned int auxoffset;
+ int cnt2;
+
+ /* Get the data at the next offset. */
+ need = gelf_getverneed (needdata, offset, &needmem);
+ if (unlikely (need == NULL))
+ break;
+
+ /* Run through the auxiliary entries. */
+ auxoffset = offset + need->vn_aux;
+ for (cnt2 = need->vn_cnt; --cnt2 >= 0; )
+ {
+ GElf_Vernaux auxmem;
+ GElf_Vernaux *aux;
+
+ aux = gelf_getvernaux (needdata, auxoffset, &auxmem);
+ if (unlikely (aux == NULL))
+ break;
+
+ nvername = MAX (nvername,
+ (size_t) (aux->vna_other & 0x7fff));
+
+ if (aux->vna_next == 0)
+ break;
+ auxoffset += aux->vna_next;
+ }
+
+ if (need->vn_next == 0)
+ break;
+ offset += need->vn_next;
+ }
+ }
+
+ /* This is the number of versions we know about. */
+ ++nvername;
+
+ /* Allocate the array. */
+ vername = (const char **) alloca (nvername * sizeof (const char *));
+ memset(vername, 0, nvername * sizeof (const char *));
+ filename = (const char **) alloca (nvername * sizeof (const char *));
+ memset(filename, 0, nvername * sizeof (const char *));
+
+ /* Run through the data structures again and collect the strings. */
+ if (defscn != NULL)
+ {
+ /* Run through the version definitions and find the highest
+ index. */
+ unsigned int offset = 0;
+ Elf_Data *defdata;
+ GElf_Shdr defshdrmem;
+ GElf_Shdr *defshdr;
+
+ defdata = elf_getdata (defscn, NULL);
+ if (unlikely (defdata == NULL))
+ return;
+
+ defshdr = gelf_getshdr (defscn, &defshdrmem);
+ if (unlikely (defshdr == NULL))
+ return;
+
+ for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt)
+ {
+
+ /* Get the data at the next offset. */
+ GElf_Verdef defmem;
+ GElf_Verdef *def = gelf_getverdef (defdata, offset, &defmem);
+ if (unlikely (def == NULL))
+ break;
+
+ GElf_Verdaux auxmem;
+ GElf_Verdaux *aux = gelf_getverdaux (defdata,
+ offset + def->vd_aux,
+ &auxmem);
+ if (unlikely (aux == NULL))
+ break;
+
+ vername[def->vd_ndx & 0x7fff]
+ = elf_strptr (ebl->elf, defshdr->sh_link, aux->vda_name);
+ filename[def->vd_ndx & 0x7fff] = NULL;
+
+ if (def->vd_next == 0)
+ break;
+ offset += def->vd_next;
+ }
+ }
+ if (needscn != NULL)
+ {
+ unsigned int offset = 0;
+
+ Elf_Data *needdata = elf_getdata (needscn, NULL);
+ GElf_Shdr needshdrmem;
+ GElf_Shdr *needshdr = gelf_getshdr (needscn, &needshdrmem);
+ if (unlikely (needdata == NULL || needshdr == NULL))
+ return;
+
+ for (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt)
+ {
+ /* Get the data at the next offset. */
+ GElf_Verneed needmem;
+ GElf_Verneed *need = gelf_getverneed (needdata, offset,
+ &needmem);
+ if (unlikely (need == NULL))
+ break;
+
+ /* Run through the auxiliary entries. */
+ unsigned int auxoffset = offset + need->vn_aux;
+ for (int cnt2 = need->vn_cnt; --cnt2 >= 0; )
+ {
+ GElf_Vernaux auxmem;
+ GElf_Vernaux *aux = gelf_getvernaux (needdata, auxoffset,
+ &auxmem);
+ if (unlikely (aux == NULL))
+ break;
+
+ vername[aux->vna_other & 0x7fff]
+ = elf_strptr (ebl->elf, needshdr->sh_link, aux->vna_name);
+ filename[aux->vna_other & 0x7fff]
+ = elf_strptr (ebl->elf, needshdr->sh_link, need->vn_file);
+
+ if (aux->vna_next == 0)
+ break;
+ auxoffset += aux->vna_next;
+ }
+
+ if (need->vn_next == 0)
+ break;
+ offset += need->vn_next;
+ }
+ }
+ }
+ else
+ {
+ vername = NULL;
+ nvername = 1;
+ filename = NULL;
+ }
+
+ GElf_Shdr glink_mem;
+ GElf_Shdr *glink = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
+ &glink_mem);
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_HALF, 1, EV_CURRENT);
+ if (glink == NULL)
+ error (EXIT_FAILURE, 0, gettext ("invalid sh_link value in section %zu"),
+ elf_ndxscn (scn));
+
+ /* Print the header. */
+ printf (ngettext ("\
+\nVersion symbols section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'",
+ "\
+\nVersion symbols section [%2u] '%s' contains %d entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'",
+ shdr->sh_size / sh_entsize),
+ (unsigned int) elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ (int) (shdr->sh_size / sh_entsize),
+ class == ELFCLASS32 ? 10 : 18, shdr->sh_addr,
+ shdr->sh_offset,
+ (unsigned int) shdr->sh_link,
+ elf_strptr (ebl->elf, shstrndx, glink->sh_name));
+
+ /* Now we can finally look at the actual contents of this section. */
+ for (unsigned int cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt)
+ {
+ if (cnt % 2 == 0)
+ printf ("\n %4d:", cnt);
+
+ GElf_Versym symmem;
+ GElf_Versym *sym = gelf_getversym (data, cnt, &symmem);
+ if (sym == NULL)
+ break;
+
+ switch (*sym)
+ {
+ ssize_t n;
+ case 0:
+ fputs_unlocked (gettext (" 0 *local* "),
+ stdout);
+ break;
+
+ case 1:
+ fputs_unlocked (gettext (" 1 *global* "),
+ stdout);
+ break;
+
+ default:
+ n = printf ("%4d%c%s",
+ *sym & 0x7fff, *sym & 0x8000 ? 'h' : ' ',
+ (vername != NULL
+ && (unsigned int) (*sym & 0x7fff) < nvername)
+ ? vername[*sym & 0x7fff] : "???");
+ if ((unsigned int) (*sym & 0x7fff) < nvername
+ && filename != NULL && filename[*sym & 0x7fff] != NULL)
+ n += printf ("(%s)", filename[*sym & 0x7fff]);
+ printf ("%*s", MAX (0, 33 - (int) n), " ");
+ break;
+ }
+ }
+ putchar_unlocked ('\n');
+}
+
+
+static void
+print_hash_info (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx,
+ uint_fast32_t maxlength, Elf32_Word nbucket,
+ uint_fast32_t nsyms, uint32_t *lengths, const char *extrastr)
+{
+ uint32_t *counts = (uint32_t *) xcalloc (maxlength + 1, sizeof (uint32_t));
+
+ for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
+ ++counts[lengths[cnt]];
+
+ GElf_Shdr glink_mem;
+ GElf_Shdr *glink = gelf_getshdr (elf_getscn (ebl->elf,
+ shdr->sh_link),
+ &glink_mem);
+ if (glink == NULL)
+ {
+ error (0, 0, gettext ("invalid sh_link value in section %zu"),
+ elf_ndxscn (scn));
+ return;
+ }
+
+ printf (ngettext ("\
+\nHistogram for bucket list length in section [%2u] '%s' (total of %d bucket):\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n",
+ "\
+\nHistogram for bucket list length in section [%2u] '%s' (total of %d buckets):\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n",
+ nbucket),
+ (unsigned int) elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ (int) nbucket,
+ gelf_getclass (ebl->elf) == ELFCLASS32 ? 10 : 18,
+ shdr->sh_addr,
+ shdr->sh_offset,
+ (unsigned int) shdr->sh_link,
+ elf_strptr (ebl->elf, shstrndx, glink->sh_name));
+
+ if (extrastr != NULL)
+ fputs (extrastr, stdout);
+
+ if (likely (nbucket > 0))
+ {
+ uint64_t success = 0;
+
+ /* xgettext:no-c-format */
+ fputs_unlocked (gettext ("\
+ Length Number % of total Coverage\n"), stdout);
+ printf (gettext (" 0 %6" PRIu32 " %5.1f%%\n"),
+ counts[0], (counts[0] * 100.0) / nbucket);
+
+ uint64_t nzero_counts = 0;
+ for (Elf32_Word cnt = 1; cnt <= maxlength; ++cnt)
+ {
+ nzero_counts += counts[cnt] * cnt;
+ printf (gettext ("\
+%7d %6" PRIu32 " %5.1f%% %5.1f%%\n"),
+ (int) cnt, counts[cnt], (counts[cnt] * 100.0) / nbucket,
+ (nzero_counts * 100.0) / nsyms);
+ }
+
+ Elf32_Word acc = 0;
+ for (Elf32_Word cnt = 1; cnt <= maxlength; ++cnt)
+ {
+ acc += cnt;
+ success += counts[cnt] * acc;
+ }
+
+ printf (gettext ("\
+ Average number of tests: successful lookup: %f\n\
+ unsuccessful lookup: %f\n"),
+ (double) success / (double) nzero_counts,
+ (double) nzero_counts / (double) nbucket);
+ }
+
+ free (counts);
+}
+
+
+/* This function handles the traditional System V-style hash table format. */
+static void
+handle_sysv_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
+{
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (unlikely (data == NULL))
+ {
+ error (0, 0, gettext ("cannot get data for section %d: %s"),
+ (int) elf_ndxscn (scn), elf_errmsg (-1));
+ return;
+ }
+
+ if (unlikely (data->d_size < 2 * sizeof (Elf32_Word)))
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data in sysv.hash section %d"),
+ (int) elf_ndxscn (scn));
+ return;
+ }
+
+ Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
+ Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1];
+
+ uint64_t used_buf = (2ULL + nchain + nbucket) * sizeof (Elf32_Word);
+ if (used_buf > data->d_size)
+ goto invalid_data;
+
+ Elf32_Word *bucket = &((Elf32_Word *) data->d_buf)[2];
+ Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[2 + nbucket];
+
+ uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t));
+
+ uint_fast32_t maxlength = 0;
+ uint_fast32_t nsyms = 0;
+ for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
+ {
+ Elf32_Word inner = bucket[cnt];
+ while (inner > 0 && inner < nchain)
+ {
+ ++nsyms;
+ if (maxlength < ++lengths[cnt])
+ ++maxlength;
+
+ inner = chain[inner];
+ }
+ }
+
+ print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms,
+ lengths, NULL);
+
+ free (lengths);
+}
+
+
+/* This function handles the incorrect, System V-style hash table
+ format some 64-bit architectures use. */
+static void
+handle_sysv_hash64 (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
+{
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (unlikely (data == NULL))
+ {
+ error (0, 0, gettext ("cannot get data for section %d: %s"),
+ (int) elf_ndxscn (scn), elf_errmsg (-1));
+ return;
+ }
+
+ if (unlikely (data->d_size < 2 * sizeof (Elf64_Xword)))
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data in sysv.hash64 section %d"),
+ (int) elf_ndxscn (scn));
+ return;
+ }
+
+ Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0];
+ Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1];
+
+ uint64_t maxwords = data->d_size / sizeof (Elf64_Xword);
+ if (maxwords < 2
+ || maxwords - 2 < nbucket
+ || maxwords - 2 - nbucket < nchain)
+ goto invalid_data;
+
+ Elf64_Xword *bucket = &((Elf64_Xword *) data->d_buf)[2];
+ Elf64_Xword *chain = &((Elf64_Xword *) data->d_buf)[2 + nbucket];
+
+ uint32_t *lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t));
+
+ uint_fast32_t maxlength = 0;
+ uint_fast32_t nsyms = 0;
+ for (Elf64_Xword cnt = 0; cnt < nbucket; ++cnt)
+ {
+ Elf64_Xword inner = bucket[cnt];
+ while (inner > 0 && inner < nchain)
+ {
+ ++nsyms;
+ if (maxlength < ++lengths[cnt])
+ ++maxlength;
+
+ inner = chain[inner];
+ }
+ }
+
+ print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms,
+ lengths, NULL);
+
+ free (lengths);
+}
+
+
+/* This function handles the GNU-style hash table format. */
+static void
+handle_gnu_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx)
+{
+ uint32_t *lengths = NULL;
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (unlikely (data == NULL))
+ {
+ error (0, 0, gettext ("cannot get data for section %d: %s"),
+ (int) elf_ndxscn (scn), elf_errmsg (-1));
+ return;
+ }
+
+ if (unlikely (data->d_size < 4 * sizeof (Elf32_Word)))
+ {
+ invalid_data:
+ free (lengths);
+ error (0, 0, gettext ("invalid data in gnu.hash section %d"),
+ (int) elf_ndxscn (scn));
+ return;
+ }
+
+ Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0];
+ Elf32_Word symbias = ((Elf32_Word *) data->d_buf)[1];
+
+ /* Next comes the size of the bitmap. It's measured in words for
+ the architecture. It's 32 bits for 32 bit archs, and 64 bits for
+ 64 bit archs. There is always a bloom filter present, so zero is
+ an invalid value. */
+ Elf32_Word bitmask_words = ((Elf32_Word *) data->d_buf)[2];
+ if (gelf_getclass (ebl->elf) == ELFCLASS64)
+ bitmask_words *= 2;
+
+ if (bitmask_words == 0)
+ goto invalid_data;
+
+ Elf32_Word shift = ((Elf32_Word *) data->d_buf)[3];
+
+ /* Is there still room for the sym chain?
+ Use uint64_t calculation to prevent 32bit overlow. */
+ uint64_t used_buf = (4ULL + bitmask_words + nbucket) * sizeof (Elf32_Word);
+ uint32_t max_nsyms = (data->d_size - used_buf) / sizeof (Elf32_Word);
+ if (used_buf > data->d_size)
+ goto invalid_data;
+
+ lengths = (uint32_t *) xcalloc (nbucket, sizeof (uint32_t));
+
+ Elf32_Word *bitmask = &((Elf32_Word *) data->d_buf)[4];
+ Elf32_Word *bucket = &((Elf32_Word *) data->d_buf)[4 + bitmask_words];
+ Elf32_Word *chain = &((Elf32_Word *) data->d_buf)[4 + bitmask_words
+ + nbucket];
+
+ /* Compute distribution of chain lengths. */
+ uint_fast32_t maxlength = 0;
+ uint_fast32_t nsyms = 0;
+ for (Elf32_Word cnt = 0; cnt < nbucket; ++cnt)
+ if (bucket[cnt] != 0)
+ {
+ Elf32_Word inner = bucket[cnt] - symbias;
+ do
+ {
+ ++nsyms;
+ if (maxlength < ++lengths[cnt])
+ ++maxlength;
+ if (inner >= max_nsyms)
+ goto invalid_data;
+ }
+ while ((chain[inner++] & 1) == 0);
+ }
+
+ /* Count bits in bitmask. */
+ uint_fast32_t nbits = 0;
+ for (Elf32_Word cnt = 0; cnt < bitmask_words; ++cnt)
+ {
+ uint_fast32_t word = bitmask[cnt];
+
+ word = (word & 0x55555555) + ((word >> 1) & 0x55555555);
+ word = (word & 0x33333333) + ((word >> 2) & 0x33333333);
+ word = (word & 0x0f0f0f0f) + ((word >> 4) & 0x0f0f0f0f);
+ word = (word & 0x00ff00ff) + ((word >> 8) & 0x00ff00ff);
+ nbits += (word & 0x0000ffff) + ((word >> 16) & 0x0000ffff);
+ }
+
+ char *str;
+ if (unlikely (asprintf (&str, gettext ("\
+ Symbol Bias: %u\n\
+ Bitmask Size: %zu bytes %" PRIuFAST32 "%% bits set 2nd hash shift: %u\n"),
+ (unsigned int) symbias,
+ bitmask_words * sizeof (Elf32_Word),
+ ((nbits * 100 + 50)
+ / (uint_fast32_t) (bitmask_words
+ * sizeof (Elf32_Word) * 8)),
+ (unsigned int) shift) == -1))
+ error (EXIT_FAILURE, 0, gettext ("memory exhausted"));
+
+ print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms,
+ lengths, str);
+
+ free (str);
+ free (lengths);
+}
+
+
+/* Find the symbol table(s). For this we have to search through the
+ section table. */
+static void
+handle_hash (Ebl *ebl)
+{
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ /* Handle the section if it is a symbol table. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (likely (shdr != NULL))
+ {
+ if ((shdr->sh_type == SHT_HASH || shdr->sh_type == SHT_GNU_HASH)
+ && (shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ if (elf_compress (scn, 0, 0) < 0)
+ printf ("WARNING: %s [%zd]\n",
+ gettext ("Couldn't uncompress section"),
+ elf_ndxscn (scn));
+ shdr = gelf_getshdr (scn, &shdr_mem);
+ if (unlikely (shdr == NULL))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section [%zd] header: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+ }
+
+ if (shdr->sh_type == SHT_HASH)
+ {
+ if (ebl_sysvhash_entrysize (ebl) == sizeof (Elf64_Xword))
+ handle_sysv_hash64 (ebl, scn, shdr, shstrndx);
+ else
+ handle_sysv_hash (ebl, scn, shdr, shstrndx);
+ }
+ else if (shdr->sh_type == SHT_GNU_HASH)
+ handle_gnu_hash (ebl, scn, shdr, shstrndx);
+ }
+ }
+}
+
+
+static void
+print_liblist (Ebl *ebl)
+{
+ /* Find the library list sections. For this we have to search
+ through the section table. */
+ Elf_Scn *scn = NULL;
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr != NULL && shdr->sh_type == SHT_GNU_LIBLIST)
+ {
+ size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_LIB, 1, EV_CURRENT);
+ int nentries = shdr->sh_size / sh_entsize;
+ printf (ngettext ("\
+\nLibrary list section [%2zu] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
+ "\
+\nLibrary list section [%2zu] '%s' at offset %#0" PRIx64 " contains %d entries:\n",
+ nentries),
+ elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ shdr->sh_offset,
+ nentries);
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ if (data == NULL)
+ return;
+
+ puts (gettext ("\
+ Library Time Stamp Checksum Version Flags"));
+
+ for (int cnt = 0; cnt < nentries; ++cnt)
+ {
+ GElf_Lib lib_mem;
+ GElf_Lib *lib = gelf_getlib (data, cnt, &lib_mem);
+ if (unlikely (lib == NULL))
+ continue;
+
+ time_t t = (time_t) lib->l_time_stamp;
+ struct tm *tm = gmtime (&t);
+ if (unlikely (tm == NULL))
+ continue;
+
+ printf (" [%2d] %-29s %04u-%02u-%02uT%02u:%02u:%02u %08x %-7u %u\n",
+ cnt, elf_strptr (ebl->elf, shdr->sh_link, lib->l_name),
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec,
+ (unsigned int) lib->l_checksum,
+ (unsigned int) lib->l_version,
+ (unsigned int) lib->l_flags);
+ }
+ }
+ }
+}
+
+static void
+print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr)
+{
+ /* Find the object attributes sections. For this we have to search
+ through the section table. */
+ Elf_Scn *scn = NULL;
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr == NULL || (shdr->sh_type != SHT_GNU_ATTRIBUTES
+ && (shdr->sh_type != SHT_ARM_ATTRIBUTES
+ || ehdr->e_machine != EM_ARM)))
+ continue;
+
+ printf (gettext ("\
+\nObject attributes section [%2zu] '%s' of %" PRIu64
+ " bytes at offset %#0" PRIx64 ":\n"),
+ elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ shdr->sh_size, shdr->sh_offset);
+
+ Elf_Data *data = elf_rawdata (scn, NULL);
+ if (unlikely (data == NULL || data->d_size == 0))
+ return;
+
+ const unsigned char *p = data->d_buf;
+
+ /* There is only one 'version', A. */
+ if (unlikely (*p++ != 'A'))
+ return;
+
+ fputs_unlocked (gettext (" Owner Size\n"), stdout);
+
+ inline size_t left (void)
+ {
+ return (const unsigned char *) data->d_buf + data->d_size - p;
+ }
+
+ /* Loop over the sections. */
+ while (left () >= 4)
+ {
+ /* Section length. */
+ uint32_t len;
+ memcpy (&len, p, sizeof len);
+
+ if (MY_ELFDATA != ehdr->e_ident[EI_DATA])
+ CONVERT (len);
+
+ if (unlikely (len > left ()))
+ break;
+
+ /* Section vendor name. */
+ const unsigned char *name = p + sizeof len;
+ p += len;
+
+ unsigned const char *q = memchr (name, '\0', len);
+ if (unlikely (q == NULL))
+ break;
+ ++q;
+
+ printf (gettext (" %-13s %4" PRIu32 "\n"), name, len);
+
+ bool gnu_vendor = (q - name == sizeof "gnu"
+ && !memcmp (name, "gnu", sizeof "gnu"));
+
+ /* Loop over subsections. */
+ if (shdr->sh_type != SHT_GNU_ATTRIBUTES
+ || gnu_vendor)
+ while (q < p)
+ {
+ const unsigned char *const sub = q;
+
+ unsigned int subsection_tag;
+ get_uleb128 (subsection_tag, q, p);
+ if (unlikely (q >= p))
+ break;
+
+ uint32_t subsection_len;
+ if (unlikely (p - sub < (ptrdiff_t) sizeof subsection_len))
+ break;
+
+ memcpy (&subsection_len, q, sizeof subsection_len);
+
+ if (MY_ELFDATA != ehdr->e_ident[EI_DATA])
+ CONVERT (subsection_len);
+
+ /* Don't overflow, ptrdiff_t might be 32bits, but signed. */
+ if (unlikely (subsection_len == 0
+ || subsection_len >= (uint32_t) PTRDIFF_MAX
+ || p - sub < (ptrdiff_t) subsection_len))
+ break;
+
+ const unsigned char *r = q + sizeof subsection_len;
+ q = sub + subsection_len;
+
+ switch (subsection_tag)
+ {
+ default:
+ /* Unknown subsection, print and skip. */
+ printf (gettext (" %-4u %12" PRIu32 "\n"),
+ subsection_tag, subsection_len);
+ break;
+
+ case 1: /* Tag_File */
+ printf (gettext (" File: %11" PRIu32 "\n"),
+ subsection_len);
+
+ while (r < q)
+ {
+ unsigned int tag;
+ get_uleb128 (tag, r, q);
+ if (unlikely (r >= q))
+ break;
+
+ /* GNU style tags have either a uleb128 value,
+ when lowest bit is not set, or a string
+ when the lowest bit is set.
+ "compatibility" (32) is special. It has
+ both a string and a uleb128 value. For
+ non-gnu we assume 6 till 31 only take ints.
+ XXX see arm backend, do we need a separate
+ hook? */
+ uint64_t value = 0;
+ const char *string = NULL;
+ if (tag == 32 || (tag & 1) == 0
+ || (! gnu_vendor && (tag > 5 && tag < 32)))
+ {
+ get_uleb128 (value, r, q);
+ if (r > q)
+ break;
+ }
+ if (tag == 32
+ || ((tag & 1) != 0
+ && (gnu_vendor
+ || (! gnu_vendor && tag > 32)))
+ || (! gnu_vendor && tag > 3 && tag < 6))
+ {
+ string = (const char *) r;
+ r = memchr (r, '\0', q - r);
+ if (r == NULL)
+ break;
+ ++r;
+ }
+
+ const char *tag_name = NULL;
+ const char *value_name = NULL;
+ ebl_check_object_attribute (ebl, (const char *) name,
+ tag, value,
+ &tag_name, &value_name);
+
+ if (tag_name != NULL)
+ {
+ if (tag == 32)
+ printf (gettext (" %s: %" PRId64 ", %s\n"),
+ tag_name, value, string);
+ else if (string == NULL && value_name == NULL)
+ printf (gettext (" %s: %" PRId64 "\n"),
+ tag_name, value);
+ else
+ printf (gettext (" %s: %s\n"),
+ tag_name, string ?: value_name);
+ }
+ else
+ {
+ /* For "gnu" vendor 32 "compatibility" has
+ already been handled above. */
+ assert (tag != 32
+ || strcmp ((const char *) name, "gnu"));
+ if (string == NULL)
+ printf (gettext (" %u: %" PRId64 "\n"),
+ tag, value);
+ else
+ printf (gettext (" %u: %s\n"),
+ tag, string);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static char *
+format_dwarf_addr (Dwfl_Module *dwflmod,
+ int address_size, Dwarf_Addr address, Dwarf_Addr raw)
+{
+ /* See if there is a name we can give for this address. */
+ GElf_Sym sym;
+ GElf_Off off = 0;
+ const char *name = (print_address_names && ! print_unresolved_addresses)
+ ? dwfl_module_addrinfo (dwflmod, address, &off, &sym, NULL, NULL, NULL)
+ : NULL;
+
+ const char *scn;
+ if (print_unresolved_addresses)
+ {
+ address = raw;
+ scn = NULL;
+ }
+ else
+ {
+ /* Relativize the address. */
+ int n = dwfl_module_relocations (dwflmod);
+ int i = n < 1 ? -1 : dwfl_module_relocate_address (dwflmod, &address);
+
+ /* In an ET_REL file there is a section name to refer to. */
+ scn = (i < 0 ? NULL
+ : dwfl_module_relocation_info (dwflmod, i, NULL));
+ }
+
+ char *result;
+ if ((name != NULL
+ ? (off != 0
+ ? (scn != NULL
+ ? (address_size == 0
+ ? asprintf (&result,
+ gettext ("%s+%#" PRIx64 " <%s+%#" PRIx64 ">"),
+ scn, address, name, off)
+ : asprintf (&result,
+ gettext ("%s+%#0*" PRIx64 " <%s+%#" PRIx64 ">"),
+ scn, 2 + address_size * 2, address,
+ name, off))
+ : (address_size == 0
+ ? asprintf (&result,
+ gettext ("%#" PRIx64 " <%s+%#" PRIx64 ">"),
+ address, name, off)
+ : asprintf (&result,
+ gettext ("%#0*" PRIx64 " <%s+%#" PRIx64 ">"),
+ 2 + address_size * 2, address,
+ name, off)))
+ : (scn != NULL
+ ? (address_size == 0
+ ? asprintf (&result,
+ gettext ("%s+%#" PRIx64 " <%s>"),
+ scn, address, name)
+ : asprintf (&result,
+ gettext ("%s+%#0*" PRIx64 " <%s>"),
+ scn, 2 + address_size * 2, address, name))
+ : (address_size == 0
+ ? asprintf (&result,
+ gettext ("%#" PRIx64 " <%s>"),
+ address, name)
+ : asprintf (&result,
+ gettext ("%#0*" PRIx64 " <%s>"),
+ 2 + address_size * 2, address, name))))
+ : (scn != NULL
+ ? (address_size == 0
+ ? asprintf (&result,
+ gettext ("%s+%#" PRIx64),
+ scn, address)
+ : asprintf (&result,
+ gettext ("%s+%#0*" PRIx64),
+ scn, 2 + address_size * 2, address))
+ : (address_size == 0
+ ? asprintf (&result,
+ "%#" PRIx64,
+ address)
+ : asprintf (&result,
+ "%#0*" PRIx64,
+ 2 + address_size * 2, address)))) < 0)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+
+ return result;
+}
+
+static const char *
+dwarf_tag_string (unsigned int tag)
+{
+ switch (tag)
+ {
+#define DWARF_ONE_KNOWN_DW_TAG(NAME, CODE) case CODE: return #NAME;
+ DWARF_ALL_KNOWN_DW_TAG
+#undef DWARF_ONE_KNOWN_DW_TAG
+ default:
+ return NULL;
+ }
+}
+
+
+static const char *
+dwarf_attr_string (unsigned int attrnum)
+{
+ switch (attrnum)
+ {
+#define DWARF_ONE_KNOWN_DW_AT(NAME, CODE) case CODE: return #NAME;
+ DWARF_ALL_KNOWN_DW_AT
+#undef DWARF_ONE_KNOWN_DW_AT
+ default:
+ return NULL;
+ }
+}
+
+
+static const char *
+dwarf_form_string (unsigned int form)
+{
+ switch (form)
+ {
+#define DWARF_ONE_KNOWN_DW_FORM(NAME, CODE) case CODE: return #NAME;
+ DWARF_ALL_KNOWN_DW_FORM
+#undef DWARF_ONE_KNOWN_DW_FORM
+ default:
+ return NULL;
+ }
+}
+
+
+static const char *
+dwarf_lang_string (unsigned int lang)
+{
+ switch (lang)
+ {
+#define DWARF_ONE_KNOWN_DW_LANG(NAME, CODE) case CODE: return #NAME;
+ DWARF_ALL_KNOWN_DW_LANG
+#undef DWARF_ONE_KNOWN_DW_LANG
+ default:
+ return NULL;
+ }
+}
+
+
+static const char *
+dwarf_inline_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+#define DWARF_ONE_KNOWN_DW_INL(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_INL
+#undef DWARF_ONE_KNOWN_DW_INL
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+static const char *
+dwarf_encoding_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+#define DWARF_ONE_KNOWN_DW_ATE(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_ATE
+#undef DWARF_ONE_KNOWN_DW_ATE
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+static const char *
+dwarf_access_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+#define DWARF_ONE_KNOWN_DW_ACCESS(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_ACCESS
+#undef DWARF_ONE_KNOWN_DW_ACCESS
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+static const char *
+dwarf_defaulted_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+#define DWARF_ONE_KNOWN_DW_DEFAULTED(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_DEFAULTED
+#undef DWARF_ONE_KNOWN_DW_DEFAULTED
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+static const char *
+dwarf_visibility_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+#define DWARF_ONE_KNOWN_DW_VIS(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_VIS
+#undef DWARF_ONE_KNOWN_DW_VIS
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+static const char *
+dwarf_virtuality_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+#define DWARF_ONE_KNOWN_DW_VIRTUALITY(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_VIRTUALITY
+#undef DWARF_ONE_KNOWN_DW_VIRTUALITY
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+static const char *
+dwarf_identifier_case_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+#define DWARF_ONE_KNOWN_DW_ID(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_ID
+#undef DWARF_ONE_KNOWN_DW_ID
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+static const char *
+dwarf_calling_convention_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+#define DWARF_ONE_KNOWN_DW_CC(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_CC
+#undef DWARF_ONE_KNOWN_DW_CC
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+static const char *
+dwarf_ordering_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+#define DWARF_ONE_KNOWN_DW_ORD(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_ORD
+#undef DWARF_ONE_KNOWN_DW_ORD
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+static const char *
+dwarf_discr_list_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+#define DWARF_ONE_KNOWN_DW_DSC(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_DSC
+#undef DWARF_ONE_KNOWN_DW_DSC
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+static const char *
+dwarf_locexpr_opcode_string (unsigned int code)
+{
+ static const char *const known[] =
+ {
+ /* Normally we can't affort building huge table of 64K entries,
+ most of them zero, just because there are a couple defined
+ values at the far end. In case of opcodes, it's OK. */
+#define DWARF_ONE_KNOWN_DW_OP(NAME, CODE) [CODE] = #NAME,
+ DWARF_ALL_KNOWN_DW_OP
+#undef DWARF_ONE_KNOWN_DW_OP
+ };
+
+ if (likely (code < sizeof (known) / sizeof (known[0])))
+ return known[code];
+
+ return NULL;
+}
+
+
+/* Used by all dwarf_foo_name functions. */
+static const char *
+string_or_unknown (const char *known, unsigned int code,
+ unsigned int lo_user, unsigned int hi_user,
+ bool print_unknown_num)
+{
+ static char unknown_buf[20];
+
+ if (likely (known != NULL))
+ return known;
+
+ if (lo_user != 0 && code >= lo_user && code <= hi_user)
+ {
+ snprintf (unknown_buf, sizeof unknown_buf, "lo_user+%#x",
+ code - lo_user);
+ return unknown_buf;
+ }
+
+ if (print_unknown_num)
+ {
+ snprintf (unknown_buf, sizeof unknown_buf, "??? (%#x)", code);
+ return unknown_buf;
+ }
+
+ return "???";
+}
+
+
+static const char *
+dwarf_tag_name (unsigned int tag)
+{
+ const char *ret = dwarf_tag_string (tag);
+ return string_or_unknown (ret, tag, DW_TAG_lo_user, DW_TAG_hi_user, true);
+}
+
+static const char *
+dwarf_attr_name (unsigned int attr)
+{
+ const char *ret = dwarf_attr_string (attr);
+ return string_or_unknown (ret, attr, DW_AT_lo_user, DW_AT_hi_user, true);
+}
+
+
+static const char *
+dwarf_form_name (unsigned int form)
+{
+ const char *ret = dwarf_form_string (form);
+ return string_or_unknown (ret, form, 0, 0, true);
+}
+
+
+static const char *
+dwarf_lang_name (unsigned int lang)
+{
+ const char *ret = dwarf_lang_string (lang);
+ return string_or_unknown (ret, lang, DW_LANG_lo_user, DW_LANG_hi_user, false);
+}
+
+
+static const char *
+dwarf_inline_name (unsigned int code)
+{
+ const char *ret = dwarf_inline_string (code);
+ return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_encoding_name (unsigned int code)
+{
+ const char *ret = dwarf_encoding_string (code);
+ return string_or_unknown (ret, code, DW_ATE_lo_user, DW_ATE_hi_user, false);
+}
+
+
+static const char *
+dwarf_access_name (unsigned int code)
+{
+ const char *ret = dwarf_access_string (code);
+ return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_defaulted_name (unsigned int code)
+{
+ const char *ret = dwarf_defaulted_string (code);
+ return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_visibility_name (unsigned int code)
+{
+ const char *ret = dwarf_visibility_string (code);
+ return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_virtuality_name (unsigned int code)
+{
+ const char *ret = dwarf_virtuality_string (code);
+ return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_identifier_case_name (unsigned int code)
+{
+ const char *ret = dwarf_identifier_case_string (code);
+ return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_calling_convention_name (unsigned int code)
+{
+ const char *ret = dwarf_calling_convention_string (code);
+ return string_or_unknown (ret, code, DW_CC_lo_user, DW_CC_hi_user, false);
+}
+
+
+static const char *
+dwarf_ordering_name (unsigned int code)
+{
+ const char *ret = dwarf_ordering_string (code);
+ return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static const char *
+dwarf_discr_list_name (unsigned int code)
+{
+ const char *ret = dwarf_discr_list_string (code);
+ return string_or_unknown (ret, code, 0, 0, false);
+}
+
+
+static void
+print_block (size_t n, const void *block)
+{
+ if (n == 0)
+ puts (_("empty block"));
+ else
+ {
+ printf (_("%zu byte block:"), n);
+ const unsigned char *data = block;
+ do
+ printf (" %02x", *data++);
+ while (--n > 0);
+ putchar ('\n');
+ }
+}
+
+static void
+print_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
+ unsigned int vers, unsigned int addrsize, unsigned int offset_size,
+ struct Dwarf_CU *cu, Dwarf_Word len, const unsigned char *data)
+{
+ const unsigned int ref_size = vers < 3 ? addrsize : offset_size;
+
+ if (len == 0)
+ {
+ printf ("%*s(empty)\n", indent, "");
+ return;
+ }
+
+#define NEED(n) if (len < (Dwarf_Word) (n)) goto invalid
+#define CONSUME(n) NEED (n); else len -= (n)
+
+ Dwarf_Word offset = 0;
+ while (len-- > 0)
+ {
+ uint_fast8_t op = *data++;
+
+ const char *op_name = dwarf_locexpr_opcode_string (op);
+ if (unlikely (op_name == NULL))
+ {
+ static char buf[20];
+ if (op >= DW_OP_lo_user)
+ snprintf (buf, sizeof buf, "lo_user+%#x", op - DW_OP_lo_user);
+ else
+ snprintf (buf, sizeof buf, "??? (%#x)", op);
+ op_name = buf;
+ }
+
+ switch (op)
+ {
+ case DW_OP_addr:;
+ /* Address operand. */
+ Dwarf_Word addr;
+ NEED (addrsize);
+ if (addrsize == 4)
+ addr = read_4ubyte_unaligned (dbg, data);
+ else if (addrsize == 8)
+ addr = read_8ubyte_unaligned (dbg, data);
+ else
+ goto invalid;
+ data += addrsize;
+ CONSUME (addrsize);
+
+ char *a = format_dwarf_addr (dwflmod, 0, addr, addr);
+ printf ("%*s[%2" PRIuMAX "] %s %s\n",
+ indent, "", (uintmax_t) offset, op_name, a);
+ free (a);
+
+ offset += 1 + addrsize;
+ break;
+
+ case DW_OP_call_ref:
+ case DW_OP_GNU_variable_value:
+ /* Offset operand. */
+ if (ref_size != 4 && ref_size != 8)
+ goto invalid; /* Cannot be used in CFA. */
+ NEED (ref_size);
+ if (ref_size == 4)
+ addr = read_4ubyte_unaligned (dbg, data);
+ else
+ addr = read_8ubyte_unaligned (dbg, data);
+ data += ref_size;
+ CONSUME (ref_size);
+ /* addr is a DIE offset, so format it as one. */
+ printf ("%*s[%2" PRIuMAX "] %s [%6" PRIxMAX "]\n",
+ indent, "", (uintmax_t) offset,
+ op_name, (uintmax_t) addr);
+ offset += 1 + ref_size;
+ break;
+
+ case DW_OP_deref_size:
+ case DW_OP_xderef_size:
+ case DW_OP_pick:
+ case DW_OP_const1u:
+ // XXX value might be modified by relocation
+ NEED (1);
+ printf ("%*s[%2" PRIuMAX "] %s %" PRIu8 "\n",
+ indent, "", (uintmax_t) offset,
+ op_name, *((uint8_t *) data));
+ ++data;
+ --len;
+ offset += 2;
+ break;
+
+ case DW_OP_const2u:
+ NEED (2);
+ // XXX value might be modified by relocation
+ printf ("%*s[%2" PRIuMAX "] %s %" PRIu16 "\n",
+ indent, "", (uintmax_t) offset,
+ op_name, read_2ubyte_unaligned (dbg, data));
+ CONSUME (2);
+ data += 2;
+ offset += 3;
+ break;
+
+ case DW_OP_const4u:
+ NEED (4);
+ // XXX value might be modified by relocation
+ printf ("%*s[%2" PRIuMAX "] %s %" PRIu32 "\n",
+ indent, "", (uintmax_t) offset,
+ op_name, read_4ubyte_unaligned (dbg, data));
+ CONSUME (4);
+ data += 4;
+ offset += 5;
+ break;
+
+ case DW_OP_const8u:
+ NEED (8);
+ // XXX value might be modified by relocation
+ printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 "\n",
+ indent, "", (uintmax_t) offset,
+ op_name, (uint64_t) read_8ubyte_unaligned (dbg, data));
+ CONSUME (8);
+ data += 8;
+ offset += 9;
+ break;
+
+ case DW_OP_const1s:
+ NEED (1);
+ // XXX value might be modified by relocation
+ printf ("%*s[%2" PRIuMAX "] %s %" PRId8 "\n",
+ indent, "", (uintmax_t) offset,
+ op_name, *((int8_t *) data));
+ ++data;
+ --len;
+ offset += 2;
+ break;
+
+ case DW_OP_const2s:
+ NEED (2);
+ // XXX value might be modified by relocation
+ printf ("%*s[%2" PRIuMAX "] %s %" PRId16 "\n",
+ indent, "", (uintmax_t) offset,
+ op_name, read_2sbyte_unaligned (dbg, data));
+ CONSUME (2);
+ data += 2;
+ offset += 3;
+ break;
+
+ case DW_OP_const4s:
+ NEED (4);
+ // XXX value might be modified by relocation
+ printf ("%*s[%2" PRIuMAX "] %s %" PRId32 "\n",
+ indent, "", (uintmax_t) offset,
+ op_name, read_4sbyte_unaligned (dbg, data));
+ CONSUME (4);
+ data += 4;
+ offset += 5;
+ break;
+
+ case DW_OP_const8s:
+ NEED (8);
+ // XXX value might be modified by relocation
+ printf ("%*s[%2" PRIuMAX "] %s %" PRId64 "\n",
+ indent, "", (uintmax_t) offset,
+ op_name, read_8sbyte_unaligned (dbg, data));
+ CONSUME (8);
+ data += 8;
+ offset += 9;
+ break;
+
+ case DW_OP_piece:
+ case DW_OP_regx:
+ case DW_OP_plus_uconst:
+ case DW_OP_constu:;
+ const unsigned char *start = data;
+ uint64_t uleb;
+ NEED (1);
+ get_uleb128 (uleb, data, data + len);
+ printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 "\n",
+ indent, "", (uintmax_t) offset, op_name, uleb);
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_bit_piece:
+ start = data;
+ uint64_t uleb2;
+ NEED (1);
+ get_uleb128 (uleb, data, data + len);
+ NEED (1);
+ get_uleb128 (uleb2, data, data + len);
+ printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 ", %" PRIu64 "\n",
+ indent, "", (uintmax_t) offset, op_name, uleb, uleb2);
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_fbreg:
+ case DW_OP_breg0 ... DW_OP_breg31:
+ case DW_OP_consts:
+ start = data;
+ int64_t sleb;
+ NEED (1);
+ get_sleb128 (sleb, data, data + len);
+ printf ("%*s[%2" PRIuMAX "] %s %" PRId64 "\n",
+ indent, "", (uintmax_t) offset, op_name, sleb);
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_bregx:
+ start = data;
+ NEED (1);
+ get_uleb128 (uleb, data, data + len);
+ NEED (1);
+ get_sleb128 (sleb, data, data + len);
+ printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 " %" PRId64 "\n",
+ indent, "", (uintmax_t) offset, op_name, uleb, sleb);
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_call2:
+ NEED (2);
+ printf ("%*s[%2" PRIuMAX "] %s [%6" PRIx16 "]\n",
+ indent, "", (uintmax_t) offset, op_name,
+ read_2ubyte_unaligned (dbg, data));
+ CONSUME (2);
+ data += 2;
+ offset += 3;
+ break;
+
+ case DW_OP_call4:
+ NEED (4);
+ printf ("%*s[%2" PRIuMAX "] %s [%6" PRIx32 "]\n",
+ indent, "", (uintmax_t) offset, op_name,
+ read_4ubyte_unaligned (dbg, data));
+ CONSUME (4);
+ data += 4;
+ offset += 5;
+ break;
+
+ case DW_OP_skip:
+ case DW_OP_bra:
+ NEED (2);
+ printf ("%*s[%2" PRIuMAX "] %s %" PRIuMAX "\n",
+ indent, "", (uintmax_t) offset, op_name,
+ (uintmax_t) (offset + read_2sbyte_unaligned (dbg, data) + 3));
+ CONSUME (2);
+ data += 2;
+ offset += 3;
+ break;
+
+ case DW_OP_implicit_value:
+ start = data;
+ NEED (1);
+ get_uleb128 (uleb, data, data + len);
+ printf ("%*s[%2" PRIuMAX "] %s: ",
+ indent, "", (uintmax_t) offset, op_name);
+ NEED (uleb);
+ print_block (uleb, data);
+ data += uleb;
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_GNU_implicit_pointer:
+ /* DIE offset operand. */
+ start = data;
+ NEED (ref_size);
+ if (ref_size != 4 && ref_size != 8)
+ goto invalid; /* Cannot be used in CFA. */
+ if (ref_size == 4)
+ addr = read_4ubyte_unaligned (dbg, data);
+ else
+ addr = read_8ubyte_unaligned (dbg, data);
+ data += ref_size;
+ /* Byte offset operand. */
+ NEED (1);
+ get_sleb128 (sleb, data, data + len);
+
+ printf ("%*s[%2" PRIuMAX "] %s [%6" PRIxMAX "] %+" PRId64 "\n",
+ indent, "", (intmax_t) offset,
+ op_name, (uintmax_t) addr, sleb);
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_GNU_entry_value:
+ /* Size plus expression block. */
+ start = data;
+ NEED (1);
+ get_uleb128 (uleb, data, data + len);
+ printf ("%*s[%2" PRIuMAX "] %s:\n",
+ indent, "", (uintmax_t) offset, op_name);
+ NEED (uleb);
+ print_ops (dwflmod, dbg, indent + 5, indent + 5, vers,
+ addrsize, offset_size, cu, uleb, data);
+ data += uleb;
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_GNU_const_type:
+ /* uleb128 CU relative DW_TAG_base_type DIE offset, 1-byte
+ unsigned size plus block. */
+ start = data;
+ NEED (1);
+ get_uleb128 (uleb, data, data + len);
+ if (! print_unresolved_addresses && cu != NULL)
+ uleb += cu->start;
+ NEED (1);
+ uint8_t usize = *(uint8_t *) data++;
+ NEED (usize);
+ printf ("%*s[%2" PRIuMAX "] %s [%6" PRIxMAX "] ",
+ indent, "", (uintmax_t) offset, op_name, uleb);
+ print_block (usize, data);
+ data += usize;
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_GNU_regval_type:
+ /* uleb128 register number, uleb128 CU relative
+ DW_TAG_base_type DIE offset. */
+ start = data;
+ NEED (1);
+ get_uleb128 (uleb, data, data + len);
+ NEED (1);
+ get_uleb128 (uleb2, data, data + len);
+ if (! print_unresolved_addresses && cu != NULL)
+ uleb2 += cu->start;
+ printf ("%*s[%2" PRIuMAX "] %s %" PRIu64 " [%6" PRIx64 "]\n",
+ indent, "", (uintmax_t) offset, op_name, uleb, uleb2);
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_GNU_deref_type:
+ /* 1-byte unsigned size of value, uleb128 CU relative
+ DW_TAG_base_type DIE offset. */
+ start = data;
+ NEED (1);
+ usize = *(uint8_t *) data++;
+ NEED (1);
+ get_uleb128 (uleb, data, data + len);
+ if (! print_unresolved_addresses && cu != NULL)
+ uleb += cu->start;
+ printf ("%*s[%2" PRIuMAX "] %s %" PRIu8 " [%6" PRIxMAX "]\n",
+ indent, "", (uintmax_t) offset,
+ op_name, usize, uleb);
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_GNU_convert:
+ case DW_OP_GNU_reinterpret:
+ /* uleb128 CU relative offset to DW_TAG_base_type, or zero
+ for conversion to untyped. */
+ start = data;
+ NEED (1);
+ get_uleb128 (uleb, data, data + len);
+ if (uleb != 0 && ! print_unresolved_addresses && cu != NULL)
+ uleb += cu->start;
+ printf ("%*s[%2" PRIuMAX "] %s [%6" PRIxMAX "]\n",
+ indent, "", (uintmax_t) offset, op_name, uleb);
+ CONSUME (data - start);
+ offset += 1 + (data - start);
+ break;
+
+ case DW_OP_GNU_parameter_ref:
+ /* 4 byte CU relative reference to the abstract optimized away
+ DW_TAG_formal_parameter. */
+ NEED (4);
+ uintmax_t param_off = (uintmax_t) read_4ubyte_unaligned (dbg, data);
+ if (! print_unresolved_addresses && cu != NULL)
+ param_off += cu->start;
+ printf ("%*s[%2" PRIuMAX "] %s [%6" PRIxMAX "]\n",
+ indent, "", (uintmax_t) offset, op_name, param_off);
+ CONSUME (4);
+ data += 4;
+ offset += 5;
+ break;
+
+ default:
+ /* No Operand. */
+ printf ("%*s[%2" PRIuMAX "] %s\n",
+ indent, "", (uintmax_t) offset, op_name);
+ ++offset;
+ break;
+ }
+
+ indent = indentrest;
+ continue;
+
+ invalid:
+ printf (gettext ("%*s[%2" PRIuMAX "] %s <TRUNCATED>\n"),
+ indent, "", (uintmax_t) offset, op_name);
+ break;
+ }
+}
+
+
+struct listptr
+{
+ Dwarf_Off offset:(64 - 3);
+ bool addr64:1;
+ bool dwarf64:1;
+ bool warned:1;
+ struct Dwarf_CU *cu;
+};
+
+#define listptr_offset_size(p) ((p)->dwarf64 ? 8 : 4)
+#define listptr_address_size(p) ((p)->addr64 ? 8 : 4)
+
+static Dwarf_Addr
+listptr_base (struct listptr *p)
+{
+ Dwarf_Addr base;
+ Dwarf_Die cu = CUDIE (p->cu);
+ /* Find the base address of the compilation unit. It will normally
+ be specified by DW_AT_low_pc. In DWARF-3 draft 4, the base
+ address could be overridden by DW_AT_entry_pc. It's been
+ removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc for
+ compilation units with discontinuous ranges. */
+ if (unlikely (dwarf_lowpc (&cu, &base) != 0))
+ {
+ Dwarf_Attribute attr_mem;
+ if (dwarf_formaddr (dwarf_attr (&cu, DW_AT_entry_pc, &attr_mem),
+ &base) != 0)
+ base = 0;
+ }
+ return base;
+}
+
+static int
+compare_listptr (const void *a, const void *b, void *arg)
+{
+ const char *name = arg;
+ struct listptr *p1 = (void *) a;
+ struct listptr *p2 = (void *) b;
+
+ if (p1->offset < p2->offset)
+ return -1;
+ if (p1->offset > p2->offset)
+ return 1;
+
+ if (!p1->warned && !p2->warned)
+ {
+ if (p1->addr64 != p2->addr64)
+ {
+ p1->warned = p2->warned = true;
+ error (0, 0,
+ gettext ("%s %#" PRIx64 " used with different address sizes"),
+ name, (uint64_t) p1->offset);
+ }
+ if (p1->dwarf64 != p2->dwarf64)
+ {
+ p1->warned = p2->warned = true;
+ error (0, 0,
+ gettext ("%s %#" PRIx64 " used with different offset sizes"),
+ name, (uint64_t) p1->offset);
+ }
+ if (listptr_base (p1) != listptr_base (p2))
+ {
+ p1->warned = p2->warned = true;
+ error (0, 0,
+ gettext ("%s %#" PRIx64 " used with different base addresses"),
+ name, (uint64_t) p1->offset);
+ }
+ }
+
+ return 0;
+}
+
+struct listptr_table
+{
+ size_t n;
+ size_t alloc;
+ struct listptr *table;
+};
+
+static struct listptr_table known_loclistptr;
+static struct listptr_table known_rangelistptr;
+
+static void
+reset_listptr (struct listptr_table *table)
+{
+ free (table->table);
+ table->table = NULL;
+ table->n = table->alloc = 0;
+}
+
+/* Returns false if offset doesn't fit. See struct listptr. */
+static bool
+notice_listptr (enum section_e section, struct listptr_table *table,
+ uint_fast8_t address_size, uint_fast8_t offset_size,
+ struct Dwarf_CU *cu, Dwarf_Off offset)
+{
+ if (print_debug_sections & section)
+ {
+ if (table->n == table->alloc)
+ {
+ if (table->alloc == 0)
+ table->alloc = 128;
+ else
+ table->alloc *= 2;
+ table->table = xrealloc (table->table,
+ table->alloc * sizeof table->table[0]);
+ }
+
+ struct listptr *p = &table->table[table->n++];
+
+ *p = (struct listptr)
+ {
+ .addr64 = address_size == 8,
+ .dwarf64 = offset_size == 8,
+ .offset = offset,
+ .cu = cu
+ };
+
+ if (p->offset != offset)
+ {
+ table->n--;
+ return false;
+ }
+ }
+ return true;
+}
+
+static void
+sort_listptr (struct listptr_table *table, const char *name)
+{
+ if (table->n > 0)
+ qsort_r (table->table, table->n, sizeof table->table[0],
+ &compare_listptr, (void *) name);
+}
+
+static bool
+skip_listptr_hole (struct listptr_table *table, size_t *idxp,
+ uint_fast8_t *address_sizep, uint_fast8_t *offset_sizep,
+ Dwarf_Addr *base, struct Dwarf_CU **cu, ptrdiff_t offset,
+ unsigned char **readp, unsigned char *endp)
+{
+ if (table->n == 0)
+ return false;
+
+ while (*idxp < table->n && table->table[*idxp].offset < (Dwarf_Off) offset)
+ ++*idxp;
+
+ struct listptr *p = &table->table[*idxp];
+
+ if (*idxp == table->n
+ || p->offset >= (Dwarf_Off) (endp - *readp + offset))
+ {
+ *readp = endp;
+ printf (gettext (" [%6tx] <UNUSED GARBAGE IN REST OF SECTION>\n"),
+ offset);
+ return true;
+ }
+
+ if (p->offset != (Dwarf_Off) offset)
+ {
+ *readp += p->offset - offset;
+ printf (gettext (" [%6tx] <UNUSED GARBAGE> ... %" PRIu64 " bytes ...\n"),
+ offset, (Dwarf_Off) p->offset - offset);
+ return true;
+ }
+
+ if (address_sizep != NULL)
+ *address_sizep = listptr_address_size (p);
+ if (offset_sizep != NULL)
+ *offset_sizep = listptr_offset_size (p);
+ if (base != NULL)
+ *base = listptr_base (p);
+ if (cu != NULL)
+ *cu = p->cu;
+
+ return false;
+}
+
+
+static void
+print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
+ Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ const size_t sh_size = (dbg->sectiondata[IDX_debug_abbrev] ?
+ dbg->sectiondata[IDX_debug_abbrev]->d_size : 0);
+
+ printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"
+ " [ Code]\n"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset);
+
+ Dwarf_Off offset = 0;
+ while (offset < sh_size)
+ {
+ printf (gettext ("\nAbbreviation section at offset %" PRIu64 ":\n"),
+ offset);
+
+ while (1)
+ {
+ size_t length;
+ Dwarf_Abbrev abbrev;
+
+ int res = dwarf_offabbrev (dbg, offset, &length, &abbrev);
+ if (res != 0)
+ {
+ if (unlikely (res < 0))
+ {
+ printf (gettext ("\
+ *** error while reading abbreviation: %s\n"),
+ dwarf_errmsg (-1));
+ return;
+ }
+
+ /* This is the NUL byte at the end of the section. */
+ ++offset;
+ break;
+ }
+
+ /* We know these calls can never fail. */
+ unsigned int code = dwarf_getabbrevcode (&abbrev);
+ unsigned int tag = dwarf_getabbrevtag (&abbrev);
+ int has_children = dwarf_abbrevhaschildren (&abbrev);
+
+ printf (gettext (" [%5u] offset: %" PRId64
+ ", children: %s, tag: %s\n"),
+ code, (int64_t) offset,
+ has_children ? gettext ("yes") : gettext ("no"),
+ dwarf_tag_name (tag));
+
+ size_t cnt = 0;
+ unsigned int name;
+ unsigned int form;
+ Dwarf_Off enoffset;
+ while (dwarf_getabbrevattr (&abbrev, cnt,
+ &name, &form, &enoffset) == 0)
+ {
+ printf (" attr: %s, form: %s, offset: %#" PRIx64 "\n",
+ dwarf_attr_name (name), dwarf_form_name (form),
+ (uint64_t) enoffset);
+
+ ++cnt;
+ }
+
+ offset += length;
+ }
+ }
+}
+
+
+/* Print content of DWARF .debug_aranges section. We fortunately do
+ not have to know a bit about the structure of the section, libdwarf
+ takes care of it. */
+static void
+print_decoded_aranges_section (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr, Dwarf *dbg)
+{
+ Dwarf_Aranges *aranges;
+ size_t cnt;
+ if (unlikely (dwarf_getaranges (dbg, &aranges, &cnt) != 0))
+ {
+ error (0, 0, gettext ("cannot get .debug_aranges content: %s"),
+ dwarf_errmsg (-1));
+ return;
+ }
+
+ GElf_Shdr glink_mem;
+ GElf_Shdr *glink;
+ glink = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &glink_mem);
+ if (glink == NULL)
+ {
+ error (0, 0, gettext ("invalid sh_link value in section %zu"),
+ elf_ndxscn (scn));
+ return;
+ }
+
+ printf (ngettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 " contains %zu entry:\n",
+ "\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 " contains %zu entries:\n",
+ cnt),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset, cnt);
+
+ /* Compute floor(log16(cnt)). */
+ size_t tmp = cnt;
+ int digits = 1;
+ while (tmp >= 16)
+ {
+ ++digits;
+ tmp >>= 4;
+ }
+
+ for (size_t n = 0; n < cnt; ++n)
+ {
+ Dwarf_Arange *runp = dwarf_onearange (aranges, n);
+ if (unlikely (runp == NULL))
+ {
+ printf ("cannot get arange %zu: %s\n", n, dwarf_errmsg (-1));
+ return;
+ }
+
+ Dwarf_Addr start;
+ Dwarf_Word length;
+ Dwarf_Off offset;
+
+ if (unlikely (dwarf_getarangeinfo (runp, &start, &length, &offset) != 0))
+ printf (gettext (" [%*zu] ???\n"), digits, n);
+ else
+ printf (gettext (" [%*zu] start: %0#*" PRIx64
+ ", length: %5" PRIu64 ", CU DIE offset: %6"
+ PRId64 "\n"),
+ digits, n, ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 10 : 18,
+ (uint64_t) start, (uint64_t) length, (int64_t) offset);
+ }
+}
+
+
+/* Print content of DWARF .debug_aranges section. */
+static void
+print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
+ Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
+ GElf_Shdr *shdr, Dwarf *dbg)
+{
+ if (decodedaranges)
+ {
+ print_decoded_aranges_section (ebl, ehdr, scn, shdr, dbg);
+ return;
+ }
+
+ Elf_Data *data = dbg->sectiondata[IDX_debug_aranges];
+
+ if (unlikely (data == NULL))
+ {
+ error (0, 0, gettext ("cannot get .debug_aranges content: %s"),
+ elf_errmsg (-1));
+ return;
+ }
+
+ printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset);
+
+ const unsigned char *readp = data->d_buf;
+ const unsigned char *readendp = readp + data->d_size;
+
+ while (readp < readendp)
+ {
+ const unsigned char *hdrstart = readp;
+ size_t start_offset = hdrstart - (const unsigned char *) data->d_buf;
+
+ printf (gettext ("\nTable at offset %zu:\n"), start_offset);
+ if (readp + 4 > readendp)
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data in section [%zu] '%s'"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr));
+ return;
+ }
+
+ Dwarf_Word length = read_4ubyte_unaligned_inc (dbg, readp);
+ unsigned int length_bytes = 4;
+ if (length == DWARF3_LENGTH_64_BIT)
+ {
+ if (readp + 8 > readendp)
+ goto invalid_data;
+ length = read_8ubyte_unaligned_inc (dbg, readp);
+ length_bytes = 8;
+ }
+
+ const unsigned char *nexthdr = readp + length;
+ printf (gettext ("\n Length: %6" PRIu64 "\n"),
+ (uint64_t) length);
+
+ if (unlikely (length > (size_t) (readendp - readp)))
+ goto invalid_data;
+
+ if (length == 0)
+ continue;
+
+ if (readp + 2 > readendp)
+ goto invalid_data;
+ uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, readp);
+ printf (gettext (" DWARF version: %6" PRIuFAST16 "\n"),
+ version);
+ if (version != 2)
+ {
+ error (0, 0, gettext ("unsupported aranges version"));
+ goto next_table;
+ }
+
+ Dwarf_Word offset;
+ if (readp + length_bytes > readendp)
+ goto invalid_data;
+ if (length_bytes == 8)
+ offset = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ offset = read_4ubyte_unaligned_inc (dbg, readp);
+ printf (gettext (" CU offset: %6" PRIx64 "\n"),
+ (uint64_t) offset);
+
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ unsigned int address_size = *readp++;
+ printf (gettext (" Address size: %6" PRIu64 "\n"),
+ (uint64_t) address_size);
+ if (address_size != 4 && address_size != 8)
+ {
+ error (0, 0, gettext ("unsupported address size"));
+ goto next_table;
+ }
+
+ unsigned int segment_size = *readp++;
+ printf (gettext (" Segment size: %6" PRIu64 "\n\n"),
+ (uint64_t) segment_size);
+ if (segment_size != 0 && segment_size != 4 && segment_size != 8)
+ {
+ error (0, 0, gettext ("unsupported segment size"));
+ goto next_table;
+ }
+
+ /* Round the address to the next multiple of 2*address_size. */
+ readp += ((2 * address_size - ((readp - hdrstart) % (2 * address_size)))
+ % (2 * address_size));
+
+ while (readp < nexthdr)
+ {
+ Dwarf_Word range_address;
+ Dwarf_Word range_length;
+ Dwarf_Word segment = 0;
+ if (readp + 2 * address_size + segment_size > readendp)
+ goto invalid_data;
+ if (address_size == 4)
+ {
+ range_address = read_4ubyte_unaligned_inc (dbg, readp);
+ range_length = read_4ubyte_unaligned_inc (dbg, readp);
+ }
+ else
+ {
+ range_address = read_8ubyte_unaligned_inc (dbg, readp);
+ range_length = read_8ubyte_unaligned_inc (dbg, readp);
+ }
+
+ if (segment_size == 4)
+ segment = read_4ubyte_unaligned_inc (dbg, readp);
+ else if (segment_size == 8)
+ segment = read_8ubyte_unaligned_inc (dbg, readp);
+
+ if (range_address == 0 && range_length == 0 && segment == 0)
+ break;
+
+ char *b = format_dwarf_addr (dwflmod, address_size, range_address,
+ range_address);
+ char *e = format_dwarf_addr (dwflmod, address_size,
+ range_address + range_length - 1,
+ range_length);
+ if (segment_size != 0)
+ printf (gettext (" %s..%s (%" PRIx64 ")\n"), b, e,
+ (uint64_t) segment);
+ else
+ printf (gettext (" %s..%s\n"), b, e);
+ free (b);
+ free (e);
+ }
+
+ next_table:
+ if (readp != nexthdr)
+ {
+ size_t padding = nexthdr - readp;
+ printf (gettext (" %zu padding bytes\n"), padding);
+ readp = nexthdr;
+ }
+ }
+}
+
+
+/* Print content of DWARF .debug_ranges section. */
+static void
+print_debug_ranges_section (Dwfl_Module *dwflmod,
+ Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr,
+ Dwarf *dbg)
+{
+ Elf_Data *data = dbg->sectiondata[IDX_debug_ranges];
+
+ if (unlikely (data == NULL))
+ {
+ error (0, 0, gettext ("cannot get .debug_ranges content: %s"),
+ elf_errmsg (-1));
+ return;
+ }
+
+ printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset);
+
+ sort_listptr (&known_rangelistptr, "rangelistptr");
+ size_t listptr_idx = 0;
+
+ uint_fast8_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+
+ bool first = true;
+ Dwarf_Addr base = 0;
+ unsigned char *const endp = (unsigned char *) data->d_buf + data->d_size;
+ unsigned char *readp = data->d_buf;
+ Dwarf_CU *last_cu = NULL;
+ while (readp < endp)
+ {
+ ptrdiff_t offset = readp - (unsigned char *) data->d_buf;
+ Dwarf_CU *cu = last_cu;
+
+ if (first && skip_listptr_hole (&known_rangelistptr, &listptr_idx,
+ &address_size, NULL, &base, &cu,
+ offset, &readp, endp))
+ continue;
+
+ if (last_cu != cu)
+ {
+ char *basestr = format_dwarf_addr (dwflmod, address_size,
+ base, base);
+ Dwarf_Die cudie;
+ if (dwarf_cu_die (cu, &cudie,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL) == NULL)
+ printf (gettext ("\n Unknown CU base: %s\n"), basestr);
+ else
+ printf (gettext ("\n CU [%6" PRIx64 "] base: %s\n"),
+ dwarf_dieoffset (&cudie), basestr);
+ free (basestr);
+ }
+ last_cu = cu;
+
+ if (unlikely (data->d_size - offset < (size_t) address_size * 2))
+ {
+ printf (gettext (" [%6tx] <INVALID DATA>\n"), offset);
+ break;
+ }
+
+ Dwarf_Addr begin;
+ Dwarf_Addr end;
+ if (address_size == 8)
+ {
+ begin = read_8ubyte_unaligned_inc (dbg, readp);
+ end = read_8ubyte_unaligned_inc (dbg, readp);
+ }
+ else
+ {
+ begin = read_4ubyte_unaligned_inc (dbg, readp);
+ end = read_4ubyte_unaligned_inc (dbg, readp);
+ if (begin == (Dwarf_Addr) (uint32_t) -1)
+ begin = (Dwarf_Addr) -1l;
+ }
+
+ if (begin == (Dwarf_Addr) -1l) /* Base address entry. */
+ {
+ char *b = format_dwarf_addr (dwflmod, address_size, end, end);
+ printf (gettext (" [%6tx] base address\n %s\n"), offset, b);
+ free (b);
+ base = end;
+ }
+ else if (begin == 0 && end == 0) /* End of list entry. */
+ {
+ if (first)
+ printf (gettext (" [%6tx] empty list\n"), offset);
+ first = true;
+ }
+ else
+ {
+ /* We have an address range entry. */
+ if (first) /* First address range entry in a list. */
+ printf (" [%6tx] ", offset);
+ else
+ printf (" ");
+
+ printf ("range %" PRIx64 ", %" PRIx64 "\n", begin, end);
+ if (! print_unresolved_addresses)
+ {
+ char *b = format_dwarf_addr (dwflmod, address_size, base + begin,
+ base + begin);
+ char *e = format_dwarf_addr (dwflmod, address_size,
+ base + end - 1, base + end);
+ printf (" %s..\n", b);
+ printf (" %s\n", e);
+ free (b);
+ free (e);
+ }
+
+ first = false;
+ }
+ }
+}
+
+#define REGNAMESZ 16
+static const char *
+register_info (Ebl *ebl, unsigned int regno, const Ebl_Register_Location *loc,
+ char name[REGNAMESZ], int *bits, int *type)
+{
+ const char *set;
+ const char *pfx;
+ int ignore;
+ ssize_t n = ebl_register_info (ebl, regno, name, REGNAMESZ, &pfx, &set,
+ bits ?: &ignore, type ?: &ignore);
+ if (n <= 0)
+ {
+ if (loc != NULL)
+ snprintf (name, REGNAMESZ, "reg%u", loc->regno);
+ else
+ snprintf (name, REGNAMESZ, "??? 0x%x", regno);
+ if (bits != NULL)
+ *bits = loc != NULL ? loc->bits : 0;
+ if (type != NULL)
+ *type = DW_ATE_unsigned;
+ set = "??? unrecognized";
+ }
+ else
+ {
+ if (bits != NULL && *bits <= 0)
+ *bits = loc != NULL ? loc->bits : 0;
+ if (type != NULL && *type == DW_ATE_void)
+ *type = DW_ATE_unsigned;
+
+ }
+ return set;
+}
+
+static const unsigned char *
+read_encoded (unsigned int encoding, const unsigned char *readp,
+ const unsigned char *const endp, uint64_t *res, Dwarf *dbg)
+{
+ if ((encoding & 0xf) == DW_EH_PE_absptr)
+ encoding = gelf_getclass (dbg->elf) == ELFCLASS32
+ ? DW_EH_PE_udata4 : DW_EH_PE_udata8;
+
+ switch (encoding & 0xf)
+ {
+ case DW_EH_PE_uleb128:
+ get_uleb128 (*res, readp, endp);
+ break;
+ case DW_EH_PE_sleb128:
+ get_sleb128 (*res, readp, endp);
+ break;
+ case DW_EH_PE_udata2:
+ if (readp + 2 > endp)
+ goto invalid;
+ *res = read_2ubyte_unaligned_inc (dbg, readp);
+ break;
+ case DW_EH_PE_udata4:
+ if (readp + 4 > endp)
+ goto invalid;
+ *res = read_4ubyte_unaligned_inc (dbg, readp);
+ break;
+ case DW_EH_PE_udata8:
+ if (readp + 8 > endp)
+ goto invalid;
+ *res = read_8ubyte_unaligned_inc (dbg, readp);
+ break;
+ case DW_EH_PE_sdata2:
+ if (readp + 2 > endp)
+ goto invalid;
+ *res = read_2sbyte_unaligned_inc (dbg, readp);
+ break;
+ case DW_EH_PE_sdata4:
+ if (readp + 4 > endp)
+ goto invalid;
+ *res = read_4sbyte_unaligned_inc (dbg, readp);
+ break;
+ case DW_EH_PE_sdata8:
+ if (readp + 8 > endp)
+ goto invalid;
+ *res = read_8sbyte_unaligned_inc (dbg, readp);
+ break;
+ default:
+ invalid:
+ error (1, 0,
+ gettext ("invalid encoding"));
+ }
+
+ return readp;
+}
+
+
+static void
+print_cfa_program (const unsigned char *readp, const unsigned char *const endp,
+ Dwarf_Word vma_base, unsigned int code_align,
+ int data_align,
+ unsigned int version, unsigned int ptr_size,
+ unsigned int encoding,
+ Dwfl_Module *dwflmod, Ebl *ebl, Dwarf *dbg)
+{
+ char regnamebuf[REGNAMESZ];
+ const char *regname (unsigned int regno)
+ {
+ register_info (ebl, regno, NULL, regnamebuf, NULL, NULL);
+ return regnamebuf;
+ }
+
+ puts ("\n Program:");
+ Dwarf_Word pc = vma_base;
+ while (readp < endp)
+ {
+ unsigned int opcode = *readp++;
+
+ if (opcode < DW_CFA_advance_loc)
+ /* Extended opcode. */
+ switch (opcode)
+ {
+ uint64_t op1;
+ int64_t sop1;
+ uint64_t op2;
+ int64_t sop2;
+
+ case DW_CFA_nop:
+ puts (" nop");
+ break;
+ case DW_CFA_set_loc:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ readp = read_encoded (encoding, readp, endp, &op1, dbg);
+ printf (" set_loc %#" PRIx64 " to %#" PRIx64 "\n",
+ op1, pc = vma_base + op1);
+ break;
+ case DW_CFA_advance_loc1:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ printf (" advance_loc1 %u to %#" PRIx64 "\n",
+ *readp, pc += *readp * code_align);
+ ++readp;
+ break;
+ case DW_CFA_advance_loc2:
+ if ((uint64_t) (endp - readp) < 2)
+ goto invalid;
+ op1 = read_2ubyte_unaligned_inc (dbg, readp);
+ printf (" advance_loc2 %" PRIu64 " to %#" PRIx64 "\n",
+ op1, pc += op1 * code_align);
+ break;
+ case DW_CFA_advance_loc4:
+ if ((uint64_t) (endp - readp) < 4)
+ goto invalid;
+ op1 = read_4ubyte_unaligned_inc (dbg, readp);
+ printf (" advance_loc4 %" PRIu64 " to %#" PRIx64 "\n",
+ op1, pc += op1 * code_align);
+ break;
+ case DW_CFA_offset_extended:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op2, readp, endp);
+ printf (" offset_extended r%" PRIu64 " (%s) at cfa%+" PRId64
+ "\n",
+ op1, regname (op1), op2 * data_align);
+ break;
+ case DW_CFA_restore_extended:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ printf (" restore_extended r%" PRIu64 " (%s)\n",
+ op1, regname (op1));
+ break;
+ case DW_CFA_undefined:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ printf (" undefined r%" PRIu64 " (%s)\n", op1, regname (op1));
+ break;
+ case DW_CFA_same_value:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ printf (" same_value r%" PRIu64 " (%s)\n", op1, regname (op1));
+ break;
+ case DW_CFA_register:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op2, readp, endp);
+ printf (" register r%" PRIu64 " (%s) in r%" PRIu64 " (%s)\n",
+ op1, regname (op1), op2, regname (op2));
+ break;
+ case DW_CFA_remember_state:
+ puts (" remember_state");
+ break;
+ case DW_CFA_restore_state:
+ puts (" restore_state");
+ break;
+ case DW_CFA_def_cfa:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op2, readp, endp);
+ printf (" def_cfa r%" PRIu64 " (%s) at offset %" PRIu64 "\n",
+ op1, regname (op1), op2);
+ break;
+ case DW_CFA_def_cfa_register:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ printf (" def_cfa_register r%" PRIu64 " (%s)\n",
+ op1, regname (op1));
+ break;
+ case DW_CFA_def_cfa_offset:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ printf (" def_cfa_offset %" PRIu64 "\n", op1);
+ break;
+ case DW_CFA_def_cfa_expression:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp); /* Length of DW_FORM_block. */
+ printf (" def_cfa_expression %" PRIu64 "\n", op1);
+ if ((uint64_t) (endp - readp) < op1)
+ {
+ invalid:
+ fputs (gettext (" <INVALID DATA>\n"), stdout);
+ return;
+ }
+ print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, NULL,
+ op1, readp);
+ readp += op1;
+ break;
+ case DW_CFA_expression:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op2, readp, endp); /* Length of DW_FORM_block. */
+ printf (" expression r%" PRIu64 " (%s) \n",
+ op1, regname (op1));
+ if ((uint64_t) (endp - readp) < op2)
+ goto invalid;
+ print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, NULL,
+ op2, readp);
+ readp += op2;
+ break;
+ case DW_CFA_offset_extended_sf:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_sleb128 (sop2, readp, endp);
+ printf (" offset_extended_sf r%" PRIu64 " (%s) at cfa%+"
+ PRId64 "\n",
+ op1, regname (op1), sop2 * data_align);
+ break;
+ case DW_CFA_def_cfa_sf:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_sleb128 (sop2, readp, endp);
+ printf (" def_cfa_sf r%" PRIu64 " (%s) at offset %" PRId64 "\n",
+ op1, regname (op1), sop2 * data_align);
+ break;
+ case DW_CFA_def_cfa_offset_sf:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_sleb128 (sop1, readp, endp);
+ printf (" def_cfa_offset_sf %" PRId64 "\n", sop1 * data_align);
+ break;
+ case DW_CFA_val_offset:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op2, readp, endp);
+ printf (" val_offset %" PRIu64 " at offset %" PRIu64 "\n",
+ op1, op2 * data_align);
+ break;
+ case DW_CFA_val_offset_sf:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_sleb128 (sop2, readp, endp);
+ printf (" val_offset_sf %" PRIu64 " at offset %" PRId64 "\n",
+ op1, sop2 * data_align);
+ break;
+ case DW_CFA_val_expression:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op2, readp, endp); /* Length of DW_FORM_block. */
+ printf (" val_expression r%" PRIu64 " (%s)\n",
+ op1, regname (op1));
+ if ((uint64_t) (endp - readp) < op2)
+ goto invalid;
+ print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0,
+ NULL, op2, readp);
+ readp += op2;
+ break;
+ case DW_CFA_MIPS_advance_loc8:
+ if ((uint64_t) (endp - readp) < 8)
+ goto invalid;
+ op1 = read_8ubyte_unaligned_inc (dbg, readp);
+ printf (" MIPS_advance_loc8 %" PRIu64 " to %#" PRIx64 "\n",
+ op1, pc += op1 * code_align);
+ break;
+ case DW_CFA_GNU_window_save:
+ puts (" GNU_window_save");
+ break;
+ case DW_CFA_GNU_args_size:
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (op1, readp, endp);
+ printf (" args_size %" PRIu64 "\n", op1);
+ break;
+ default:
+ printf (" ??? (%u)\n", opcode);
+ break;
+ }
+ else if (opcode < DW_CFA_offset)
+ printf (" advance_loc %u to %#" PRIx64 "\n",
+ opcode & 0x3f, pc += (opcode & 0x3f) * code_align);
+ else if (opcode < DW_CFA_restore)
+ {
+ uint64_t offset;
+ if ((uint64_t) (endp - readp) < 1)
+ goto invalid;
+ get_uleb128 (offset, readp, endp);
+ printf (" offset r%u (%s) at cfa%+" PRId64 "\n",
+ opcode & 0x3f, regname (opcode & 0x3f), offset * data_align);
+ }
+ else
+ printf (" restore r%u (%s)\n",
+ opcode & 0x3f, regname (opcode & 0x3f));
+ }
+}
+
+
+static unsigned int
+encoded_ptr_size (int encoding, unsigned int ptr_size)
+{
+ switch (encoding & 7)
+ {
+ case DW_EH_PE_udata4:
+ return 4;
+ case DW_EH_PE_udata8:
+ return 8;
+ case 0:
+ return ptr_size;
+ }
+
+ fprintf (stderr, "Unsupported pointer encoding: %#x, "
+ "assuming pointer size of %d.\n", encoding, ptr_size);
+ return ptr_size;
+}
+
+
+static unsigned int
+print_encoding (unsigned int val)
+{
+ switch (val & 0xf)
+ {
+ case DW_EH_PE_absptr:
+ fputs ("absptr", stdout);
+ break;
+ case DW_EH_PE_uleb128:
+ fputs ("uleb128", stdout);
+ break;
+ case DW_EH_PE_udata2:
+ fputs ("udata2", stdout);
+ break;
+ case DW_EH_PE_udata4:
+ fputs ("udata4", stdout);
+ break;
+ case DW_EH_PE_udata8:
+ fputs ("udata8", stdout);
+ break;
+ case DW_EH_PE_sleb128:
+ fputs ("sleb128", stdout);
+ break;
+ case DW_EH_PE_sdata2:
+ fputs ("sdata2", stdout);
+ break;
+ case DW_EH_PE_sdata4:
+ fputs ("sdata4", stdout);
+ break;
+ case DW_EH_PE_sdata8:
+ fputs ("sdata8", stdout);
+ break;
+ default:
+ /* We did not use any of the bits after all. */
+ return val;
+ }
+
+ return val & ~0xf;
+}
+
+
+static unsigned int
+print_relinfo (unsigned int val)
+{
+ switch (val & 0x70)
+ {
+ case DW_EH_PE_pcrel:
+ fputs ("pcrel", stdout);
+ break;
+ case DW_EH_PE_textrel:
+ fputs ("textrel", stdout);
+ break;
+ case DW_EH_PE_datarel:
+ fputs ("datarel", stdout);
+ break;
+ case DW_EH_PE_funcrel:
+ fputs ("funcrel", stdout);
+ break;
+ case DW_EH_PE_aligned:
+ fputs ("aligned", stdout);
+ break;
+ default:
+ return val;
+ }
+
+ return val & ~0x70;
+}
+
+
+static void
+print_encoding_base (const char *pfx, unsigned int fde_encoding)
+{
+ printf ("(%s", pfx);
+
+ if (fde_encoding == DW_EH_PE_omit)
+ puts ("omit)");
+ else
+ {
+ unsigned int w = fde_encoding;
+
+ w = print_encoding (w);
+
+ if (w & 0x70)
+ {
+ if (w != fde_encoding)
+ fputc_unlocked (' ', stdout);
+
+ w = print_relinfo (w);
+ }
+
+ if (w != 0)
+ printf ("%s%x", w != fde_encoding ? " " : "", w);
+
+ puts (")");
+ }
+}
+
+
+static void
+print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ size_t shstrndx;
+ /* We know this call will succeed since it did in the caller. */
+ (void) elf_getshdrstrndx (ebl->elf, &shstrndx);
+ const char *scnname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name);
+
+ /* Needed if we find PC-relative addresses. */
+ GElf_Addr bias;
+ if (dwfl_module_getelf (dwflmod, &bias) == NULL)
+ {
+ error (0, 0, gettext ("cannot get ELF: %s"), dwfl_errmsg (-1));
+ return;
+ }
+
+ bool is_eh_frame = strcmp (scnname, ".eh_frame") == 0;
+ Elf_Data *data = (is_eh_frame
+ ? elf_rawdata (scn, NULL)
+ : dbg->sectiondata[IDX_debug_frame]);
+
+ if (unlikely (data == NULL))
+ {
+ error (0, 0, gettext ("cannot get %s content: %s"),
+ scnname, elf_errmsg (-1));
+ return;
+ }
+
+ if (is_eh_frame)
+ printf (gettext ("\
+\nCall frame information section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+ elf_ndxscn (scn), scnname, (uint64_t) shdr->sh_offset);
+ else
+ printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+ elf_ndxscn (scn), scnname, (uint64_t) shdr->sh_offset);
+
+ struct cieinfo
+ {
+ ptrdiff_t cie_offset;
+ const char *augmentation;
+ unsigned int code_alignment_factor;
+ unsigned int data_alignment_factor;
+ uint8_t address_size;
+ uint8_t fde_encoding;
+ uint8_t lsda_encoding;
+ struct cieinfo *next;
+ } *cies = NULL;
+
+ const unsigned char *readp = data->d_buf;
+ const unsigned char *const dataend = ((unsigned char *) data->d_buf
+ + data->d_size);
+ while (readp < dataend)
+ {
+ if (unlikely (readp + 4 > dataend))
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data in section [%zu] '%s'"),
+ elf_ndxscn (scn), scnname);
+ return;
+ }
+
+ /* At the beginning there must be a CIE. There can be multiple,
+ hence we test tis in a loop. */
+ ptrdiff_t offset = readp - (unsigned char *) data->d_buf;
+
+ Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, readp);
+ unsigned int length = 4;
+ if (unlikely (unit_length == 0xffffffff))
+ {
+ if (unlikely (readp + 8 > dataend))
+ goto invalid_data;
+
+ unit_length = read_8ubyte_unaligned_inc (dbg, readp);
+ length = 8;
+ }
+
+ if (unlikely (unit_length == 0))
+ {
+ printf (gettext ("\n [%6tx] Zero terminator\n"), offset);
+ continue;
+ }
+
+ Dwarf_Word maxsize = dataend - readp;
+ if (unlikely (unit_length > maxsize))
+ goto invalid_data;
+
+ unsigned int ptr_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+
+ ptrdiff_t start = readp - (unsigned char *) data->d_buf;
+ const unsigned char *const cieend = readp + unit_length;
+ if (unlikely (cieend > dataend || readp + 8 > dataend))
+ goto invalid_data;
+
+ Dwarf_Off cie_id;
+ if (length == 4)
+ {
+ cie_id = read_4ubyte_unaligned_inc (dbg, readp);
+ if (!is_eh_frame && cie_id == DW_CIE_ID_32)
+ cie_id = DW_CIE_ID_64;
+ }
+ else
+ cie_id = read_8ubyte_unaligned_inc (dbg, readp);
+
+ uint_fast8_t version = 2;
+ unsigned int code_alignment_factor;
+ int data_alignment_factor;
+ unsigned int fde_encoding = 0;
+ unsigned int lsda_encoding = 0;
+ Dwarf_Word initial_location = 0;
+ Dwarf_Word vma_base = 0;
+
+ if (cie_id == (is_eh_frame ? 0 : DW_CIE_ID_64))
+ {
+ version = *readp++;
+ const char *const augmentation = (const char *) readp;
+ readp = memchr (readp, '\0', cieend - readp);
+ if (unlikely (readp == NULL))
+ goto invalid_data;
+ ++readp;
+
+ uint_fast8_t segment_size = 0;
+ if (version >= 4)
+ {
+ if (cieend - readp < 5)
+ goto invalid_data;
+ ptr_size = *readp++;
+ segment_size = *readp++;
+ }
+
+ if (cieend - readp < 1)
+ goto invalid_data;
+ get_uleb128 (code_alignment_factor, readp, cieend);
+ if (cieend - readp < 1)
+ goto invalid_data;
+ get_sleb128 (data_alignment_factor, readp, cieend);
+
+ /* In some variant for unwind data there is another field. */
+ if (strcmp (augmentation, "eh") == 0)
+ readp += ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+
+ unsigned int return_address_register;
+ if (cieend - readp < 1)
+ goto invalid_data;
+ if (unlikely (version == 1))
+ return_address_register = *readp++;
+ else
+ get_uleb128 (return_address_register, readp, cieend);
+
+ printf ("\n [%6tx] CIE length=%" PRIu64 "\n"
+ " CIE_id: %" PRIu64 "\n"
+ " version: %u\n"
+ " augmentation: \"%s\"\n",
+ offset, (uint64_t) unit_length, (uint64_t) cie_id,
+ version, augmentation);
+ if (version >= 4)
+ printf (" address_size: %u\n"
+ " segment_size: %u\n",
+ ptr_size, segment_size);
+ printf (" code_alignment_factor: %u\n"
+ " data_alignment_factor: %d\n"
+ " return_address_register: %u\n",
+ code_alignment_factor,
+ data_alignment_factor, return_address_register);
+
+ if (augmentation[0] == 'z')
+ {
+ unsigned int augmentationlen;
+ get_uleb128 (augmentationlen, readp, cieend);
+
+ if (augmentationlen > (size_t) (cieend - readp))
+ {
+ error (0, 0, gettext ("invalid augmentation length"));
+ readp = cieend;
+ continue;
+ }
+
+ const char *hdr = "Augmentation data:";
+ const char *cp = augmentation + 1;
+ while (*cp != '\0' && cp < augmentation + augmentationlen + 1)
+ {
+ printf (" %-26s%#x ", hdr, *readp);
+ hdr = "";
+
+ if (*cp == 'R')
+ {
+ fde_encoding = *readp++;
+ print_encoding_base (gettext ("FDE address encoding: "),
+ fde_encoding);
+ }
+ else if (*cp == 'L')
+ {
+ lsda_encoding = *readp++;
+ print_encoding_base (gettext ("LSDA pointer encoding: "),
+ lsda_encoding);
+ }
+ else if (*cp == 'P')
+ {
+ /* Personality. This field usually has a relocation
+ attached pointing to __gcc_personality_v0. */
+ const unsigned char *startp = readp;
+ unsigned int encoding = *readp++;
+ uint64_t val = 0;
+ readp = read_encoded (encoding, readp,
+ readp - 1 + augmentationlen,
+ &val, dbg);
+
+ while (++startp < readp)
+ printf ("%#x ", *startp);
+
+ putchar ('(');
+ print_encoding (encoding);
+ putchar (' ');
+ switch (encoding & 0xf)
+ {
+ case DW_EH_PE_sleb128:
+ case DW_EH_PE_sdata2:
+ case DW_EH_PE_sdata4:
+ printf ("%" PRId64 ")\n", val);
+ break;
+ default:
+ printf ("%#" PRIx64 ")\n", val);
+ break;
+ }
+ }
+ else
+ printf ("(%x)\n", *readp++);
+
+ ++cp;
+ }
+ }
+
+ if (likely (ptr_size == 4 || ptr_size == 8))
+ {
+ struct cieinfo *newp = alloca (sizeof (*newp));
+ newp->cie_offset = offset;
+ newp->augmentation = augmentation;
+ newp->fde_encoding = fde_encoding;
+ newp->lsda_encoding = lsda_encoding;
+ newp->address_size = ptr_size;
+ newp->code_alignment_factor = code_alignment_factor;
+ newp->data_alignment_factor = data_alignment_factor;
+ newp->next = cies;
+ cies = newp;
+ }
+ }
+ else
+ {
+ struct cieinfo *cie = cies;
+ while (cie != NULL)
+ if (is_eh_frame
+ ? ((Dwarf_Off) start - cie_id) == (Dwarf_Off) cie->cie_offset
+ : cie_id == (Dwarf_Off) cie->cie_offset)
+ break;
+ else
+ cie = cie->next;
+ if (unlikely (cie == NULL))
+ {
+ puts ("invalid CIE reference in FDE");
+ return;
+ }
+
+ /* Initialize from CIE data. */
+ fde_encoding = cie->fde_encoding;
+ lsda_encoding = cie->lsda_encoding;
+ ptr_size = encoded_ptr_size (fde_encoding, cie->address_size);
+ code_alignment_factor = cie->code_alignment_factor;
+ data_alignment_factor = cie->data_alignment_factor;
+
+ const unsigned char *base = readp;
+ // XXX There are sometimes relocations for this value
+ initial_location = read_addr_unaligned_inc (ptr_size, dbg, readp);
+ Dwarf_Word address_range
+ = read_addr_unaligned_inc (ptr_size, dbg, readp);
+
+ /* pcrel for an FDE address is relative to the runtime
+ address of the start_address field itself. Sign extend
+ if necessary to make sure the calculation is done on the
+ full 64 bit address even when initial_location only holds
+ the lower 32 bits. */
+ Dwarf_Addr pc_start = initial_location;
+ if (ptr_size == 4)
+ pc_start = (uint64_t) (int32_t) pc_start;
+ if ((fde_encoding & 0x70) == DW_EH_PE_pcrel)
+ pc_start += ((uint64_t) shdr->sh_addr
+ + (base - (const unsigned char *) data->d_buf)
+ - bias);
+
+ char *a = format_dwarf_addr (dwflmod, cie->address_size,
+ pc_start, initial_location);
+ printf ("\n [%6tx] FDE length=%" PRIu64 " cie=[%6tx]\n"
+ " CIE_pointer: %" PRIu64 "\n"
+ " initial_location: %s",
+ offset, (uint64_t) unit_length,
+ cie->cie_offset, (uint64_t) cie_id, a);
+ free (a);
+ if ((fde_encoding & 0x70) == DW_EH_PE_pcrel)
+ {
+ vma_base = (((uint64_t) shdr->sh_offset
+ + (base - (const unsigned char *) data->d_buf)
+ + (uint64_t) initial_location)
+ & (ptr_size == 4
+ ? UINT64_C (0xffffffff)
+ : UINT64_C (0xffffffffffffffff)));
+ printf (gettext (" (offset: %#" PRIx64 ")"),
+ (uint64_t) vma_base);
+ }
+
+ printf ("\n address_range: %#" PRIx64,
+ (uint64_t) address_range);
+ if ((fde_encoding & 0x70) == DW_EH_PE_pcrel)
+ printf (gettext (" (end offset: %#" PRIx64 ")"),
+ ((uint64_t) vma_base + (uint64_t) address_range)
+ & (ptr_size == 4
+ ? UINT64_C (0xffffffff)
+ : UINT64_C (0xffffffffffffffff)));
+ putchar ('\n');
+
+ if (cie->augmentation[0] == 'z')
+ {
+ unsigned int augmentationlen;
+ if (cieend - readp < 1)
+ goto invalid_data;
+ get_uleb128 (augmentationlen, readp, cieend);
+
+ if (augmentationlen > (size_t) (cieend - readp))
+ {
+ error (0, 0, gettext ("invalid augmentation length"));
+ readp = cieend;
+ continue;
+ }
+
+ if (augmentationlen > 0)
+ {
+ const char *hdr = "Augmentation data:";
+ const char *cp = cie->augmentation + 1;
+ unsigned int u = 0;
+ while (*cp != '\0'
+ && cp < cie->augmentation + augmentationlen + 1)
+ {
+ if (*cp == 'L')
+ {
+ uint64_t lsda_pointer;
+ const unsigned char *p
+ = read_encoded (lsda_encoding, &readp[u],
+ &readp[augmentationlen],
+ &lsda_pointer, dbg);
+ u = p - readp;
+ printf (gettext ("\
+ %-26sLSDA pointer: %#" PRIx64 "\n"),
+ hdr, lsda_pointer);
+ hdr = "";
+ }
+ ++cp;
+ }
+
+ while (u < augmentationlen)
+ {
+ printf (" %-26s%#x\n", hdr, readp[u++]);
+ hdr = "";
+ }
+ }
+
+ readp += augmentationlen;
+ }
+ }
+
+ /* Handle the initialization instructions. */
+ if (ptr_size != 4 && ptr_size !=8)
+ printf ("invalid CIE pointer size (%u), must be 4 or 8.\n", ptr_size);
+ else
+ print_cfa_program (readp, cieend, vma_base, code_alignment_factor,
+ data_alignment_factor, version, ptr_size,
+ fde_encoding, dwflmod, ebl, dbg);
+ readp = cieend;
+ }
+}
+
+
+struct attrcb_args
+{
+ Dwfl_Module *dwflmod;
+ Dwarf *dbg;
+ Dwarf_Die *die;
+ int level;
+ bool silent;
+ unsigned int version;
+ unsigned int addrsize;
+ unsigned int offset_size;
+ struct Dwarf_CU *cu;
+};
+
+
+static int
+attr_callback (Dwarf_Attribute *attrp, void *arg)
+{
+ struct attrcb_args *cbargs = (struct attrcb_args *) arg;
+ const int level = cbargs->level;
+ Dwarf_Die *die = cbargs->die;
+
+ unsigned int attr = dwarf_whatattr (attrp);
+ if (unlikely (attr == 0))
+ {
+ if (!cbargs->silent)
+ error (0, 0, gettext ("DIE [%" PRIx64 "] "
+ "cannot get attribute code: %s"),
+ dwarf_dieoffset (die), dwarf_errmsg (-1));
+ return DWARF_CB_ABORT;
+ }
+
+ unsigned int form = dwarf_whatform (attrp);
+ if (unlikely (form == 0))
+ {
+ if (!cbargs->silent)
+ error (0, 0, gettext ("DIE [%" PRIx64 "] "
+ "cannot get attribute form: %s"),
+ dwarf_dieoffset (die), dwarf_errmsg (-1));
+ return DWARF_CB_ABORT;
+ }
+
+ switch (form)
+ {
+ case DW_FORM_addr:
+ if (!cbargs->silent)
+ {
+ Dwarf_Addr addr;
+ if (unlikely (dwarf_formaddr (attrp, &addr) != 0))
+ {
+ attrval_out:
+ if (!cbargs->silent)
+ error (0, 0, gettext ("DIE [%" PRIx64 "] "
+ "cannot get attribute '%s' (%s) value: "
+ "%s"),
+ dwarf_dieoffset (die),
+ dwarf_attr_name (attr),
+ dwarf_form_name (form),
+ dwarf_errmsg (-1));
+ /* Don't ABORT, it might be other attributes can be resolved. */
+ return DWARF_CB_OK;
+ }
+ char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize,
+ addr, addr);
+ printf (" %*s%-20s (%s) %s\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), a);
+ free (a);
+ }
+ break;
+
+ case DW_FORM_indirect:
+ case DW_FORM_strp:
+ case DW_FORM_string:
+ case DW_FORM_GNU_strp_alt:
+ if (cbargs->silent)
+ break;
+ const char *str = dwarf_formstring (attrp);
+ if (unlikely (str == NULL))
+ goto attrval_out;
+ printf (" %*s%-20s (%s) \"%s\"\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), str);
+ break;
+
+ case DW_FORM_ref_addr:
+ case DW_FORM_ref_udata:
+ case DW_FORM_ref8:
+ case DW_FORM_ref4:
+ case DW_FORM_ref2:
+ case DW_FORM_ref1:
+ case DW_FORM_GNU_ref_alt:
+ if (cbargs->silent)
+ break;
+ Dwarf_Die ref;
+ if (unlikely (dwarf_formref_die (attrp, &ref) == NULL))
+ goto attrval_out;
+
+ printf (" %*s%-20s (%s) [%6" PRIxMAX "]\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), (uintmax_t) dwarf_dieoffset (&ref));
+ break;
+
+ case DW_FORM_ref_sig8:
+ if (cbargs->silent)
+ break;
+ printf (" %*s%-20s (%s) {%6" PRIx64 "}\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form),
+ (uint64_t) read_8ubyte_unaligned (attrp->cu->dbg, attrp->valp));
+ break;
+
+ case DW_FORM_sec_offset:
+ case DW_FORM_udata:
+ case DW_FORM_sdata:
+ case DW_FORM_data8:
+ case DW_FORM_data4:
+ case DW_FORM_data2:
+ case DW_FORM_data1:;
+ Dwarf_Word num;
+ if (unlikely (dwarf_formudata (attrp, &num) != 0))
+ goto attrval_out;
+
+ const char *valuestr = NULL;
+ switch (attr)
+ {
+ /* This case can take either a constant or a loclistptr. */
+ case DW_AT_data_member_location:
+ if (form != DW_FORM_sec_offset
+ && (cbargs->version >= 4
+ || (form != DW_FORM_data4 && form != DW_FORM_data8)))
+ {
+ if (!cbargs->silent)
+ printf (" %*s%-20s (%s) %" PRIxMAX "\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), (uintmax_t) num);
+ return DWARF_CB_OK;
+ }
+ FALLTHROUGH;
+
+ /* These cases always take a loclistptr and no constant. */
+ case DW_AT_location:
+ case DW_AT_data_location:
+ case DW_AT_vtable_elem_location:
+ case DW_AT_string_length:
+ case DW_AT_use_location:
+ case DW_AT_frame_base:
+ case DW_AT_return_addr:
+ case DW_AT_static_link:
+ case DW_AT_GNU_call_site_value:
+ case DW_AT_GNU_call_site_data_value:
+ case DW_AT_GNU_call_site_target:
+ case DW_AT_GNU_call_site_target_clobbered:
+ {
+ bool nlpt = notice_listptr (section_loc, &known_loclistptr,
+ cbargs->addrsize, cbargs->offset_size,
+ cbargs->cu, num);
+ if (!cbargs->silent)
+ printf (" %*s%-20s (%s) location list [%6" PRIxMAX "]%s\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), (uintmax_t) num,
+ nlpt ? "" : " <WARNING offset too big>");
+ }
+ return DWARF_CB_OK;
+
+ case DW_AT_ranges:
+ {
+ bool nlpt = notice_listptr (section_ranges, &known_rangelistptr,
+ cbargs->addrsize, cbargs->offset_size,
+ cbargs->cu, num);
+ if (!cbargs->silent)
+ printf (" %*s%-20s (%s) range list [%6" PRIxMAX "]%s\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), (uintmax_t) num,
+ nlpt ? "" : " <WARNING offset too big>");
+ }
+ return DWARF_CB_OK;
+
+ case DW_AT_language:
+ valuestr = dwarf_lang_name (num);
+ break;
+ case DW_AT_encoding:
+ valuestr = dwarf_encoding_name (num);
+ break;
+ case DW_AT_accessibility:
+ valuestr = dwarf_access_name (num);
+ break;
+ case DW_AT_defaulted:
+ valuestr = dwarf_defaulted_name (num);
+ break;
+ case DW_AT_visibility:
+ valuestr = dwarf_visibility_name (num);
+ break;
+ case DW_AT_virtuality:
+ valuestr = dwarf_virtuality_name (num);
+ break;
+ case DW_AT_identifier_case:
+ valuestr = dwarf_identifier_case_name (num);
+ break;
+ case DW_AT_calling_convention:
+ valuestr = dwarf_calling_convention_name (num);
+ break;
+ case DW_AT_inline:
+ valuestr = dwarf_inline_name (num);
+ break;
+ case DW_AT_ordering:
+ valuestr = dwarf_ordering_name (num);
+ break;
+ case DW_AT_discr_list:
+ valuestr = dwarf_discr_list_name (num);
+ break;
+ case DW_AT_decl_file:
+ case DW_AT_call_file:
+ {
+ /* Try to get the actual file, the current interface only
+ gives us full paths, but we only want to show the file
+ name for now. */
+ Dwarf_Die cudie;
+ if (dwarf_cu_die (cbargs->cu, &cudie,
+ NULL, NULL, NULL, NULL, NULL, NULL) != NULL)
+ {
+ Dwarf_Files *files;
+ size_t nfiles;
+ if (dwarf_getsrcfiles (&cudie, &files, &nfiles) == 0)
+ {
+ valuestr = dwarf_filesrc (files, num, NULL, NULL);
+ char *filename = strrchr (valuestr, '/');
+ if (filename != NULL)
+ valuestr = filename + 1;
+ }
+ }
+ }
+ break;
+ default:
+ /* Nothing. */
+ break;
+ }
+
+ if (cbargs->silent)
+ break;
+
+ /* When highpc is in constant form it is relative to lowpc.
+ In that case also show the address. */
+ Dwarf_Addr highpc;
+ if (attr == DW_AT_high_pc && dwarf_highpc (cbargs->die, &highpc) == 0)
+ {
+ char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize,
+ highpc, highpc);
+ printf (" %*s%-20s (%s) %" PRIuMAX " (%s)\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), (uintmax_t) num, a);
+ free (a);
+ }
+ else
+ {
+ Dwarf_Sword snum = 0;
+ if (form == DW_FORM_sdata)
+ if (unlikely (dwarf_formsdata (attrp, &snum) != 0))
+ goto attrval_out;
+
+ if (valuestr == NULL)
+ {
+ printf (" %*s%-20s (%s)",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form));
+ if (form == DW_FORM_sdata)
+ printf (" %" PRIdMAX "\n", (intmax_t) snum);
+ else
+ printf (" %" PRIuMAX "\n", (uintmax_t) num);
+ }
+ else
+ {
+ printf (" %*s%-20s (%s) %s",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), valuestr);
+ if (form == DW_FORM_sdata)
+ printf (" (%" PRIdMAX ")\n", (intmax_t) snum);
+ else
+ printf (" (%" PRIuMAX ")\n", (uintmax_t) num);
+ }
+ }
+ break;
+
+ case DW_FORM_flag:
+ if (cbargs->silent)
+ break;
+ bool flag;
+ if (unlikely (dwarf_formflag (attrp, &flag) != 0))
+ goto attrval_out;
+
+ printf (" %*s%-20s (%s) %s\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), flag ? gettext ("yes") : gettext ("no"));
+ break;
+
+ case DW_FORM_flag_present:
+ if (cbargs->silent)
+ break;
+ printf (" %*s%-20s (%s) %s\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form), gettext ("yes"));
+ break;
+
+ case DW_FORM_exprloc:
+ case DW_FORM_block4:
+ case DW_FORM_block2:
+ case DW_FORM_block1:
+ case DW_FORM_block:
+ if (cbargs->silent)
+ break;
+ Dwarf_Block block;
+ if (unlikely (dwarf_formblock (attrp, &block) != 0))
+ goto attrval_out;
+
+ printf (" %*s%-20s (%s) ",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form));
+
+ switch (attr)
+ {
+ default:
+ if (form != DW_FORM_exprloc)
+ {
+ print_block (block.length, block.data);
+ break;
+ }
+ FALLTHROUGH;
+
+ case DW_AT_location:
+ case DW_AT_data_location:
+ case DW_AT_data_member_location:
+ case DW_AT_vtable_elem_location:
+ case DW_AT_string_length:
+ case DW_AT_use_location:
+ case DW_AT_frame_base:
+ case DW_AT_return_addr:
+ case DW_AT_static_link:
+ case DW_AT_allocated:
+ case DW_AT_associated:
+ case DW_AT_bit_size:
+ case DW_AT_bit_offset:
+ case DW_AT_bit_stride:
+ case DW_AT_byte_size:
+ case DW_AT_byte_stride:
+ case DW_AT_count:
+ case DW_AT_lower_bound:
+ case DW_AT_upper_bound:
+ case DW_AT_GNU_call_site_value:
+ case DW_AT_GNU_call_site_data_value:
+ case DW_AT_GNU_call_site_target:
+ case DW_AT_GNU_call_site_target_clobbered:
+ putchar ('\n');
+ print_ops (cbargs->dwflmod, cbargs->dbg,
+ 12 + level * 2, 12 + level * 2,
+ cbargs->version, cbargs->addrsize, cbargs->offset_size,
+ attrp->cu, block.length, block.data);
+ break;
+ }
+ break;
+
+ default:
+ if (cbargs->silent)
+ break;
+ printf (" %*s%-20s (%s) ???\n",
+ (int) (level * 2), "", dwarf_attr_name (attr),
+ dwarf_form_name (form));
+ break;
+ }
+
+ return DWARF_CB_OK;
+}
+
+static void
+print_debug_units (Dwfl_Module *dwflmod,
+ Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr,
+ Dwarf *dbg, bool debug_types)
+{
+ const bool silent = !(print_debug_sections & section_info);
+ const char *secname = section_name (ebl, ehdr, shdr);
+
+ if (!silent)
+ printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n [Offset]\n"),
+ elf_ndxscn (scn), secname, (uint64_t) shdr->sh_offset);
+
+ /* If the section is empty we don't have to do anything. */
+ if (!silent && shdr->sh_size == 0)
+ return;
+
+ int maxdies = 20;
+ Dwarf_Die *dies = (Dwarf_Die *) xmalloc (maxdies * sizeof (Dwarf_Die));
+
+ Dwarf_Off offset = 0;
+
+ /* New compilation unit. */
+ size_t cuhl;
+ Dwarf_Half version;
+ Dwarf_Off abbroffset;
+ uint8_t addrsize;
+ uint8_t offsize;
+ Dwarf_Off nextcu;
+ uint64_t typesig;
+ Dwarf_Off typeoff;
+ next_cu:
+ if (dwarf_next_unit (dbg, offset, &nextcu, &cuhl, &version,
+ &abbroffset, &addrsize, &offsize,
+ debug_types ? &typesig : NULL,
+ debug_types ? &typeoff : NULL) != 0)
+ goto do_return;
+
+ if (!silent)
+ {
+ if (debug_types)
+ printf (gettext (" Type unit at offset %" PRIu64 ":\n"
+ " Version: %" PRIu16 ", Abbreviation section offset: %"
+ PRIu64 ", Address size: %" PRIu8
+ ", Offset size: %" PRIu8
+ "\n Type signature: %#" PRIx64
+ ", Type offset: %#" PRIx64 "\n"),
+ (uint64_t) offset, version, abbroffset, addrsize, offsize,
+ typesig, (uint64_t) typeoff);
+ else
+ printf (gettext (" Compilation unit at offset %" PRIu64 ":\n"
+ " Version: %" PRIu16 ", Abbreviation section offset: %"
+ PRIu64 ", Address size: %" PRIu8
+ ", Offset size: %" PRIu8 "\n"),
+ (uint64_t) offset, version, abbroffset, addrsize, offsize);
+ }
+
+ struct attrcb_args args =
+ {
+ .dwflmod = dwflmod,
+ .dbg = dbg,
+ .silent = silent,
+ .version = version,
+ .addrsize = addrsize,
+ .offset_size = offsize
+ };
+
+ offset += cuhl;
+
+ int level = 0;
+
+ if (unlikely ((debug_types ? dwarf_offdie_types : dwarf_offdie)
+ (dbg, offset, &dies[level]) == NULL))
+ {
+ if (!silent)
+ error (0, 0, gettext ("cannot get DIE at offset %" PRIu64
+ " in section '%s': %s"),
+ (uint64_t) offset, secname, dwarf_errmsg (-1));
+ goto do_return;
+ }
+
+ args.cu = dies[0].cu;
+
+ do
+ {
+ offset = dwarf_dieoffset (&dies[level]);
+ if (unlikely (offset == ~0ul))
+ {
+ if (!silent)
+ error (0, 0, gettext ("cannot get DIE offset: %s"),
+ dwarf_errmsg (-1));
+ goto do_return;
+ }
+
+ int tag = dwarf_tag (&dies[level]);
+ if (unlikely (tag == DW_TAG_invalid))
+ {
+ if (!silent)
+ error (0, 0, gettext ("cannot get tag of DIE at offset [%" PRIx64
+ "] in section '%s': %s"),
+ (uint64_t) offset, secname, dwarf_errmsg (-1));
+ goto do_return;
+ }
+
+ if (!silent)
+ {
+ unsigned int code = dwarf_getabbrevcode (dies[level].abbrev);
+ printf (" [%6" PRIx64 "] %*s%-20s abbrev: %u\n",
+ (uint64_t) offset, (int) (level * 2), "",
+ dwarf_tag_name (tag), code);
+ }
+
+ /* Print the attribute values. */
+ args.level = level;
+ args.die = &dies[level];
+ (void) dwarf_getattrs (&dies[level], attr_callback, &args, 0);
+
+ /* Make room for the next level's DIE. */
+ if (level + 1 == maxdies)
+ dies = (Dwarf_Die *) xrealloc (dies,
+ (maxdies += 10)
+ * sizeof (Dwarf_Die));
+
+ int res = dwarf_child (&dies[level], &dies[level + 1]);
+ if (res > 0)
+ {
+ while ((res = dwarf_siblingof (&dies[level], &dies[level])) == 1)
+ if (level-- == 0)
+ break;
+
+ if (unlikely (res == -1))
+ {
+ if (!silent)
+ error (0, 0, gettext ("cannot get next DIE: %s\n"),
+ dwarf_errmsg (-1));
+ goto do_return;
+ }
+ }
+ else if (unlikely (res < 0))
+ {
+ if (!silent)
+ error (0, 0, gettext ("cannot get next DIE: %s"),
+ dwarf_errmsg (-1));
+ goto do_return;
+ }
+ else
+ ++level;
+ }
+ while (level >= 0);
+
+ offset = nextcu;
+ if (offset != 0)
+ goto next_cu;
+
+ do_return:
+ free (dies);
+}
+
+static void
+print_debug_info_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ print_debug_units (dwflmod, ebl, ehdr, scn, shdr, dbg, false);
+}
+
+static void
+print_debug_types_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ print_debug_units (dwflmod, ebl, ehdr, scn, shdr, dbg, true);
+}
+
+
+static void
+print_decoded_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n\n"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset);
+
+ size_t address_size
+ = elf_getident (ebl->elf, NULL)[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+
+ Dwarf_Off cuoffset;
+ Dwarf_Off ncuoffset = 0;
+ size_t hsize;
+ while (dwarf_nextcu (dbg, cuoffset = ncuoffset, &ncuoffset, &hsize,
+ NULL, NULL, NULL) == 0)
+ {
+ Dwarf_Die cudie;
+ if (dwarf_offdie (dbg, cuoffset + hsize, &cudie) == NULL)
+ continue;
+
+ size_t nlines;
+ Dwarf_Lines *lines;
+ if (dwarf_getsrclines (&cudie, &lines, &nlines) != 0)
+ continue;
+
+ printf (" CU [%" PRIx64 "] %s\n",
+ dwarf_dieoffset (&cudie), dwarf_diename (&cudie));
+ printf (" line:col SBPE* disc isa op address"
+ " (Statement Block Prologue Epilogue *End)\n");
+ const char *last_file = "";
+ for (size_t n = 0; n < nlines; n++)
+ {
+ Dwarf_Line *line = dwarf_onesrcline (lines, n);
+ if (line == NULL)
+ {
+ printf (" dwarf_onesrcline: %s\n", dwarf_errmsg (-1));
+ continue;
+ }
+ Dwarf_Word mtime, length;
+ const char *file = dwarf_linesrc (line, &mtime, &length);
+ if (file == NULL)
+ {
+ printf (" <%s> (mtime: ?, length: ?)\n", dwarf_errmsg (-1));
+ last_file = "";
+ }
+ else if (strcmp (last_file, file) != 0)
+ {
+ printf (" %s (mtime: %" PRIu64 ", length: %" PRIu64 ")\n",
+ file, mtime, length);
+ last_file = file;
+ }
+
+ int lineno, colno;
+ bool statement, endseq, block, prologue_end, epilogue_begin;
+ unsigned int lineop, isa, disc;
+ Dwarf_Addr address;
+ dwarf_lineaddr (line, &address);
+ dwarf_lineno (line, &lineno);
+ dwarf_linecol (line, &colno);
+ dwarf_lineop_index (line, &lineop);
+ dwarf_linebeginstatement (line, &statement);
+ dwarf_lineendsequence (line, &endseq);
+ dwarf_lineblock (line, &block);
+ dwarf_lineprologueend (line, &prologue_end);
+ dwarf_lineepiloguebegin (line, &epilogue_begin);
+ dwarf_lineisa (line, &isa);
+ dwarf_linediscriminator (line, &disc);
+
+ /* End sequence is special, it is one byte past. */
+ char *a = format_dwarf_addr (dwflmod, address_size,
+ address - (endseq ? 1 : 0), address);
+ printf (" %4d:%-3d %c%c%c%c%c %4d %3d %2d %s\n",
+ lineno, colno,
+ (statement ? 'S' : ' '),
+ (block ? 'B' : ' '),
+ (prologue_end ? 'P' : ' '),
+ (epilogue_begin ? 'E' : ' '),
+ (endseq ? '*' : ' '),
+ disc, isa, lineop, a);
+ free (a);
+
+ if (endseq)
+ printf("\n");
+ }
+ }
+}
+
+
+static void
+print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ if (decodedline)
+ {
+ print_decoded_line_section (dwflmod, ebl, ehdr, scn, shdr, dbg);
+ return;
+ }
+
+ printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset);
+
+ if (shdr->sh_size == 0)
+ return;
+
+ /* There is no functionality in libdw to read the information in the
+ way it is represented here. Hardcode the decoder. */
+ Elf_Data *data = dbg->sectiondata[IDX_debug_line];
+ if (unlikely (data == NULL || data->d_buf == NULL))
+ {
+ error (0, 0, gettext ("cannot get line data section data: %s"),
+ elf_errmsg (-1));
+ return;
+ }
+
+ const unsigned char *linep = (const unsigned char *) data->d_buf;
+ const unsigned char *lineendp;
+
+ while (linep
+ < (lineendp = (const unsigned char *) data->d_buf + data->d_size))
+ {
+ size_t start_offset = linep - (const unsigned char *) data->d_buf;
+
+ printf (gettext ("\nTable at offset %zu:\n"), start_offset);
+
+ if (unlikely (linep + 4 > lineendp))
+ goto invalid_data;
+ Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep);
+ unsigned int length = 4;
+ if (unlikely (unit_length == 0xffffffff))
+ {
+ if (unlikely (linep + 8 > lineendp))
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data in section [%zu] '%s'"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr));
+ return;
+ }
+ unit_length = read_8ubyte_unaligned_inc (dbg, linep);
+ length = 8;
+ }
+
+ /* Check whether we have enough room in the section. */
+ if (unlikely (unit_length > (size_t) (lineendp - linep)
+ || unit_length < 2 + length + 5 * 1))
+ goto invalid_data;
+ lineendp = linep + unit_length;
+
+ /* The next element of the header is the version identifier. */
+ uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep);
+
+ /* Next comes the header length. */
+ Dwarf_Word header_length;
+ if (length == 4)
+ header_length = read_4ubyte_unaligned_inc (dbg, linep);
+ else
+ header_length = read_8ubyte_unaligned_inc (dbg, linep);
+ //const unsigned char *header_start = linep;
+
+ /* Next the minimum instruction length. */
+ uint_fast8_t minimum_instr_len = *linep++;
+
+ /* Next the maximum operations per instruction, in version 4 format. */
+ uint_fast8_t max_ops_per_instr = version < 4 ? 1 : *linep++;
+
+ /* Then the flag determining the default value of the is_stmt
+ register. */
+ uint_fast8_t default_is_stmt = *linep++;
+
+ /* Now the line base. */
+ int_fast8_t line_base = *((const int_fast8_t *) linep);
+ ++linep;
+
+ /* And the line range. */
+ uint_fast8_t line_range = *linep++;
+
+ /* The opcode base. */
+ uint_fast8_t opcode_base = *linep++;
+
+ /* Print what we got so far. */
+ printf (gettext ("\n"
+ " Length: %" PRIu64 "\n"
+ " DWARF version: %" PRIuFAST16 "\n"
+ " Prologue length: %" PRIu64 "\n"
+ " Minimum instruction length: %" PRIuFAST8 "\n"
+ " Maximum operations per instruction: %" PRIuFAST8 "\n"
+ " Initial value if '%s': %" PRIuFAST8 "\n"
+ " Line base: %" PRIdFAST8 "\n"
+ " Line range: %" PRIuFAST8 "\n"
+ " Opcode base: %" PRIuFAST8 "\n"
+ "\n"
+ "Opcodes:\n"),
+ (uint64_t) unit_length, version, (uint64_t) header_length,
+ minimum_instr_len, max_ops_per_instr,
+ "is_stmt", default_is_stmt, line_base,
+ line_range, opcode_base);
+
+ if (unlikely (linep + opcode_base - 1 >= lineendp))
+ {
+ invalid_unit:
+ error (0, 0,
+ gettext ("invalid data at offset %tu in section [%zu] '%s'"),
+ linep - (const unsigned char *) data->d_buf,
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr));
+ linep = lineendp;
+ continue;
+ }
+ int opcode_base_l10 = 1;
+ unsigned int tmp = opcode_base;
+ while (tmp > 10)
+ {
+ tmp /= 10;
+ ++opcode_base_l10;
+ }
+ const uint8_t *standard_opcode_lengths = linep - 1;
+ for (uint_fast8_t cnt = 1; cnt < opcode_base; ++cnt)
+ printf (ngettext (" [%*" PRIuFAST8 "] %hhu argument\n",
+ " [%*" PRIuFAST8 "] %hhu arguments\n",
+ (int) linep[cnt - 1]),
+ opcode_base_l10, cnt, linep[cnt - 1]);
+ linep += opcode_base - 1;
+ if (unlikely (linep >= lineendp))
+ goto invalid_unit;
+
+ puts (gettext ("\nDirectory table:"));
+ while (*linep != 0)
+ {
+ unsigned char *endp = memchr (linep, '\0', lineendp - linep);
+ if (unlikely (endp == NULL))
+ goto invalid_unit;
+
+ printf (" %s\n", (char *) linep);
+
+ linep = endp + 1;
+ }
+ /* Skip the final NUL byte. */
+ ++linep;
+
+ if (unlikely (linep >= lineendp))
+ goto invalid_unit;
+ puts (gettext ("\nFile name table:\n"
+ " Entry Dir Time Size Name"));
+ for (unsigned int cnt = 1; *linep != 0; ++cnt)
+ {
+ /* First comes the file name. */
+ char *fname = (char *) linep;
+ unsigned char *endp = memchr (fname, '\0', lineendp - linep);
+ if (unlikely (endp == NULL))
+ goto invalid_unit;
+ linep = endp + 1;
+
+ /* Then the index. */
+ unsigned int diridx;
+ if (lineendp - linep < 1)
+ goto invalid_unit;
+ get_uleb128 (diridx, linep, lineendp);
+
+ /* Next comes the modification time. */
+ unsigned int mtime;
+ if (lineendp - linep < 1)
+ goto invalid_unit;
+ get_uleb128 (mtime, linep, lineendp);
+
+ /* Finally the length of the file. */
+ unsigned int fsize;
+ if (lineendp - linep < 1)
+ goto invalid_unit;
+ get_uleb128 (fsize, linep, lineendp);
+
+ printf (" %-5u %-5u %-9u %-9u %s\n",
+ cnt, diridx, mtime, fsize, fname);
+ }
+ /* Skip the final NUL byte. */
+ ++linep;
+
+ puts (gettext ("\nLine number statements:"));
+ Dwarf_Word address = 0;
+ unsigned int op_index = 0;
+ size_t line = 1;
+ uint_fast8_t is_stmt = default_is_stmt;
+
+ /* Default address value, in case we do not find the CU. */
+ size_t address_size
+ = elf_getident (ebl->elf, NULL)[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+
+ /* Determine the CU this block is for. */
+ Dwarf_Off cuoffset;
+ Dwarf_Off ncuoffset = 0;
+ size_t hsize;
+ while (dwarf_nextcu (dbg, cuoffset = ncuoffset, &ncuoffset, &hsize,
+ NULL, NULL, NULL) == 0)
+ {
+ Dwarf_Die cudie;
+ if (dwarf_offdie (dbg, cuoffset + hsize, &cudie) == NULL)
+ continue;
+ Dwarf_Attribute stmt_list;
+ if (dwarf_attr (&cudie, DW_AT_stmt_list, &stmt_list) == NULL)
+ continue;
+ Dwarf_Word lineoff;
+ if (dwarf_formudata (&stmt_list, &lineoff) != 0)
+ continue;
+ if (lineoff == start_offset)
+ {
+ /* Found the CU. */
+ address_size = cudie.cu->address_size;
+ break;
+ }
+ }
+
+ /* Apply the "operation advance" from a special opcode
+ or DW_LNS_advance_pc (as per DWARF4 6.2.5.1). */
+ unsigned int op_addr_advance;
+ bool show_op_index;
+ inline void advance_pc (unsigned int op_advance)
+ {
+ op_addr_advance = minimum_instr_len * ((op_index + op_advance)
+ / max_ops_per_instr);
+ address += op_advance;
+ show_op_index = (op_index > 0 ||
+ (op_index + op_advance) % max_ops_per_instr > 0);
+ op_index = (op_index + op_advance) % max_ops_per_instr;
+ }
+
+ if (max_ops_per_instr == 0)
+ {
+ error (0, 0,
+ gettext ("invalid maximum operations per instruction is zero"));
+ linep = lineendp;
+ continue;
+ }
+
+ while (linep < lineendp)
+ {
+ size_t offset = linep - (const unsigned char *) data->d_buf;
+ unsigned int u128;
+ int s128;
+
+ /* Read the opcode. */
+ unsigned int opcode = *linep++;
+
+ printf (" [%6" PRIx64 "]", (uint64_t)offset);
+ /* Is this a special opcode? */
+ if (likely (opcode >= opcode_base))
+ {
+ if (unlikely (line_range == 0))
+ goto invalid_unit;
+
+ /* Yes. Handling this is quite easy since the opcode value
+ is computed with
+
+ opcode = (desired line increment - line_base)
+ + (line_range * address advance) + opcode_base
+ */
+ int line_increment = (line_base
+ + (opcode - opcode_base) % line_range);
+
+ /* Perform the increments. */
+ line += line_increment;
+ advance_pc ((opcode - opcode_base) / line_range);
+
+ char *a = format_dwarf_addr (dwflmod, 0, address, address);
+ if (show_op_index)
+ printf (gettext ("\
+ special opcode %u: address+%u = %s, op_index = %u, line%+d = %zu\n"),
+ opcode, op_addr_advance, a, op_index,
+ line_increment, line);
+ else
+ printf (gettext ("\
+ special opcode %u: address+%u = %s, line%+d = %zu\n"),
+ opcode, op_addr_advance, a, line_increment, line);
+ free (a);
+ }
+ else if (opcode == 0)
+ {
+ /* This an extended opcode. */
+ if (unlikely (linep + 2 > lineendp))
+ goto invalid_unit;
+
+ /* The length. */
+ unsigned int len = *linep++;
+
+ if (unlikely (linep + len > lineendp))
+ goto invalid_unit;
+
+ /* The sub-opcode. */
+ opcode = *linep++;
+
+ printf (gettext (" extended opcode %u: "), opcode);
+
+ switch (opcode)
+ {
+ case DW_LNE_end_sequence:
+ puts (gettext (" end of sequence"));
+
+ /* Reset the registers we care about. */
+ address = 0;
+ op_index = 0;
+ line = 1;
+ is_stmt = default_is_stmt;
+ break;
+
+ case DW_LNE_set_address:
+ op_index = 0;
+ if (unlikely ((size_t) (lineendp - linep) < address_size))
+ goto invalid_unit;
+ if (address_size == 4)
+ address = read_4ubyte_unaligned_inc (dbg, linep);
+ else
+ address = read_8ubyte_unaligned_inc (dbg, linep);
+ {
+ char *a = format_dwarf_addr (dwflmod, 0, address, address);
+ printf (gettext (" set address to %s\n"), a);
+ free (a);
+ }
+ break;
+
+ case DW_LNE_define_file:
+ {
+ char *fname = (char *) linep;
+ unsigned char *endp = memchr (linep, '\0',
+ lineendp - linep);
+ if (unlikely (endp == NULL))
+ goto invalid_unit;
+ linep = endp + 1;
+
+ unsigned int diridx;
+ if (lineendp - linep < 1)
+ goto invalid_unit;
+ get_uleb128 (diridx, linep, lineendp);
+ Dwarf_Word mtime;
+ if (lineendp - linep < 1)
+ goto invalid_unit;
+ get_uleb128 (mtime, linep, lineendp);
+ Dwarf_Word filelength;
+ if (lineendp - linep < 1)
+ goto invalid_unit;
+ get_uleb128 (filelength, linep, lineendp);
+
+ printf (gettext ("\
+ define new file: dir=%u, mtime=%" PRIu64 ", length=%" PRIu64 ", name=%s\n"),
+ diridx, (uint64_t) mtime, (uint64_t) filelength,
+ fname);
+ }
+ break;
+
+ case DW_LNE_set_discriminator:
+ /* Takes one ULEB128 parameter, the discriminator. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_unit;
+
+ get_uleb128 (u128, linep, lineendp);
+ printf (gettext (" set discriminator to %u\n"), u128);
+ break;
+
+ default:
+ /* Unknown, ignore it. */
+ puts (gettext (" unknown opcode"));
+ linep += len - 1;
+ break;
+ }
+ }
+ else if (opcode <= DW_LNS_set_isa)
+ {
+ /* This is a known standard opcode. */
+ switch (opcode)
+ {
+ case DW_LNS_copy:
+ /* Takes no argument. */
+ puts (gettext (" copy"));
+ break;
+
+ case DW_LNS_advance_pc:
+ /* Takes one uleb128 parameter which is added to the
+ address. */
+ get_uleb128 (u128, linep, lineendp);
+ advance_pc (u128);
+ {
+ char *a = format_dwarf_addr (dwflmod, 0, address, address);
+ if (show_op_index)
+ printf (gettext ("\
+ advance address by %u to %s, op_index to %u\n"),
+ op_addr_advance, a, op_index);
+ else
+ printf (gettext (" advance address by %u to %s\n"),
+ op_addr_advance, a);
+ free (a);
+ }
+ break;
+
+ case DW_LNS_advance_line:
+ /* Takes one sleb128 parameter which is added to the
+ line. */
+ get_sleb128 (s128, linep, lineendp);
+ line += s128;
+ printf (gettext ("\
+ advance line by constant %d to %" PRId64 "\n"),
+ s128, (int64_t) line);
+ break;
+
+ case DW_LNS_set_file:
+ /* Takes one uleb128 parameter which is stored in file. */
+ get_uleb128 (u128, linep, lineendp);
+ printf (gettext (" set file to %" PRIu64 "\n"),
+ (uint64_t) u128);
+ break;
+
+ case DW_LNS_set_column:
+ /* Takes one uleb128 parameter which is stored in column. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_unit;
+
+ get_uleb128 (u128, linep, lineendp);
+ printf (gettext (" set column to %" PRIu64 "\n"),
+ (uint64_t) u128);
+ break;
+
+ case DW_LNS_negate_stmt:
+ /* Takes no argument. */
+ is_stmt = 1 - is_stmt;
+ printf (gettext (" set '%s' to %" PRIuFAST8 "\n"),
+ "is_stmt", is_stmt);
+ break;
+
+ case DW_LNS_set_basic_block:
+ /* Takes no argument. */
+ puts (gettext (" set basic block flag"));
+ break;
+
+ case DW_LNS_const_add_pc:
+ /* Takes no argument. */
+
+ if (unlikely (line_range == 0))
+ goto invalid_unit;
+
+ advance_pc ((255 - opcode_base) / line_range);
+ {
+ char *a = format_dwarf_addr (dwflmod, 0, address, address);
+ if (show_op_index)
+ printf (gettext ("\
+ advance address by constant %u to %s, op_index to %u\n"),
+ op_addr_advance, a, op_index);
+ else
+ printf (gettext ("\
+ advance address by constant %u to %s\n"),
+ op_addr_advance, a);
+ free (a);
+ }
+ break;
+
+ case DW_LNS_fixed_advance_pc:
+ /* Takes one 16 bit parameter which is added to the
+ address. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_unit;
+
+ u128 = read_2ubyte_unaligned_inc (dbg, linep);
+ address += u128;
+ op_index = 0;
+ {
+ char *a = format_dwarf_addr (dwflmod, 0, address, address);
+ printf (gettext ("\
+ advance address by fixed value %u to %s\n"),
+ u128, a);
+ free (a);
+ }
+ break;
+
+ case DW_LNS_set_prologue_end:
+ /* Takes no argument. */
+ puts (gettext (" set prologue end flag"));
+ break;
+
+ case DW_LNS_set_epilogue_begin:
+ /* Takes no argument. */
+ puts (gettext (" set epilogue begin flag"));
+ break;
+
+ case DW_LNS_set_isa:
+ /* Takes one uleb128 parameter which is stored in isa. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_unit;
+
+ get_uleb128 (u128, linep, lineendp);
+ printf (gettext (" set isa to %u\n"), u128);
+ break;
+ }
+ }
+ else
+ {
+ /* This is a new opcode the generator but not we know about.
+ Read the parameters associated with it but then discard
+ everything. Read all the parameters for this opcode. */
+ printf (ngettext (" unknown opcode with %" PRIu8 " parameter:",
+ " unknown opcode with %" PRIu8 " parameters:",
+ standard_opcode_lengths[opcode]),
+ standard_opcode_lengths[opcode]);
+ for (int n = standard_opcode_lengths[opcode]; n > 0; --n)
+ {
+ get_uleb128 (u128, linep, lineendp);
+ if (n != standard_opcode_lengths[opcode])
+ putc_unlocked (',', stdout);
+ printf (" %u", u128);
+ }
+
+ /* Next round, ignore this opcode. */
+ continue;
+ }
+ }
+ }
+
+ /* There must only be one data block. */
+ assert (elf_getdata (scn, data) == NULL);
+}
+
+
+static void
+print_debug_loc_section (Dwfl_Module *dwflmod,
+ Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ Elf_Data *data = dbg->sectiondata[IDX_debug_loc];
+
+ if (unlikely (data == NULL))
+ {
+ error (0, 0, gettext ("cannot get .debug_loc content: %s"),
+ elf_errmsg (-1));
+ return;
+ }
+
+ printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset);
+
+ sort_listptr (&known_loclistptr, "loclistptr");
+ size_t listptr_idx = 0;
+
+ uint_fast8_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+ uint_fast8_t offset_size = 4;
+
+ bool first = true;
+ Dwarf_Addr base = 0;
+ unsigned char *readp = data->d_buf;
+ unsigned char *const endp = (unsigned char *) data->d_buf + data->d_size;
+ Dwarf_CU *last_cu = NULL;
+ while (readp < endp)
+ {
+ ptrdiff_t offset = readp - (unsigned char *) data->d_buf;
+ Dwarf_CU *cu = last_cu;
+
+ if (first && skip_listptr_hole (&known_loclistptr, &listptr_idx,
+ &address_size, &offset_size, &base,
+ &cu, offset, &readp, endp))
+ continue;
+
+ if (last_cu != cu)
+ {
+ char *basestr = format_dwarf_addr (dwflmod, address_size,
+ base, base);
+ Dwarf_Die cudie;
+ if (dwarf_cu_die (cu, &cudie,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL) == NULL)
+ printf (gettext ("\n Unknown CU base: %s\n"), basestr);
+ else
+ printf (gettext ("\n CU [%6" PRIx64 "] base: %s\n"),
+ dwarf_dieoffset (&cudie), basestr);
+ free (basestr);
+ }
+ last_cu = cu;
+
+ if (unlikely (data->d_size - offset < (size_t) address_size * 2))
+ {
+ printf (gettext (" [%6tx] <INVALID DATA>\n"), offset);
+ break;
+ }
+
+ Dwarf_Addr begin;
+ Dwarf_Addr end;
+ if (address_size == 8)
+ {
+ begin = read_8ubyte_unaligned_inc (dbg, readp);
+ end = read_8ubyte_unaligned_inc (dbg, readp);
+ }
+ else
+ {
+ begin = read_4ubyte_unaligned_inc (dbg, readp);
+ end = read_4ubyte_unaligned_inc (dbg, readp);
+ if (begin == (Dwarf_Addr) (uint32_t) -1)
+ begin = (Dwarf_Addr) -1l;
+ }
+
+ if (begin == (Dwarf_Addr) -1l) /* Base address entry. */
+ {
+ char *b = format_dwarf_addr (dwflmod, address_size, end, end);
+ printf (gettext (" [%6tx] base address\n %s\n"), offset, b);
+ free (b);
+ base = end;
+ }
+ else if (begin == 0 && end == 0) /* End of list entry. */
+ {
+ if (first)
+ printf (gettext (" [%6tx] empty list\n"), offset);
+ first = true;
+ }
+ else
+ {
+ /* We have a location expression entry. */
+ uint_fast16_t len = read_2ubyte_unaligned_inc (dbg, readp);
+
+ if (first) /* First entry in a list. */
+ printf (" [%6tx] ", offset);
+ else
+ printf (" ");
+
+ printf ("range %" PRIx64 ", %" PRIx64 "\n", begin, end);
+ if (! print_unresolved_addresses)
+ {
+ char *b = format_dwarf_addr (dwflmod, address_size, base + begin,
+ base + begin);
+ char *e = format_dwarf_addr (dwflmod, address_size,
+ base + end - 1, base + end);
+ printf (" %s..\n", b);
+ printf (" %s\n", e);
+ free (b);
+ free (e);
+ }
+
+ if (endp - readp <= (ptrdiff_t) len)
+ {
+ fputs (gettext (" <INVALID DATA>\n"), stdout);
+ break;
+ }
+
+ print_ops (dwflmod, dbg, 11, 11,
+ cu != NULL ? cu->version : 3,
+ address_size, offset_size, cu, len, readp);
+
+ first = false;
+ readp += len;
+ }
+ }
+}
+
+struct mac_culist
+{
+ Dwarf_Die die;
+ Dwarf_Off offset;
+ Dwarf_Files *files;
+ struct mac_culist *next;
+};
+
+
+static int
+mac_compare (const void *p1, const void *p2)
+{
+ struct mac_culist *m1 = (struct mac_culist *) p1;
+ struct mac_culist *m2 = (struct mac_culist *) p2;
+
+ if (m1->offset < m2->offset)
+ return -1;
+ if (m1->offset > m2->offset)
+ return 1;
+ return 0;
+}
+
+
+static void
+print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
+ Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset);
+ putc_unlocked ('\n', stdout);
+
+ /* There is no function in libdw to iterate over the raw content of
+ the section but it is easy enough to do. */
+ Elf_Data *data = dbg->sectiondata[IDX_debug_macinfo];
+ if (unlikely (data == NULL || data->d_buf == NULL))
+ {
+ error (0, 0, gettext ("cannot get macro information section data: %s"),
+ elf_errmsg (-1));
+ return;
+ }
+
+ /* Get the source file information for all CUs. */
+ Dwarf_Off offset;
+ Dwarf_Off ncu = 0;
+ size_t hsize;
+ struct mac_culist *culist = NULL;
+ size_t nculist = 0;
+ while (dwarf_nextcu (dbg, offset = ncu, &ncu, &hsize, NULL, NULL, NULL) == 0)
+ {
+ Dwarf_Die cudie;
+ if (dwarf_offdie (dbg, offset + hsize, &cudie) == NULL)
+ continue;
+
+ Dwarf_Attribute attr;
+ if (dwarf_attr (&cudie, DW_AT_macro_info, &attr) == NULL)
+ continue;
+
+ Dwarf_Word macoff;
+ if (dwarf_formudata (&attr, &macoff) != 0)
+ continue;
+
+ struct mac_culist *newp = (struct mac_culist *) alloca (sizeof (*newp));
+ newp->die = cudie;
+ newp->offset = macoff;
+ newp->files = NULL;
+ newp->next = culist;
+ culist = newp;
+ ++nculist;
+ }
+
+ /* Convert the list into an array for easier consumption. */
+ struct mac_culist *cus = (struct mac_culist *) alloca ((nculist + 1)
+ * sizeof (*cus));
+ /* Add sentinel. */
+ cus[nculist].offset = data->d_size;
+ cus[nculist].files = (Dwarf_Files *) -1l;
+ if (nculist > 0)
+ {
+ for (size_t cnt = nculist - 1; culist != NULL; --cnt)
+ {
+ assert (cnt < nculist);
+ cus[cnt] = *culist;
+ culist = culist->next;
+ }
+
+ /* Sort the array according to the offset in the .debug_macinfo
+ section. Note we keep the sentinel at the end. */
+ qsort (cus, nculist, sizeof (*cus), mac_compare);
+ }
+
+ const unsigned char *readp = (const unsigned char *) data->d_buf;
+ const unsigned char *readendp = readp + data->d_size;
+ int level = 1;
+
+ while (readp < readendp)
+ {
+ unsigned int opcode = *readp++;
+ unsigned int u128;
+ unsigned int u128_2;
+ const unsigned char *endp;
+
+ switch (opcode)
+ {
+ case DW_MACINFO_define:
+ case DW_MACINFO_undef:
+ case DW_MACINFO_vendor_ext:
+ /* For the first two opcodes the parameters are
+ line, string
+ For the latter
+ number, string.
+ We can treat these cases together. */
+ get_uleb128 (u128, readp, readendp);
+
+ endp = memchr (readp, '\0', readendp - readp);
+ if (unlikely (endp == NULL))
+ {
+ printf (gettext ("\
+%*s*** non-terminated string at end of section"),
+ level, "");
+ return;
+ }
+
+ if (opcode == DW_MACINFO_define)
+ printf ("%*s#define %s, line %u\n",
+ level, "", (char *) readp, u128);
+ else if (opcode == DW_MACINFO_undef)
+ printf ("%*s#undef %s, line %u\n",
+ level, "", (char *) readp, u128);
+ else
+ printf (" #vendor-ext %s, number %u\n", (char *) readp, u128);
+
+ readp = endp + 1;
+ break;
+
+ case DW_MACINFO_start_file:
+ /* The two parameters are line and file index, in this order. */
+ get_uleb128 (u128, readp, readendp);
+ if (readendp - readp < 1)
+ {
+ printf (gettext ("\
+%*s*** missing DW_MACINFO_start_file argument at end of section"),
+ level, "");
+ return;
+ }
+ get_uleb128 (u128_2, readp, readendp);
+
+ /* Find the CU DIE for this file. */
+ size_t macoff = readp - (const unsigned char *) data->d_buf;
+ const char *fname = "???";
+ if (macoff >= cus[0].offset)
+ {
+ while (macoff >= cus[1].offset && cus[1].offset != data->d_size)
+ ++cus;
+
+ if (cus[0].files == NULL
+ && dwarf_getsrcfiles (&cus[0].die, &cus[0].files, NULL) != 0)
+ cus[0].files = (Dwarf_Files *) -1l;
+
+ if (cus[0].files != (Dwarf_Files *) -1l)
+ fname = (dwarf_filesrc (cus[0].files, u128_2, NULL, NULL)
+ ?: "???");
+ }
+
+ printf ("%*sstart_file %u, [%u] %s\n",
+ level, "", u128, u128_2, fname);
+ ++level;
+ break;
+
+ case DW_MACINFO_end_file:
+ --level;
+ printf ("%*send_file\n", level, "");
+ /* Nothing more to do. */
+ break;
+
+ default:
+ // XXX gcc seems to generate files with a trailing zero.
+ if (unlikely (opcode != 0 || readp != readendp))
+ printf ("%*s*** invalid opcode %u\n", level, "", opcode);
+ break;
+ }
+ }
+}
+
+
+static void
+print_debug_macro_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
+ Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ printf (gettext ("\
+\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset);
+ putc_unlocked ('\n', stdout);
+
+ Elf_Data *data = dbg->sectiondata[IDX_debug_macro];
+ if (unlikely (data == NULL || data->d_buf == NULL))
+ {
+ error (0, 0, gettext ("cannot get macro information section data: %s"),
+ elf_errmsg (-1));
+ return;
+ }
+
+ /* Get the source file information for all CUs. Uses same
+ datastructure as macinfo. But uses offset field to directly
+ match .debug_line offset. And just stored in a list. */
+ Dwarf_Off offset;
+ Dwarf_Off ncu = 0;
+ size_t hsize;
+ struct mac_culist *culist = NULL;
+ size_t nculist = 0;
+ while (dwarf_nextcu (dbg, offset = ncu, &ncu, &hsize, NULL, NULL, NULL) == 0)
+ {
+ Dwarf_Die cudie;
+ if (dwarf_offdie (dbg, offset + hsize, &cudie) == NULL)
+ continue;
+
+ Dwarf_Attribute attr;
+ if (dwarf_attr (&cudie, DW_AT_stmt_list, &attr) == NULL)
+ continue;
+
+ Dwarf_Word lineoff;
+ if (dwarf_formudata (&attr, &lineoff) != 0)
+ continue;
+
+ struct mac_culist *newp = (struct mac_culist *) alloca (sizeof (*newp));
+ newp->die = cudie;
+ newp->offset = lineoff;
+ newp->files = NULL;
+ newp->next = culist;
+ culist = newp;
+ ++nculist;
+ }
+
+ const unsigned char *readp = (const unsigned char *) data->d_buf;
+ const unsigned char *readendp = readp + data->d_size;
+
+ while (readp < readendp)
+ {
+ printf (gettext (" Offset: 0x%" PRIx64 "\n"),
+ (uint64_t) (readp - (const unsigned char *) data->d_buf));
+
+ // Header, 2 byte version, 1 byte flag, optional .debug_line offset,
+ // optional vendor extension macro entry table.
+ if (readp + 2 > readendp)
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data"));
+ return;
+ }
+ const uint16_t vers = read_2ubyte_unaligned_inc (dbg, readp);
+ printf (gettext (" Version: %" PRIu16 "\n"), vers);
+
+ // Version 4 is the GNU extension for DWARF4. DWARF5 will use version
+ // 5 when it gets standardized.
+ if (vers != 4 && vers != 5)
+ {
+ printf (gettext (" unknown version, cannot parse section\n"));
+ return;
+ }
+
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ const unsigned char flag = *readp++;
+ printf (gettext (" Flag: 0x%" PRIx8 "\n"), flag);
+
+ unsigned int offset_len = (flag & 0x01) ? 8 : 4;
+ printf (gettext (" Offset length: %" PRIu8 "\n"), offset_len);
+ Dwarf_Off line_offset = -1;
+ if (flag & 0x02)
+ {
+ if (offset_len == 8)
+ line_offset = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ line_offset = read_4ubyte_unaligned_inc (dbg, readp);
+ printf (gettext (" .debug_line offset: 0x%" PRIx64 "\n"),
+ line_offset);
+ }
+
+ const unsigned char *vendor[DW_MACRO_hi_user - DW_MACRO_lo_user];
+ memset (vendor, 0, sizeof vendor);
+ if (flag & 0x04)
+ {
+ // 1 byte length, for each item, 1 byte opcode, uleb128 number
+ // of arguments, for each argument 1 byte form code.
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ unsigned int tlen = *readp++;
+ printf (gettext (" extension opcode table, %" PRIu8 " items:\n"),
+ tlen);
+ for (unsigned int i = 0; i < tlen; i++)
+ {
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ unsigned int opcode = *readp++;
+ printf (gettext (" [%" PRIx8 "]"), opcode);
+ if (opcode < DW_MACRO_lo_user
+ || opcode > DW_MACRO_hi_user)
+ goto invalid_data;
+ // Record the start of description for this vendor opcode.
+ // uleb128 nr args, 1 byte per arg form.
+ vendor[opcode - DW_MACRO_lo_user] = readp;
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ unsigned int args = *readp++;
+ if (args > 0)
+ {
+ printf (gettext (" %" PRIu8 " arguments:"), args);
+ while (args > 0)
+ {
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ unsigned int form = *readp++;
+ printf (" %s", dwarf_form_name (form));
+ if (form != DW_FORM_data1
+ && form != DW_FORM_data2
+ && form != DW_FORM_data4
+ && form != DW_FORM_data8
+ && form != DW_FORM_sdata
+ && form != DW_FORM_udata
+ && form != DW_FORM_block
+ && form != DW_FORM_block1
+ && form != DW_FORM_block2
+ && form != DW_FORM_block4
+ && form != DW_FORM_flag
+ && form != DW_FORM_string
+ && form != DW_FORM_strp
+ && form != DW_FORM_sec_offset)
+ goto invalid_data;
+ args--;
+ if (args > 0)
+ putchar_unlocked (',');
+ }
+ }
+ else
+ printf (gettext (" no arguments."));
+ putchar_unlocked ('\n');
+ }
+ }
+ putchar_unlocked ('\n');
+
+ int level = 1;
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ unsigned int opcode = *readp++;
+ while (opcode != 0)
+ {
+ unsigned int u128;
+ unsigned int u128_2;
+ const unsigned char *endp;
+ uint64_t off;
+
+ switch (opcode)
+ {
+ case DW_MACRO_start_file:
+ get_uleb128 (u128, readp, readendp);
+ if (readp >= readendp)
+ goto invalid_data;
+ get_uleb128 (u128_2, readp, readendp);
+
+ /* Find the CU DIE that matches this line offset. */
+ const char *fname = "???";
+ if (line_offset != (Dwarf_Off) -1)
+ {
+ struct mac_culist *cu = culist;
+ while (cu != NULL && line_offset != cu->offset)
+ cu = cu->next;
+ if (cu != NULL)
+ {
+ if (cu->files == NULL
+ && dwarf_getsrcfiles (&cu->die, &cu->files,
+ NULL) != 0)
+ cu->files = (Dwarf_Files *) -1l;
+
+ if (cu->files != (Dwarf_Files *) -1l)
+ fname = (dwarf_filesrc (cu->files, u128_2,
+ NULL, NULL) ?: "???");
+ }
+ }
+ printf ("%*sstart_file %u, [%u] %s\n",
+ level, "", u128, u128_2, fname);
+ ++level;
+ break;
+
+ case DW_MACRO_end_file:
+ --level;
+ printf ("%*send_file\n", level, "");
+ break;
+
+ case DW_MACRO_define:
+ get_uleb128 (u128, readp, readendp);
+ endp = memchr (readp, '\0', readendp - readp);
+ if (endp == NULL)
+ goto invalid_data;
+ printf ("%*s#define %s, line %u\n",
+ level, "", readp, u128);
+ readp = endp + 1;
+ break;
+
+ case DW_MACRO_undef:
+ get_uleb128 (u128, readp, readendp);
+ endp = memchr (readp, '\0', readendp - readp);
+ if (endp == NULL)
+ goto invalid_data;
+ printf ("%*s#undef %s, line %u\n",
+ level, "", readp, u128);
+ readp = endp + 1;
+ break;
+
+ case DW_MACRO_define_strp:
+ get_uleb128 (u128, readp, readendp);
+ if (readp + offset_len > readendp)
+ goto invalid_data;
+ if (offset_len == 8)
+ off = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ off = read_4ubyte_unaligned_inc (dbg, readp);
+ printf ("%*s#define %s, line %u (indirect)\n",
+ level, "", dwarf_getstring (dbg, off, NULL), u128);
+ break;
+
+ case DW_MACRO_undef_strp:
+ get_uleb128 (u128, readp, readendp);
+ if (readp + offset_len > readendp)
+ goto invalid_data;
+ if (offset_len == 8)
+ off = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ off = read_4ubyte_unaligned_inc (dbg, readp);
+ printf ("%*s#undef %s, line %u (indirect)\n",
+ level, "", dwarf_getstring (dbg, off, NULL), u128);
+ break;
+
+ case DW_MACRO_import:
+ if (readp + offset_len > readendp)
+ goto invalid_data;
+ if (offset_len == 8)
+ off = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ off = read_4ubyte_unaligned_inc (dbg, readp);
+ printf ("%*s#include offset 0x%" PRIx64 "\n",
+ level, "", off);
+ break;
+
+ case DW_MACRO_define_sup:
+ get_uleb128 (u128, readp, readendp);
+ if (readp + offset_len > readendp)
+ goto invalid_data;
+ if (offset_len == 8)
+ off = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ off = read_4ubyte_unaligned_inc (dbg, readp);
+ // Needs support for reading from supplementary object file.
+ printf ("%*s#define <str-at-0x%" PRIx64 ">, line %u (sup)\n",
+ level, "", off, u128);
+ break;
+
+ case DW_MACRO_undef_sup:
+ get_uleb128 (u128, readp, readendp);
+ if (readp + offset_len > readendp)
+ goto invalid_data;
+ if (offset_len == 8)
+ off = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ off = read_4ubyte_unaligned_inc (dbg, readp);
+ // Needs support for reading from supplementary object file.
+ printf ("%*s#undef <str-at-0x%" PRIx64 ">, line %u (sup)\n",
+ level, "", off, u128);
+ break;
+
+ case DW_MACRO_import_sup:
+ if (readp + offset_len > readendp)
+ goto invalid_data;
+ if (offset_len == 8)
+ off = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ off = read_4ubyte_unaligned_inc (dbg, readp);
+ printf ("%*s#include offset 0x%" PRIx64 " (sup)\n",
+ level, "", off);
+ break;
+
+ case DW_MACRO_define_strx:
+ get_uleb128 (u128, readp, readendp);
+ if (readp + offset_len > readendp)
+ goto invalid_data;
+ if (offset_len == 8)
+ off = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ off = read_4ubyte_unaligned_inc (dbg, readp);
+ // Needs support for reading indirect string offset table
+ printf ("%*s#define <str-at-0x%" PRIx64 ">, line %u (strx)\n",
+ level, "", off, u128);
+ break;
+
+ case DW_MACRO_undef_strx:
+ get_uleb128 (u128, readp, readendp);
+ if (readp + offset_len > readendp)
+ goto invalid_data;
+ if (offset_len == 8)
+ off = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ off = read_4ubyte_unaligned_inc (dbg, readp);
+ // Needs support for reading indirect string offset table.
+ printf ("%*s#undef <str-at-0x%" PRIx64 ">, line %u (strx)\n",
+ level, "", off, u128);
+ break;
+
+ default:
+ printf ("%*svendor opcode 0x%" PRIx8, level, "", opcode);
+ if (opcode < DW_MACRO_lo_user
+ || opcode > DW_MACRO_lo_user
+ || vendor[opcode - DW_MACRO_lo_user] == NULL)
+ goto invalid_data;
+
+ const unsigned char *op_desc;
+ op_desc = vendor[opcode - DW_MACRO_lo_user];
+
+ // Just skip the arguments, we cannot really interpret them,
+ // but print as much as we can.
+ unsigned int args = *op_desc++;
+ while (args > 0)
+ {
+ unsigned int form = *op_desc++;
+ Dwarf_Word val;
+ switch (form)
+ {
+ case DW_FORM_data1:
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ val = *readp++;
+ printf (" %" PRIx8, (unsigned int) val);
+ break;
+
+ case DW_FORM_data2:
+ if (readp + 2 > readendp)
+ goto invalid_data;
+ val = read_2ubyte_unaligned_inc (dbg, readp);
+ printf(" %" PRIx16, (unsigned int) val);
+ break;
+
+ case DW_FORM_data4:
+ if (readp + 4 > readendp)
+ goto invalid_data;
+ val = read_4ubyte_unaligned_inc (dbg, readp);
+ printf (" %" PRIx32, (unsigned int) val);
+ break;
+
+ case DW_FORM_data8:
+ if (readp + 8 > readendp)
+ goto invalid_data;
+ val = read_8ubyte_unaligned_inc (dbg, readp);
+ printf (" %" PRIx64, val);
+ break;
+
+ case DW_FORM_sdata:
+ get_sleb128 (val, readp, readendp);
+ printf (" %" PRIx64, val);
+ break;
+
+ case DW_FORM_udata:
+ get_uleb128 (val, readp, readendp);
+ printf (" %" PRIx64, val);
+ break;
+
+ case DW_FORM_block:
+ get_uleb128 (val, readp, readendp);
+ printf (" block[%" PRIu64 "]", val);
+ if (readp + val > readendp)
+ goto invalid_data;
+ readp += val;
+ break;
+
+ case DW_FORM_block1:
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ val = *readp++;
+ printf (" block[%" PRIu64 "]", val);
+ if (readp + val > readendp)
+ goto invalid_data;
+ break;
+
+ case DW_FORM_block2:
+ if (readp + 2 > readendp)
+ goto invalid_data;
+ val = read_2ubyte_unaligned_inc (dbg, readp);
+ printf (" block[%" PRIu64 "]", val);
+ if (readp + val > readendp)
+ goto invalid_data;
+ break;
+
+ case DW_FORM_block4:
+ if (readp + 2 > readendp)
+ goto invalid_data;
+ val =read_4ubyte_unaligned_inc (dbg, readp);
+ printf (" block[%" PRIu64 "]", val);
+ if (readp + val > readendp)
+ goto invalid_data;
+ break;
+
+ case DW_FORM_flag:
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ val = *readp++;
+ printf (" %s", val != 0 ? gettext ("yes") : gettext ("no"));
+ break;
+
+ case DW_FORM_string:
+ endp = memchr (readp, '\0', readendp - readp);
+ if (endp == NULL)
+ goto invalid_data;
+ printf (" %s", readp);
+ readp = endp + 1;
+ break;
+
+ case DW_FORM_strp:
+ if (readp + offset_len > readendp)
+ goto invalid_data;
+ if (offset_len == 8)
+ val = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ val = read_4ubyte_unaligned_inc (dbg, readp);
+ printf (" %s", dwarf_getstring (dbg, val, NULL));
+ break;
+
+ case DW_FORM_sec_offset:
+ if (readp + offset_len > readendp)
+ goto invalid_data;
+ if (offset_len == 8)
+ val = read_8ubyte_unaligned_inc (dbg, readp);
+ else
+ val = read_4ubyte_unaligned_inc (dbg, readp);
+ printf (" %" PRIx64, val);
+ break;
+
+ default:
+ error (0, 0, gettext ("vendor opcode not verified?"));
+ return;
+ }
+
+ args--;
+ if (args > 0)
+ putchar_unlocked (',');
+ }
+ putchar_unlocked ('\n');
+ }
+
+ if (readp + 1 > readendp)
+ goto invalid_data;
+ opcode = *readp++;
+ if (opcode == 0)
+ putchar_unlocked ('\n');
+ }
+ }
+}
+
+
+/* Callback for printing global names. */
+static int
+print_pubnames (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global,
+ void *arg)
+{
+ int *np = (int *) arg;
+
+ printf (gettext (" [%5d] DIE offset: %6" PRId64
+ ", CU DIE offset: %6" PRId64 ", name: %s\n"),
+ (*np)++, global->die_offset, global->cu_offset, global->name);
+
+ return 0;
+}
+
+
+/* Print the known exported symbols in the DWARF section '.debug_pubnames'. */
+static void
+print_debug_pubnames_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
+ Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset);
+
+ int n = 0;
+ (void) dwarf_getpubnames (dbg, print_pubnames, &n, 0);
+}
+
+/* Print the content of the DWARF string section '.debug_str'. */
+static void
+print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
+ Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ const size_t sh_size = (dbg->sectiondata[IDX_debug_str] ?
+ dbg->sectiondata[IDX_debug_str]->d_size : 0);
+
+ /* Compute floor(log16(shdr->sh_size)). */
+ GElf_Addr tmp = sh_size;
+ int digits = 1;
+ while (tmp >= 16)
+ {
+ ++digits;
+ tmp >>= 4;
+ }
+ digits = MAX (4, digits);
+
+ printf (gettext ("\nDWARF section [%2zu] '%s' at offset %#" PRIx64 ":\n"
+ " %*s String\n"),
+ elf_ndxscn (scn),
+ section_name (ebl, ehdr, shdr), (uint64_t) shdr->sh_offset,
+ /* TRANS: the debugstr| prefix makes the string unique. */
+ digits + 2, sgettext ("debugstr|Offset"));
+
+ Dwarf_Off offset = 0;
+ while (offset < sh_size)
+ {
+ size_t len;
+ const char *str = dwarf_getstring (dbg, offset, &len);
+ if (unlikely (str == NULL))
+ {
+ printf (gettext (" *** error while reading strings: %s\n"),
+ dwarf_errmsg (-1));
+ break;
+ }
+
+ printf (" [%*" PRIx64 "] \"%s\"\n", digits, (uint64_t) offset, str);
+
+ offset += len + 1;
+ }
+}
+
+
+/* Print the content of the call frame search table section
+ '.eh_frame_hdr'. */
+static void
+print_debug_frame_hdr_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
+ Ebl *ebl __attribute__ ((unused)),
+ GElf_Ehdr *ehdr __attribute__ ((unused)),
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ printf (gettext ("\
+\nCall frame search table section [%2zu] '.eh_frame_hdr':\n"),
+ elf_ndxscn (scn));
+
+ Elf_Data *data = elf_rawdata (scn, NULL);
+
+ if (unlikely (data == NULL))
+ {
+ error (0, 0, gettext ("cannot get %s content: %s"),
+ ".eh_frame_hdr", elf_errmsg (-1));
+ return;
+ }
+
+ const unsigned char *readp = data->d_buf;
+ const unsigned char *const dataend = ((unsigned char *) data->d_buf
+ + data->d_size);
+
+ if (unlikely (readp + 4 > dataend))
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data"));
+ return;
+ }
+
+ unsigned int version = *readp++;
+ unsigned int eh_frame_ptr_enc = *readp++;
+ unsigned int fde_count_enc = *readp++;
+ unsigned int table_enc = *readp++;
+
+ printf (" version: %u\n"
+ " eh_frame_ptr_enc: %#x ",
+ version, eh_frame_ptr_enc);
+ print_encoding_base ("", eh_frame_ptr_enc);
+ printf (" fde_count_enc: %#x ", fde_count_enc);
+ print_encoding_base ("", fde_count_enc);
+ printf (" table_enc: %#x ", table_enc);
+ print_encoding_base ("", table_enc);
+
+ uint64_t eh_frame_ptr = 0;
+ if (eh_frame_ptr_enc != DW_EH_PE_omit)
+ {
+ readp = read_encoded (eh_frame_ptr_enc, readp, dataend, &eh_frame_ptr,
+ dbg);
+ if (unlikely (readp == NULL))
+ goto invalid_data;
+
+ printf (" eh_frame_ptr: %#" PRIx64, eh_frame_ptr);
+ if ((eh_frame_ptr_enc & 0x70) == DW_EH_PE_pcrel)
+ printf (" (offset: %#" PRIx64 ")",
+ /* +4 because of the 4 byte header of the section. */
+ (uint64_t) shdr->sh_offset + 4 + eh_frame_ptr);
+
+ putchar_unlocked ('\n');
+ }
+
+ uint64_t fde_count = 0;
+ if (fde_count_enc != DW_EH_PE_omit)
+ {
+ readp = read_encoded (fde_count_enc, readp, dataend, &fde_count, dbg);
+ if (unlikely (readp == NULL))
+ goto invalid_data;
+
+ printf (" fde_count: %" PRIu64 "\n", fde_count);
+ }
+
+ if (fde_count == 0 || table_enc == DW_EH_PE_omit)
+ return;
+
+ puts (" Table:");
+
+ /* Optimize for the most common case. */
+ if (table_enc == (DW_EH_PE_datarel | DW_EH_PE_sdata4))
+ while (fde_count > 0 && readp + 8 <= dataend)
+ {
+ int32_t initial_location = read_4sbyte_unaligned_inc (dbg, readp);
+ uint64_t initial_offset = ((uint64_t) shdr->sh_offset
+ + (int64_t) initial_location);
+ int32_t address = read_4sbyte_unaligned_inc (dbg, readp);
+ // XXX Possibly print symbol name or section offset for initial_offset
+ printf (" %#" PRIx32 " (offset: %#6" PRIx64 ") -> %#" PRIx32
+ " fde=[%6" PRIx64 "]\n",
+ initial_location, initial_offset,
+ address, address - (eh_frame_ptr + 4));
+ }
+ else
+ while (0 && readp < dataend)
+ {
+
+ }
+}
+
+
+/* Print the content of the exception handling table section
+ '.eh_frame_hdr'. */
+static void
+print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)),
+ Ebl *ebl __attribute__ ((unused)),
+ GElf_Ehdr *ehdr __attribute__ ((unused)),
+ Elf_Scn *scn,
+ GElf_Shdr *shdr __attribute__ ((unused)),
+ Dwarf *dbg __attribute__ ((unused)))
+{
+ printf (gettext ("\
+\nException handling table section [%2zu] '.gcc_except_table':\n"),
+ elf_ndxscn (scn));
+
+ Elf_Data *data = elf_rawdata (scn, NULL);
+
+ if (unlikely (data == NULL))
+ {
+ error (0, 0, gettext ("cannot get %s content: %s"),
+ ".gcc_except_table", elf_errmsg (-1));
+ return;
+ }
+
+ const unsigned char *readp = data->d_buf;
+ const unsigned char *const dataend = readp + data->d_size;
+
+ if (unlikely (readp + 1 > dataend))
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data"));
+ return;
+ }
+ unsigned int lpstart_encoding = *readp++;
+ printf (gettext (" LPStart encoding: %#x "), lpstart_encoding);
+ print_encoding_base ("", lpstart_encoding);
+ if (lpstart_encoding != DW_EH_PE_omit)
+ {
+ uint64_t lpstart;
+ readp = read_encoded (lpstart_encoding, readp, dataend, &lpstart, dbg);
+ printf (" LPStart: %#" PRIx64 "\n", lpstart);
+ }
+
+ if (unlikely (readp + 1 > dataend))
+ goto invalid_data;
+ unsigned int ttype_encoding = *readp++;
+ printf (gettext (" TType encoding: %#x "), ttype_encoding);
+ print_encoding_base ("", ttype_encoding);
+ const unsigned char *ttype_base = NULL;
+ if (ttype_encoding != DW_EH_PE_omit)
+ {
+ unsigned int ttype_base_offset;
+ get_uleb128 (ttype_base_offset, readp, dataend);
+ printf (" TType base offset: %#x\n", ttype_base_offset);
+ if ((size_t) (dataend - readp) > ttype_base_offset)
+ ttype_base = readp + ttype_base_offset;
+ }
+
+ if (unlikely (readp + 1 > dataend))
+ goto invalid_data;
+ unsigned int call_site_encoding = *readp++;
+ printf (gettext (" Call site encoding: %#x "), call_site_encoding);
+ print_encoding_base ("", call_site_encoding);
+ unsigned int call_site_table_len;
+ get_uleb128 (call_site_table_len, readp, dataend);
+
+ const unsigned char *const action_table = readp + call_site_table_len;
+ if (unlikely (action_table > dataend))
+ goto invalid_data;
+ unsigned int u = 0;
+ unsigned int max_action = 0;
+ while (readp < action_table)
+ {
+ if (u == 0)
+ puts (gettext ("\n Call site table:"));
+
+ uint64_t call_site_start;
+ readp = read_encoded (call_site_encoding, readp, dataend,
+ &call_site_start, dbg);
+ uint64_t call_site_length;
+ readp = read_encoded (call_site_encoding, readp, dataend,
+ &call_site_length, dbg);
+ uint64_t landing_pad;
+ readp = read_encoded (call_site_encoding, readp, dataend,
+ &landing_pad, dbg);
+ unsigned int action;
+ get_uleb128 (action, readp, dataend);
+ max_action = MAX (action, max_action);
+ printf (gettext (" [%4u] Call site start: %#" PRIx64 "\n"
+ " Call site length: %" PRIu64 "\n"
+ " Landing pad: %#" PRIx64 "\n"
+ " Action: %u\n"),
+ u++, call_site_start, call_site_length, landing_pad, action);
+ }
+ if (readp != action_table)
+ goto invalid_data;
+
+ unsigned int max_ar_filter = 0;
+ if (max_action > 0)
+ {
+ puts ("\n Action table:");
+
+ size_t maxdata = (size_t) (dataend - action_table);
+ if (max_action > maxdata || maxdata - max_action < 1)
+ {
+ invalid_action_table:
+ fputs (gettext (" <INVALID DATA>\n"), stdout);
+ return;
+ }
+
+ const unsigned char *const action_table_end
+ = action_table + max_action + 1;
+
+ u = 0;
+ do
+ {
+ int ar_filter;
+ get_sleb128 (ar_filter, readp, action_table_end);
+ if (ar_filter > 0 && (unsigned int) ar_filter > max_ar_filter)
+ max_ar_filter = ar_filter;
+ int ar_disp;
+ if (readp >= action_table_end)
+ goto invalid_action_table;
+ get_sleb128 (ar_disp, readp, action_table_end);
+
+ printf (" [%4u] ar_filter: % d\n"
+ " ar_disp: % -5d",
+ u, ar_filter, ar_disp);
+ if (abs (ar_disp) & 1)
+ printf (" -> [%4u]\n", u + (ar_disp + 1) / 2);
+ else if (ar_disp != 0)
+ puts (" -> ???");
+ else
+ putchar_unlocked ('\n');
+ ++u;
+ }
+ while (readp < action_table_end);
+ }
+
+ if (max_ar_filter > 0 && ttype_base != NULL)
+ {
+ unsigned char dsize;
+ puts ("\n TType table:");
+
+ // XXX Not *4, size of encoding;
+ switch (ttype_encoding & 7)
+ {
+ case DW_EH_PE_udata2:
+ case DW_EH_PE_sdata2:
+ dsize = 2;
+ break;
+ case DW_EH_PE_udata4:
+ case DW_EH_PE_sdata4:
+ dsize = 4;
+ break;
+ case DW_EH_PE_udata8:
+ case DW_EH_PE_sdata8:
+ dsize = 8;
+ break;
+ default:
+ dsize = 0;
+ error (1, 0, gettext ("invalid TType encoding"));
+ }
+
+ if (max_ar_filter
+ > (size_t) (ttype_base - (const unsigned char *) data->d_buf) / dsize)
+ goto invalid_data;
+
+ readp = ttype_base - max_ar_filter * dsize;
+ do
+ {
+ uint64_t ttype;
+ readp = read_encoded (ttype_encoding, readp, ttype_base, &ttype,
+ dbg);
+ printf (" [%4u] %#" PRIx64 "\n", max_ar_filter--, ttype);
+ }
+ while (readp < ttype_base);
+ }
+}
+
+/* Print the content of the '.gdb_index' section.
+ http://sourceware.org/gdb/current/onlinedocs/gdb/Index-Section-Format.html
+*/
+static void
+print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
+ Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
+{
+ printf (gettext ("\nGDB section [%2zu] '%s' at offset %#" PRIx64
+ " contains %" PRId64 " bytes :\n"),
+ elf_ndxscn (scn), section_name (ebl, ehdr, shdr),
+ (uint64_t) shdr->sh_offset, (uint64_t) shdr->sh_size);
+
+ Elf_Data *data = elf_rawdata (scn, NULL);
+
+ if (unlikely (data == NULL))
+ {
+ error (0, 0, gettext ("cannot get %s content: %s"),
+ ".gdb_index", elf_errmsg (-1));
+ return;
+ }
+
+ // .gdb_index is always in little endian.
+ Dwarf dummy_dbg = { .other_byte_order = MY_ELFDATA != ELFDATA2LSB };
+ dbg = &dummy_dbg;
+
+ const unsigned char *readp = data->d_buf;
+ const unsigned char *const dataend = readp + data->d_size;
+
+ if (unlikely (readp + 4 > dataend))
+ {
+ invalid_data:
+ error (0, 0, gettext ("invalid data"));
+ return;
+ }
+
+ int32_t vers = read_4ubyte_unaligned (dbg, readp);
+ printf (gettext (" Version: %" PRId32 "\n"), vers);
+
+ // The only difference between version 4 and version 5 is the
+ // hash used for generating the table. Version 6 contains symbols
+ // for inlined functions, older versions didn't. Version 7 adds
+ // symbol kinds. Version 8 just indicates that it correctly includes
+ // TUs for symbols.
+ if (vers < 4 || vers > 8)
+ {
+ printf (gettext (" unknown version, cannot parse section\n"));
+ return;
+ }
+
+ readp += 4;
+ if (unlikely (readp + 4 > dataend))
+ goto invalid_data;
+
+ uint32_t cu_off = read_4ubyte_unaligned (dbg, readp);
+ printf (gettext (" CU offset: %#" PRIx32 "\n"), cu_off);
+
+ readp += 4;
+ if (unlikely (readp + 4 > dataend))
+ goto invalid_data;
+
+ uint32_t tu_off = read_4ubyte_unaligned (dbg, readp);
+ printf (gettext (" TU offset: %#" PRIx32 "\n"), tu_off);
+
+ readp += 4;
+ if (unlikely (readp + 4 > dataend))
+ goto invalid_data;
+
+ uint32_t addr_off = read_4ubyte_unaligned (dbg, readp);
+ printf (gettext (" address offset: %#" PRIx32 "\n"), addr_off);
+
+ readp += 4;
+ if (unlikely (readp + 4 > dataend))
+ goto invalid_data;
+
+ uint32_t sym_off = read_4ubyte_unaligned (dbg, readp);
+ printf (gettext (" symbol offset: %#" PRIx32 "\n"), sym_off);
+
+ readp += 4;
+ if (unlikely (readp + 4 > dataend))
+ goto invalid_data;
+
+ uint32_t const_off = read_4ubyte_unaligned (dbg, readp);
+ printf (gettext (" constant offset: %#" PRIx32 "\n"), const_off);
+
+ if (unlikely ((size_t) (dataend - (const unsigned char *) data->d_buf)
+ < const_off))
+ goto invalid_data;
+
+ readp = data->d_buf + cu_off;
+
+ const unsigned char *nextp = data->d_buf + tu_off;
+ if (tu_off >= data->d_size)
+ goto invalid_data;
+
+ size_t cu_nr = (nextp - readp) / 16;
+
+ printf (gettext ("\n CU list at offset %#" PRIx32
+ " contains %zu entries:\n"),
+ cu_off, cu_nr);
+
+ size_t n = 0;
+ while (dataend - readp >= 16 && n < cu_nr)
+ {
+ uint64_t off = read_8ubyte_unaligned (dbg, readp);
+ readp += 8;
+
+ uint64_t len = read_8ubyte_unaligned (dbg, readp);
+ readp += 8;
+
+ printf (" [%4zu] start: %0#8" PRIx64
+ ", length: %5" PRIu64 "\n", n, off, len);
+ n++;
+ }
+
+ readp = data->d_buf + tu_off;
+ nextp = data->d_buf + addr_off;
+ if (addr_off >= data->d_size)
+ goto invalid_data;
+
+ size_t tu_nr = (nextp - readp) / 24;
+
+ printf (gettext ("\n TU list at offset %#" PRIx32
+ " contains %zu entries:\n"),
+ tu_off, tu_nr);
+
+ n = 0;
+ while (dataend - readp >= 24 && n < tu_nr)
+ {
+ uint64_t off = read_8ubyte_unaligned (dbg, readp);
+ readp += 8;
+
+ uint64_t type = read_8ubyte_unaligned (dbg, readp);
+ readp += 8;
+
+ uint64_t sig = read_8ubyte_unaligned (dbg, readp);
+ readp += 8;
+
+ printf (" [%4zu] CU offset: %5" PRId64
+ ", type offset: %5" PRId64
+ ", signature: %0#8" PRIx64 "\n", n, off, type, sig);
+ n++;
+ }
+
+ readp = data->d_buf + addr_off;
+ nextp = data->d_buf + sym_off;
+ if (sym_off >= data->d_size)
+ goto invalid_data;
+
+ size_t addr_nr = (nextp - readp) / 20;
+
+ printf (gettext ("\n Address list at offset %#" PRIx32
+ " contains %zu entries:\n"),
+ addr_off, addr_nr);
+
+ n = 0;
+ while (dataend - readp >= 20 && n < addr_nr)
+ {
+ uint64_t low = read_8ubyte_unaligned (dbg, readp);
+ readp += 8;
+
+ uint64_t high = read_8ubyte_unaligned (dbg, readp);
+ readp += 8;
+
+ uint32_t idx = read_4ubyte_unaligned (dbg, readp);
+ readp += 4;
+
+ char *l = format_dwarf_addr (dwflmod, 8, low, low);
+ char *h = format_dwarf_addr (dwflmod, 8, high - 1, high);
+ printf (" [%4zu] %s..%s, CU index: %5" PRId32 "\n",
+ n, l, h, idx);
+ free (l);
+ free (h);
+ n++;
+ }
+
+ const unsigned char *const_start = data->d_buf + const_off;
+ if (const_off >= data->d_size)
+ goto invalid_data;
+
+ readp = data->d_buf + sym_off;
+ nextp = const_start;
+ size_t sym_nr = (nextp - readp) / 8;
+
+ printf (gettext ("\n Symbol table at offset %#" PRIx32
+ " contains %zu slots:\n"),
+ addr_off, sym_nr);
+
+ n = 0;
+ while (dataend - readp >= 8 && n < sym_nr)
+ {
+ uint32_t name = read_4ubyte_unaligned (dbg, readp);
+ readp += 4;
+
+ uint32_t vector = read_4ubyte_unaligned (dbg, readp);
+ readp += 4;
+
+ if (name != 0 || vector != 0)
+ {
+ const unsigned char *sym = const_start + name;
+ if (unlikely ((size_t) (dataend - const_start) < name
+ || memchr (sym, '\0', dataend - sym) == NULL))
+ goto invalid_data;
+
+ printf (" [%4zu] symbol: %s, CUs: ", n, sym);
+
+ const unsigned char *readcus = const_start + vector;
+ if (unlikely ((size_t) (dataend - const_start) < vector))
+ goto invalid_data;
+ uint32_t cus = read_4ubyte_unaligned (dbg, readcus);
+ while (cus--)
+ {
+ uint32_t cu_kind, cu, kind;
+ bool is_static;
+ readcus += 4;
+ if (unlikely (readcus + 4 > dataend))
+ goto invalid_data;
+ cu_kind = read_4ubyte_unaligned (dbg, readcus);
+ cu = cu_kind & ((1 << 24) - 1);
+ kind = (cu_kind >> 28) & 7;
+ is_static = cu_kind & (1U << 31);
+ if (cu > cu_nr - 1)
+ printf ("%" PRId32 "T", cu - (uint32_t) cu_nr);
+ else
+ printf ("%" PRId32, cu);
+ if (kind != 0)
+ {
+ printf (" (");
+ switch (kind)
+ {
+ case 1:
+ printf ("type");
+ break;
+ case 2:
+ printf ("var");
+ break;
+ case 3:
+ printf ("func");
+ break;
+ case 4:
+ printf ("other");
+ break;
+ default:
+ printf ("unknown-0x%" PRIx32, kind);
+ break;
+ }
+ printf (":%c)", (is_static ? 'S' : 'G'));
+ }
+ if (cus > 0)
+ printf (", ");
+ }
+ printf ("\n");
+ }
+ n++;
+ }
+}
+
+static void
+print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr)
+{
+ /* Before we start the real work get a debug context descriptor. */
+ Dwarf_Addr dwbias;
+ Dwarf *dbg = dwfl_module_getdwarf (dwflmod, &dwbias);
+ Dwarf dummy_dbg =
+ {
+ .elf = ebl->elf,
+ .other_byte_order = MY_ELFDATA != ehdr->e_ident[EI_DATA]
+ };
+ if (dbg == NULL)
+ {
+ if ((print_debug_sections & ~section_exception) != 0)
+ error (0, 0, gettext ("cannot get debug context descriptor: %s"),
+ dwfl_errmsg (-1));
+ dbg = &dummy_dbg;
+ }
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ /* Look through all the sections for the debugging sections to print. */
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr != NULL && shdr->sh_type == SHT_PROGBITS)
+ {
+ static const struct
+ {
+ const char *name;
+ enum section_e bitmask;
+ void (*fp) (Dwfl_Module *, Ebl *,
+ GElf_Ehdr *, Elf_Scn *, GElf_Shdr *, Dwarf *);
+ } debug_sections[] =
+ {
+#define NEW_SECTION(name) \
+ { ".debug_" #name, section_##name, print_debug_##name##_section }
+ NEW_SECTION (abbrev),
+ NEW_SECTION (aranges),
+ NEW_SECTION (frame),
+ NEW_SECTION (info),
+ NEW_SECTION (types),
+ NEW_SECTION (line),
+ NEW_SECTION (loc),
+ NEW_SECTION (pubnames),
+ NEW_SECTION (str),
+ NEW_SECTION (macinfo),
+ NEW_SECTION (macro),
+ NEW_SECTION (ranges),
+ { ".eh_frame", section_frame | section_exception,
+ print_debug_frame_section },
+ { ".eh_frame_hdr", section_frame | section_exception,
+ print_debug_frame_hdr_section },
+ { ".gcc_except_table", section_frame | section_exception,
+ print_debug_exception_table },
+ { ".gdb_index", section_gdb_index, print_gdb_index_section }
+ };
+ const int ndebug_sections = (sizeof (debug_sections)
+ / sizeof (debug_sections[0]));
+ const char *name = elf_strptr (ebl->elf, shstrndx,
+ shdr->sh_name);
+ if (name == NULL)
+ continue;
+
+ int n;
+ for (n = 0; n < ndebug_sections; ++n)
+ if (strcmp (name, debug_sections[n].name) == 0
+ || (name[0] == '.' && name[1] == 'z'
+ && debug_sections[n].name[1] == 'd'
+ && strcmp (&name[2], &debug_sections[n].name[1]) == 0)
+ )
+ {
+ if ((print_debug_sections | implicit_debug_sections)
+ & debug_sections[n].bitmask)
+ debug_sections[n].fp (dwflmod, ebl, ehdr, scn, shdr, dbg);
+ break;
+ }
+ }
+ }
+
+ reset_listptr (&known_loclistptr);
+ reset_listptr (&known_rangelistptr);
+}
+
+
+#define ITEM_INDENT 4
+#define WRAP_COLUMN 75
+
+/* Print "NAME: FORMAT", wrapping when output text would make the line
+ exceed WRAP_COLUMN. Unpadded numbers look better for the core items
+ but this function is also used for registers which should be printed
+ aligned. Fortunately registers output uses fixed fields width (such
+ as %11d) for the alignment.
+
+ Line breaks should not depend on the particular values although that
+ may happen in some cases of the core items. */
+
+static unsigned int
+__attribute__ ((format (printf, 6, 7)))
+print_core_item (unsigned int colno, char sep, unsigned int wrap,
+ size_t name_width, const char *name, const char *format, ...)
+{
+ size_t len = strlen (name);
+ if (name_width < len)
+ name_width = len;
+
+ char *out;
+ va_list ap;
+ va_start (ap, format);
+ int out_len = vasprintf (&out, format, ap);
+ va_end (ap);
+ if (out_len == -1)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+
+ size_t n = name_width + sizeof ": " - 1 + out_len;
+
+ if (colno == 0)
+ {
+ printf ("%*s", ITEM_INDENT, "");
+ colno = ITEM_INDENT + n;
+ }
+ else if (colno + 2 + n < wrap)
+ {
+ printf ("%c ", sep);
+ colno += 2 + n;
+ }
+ else
+ {
+ printf ("\n%*s", ITEM_INDENT, "");
+ colno = ITEM_INDENT + n;
+ }
+
+ printf ("%s: %*s%s", name, (int) (name_width - len), "", out);
+
+ free (out);
+
+ return colno;
+}
+
+static const void *
+convert (Elf *core, Elf_Type type, uint_fast16_t count,
+ void *value, const void *data, size_t size)
+{
+ Elf_Data valuedata =
+ {
+ .d_type = type,
+ .d_buf = value,
+ .d_size = size ?: gelf_fsize (core, type, count, EV_CURRENT),
+ .d_version = EV_CURRENT,
+ };
+ Elf_Data indata =
+ {
+ .d_type = type,
+ .d_buf = (void *) data,
+ .d_size = valuedata.d_size,
+ .d_version = EV_CURRENT,
+ };
+
+ Elf_Data *d = (gelf_getclass (core) == ELFCLASS32
+ ? elf32_xlatetom : elf64_xlatetom)
+ (&valuedata, &indata, elf_getident (core, NULL)[EI_DATA]);
+ if (d == NULL)
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot convert core note data: %s"), elf_errmsg (-1));
+
+ return data + indata.d_size;
+}
+
+typedef uint8_t GElf_Byte;
+
+static unsigned int
+handle_core_item (Elf *core, const Ebl_Core_Item *item, const void *desc,
+ unsigned int colno, size_t *repeated_size)
+{
+ uint_fast16_t count = item->count ?: 1;
+ /* Ebl_Core_Item count is always a small number.
+ Make sure the backend didn't put in some large bogus value. */
+ assert (count < 128);
+
+#define TYPES \
+ DO_TYPE (BYTE, Byte, "0x%.2" PRIx8, "%" PRId8); \
+ DO_TYPE (HALF, Half, "0x%.4" PRIx16, "%" PRId16); \
+ DO_TYPE (WORD, Word, "0x%.8" PRIx32, "%" PRId32); \
+ DO_TYPE (SWORD, Sword, "%" PRId32, "%" PRId32); \
+ DO_TYPE (XWORD, Xword, "0x%.16" PRIx64, "%" PRId64); \
+ DO_TYPE (SXWORD, Sxword, "%" PRId64, "%" PRId64)
+
+#define DO_TYPE(NAME, Name, hex, dec) GElf_##Name Name
+ typedef union { TYPES; } value_t;
+ void *data = alloca (count * sizeof (value_t));
+#undef DO_TYPE
+
+#define DO_TYPE(NAME, Name, hex, dec) \
+ GElf_##Name *value_##Name __attribute__((unused)) = data
+ TYPES;
+#undef DO_TYPE
+
+ size_t size = gelf_fsize (core, item->type, count, EV_CURRENT);
+ size_t convsize = size;
+ if (repeated_size != NULL)
+ {
+ if (*repeated_size > size && (item->format == 'b' || item->format == 'B'))
+ {
+ data = alloca (*repeated_size);
+ count *= *repeated_size / size;
+ convsize = count * size;
+ *repeated_size -= convsize;
+ }
+ else if (item->count != 0 || item->format != '\n')
+ *repeated_size -= size;
+ }
+
+ convert (core, item->type, count, data, desc + item->offset, convsize);
+
+ Elf_Type type = item->type;
+ if (type == ELF_T_ADDR)
+ type = gelf_getclass (core) == ELFCLASS32 ? ELF_T_WORD : ELF_T_XWORD;
+
+ switch (item->format)
+ {
+ case 'd':
+ assert (count == 1);
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name, hex, dec) \
+ case ELF_T_##NAME: \
+ colno = print_core_item (colno, ',', WRAP_COLUMN, \
+ 0, item->name, dec, value_##Name[0]); \
+ break
+ TYPES;
+#undef DO_TYPE
+ default:
+ abort ();
+ }
+ break;
+
+ case 'x':
+ assert (count == 1);
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name, hex, dec) \
+ case ELF_T_##NAME: \
+ colno = print_core_item (colno, ',', WRAP_COLUMN, \
+ 0, item->name, hex, value_##Name[0]); \
+ break
+ TYPES;
+#undef DO_TYPE
+ default:
+ abort ();
+ }
+ break;
+
+ case 'b':
+ case 'B':
+ assert (size % sizeof (unsigned int) == 0);
+ unsigned int nbits = count * size * 8;
+ unsigned int pop = 0;
+ for (const unsigned int *i = data; (void *) i < data + count * size; ++i)
+ pop += __builtin_popcount (*i);
+ bool negate = pop > nbits / 2;
+ const unsigned int bias = item->format == 'b';
+
+ {
+ char printed[(negate ? nbits - pop : pop) * 16 + 1];
+ char *p = printed;
+ *p = '\0';
+
+ if (BYTE_ORDER != LITTLE_ENDIAN && size > sizeof (unsigned int))
+ {
+ assert (size == sizeof (unsigned int) * 2);
+ for (unsigned int *i = data;
+ (void *) i < data + count * size; i += 2)
+ {
+ unsigned int w = i[1];
+ i[1] = i[0];
+ i[0] = w;
+ }
+ }
+
+ unsigned int lastbit = 0;
+ unsigned int run = 0;
+ for (const unsigned int *i = data;
+ (void *) i < data + count * size; ++i)
+ {
+ unsigned int bit = ((void *) i - data) * 8;
+ unsigned int w = negate ? ~*i : *i;
+ while (w != 0)
+ {
+ /* Note that a right shift equal to (or greater than)
+ the number of bits of w is undefined behaviour. In
+ particular when the least significant bit is bit 32
+ (w = 0x8000000) then w >>= n is undefined. So
+ explicitly handle that case separately. */
+ unsigned int n = ffs (w);
+ if (n < sizeof (w) * 8)
+ w >>= n;
+ else
+ w = 0;
+ bit += n;
+
+ if (lastbit != 0 && lastbit + 1 == bit)
+ ++run;
+ else
+ {
+ if (lastbit == 0)
+ p += sprintf (p, "%u", bit - bias);
+ else if (run == 0)
+ p += sprintf (p, ",%u", bit - bias);
+ else
+ p += sprintf (p, "-%u,%u", lastbit - bias, bit - bias);
+ run = 0;
+ }
+
+ lastbit = bit;
+ }
+ }
+ if (lastbit > 0 && run > 0 && lastbit + 1 != nbits)
+ p += sprintf (p, "-%u", lastbit - bias);
+
+ colno = print_core_item (colno, ',', WRAP_COLUMN, 0, item->name,
+ negate ? "~<%s>" : "<%s>", printed);
+ }
+ break;
+
+ case 'T':
+ case (char) ('T'|0x80):
+ assert (count == 2);
+ Dwarf_Word sec;
+ Dwarf_Word usec;
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name, hex, dec) \
+ case ELF_T_##NAME: \
+ sec = value_##Name[0]; \
+ usec = value_##Name[1]; \
+ break
+ TYPES;
+#undef DO_TYPE
+ default:
+ abort ();
+ }
+ if (unlikely (item->format == (char) ('T'|0x80)))
+ {
+ /* This is a hack for an ill-considered 64-bit ABI where
+ tv_usec is actually a 32-bit field with 32 bits of padding
+ rounding out struct timeval. We've already converted it as
+ a 64-bit field. For little-endian, this just means the
+ high half is the padding; it's presumably zero, but should
+ be ignored anyway. For big-endian, it means the 32-bit
+ field went into the high half of USEC. */
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (core, &ehdr_mem);
+ if (likely (ehdr->e_ident[EI_DATA] == ELFDATA2MSB))
+ usec >>= 32;
+ else
+ usec &= UINT32_MAX;
+ }
+ colno = print_core_item (colno, ',', WRAP_COLUMN, 0, item->name,
+ "%" PRIu64 ".%.6" PRIu64, sec, usec);
+ break;
+
+ case 'c':
+ assert (count == 1);
+ colno = print_core_item (colno, ',', WRAP_COLUMN, 0, item->name,
+ "%c", value_Byte[0]);
+ break;
+
+ case 's':
+ colno = print_core_item (colno, ',', WRAP_COLUMN, 0, item->name,
+ "%.*s", (int) count, value_Byte);
+ break;
+
+ case '\n':
+ /* This is a list of strings separated by '\n'. */
+ assert (item->count == 0);
+ assert (repeated_size != NULL);
+ assert (item->name == NULL);
+ if (unlikely (item->offset >= *repeated_size))
+ break;
+
+ const char *s = desc + item->offset;
+ size = *repeated_size - item->offset;
+ *repeated_size = 0;
+ while (size > 0)
+ {
+ const char *eol = memchr (s, '\n', size);
+ int len = size;
+ if (eol != NULL)
+ len = eol - s;
+ printf ("%*s%.*s\n", ITEM_INDENT, "", len, s);
+ if (eol == NULL)
+ break;
+ size -= eol + 1 - s;
+ s = eol + 1;
+ }
+
+ colno = WRAP_COLUMN;
+ break;
+
+ case 'h':
+ break;
+
+ default:
+ error (0, 0, "XXX not handling format '%c' for %s",
+ item->format, item->name);
+ break;
+ }
+
+#undef TYPES
+
+ return colno;
+}
+
+
+/* Sort items by group, and by layout offset within each group. */
+static int
+compare_core_items (const void *a, const void *b)
+{
+ const Ebl_Core_Item *const *p1 = a;
+ const Ebl_Core_Item *const *p2 = b;
+ const Ebl_Core_Item *item1 = *p1;
+ const Ebl_Core_Item *item2 = *p2;
+
+ return ((item1->group == item2->group ? 0
+ : strcmp (item1->group, item2->group))
+ ?: (int) item1->offset - (int) item2->offset);
+}
+
+/* Sort item groups by layout offset of the first item in the group. */
+static int
+compare_core_item_groups (const void *a, const void *b)
+{
+ const Ebl_Core_Item *const *const *p1 = a;
+ const Ebl_Core_Item *const *const *p2 = b;
+ const Ebl_Core_Item *const *group1 = *p1;
+ const Ebl_Core_Item *const *group2 = *p2;
+ const Ebl_Core_Item *item1 = *group1;
+ const Ebl_Core_Item *item2 = *group2;
+
+ return (int) item1->offset - (int) item2->offset;
+}
+
+static unsigned int
+handle_core_items (Elf *core, const void *desc, size_t descsz,
+ const Ebl_Core_Item *items, size_t nitems)
+{
+ if (nitems == 0)
+ return 0;
+ unsigned int colno = 0;
+
+ /* FORMAT '\n' makes sense to be present only as a single item as it
+ processes all the data of a note. FORMATs 'b' and 'B' have a special case
+ if present as a single item but they can be also processed with other
+ items below. */
+ if (nitems == 1 && (items[0].format == '\n' || items[0].format == 'b'
+ || items[0].format == 'B'))
+ {
+ assert (items[0].offset == 0);
+ size_t size = descsz;
+ colno = handle_core_item (core, items, desc, colno, &size);
+ /* If SIZE is not zero here there is some remaining data. But we do not
+ know how to process it anyway. */
+ return colno;
+ }
+ for (size_t i = 0; i < nitems; ++i)
+ assert (items[i].format != '\n');
+
+ /* Sort to collect the groups together. */
+ const Ebl_Core_Item *sorted_items[nitems];
+ for (size_t i = 0; i < nitems; ++i)
+ sorted_items[i] = &items[i];
+ qsort (sorted_items, nitems, sizeof sorted_items[0], &compare_core_items);
+
+ /* Collect the unique groups and sort them. */
+ const Ebl_Core_Item **groups[nitems];
+ groups[0] = &sorted_items[0];
+ size_t ngroups = 1;
+ for (size_t i = 1; i < nitems; ++i)
+ if (sorted_items[i]->group != sorted_items[i - 1]->group
+ && strcmp (sorted_items[i]->group, sorted_items[i - 1]->group))
+ groups[ngroups++] = &sorted_items[i];
+ qsort (groups, ngroups, sizeof groups[0], &compare_core_item_groups);
+
+ /* Write out all the groups. */
+ const void *last = desc;
+ do
+ {
+ for (size_t i = 0; i < ngroups; ++i)
+ {
+ for (const Ebl_Core_Item **item = groups[i];
+ (item < &sorted_items[nitems]
+ && ((*item)->group == groups[i][0]->group
+ || !strcmp ((*item)->group, groups[i][0]->group)));
+ ++item)
+ colno = handle_core_item (core, *item, desc, colno, NULL);
+
+ /* Force a line break at the end of the group. */
+ colno = WRAP_COLUMN;
+ }
+
+ if (descsz == 0)
+ break;
+
+ /* This set of items consumed a certain amount of the note's data.
+ If there is more data there, we have another unit of the same size.
+ Loop to print that out too. */
+ const Ebl_Core_Item *item = &items[nitems - 1];
+ size_t eltsz = item->offset + gelf_fsize (core, item->type,
+ item->count ?: 1, EV_CURRENT);
+
+ int reps = -1;
+ do
+ {
+ ++reps;
+ desc += eltsz;
+ descsz -= eltsz;
+ }
+ while (descsz >= eltsz && !memcmp (desc, last, eltsz));
+
+ if (reps == 1)
+ {
+ /* For just one repeat, print it unabridged twice. */
+ desc -= eltsz;
+ descsz += eltsz;
+ }
+ else if (reps > 1)
+ printf (gettext ("\n%*s... <repeats %u more times> ..."),
+ ITEM_INDENT, "", reps);
+
+ last = desc;
+ }
+ while (descsz > 0);
+
+ return colno;
+}
+
+static unsigned int
+handle_bit_registers (const Ebl_Register_Location *regloc, const void *desc,
+ unsigned int colno)
+{
+ desc += regloc->offset;
+
+ abort (); /* XXX */
+ return colno;
+}
+
+
+static unsigned int
+handle_core_register (Ebl *ebl, Elf *core, int maxregname,
+ const Ebl_Register_Location *regloc, const void *desc,
+ unsigned int colno)
+{
+ if (regloc->bits % 8 != 0)
+ return handle_bit_registers (regloc, desc, colno);
+
+ desc += regloc->offset;
+
+ for (int reg = regloc->regno; reg < regloc->regno + regloc->count; ++reg)
+ {
+ char name[REGNAMESZ];
+ int bits;
+ int type;
+ register_info (ebl, reg, regloc, name, &bits, &type);
+
+#define TYPES \
+ BITS (8, BYTE, "%4" PRId8, "0x%.2" PRIx8); \
+ BITS (16, HALF, "%6" PRId16, "0x%.4" PRIx16); \
+ BITS (32, WORD, "%11" PRId32, " 0x%.8" PRIx32); \
+ BITS (64, XWORD, "%20" PRId64, " 0x%.16" PRIx64)
+
+#define BITS(bits, xtype, sfmt, ufmt) \
+ uint##bits##_t b##bits; int##bits##_t b##bits##s
+ union { TYPES; uint64_t b128[2]; } value;
+#undef BITS
+
+ switch (type)
+ {
+ case DW_ATE_unsigned:
+ case DW_ATE_signed:
+ case DW_ATE_address:
+ switch (bits)
+ {
+#define BITS(bits, xtype, sfmt, ufmt) \
+ case bits: \
+ desc = convert (core, ELF_T_##xtype, 1, &value, desc, 0); \
+ if (type == DW_ATE_signed) \
+ colno = print_core_item (colno, ' ', WRAP_COLUMN, \
+ maxregname, name, \
+ sfmt, value.b##bits##s); \
+ else \
+ colno = print_core_item (colno, ' ', WRAP_COLUMN, \
+ maxregname, name, \
+ ufmt, value.b##bits); \
+ break
+
+ TYPES;
+
+ case 128:
+ assert (type == DW_ATE_unsigned);
+ desc = convert (core, ELF_T_XWORD, 2, &value, desc, 0);
+ int be = elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB;
+ colno = print_core_item (colno, ' ', WRAP_COLUMN,
+ maxregname, name,
+ "0x%.16" PRIx64 "%.16" PRIx64,
+ value.b128[!be], value.b128[be]);
+ break;
+
+ default:
+ abort ();
+#undef BITS
+ }
+ break;
+
+ default:
+ /* Print each byte in hex, the whole thing in native byte order. */
+ assert (bits % 8 == 0);
+ const uint8_t *bytes = desc;
+ desc += bits / 8;
+ char hex[bits / 4 + 1];
+ hex[bits / 4] = '\0';
+ int incr = 1;
+ if (elf_getident (core, NULL)[EI_DATA] == ELFDATA2LSB)
+ {
+ bytes += bits / 8 - 1;
+ incr = -1;
+ }
+ size_t idx = 0;
+ for (char *h = hex; bits > 0; bits -= 8, idx += incr)
+ {
+ *h++ = "0123456789abcdef"[bytes[idx] >> 4];
+ *h++ = "0123456789abcdef"[bytes[idx] & 0xf];
+ }
+ colno = print_core_item (colno, ' ', WRAP_COLUMN,
+ maxregname, name, "0x%s", hex);
+ break;
+ }
+ desc += regloc->pad;
+
+#undef TYPES
+ }
+
+ return colno;
+}
+
+
+struct register_info
+{
+ const Ebl_Register_Location *regloc;
+ const char *set;
+ char name[REGNAMESZ];
+ int regno;
+ int bits;
+ int type;
+};
+
+static int
+register_bitpos (const struct register_info *r)
+{
+ return (r->regloc->offset * 8
+ + ((r->regno - r->regloc->regno)
+ * (r->regloc->bits + r->regloc->pad * 8)));
+}
+
+static int
+compare_sets_by_info (const struct register_info *r1,
+ const struct register_info *r2)
+{
+ return ((int) r2->bits - (int) r1->bits
+ ?: register_bitpos (r1) - register_bitpos (r2));
+}
+
+/* Sort registers by set, and by size and layout offset within each set. */
+static int
+compare_registers (const void *a, const void *b)
+{
+ const struct register_info *r1 = a;
+ const struct register_info *r2 = b;
+
+ /* Unused elements sort last. */
+ if (r1->regloc == NULL)
+ return r2->regloc == NULL ? 0 : 1;
+ if (r2->regloc == NULL)
+ return -1;
+
+ return ((r1->set == r2->set ? 0 : strcmp (r1->set, r2->set))
+ ?: compare_sets_by_info (r1, r2));
+}
+
+/* Sort register sets by layout offset of the first register in the set. */
+static int
+compare_register_sets (const void *a, const void *b)
+{
+ const struct register_info *const *p1 = a;
+ const struct register_info *const *p2 = b;
+ return compare_sets_by_info (*p1, *p2);
+}
+
+static unsigned int
+handle_core_registers (Ebl *ebl, Elf *core, const void *desc,
+ const Ebl_Register_Location *reglocs, size_t nregloc)
+{
+ if (nregloc == 0)
+ return 0;
+
+ ssize_t maxnreg = ebl_register_info (ebl, 0, NULL, 0, NULL, NULL, NULL, NULL);
+ if (maxnreg <= 0)
+ {
+ for (size_t i = 0; i < nregloc; ++i)
+ if (maxnreg < reglocs[i].regno + reglocs[i].count)
+ maxnreg = reglocs[i].regno + reglocs[i].count;
+ assert (maxnreg > 0);
+ }
+
+ struct register_info regs[maxnreg];
+ memset (regs, 0, sizeof regs);
+
+ /* Sort to collect the sets together. */
+ int maxreg = 0;
+ for (size_t i = 0; i < nregloc; ++i)
+ for (int reg = reglocs[i].regno;
+ reg < reglocs[i].regno + reglocs[i].count;
+ ++reg)
+ {
+ assert (reg < maxnreg);
+ if (reg > maxreg)
+ maxreg = reg;
+ struct register_info *info = ®s[reg];
+ info->regloc = ®locs[i];
+ info->regno = reg;
+ info->set = register_info (ebl, reg, ®locs[i],
+ info->name, &info->bits, &info->type);
+ }
+ qsort (regs, maxreg + 1, sizeof regs[0], &compare_registers);
+
+ /* Collect the unique sets and sort them. */
+ inline bool same_set (const struct register_info *a,
+ const struct register_info *b)
+ {
+ return (a < ®s[maxnreg] && a->regloc != NULL
+ && b < ®s[maxnreg] && b->regloc != NULL
+ && a->bits == b->bits
+ && (a->set == b->set || !strcmp (a->set, b->set)));
+ }
+ struct register_info *sets[maxreg + 1];
+ sets[0] = ®s[0];
+ size_t nsets = 1;
+ for (int i = 1; i <= maxreg; ++i)
+ if (regs[i].regloc != NULL && !same_set (®s[i], ®s[i - 1]))
+ sets[nsets++] = ®s[i];
+ qsort (sets, nsets, sizeof sets[0], &compare_register_sets);
+
+ /* Write out all the sets. */
+ unsigned int colno = 0;
+ for (size_t i = 0; i < nsets; ++i)
+ {
+ /* Find the longest name of a register in this set. */
+ size_t maxname = 0;
+ const struct register_info *end;
+ for (end = sets[i]; same_set (sets[i], end); ++end)
+ {
+ size_t len = strlen (end->name);
+ if (len > maxname)
+ maxname = len;
+ }
+
+ for (const struct register_info *reg = sets[i];
+ reg < end;
+ reg += reg->regloc->count ?: 1)
+ colno = handle_core_register (ebl, core, maxname,
+ reg->regloc, desc, colno);
+
+ /* Force a line break at the end of the group. */
+ colno = WRAP_COLUMN;
+ }
+
+ return colno;
+}
+
+static void
+handle_auxv_note (Ebl *ebl, Elf *core, GElf_Word descsz, GElf_Off desc_pos)
+{
+ Elf_Data *data = elf_getdata_rawchunk (core, desc_pos, descsz, ELF_T_AUXV);
+ if (data == NULL)
+ elf_error:
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot convert core note data: %s"), elf_errmsg (-1));
+
+ const size_t nauxv = descsz / gelf_fsize (core, ELF_T_AUXV, 1, EV_CURRENT);
+ for (size_t i = 0; i < nauxv; ++i)
+ {
+ GElf_auxv_t av_mem;
+ GElf_auxv_t *av = gelf_getauxv (data, i, &av_mem);
+ if (av == NULL)
+ goto elf_error;
+
+ const char *name;
+ const char *fmt;
+ if (ebl_auxv_info (ebl, av->a_type, &name, &fmt) == 0)
+ {
+ /* Unknown type. */
+ if (av->a_un.a_val == 0)
+ printf (" %" PRIu64 "\n", av->a_type);
+ else
+ printf (" %" PRIu64 ": %#" PRIx64 "\n",
+ av->a_type, av->a_un.a_val);
+ }
+ else
+ switch (fmt[0])
+ {
+ case '\0': /* Normally zero. */
+ if (av->a_un.a_val == 0)
+ {
+ printf (" %s\n", name);
+ break;
+ }
+ FALLTHROUGH;
+ case 'x': /* hex */
+ case 'p': /* address */
+ case 's': /* address of string */
+ printf (" %s: %#" PRIx64 "\n", name, av->a_un.a_val);
+ break;
+ case 'u':
+ printf (" %s: %" PRIu64 "\n", name, av->a_un.a_val);
+ break;
+ case 'd':
+ printf (" %s: %" PRId64 "\n", name, av->a_un.a_val);
+ break;
+
+ case 'b':
+ printf (" %s: %#" PRIx64 " ", name, av->a_un.a_val);
+ GElf_Xword bit = 1;
+ const char *pfx = "<";
+ for (const char *p = fmt + 1; *p != 0; p = strchr (p, '\0') + 1)
+ {
+ if (av->a_un.a_val & bit)
+ {
+ printf ("%s%s", pfx, p);
+ pfx = " ";
+ }
+ bit <<= 1;
+ }
+ printf (">\n");
+ break;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+static bool
+buf_has_data (unsigned char const *ptr, unsigned char const *end, size_t sz)
+{
+ return ptr < end && (size_t) (end - ptr) >= sz;
+}
+
+static bool
+buf_read_int (Elf *core, unsigned char const **ptrp, unsigned char const *end,
+ int *retp)
+{
+ if (! buf_has_data (*ptrp, end, 4))
+ return false;
+
+ *ptrp = convert (core, ELF_T_WORD, 1, retp, *ptrp, 4);
+ return true;
+}
+
+static bool
+buf_read_ulong (Elf *core, unsigned char const **ptrp, unsigned char const *end,
+ uint64_t *retp)
+{
+ size_t sz = gelf_fsize (core, ELF_T_ADDR, 1, EV_CURRENT);
+ if (! buf_has_data (*ptrp, end, sz))
+ return false;
+
+ union
+ {
+ uint64_t u64;
+ uint32_t u32;
+ } u;
+
+ *ptrp = convert (core, ELF_T_ADDR, 1, &u, *ptrp, sz);
+
+ if (sz == 4)
+ *retp = u.u32;
+ else
+ *retp = u.u64;
+ return true;
+}
+
+static void
+handle_siginfo_note (Elf *core, GElf_Word descsz, GElf_Off desc_pos)
+{
+ Elf_Data *data = elf_getdata_rawchunk (core, desc_pos, descsz, ELF_T_BYTE);
+ if (data == NULL)
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot convert core note data: %s"), elf_errmsg (-1));
+
+ unsigned char const *ptr = data->d_buf;
+ unsigned char const *const end = data->d_buf + data->d_size;
+
+ /* Siginfo head is three ints: signal number, error number, origin
+ code. */
+ int si_signo, si_errno, si_code;
+ if (! buf_read_int (core, &ptr, end, &si_signo)
+ || ! buf_read_int (core, &ptr, end, &si_errno)
+ || ! buf_read_int (core, &ptr, end, &si_code))
+ {
+ fail:
+ printf (" Not enough data in NT_SIGINFO note.\n");
+ return;
+ }
+
+ /* Next is a pointer-aligned union of structures. On 64-bit
+ machines, that implies a word of padding. */
+ if (gelf_getclass (core) == ELFCLASS64)
+ ptr += 4;
+
+ printf (" si_signo: %d, si_errno: %d, si_code: %d\n",
+ si_signo, si_errno, si_code);
+
+ if (si_code > 0)
+ switch (si_signo)
+ {
+ case CORE_SIGILL:
+ case CORE_SIGFPE:
+ case CORE_SIGSEGV:
+ case CORE_SIGBUS:
+ {
+ uint64_t addr;
+ if (! buf_read_ulong (core, &ptr, end, &addr))
+ goto fail;
+ printf (" fault address: %#" PRIx64 "\n", addr);
+ break;
+ }
+ default:
+ ;
+ }
+ else if (si_code == CORE_SI_USER)
+ {
+ int pid, uid;
+ if (! buf_read_int (core, &ptr, end, &pid)
+ || ! buf_read_int (core, &ptr, end, &uid))
+ goto fail;
+ printf (" sender PID: %d, sender UID: %d\n", pid, uid);
+ }
+}
+
+static void
+handle_file_note (Elf *core, GElf_Word descsz, GElf_Off desc_pos)
+{
+ Elf_Data *data = elf_getdata_rawchunk (core, desc_pos, descsz, ELF_T_BYTE);
+ if (data == NULL)
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot convert core note data: %s"), elf_errmsg (-1));
+
+ unsigned char const *ptr = data->d_buf;
+ unsigned char const *const end = data->d_buf + data->d_size;
+
+ uint64_t count, page_size;
+ if (! buf_read_ulong (core, &ptr, end, &count)
+ || ! buf_read_ulong (core, &ptr, end, &page_size))
+ {
+ fail:
+ printf (" Not enough data in NT_FILE note.\n");
+ return;
+ }
+
+ size_t addrsize = gelf_fsize (core, ELF_T_ADDR, 1, EV_CURRENT);
+ uint64_t maxcount = (size_t) (end - ptr) / (3 * addrsize);
+ if (count > maxcount)
+ goto fail;
+
+ /* Where file names are stored. */
+ unsigned char const *const fstart = ptr + 3 * count * addrsize;
+ char const *fptr = (char *) fstart;
+
+ printf (" %" PRId64 " files:\n", count);
+ for (uint64_t i = 0; i < count; ++i)
+ {
+ uint64_t mstart, mend, moffset;
+ if (! buf_read_ulong (core, &ptr, fstart, &mstart)
+ || ! buf_read_ulong (core, &ptr, fstart, &mend)
+ || ! buf_read_ulong (core, &ptr, fstart, &moffset))
+ goto fail;
+
+ const char *fnext = memchr (fptr, '\0', (char *) end - fptr);
+ if (fnext == NULL)
+ goto fail;
+
+ int ct = printf (" %08" PRIx64 "-%08" PRIx64
+ " %08" PRIx64 " %" PRId64,
+ mstart, mend, moffset * page_size, mend - mstart);
+ printf ("%*s%s\n", ct > 50 ? 3 : 53 - ct, "", fptr);
+
+ fptr = fnext + 1;
+ }
+}
+
+static void
+handle_core_note (Ebl *ebl, const GElf_Nhdr *nhdr,
+ const char *name, const void *desc)
+{
+ GElf_Word regs_offset;
+ size_t nregloc;
+ const Ebl_Register_Location *reglocs;
+ size_t nitems;
+ const Ebl_Core_Item *items;
+
+ if (! ebl_core_note (ebl, nhdr, name,
+ ®s_offset, &nregloc, ®locs, &nitems, &items))
+ return;
+
+ /* Pass 0 for DESCSZ when there are registers in the note,
+ so that the ITEMS array does not describe the whole thing.
+ For non-register notes, the actual descsz might be a multiple
+ of the unit size, not just exactly the unit size. */
+ unsigned int colno = handle_core_items (ebl->elf, desc,
+ nregloc == 0 ? nhdr->n_descsz : 0,
+ items, nitems);
+ if (colno != 0)
+ putchar_unlocked ('\n');
+
+ colno = handle_core_registers (ebl, ebl->elf, desc + regs_offset,
+ reglocs, nregloc);
+ if (colno != 0)
+ putchar_unlocked ('\n');
+}
+
+static void
+handle_notes_data (Ebl *ebl, const GElf_Ehdr *ehdr,
+ GElf_Off start, Elf_Data *data)
+{
+ fputs_unlocked (gettext (" Owner Data size Type\n"), stdout);
+
+ if (data == NULL)
+ goto bad_note;
+
+ size_t offset = 0;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ while (offset < data->d_size
+ && (offset = gelf_getnote (data, offset,
+ &nhdr, &name_offset, &desc_offset)) > 0)
+ {
+ const char *name = nhdr.n_namesz == 0 ? "" : data->d_buf + name_offset;
+ const char *desc = data->d_buf + desc_offset;
+
+ char buf[100];
+ char buf2[100];
+ printf (gettext (" %-13.*s %9" PRId32 " %s\n"),
+ (int) nhdr.n_namesz, name, nhdr.n_descsz,
+ ehdr->e_type == ET_CORE
+ ? ebl_core_note_type_name (ebl, nhdr.n_type,
+ buf, sizeof (buf))
+ : ebl_object_note_type_name (ebl, name, nhdr.n_type,
+ buf2, sizeof (buf2)));
+
+ /* Filter out invalid entries. */
+ if (memchr (name, '\0', nhdr.n_namesz) != NULL
+ /* XXX For now help broken Linux kernels. */
+ || 1)
+ {
+ if (ehdr->e_type == ET_CORE)
+ {
+ if (nhdr.n_type == NT_AUXV
+ && (nhdr.n_namesz == 4 /* Broken old Linux kernels. */
+ || (nhdr.n_namesz == 5 && name[4] == '\0'))
+ && !memcmp (name, "CORE", 4))
+ handle_auxv_note (ebl, ebl->elf, nhdr.n_descsz,
+ start + desc_offset);
+ else if (nhdr.n_namesz == 5 && strcmp (name, "CORE") == 0)
+ switch (nhdr.n_type)
+ {
+ case NT_SIGINFO:
+ handle_siginfo_note (ebl->elf, nhdr.n_descsz,
+ start + desc_offset);
+ break;
+
+ case NT_FILE:
+ handle_file_note (ebl->elf, nhdr.n_descsz,
+ start + desc_offset);
+ break;
+
+ default:
+ handle_core_note (ebl, &nhdr, name, desc);
+ }
+ else
+ handle_core_note (ebl, &nhdr, name, desc);
+ }
+ else
+ ebl_object_note (ebl, name, nhdr.n_type, nhdr.n_descsz, desc);
+ }
+ }
+
+ if (offset == data->d_size)
+ return;
+
+ bad_note:
+ error (0, 0,
+ gettext ("cannot get content of note: %s"),
+ data != NULL ? "garbage data" : elf_errmsg (-1));
+}
+
+static void
+handle_notes (Ebl *ebl, GElf_Ehdr *ehdr)
+{
+ /* If we have section headers, just look for SHT_NOTE sections.
+ In a debuginfo file, the program headers are not reliable. */
+ if (shnum != 0)
+ {
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr == NULL || shdr->sh_type != SHT_NOTE)
+ /* Not what we are looking for. */
+ continue;
+
+ printf (gettext ("\
+\nNote section [%2zu] '%s' of %" PRIu64 " bytes at offset %#0" PRIx64 ":\n"),
+ elf_ndxscn (scn),
+ elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
+ shdr->sh_size, shdr->sh_offset);
+
+ handle_notes_data (ebl, ehdr, shdr->sh_offset,
+ elf_getdata (scn, NULL));
+ }
+ return;
+ }
+
+ /* We have to look through the program header to find the note
+ sections. There can be more than one. */
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr mem;
+ GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &mem);
+
+ if (phdr == NULL || phdr->p_type != PT_NOTE)
+ /* Not what we are looking for. */
+ continue;
+
+ printf (gettext ("\
+\nNote segment of %" PRIu64 " bytes at offset %#0" PRIx64 ":\n"),
+ phdr->p_filesz, phdr->p_offset);
+
+ handle_notes_data (ebl, ehdr, phdr->p_offset,
+ elf_getdata_rawchunk (ebl->elf,
+ phdr->p_offset, phdr->p_filesz,
+ ELF_T_NHDR));
+ }
+}
+
+
+static void
+hex_dump (const uint8_t *data, size_t len)
+{
+ size_t pos = 0;
+ while (pos < len)
+ {
+ printf (" 0x%08zx ", pos);
+
+ const size_t chunk = MIN (len - pos, 16);
+
+ for (size_t i = 0; i < chunk; ++i)
+ if (i % 4 == 3)
+ printf ("%02x ", data[pos + i]);
+ else
+ printf ("%02x", data[pos + i]);
+
+ if (chunk < 16)
+ printf ("%*s", (int) ((16 - chunk) * 2 + (16 - chunk + 3) / 4), "");
+
+ for (size_t i = 0; i < chunk; ++i)
+ {
+ unsigned char b = data[pos + i];
+ printf ("%c", isprint (b) ? b : '.');
+ }
+
+ putchar ('\n');
+ pos += chunk;
+ }
+}
+
+static void
+dump_data_section (Elf_Scn *scn, const GElf_Shdr *shdr, const char *name)
+{
+ if (shdr->sh_size == 0 || shdr->sh_type == SHT_NOBITS)
+ printf (gettext ("\nSection [%zu] '%s' has no data to dump.\n"),
+ elf_ndxscn (scn), name);
+ else
+ {
+ if (print_decompress)
+ {
+ /* We try to decompress the section, but keep the old shdr around
+ so we can show both the original shdr size and the uncompressed
+ data size. */
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ if (elf_compress (scn, 0, 0) < 0)
+ printf ("WARNING: %s [%zd]\n",
+ gettext ("Couldn't uncompress section"),
+ elf_ndxscn (scn));
+ }
+ else if (strncmp (name, ".zdebug", strlen (".zdebug")) == 0)
+ {
+ if (elf_compress_gnu (scn, 0, 0) < 0)
+ printf ("WARNING: %s [%zd]\n",
+ gettext ("Couldn't uncompress section"),
+ elf_ndxscn (scn));
+ }
+ }
+
+ Elf_Data *data = elf_rawdata (scn, NULL);
+ if (data == NULL)
+ error (0, 0, gettext ("cannot get data for section [%zu] '%s': %s"),
+ elf_ndxscn (scn), name, elf_errmsg (-1));
+ else
+ {
+ if (data->d_size == shdr->sh_size)
+ printf (gettext ("\nHex dump of section [%zu] '%s', %" PRIu64
+ " bytes at offset %#0" PRIx64 ":\n"),
+ elf_ndxscn (scn), name,
+ shdr->sh_size, shdr->sh_offset);
+ else
+ printf (gettext ("\nHex dump of section [%zu] '%s', %" PRIu64
+ " bytes (%zd uncompressed) at offset %#0"
+ PRIx64 ":\n"),
+ elf_ndxscn (scn), name,
+ shdr->sh_size, data->d_size, shdr->sh_offset);
+ hex_dump (data->d_buf, data->d_size);
+ }
+ }
+}
+
+static void
+print_string_section (Elf_Scn *scn, const GElf_Shdr *shdr, const char *name)
+{
+ if (shdr->sh_size == 0 || shdr->sh_type == SHT_NOBITS)
+ printf (gettext ("\nSection [%zu] '%s' has no strings to dump.\n"),
+ elf_ndxscn (scn), name);
+ else
+ {
+ if (print_decompress)
+ {
+ /* We try to decompress the section, but keep the old shdr around
+ so we can show both the original shdr size and the uncompressed
+ data size. */
+ if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
+ {
+ if (elf_compress (scn, 0, 0) < 0)
+ printf ("WARNING: %s [%zd]\n",
+ gettext ("Couldn't uncompress section"),
+ elf_ndxscn (scn));
+ }
+ else if (strncmp (name, ".zdebug", strlen (".zdebug")) == 0)
+ {
+ if (elf_compress_gnu (scn, 0, 0) < 0)
+ printf ("WARNING: %s [%zd]\n",
+ gettext ("Couldn't uncompress section"),
+ elf_ndxscn (scn));
+ }
+ }
+
+ Elf_Data *data = elf_rawdata (scn, NULL);
+ if (data == NULL)
+ error (0, 0, gettext ("cannot get data for section [%zu] '%s': %s"),
+ elf_ndxscn (scn), name, elf_errmsg (-1));
+ else
+ {
+ if (data->d_size == shdr->sh_size)
+ printf (gettext ("\nString section [%zu] '%s' contains %" PRIu64
+ " bytes at offset %#0" PRIx64 ":\n"),
+ elf_ndxscn (scn), name,
+ shdr->sh_size, shdr->sh_offset);
+ else
+ printf (gettext ("\nString section [%zu] '%s' contains %" PRIu64
+ " bytes (%zd uncompressed) at offset %#0"
+ PRIx64 ":\n"),
+ elf_ndxscn (scn), name,
+ shdr->sh_size, data->d_size, shdr->sh_offset);
+
+ const char *start = data->d_buf;
+ const char *const limit = start + data->d_size;
+ do
+ {
+ const char *end = memchr (start, '\0', limit - start);
+ const size_t pos = start - (const char *) data->d_buf;
+ if (unlikely (end == NULL))
+ {
+ printf (" [%6zx]- %.*s\n",
+ pos, (int) (limit - start), start);
+ break;
+ }
+ printf (" [%6zx] %s\n", pos, start);
+ start = end + 1;
+ } while (start < limit);
+ }
+ }
+}
+
+static void
+for_each_section_argument (Elf *elf, const struct section_argument *list,
+ void (*dump) (Elf_Scn *scn, const GElf_Shdr *shdr,
+ const char *name))
+{
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (elf_getshdrstrndx (elf, &shstrndx) < 0)
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ for (const struct section_argument *a = list; a != NULL; a = a->next)
+ {
+ Elf_Scn *scn;
+ GElf_Shdr shdr_mem;
+ const char *name = NULL;
+
+ char *endp = NULL;
+ unsigned long int shndx = strtoul (a->arg, &endp, 0);
+ if (endp != a->arg && *endp == '\0')
+ {
+ scn = elf_getscn (elf, shndx);
+ if (scn == NULL)
+ {
+ error (0, 0, gettext ("\nsection [%lu] does not exist"), shndx);
+ continue;
+ }
+
+ if (gelf_getshdr (scn, &shdr_mem) == NULL)
+ error (EXIT_FAILURE, 0, gettext ("cannot get section header: %s"),
+ elf_errmsg (-1));
+ name = elf_strptr (elf, shstrndx, shdr_mem.sh_name);
+ }
+ else
+ {
+ /* Need to look up the section by name. */
+ scn = NULL;
+ bool found = false;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ if (gelf_getshdr (scn, &shdr_mem) == NULL)
+ continue;
+ name = elf_strptr (elf, shstrndx, shdr_mem.sh_name);
+ if (name == NULL)
+ continue;
+ if (!strcmp (name, a->arg))
+ {
+ found = true;
+ (*dump) (scn, &shdr_mem, name);
+ }
+ }
+
+ if (unlikely (!found) && !a->implicit)
+ error (0, 0, gettext ("\nsection '%s' does not exist"), a->arg);
+ }
+ }
+}
+
+static void
+dump_data (Ebl *ebl)
+{
+ for_each_section_argument (ebl->elf, dump_data_sections, &dump_data_section);
+}
+
+static void
+dump_strings (Ebl *ebl)
+{
+ for_each_section_argument (ebl->elf, string_sections, &print_string_section);
+}
+
+static void
+print_strings (Ebl *ebl)
+{
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ Elf_Scn *scn;
+ GElf_Shdr shdr_mem;
+ const char *name;
+ scn = NULL;
+ while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
+ {
+ if (gelf_getshdr (scn, &shdr_mem) == NULL)
+ continue;
+
+ if (shdr_mem.sh_type != SHT_PROGBITS
+ || !(shdr_mem.sh_flags & SHF_STRINGS))
+ continue;
+
+ name = elf_strptr (ebl->elf, shstrndx, shdr_mem.sh_name);
+ if (name == NULL)
+ continue;
+
+ print_string_section (scn, &shdr_mem, name);
+ }
+}
+
+static void
+dump_archive_index (Elf *elf, const char *fname)
+{
+ size_t narsym;
+ const Elf_Arsym *arsym = elf_getarsym (elf, &narsym);
+ if (arsym == NULL)
+ {
+ int result = elf_errno ();
+ if (unlikely (result != ELF_E_NO_INDEX))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get symbol index of archive '%s': %s"),
+ fname, elf_errmsg (result));
+ else
+ printf (gettext ("\nArchive '%s' has no symbol index\n"), fname);
+ return;
+ }
+
+ printf (gettext ("\nIndex of archive '%s' has %zu entries:\n"),
+ fname, narsym);
+
+ size_t as_off = 0;
+ for (const Elf_Arsym *s = arsym; s < &arsym[narsym - 1]; ++s)
+ {
+ if (s->as_off != as_off)
+ {
+ as_off = s->as_off;
+
+ Elf *subelf = NULL;
+ if (unlikely (elf_rand (elf, as_off) == 0)
+ || unlikely ((subelf = elf_begin (-1, ELF_C_READ_MMAP, elf))
+ == NULL))
+#if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 7)
+ while (1)
+#endif
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot extract member at offset %zu in '%s': %s"),
+ as_off, fname, elf_errmsg (-1));
+
+ const Elf_Arhdr *h = elf_getarhdr (subelf);
+
+ printf (gettext ("Archive member '%s' contains:\n"), h->ar_name);
+
+ elf_end (subelf);
+ }
+
+ printf ("\t%s\n", s->as_name);
+ }
+}
+
+#include "debugpred.h"
diff --git a/src/size.c b/src/size.c
new file mode 100644
index 0000000..ad8dbcb
--- /dev/null
+++ b/src/size.c
@@ -0,0 +1,657 @@
+/* Print size information from ELF file.
+ Copyright (C) 2000-2007,2009,2012,2014,2015 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2000.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <libelf.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <system.h>
+#include <printversion.h>
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+
+/* Values for the parameters which have no short form. */
+#define OPT_FORMAT 0x100
+#define OPT_RADIX 0x101
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Output format:"), 0 },
+ { "format", OPT_FORMAT, "FORMAT", 0,
+ N_("Use the output format FORMAT. FORMAT can be `bsd' or `sysv'. "
+ "The default is `bsd'"), 0 },
+ { NULL, 'A', NULL, 0, N_("Same as `--format=sysv'"), 0 },
+ { NULL, 'B', NULL, 0, N_("Same as `--format=bsd'"), 0 },
+ { "radix", OPT_RADIX, "RADIX", 0, N_("Use RADIX for printing symbol values"),
+ 0},
+ { NULL, 'd', NULL, 0, N_("Same as `--radix=10'"), 0 },
+ { NULL, 'o', NULL, 0, N_("Same as `--radix=8'"), 0 },
+ { NULL, 'x', NULL, 0, N_("Same as `--radix=16'"), 0 },
+ { NULL, 'f', NULL, 0,
+ N_("Similar to `--format=sysv' output but in one line"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output options:"), 0 },
+ { NULL, 'F', NULL, 0,
+ N_("Print size and permission flags for loadable segments"), 0 },
+ { "totals", 't', NULL, 0, N_("Display the total sizes (bsd only)"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("\
+List section sizes of FILEs (a.out by default).");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("[FILE...]");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+
+/* Print symbols in file named FNAME. */
+static int process_file (const char *fname);
+
+/* Handle content of archive. */
+static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname);
+
+/* Handle ELF file. */
+static void handle_elf (Elf *elf, const char *fullname, const char *fname);
+
+/* Show total size. */
+static void show_bsd_totals (void);
+
+#define INTERNAL_ERROR(fname) \
+ error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s): %s"), \
+ fname, __LINE__, PACKAGE_VERSION, elf_errmsg (-1))
+
+
+/* User-selectable options. */
+
+/* The selected output format. */
+static enum
+{
+ format_bsd = 0,
+ format_sysv,
+ format_sysv_one_line,
+ format_segments
+} format;
+
+/* Radix for printed numbers. */
+static enum
+{
+ radix_decimal = 0,
+ radix_hex,
+ radix_octal
+} radix;
+
+
+/* Mapping of radix and binary class to length. */
+static const int length_map[2][3] =
+{
+ [ELFCLASS32 - 1] =
+ {
+ [radix_hex] = 8,
+ [radix_decimal] = 10,
+ [radix_octal] = 11
+ },
+ [ELFCLASS64 - 1] =
+ {
+ [radix_hex] = 16,
+ [radix_decimal] = 20,
+ [radix_octal] = 22
+ }
+};
+
+/* True if total sizes should be printed. */
+static bool totals;
+/* To print the total sizes in a reasonable format remember the higest
+ "class" of ELF binaries processed. */
+static int totals_class;
+
+
+int
+main (int argc, char *argv[])
+{
+ int remaining;
+ int result = 0;
+
+ /* We use no threads here which can interfere with handling a stream. */
+ __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+
+ /* Tell the library which version we are expecting. */
+ elf_version (EV_CURRENT);
+
+ if (remaining == argc)
+ /* The user didn't specify a name so we use a.out. */
+ result = process_file ("a.out");
+ else
+ /* Process all the remaining files. */
+ do
+ result |= process_file (argv[remaining]);
+ while (++remaining < argc);
+
+ /* Print the total sizes but only if the output format is BSD and at
+ least one file has been correctly read (i.e., we recognized the
+ class). */
+ if (totals && format == format_bsd && totals_class != 0)
+ show_bsd_totals ();
+
+ return result;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg,
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case 'd':
+ radix = radix_decimal;
+ break;
+
+ case 'f':
+ format = format_sysv_one_line;
+ break;
+
+ case 'o':
+ radix = radix_octal;
+ break;
+
+ case 'x':
+ radix = radix_hex;
+ break;
+
+ case 'A':
+ format = format_sysv;
+ break;
+
+ case 'B':
+ format = format_bsd;
+ break;
+
+ case 'F':
+ format = format_segments;
+ break;
+
+ case OPT_FORMAT:
+ if (strcmp (arg, "bsd") == 0 || strcmp (arg, "berkeley") == 0)
+ format = format_bsd;
+ else if (likely (strcmp (arg, "sysv") == 0))
+ format = format_sysv;
+ else
+ error (EXIT_FAILURE, 0, gettext ("Invalid format: %s"), arg);
+ break;
+
+ case OPT_RADIX:
+ if (strcmp (arg, "x") == 0 || strcmp (arg, "16") == 0)
+ radix = radix_hex;
+ else if (strcmp (arg, "d") == 0 || strcmp (arg, "10") == 0)
+ radix = radix_decimal;
+ else if (strcmp (arg, "o") == 0 || strcmp (arg, "8") == 0)
+ radix = radix_octal;
+ else
+ error (EXIT_FAILURE, 0, gettext ("Invalid radix: %s"), arg);
+ break;
+
+ case 't':
+ totals = true;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+/* Open the file and determine the type. */
+static int
+process_file (const char *fname)
+{
+ int fd = open (fname, O_RDONLY);
+ if (unlikely (fd == -1))
+ {
+ error (0, errno, gettext ("cannot open '%s'"), fname);
+ return 1;
+ }
+
+ /* Now get the ELF descriptor. */
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (likely (elf != NULL))
+ {
+ if (elf_kind (elf) == ELF_K_ELF)
+ {
+ handle_elf (elf, NULL, fname);
+
+ if (unlikely (elf_end (elf) != 0))
+ INTERNAL_ERROR (fname);
+
+ if (unlikely (close (fd) != 0))
+ error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
+
+ return 0;
+ }
+ else if (likely (elf_kind (elf) == ELF_K_AR))
+ {
+ int result = handle_ar (fd, elf, NULL, fname);
+
+ if (unlikely (close (fd) != 0))
+ error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
+
+ return result;
+ }
+
+ /* We cannot handle this type. Close the descriptor anyway. */
+ if (unlikely (elf_end (elf) != 0))
+ INTERNAL_ERROR (fname);
+ }
+
+ if (unlikely (close (fd) != 0))
+ error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
+
+ error (0, 0, gettext ("%s: file format not recognized"), fname);
+
+ return 1;
+}
+
+
+/* Print the BSD-style header. This is done exactly once. */
+static void
+print_header (Elf *elf)
+{
+ static int done;
+
+ if (! done)
+ {
+ int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal];
+ int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex];
+
+ printf ("%*s %*s %*s %*s %*s %s\n",
+ ddigits - 2, sgettext ("bsd|text"),
+ ddigits - 2, sgettext ("bsd|data"),
+ ddigits - 2, sgettext ("bsd|bss"),
+ ddigits - 2, sgettext ("bsd|dec"),
+ xdigits - 2, sgettext ("bsd|hex"),
+ sgettext ("bsd|filename"));
+
+ done = 1;
+ }
+}
+
+
+static int
+handle_ar (int fd, Elf *elf, const char *prefix, const char *fname)
+{
+ size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
+ size_t fname_len = strlen (fname) + 1;
+ char new_prefix[prefix_len + 1 + fname_len];
+ char *cp = new_prefix;
+
+ /* Create the full name of the file. */
+ if (prefix != NULL)
+ {
+ cp = mempcpy (cp, prefix, prefix_len);
+ *cp++ = ':';
+ }
+ memcpy (cp, fname, fname_len);
+
+ /* Process all the files contained in the archive. */
+ int result = 0;
+ Elf *subelf;
+ Elf_Cmd cmd = ELF_C_READ_MMAP;
+ while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
+ {
+ /* The the header for this element. */
+ Elf_Arhdr *arhdr = elf_getarhdr (subelf);
+
+ if (elf_kind (subelf) == ELF_K_ELF)
+ handle_elf (subelf, new_prefix, arhdr->ar_name);
+ else if (likely (elf_kind (subelf) == ELF_K_AR))
+ result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name);
+ /* else signal error??? */
+
+ /* Get next archive element. */
+ cmd = elf_next (subelf);
+ if (unlikely (elf_end (subelf) != 0))
+ INTERNAL_ERROR (fname);
+ }
+
+ if (unlikely (elf_end (elf) != 0))
+ INTERNAL_ERROR (fname);
+
+ return result;
+}
+
+
+/* Show sizes in SysV format. */
+static void
+show_sysv (Elf *elf, const char *prefix, const char *fname,
+ const char *fullname)
+{
+ int maxlen = 10;
+ const int digits = length_map[gelf_getclass (elf) - 1][radix];
+
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ /* First round over the sections: determine the longest section name. */
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr == NULL)
+ INTERNAL_ERROR (fullname);
+
+ /* Ignore all sections which are not used at runtime. */
+ const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
+ if (name != NULL && (shdr->sh_flags & SHF_ALLOC) != 0)
+ maxlen = MAX (maxlen, (int) strlen (name));
+ }
+
+ fputs_unlocked (fname, stdout);
+ if (prefix != NULL)
+ printf (gettext (" (ex %s)"), prefix);
+ printf (":\n%-*s %*s %*s\n",
+ maxlen, sgettext ("sysv|section"),
+ digits - 2, sgettext ("sysv|size"),
+ digits, sgettext ("sysv|addr"));
+
+ /* Iterate over all sections. */
+ GElf_Off total = 0;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ /* Ignore all sections which are not used at runtime. */
+ if ((shdr->sh_flags & SHF_ALLOC) != 0)
+ {
+ printf ((radix == radix_hex
+ ? "%-*s %*" PRIx64 " %*" PRIx64 "\n"
+ : (radix == radix_decimal
+ ? "%-*s %*" PRId64 " %*" PRId64 "\n"
+ : "%-*s %*" PRIo64 " %*" PRIo64 "\n")),
+ maxlen, elf_strptr (elf, shstrndx, shdr->sh_name),
+ digits - 2, shdr->sh_size,
+ digits, shdr->sh_addr);
+
+ total += shdr->sh_size;
+ }
+ }
+
+ if (radix == radix_hex)
+ printf ("%-*s %*" PRIx64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
+ digits - 2, total);
+ else if (radix == radix_decimal)
+ printf ("%-*s %*" PRId64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
+ digits - 2, total);
+ else
+ printf ("%-*s %*" PRIo64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
+ digits - 2, total);
+}
+
+
+/* Show sizes in SysV format in one line. */
+static void
+show_sysv_one_line (Elf *elf)
+{
+ /* Get the section header string table index. */
+ size_t shstrndx;
+ if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+
+ /* Iterate over all sections. */
+ GElf_Off total = 0;
+ bool first = true;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ /* Ignore all sections which are not used at runtime. */
+ if ((shdr->sh_flags & SHF_ALLOC) == 0)
+ continue;
+
+ if (! first)
+ fputs_unlocked (" + ", stdout);
+ first = false;
+
+ printf ((radix == radix_hex ? "%" PRIx64 "(%s)"
+ : (radix == radix_decimal ? "%" PRId64 "(%s)"
+ : "%" PRIo64 "(%s)")),
+ shdr->sh_size, elf_strptr (elf, shstrndx, shdr->sh_name));
+
+ total += shdr->sh_size;
+ }
+
+ if (radix == radix_hex)
+ printf (" = %#" PRIx64 "\n", total);
+ else if (radix == radix_decimal)
+ printf (" = %" PRId64 "\n", total);
+ else
+ printf (" = %" PRIo64 "\n", total);
+}
+
+
+/* Variables to add up the sizes of all files. */
+static uintmax_t total_textsize;
+static uintmax_t total_datasize;
+static uintmax_t total_bsssize;
+
+
+/* Show sizes in BSD format. */
+static void
+show_bsd (Elf *elf, const char *prefix, const char *fname,
+ const char *fullname)
+{
+ GElf_Off textsize = 0;
+ GElf_Off datasize = 0;
+ GElf_Off bsssize = 0;
+ const int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal];
+ const int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex];
+
+ /* Iterate over all sections. */
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr == NULL)
+ INTERNAL_ERROR (fullname);
+
+ /* Ignore all sections which are not marked as loaded. */
+ if ((shdr->sh_flags & SHF_ALLOC) == 0)
+ continue;
+
+ if ((shdr->sh_flags & SHF_WRITE) == 0)
+ textsize += shdr->sh_size;
+ else if (shdr->sh_type == SHT_NOBITS)
+ bsssize += shdr->sh_size;
+ else
+ datasize += shdr->sh_size;
+ }
+
+ printf ("%*" PRId64 " %*" PRId64 " %*" PRId64 " %*" PRId64 " %*"
+ PRIx64 " %s",
+ ddigits - 2, textsize,
+ ddigits - 2, datasize,
+ ddigits - 2, bsssize,
+ ddigits - 2, textsize + datasize + bsssize,
+ xdigits - 2, textsize + datasize + bsssize,
+ fname);
+ if (prefix != NULL)
+ printf (gettext (" (ex %s)"), prefix);
+ fputs_unlocked ("\n", stdout);
+
+ total_textsize += textsize;
+ total_datasize += datasize;
+ total_bsssize += bsssize;
+
+ totals_class = MAX (totals_class, gelf_getclass (elf));
+}
+
+
+/* Show total size. */
+static void
+show_bsd_totals (void)
+{
+ int ddigits = length_map[totals_class - 1][radix_decimal];
+ int xdigits = length_map[totals_class - 1][radix_hex];
+
+ printf ("%*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*"
+ PRIxMAX " %s",
+ ddigits - 2, total_textsize,
+ ddigits - 2, total_datasize,
+ ddigits - 2, total_bsssize,
+ ddigits - 2, total_textsize + total_datasize + total_bsssize,
+ xdigits - 2, total_textsize + total_datasize + total_bsssize,
+ gettext ("(TOTALS)\n"));
+}
+
+
+/* Show size and permission of loadable segments. */
+static void
+show_segments (Elf *elf, const char *fullname)
+{
+ size_t phnum;
+ if (elf_getphdrnum (elf, &phnum) != 0)
+ INTERNAL_ERROR (fullname);
+
+ GElf_Off total = 0;
+ bool first = true;
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr;
+
+ phdr = gelf_getphdr (elf, cnt, &phdr_mem);
+ if (phdr == NULL)
+ INTERNAL_ERROR (fullname);
+
+ if (phdr->p_type != PT_LOAD)
+ /* Only load segments. */
+ continue;
+
+ if (! first)
+ fputs_unlocked (" + ", stdout);
+ first = false;
+
+ printf (radix == radix_hex ? "%" PRIx64 "(%c%c%c)"
+ : (radix == radix_decimal ? "%" PRId64 "(%c%c%c)"
+ : "%" PRIo64 "(%c%c%c)"),
+ phdr->p_memsz,
+ (phdr->p_flags & PF_R) == 0 ? '-' : 'r',
+ (phdr->p_flags & PF_W) == 0 ? '-' : 'w',
+ (phdr->p_flags & PF_X) == 0 ? '-' : 'x');
+
+ total += phdr->p_memsz;
+ }
+
+ if (radix == radix_hex)
+ printf (" = %#" PRIx64 "\n", total);
+ else if (radix == radix_decimal)
+ printf (" = %" PRId64 "\n", total);
+ else
+ printf (" = %" PRIo64 "\n", total);
+}
+
+
+static void
+handle_elf (Elf *elf, const char *prefix, const char *fname)
+{
+ size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
+ size_t fname_len = strlen (fname) + 1;
+ char fullname[prefix_len + 1 + fname_len];
+ char *cp = fullname;
+
+ /* Create the full name of the file. */
+ if (prefix != NULL)
+ {
+ cp = mempcpy (cp, prefix, prefix_len);
+ *cp++ = ':';
+ }
+ memcpy (cp, fname, fname_len);
+
+ if (format == format_sysv)
+ show_sysv (elf, prefix, fname, fullname);
+ else if (format == format_sysv_one_line)
+ show_sysv_one_line (elf);
+ else if (format == format_segments)
+ show_segments (elf, fullname);
+ else
+ {
+ print_header (elf);
+
+ show_bsd (elf, prefix, fname, fullname);
+ }
+}
+
+
+#include "debugpred.h"
diff --git a/src/stack.c b/src/stack.c
new file mode 100644
index 0000000..52ae3a8
--- /dev/null
+++ b/src/stack.c
@@ -0,0 +1,759 @@
+/* Unwinding of frames like gstack/pstack.
+ Copyright (C) 2013-2014 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <assert.h>
+#include <argp.h>
+#include <error.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <string.h>
+#include <locale.h>
+#include <fcntl.h>
+#include ELFUTILS_HEADER(dwfl)
+
+#include <dwarf.h>
+#include <system.h>
+#include <printversion.h>
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+/* non-printable argp options. */
+#define OPT_DEBUGINFO 0x100
+#define OPT_COREFILE 0x101
+
+static bool show_activation = false;
+static bool show_module = false;
+static bool show_build_id = false;
+static bool show_source = false;
+static bool show_one_tid = false;
+static bool show_quiet = false;
+static bool show_raw = false;
+static bool show_modules = false;
+static bool show_debugname = false;
+static bool show_inlines = false;
+
+static int maxframes = 256;
+
+struct frame
+{
+ Dwarf_Addr pc;
+ bool isactivation;
+};
+
+struct frames
+{
+ int frames;
+ int allocated;
+ struct frame *frame;
+};
+
+static Dwfl *dwfl = NULL;
+static pid_t pid = 0;
+static int core_fd = -1;
+static Elf *core = NULL;
+static const char *exec = NULL;
+static char *debuginfo_path = NULL;
+
+static const Dwfl_Callbacks proc_callbacks =
+ {
+ .find_elf = dwfl_linux_proc_find_elf,
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+ };
+
+static const Dwfl_Callbacks core_callbacks =
+ {
+ .find_elf = dwfl_build_id_find_elf,
+ .find_debuginfo = dwfl_standard_find_debuginfo,
+ .debuginfo_path = &debuginfo_path,
+ };
+
+#ifdef USE_DEMANGLE
+static size_t demangle_buffer_len = 0;
+static char *demangle_buffer = NULL;
+#endif
+
+/* Whether any frames have been shown at all. Determines exit status. */
+static bool frames_shown = false;
+
+/* Program exit codes. All frames shown without any errors is GOOD.
+ Some frames shown with some non-fatal errors is an ERROR. A fatal
+ error or no frames shown at all is BAD. A command line USAGE exit
+ is generated by argp_error. */
+#define EXIT_OK 0
+#define EXIT_ERROR 1
+#define EXIT_BAD 2
+#define EXIT_USAGE 64
+
+static int
+get_addr_width (Dwfl_Module *mod)
+{
+ // Try to find the address wide if possible.
+ static int width = 0;
+ if (width == 0 && mod)
+ {
+ Dwarf_Addr bias;
+ Elf *elf = dwfl_module_getelf (mod, &bias);
+ if (elf)
+ {
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr)
+ width = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16;
+ }
+ }
+ if (width == 0)
+ width = 16;
+
+ return width;
+}
+
+static int
+module_callback (Dwfl_Module *mod, void **userdata __attribute__((unused)),
+ const char *name, Dwarf_Addr start,
+ void *arg __attribute__((unused)))
+{
+ /* Forces resolving of main elf and debug files. */
+ Dwarf_Addr bias;
+ Elf *elf = dwfl_module_getelf (mod, &bias);
+ Dwarf *dwarf = dwfl_module_getdwarf (mod, &bias);
+
+ Dwarf_Addr end;
+ const char *mainfile;
+ const char *debugfile;
+ const char *modname = dwfl_module_info (mod, NULL, NULL, &end, NULL,
+ NULL, &mainfile, &debugfile);
+ assert (strcmp (modname, name) == 0);
+
+ int width = get_addr_width (mod);
+ printf ("0x%0*" PRIx64 "-0x%0*" PRIx64 " %s\n",
+ width, start, width, end, basename (name));
+
+ const unsigned char *id;
+ GElf_Addr id_vaddr;
+ int id_len = dwfl_module_build_id (mod, &id, &id_vaddr);
+ if (id_len > 0)
+ {
+ printf (" [");
+ do
+ printf ("%02" PRIx8, *id++);
+ while (--id_len > 0);
+ printf ("]\n");
+ }
+
+ if (elf != NULL)
+ printf (" %s\n", mainfile != NULL ? mainfile : "-");
+ if (dwarf != NULL)
+ printf (" %s\n", debugfile != NULL ? debugfile : "-");
+
+ return DWARF_CB_OK;
+}
+
+static int
+frame_callback (Dwfl_Frame *state, void *arg)
+{
+ struct frames *frames = (struct frames *) arg;
+ int nr = frames->frames;
+ if (! dwfl_frame_pc (state, &frames->frame[nr].pc,
+ &frames->frame[nr].isactivation))
+ return -1;
+
+ frames->frames++;
+ if (frames->frames == maxframes)
+ return DWARF_CB_ABORT;
+
+ if (frames->frames == frames->allocated)
+ {
+ frames->allocated *= 2;
+ frames->frame = realloc (frames->frame,
+ sizeof (struct frame) * frames->allocated);
+ if (frames->frame == NULL)
+ error (EXIT_BAD, errno, "realloc frames.frame");
+ }
+
+ return DWARF_CB_OK;
+}
+
+static const char*
+die_name (Dwarf_Die *die)
+{
+ Dwarf_Attribute attr;
+ const char *name;
+ name = dwarf_formstring (dwarf_attr_integrate (die,
+ DW_AT_MIPS_linkage_name,
+ &attr)
+ ?: dwarf_attr_integrate (die,
+ DW_AT_linkage_name,
+ &attr));
+ if (name == NULL)
+ name = dwarf_diename (die);
+
+ return name;
+}
+
+static void
+print_frame (int nr, Dwarf_Addr pc, bool isactivation,
+ Dwarf_Addr pc_adjusted, Dwfl_Module *mod,
+ const char *symname, Dwarf_Die *cudie,
+ Dwarf_Die *die)
+{
+ int width = get_addr_width (mod);
+ printf ("#%-2u 0x%0*" PRIx64, nr, width, (uint64_t) pc);
+
+ if (show_activation)
+ printf ("%4s", ! isactivation ? "- 1" : "");
+
+ if (symname != NULL)
+ {
+#ifdef USE_DEMANGLE
+ // Require GNU v3 ABI by the "_Z" prefix.
+ if (! show_raw && symname[0] == '_' && symname[1] == 'Z')
+ {
+ int status = -1;
+ char *dsymname = __cxa_demangle (symname, demangle_buffer,
+ &demangle_buffer_len, &status);
+ if (status == 0)
+ symname = demangle_buffer = dsymname;
+ }
+#endif
+ printf (" %s", symname);
+ }
+
+ const char* fname;
+ Dwarf_Addr start;
+ fname = dwfl_module_info(mod, NULL, &start,
+ NULL, NULL, NULL, NULL, NULL);
+ if (show_module)
+ {
+ if (fname != NULL)
+ printf (" - %s", fname);
+ }
+
+ if (show_build_id)
+ {
+ const unsigned char *id;
+ GElf_Addr id_vaddr;
+ int id_len = dwfl_module_build_id (mod, &id, &id_vaddr);
+ if (id_len > 0)
+ {
+ printf ("\n [");
+ do
+ printf ("%02" PRIx8, *id++);
+ while (--id_len > 0);
+ printf ("]@0x%0" PRIx64 "+0x%" PRIx64,
+ start, pc_adjusted - start);
+ }
+ }
+
+ if (show_source)
+ {
+ int line, col;
+ const char* sname;
+ line = col = -1;
+ sname = NULL;
+ if (die != NULL)
+ {
+ Dwarf_Files *files;
+ if (dwarf_getsrcfiles (cudie, &files, NULL) == 0)
+ {
+ Dwarf_Attribute attr;
+ Dwarf_Word val;
+ if (dwarf_formudata (dwarf_attr (die, DW_AT_call_file, &attr),
+ &val) == 0)
+ {
+ sname = dwarf_filesrc (files, val, NULL, NULL);
+ if (dwarf_formudata (dwarf_attr (die, DW_AT_call_line,
+ &attr), &val) == 0)
+ {
+ line = val;
+ if (dwarf_formudata (dwarf_attr (die, DW_AT_call_column,
+ &attr), &val) == 0)
+ col = val;
+ }
+ }
+ }
+ }
+ else
+ {
+ Dwfl_Line *lineobj = dwfl_module_getsrc(mod, pc_adjusted);
+ if (lineobj)
+ sname = dwfl_lineinfo (lineobj, NULL, &line, &col, NULL, NULL);
+ }
+
+ if (sname != NULL)
+ {
+ printf ("\n %s", sname);
+ if (line > 0)
+ {
+ printf (":%d", line);
+ if (col > 0)
+ printf (":%d", col);
+ }
+ }
+ }
+ printf ("\n");
+}
+
+static void
+print_inline_frames (int *nr, Dwarf_Addr pc, bool isactivation,
+ Dwarf_Addr pc_adjusted, Dwfl_Module *mod,
+ const char *symname, Dwarf_Die *cudie, Dwarf_Die *die)
+{
+ Dwarf_Die *scopes = NULL;
+ int nscopes = dwarf_getscopes_die (die, &scopes);
+ if (nscopes > 0)
+ {
+ /* scopes[0] == die, the lowest level, for which we already have
+ the name. This is the actual source location where it
+ happened. */
+ print_frame ((*nr)++, pc, isactivation, pc_adjusted, mod, symname,
+ NULL, NULL);
+
+ /* last_scope is the source location where the next frame/function
+ call was done. */
+ Dwarf_Die *last_scope = &scopes[0];
+ for (int i = 1; i < nscopes && (maxframes == 0 || *nr < maxframes); i++)
+ {
+ Dwarf_Die *scope = &scopes[i];
+ int tag = dwarf_tag (scope);
+ if (tag != DW_TAG_inlined_subroutine
+ && tag != DW_TAG_entry_point
+ && tag != DW_TAG_subprogram)
+ continue;
+
+ symname = die_name (scope);
+ print_frame ((*nr)++, pc, isactivation, pc_adjusted, mod, symname,
+ cudie, last_scope);
+
+ /* Found the "top-level" in which everything was inlined? */
+ if (tag == DW_TAG_subprogram)
+ break;
+
+ last_scope = scope;
+ }
+ }
+ free (scopes);
+}
+
+static void
+print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
+{
+ if (frames->frames > 0)
+ frames_shown = true;
+
+ printf ("TID %lld:\n", (long long) tid);
+ int frame_nr = 0;
+ for (int nr = 0; nr < frames->frames && (maxframes == 0
+ || frame_nr < maxframes); nr++)
+ {
+ Dwarf_Addr pc = frames->frame[nr].pc;
+ bool isactivation = frames->frame[nr].isactivation;
+ Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
+
+ /* Get PC->SYMNAME. */
+ Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
+ const char *symname = NULL;
+ Dwarf_Die die_mem;
+ Dwarf_Die *die = NULL;
+ Dwarf_Die *cudie = NULL;
+ if (mod && ! show_quiet)
+ {
+ if (show_debugname)
+ {
+ Dwarf_Addr bias = 0;
+ Dwarf_Die *scopes = NULL;
+ cudie = dwfl_module_addrdie (mod, pc_adjusted, &bias);
+ int nscopes = dwarf_getscopes (cudie, pc_adjusted - bias,
+ &scopes);
+
+ /* Find the first function-like DIE with a name in scope. */
+ for (int i = 0; symname == NULL && i < nscopes; i++)
+ {
+ Dwarf_Die *scope = &scopes[i];
+ int tag = dwarf_tag (scope);
+ if (tag == DW_TAG_subprogram
+ || tag == DW_TAG_inlined_subroutine
+ || tag == DW_TAG_entry_point)
+ symname = die_name (scope);
+
+ if (symname != NULL)
+ {
+ die_mem = *scope;
+ die = &die_mem;
+ }
+ }
+ free (scopes);
+ }
+
+ if (symname == NULL)
+ symname = dwfl_module_addrname (mod, pc_adjusted);
+ }
+
+ if (show_inlines && die != NULL)
+ print_inline_frames (&frame_nr, pc, isactivation, pc_adjusted, mod,
+ symname, cudie, die);
+ else
+ print_frame (frame_nr++, pc, isactivation, pc_adjusted, mod, symname,
+ NULL, NULL);
+ }
+
+ if (frames->frames > 0 && frame_nr == maxframes)
+ error (0, 0, "tid %lld: shown max number of frames "
+ "(%d, use -n 0 for unlimited)", (long long) tid, maxframes);
+ else if (dwflerr != 0)
+ {
+ if (frames->frames > 0)
+ {
+ unsigned nr = frames->frames - 1;
+ Dwarf_Addr pc = frames->frame[nr].pc;
+ bool isactivation = frames->frame[nr].isactivation;
+ Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
+ Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
+ const char *mainfile = NULL;
+ const char *modname = dwfl_module_info (mod, NULL, NULL, NULL, NULL,
+ NULL, &mainfile, NULL);
+ if (modname == NULL || modname[0] == '\0')
+ {
+ if (mainfile != NULL)
+ modname = mainfile;
+ else
+ modname = "<unknown>";
+ }
+ error (0, 0, "%s tid %lld at 0x%" PRIx64 " in %s: %s", what,
+ (long long) tid, pc_adjusted, modname, dwfl_errmsg (dwflerr));
+ }
+ else
+ error (0, 0, "%s tid %lld: %s", what, (long long) tid,
+ dwfl_errmsg (dwflerr));
+ }
+}
+
+static int
+thread_callback (Dwfl_Thread *thread, void *thread_arg)
+{
+ struct frames *frames = (struct frames *) thread_arg;
+ pid_t tid = dwfl_thread_tid (thread);
+ int err = 0;
+ frames->frames = 0;
+ switch (dwfl_thread_getframes (thread, frame_callback, thread_arg))
+ {
+ case DWARF_CB_OK:
+ case DWARF_CB_ABORT:
+ break;
+ case -1:
+ err = dwfl_errno ();
+ break;
+ default:
+ abort ();
+ }
+ print_frames (frames, tid, err, "dwfl_thread_getframes");
+ return DWARF_CB_OK;
+}
+
+static error_t
+parse_opt (int key, char *arg __attribute__ ((unused)),
+ struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'p':
+ pid = atoi (arg);
+ if (pid == 0)
+ argp_error (state, N_("-p PID should be a positive process id."));
+ break;
+
+ case OPT_COREFILE:
+ core_fd = open (arg, O_RDONLY);
+ if (core_fd < 0)
+ error (EXIT_BAD, errno, N_("Cannot open core file '%s'"), arg);
+ elf_version (EV_CURRENT);
+ core = elf_begin (core_fd, ELF_C_READ_MMAP, NULL);
+ if (core == NULL)
+ error (EXIT_BAD, 0, "core '%s' elf_begin: %s", arg, elf_errmsg(-1));
+ break;
+
+ case 'e':
+ exec = arg;
+ break;
+
+ case OPT_DEBUGINFO:
+ debuginfo_path = arg;
+ break;
+
+ case 'm':
+ show_module = true;
+ break;
+
+ case 's':
+ show_source = true;
+ break;
+
+ case 'a':
+ show_activation = true;
+ break;
+
+ case 'd':
+ show_debugname = true;
+ break;
+
+ case 'i':
+ show_inlines = show_debugname = true;
+ break;
+
+ case 'v':
+ show_activation = show_source = show_module = show_debugname = true;
+ show_inlines = true;
+ break;
+
+ case 'b':
+ show_build_id = true;
+ break;
+
+ case 'q':
+ show_quiet = true;
+ break;
+
+ case 'r':
+ show_raw = true;
+ break;
+
+ case '1':
+ show_one_tid = true;
+ break;
+
+ case 'n':
+ maxframes = atoi (arg);
+ if (maxframes < 0)
+ {
+ argp_error (state, N_("-n MAXFRAMES should be 0 or higher."));
+ return EINVAL;
+ }
+ break;
+
+ case 'l':
+ show_modules = true;
+ break;
+
+ case ARGP_KEY_END:
+ if (core == NULL && exec != NULL)
+ argp_error (state,
+ N_("-e EXEC needs a core given by --core."));
+
+ if (pid == 0 && show_one_tid == true)
+ argp_error (state,
+ N_("-1 needs a thread id given by -p."));
+
+ if ((pid == 0 && core == NULL) || (pid != 0 && core != NULL))
+ argp_error (state,
+ N_("One of -p PID or --core COREFILE should be given."));
+
+ if (pid != 0)
+ {
+ dwfl = dwfl_begin (&proc_callbacks);
+ if (dwfl == NULL)
+ error (EXIT_BAD, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+
+ int err = dwfl_linux_proc_report (dwfl, pid);
+ if (err < 0)
+ error (EXIT_BAD, 0, "dwfl_linux_proc_report pid %lld: %s",
+ (long long) pid, dwfl_errmsg (-1));
+ else if (err > 0)
+ error (EXIT_BAD, err, "dwfl_linux_proc_report pid %lld",
+ (long long) pid);
+ }
+
+ if (core != NULL)
+ {
+ dwfl = dwfl_begin (&core_callbacks);
+ if (dwfl == NULL)
+ error (EXIT_BAD, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+ if (dwfl_core_file_report (dwfl, core, exec) < 0)
+ error (EXIT_BAD, 0, "dwfl_core_file_report: %s", dwfl_errmsg (-1));
+ }
+
+ if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+ error (EXIT_BAD, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+
+ if (pid != 0)
+ {
+ int err = dwfl_linux_proc_attach (dwfl, pid, false);
+ if (err < 0)
+ error (EXIT_BAD, 0, "dwfl_linux_proc_attach pid %lld: %s",
+ (long long) pid, dwfl_errmsg (-1));
+ else if (err > 0)
+ error (EXIT_BAD, err, "dwfl_linux_proc_attach pid %lld",
+ (long long) pid);
+ }
+
+ if (core != NULL)
+ {
+ if (dwfl_core_file_attach (dwfl, core) < 0)
+ error (EXIT_BAD, 0, "dwfl_core_file_report: %s", dwfl_errmsg (-1));
+ }
+
+ /* Makes sure we are properly attached. */
+ if (dwfl_pid (dwfl) < 0)
+ error (EXIT_BAD, 0, "dwfl_pid: %s\n", dwfl_errmsg (-1));
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ /* We use no threads here which can interfere with handling a stream. */
+ __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ const struct argp_option options[] =
+ {
+ { NULL, 0, NULL, 0, N_("Input selection options:"), 0 },
+ { "pid", 'p', "PID", 0,
+ N_("Show stack of process PID"), 0 },
+ { "core", OPT_COREFILE, "COREFILE", 0,
+ N_("Show stack found in COREFILE"), 0 },
+ { "executable", 'e', "EXEC", 0, N_("(optional) EXECUTABLE that produced COREFILE"), 0 },
+ { "debuginfo-path", OPT_DEBUGINFO, "PATH", 0,
+ N_("Search path for separate debuginfo files"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output selection options:"), 0 },
+ { "activation", 'a', NULL, 0,
+ N_("Additionally show frame activation"), 0 },
+ { "debugname", 'd', NULL, 0,
+ N_("Additionally try to lookup DWARF debuginfo name for frame address"),
+ 0 },
+ { "inlines", 'i', NULL, 0,
+ N_("Additionally show inlined function frames using DWARF debuginfo if available (implies -d)"), 0 },
+ { "module", 'm', NULL, 0,
+ N_("Additionally show module file information"), 0 },
+ { "source", 's', NULL, 0,
+ N_("Additionally show source file information"), 0 },
+ { "verbose", 'v', NULL, 0,
+ N_("Show all additional information (activation, debugname, inlines, module and source)"), 0 },
+ { "quiet", 'q', NULL, 0,
+ N_("Do not resolve address to function symbol name"), 0 },
+ { "raw", 'r', NULL, 0,
+ N_("Show raw function symbol names, do not try to demangle names"), 0 },
+ { "build-id", 'b', NULL, 0,
+ N_("Show module build-id, load address and pc offset"), 0 },
+ { NULL, '1', NULL, 0,
+ N_("Show the backtrace of only one thread"), 0 },
+ { NULL, 'n', "MAXFRAMES", 0,
+ N_("Show at most MAXFRAMES per thread (default 256, use 0 for unlimited)"), 0 },
+ { "list-modules", 'l', NULL, 0,
+ N_("Show module memory map with build-id, elf and debug files detected"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+ };
+
+ const struct argp argp =
+ {
+ .options = options,
+ .parser = parse_opt,
+ .doc = N_("Print a stack for each thread in a process or core file.\n\
+\n\
+Program exits with return code 0 if all frames were shown without \
+any errors. If some frames were shown, but there were some non-fatal \
+errors, possibly causing an incomplete backtrace, the program exits \
+with return code 1. If no frames could be shown, or a fatal error \
+occured the program exits with return code 2. If the program was \
+invoked with bad or missing arguments it will exit with return code 64.")
+ };
+
+ argp_parse (&argp, argc, argv, 0, NULL, NULL);
+
+ if (show_modules)
+ {
+ printf ("PID %lld - %s module memory map\n", (long long) dwfl_pid (dwfl),
+ pid != 0 ? "process" : "core");
+ if (dwfl_getmodules (dwfl, module_callback, NULL, 0) != 0)
+ error (EXIT_BAD, 0, "dwfl_getmodules: %s", dwfl_errmsg (-1));
+ }
+
+ struct frames frames;
+ /* When maxframes is zero, then 2048 is just the initial allocation
+ that will be increased using realloc in framecallback (). */
+ frames.allocated = maxframes == 0 ? 2048 : maxframes;
+ frames.frames = 0;
+ frames.frame = malloc (sizeof (struct frame) * frames.allocated);
+ if (frames.frame == NULL)
+ error (EXIT_BAD, errno, "malloc frames.frame");
+
+ if (show_one_tid)
+ {
+ int err = 0;
+ switch (dwfl_getthread_frames (dwfl, pid, frame_callback, &frames))
+ {
+ case DWARF_CB_OK:
+ case DWARF_CB_ABORT:
+ break;
+ case -1:
+ err = dwfl_errno ();
+ break;
+ default:
+ abort ();
+ }
+ print_frames (&frames, pid, err, "dwfl_getthread_frames");
+ }
+ else
+ {
+ printf ("PID %lld - %s\n", (long long) dwfl_pid (dwfl),
+ pid != 0 ? "process" : "core");
+ switch (dwfl_getthreads (dwfl, thread_callback, &frames))
+ {
+ case DWARF_CB_OK:
+ case DWARF_CB_ABORT:
+ break;
+ case -1:
+ error (0, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1));
+ break;
+ default:
+ abort ();
+ }
+ }
+ free (frames.frame);
+ dwfl_end (dwfl);
+
+ if (core != NULL)
+ elf_end (core);
+
+ if (core_fd != -1)
+ close (core_fd);
+
+#ifdef USE_DEMANGLE
+ free (demangle_buffer);
+#endif
+
+ if (! frames_shown)
+ error (EXIT_BAD, 0, N_("Couldn't show any frames."));
+
+ return error_message_count != 0 ? EXIT_ERROR : EXIT_OK;
+}
diff --git a/src/strings.c b/src/strings.c
new file mode 100644
index 0000000..03d0f13
--- /dev/null
+++ b/src/strings.c
@@ -0,0 +1,748 @@
+/* Print the strings of printable characters in files.
+ Copyright (C) 2005-2010, 2012, 2014 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2005.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <ctype.h>
+#include <endian.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <libeu.h>
+#include <system.h>
+#include <printversion.h>
+
+#ifndef MAP_POPULATE
+# define MAP_POPULATE 0
+#endif
+
+
+/* Prototypes of local functions. */
+static int read_fd (int fd, const char *fname, off_t fdlen);
+static int read_elf (Elf *elf, int fd, const char *fname, off_t fdlen);
+
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Output Selection:"), 0 },
+ { "all", 'a', NULL, 0, N_("Scan entire file, not only loaded sections"), 0 },
+ { "bytes", 'n', "MIN-LEN", 0,
+ N_("Only NUL-terminated sequences of MIN-LEN characters or more are printed"), 0 },
+ { "encoding", 'e', "SELECTOR", 0, N_("\
+Select character size and endianess: s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit"),
+ 0},
+ { "print-file-name", 'f', NULL, 0,
+ N_("Print name of the file before each string."), 0 },
+ { "radix", 't', "{o,d,x}", 0,
+ N_("Print location of the string in base 8, 10, or 16 respectively."), 0 },
+ { NULL, 'o', NULL, 0, N_("Alias for --radix=o"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("\
+Print the strings of printable characters in files.");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("[FILE...]");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+
+/* Global variables. */
+
+/* True if whole file and not only loaded sections are looked at. */
+static bool entire_file;
+
+/* Minimum length of any sequence reported. */
+static size_t min_len = 4;
+
+/* Number of bytes per character. */
+static size_t bytes_per_char = 1;
+
+/* Minimum length of any sequence reported in bytes. */
+static size_t min_len_bytes;
+
+/* True if multibyte characters are in big-endian order. */
+static bool big_endian;
+
+/* True unless 7-bit ASCII are expected. */
+static bool char_7bit;
+
+/* True if file names should be printed before strings. */
+static bool print_file_name;
+
+/* Radix for printed numbers. */
+static enum
+{
+ radix_none = 0,
+ radix_decimal,
+ radix_hex,
+ radix_octal
+} radix = radix_none;
+
+
+/* Page size in use. */
+static size_t ps;
+
+
+/* Mapped parts of the ELF file. */
+static unsigned char *elfmap;
+static unsigned char *elfmap_base;
+static size_t elfmap_size;
+static off_t elfmap_off;
+
+
+int
+main (int argc, char *argv[])
+{
+ /* We use no threads. */
+ __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ (void) textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ int remaining;
+ (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
+
+ /* Tell the library which version we are expecting. */
+ elf_version (EV_CURRENT);
+
+ /* Determine the page size. We will likely need it a couple of times. */
+ ps = sysconf (_SC_PAGESIZE);
+
+ struct stat st;
+ int result = 0;
+ if (remaining == argc)
+ /* We read from standard input. This we cannot do for a
+ structured file. */
+ result = read_fd (STDIN_FILENO,
+ print_file_name ? "{standard input}" : NULL,
+ (fstat (STDIN_FILENO, &st) == 0 && S_ISREG (st.st_mode))
+ ? st.st_size : INT64_C (0x7fffffffffffffff));
+ else
+ do
+ {
+ int fd = (strcmp (argv[remaining], "-") == 0
+ ? STDIN_FILENO : open (argv[remaining], O_RDONLY));
+ if (unlikely (fd == -1))
+ {
+ error (0, errno, gettext ("cannot open '%s'"), argv[remaining]);
+ result = 1;
+ }
+ else
+ {
+ const char *fname = print_file_name ? argv[remaining] : NULL;
+ int fstat_fail = fstat (fd, &st);
+ off_t fdlen = (fstat_fail
+ ? INT64_C (0x7fffffffffffffff) : st.st_size);
+ if (fdlen > (off_t) min_len_bytes)
+ {
+ Elf *elf = NULL;
+ if (entire_file
+ || fstat_fail
+ || !S_ISREG (st.st_mode)
+ || (elf = elf_begin (fd, ELF_C_READ, NULL)) == NULL
+ || elf_kind (elf) != ELF_K_ELF)
+ result |= read_fd (fd, fname, fdlen);
+ else
+ result |= read_elf (elf, fd, fname, fdlen);
+
+ /* This call will succeed even if ELF is NULL. */
+ elf_end (elf);
+ }
+
+ if (strcmp (argv[remaining], "-") != 0)
+ close (fd);
+ }
+
+ if (elfmap != NULL && elfmap != MAP_FAILED)
+ munmap (elfmap, elfmap_size);
+ elfmap = NULL;
+ }
+ while (++remaining < argc);
+
+ return result;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg,
+ struct argp_state *state __attribute__ ((unused)))
+{
+ switch (key)
+ {
+ case 'a':
+ entire_file = true;
+ break;
+
+ case 'e':
+ /* We expect a string of one character. */
+ switch (arg[1] != '\0' ? '\0' : arg[0])
+ {
+ case 's':
+ case 'S':
+ char_7bit = arg[0] == 's';
+ bytes_per_char = 1;
+ break;
+
+ case 'b':
+ case 'B':
+ big_endian = true;
+ FALLTHROUGH;
+
+ case 'l':
+ case 'L':
+ bytes_per_char = isupper (arg[0]) ? 4 : 2;
+ break;
+
+ default:
+ error (0, 0, gettext ("invalid value '%s' for %s parameter"),
+ arg, "-e");
+ argp_help (&argp, stderr, ARGP_HELP_SEE, "strings");
+ return ARGP_ERR_UNKNOWN;
+ }
+ break;
+
+ case 'f':
+ print_file_name = true;
+ break;
+
+ case 'n':
+ min_len = atoi (arg);
+ break;
+
+ case 'o':
+ goto octfmt;
+
+ case 't':
+ switch (arg[0])
+ {
+ case 'd':
+ radix = radix_decimal;
+ break;
+
+ case 'o':
+ octfmt:
+ radix = radix_octal;
+ break;
+
+ case 'x':
+ radix = radix_hex;
+ break;
+
+ default:
+ error (0, 0, gettext ("invalid value '%s' for %s parameter"),
+ arg, "-t");
+ argp_help (&argp, stderr, ARGP_HELP_SEE, "strings");
+ return ARGP_ERR_UNKNOWN;
+ }
+ break;
+
+ case ARGP_KEY_FINI:
+ /* Compute the length in bytes of any match. */
+ if (min_len <= 0 || min_len > INT_MAX / bytes_per_char)
+ error (EXIT_FAILURE, 0,
+ gettext ("invalid minimum length of matched string size"));
+ min_len_bytes = min_len * bytes_per_char;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+static void
+process_chunk_mb (const char *fname, const unsigned char *buf, off_t to,
+ size_t len, char **unprinted)
+{
+ size_t curlen = *unprinted == NULL ? 0 : strlen (*unprinted);
+ const unsigned char *start = buf;
+ while (len >= bytes_per_char)
+ {
+ uint32_t ch;
+
+ if (bytes_per_char == 2)
+ {
+ if (big_endian)
+ ch = buf[0] << 8 | buf[1];
+ else
+ ch = buf[1] << 8 | buf[0];
+ }
+ else
+ {
+ if (big_endian)
+ ch = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
+ else
+ ch = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
+ }
+
+ if (ch <= 255 && (isprint (ch) || ch == '\t'))
+ {
+ ++buf;
+ ++curlen;
+ }
+ else
+ {
+ if (curlen >= min_len)
+ {
+ /* We found a match. */
+ if (unlikely (fname != NULL))
+ {
+ fputs_unlocked (fname, stdout);
+ fputs_unlocked (": ", stdout);
+ }
+
+ if (unlikely (radix != radix_none))
+ printf ((radix == radix_octal ? "%7" PRIo64 " "
+ : (radix == radix_decimal ? "%7" PRId64 " "
+ : "%7" PRIx64 " ")),
+ (int64_t) to - len - (buf - start));
+
+ if (unlikely (*unprinted != NULL))
+ {
+ fputs_unlocked (*unprinted, stdout);
+ free (*unprinted);
+ *unprinted = NULL;
+ }
+
+ /* There is no sane way of printing the string. If we
+ assume the file data is encoded in UCS-2/UTF-16 or
+ UCS-4/UTF-32 respectively we could covert the string.
+ But there is no such guarantee. */
+ fwrite_unlocked (start, 1, buf - start, stdout);
+ putc_unlocked ('\n', stdout);
+ }
+
+ start = ++buf;
+ curlen = 0;
+
+ if (len <= min_len)
+ break;
+ }
+
+ --len;
+ }
+
+ if (curlen != 0)
+ *unprinted = xstrndup ((const char *) start, curlen);
+}
+
+
+static void
+process_chunk (const char *fname, const unsigned char *buf, off_t to,
+ size_t len, char **unprinted)
+{
+ /* We are not going to slow the check down for the 2- and 4-byte
+ encodings. Handle them special. */
+ if (unlikely (bytes_per_char != 1))
+ {
+ process_chunk_mb (fname, buf, to, len, unprinted);
+ return;
+ }
+
+ size_t curlen = *unprinted == NULL ? 0 : strlen (*unprinted);
+ const unsigned char *start = buf;
+ while (len > 0)
+ {
+ if ((isprint (*buf) || *buf == '\t') && (! char_7bit || *buf <= 127))
+ {
+ ++buf;
+ ++curlen;
+ }
+ else
+ {
+ if (curlen >= min_len)
+ {
+ /* We found a match. */
+ if (likely (fname != NULL))
+ {
+ fputs_unlocked (fname, stdout);
+ fputs_unlocked (": ", stdout);
+ }
+
+ if (likely (radix != radix_none))
+ printf ((radix == radix_octal ? "%7" PRIo64 " "
+ : (radix == radix_decimal ? "%7" PRId64 " "
+ : "%7" PRIx64 " ")),
+ (int64_t) to - len - (buf - start));
+
+ if (unlikely (*unprinted != NULL))
+ {
+ fputs_unlocked (*unprinted, stdout);
+ free (*unprinted);
+ *unprinted = NULL;
+ }
+ fwrite_unlocked (start, 1, buf - start, stdout);
+ putc_unlocked ('\n', stdout);
+ }
+
+ start = ++buf;
+ curlen = 0;
+
+ if (len <= min_len)
+ break;
+ }
+
+ --len;
+ }
+
+ if (curlen != 0)
+ *unprinted = xstrndup ((const char *) start, curlen);
+}
+
+
+/* Map a file in as large chunks as possible. */
+static void *
+map_file (int fd, off_t start_off, off_t fdlen, size_t *map_sizep)
+{
+ /* Maximum size we mmap. We use an #ifdef to avoid overflows on
+ 32-bit machines. 64-bit machines these days do not have usable
+ address spaces larger than about 43 bits. Not that any file
+ should be that large. */
+# if SIZE_MAX > 0xffffffff
+ const size_t mmap_max = 0x4000000000lu;
+# else
+ const size_t mmap_max = 0x40000000lu;
+# endif
+
+ /* Try to mmap the file. */
+ size_t map_size = MIN ((off_t) mmap_max, fdlen);
+ const size_t map_size_min = MAX (MAX (SIZE_MAX / 16, 2 * ps),
+ roundup (2 * min_len_bytes + 1, ps));
+ void *mem;
+ while (1)
+ {
+ /* We map the memory for reading only here. Since we will
+ always look at every byte of the file it makes sense to
+ use MAP_POPULATE. */
+ mem = mmap (NULL, map_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE,
+ fd, start_off);
+ if (mem != MAP_FAILED)
+ {
+ /* We will go through the mapping sequentially. */
+ (void) posix_madvise (mem, map_size, POSIX_MADV_SEQUENTIAL);
+ break;
+ }
+ if (errno != EINVAL && errno != ENOMEM)
+ /* This is an error other than the lack of address space. */
+ break;
+
+ /* Maybe the size of the mapping is too big. Try again. */
+ map_size /= 2;
+ if (map_size < map_size_min)
+ /* That size should have fit. */
+ break;
+ }
+
+ *map_sizep = map_size;
+ return mem;
+}
+
+
+/* Read the file without mapping. */
+static int
+read_block_no_mmap (int fd, const char *fname, off_t from, off_t fdlen)
+{
+ char *unprinted = NULL;
+#define CHUNKSIZE 65536
+ unsigned char *buf = xmalloc (CHUNKSIZE + min_len_bytes
+ + bytes_per_char - 1);
+ size_t ntrailer = 0;
+ int result = 0;
+ while (fdlen > 0)
+ {
+ ssize_t n = TEMP_FAILURE_RETRY (read (fd, buf + ntrailer,
+ MIN (fdlen, CHUNKSIZE)));
+ if (n == 0)
+ {
+ /* There are less than MIN_LEN+1 bytes left so there cannot be
+ another match. */
+ assert (unprinted == NULL || ntrailer == 0);
+ break;
+ }
+ if (unlikely (n < 0))
+ {
+ /* Something went wrong. */
+ result = 1;
+ break;
+ }
+
+ /* Account for the number of bytes read in this round. */
+ fdlen -= n;
+
+ /* Do not use the signed N value. Note that the addition cannot
+ overflow. */
+ size_t nb = (size_t) n + ntrailer;
+ if (nb >= min_len_bytes)
+ {
+ /* We only use complete characters. */
+ nb &= ~(bytes_per_char - 1);
+
+ process_chunk (fname, buf, from + nb, nb, &unprinted);
+
+ /* If the last bytes of the buffer (modulo the character
+ size) have been printed we are not copying them. */
+ size_t to_keep = unprinted != NULL ? 0 : min_len_bytes;
+
+ memmove (buf, buf + nb - to_keep, to_keep);
+ ntrailer = to_keep;
+ from += nb;
+ }
+ else
+ ntrailer = nb;
+ }
+
+ free (buf);
+
+ /* Don't print anything we collected so far. There is no
+ terminating NUL byte. */
+ free (unprinted);
+
+ return result;
+}
+
+
+static int
+read_block (int fd, const char *fname, off_t fdlen, off_t from, off_t to)
+{
+ if (elfmap == NULL)
+ {
+ /* We need a completely new mapping. */
+ elfmap_off = from & ~(ps - 1);
+ elfmap_base = elfmap = map_file (fd, elfmap_off, fdlen, &elfmap_size);
+
+ if (unlikely (elfmap == MAP_FAILED))
+ /* Let the kernel know we are going to read everything in sequence. */
+ (void) posix_fadvise (fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+ }
+
+ if (unlikely (elfmap == MAP_FAILED))
+ {
+ /* Read from the file descriptor. For this we must position the
+ read pointer. */
+ // XXX Eventually add flag which avoids this if the position
+ // XXX is known to match.
+ if (from != 0 && lseek (fd, from, SEEK_SET) != from)
+ error (EXIT_FAILURE, errno, gettext ("lseek failed"));
+
+ return read_block_no_mmap (fd, fname, from, to - from);
+ }
+
+ assert ((off_t) min_len_bytes < fdlen);
+
+ if (to < (off_t) elfmap_off || from > (off_t) (elfmap_off + elfmap_size))
+ {
+ /* The existing mapping cannot fit at all. Map the new area.
+ We always map the full range of ELFMAP_SIZE bytes even if
+ this extend beyond the end of the file. The Linux kernel
+ handles this OK if the access pages are not touched. */
+ elfmap_off = from & ~(ps - 1);
+ if (mmap (elfmap, elfmap_size, PROT_READ,
+ MAP_PRIVATE | MAP_POPULATE | MAP_FIXED, fd, from)
+ == MAP_FAILED)
+ error (EXIT_FAILURE, errno, gettext ("re-mmap failed"));
+ elfmap_base = elfmap;
+ }
+
+ char *unprinted = NULL;
+
+ /* Use the existing mapping as much as possible. If necessary, map
+ new pages. */
+ if (from >= (off_t) elfmap_off
+ && from < (off_t) (elfmap_off + elfmap_size))
+ /* There are at least a few bytes in this mapping which we can
+ use. */
+ process_chunk (fname, elfmap_base + (from - elfmap_off),
+ MIN (to, (off_t) (elfmap_off + elfmap_size)),
+ MIN (to, (off_t) (elfmap_off + elfmap_size)) - from,
+ &unprinted);
+
+ if (to > (off_t) (elfmap_off + elfmap_size))
+ {
+ unsigned char *remap_base = elfmap_base;
+ size_t read_now = elfmap_size - (elfmap_base - elfmap);
+
+ assert (from >= (off_t) elfmap_off
+ && from < (off_t) (elfmap_off + elfmap_size));
+ off_t handled_to = elfmap_off + elfmap_size;
+ assert (elfmap == elfmap_base
+ || (elfmap_base - elfmap
+ == (ptrdiff_t) ((min_len_bytes + ps - 1) & ~(ps - 1))));
+ if (elfmap == elfmap_base)
+ {
+ size_t keep_area = (min_len_bytes + ps - 1) & ~(ps - 1);
+ assert (elfmap_size >= keep_area + ps);
+ /* The keep area is used for the content of the previous
+ buffer we have to keep. This means copying those bytes
+ and for this we have to make the data writable. */
+ if (unlikely (mprotect (elfmap, keep_area, PROT_READ | PROT_WRITE)
+ != 0))
+ error (EXIT_FAILURE, errno, gettext ("mprotect failed"));
+
+ elfmap_base = elfmap + keep_area;
+ }
+
+ while (1)
+ {
+ /* Map the rest of the file, eventually again in pieces.
+ We speed things up with a nice Linux feature. Note
+ that we have at least two pages mapped. */
+ size_t to_keep = unprinted != NULL ? 0 : min_len_bytes;
+
+ assert (read_now >= to_keep);
+ memmove (elfmap_base - to_keep,
+ remap_base + read_now - to_keep, to_keep);
+ remap_base = elfmap_base;
+
+ assert ((elfmap_size - (elfmap_base - elfmap)) % bytes_per_char
+ == 0);
+ read_now = MIN (to - handled_to,
+ (ptrdiff_t) elfmap_size - (elfmap_base - elfmap));
+
+ assert (handled_to % ps == 0);
+ assert (handled_to % bytes_per_char == 0);
+ if (mmap (remap_base, read_now, PROT_READ,
+ MAP_PRIVATE | MAP_POPULATE | MAP_FIXED, fd, handled_to)
+ == MAP_FAILED)
+ error (EXIT_FAILURE, errno, gettext ("re-mmap failed"));
+ elfmap_off = handled_to;
+
+ process_chunk (fname, remap_base - to_keep,
+ elfmap_off + (read_now & ~(bytes_per_char - 1)),
+ to_keep + (read_now & ~(bytes_per_char - 1)),
+ &unprinted);
+ handled_to += read_now;
+ if (handled_to >= to)
+ break;
+ }
+ }
+
+ /* Don't print anything we collected so far. There is no
+ terminating NUL byte. */
+ free (unprinted);
+
+ return 0;
+}
+
+
+static int
+read_fd (int fd, const char *fname, off_t fdlen)
+{
+ return read_block (fd, fname, fdlen, 0, fdlen);
+}
+
+
+static int
+read_elf (Elf *elf, int fd, const char *fname, off_t fdlen)
+{
+ assert (fdlen >= 0);
+
+ /* We will look at each section separately. The ELF file is not
+ mmapped. The libelf implementation will load the needed parts on
+ demand. Since we only interate over the section header table the
+ memory consumption at this stage is kept minimal. */
+ Elf_Scn *scn = elf_nextscn (elf, NULL);
+ if (scn == NULL)
+ return read_fd (fd, fname, fdlen);
+
+ int result = 0;
+ do
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ /* Only look in sections which are loaded at runtime and
+ actually have content. */
+ if (shdr != NULL && shdr->sh_type != SHT_NOBITS
+ && (shdr->sh_flags & SHF_ALLOC) != 0)
+ {
+ if (shdr->sh_offset > (Elf64_Off) fdlen
+ || fdlen - shdr->sh_offset < shdr->sh_size)
+ {
+ size_t strndx = 0;
+ const char *sname;
+ if (unlikely (elf_getshdrstrndx (elf, &strndx) < 0))
+ sname = "<unknown>";
+ else
+ sname = elf_strptr (elf, strndx, shdr->sh_name) ?: "<unknown>";
+ error (0, 0,
+ gettext ("Skipping section %zd '%s' data outside file"),
+ elf_ndxscn (scn), sname);
+ result = 1;
+ }
+ else
+ result |= read_block (fd, fname, fdlen, shdr->sh_offset,
+ shdr->sh_offset + shdr->sh_size);
+ }
+ }
+ while ((scn = elf_nextscn (elf, scn)) != NULL);
+
+ if (elfmap != NULL && elfmap != MAP_FAILED)
+ munmap (elfmap, elfmap_size);
+ elfmap = NULL;
+
+ return result;
+}
+
+
+#include "debugpred.h"
diff --git a/src/strip.c b/src/strip.c
new file mode 100644
index 0000000..773ed54
--- /dev/null
+++ b/src/strip.c
@@ -0,0 +1,2427 @@
+/* Discard section not used at runtime from object files.
+ Copyright (C) 2000-2012, 2014, 2015, 2016, 2017 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2000.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <endian.h>
+#include <error.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <gelf.h>
+#include <libelf.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <elf-knowledge.h>
+#include <libebl.h>
+#include "libdwelf.h"
+#include <libeu.h>
+#include <system.h>
+#include <printversion.h>
+
+typedef uint8_t GElf_Byte;
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+
+/* Values for the parameters which have no short form. */
+#define OPT_REMOVE_COMMENT 0x100
+#define OPT_PERMISSIVE 0x101
+#define OPT_STRIP_SECTIONS 0x102
+#define OPT_RELOC_DEBUG 0x103
+#define OPT_KEEP_SECTION 0x104
+
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Output selection:"), 0 },
+ { "output", 'o', "FILE", 0, N_("Place stripped output into FILE"), 0 },
+ { NULL, 'f', "FILE", 0, N_("Extract the removed sections into FILE"), 0 },
+ { NULL, 'F', "FILE", 0, N_("Embed name FILE instead of -f argument"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output options:"), 0 },
+ { "strip-all", 's', NULL, OPTION_HIDDEN, NULL, 0 },
+ { "strip-debug", 'g', NULL, 0, N_("Remove all debugging symbols"), 0 },
+ { NULL, 'd', NULL, OPTION_ALIAS, NULL, 0 },
+ { NULL, 'S', NULL, OPTION_ALIAS, NULL, 0 },
+ { "strip-sections", OPT_STRIP_SECTIONS, NULL, 0,
+ N_("Remove section headers (not recommended)"), 0 },
+ { "preserve-dates", 'p', NULL, 0,
+ N_("Copy modified/access timestamps to the output"), 0 },
+ { "reloc-debug-sections", OPT_RELOC_DEBUG, NULL, 0,
+ N_("Resolve all trivial relocations between debug sections if the removed sections are placed in a debug file (only relevant for ET_REL files, operation is not reversable, needs -f)"), 0 },
+ { "remove-comment", OPT_REMOVE_COMMENT, NULL, 0,
+ N_("Remove .comment section"), 0 },
+ { "remove-section", 'R', "SECTION", 0, N_("Remove the named section. SECTION is an extended wildcard pattern. May be given more than once. Only non-allocated sections can be removed."), 0 },
+ { "keep-section", OPT_KEEP_SECTION, "SECTION", 0, N_("Keep the named section. SECTION is an extended wildcard pattern. May be given more than once."), 0 },
+ { "permissive", OPT_PERMISSIVE, NULL, 0,
+ N_("Relax a few rules to handle slightly broken ELF files"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+/* Short description of program. */
+static const char doc[] = N_("Discard symbols from object files.");
+
+/* Strings for arguments in help texts. */
+static const char args_doc[] = N_("[FILE...]");
+
+/* Prototype for option handler. */
+static error_t parse_opt (int key, char *arg, struct argp_state *state);
+
+/* Data structure to communicate with argp functions. */
+static struct argp argp =
+{
+ options, parse_opt, args_doc, doc, NULL, NULL, NULL
+};
+
+
+/* Print symbols in file named FNAME. */
+static int process_file (const char *fname);
+
+/* Handle one ELF file. */
+static int handle_elf (int fd, Elf *elf, const char *prefix,
+ const char *fname, mode_t mode, struct timespec tvp[2]);
+
+/* Handle all files contained in the archive. */
+static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
+ struct timespec tvp[2]) __attribute__ ((unused));
+
+static int debug_fd = -1;
+static char *tmp_debug_fname = NULL;
+
+/* Close debug file descriptor, if opened. And remove temporary debug file. */
+static void cleanup_debug (void);
+
+#define INTERNAL_ERROR(fname) \
+ do { \
+ cleanup_debug (); \
+ error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s): %s"), \
+ fname, __LINE__, PACKAGE_VERSION, elf_errmsg (-1)); \
+ } while (0)
+
+
+/* Name of the output file. */
+static const char *output_fname;
+
+/* Name of the debug output file. */
+static const char *debug_fname;
+
+/* Name to pretend the debug output file has. */
+static const char *debug_fname_embed;
+
+/* If true output files shall have same date as the input file. */
+static bool preserve_dates;
+
+/* If true .comment sections will be removed. */
+static bool remove_comment;
+
+/* If true remove all debug sections. */
+static bool remove_debug;
+
+/* If true remove all section headers. */
+static bool remove_shdrs;
+
+/* If true relax some ELF rules for input files. */
+static bool permissive;
+
+/* If true perform relocations between debug sections. */
+static bool reloc_debug;
+
+/* Sections the user explicitly wants to keep or remove. */
+struct section_pattern
+{
+ char *pattern;
+ struct section_pattern *next;
+};
+
+static struct section_pattern *keep_secs = NULL;
+static struct section_pattern *remove_secs = NULL;
+
+static void
+add_pattern (struct section_pattern **patterns, const char *pattern)
+{
+ struct section_pattern *p = xmalloc (sizeof *p);
+ p->pattern = xstrdup (pattern);
+ p->next = *patterns;
+ *patterns = p;
+}
+
+static void
+free_sec_patterns (struct section_pattern *patterns)
+{
+ struct section_pattern *pattern = patterns;
+ while (pattern != NULL)
+ {
+ struct section_pattern *p = pattern;
+ pattern = p->next;
+ free (p->pattern);
+ free (p);
+ }
+}
+
+static void
+free_patterns (void)
+{
+ free_sec_patterns (keep_secs);
+ free_sec_patterns (remove_secs);
+}
+
+static bool
+section_name_matches (struct section_pattern *patterns, const char *name)
+{
+ struct section_pattern *pattern = patterns;
+ while (pattern != NULL)
+ {
+ if (fnmatch (pattern->pattern, name, FNM_EXTMATCH) == 0)
+ return true;
+ pattern = pattern->next;
+ }
+ return false;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int remaining;
+ int result = 0;
+
+ /* We use no threads here which can interfere with handling a stream. */
+ __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0)
+ return EXIT_FAILURE;
+
+ if (reloc_debug && debug_fname == NULL)
+ error (EXIT_FAILURE, 0,
+ gettext ("--reloc-debug-sections used without -f"));
+
+ /* Tell the library which version we are expecting. */
+ elf_version (EV_CURRENT);
+
+ if (remaining == argc)
+ /* The user didn't specify a name so we use a.out. */
+ result = process_file ("a.out");
+ else
+ {
+ /* If we have seen the '-o' or '-f' option there must be exactly one
+ input file. */
+ if ((output_fname != NULL || debug_fname != NULL)
+ && remaining + 1 < argc)
+ error (EXIT_FAILURE, 0, gettext ("\
+Only one input file allowed together with '-o' and '-f'"));
+
+ /* Process all the remaining files. */
+ do
+ result |= process_file (argv[remaining]);
+ while (++remaining < argc);
+ }
+
+ free_patterns ();
+ return result;
+}
+
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'f':
+ if (debug_fname != NULL)
+ {
+ error (0, 0, gettext ("-f option specified twice"));
+ return EINVAL;
+ }
+ debug_fname = arg;
+ break;
+
+ case 'F':
+ if (debug_fname_embed != NULL)
+ {
+ error (0, 0, gettext ("-F option specified twice"));
+ return EINVAL;
+ }
+ debug_fname_embed = arg;
+ break;
+
+ case 'o':
+ if (output_fname != NULL)
+ {
+ error (0, 0, gettext ("-o option specified twice"));
+ return EINVAL;
+ }
+ output_fname = arg;
+ break;
+
+ case 'p':
+ preserve_dates = true;
+ break;
+
+ case OPT_RELOC_DEBUG:
+ reloc_debug = true;
+ break;
+
+ case OPT_REMOVE_COMMENT:
+ remove_comment = true;
+ break;
+
+ case 'R':
+ if (fnmatch (arg, ".comment", FNM_EXTMATCH) == 0)
+ remove_comment = true;
+ add_pattern (&remove_secs, arg);
+ break;
+
+ case OPT_KEEP_SECTION:
+ add_pattern (&keep_secs, arg);
+ break;
+
+ case 'g':
+ case 'd':
+ case 'S':
+ remove_debug = true;
+ break;
+
+ case OPT_STRIP_SECTIONS:
+ remove_shdrs = true;
+ break;
+
+ case OPT_PERMISSIVE:
+ permissive = true;
+ break;
+
+ case 's': /* Ignored for compatibility. */
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ if (remove_comment == true
+ && section_name_matches (keep_secs, ".comment"))
+ {
+ argp_error (state,
+ gettext ("cannot both keep and remove .comment section"));
+ return EINVAL;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+static int
+process_file (const char *fname)
+{
+ /* If we have to preserve the modify and access timestamps get them
+ now. We cannot use fstat() after opening the file since the open
+ would change the access time. */
+ struct stat pre_st;
+ struct timespec tv[2];
+ again:
+ if (preserve_dates)
+ {
+ if (stat (fname, &pre_st) != 0)
+ {
+ error (0, errno, gettext ("cannot stat input file '%s'"), fname);
+ return 1;
+ }
+
+ /* If we have to preserve the timestamp, we need it in the
+ format utimes() understands. */
+ tv[0] = pre_st.st_atim;
+ tv[1] = pre_st.st_mtim;
+ }
+
+ /* Open the file. */
+ int fd = open (fname, output_fname == NULL ? O_RDWR : O_RDONLY);
+ if (fd == -1)
+ {
+ error (0, errno, gettext ("while opening '%s'"), fname);
+ return 1;
+ }
+
+ /* We always use fstat() even if we called stat() before. This is
+ done to make sure the information returned by stat() is for the
+ same file. */
+ struct stat st;
+ if (fstat (fd, &st) != 0)
+ {
+ error (0, errno, gettext ("cannot stat input file '%s'"), fname);
+ return 1;
+ }
+ /* Paranoid mode on. */
+ if (preserve_dates
+ && (st.st_ino != pre_st.st_ino || st.st_dev != pre_st.st_dev))
+ {
+ /* We detected a race. Try again. */
+ close (fd);
+ goto again;
+ }
+
+ /* Now get the ELF descriptor. */
+ Elf *elf = elf_begin (fd, output_fname == NULL ? ELF_C_RDWR : ELF_C_READ,
+ NULL);
+ int result;
+ switch (elf_kind (elf))
+ {
+ case ELF_K_ELF:
+ result = handle_elf (fd, elf, NULL, fname, st.st_mode & ACCESSPERMS,
+ preserve_dates ? tv : NULL);
+ break;
+
+ case ELF_K_AR:
+ /* It is not possible to strip the content of an archive direct
+ the output to a specific file. */
+ if (unlikely (output_fname != NULL || debug_fname != NULL))
+ {
+ error (0, 0, gettext ("%s: cannot use -o or -f when stripping archive"),
+ fname);
+ result = 1;
+ }
+ else
+ {
+ /* We would like to support ar archives, but currently it just
+ doesn't work at all since we call elf_clone on the members
+ which doesn't really support ar members.
+ result = handle_ar (fd, elf, NULL, fname,
+ preserve_dates ? tv : NULL);
+ */
+ error (0, 0, gettext ("%s: no support for stripping archive"),
+ fname);
+ result = 1;
+ }
+ break;
+
+ default:
+ error (0, 0, gettext ("%s: File format not recognized"), fname);
+ result = 1;
+ break;
+ }
+
+ if (unlikely (elf_end (elf) != 0))
+ INTERNAL_ERROR (fname);
+
+ close (fd);
+
+ return result;
+}
+
+
+/* Maximum size of array allocated on stack. */
+#define MAX_STACK_ALLOC (400 * 1024)
+
+static int
+handle_elf (int fd, Elf *elf, const char *prefix, const char *fname,
+ mode_t mode, struct timespec tvp[2])
+{
+ size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
+ size_t fname_len = strlen (fname) + 1;
+ char *fullname = alloca (prefix_len + 1 + fname_len);
+ char *cp = fullname;
+ Elf *debugelf = NULL;
+ tmp_debug_fname = NULL;
+ int result = 0;
+ size_t shdridx = 0;
+ size_t shstrndx;
+ struct shdr_info
+ {
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ Elf_Data *debug_data;
+ const char *name;
+ Elf32_Word idx; /* Index in new file. */
+ Elf32_Word old_sh_link; /* Original value of shdr.sh_link. */
+ Elf32_Word symtab_idx;
+ Elf32_Word version_idx;
+ Elf32_Word group_idx;
+ Elf32_Word group_cnt;
+ Elf_Scn *newscn;
+ Dwelf_Strent *se;
+ Elf32_Word *newsymidx;
+ } *shdr_info = NULL;
+ Elf_Scn *scn;
+ size_t cnt;
+ size_t idx;
+ bool changes;
+ GElf_Ehdr newehdr_mem;
+ GElf_Ehdr *newehdr;
+ GElf_Ehdr debugehdr_mem;
+ GElf_Ehdr *debugehdr;
+ Dwelf_Strtab *shst = NULL;
+ Elf_Data debuglink_crc_data;
+ bool any_symtab_changes = false;
+ Elf_Data *shstrtab_data = NULL;
+ void *debuglink_buf = NULL;
+
+ /* Create the full name of the file. */
+ if (prefix != NULL)
+ {
+ cp = mempcpy (cp, prefix, prefix_len);
+ *cp++ = ':';
+ }
+ memcpy (cp, fname, fname_len);
+
+ /* If we are not replacing the input file open a new file here. */
+ if (output_fname != NULL)
+ {
+ fd = open (output_fname, O_RDWR | O_CREAT, mode);
+ if (unlikely (fd == -1))
+ {
+ error (0, errno, gettext ("cannot open '%s'"), output_fname);
+ return 1;
+ }
+ }
+
+ debug_fd = -1;
+
+ /* Get the EBL handling. Removing all debugging symbols with the -g
+ option or resolving all relocations between debug sections with
+ the --reloc-debug-sections option are currently the only reasons
+ we need EBL so don't open the backend unless necessary. */
+ Ebl *ebl = NULL;
+ if (remove_debug || reloc_debug)
+ {
+ ebl = ebl_openbackend (elf);
+ if (ebl == NULL)
+ {
+ error (0, errno, gettext ("cannot open EBL backend"));
+ result = 1;
+ goto fail;
+ }
+ }
+
+ /* Open the additional file the debug information will be stored in. */
+ if (debug_fname != NULL)
+ {
+ /* Create a temporary file name. We do not want to overwrite
+ the debug file if the file would not contain any
+ information. */
+ size_t debug_fname_len = strlen (debug_fname);
+ tmp_debug_fname = (char *) xmalloc (debug_fname_len + sizeof (".XXXXXX"));
+ strcpy (mempcpy (tmp_debug_fname, debug_fname, debug_fname_len),
+ ".XXXXXX");
+
+ debug_fd = mkstemp (tmp_debug_fname);
+ if (unlikely (debug_fd == -1))
+ {
+ error (0, errno, gettext ("cannot open '%s'"), debug_fname);
+ result = 1;
+ goto fail;
+ }
+ }
+
+ /* Get the information from the old file. */
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ INTERNAL_ERROR (fname);
+
+ /* Get the section header string table index. */
+ if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot get section header string table index"));
+ }
+
+ /* Get the number of phdrs in the old file. */
+ size_t phnum;
+ if (elf_getphdrnum (elf, &phnum) != 0)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0, gettext ("cannot get number of phdrs"));
+ }
+
+ /* We now create a new ELF descriptor for the same file. We
+ construct it almost exactly in the same way with some information
+ dropped. */
+ Elf *newelf;
+ if (output_fname != NULL)
+ newelf = elf_begin (fd, ELF_C_WRITE_MMAP, NULL);
+ else
+ newelf = elf_clone (elf, ELF_C_EMPTY);
+
+ if (unlikely (gelf_newehdr (newelf, gelf_getclass (elf)) == 0)
+ || (ehdr->e_type != ET_REL
+ && unlikely (gelf_newphdr (newelf, phnum) == 0)))
+ {
+ error (0, 0, gettext ("cannot create new file '%s': %s"),
+ output_fname ?: fname, elf_errmsg (-1));
+ goto fail;
+ }
+
+ /* Copy over the old program header if needed. */
+ if (ehdr->e_type != ET_REL)
+ for (cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
+ if (phdr == NULL
+ || unlikely (gelf_update_phdr (newelf, cnt, phdr) == 0))
+ INTERNAL_ERROR (fname);
+ }
+
+ if (debug_fname != NULL)
+ {
+ /* Also create an ELF descriptor for the debug file */
+ debugelf = elf_begin (debug_fd, ELF_C_WRITE_MMAP, NULL);
+ if (unlikely (gelf_newehdr (debugelf, gelf_getclass (elf)) == 0)
+ || (ehdr->e_type != ET_REL
+ && unlikely (gelf_newphdr (debugelf, phnum) == 0)))
+ {
+ error (0, 0, gettext ("cannot create new file '%s': %s"),
+ debug_fname, elf_errmsg (-1));
+ goto fail_close;
+ }
+
+ /* Copy over the old program header if needed. */
+ if (ehdr->e_type != ET_REL)
+ for (cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (elf, cnt, &phdr_mem);
+ if (phdr == NULL
+ || unlikely (gelf_update_phdr (debugelf, cnt, phdr) == 0))
+ INTERNAL_ERROR (fname);
+ }
+ }
+
+ /* Number of sections. */
+ size_t shnum;
+ if (unlikely (elf_getshdrnum (elf, &shnum) < 0))
+ {
+ error (0, 0, gettext ("cannot determine number of sections: %s"),
+ elf_errmsg (-1));
+ goto fail_close;
+ }
+
+ if (shstrndx >= shnum)
+ goto illformed;
+
+#define elf_assert(test) do { if (!(test)) goto illformed; } while (0)
+
+ /* Storage for section information. We leave room for two more
+ entries since we unconditionally create a section header string
+ table. Maybe some weird tool created an ELF file without one.
+ The other one is used for the debug link section. */
+ if ((shnum + 2) * sizeof (struct shdr_info) > MAX_STACK_ALLOC)
+ shdr_info = (struct shdr_info *) xcalloc (shnum + 2,
+ sizeof (struct shdr_info));
+ else
+ {
+ shdr_info = (struct shdr_info *) alloca ((shnum + 2)
+ * sizeof (struct shdr_info));
+ memset (shdr_info, '\0', (shnum + 2) * sizeof (struct shdr_info));
+ }
+
+ /* Prepare section information data structure. */
+ scn = NULL;
+ cnt = 1;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ /* This should always be true (i.e., there should not be any
+ holes in the numbering). */
+ elf_assert (elf_ndxscn (scn) == cnt);
+
+ shdr_info[cnt].scn = scn;
+
+ /* Get the header. */
+ if (gelf_getshdr (scn, &shdr_info[cnt].shdr) == NULL)
+ INTERNAL_ERROR (fname);
+
+ /* Get the name of the section. */
+ shdr_info[cnt].name = elf_strptr (elf, shstrndx,
+ shdr_info[cnt].shdr.sh_name);
+ if (shdr_info[cnt].name == NULL)
+ {
+ illformed:
+ error (0, 0, gettext ("illformed file '%s'"), fname);
+ goto fail_close;
+ }
+
+ /* Sanity check the user. */
+ if (section_name_matches (remove_secs, shdr_info[cnt].name))
+ {
+ if ((shdr_info[cnt].shdr.sh_flags & SHF_ALLOC) != 0)
+ {
+ error (0, 0,
+ gettext ("Cannot remove allocated section '%s'"),
+ shdr_info[cnt].name);
+ result = 1;
+ goto fail_close;
+ }
+
+ if (section_name_matches (keep_secs, shdr_info[cnt].name))
+ {
+ error (0, 0,
+ gettext ("Cannot both keep and remove section '%s'"),
+ shdr_info[cnt].name);
+ result = 1;
+ goto fail_close;
+ }
+ }
+
+ /* Mark them as present but not yet investigated. */
+ shdr_info[cnt].idx = 1;
+
+ /* Remember the shdr.sh_link value. */
+ shdr_info[cnt].old_sh_link = shdr_info[cnt].shdr.sh_link;
+ if (shdr_info[cnt].old_sh_link >= shnum)
+ goto illformed;
+
+ /* Sections in files other than relocatable object files which
+ not loaded can be freely moved by us. In theory we can also
+ freely move around allocated nobits sections. But we don't
+ to keep the layout of all allocated sections as similar as
+ possible to the original file. In relocatable object files
+ everything can be moved. */
+ if (ehdr->e_type == ET_REL
+ || (shdr_info[cnt].shdr.sh_flags & SHF_ALLOC) == 0)
+ shdr_info[cnt].shdr.sh_offset = 0;
+
+ /* If this is an extended section index table store an
+ appropriate reference. */
+ if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB_SHNDX))
+ {
+ elf_assert (shdr_info[shdr_info[cnt].shdr.sh_link].symtab_idx == 0);
+ shdr_info[shdr_info[cnt].shdr.sh_link].symtab_idx = cnt;
+ }
+ else if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_GROUP))
+ {
+ /* Cross-reference the sections contained in the section
+ group. */
+ shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL);
+ if (shdr_info[cnt].data == NULL
+ || shdr_info[cnt].data->d_size < sizeof (Elf32_Word))
+ INTERNAL_ERROR (fname);
+
+ /* XXX Fix for unaligned access. */
+ Elf32_Word *grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf;
+ size_t inner;
+ for (inner = 1;
+ inner < shdr_info[cnt].data->d_size / sizeof (Elf32_Word);
+ ++inner)
+ {
+ if (grpref[inner] < shnum)
+ shdr_info[grpref[inner]].group_idx = cnt;
+ else
+ goto illformed;
+ }
+
+ if (inner == 1 || (inner == 2 && (grpref[0] & GRP_COMDAT) == 0))
+ /* If the section group contains only one element and this
+ is n COMDAT section we can drop it right away. */
+ shdr_info[cnt].idx = 0;
+ else
+ shdr_info[cnt].group_cnt = inner - 1;
+ }
+ else if (unlikely (shdr_info[cnt].shdr.sh_type == SHT_GNU_versym))
+ {
+ elf_assert (shdr_info[shdr_info[cnt].shdr.sh_link].version_idx == 0);
+ shdr_info[shdr_info[cnt].shdr.sh_link].version_idx = cnt;
+ }
+
+ /* If this section is part of a group make sure it is not
+ discarded right away. */
+ if ((shdr_info[cnt].shdr.sh_flags & SHF_GROUP) != 0)
+ {
+ elf_assert (shdr_info[cnt].group_idx != 0);
+
+ if (shdr_info[shdr_info[cnt].group_idx].idx == 0)
+ {
+ /* The section group section will be removed. */
+ shdr_info[cnt].group_idx = 0;
+ shdr_info[cnt].shdr.sh_flags &= ~SHF_GROUP;
+ }
+ }
+
+ /* Increment the counter. */
+ ++cnt;
+ }
+
+ /* Now determine which sections can go away. The general rule is that
+ all sections which are not used at runtime are stripped out. But
+ there are a few exceptions:
+
+ - special sections named ".comment" and ".note" are kept
+ - OS or architecture specific sections are kept since we might not
+ know how to handle them
+ - if a section is referred to from a section which is not removed
+ in the sh_link or sh_info element it cannot be removed either
+ - the user might have explicitly said to remove or keep a section
+ */
+ for (cnt = 1; cnt < shnum; ++cnt)
+ /* Check whether the section can be removed. Since we will create
+ a new .shstrtab assume it will be removed too. */
+ if (remove_shdrs ? !(shdr_info[cnt].shdr.sh_flags & SHF_ALLOC)
+ : (ebl_section_strip_p (ebl, ehdr, &shdr_info[cnt].shdr,
+ shdr_info[cnt].name, remove_comment,
+ remove_debug)
+ || cnt == ehdr->e_shstrndx
+ || section_name_matches (remove_secs, shdr_info[cnt].name)))
+ {
+ /* The user might want to explicitly keep this one. */
+ if (section_name_matches (keep_secs, shdr_info[cnt].name))
+ continue;
+
+ /* For now assume this section will be removed. */
+ shdr_info[cnt].idx = 0;
+
+ idx = shdr_info[cnt].group_idx;
+ while (idx != 0)
+ {
+ /* The section group data is already loaded. */
+ elf_assert (shdr_info[idx].data != NULL
+ && shdr_info[idx].data->d_buf != NULL
+ && shdr_info[idx].data->d_size >= sizeof (Elf32_Word));
+
+ /* If the references section group is a normal section
+ group and has one element remaining, or if it is an
+ empty COMDAT section group it is removed. */
+ bool is_comdat = (((Elf32_Word *) shdr_info[idx].data->d_buf)[0]
+ & GRP_COMDAT) != 0;
+
+ --shdr_info[idx].group_cnt;
+ if ((!is_comdat && shdr_info[idx].group_cnt == 1)
+ || (is_comdat && shdr_info[idx].group_cnt == 0))
+ {
+ shdr_info[idx].idx = 0;
+ /* Continue recursively. */
+ idx = shdr_info[idx].group_idx;
+ }
+ else
+ break;
+ }
+ }
+
+ /* Mark the SHT_NULL section as handled. */
+ shdr_info[0].idx = 2;
+
+
+ /* Handle exceptions: section groups and cross-references. We might
+ have to repeat this a few times since the resetting of the flag
+ might propagate. */
+ do
+ {
+ changes = false;
+
+ for (cnt = 1; cnt < shnum; ++cnt)
+ {
+ if (shdr_info[cnt].idx == 0)
+ {
+ /* If a relocation section is marked as being removed make
+ sure the section it is relocating is removed, too. */
+ if (shdr_info[cnt].shdr.sh_type == SHT_REL
+ || shdr_info[cnt].shdr.sh_type == SHT_RELA)
+ {
+ if (shdr_info[cnt].shdr.sh_info >= shnum)
+ goto illformed;
+ else if (shdr_info[shdr_info[cnt].shdr.sh_info].idx != 0)
+ shdr_info[cnt].idx = 1;
+ }
+
+ /* If a group section is marked as being removed make
+ sure all the sections it contains are being removed, too. */
+ if (shdr_info[cnt].shdr.sh_type == SHT_GROUP)
+ {
+ Elf32_Word *grpref;
+ grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf;
+ for (size_t in = 1;
+ in < shdr_info[cnt].data->d_size / sizeof (Elf32_Word);
+ ++in)
+ if (grpref[in] < shnum)
+ {
+ if (shdr_info[grpref[in]].idx != 0)
+ {
+ shdr_info[cnt].idx = 1;
+ break;
+ }
+ }
+ else
+ goto illformed;
+ }
+ }
+
+ if (shdr_info[cnt].idx == 1)
+ {
+ /* The content of symbol tables we don't remove must not
+ reference any section which we do remove. Otherwise
+ we cannot remove the section. */
+ if (debug_fname != NULL
+ && shdr_info[cnt].debug_data == NULL
+ && (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM
+ || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB))
+ {
+ /* Make sure the data is loaded. */
+ if (shdr_info[cnt].data == NULL)
+ {
+ shdr_info[cnt].data
+ = elf_getdata (shdr_info[cnt].scn, NULL);
+ if (shdr_info[cnt].data == NULL)
+ INTERNAL_ERROR (fname);
+ }
+ Elf_Data *symdata = shdr_info[cnt].data;
+
+ /* If there is an extended section index table load it
+ as well. */
+ if (shdr_info[cnt].symtab_idx != 0
+ && shdr_info[shdr_info[cnt].symtab_idx].data == NULL)
+ {
+ elf_assert (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB);
+
+ shdr_info[shdr_info[cnt].symtab_idx].data
+ = elf_getdata (shdr_info[shdr_info[cnt].symtab_idx].scn,
+ NULL);
+ if (shdr_info[shdr_info[cnt].symtab_idx].data == NULL)
+ INTERNAL_ERROR (fname);
+ }
+ Elf_Data *xndxdata
+ = shdr_info[shdr_info[cnt].symtab_idx].data;
+
+ /* Go through all symbols and make sure the section they
+ reference is not removed. */
+ size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT);
+
+ for (size_t inner = 0;
+ inner < shdr_info[cnt].data->d_size / elsize;
+ ++inner)
+ {
+ GElf_Sym sym_mem;
+ Elf32_Word xndx;
+ GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata,
+ inner, &sym_mem,
+ &xndx);
+ if (sym == NULL)
+ INTERNAL_ERROR (fname);
+
+ size_t scnidx = sym->st_shndx;
+ if (scnidx == SHN_UNDEF || scnidx >= shnum
+ || (scnidx >= SHN_LORESERVE
+ && scnidx <= SHN_HIRESERVE
+ && scnidx != SHN_XINDEX)
+ /* Don't count in the section symbols. */
+ || GELF_ST_TYPE (sym->st_info) == STT_SECTION)
+ /* This is no section index, leave it alone. */
+ continue;
+ else if (scnidx == SHN_XINDEX)
+ scnidx = xndx;
+
+ if (scnidx >= shnum)
+ goto illformed;
+
+ if (shdr_info[scnidx].idx == 0)
+ /* This symbol table has a real symbol in
+ a discarded section. So preserve the
+ original table in the debug file. Unless
+ it is a redundant data marker to a debug
+ (data only) section. */
+ if (! (ebl_section_strip_p (ebl, ehdr,
+ &shdr_info[scnidx].shdr,
+ shdr_info[scnidx].name,
+ remove_comment,
+ remove_debug)
+ && ebl_data_marker_symbol (ebl, sym,
+ elf_strptr (elf,
+ shdr_info[cnt].shdr.sh_link,
+ sym->st_name))))
+ shdr_info[cnt].debug_data = symdata;
+ }
+ }
+
+ /* Cross referencing happens:
+ - for the cases the ELF specification says. That are
+ + SHT_DYNAMIC in sh_link to string table
+ + SHT_HASH in sh_link to symbol table
+ + SHT_REL and SHT_RELA in sh_link to symbol table
+ + SHT_SYMTAB and SHT_DYNSYM in sh_link to string table
+ + SHT_GROUP in sh_link to symbol table
+ + SHT_SYMTAB_SHNDX in sh_link to symbol table
+ Other (OS or architecture-specific) sections might as
+ well use this field so we process it unconditionally.
+ - references inside section groups
+ - specially marked references in sh_info if the SHF_INFO_LINK
+ flag is set
+ */
+
+ if (shdr_info[shdr_info[cnt].shdr.sh_link].idx == 0)
+ {
+ shdr_info[shdr_info[cnt].shdr.sh_link].idx = 1;
+ changes |= shdr_info[cnt].shdr.sh_link < cnt;
+ }
+
+ /* Handle references through sh_info. */
+ if (SH_INFO_LINK_P (&shdr_info[cnt].shdr))
+ {
+ if (shdr_info[cnt].shdr.sh_info >= shnum)
+ goto illformed;
+ else if ( shdr_info[shdr_info[cnt].shdr.sh_info].idx == 0)
+ {
+ shdr_info[shdr_info[cnt].shdr.sh_info].idx = 1;
+ changes |= shdr_info[cnt].shdr.sh_info < cnt;
+ }
+ }
+
+ /* Mark the section as investigated. */
+ shdr_info[cnt].idx = 2;
+ }
+
+ if (debug_fname != NULL
+ && (shdr_info[cnt].idx == 0 || shdr_info[cnt].debug_data != NULL))
+ {
+ /* This section is being preserved in the debug file.
+ Sections it refers to must be preserved there too.
+
+ In this pass we mark sections to be preserved in both
+ files by setting the .debug_data pointer to the original
+ file's .data pointer. Below, we'll copy the section
+ contents. */
+
+ inline void check_preserved (size_t i)
+ {
+ if (i != 0 && i < shnum + 2 && shdr_info[i].idx != 0
+ && shdr_info[i].debug_data == NULL)
+ {
+ if (shdr_info[i].data == NULL)
+ shdr_info[i].data = elf_getdata (shdr_info[i].scn, NULL);
+ if (shdr_info[i].data == NULL)
+ INTERNAL_ERROR (fname);
+
+ shdr_info[i].debug_data = shdr_info[i].data;
+ changes |= i < cnt;
+ }
+ }
+
+ check_preserved (shdr_info[cnt].shdr.sh_link);
+ if (SH_INFO_LINK_P (&shdr_info[cnt].shdr))
+ check_preserved (shdr_info[cnt].shdr.sh_info);
+ }
+ }
+ }
+ while (changes);
+
+ /* Copy the removed sections to the debug output file.
+ The ones that are not removed in the stripped file are SHT_NOBITS. */
+ if (debug_fname != NULL)
+ {
+ for (cnt = 1; cnt < shnum; ++cnt)
+ {
+ scn = elf_newscn (debugelf);
+ if (scn == NULL)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0,
+ gettext ("while generating output file: %s"),
+ elf_errmsg (-1));
+ }
+
+ bool discard_section = (shdr_info[cnt].idx > 0
+ && shdr_info[cnt].debug_data == NULL
+ && shdr_info[cnt].shdr.sh_type != SHT_NOTE
+ && shdr_info[cnt].shdr.sh_type != SHT_GROUP
+ && cnt != ehdr->e_shstrndx);
+
+ /* Set the section header in the new file. */
+ GElf_Shdr debugshdr = shdr_info[cnt].shdr;
+ if (discard_section)
+ debugshdr.sh_type = SHT_NOBITS;
+
+ if (unlikely (gelf_update_shdr (scn, &debugshdr) == 0))
+ /* There cannot be any overflows. */
+ INTERNAL_ERROR (fname);
+
+ /* Get the data from the old file if necessary. */
+ if (shdr_info[cnt].data == NULL)
+ {
+ shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL);
+ if (shdr_info[cnt].data == NULL)
+ INTERNAL_ERROR (fname);
+ }
+
+ /* Set the data. This is done by copying from the old file. */
+ Elf_Data *debugdata = elf_newdata (scn);
+ if (debugdata == NULL)
+ INTERNAL_ERROR (fname);
+
+ /* Copy the structure. This data may be modified in place
+ before we write out the file. */
+ *debugdata = *shdr_info[cnt].data;
+ if (discard_section)
+ debugdata->d_buf = NULL;
+ else if (shdr_info[cnt].debug_data != NULL
+ || shdr_info[cnt].shdr.sh_type == SHT_GROUP)
+ {
+ /* Copy the original data before it gets modified. */
+ shdr_info[cnt].debug_data = debugdata;
+ if (debugdata->d_buf == NULL)
+ INTERNAL_ERROR (fname);
+ debugdata->d_buf = memcpy (xmalloc (debugdata->d_size),
+ debugdata->d_buf, debugdata->d_size);
+ }
+ }
+
+ /* Finish the ELF header. Fill in the fields not handled by
+ libelf from the old file. */
+ debugehdr = gelf_getehdr (debugelf, &debugehdr_mem);
+ if (debugehdr == NULL)
+ INTERNAL_ERROR (fname);
+
+ memcpy (debugehdr->e_ident, ehdr->e_ident, EI_NIDENT);
+ debugehdr->e_type = ehdr->e_type;
+ debugehdr->e_machine = ehdr->e_machine;
+ debugehdr->e_version = ehdr->e_version;
+ debugehdr->e_entry = ehdr->e_entry;
+ debugehdr->e_flags = ehdr->e_flags;
+ debugehdr->e_shstrndx = ehdr->e_shstrndx;
+
+ if (unlikely (gelf_update_ehdr (debugelf, debugehdr) == 0))
+ {
+ error (0, 0, gettext ("%s: error while creating ELF header: %s"),
+ debug_fname, elf_errmsg (-1));
+ result = 1;
+ goto fail_close;
+ }
+ }
+
+ /* Although we always create a new section header string table we
+ don't explicitly mark the existing one as unused. It can still
+ be used through a symbol table section we are keeping. If not it
+ will already be marked as unused. */
+
+ /* We need a string table for the section headers. */
+ shst = dwelf_strtab_init (true);
+ if (shst == NULL)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, errno, gettext ("while preparing output for '%s'"),
+ output_fname ?: fname);
+ }
+
+ /* Assign new section numbers. */
+ shdr_info[0].idx = 0;
+ for (cnt = idx = 1; cnt < shnum; ++cnt)
+ if (shdr_info[cnt].idx > 0)
+ {
+ shdr_info[cnt].idx = idx++;
+
+ /* Create a new section. */
+ shdr_info[cnt].newscn = elf_newscn (newelf);
+ if (shdr_info[cnt].newscn == NULL)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0,
+ gettext ("while generating output file: %s"),
+ elf_errmsg (-1));
+ }
+
+ elf_assert (elf_ndxscn (shdr_info[cnt].newscn) == shdr_info[cnt].idx);
+
+ /* Add this name to the section header string table. */
+ shdr_info[cnt].se = dwelf_strtab_add (shst, shdr_info[cnt].name);
+ }
+
+ /* Test whether we are doing anything at all. Either all removable
+ sections are already gone. Or the only section we would remove is
+ the .shstrtab section which we would add again. */
+ bool removing_sections = !(cnt == idx
+ || (cnt == idx + 1
+ && shdr_info[ehdr->e_shstrndx].idx == 0));
+ if (output_fname == NULL && !removing_sections)
+ goto fail_close;
+
+ /* Create the reference to the file with the debug info (if any). */
+ if (debug_fname != NULL && !remove_shdrs && removing_sections)
+ {
+ /* Add the section header string table section name. */
+ shdr_info[cnt].se = dwelf_strtab_add_len (shst, ".gnu_debuglink", 15);
+ shdr_info[cnt].idx = idx++;
+
+ /* Create the section header. */
+ shdr_info[cnt].shdr.sh_type = SHT_PROGBITS;
+ shdr_info[cnt].shdr.sh_flags = 0;
+ shdr_info[cnt].shdr.sh_addr = 0;
+ shdr_info[cnt].shdr.sh_link = SHN_UNDEF;
+ shdr_info[cnt].shdr.sh_info = SHN_UNDEF;
+ shdr_info[cnt].shdr.sh_entsize = 0;
+ shdr_info[cnt].shdr.sh_addralign = 4;
+ /* We set the offset to zero here. Before we write the ELF file the
+ field must have the correct value. This is done in the final
+ loop over all section. Then we have all the information needed. */
+ shdr_info[cnt].shdr.sh_offset = 0;
+
+ /* Create the section. */
+ shdr_info[cnt].newscn = elf_newscn (newelf);
+ if (shdr_info[cnt].newscn == NULL)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0,
+ gettext ("while create section header section: %s"),
+ elf_errmsg (-1));
+ }
+ elf_assert (elf_ndxscn (shdr_info[cnt].newscn) == shdr_info[cnt].idx);
+
+ shdr_info[cnt].data = elf_newdata (shdr_info[cnt].newscn);
+ if (shdr_info[cnt].data == NULL)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0, gettext ("cannot allocate section data: %s"),
+ elf_errmsg (-1));
+ }
+
+ char *debug_basename = basename (debug_fname_embed ?: debug_fname);
+ off_t crc_offset = strlen (debug_basename) + 1;
+ /* Align to 4 byte boundary */
+ crc_offset = ((crc_offset - 1) & ~3) + 4;
+
+ shdr_info[cnt].data->d_align = 4;
+ shdr_info[cnt].shdr.sh_size = shdr_info[cnt].data->d_size
+ = crc_offset + 4;
+ debuglink_buf = xcalloc (1, shdr_info[cnt].data->d_size);
+ shdr_info[cnt].data->d_buf = debuglink_buf;
+
+ strcpy (shdr_info[cnt].data->d_buf, debug_basename);
+
+ /* Cache this Elf_Data describing the CRC32 word in the section.
+ We'll fill this in when we have written the debug file. */
+ debuglink_crc_data = *shdr_info[cnt].data;
+ debuglink_crc_data.d_buf = ((char *) debuglink_crc_data.d_buf
+ + crc_offset);
+ debuglink_crc_data.d_size = 4;
+
+ /* One more section done. */
+ ++cnt;
+ }
+
+ /* Index of the section header table in the shdr_info array. */
+ shdridx = cnt;
+
+ /* Add the section header string table section name. */
+ shdr_info[cnt].se = dwelf_strtab_add_len (shst, ".shstrtab", 10);
+ shdr_info[cnt].idx = idx;
+
+ /* Create the section header. */
+ shdr_info[cnt].shdr.sh_type = SHT_STRTAB;
+ shdr_info[cnt].shdr.sh_flags = 0;
+ shdr_info[cnt].shdr.sh_addr = 0;
+ shdr_info[cnt].shdr.sh_link = SHN_UNDEF;
+ shdr_info[cnt].shdr.sh_info = SHN_UNDEF;
+ shdr_info[cnt].shdr.sh_entsize = 0;
+ /* We set the offset to zero here. Before we write the ELF file the
+ field must have the correct value. This is done in the final
+ loop over all section. Then we have all the information needed. */
+ shdr_info[cnt].shdr.sh_offset = 0;
+ shdr_info[cnt].shdr.sh_addralign = 1;
+
+ /* Create the section. */
+ shdr_info[cnt].newscn = elf_newscn (newelf);
+ if (shdr_info[cnt].newscn == NULL)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0,
+ gettext ("while create section header section: %s"),
+ elf_errmsg (-1));
+ }
+ elf_assert (elf_ndxscn (shdr_info[cnt].newscn) == idx);
+
+ /* Finalize the string table and fill in the correct indices in the
+ section headers. */
+ shstrtab_data = elf_newdata (shdr_info[cnt].newscn);
+ if (shstrtab_data == NULL)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0,
+ gettext ("while create section header string table: %s"),
+ elf_errmsg (-1));
+ }
+ if (dwelf_strtab_finalize (shst, shstrtab_data) == NULL)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0,
+ gettext ("no memory to create section header string table"));
+ }
+
+ /* We have to set the section size. */
+ shdr_info[cnt].shdr.sh_size = shstrtab_data->d_size;
+
+ /* Update the section information. */
+ GElf_Off lastoffset = 0;
+ for (cnt = 1; cnt <= shdridx; ++cnt)
+ if (shdr_info[cnt].idx > 0)
+ {
+ Elf_Data *newdata;
+
+ scn = elf_getscn (newelf, shdr_info[cnt].idx);
+ elf_assert (scn != NULL);
+
+ /* Update the name. */
+ shdr_info[cnt].shdr.sh_name = dwelf_strent_off (shdr_info[cnt].se);
+
+ /* Update the section header from the input file. Some fields
+ might be section indeces which now have to be adjusted. Keep
+ the index to the "current" sh_link in case we need it to lookup
+ symbol table names. */
+ size_t sh_link = shdr_info[cnt].shdr.sh_link;
+ if (shdr_info[cnt].shdr.sh_link != 0)
+ shdr_info[cnt].shdr.sh_link =
+ shdr_info[shdr_info[cnt].shdr.sh_link].idx;
+
+ if (shdr_info[cnt].shdr.sh_type == SHT_GROUP)
+ {
+ elf_assert (shdr_info[cnt].data != NULL
+ && shdr_info[cnt].data->d_buf != NULL);
+
+ Elf32_Word *grpref = (Elf32_Word *) shdr_info[cnt].data->d_buf;
+ for (size_t inner = 0;
+ inner < shdr_info[cnt].data->d_size / sizeof (Elf32_Word);
+ ++inner)
+ if (grpref[inner] < shnum)
+ grpref[inner] = shdr_info[grpref[inner]].idx;
+ else
+ goto illformed;
+ }
+
+ /* Handle the SHT_REL, SHT_RELA, and SHF_INFO_LINK flag. */
+ if (SH_INFO_LINK_P (&shdr_info[cnt].shdr))
+ shdr_info[cnt].shdr.sh_info =
+ shdr_info[shdr_info[cnt].shdr.sh_info].idx;
+
+ /* Get the data from the old file if necessary. We already
+ created the data for the section header string table. */
+ if (cnt < shnum)
+ {
+ if (shdr_info[cnt].data == NULL)
+ {
+ shdr_info[cnt].data = elf_getdata (shdr_info[cnt].scn, NULL);
+ if (shdr_info[cnt].data == NULL)
+ INTERNAL_ERROR (fname);
+ }
+
+ /* Set the data. This is done by copying from the old file. */
+ newdata = elf_newdata (scn);
+ if (newdata == NULL)
+ INTERNAL_ERROR (fname);
+
+ /* Copy the structure. */
+ *newdata = *shdr_info[cnt].data;
+
+ /* We know the size. */
+ shdr_info[cnt].shdr.sh_size = shdr_info[cnt].data->d_size;
+
+ /* We have to adjust symbol tables. The st_shndx member might
+ have to be updated. */
+ if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM
+ || shdr_info[cnt].shdr.sh_type == SHT_SYMTAB)
+ {
+ Elf_Data *versiondata = NULL;
+ Elf_Data *shndxdata = NULL;
+
+ size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT);
+
+ if (shdr_info[cnt].symtab_idx != 0)
+ {
+ elf_assert (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB_SHNDX);
+ /* This section has extended section information.
+ We have to modify that information, too. */
+ shndxdata = elf_getdata (shdr_info[shdr_info[cnt].symtab_idx].scn,
+ NULL);
+
+ elf_assert (shndxdata != NULL
+ && shndxdata->d_buf != NULL
+ && ((shndxdata->d_size / sizeof (Elf32_Word))
+ >= shdr_info[cnt].data->d_size / elsize));
+ }
+
+ if (shdr_info[cnt].version_idx != 0)
+ {
+ elf_assert (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM);
+ /* This section has associated version
+ information. We have to modify that
+ information, too. */
+ versiondata = elf_getdata (shdr_info[shdr_info[cnt].version_idx].scn,
+ NULL);
+
+ elf_assert (versiondata != NULL
+ && versiondata->d_buf != NULL
+ && ((versiondata->d_size / sizeof (GElf_Versym))
+ >= shdr_info[cnt].data->d_size / elsize));
+ }
+
+ shdr_info[cnt].newsymidx
+ = (Elf32_Word *) xcalloc (shdr_info[cnt].data->d_size
+ / elsize, sizeof (Elf32_Word));
+
+ bool last_was_local = true;
+ size_t destidx;
+ size_t inner;
+ for (destidx = inner = 1;
+ inner < shdr_info[cnt].data->d_size / elsize;
+ ++inner)
+ {
+ Elf32_Word sec;
+ GElf_Sym sym_mem;
+ Elf32_Word xshndx;
+ GElf_Sym *sym = gelf_getsymshndx (shdr_info[cnt].data,
+ shndxdata, inner,
+ &sym_mem, &xshndx);
+ if (sym == NULL)
+ INTERNAL_ERROR (fname);
+
+ if (sym->st_shndx == SHN_UNDEF
+ || (sym->st_shndx >= shnum
+ && sym->st_shndx != SHN_XINDEX))
+ {
+ /* This is no section index, leave it alone
+ unless it is moved. */
+ if (destidx != inner
+ && gelf_update_symshndx (shdr_info[cnt].data,
+ shndxdata,
+ destidx, sym,
+ xshndx) == 0)
+ INTERNAL_ERROR (fname);
+
+ shdr_info[cnt].newsymidx[inner] = destidx++;
+
+ if (last_was_local
+ && GELF_ST_BIND (sym->st_info) != STB_LOCAL)
+ {
+ last_was_local = false;
+ shdr_info[cnt].shdr.sh_info = destidx - 1;
+ }
+
+ continue;
+ }
+
+ /* Get the full section index, if necessary from the
+ XINDEX table. */
+ if (sym->st_shndx == SHN_XINDEX)
+ elf_assert (shndxdata != NULL
+ && shndxdata->d_buf != NULL);
+ size_t sidx = (sym->st_shndx != SHN_XINDEX
+ ? sym->st_shndx : xshndx);
+ sec = shdr_info[sidx].idx;
+
+ if (sec != 0)
+ {
+ GElf_Section nshndx;
+ Elf32_Word nxshndx;
+
+ if (sec < SHN_LORESERVE)
+ {
+ nshndx = sec;
+ nxshndx = 0;
+ }
+ else
+ {
+ nshndx = SHN_XINDEX;
+ nxshndx = sec;
+ }
+
+ elf_assert (sec < SHN_LORESERVE || shndxdata != NULL);
+
+ if ((inner != destidx || nshndx != sym->st_shndx
+ || (shndxdata != NULL && nxshndx != xshndx))
+ && (sym->st_shndx = nshndx,
+ gelf_update_symshndx (shdr_info[cnt].data,
+ shndxdata,
+ destidx, sym,
+ nxshndx) == 0))
+ INTERNAL_ERROR (fname);
+
+ shdr_info[cnt].newsymidx[inner] = destidx++;
+
+ if (last_was_local
+ && GELF_ST_BIND (sym->st_info) != STB_LOCAL)
+ {
+ last_was_local = false;
+ shdr_info[cnt].shdr.sh_info = destidx - 1;
+ }
+ }
+ else if ((shdr_info[cnt].shdr.sh_flags & SHF_ALLOC) != 0
+ && GELF_ST_TYPE (sym->st_info) != STT_SECTION
+ && shdr_info[sidx].shdr.sh_type != SHT_GROUP)
+ {
+ /* Removing a real symbol from an allocated
+ symbol table is hard and probably a
+ mistake. Really removing it means
+ rewriting the dynamic segment and hash
+ sections. Just warn and set the symbol
+ section to UNDEF. */
+ error (0, 0,
+ gettext ("Cannot remove symbol [%zd] from allocated symbol table [%zd]"), inner, cnt);
+ sym->st_shndx = SHN_UNDEF;
+ if (gelf_update_sym (shdr_info[cnt].data, destidx,
+ sym) == 0)
+ INTERNAL_ERROR (fname);
+ shdr_info[cnt].newsymidx[inner] = destidx++;
+ }
+ else if (debug_fname != NULL
+ && shdr_info[cnt].debug_data == NULL)
+ /* The symbol points to a section that is discarded
+ but isn't preserved in the debug file. Check that
+ this is a section or group signature symbol
+ for a section which has been removed. Or a special
+ data marker symbol to a debug section. */
+ {
+ elf_assert (GELF_ST_TYPE (sym->st_info) == STT_SECTION
+ || ((shdr_info[sidx].shdr.sh_type
+ == SHT_GROUP)
+ && (shdr_info[sidx].shdr.sh_info
+ == inner))
+ || ebl_data_marker_symbol (ebl, sym,
+ elf_strptr (elf, sh_link,
+ sym->st_name)));
+ }
+ }
+
+ if (destidx != inner)
+ {
+ /* The size of the symbol table changed. */
+ shdr_info[cnt].shdr.sh_size = newdata->d_size
+ = destidx * elsize;
+ any_symtab_changes = true;
+ }
+ else
+ {
+ /* The symbol table didn't really change. */
+ free (shdr_info[cnt].newsymidx);
+ shdr_info[cnt].newsymidx = NULL;
+ }
+ }
+ }
+
+ /* If we have to, compute the offset of the section. */
+ if (shdr_info[cnt].shdr.sh_offset == 0)
+ shdr_info[cnt].shdr.sh_offset
+ = ((lastoffset + shdr_info[cnt].shdr.sh_addralign - 1)
+ & ~((GElf_Off) (shdr_info[cnt].shdr.sh_addralign - 1)));
+
+ /* Set the section header in the new file. */
+ if (unlikely (gelf_update_shdr (scn, &shdr_info[cnt].shdr) == 0))
+ /* There cannot be any overflows. */
+ INTERNAL_ERROR (fname);
+
+ /* Remember the last section written so far. */
+ GElf_Off filesz = (shdr_info[cnt].shdr.sh_type != SHT_NOBITS
+ ? shdr_info[cnt].shdr.sh_size : 0);
+ if (lastoffset < shdr_info[cnt].shdr.sh_offset + filesz)
+ lastoffset = shdr_info[cnt].shdr.sh_offset + filesz;
+ }
+
+ /* Adjust symbol references if symbol tables changed. */
+ if (any_symtab_changes)
+ /* Find all relocation sections which use this symbol table. */
+ for (cnt = 1; cnt <= shdridx; ++cnt)
+ {
+ /* Update section headers when the data size has changed.
+ We also update the SHT_NOBITS section in the debug
+ file so that the section headers match in sh_size. */
+ inline void update_section_size (const Elf_Data *newdata)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ shdr->sh_size = newdata->d_size;
+ (void) gelf_update_shdr (scn, shdr);
+ if (debugelf != NULL)
+ {
+ /* libelf will use d_size to set sh_size. */
+ Elf_Data *debugdata = elf_getdata (elf_getscn (debugelf,
+ cnt), NULL);
+ if (debugdata == NULL)
+ INTERNAL_ERROR (fname);
+ debugdata->d_size = newdata->d_size;
+ }
+ }
+
+ if (shdr_info[cnt].idx == 0 && debug_fname == NULL)
+ /* Ignore sections which are discarded. When we are saving a
+ relocation section in a separate debug file, we must fix up
+ the symbol table references. */
+ continue;
+
+ const Elf32_Word symtabidx = shdr_info[cnt].old_sh_link;
+ elf_assert (symtabidx < shnum + 2);
+ const Elf32_Word *const newsymidx = shdr_info[symtabidx].newsymidx;
+ switch (shdr_info[cnt].shdr.sh_type)
+ {
+ inline bool no_symtab_updates (void)
+ {
+ /* If the symbol table hasn't changed, do not do anything. */
+ if (shdr_info[symtabidx].newsymidx == NULL)
+ return true;
+
+ /* If the symbol table is not discarded, but additionally
+ duplicated in the separate debug file and this section
+ is discarded, don't adjust anything. */
+ return (shdr_info[cnt].idx == 0
+ && shdr_info[symtabidx].debug_data != NULL);
+ }
+
+ case SHT_REL:
+ case SHT_RELA:
+ if (no_symtab_updates ())
+ break;
+
+ Elf_Data *d = elf_getdata (shdr_info[cnt].idx == 0
+ ? elf_getscn (debugelf, cnt)
+ : elf_getscn (newelf,
+ shdr_info[cnt].idx),
+ NULL);
+ elf_assert (d != NULL && d->d_buf != NULL
+ && shdr_info[cnt].shdr.sh_entsize != 0);
+ size_t nrels = (shdr_info[cnt].shdr.sh_size
+ / shdr_info[cnt].shdr.sh_entsize);
+
+ size_t symsize = gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT);
+ const Elf32_Word symidxn = (shdr_info[symtabidx].data->d_size
+ / symsize);
+ if (shdr_info[cnt].shdr.sh_type == SHT_REL)
+ for (size_t relidx = 0; relidx < nrels; ++relidx)
+ {
+ GElf_Rel rel_mem;
+ if (gelf_getrel (d, relidx, &rel_mem) == NULL)
+ INTERNAL_ERROR (fname);
+
+ size_t symidx = GELF_R_SYM (rel_mem.r_info);
+ elf_assert (symidx < symidxn);
+ if (newsymidx[symidx] != symidx)
+ {
+ rel_mem.r_info
+ = GELF_R_INFO (newsymidx[symidx],
+ GELF_R_TYPE (rel_mem.r_info));
+
+ if (gelf_update_rel (d, relidx, &rel_mem) == 0)
+ INTERNAL_ERROR (fname);
+ }
+ }
+ else
+ for (size_t relidx = 0; relidx < nrels; ++relidx)
+ {
+ GElf_Rela rel_mem;
+ if (gelf_getrela (d, relidx, &rel_mem) == NULL)
+ INTERNAL_ERROR (fname);
+
+ size_t symidx = GELF_R_SYM (rel_mem.r_info);
+ elf_assert (symidx < symidxn);
+ if (newsymidx[symidx] != symidx)
+ {
+ rel_mem.r_info
+ = GELF_R_INFO (newsymidx[symidx],
+ GELF_R_TYPE (rel_mem.r_info));
+
+ if (gelf_update_rela (d, relidx, &rel_mem) == 0)
+ INTERNAL_ERROR (fname);
+ }
+ }
+ break;
+
+ case SHT_HASH:
+ if (no_symtab_updates ())
+ break;
+
+ /* We have to recompute the hash table. */
+
+ elf_assert (shdr_info[cnt].idx > 0);
+
+ /* The hash section in the new file. */
+ scn = elf_getscn (newelf, shdr_info[cnt].idx);
+
+ /* The symbol table data. */
+ Elf_Data *symd = elf_getdata (elf_getscn (newelf,
+ shdr_info[symtabidx].idx),
+ NULL);
+ elf_assert (symd != NULL && symd->d_buf != NULL);
+
+ /* The hash table data. */
+ Elf_Data *hashd = elf_getdata (scn, NULL);
+ elf_assert (hashd != NULL && hashd->d_buf != NULL);
+
+ if (shdr_info[cnt].shdr.sh_entsize == sizeof (Elf32_Word))
+ {
+ /* Sane arches first. */
+ elf_assert (hashd->d_size >= 2 * sizeof (Elf32_Word));
+ Elf32_Word *bucket = (Elf32_Word *) hashd->d_buf;
+
+ size_t strshndx = shdr_info[symtabidx].old_sh_link;
+ size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT);
+
+ Elf32_Word nchain = bucket[1];
+ Elf32_Word nbucket = bucket[0];
+ uint64_t used_buf = ((2ULL + nchain + nbucket)
+ * sizeof (Elf32_Word));
+ elf_assert (used_buf <= hashd->d_size);
+
+ /* Adjust the nchain value. The symbol table size
+ changed. We keep the same size for the bucket array. */
+ bucket[1] = symd->d_size / elsize;
+ bucket += 2;
+ Elf32_Word *chain = bucket + nbucket;
+
+ /* New size of the section. */
+ size_t n_size = ((2 + symd->d_size / elsize + nbucket)
+ * sizeof (Elf32_Word));
+ elf_assert (n_size <= hashd->d_size);
+ hashd->d_size = n_size;
+ update_section_size (hashd);
+
+ /* Clear the arrays. */
+ memset (bucket, '\0',
+ (symd->d_size / elsize + nbucket)
+ * sizeof (Elf32_Word));
+
+ for (size_t inner = shdr_info[symtabidx].shdr.sh_info;
+ inner < symd->d_size / elsize; ++inner)
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem);
+ elf_assert (sym != NULL);
+
+ const char *name = elf_strptr (elf, strshndx,
+ sym->st_name);
+ elf_assert (name != NULL && nbucket != 0);
+ size_t hidx = elf_hash (name) % nbucket;
+
+ if (bucket[hidx] == 0)
+ bucket[hidx] = inner;
+ else
+ {
+ hidx = bucket[hidx];
+
+ while (chain[hidx] != 0 && chain[hidx] < nchain)
+ hidx = chain[hidx];
+
+ chain[hidx] = inner;
+ }
+ }
+ }
+ else
+ {
+ /* Alpha and S390 64-bit use 64-bit SHT_HASH entries. */
+ elf_assert (shdr_info[cnt].shdr.sh_entsize
+ == sizeof (Elf64_Xword));
+
+ Elf64_Xword *bucket = (Elf64_Xword *) hashd->d_buf;
+
+ size_t strshndx = shdr_info[symtabidx].old_sh_link;
+ size_t elsize = gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT);
+
+ elf_assert (symd->d_size >= 2 * sizeof (Elf64_Xword));
+ Elf64_Xword nbucket = bucket[0];
+ Elf64_Xword nchain = bucket[1];
+ uint64_t maxwords = hashd->d_size / sizeof (Elf64_Xword);
+ elf_assert (maxwords >= 2
+ && maxwords - 2 >= nbucket
+ && maxwords - 2 - nbucket >= nchain);
+
+ /* Adjust the nchain value. The symbol table size
+ changed. We keep the same size for the bucket array. */
+ bucket[1] = symd->d_size / elsize;
+ bucket += 2;
+ Elf64_Xword *chain = bucket + nbucket;
+
+ /* New size of the section. */
+ size_t n_size = ((2 + symd->d_size / elsize + nbucket)
+ * sizeof (Elf64_Xword));
+ elf_assert (n_size <= hashd->d_size);
+ hashd->d_size = n_size;
+ update_section_size (hashd);
+
+ /* Clear the arrays. */
+ memset (bucket, '\0',
+ (symd->d_size / elsize + nbucket)
+ * sizeof (Elf64_Xword));
+
+ for (size_t inner = shdr_info[symtabidx].shdr.sh_info;
+ inner < symd->d_size / elsize; ++inner)
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym = gelf_getsym (symd, inner, &sym_mem);
+ elf_assert (sym != NULL);
+
+ const char *name = elf_strptr (elf, strshndx,
+ sym->st_name);
+ elf_assert (name != NULL && nbucket != 0);
+ size_t hidx = elf_hash (name) % nbucket;
+
+ if (bucket[hidx] == 0)
+ bucket[hidx] = inner;
+ else
+ {
+ hidx = bucket[hidx];
+
+ while (chain[hidx] != 0 && chain[hidx] < nchain)
+ hidx = chain[hidx];
+
+ chain[hidx] = inner;
+ }
+ }
+ }
+ break;
+
+ case SHT_GNU_versym:
+ /* If the symbol table changed we have to adjust the entries. */
+ if (no_symtab_updates ())
+ break;
+
+ elf_assert (shdr_info[cnt].idx > 0);
+
+ /* The symbol version section in the new file. */
+ scn = elf_getscn (newelf, shdr_info[cnt].idx);
+
+ /* The symbol table data. */
+ symd = elf_getdata (elf_getscn (newelf, shdr_info[symtabidx].idx),
+ NULL);
+ elf_assert (symd != NULL && symd->d_buf != NULL);
+ size_t symz = gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT);
+ const Elf32_Word syms = (shdr_info[symtabidx].data->d_size / symz);
+
+ /* The version symbol data. */
+ Elf_Data *verd = elf_getdata (scn, NULL);
+ elf_assert (verd != NULL && verd->d_buf != NULL);
+
+ /* The symbol version array. */
+ GElf_Half *verstab = (GElf_Half *) verd->d_buf;
+
+ /* Walk through the list and */
+ size_t elsize = gelf_fsize (elf, verd->d_type, 1, EV_CURRENT);
+ Elf32_Word vers = verd->d_size / elsize;
+ for (size_t inner = 1; inner < vers && inner < syms; ++inner)
+ if (newsymidx[inner] != 0 && newsymidx[inner] < vers)
+ /* Overwriting the same array works since the
+ reordering can only move entries to lower indices
+ in the array. */
+ verstab[newsymidx[inner]] = verstab[inner];
+
+ /* New size of the section. */
+ verd->d_size = gelf_fsize (newelf, verd->d_type,
+ symd->d_size
+ / gelf_fsize (elf, symd->d_type, 1,
+ EV_CURRENT),
+ EV_CURRENT);
+ update_section_size (verd);
+ break;
+
+ case SHT_GROUP:
+ if (no_symtab_updates ())
+ break;
+
+ /* Yes, the symbol table changed.
+ Update the section header of the section group. */
+ scn = elf_getscn (newelf, shdr_info[cnt].idx);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ elf_assert (shdr != NULL);
+
+ size_t symsz = gelf_fsize (elf, ELF_T_SYM, 1, EV_CURRENT);
+ const Elf32_Word symn = (shdr_info[symtabidx].data->d_size
+ / symsz);
+ elf_assert (shdr->sh_info < symn);
+ shdr->sh_info = newsymidx[shdr->sh_info];
+
+ (void) gelf_update_shdr (scn, shdr);
+ break;
+ }
+ }
+
+ /* Remove any relocations between debug sections in ET_REL
+ for the debug file when requested. These relocations are always
+ zero based between the unallocated sections. */
+ if (debug_fname != NULL && removing_sections
+ && reloc_debug && ehdr->e_type == ET_REL)
+ {
+ scn = NULL;
+ cnt = 0;
+ while ((scn = elf_nextscn (debugelf, scn)) != NULL)
+ {
+ cnt++;
+ /* We need the actual section and header from the debugelf
+ not just the cached original in shdr_info because we
+ might want to change the size. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+ {
+ /* Make sure that this relocation section points to a
+ section to relocate with contents, that isn't
+ allocated and that is a debug section. */
+ Elf_Scn *tscn = elf_getscn (debugelf, shdr->sh_info);
+ GElf_Shdr tshdr_mem;
+ GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
+ if (tshdr->sh_type == SHT_NOBITS
+ || tshdr->sh_size == 0
+ || (tshdr->sh_flags & SHF_ALLOC) != 0)
+ continue;
+
+ const char *tname = elf_strptr (debugelf, shstrndx,
+ tshdr->sh_name);
+ if (! tname || ! ebl_debugscn_p (ebl, tname))
+ continue;
+
+ /* OK, lets relocate all trivial cross debug section
+ relocations. */
+ Elf_Data *reldata = elf_getdata (scn, NULL);
+ if (reldata == NULL || reldata->d_buf == NULL)
+ INTERNAL_ERROR (fname);
+
+ /* Make sure we adjust the uncompressed debug data
+ (and recompress if necessary at the end). */
+ GElf_Chdr tchdr;
+ int tcompress_type = 0;
+ if (gelf_getchdr (tscn, &tchdr) != NULL)
+ {
+ tcompress_type = tchdr.ch_type;
+ if (elf_compress (tscn, 0, 0) != 1)
+ INTERNAL_ERROR (fname);
+ }
+
+ Elf_Data *tdata = elf_getdata (tscn, NULL);
+ if (tdata == NULL || tdata->d_buf == NULL
+ || tdata->d_type != ELF_T_BYTE)
+ INTERNAL_ERROR (fname);
+
+ /* Pick up the symbol table and shndx table to
+ resolve relocation symbol indexes. */
+ Elf64_Word symt = shdr->sh_link;
+ Elf_Data *symdata, *xndxdata;
+ elf_assert (symt < shnum + 2);
+ elf_assert (shdr_info[symt].symtab_idx < shnum + 2);
+ symdata = (shdr_info[symt].debug_data
+ ?: shdr_info[symt].data);
+ xndxdata = (shdr_info[shdr_info[symt].symtab_idx].debug_data
+ ?: shdr_info[shdr_info[symt].symtab_idx].data);
+
+ /* Apply one relocation. Returns true when trivial
+ relocation actually done. */
+ bool relocate (GElf_Addr offset, const GElf_Sxword addend,
+ bool is_rela, int rtype, int symndx)
+ {
+ /* R_*_NONE relocs can always just be removed. */
+ if (rtype == 0)
+ return true;
+
+ /* We only do simple absolute relocations. */
+ Elf_Type type = ebl_reloc_simple_type (ebl, rtype);
+ if (type == ELF_T_NUM)
+ return false;
+
+ /* These are the types we can relocate. */
+#define TYPES DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half); \
+ DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword); \
+ DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword)
+
+ /* And only for relocations against other debug sections. */
+ GElf_Sym sym_mem;
+ Elf32_Word xndx;
+ GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata,
+ symndx, &sym_mem,
+ &xndx);
+ Elf32_Word sec = (sym->st_shndx == SHN_XINDEX
+ ? xndx : sym->st_shndx);
+ if (sec >= shnum + 2)
+ INTERNAL_ERROR (fname);
+
+ if (ebl_debugscn_p (ebl, shdr_info[sec].name))
+ {
+ size_t size;
+
+#define DO_TYPE(NAME, Name) GElf_##Name Name;
+ union { TYPES; } tmpbuf;
+#undef DO_TYPE
+
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name) \
+ case ELF_T_##NAME: \
+ size = sizeof (GElf_##Name); \
+ tmpbuf.Name = 0; \
+ break;
+ TYPES;
+#undef DO_TYPE
+ default:
+ return false;
+ }
+
+ if (offset > tdata->d_size
+ || tdata->d_size - offset < size)
+ {
+ cleanup_debug ();
+ error (EXIT_FAILURE, 0, gettext ("bad relocation"));
+ }
+
+ /* When the symbol value is zero then for SHT_REL
+ sections this is all that needs to be checked.
+ The addend is contained in the original data at
+ the offset already. So if the (section) symbol
+ address is zero and the given addend is zero
+ just remove the relocation, it isn't needed
+ anymore. */
+ if (addend == 0 && sym->st_value == 0)
+ return true;
+
+ Elf_Data tmpdata =
+ {
+ .d_type = type,
+ .d_buf = &tmpbuf,
+ .d_size = size,
+ .d_version = EV_CURRENT,
+ };
+ Elf_Data rdata =
+ {
+ .d_type = type,
+ .d_buf = tdata->d_buf + offset,
+ .d_size = size,
+ .d_version = EV_CURRENT,
+ };
+
+ GElf_Addr value = sym->st_value;
+ if (is_rela)
+ {
+ /* For SHT_RELA sections we just take the
+ given addend and add it to the value. */
+ value += addend;
+ }
+ else
+ {
+ /* For SHT_REL sections we have to peek at
+ what is already in the section at the given
+ offset to get the addend. */
+ Elf_Data *d = gelf_xlatetom (debugelf, &tmpdata,
+ &rdata,
+ ehdr->e_ident[EI_DATA]);
+ if (d == NULL)
+ INTERNAL_ERROR (fname);
+ assert (d == &tmpdata);
+ }
+
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name) \
+ case ELF_T_##NAME: \
+ tmpbuf.Name += (GElf_##Name) value; \
+ break;
+ TYPES;
+#undef DO_TYPE
+ default:
+ abort ();
+ }
+
+ /* Now finally put in the new value. */
+ Elf_Data *s = gelf_xlatetof (debugelf, &rdata,
+ &tmpdata,
+ ehdr->e_ident[EI_DATA]);
+ if (s == NULL)
+ INTERNAL_ERROR (fname);
+ assert (s == &rdata);
+
+ return true;
+ }
+ return false;
+ }
+
+ if (shdr->sh_entsize == 0)
+ INTERNAL_ERROR (fname);
+
+ size_t nrels = shdr->sh_size / shdr->sh_entsize;
+ size_t next = 0;
+ if (shdr->sh_type == SHT_REL)
+ for (size_t relidx = 0; relidx < nrels; ++relidx)
+ {
+ GElf_Rel rel_mem;
+ GElf_Rel *r = gelf_getrel (reldata, relidx, &rel_mem);
+ if (! relocate (r->r_offset, 0, false,
+ GELF_R_TYPE (r->r_info),
+ GELF_R_SYM (r->r_info)))
+ {
+ if (relidx != next)
+ gelf_update_rel (reldata, next, r);
+ ++next;
+ }
+ }
+ else
+ for (size_t relidx = 0; relidx < nrels; ++relidx)
+ {
+ GElf_Rela rela_mem;
+ GElf_Rela *r = gelf_getrela (reldata, relidx, &rela_mem);
+ if (! relocate (r->r_offset, r->r_addend, true,
+ GELF_R_TYPE (r->r_info),
+ GELF_R_SYM (r->r_info)))
+ {
+ if (relidx != next)
+ gelf_update_rela (reldata, next, r);
+ ++next;
+ }
+ }
+
+ nrels = next;
+ shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize;
+ gelf_update_shdr (scn, shdr);
+
+ if (tcompress_type != 0)
+ if (elf_compress (tscn, tcompress_type, ELF_CHF_FORCE) != 1)
+ INTERNAL_ERROR (fname);
+ }
+ }
+ }
+
+ /* Now that we have done all adjustments to the data,
+ we can actually write out the debug file. */
+ if (debug_fname != NULL && removing_sections)
+ {
+ /* Finally write the file. */
+ if (unlikely (elf_update (debugelf, ELF_C_WRITE) == -1))
+ {
+ error (0, 0, gettext ("while writing '%s': %s"),
+ tmp_debug_fname, elf_errmsg (-1));
+ result = 1;
+ goto fail_close;
+ }
+
+ /* Create the real output file. First rename, then change the
+ mode. */
+ if (rename (tmp_debug_fname, debug_fname) != 0
+ || fchmod (debug_fd, mode) != 0)
+ {
+ error (0, errno, gettext ("while creating '%s'"), debug_fname);
+ result = 1;
+ goto fail_close;
+ }
+
+ /* The temporary file does not exist anymore. */
+ free (tmp_debug_fname);
+ tmp_debug_fname = NULL;
+
+ if (!remove_shdrs)
+ {
+ uint32_t debug_crc;
+ Elf_Data debug_crc_data =
+ {
+ .d_type = ELF_T_WORD,
+ .d_buf = &debug_crc,
+ .d_size = sizeof (debug_crc),
+ .d_version = EV_CURRENT
+ };
+
+ /* Compute the checksum which we will add to the executable. */
+ if (crc32_file (debug_fd, &debug_crc) != 0)
+ {
+ error (0, errno, gettext ("\
+while computing checksum for debug information"));
+ unlink (debug_fname);
+ result = 1;
+ goto fail_close;
+ }
+
+ /* Store it in the debuglink section data. */
+ if (unlikely (gelf_xlatetof (newelf, &debuglink_crc_data,
+ &debug_crc_data, ehdr->e_ident[EI_DATA])
+ != &debuglink_crc_data))
+ INTERNAL_ERROR (fname);
+ }
+ }
+
+ /* Finally finish the ELF header. Fill in the fields not handled by
+ libelf from the old file. */
+ newehdr = gelf_getehdr (newelf, &newehdr_mem);
+ if (newehdr == NULL)
+ INTERNAL_ERROR (fname);
+
+ memcpy (newehdr->e_ident, ehdr->e_ident, EI_NIDENT);
+ newehdr->e_type = ehdr->e_type;
+ newehdr->e_machine = ehdr->e_machine;
+ newehdr->e_version = ehdr->e_version;
+ newehdr->e_entry = ehdr->e_entry;
+ newehdr->e_flags = ehdr->e_flags;
+ newehdr->e_phoff = ehdr->e_phoff;
+
+ /* We need to position the section header table. */
+ const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT);
+ newehdr->e_shoff = ((shdr_info[shdridx].shdr.sh_offset
+ + shdr_info[shdridx].shdr.sh_size + offsize - 1)
+ & ~((GElf_Off) (offsize - 1)));
+ newehdr->e_shentsize = gelf_fsize (elf, ELF_T_SHDR, 1, EV_CURRENT);
+
+ /* The new section header string table index. */
+ if (likely (idx < SHN_HIRESERVE) && likely (idx != SHN_XINDEX))
+ newehdr->e_shstrndx = idx;
+ else
+ {
+ /* The index does not fit in the ELF header field. */
+ shdr_info[0].scn = elf_getscn (elf, 0);
+
+ if (gelf_getshdr (shdr_info[0].scn, &shdr_info[0].shdr) == NULL)
+ INTERNAL_ERROR (fname);
+
+ shdr_info[0].shdr.sh_link = idx;
+ (void) gelf_update_shdr (shdr_info[0].scn, &shdr_info[0].shdr);
+
+ newehdr->e_shstrndx = SHN_XINDEX;
+ }
+
+ if (gelf_update_ehdr (newelf, newehdr) == 0)
+ {
+ error (0, 0, gettext ("%s: error while creating ELF header: %s"),
+ output_fname ?: fname, elf_errmsg (-1));
+ cleanup_debug ();
+ return 1;
+ }
+
+ /* We have everything from the old file. */
+ if (elf_cntl (elf, ELF_C_FDDONE) != 0)
+ {
+ error (0, 0, gettext ("%s: error while reading the file: %s"),
+ fname, elf_errmsg (-1));
+ cleanup_debug ();
+ return 1;
+ }
+
+ /* The ELF library better follows our layout when this is not a
+ relocatable object file. */
+ elf_flagelf (newelf, ELF_C_SET,
+ (ehdr->e_type != ET_REL ? ELF_F_LAYOUT : 0)
+ | (permissive ? ELF_F_PERMISSIVE : 0));
+
+ /* Finally write the file. */
+ if (elf_update (newelf, ELF_C_WRITE) == -1)
+ {
+ error (0, 0, gettext ("while writing '%s': %s"),
+ output_fname ?: fname, elf_errmsg (-1));
+ result = 1;
+ }
+
+ if (remove_shdrs)
+ {
+ /* libelf can't cope without the section headers being properly intact.
+ So we just let it write them normally, and then we nuke them later. */
+
+ if (newehdr->e_ident[EI_CLASS] == ELFCLASS32)
+ {
+ assert (offsetof (Elf32_Ehdr, e_shentsize) + sizeof (Elf32_Half)
+ == offsetof (Elf32_Ehdr, e_shnum));
+ assert (offsetof (Elf32_Ehdr, e_shnum) + sizeof (Elf32_Half)
+ == offsetof (Elf32_Ehdr, e_shstrndx));
+ const Elf32_Off zero_off = 0;
+ const Elf32_Half zero[3] = { 0, 0, SHN_UNDEF };
+ if (pwrite_retry (fd, &zero_off, sizeof zero_off,
+ offsetof (Elf32_Ehdr, e_shoff)) != sizeof zero_off
+ || (pwrite_retry (fd, zero, sizeof zero,
+ offsetof (Elf32_Ehdr, e_shentsize))
+ != sizeof zero)
+ || ftruncate (fd, shdr_info[shdridx].shdr.sh_offset) < 0)
+ {
+ error (0, errno, gettext ("while writing '%s'"),
+ output_fname ?: fname);
+ result = 1;
+ }
+ }
+ else
+ {
+ assert (offsetof (Elf64_Ehdr, e_shentsize) + sizeof (Elf64_Half)
+ == offsetof (Elf64_Ehdr, e_shnum));
+ assert (offsetof (Elf64_Ehdr, e_shnum) + sizeof (Elf64_Half)
+ == offsetof (Elf64_Ehdr, e_shstrndx));
+ const Elf64_Off zero_off = 0;
+ const Elf64_Half zero[3] = { 0, 0, SHN_UNDEF };
+ if (pwrite_retry (fd, &zero_off, sizeof zero_off,
+ offsetof (Elf64_Ehdr, e_shoff)) != sizeof zero_off
+ || (pwrite_retry (fd, zero, sizeof zero,
+ offsetof (Elf64_Ehdr, e_shentsize))
+ != sizeof zero)
+ || ftruncate (fd, shdr_info[shdridx].shdr.sh_offset) < 0)
+ {
+ error (0, errno, gettext ("while writing '%s'"),
+ output_fname ?: fname);
+ result = 1;
+ }
+ }
+ }
+
+ fail_close:
+ if (shdr_info != NULL)
+ {
+ /* For some sections we might have created an table to map symbol
+ table indices. Or we might kept (original) data around to put
+ into the .debug file. */
+ for (cnt = 1; cnt <= shdridx; ++cnt)
+ {
+ free (shdr_info[cnt].newsymidx);
+ if (shdr_info[cnt].debug_data != NULL)
+ free (shdr_info[cnt].debug_data->d_buf);
+ }
+
+ /* Free data we allocated for the .gnu_debuglink section. */
+ free (debuglink_buf);
+
+ /* Free the memory. */
+ if ((shnum + 2) * sizeof (struct shdr_info) > MAX_STACK_ALLOC)
+ free (shdr_info);
+ }
+
+ /* Free other resources. */
+ if (shstrtab_data != NULL)
+ free (shstrtab_data->d_buf);
+ if (shst != NULL)
+ dwelf_strtab_free (shst);
+
+ /* That was it. Close the descriptors. */
+ if (elf_end (newelf) != 0)
+ {
+ error (0, 0, gettext ("error while finishing '%s': %s"),
+ output_fname ?: fname, elf_errmsg (-1));
+ result = 1;
+ }
+
+ if (debugelf != NULL && elf_end (debugelf) != 0)
+ {
+ error (0, 0, gettext ("error while finishing '%s': %s"), debug_fname,
+ elf_errmsg (-1));
+ result = 1;
+ }
+
+ fail:
+ /* Close the EBL backend. */
+ if (ebl != NULL)
+ ebl_closebackend (ebl);
+
+ cleanup_debug ();
+
+ /* If requested, preserve the timestamp. */
+ if (tvp != NULL)
+ {
+ if (futimens (fd, tvp) != 0)
+ {
+ error (0, errno, gettext ("\
+cannot set access and modification date of '%s'"),
+ output_fname ?: fname);
+ result = 1;
+ }
+ }
+
+ /* Close the file descriptor if we created a new file. */
+ if (output_fname != NULL)
+ {
+ close (fd);
+ if (result != 0)
+ unlink (output_fname);
+ }
+
+ return result;
+}
+
+static void
+cleanup_debug (void)
+{
+ if (debug_fd >= 0)
+ {
+ if (tmp_debug_fname != NULL)
+ {
+ unlink (tmp_debug_fname);
+ free (tmp_debug_fname);
+ tmp_debug_fname = NULL;
+ }
+ close (debug_fd);
+ debug_fd = -1;
+ }
+}
+
+static int
+handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
+ struct timespec tvp[2])
+{
+ size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
+ size_t fname_len = strlen (fname) + 1;
+ char new_prefix[prefix_len + 1 + fname_len];
+ char *cp = new_prefix;
+
+ /* Create the full name of the file. */
+ if (prefix != NULL)
+ {
+ cp = mempcpy (cp, prefix, prefix_len);
+ *cp++ = ':';
+ }
+ memcpy (cp, fname, fname_len);
+
+
+ /* Process all the files contained in the archive. */
+ Elf *subelf;
+ Elf_Cmd cmd = ELF_C_RDWR;
+ int result = 0;
+ while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
+ {
+ /* The the header for this element. */
+ Elf_Arhdr *arhdr = elf_getarhdr (subelf);
+
+ if (elf_kind (subelf) == ELF_K_ELF)
+ result |= handle_elf (fd, subelf, new_prefix, arhdr->ar_name, 0, NULL);
+ else if (elf_kind (subelf) == ELF_K_AR)
+ result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name, NULL);
+
+ /* Get next archive element. */
+ cmd = elf_next (subelf);
+ if (unlikely (elf_end (subelf) != 0))
+ INTERNAL_ERROR (fname);
+ }
+
+ if (tvp != NULL)
+ {
+ if (unlikely (futimens (fd, tvp) != 0))
+ {
+ error (0, errno, gettext ("\
+cannot set access and modification date of '%s'"), fname);
+ result = 1;
+ }
+ }
+
+ if (unlikely (close (fd) != 0))
+ error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
+
+ return result;
+}
+
+
+#include "debugpred.h"
diff --git a/src/unstrip.c b/src/unstrip.c
new file mode 100644
index 0000000..f368e69
--- /dev/null
+++ b/src/unstrip.c
@@ -0,0 +1,2456 @@
+/* Combine stripped files with separate symbols and debug information.
+ Copyright (C) 2007-2012, 2014, 2015 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Roland McGrath <roland@redhat.com>, 2007.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* TODO:
+
+ * SHX_XINDEX
+
+ * prelink vs .debug_* linked addresses
+
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <argp.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <gelf.h>
+#include <libebl.h>
+#include <libdwfl.h>
+#include "libdwelf.h"
+#include "libeu.h"
+#include "printversion.h"
+
+#ifndef _
+# define _(str) gettext (str)
+#endif
+
+/* Name and version of program. */
+ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
+
+/* Bug report address. */
+ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
+
+/* Definitions of arguments for argp functions. */
+static const struct argp_option options[] =
+{
+ /* Group 2 will follow group 1 from dwfl_standard_argp. */
+ { "match-file-names", 'f', NULL, 0,
+ N_("Match MODULE against file names, not module names"), 2 },
+ { "ignore-missing", 'i', NULL, 0, N_("Silently skip unfindable files"), 0 },
+
+ { NULL, 0, NULL, 0, N_("Output options:"), 0 },
+ { "output", 'o', "FILE", 0, N_("Place output into FILE"), 0 },
+ { "output-directory", 'd', "DIRECTORY",
+ 0, N_("Create multiple output files under DIRECTORY"), 0 },
+ { "module-names", 'm', NULL, 0, N_("Use module rather than file names"), 0 },
+ { "all", 'a', NULL, 0,
+ N_("Create output for modules that have no separate debug information"),
+ 0 },
+ { "relocate", 'R', NULL, 0,
+ N_("Apply relocations to section contents in ET_REL files"), 0 },
+ { "list-only", 'n', NULL, 0,
+ N_("Only list module and file names, build IDs"), 0 },
+ { "force", 'F', NULL, 0,
+ N_("Force combining files even if some ELF headers don't seem to match"),
+ 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+struct arg_info
+{
+ const char *output_file;
+ const char *output_dir;
+ Dwfl *dwfl;
+ char **args;
+ bool list;
+ bool all;
+ bool ignore;
+ bool modnames;
+ bool match_files;
+ bool relocate;
+ bool force;
+};
+
+/* Handle program arguments. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ struct arg_info *info = state->input;
+
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ state->child_inputs[0] = &info->dwfl;
+ break;
+
+ case 'o':
+ if (info->output_file != NULL)
+ {
+ argp_error (state, _("-o option specified twice"));
+ return EINVAL;
+ }
+ info->output_file = arg;
+ break;
+
+ case 'd':
+ if (info->output_dir != NULL)
+ {
+ argp_error (state, _("-d option specified twice"));
+ return EINVAL;
+ }
+ info->output_dir = arg;
+ break;
+
+ case 'm':
+ info->modnames = true;
+ break;
+ case 'f':
+ info->match_files = true;
+ break;
+ case 'a':
+ info->all = true;
+ break;
+ case 'i':
+ info->ignore = true;
+ break;
+ case 'n':
+ info->list = true;
+ break;
+ case 'R':
+ info->relocate = true;
+ break;
+ case 'F':
+ info->force = true;
+ break;
+
+ case ARGP_KEY_ARGS:
+ case ARGP_KEY_NO_ARGS:
+ /* We "consume" all the arguments here. */
+ info->args = &state->argv[state->next];
+
+ if (info->output_file != NULL && info->output_dir != NULL)
+ {
+ argp_error (state, _("only one of -o or -d allowed"));
+ return EINVAL;
+ }
+
+ if (info->list && (info->dwfl == NULL
+ || info->output_dir != NULL
+ || info->output_file != NULL))
+ {
+ argp_error (state,
+ _("-n cannot be used with explicit files or -o or -d"));
+ return EINVAL;
+ }
+
+ if (info->output_dir != NULL)
+ {
+ struct stat st;
+ error_t fail = 0;
+ if (stat (info->output_dir, &st) < 0)
+ fail = errno;
+ else if (!S_ISDIR (st.st_mode))
+ fail = ENOTDIR;
+ if (fail)
+ {
+ argp_failure (state, EXIT_FAILURE, fail,
+ _("output directory '%s'"), info->output_dir);
+ return fail;
+ }
+ }
+
+ if (info->dwfl == NULL)
+ {
+ if (state->next + 2 != state->argc)
+ {
+ argp_error (state, _("exactly two file arguments are required"));
+ return EINVAL;
+ }
+
+ if (info->ignore || info->all || info->modnames || info->relocate)
+ {
+ argp_error (state, _("\
+-m, -a, -R, and -i options not allowed with explicit files"));
+ return EINVAL;
+ }
+
+ /* Bail out immediately to prevent dwfl_standard_argp's parser
+ from defaulting to "-e a.out". */
+ return ENOSYS;
+ }
+ else if (info->output_file == NULL && info->output_dir == NULL
+ && !info->list)
+ {
+ argp_error (state,
+ _("-o or -d is required when using implicit files"));
+ return EINVAL;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+#define ELF_CHECK(call, msg) \
+ do \
+ { \
+ if (unlikely (!(call))) \
+ error (EXIT_FAILURE, 0, msg, elf_errmsg (-1)); \
+ } while (0)
+
+/* Copy INELF to newly-created OUTELF, exit via error for any problems. */
+static void
+copy_elf (Elf *outelf, Elf *inelf)
+{
+ ELF_CHECK (gelf_newehdr (outelf, gelf_getclass (inelf)),
+ _("cannot create ELF header: %s"));
+
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (inelf, &ehdr_mem);
+ ELF_CHECK (gelf_update_ehdr (outelf, ehdr),
+ _("cannot copy ELF header: %s"));
+
+ size_t phnum;
+ ELF_CHECK (elf_getphdrnum (inelf, &phnum) == 0,
+ _("cannot get number of program headers: %s"));
+
+ if (phnum > 0)
+ {
+ ELF_CHECK (gelf_newphdr (outelf, phnum),
+ _("cannot create program headers: %s"));
+
+ GElf_Phdr phdr_mem;
+ for (size_t i = 0; i < phnum; ++i)
+ ELF_CHECK (gelf_update_phdr (outelf, i,
+ gelf_getphdr (inelf, i, &phdr_mem)),
+ _("cannot copy program header: %s"));
+ }
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (inelf, scn)) != NULL)
+ {
+ Elf_Scn *newscn = elf_newscn (outelf);
+
+ GElf_Shdr shdr_mem;
+ ELF_CHECK (gelf_update_shdr (newscn, gelf_getshdr (scn, &shdr_mem)),
+ _("cannot copy section header: %s"));
+
+ Elf_Data *data = elf_getdata (scn, NULL);
+ ELF_CHECK (data != NULL, _("cannot get section data: %s"));
+ Elf_Data *newdata = elf_newdata (newscn);
+ ELF_CHECK (newdata != NULL, _("cannot copy section data: %s"));
+ *newdata = *data;
+ elf_flagdata (newdata, ELF_C_SET, ELF_F_DIRTY);
+ }
+}
+
+/* Create directories containing PATH. */
+static void
+make_directories (const char *path)
+{
+ const char *lastslash = strrchr (path, '/');
+ if (lastslash == NULL)
+ return;
+
+ while (lastslash > path && lastslash[-1] == '/')
+ --lastslash;
+ if (lastslash == path)
+ return;
+
+ char *dir = strndupa (path, lastslash - path);
+ while (mkdir (dir, 0777) < 0 && errno != EEXIST)
+ if (errno == ENOENT)
+ make_directories (dir);
+ else
+ error (EXIT_FAILURE, errno, _("cannot create directory '%s'"), dir);
+}
+
+/* Keep track of new section data we are creating, so we can free it
+ when done. */
+struct data_list
+{
+ void *data;
+ struct data_list *next;
+};
+
+struct data_list *new_data_list;
+
+static void
+record_new_data (void *data)
+{
+ struct data_list *next = new_data_list;
+ new_data_list = xmalloc (sizeof (struct data_list));
+ new_data_list->data = data;
+ new_data_list->next = next;
+}
+
+static void
+free_new_data (void)
+{
+ struct data_list *list = new_data_list;
+ while (list != NULL)
+ {
+ struct data_list *next = list->next;
+ free (list->data);
+ free (list);
+ list = next;
+ }
+ new_data_list = NULL;
+}
+
+/* The binutils linker leaves gratuitous section symbols in .symtab
+ that strip has to remove. Older linkers likewise include a
+ symbol for every section, even unallocated ones, in .dynsym.
+ Because of this, the related sections can shrink in the stripped
+ file from their original size. Older versions of strip do not
+ adjust the sh_size field in the debuginfo file's SHT_NOBITS
+ version of the section header, so it can appear larger. */
+static bool
+section_can_shrink (const GElf_Shdr *shdr)
+{
+ switch (shdr->sh_type)
+ {
+ case SHT_SYMTAB:
+ case SHT_DYNSYM:
+ case SHT_HASH:
+ case SHT_GNU_versym:
+ return true;
+ }
+ return false;
+}
+
+/* See if this symbol table has a leading section symbol for every single
+ section, in order. The binutils linker produces this. While we're here,
+ update each section symbol's st_value. */
+static size_t
+symtab_count_leading_section_symbols (Elf *elf, Elf_Scn *scn, size_t shnum,
+ Elf_Data *newsymdata)
+{
+ Elf_Data *data = elf_getdata (scn, NULL);
+ Elf_Data *shndxdata = NULL; /* XXX */
+
+ for (size_t i = 1; i < shnum; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx = SHN_UNDEF;
+ GElf_Sym *sym = gelf_getsymshndx (data, shndxdata, i, &sym_mem, &shndx);
+ ELF_CHECK (sym != NULL, _("cannot get symbol table entry: %s"));
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, i), &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = sym->st_shndx;
+
+ if (shndx != i || GELF_ST_TYPE (sym->st_info) != STT_SECTION)
+ return i;
+
+ sym->st_value = shdr->sh_addr;
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = SHN_UNDEF;
+ ELF_CHECK (gelf_update_symshndx (newsymdata, shndxdata, i, sym, shndx),
+ _("cannot update symbol table: %s"));
+ }
+
+ return shnum;
+}
+
+static void
+update_shdr (Elf_Scn *outscn, GElf_Shdr *newshdr)
+{
+ ELF_CHECK (gelf_update_shdr (outscn, newshdr),
+ _("cannot update section header: %s"));
+}
+
+/* We expanded the output section, so update its header. */
+static void
+update_sh_size (Elf_Scn *outscn, const Elf_Data *data)
+{
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *newshdr = gelf_getshdr (outscn, &shdr_mem);
+ ELF_CHECK (newshdr != NULL, _("cannot get section header: %s"));
+
+ newshdr->sh_size = data->d_size;
+
+ update_shdr (outscn, newshdr);
+}
+
+/* Update relocation sections using the symbol table. */
+static void
+adjust_relocs (Elf_Scn *outscn, Elf_Scn *inscn, const GElf_Shdr *shdr,
+ size_t map[], const GElf_Shdr *symshdr)
+{
+ Elf_Data *data = elf_getdata (outscn, NULL);
+
+ inline void adjust_reloc (GElf_Xword *info)
+ {
+ size_t ndx = GELF_R_SYM (*info);
+ if (ndx != STN_UNDEF)
+ *info = GELF_R_INFO (map[ndx - 1], GELF_R_TYPE (*info));
+ }
+
+ switch (shdr->sh_type)
+ {
+ case SHT_REL:
+ for (size_t i = 0; i < shdr->sh_size / shdr->sh_entsize; ++i)
+ {
+ GElf_Rel rel_mem;
+ GElf_Rel *rel = gelf_getrel (data, i, &rel_mem);
+ adjust_reloc (&rel->r_info);
+ ELF_CHECK (gelf_update_rel (data, i, rel),
+ _("cannot update relocation: %s"));
+ }
+ break;
+
+ case SHT_RELA:
+ for (size_t i = 0; i < shdr->sh_size / shdr->sh_entsize; ++i)
+ {
+ GElf_Rela rela_mem;
+ GElf_Rela *rela = gelf_getrela (data, i, &rela_mem);
+ adjust_reloc (&rela->r_info);
+ ELF_CHECK (gelf_update_rela (data, i, rela),
+ _("cannot update relocation: %s"));
+ }
+ break;
+
+ case SHT_GROUP:
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *newshdr = gelf_getshdr (outscn, &shdr_mem);
+ ELF_CHECK (newshdr != NULL, _("cannot get section header: %s"));
+ if (newshdr->sh_info != STN_UNDEF)
+ {
+ newshdr->sh_info = map[newshdr->sh_info - 1];
+ update_shdr (outscn, newshdr);
+ }
+ break;
+ }
+
+ case SHT_HASH:
+ /* We must expand the table and rejigger its contents. */
+ {
+ const size_t nsym = symshdr->sh_size / symshdr->sh_entsize;
+ const size_t onent = shdr->sh_size / shdr->sh_entsize;
+ assert (data->d_size == shdr->sh_size);
+
+#define CONVERT_HASH(Hash_Word) \
+ { \
+ const Hash_Word *const old_hash = data->d_buf; \
+ const size_t nbucket = old_hash[0]; \
+ const size_t nchain = old_hash[1]; \
+ const Hash_Word *const old_bucket = &old_hash[2]; \
+ const Hash_Word *const old_chain = &old_bucket[nbucket]; \
+ assert (onent == 2 + nbucket + nchain); \
+ \
+ const size_t nent = 2 + nbucket + nsym; \
+ Hash_Word *const new_hash = xcalloc (nent, sizeof new_hash[0]); \
+ Hash_Word *const new_bucket = &new_hash[2]; \
+ Hash_Word *const new_chain = &new_bucket[nbucket]; \
+ \
+ new_hash[0] = nbucket; \
+ new_hash[1] = nsym; \
+ for (size_t i = 0; i < nbucket; ++i) \
+ if (old_bucket[i] != STN_UNDEF) \
+ new_bucket[i] = map[old_bucket[i] - 1]; \
+ \
+ for (size_t i = 1; i < nchain; ++i) \
+ if (old_chain[i] != STN_UNDEF) \
+ new_chain[map[i - 1]] = map[old_chain[i] - 1]; \
+ \
+ record_new_data (new_hash); \
+ data->d_buf = new_hash; \
+ data->d_size = nent * sizeof new_hash[0]; \
+ }
+
+ switch (shdr->sh_entsize)
+ {
+ case 4:
+ CONVERT_HASH (Elf32_Word);
+ break;
+ case 8:
+ CONVERT_HASH (Elf64_Xword);
+ break;
+ default:
+ abort ();
+ }
+
+ elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY);
+ update_sh_size (outscn, data);
+
+#undef CONVERT_HASH
+ }
+ break;
+
+ case SHT_GNU_versym:
+ /* We must expand the table and move its elements around. */
+ {
+ const size_t nent = symshdr->sh_size / symshdr->sh_entsize;
+ const size_t onent = shdr->sh_size / shdr->sh_entsize;
+ assert (nent >= onent);
+
+ /* We don't bother using gelf_update_versym because there is
+ really no conversion to be done. */
+ assert (sizeof (Elf32_Versym) == sizeof (GElf_Versym));
+ assert (sizeof (Elf64_Versym) == sizeof (GElf_Versym));
+ GElf_Versym *versym = xcalloc (nent, sizeof versym[0]);
+
+ for (size_t i = 1; i < onent; ++i)
+ {
+ GElf_Versym *v = gelf_getversym (data, i, &versym[map[i - 1]]);
+ ELF_CHECK (v != NULL, _("cannot get symbol version: %s"));
+ }
+
+ record_new_data (versym);
+ data->d_buf = versym;
+ data->d_size = nent * shdr->sh_entsize;
+ elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY);
+ update_sh_size (outscn, data);
+ }
+ break;
+
+ default:
+ error (EXIT_FAILURE, 0,
+ _("unexpected section type in [%zu] with sh_link to symtab"),
+ elf_ndxscn (inscn));
+ }
+}
+
+/* Adjust all the relocation sections in the file. */
+static void
+adjust_all_relocs (Elf *elf, Elf_Scn *symtab, const GElf_Shdr *symshdr,
+ size_t map[])
+{
+ size_t new_sh_link = elf_ndxscn (symtab);
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ if (scn != symtab)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+ if (shdr->sh_type != SHT_NOBITS && shdr->sh_link == new_sh_link)
+ adjust_relocs (scn, scn, shdr, map, symshdr);
+ }
+}
+
+/* The original file probably had section symbols for all of its
+ sections, even the unallocated ones. To match it as closely as
+ possible, add in section symbols for the added sections. */
+static Elf_Data *
+add_new_section_symbols (Elf_Scn *old_symscn, size_t old_shnum,
+ Elf *elf, bool rel, Elf_Scn *symscn, size_t shnum)
+{
+ const size_t added = shnum - old_shnum;
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ const size_t nsym = shdr->sh_size / shdr->sh_entsize;
+ size_t symndx_map[nsym - 1];
+
+ shdr->sh_info += added;
+ shdr->sh_size += added * shdr->sh_entsize;
+ update_shdr (symscn, shdr);
+
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ Elf_Data *shndxdata = NULL; /* XXX */
+
+ symdata->d_size = shdr->sh_size;
+ symdata->d_buf = xmalloc (symdata->d_size);
+ record_new_data (symdata->d_buf);
+
+ /* Copy the existing section symbols. */
+ Elf_Data *old_symdata = elf_getdata (old_symscn, NULL);
+ for (size_t i = 0; i < old_shnum; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx = SHN_UNDEF;
+ GElf_Sym *sym = gelf_getsymshndx (old_symdata, shndxdata,
+ i, &sym_mem, &shndx);
+ ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, i,
+ sym, shndx),
+ _("cannot update symbol table: %s"));
+
+ if (i > 0)
+ symndx_map[i - 1] = i;
+ }
+
+ /* Add in the new section symbols. */
+ for (size_t i = old_shnum; i < shnum; ++i)
+ {
+ GElf_Shdr i_shdr_mem;
+ GElf_Shdr *i_shdr = gelf_getshdr (elf_getscn (elf, i), &i_shdr_mem);
+ ELF_CHECK (i_shdr != NULL, _("cannot get section header: %s"));
+ GElf_Sym sym =
+ {
+ .st_value = rel ? 0 : i_shdr->sh_addr,
+ .st_info = GELF_ST_INFO (STB_LOCAL, STT_SECTION),
+ .st_shndx = i < SHN_LORESERVE ? i : SHN_XINDEX
+ };
+ GElf_Word shndx = i < SHN_LORESERVE ? SHN_UNDEF : i;
+ ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, i,
+ &sym, shndx),
+ _("cannot update symbol table: %s"));
+ }
+
+ /* Now copy the rest of the existing symbols. */
+ for (size_t i = old_shnum; i < nsym; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx = SHN_UNDEF;
+ GElf_Sym *sym = gelf_getsymshndx (old_symdata, shndxdata,
+ i, &sym_mem, &shndx);
+ ELF_CHECK (gelf_update_symshndx (symdata, shndxdata,
+ i + added, sym, shndx),
+ _("cannot update symbol table: %s"));
+
+ symndx_map[i - 1] = i + added;
+ }
+
+ /* Adjust any relocations referring to the old symbol table. */
+ adjust_all_relocs (elf, symscn, shdr, symndx_map);
+
+ return symdata;
+}
+
+/* This has the side effect of updating STT_SECTION symbols' values,
+ in case of prelink adjustments. */
+static Elf_Data *
+check_symtab_section_symbols (Elf *elf, bool rel, Elf_Scn *scn,
+ size_t shnum, size_t shstrndx,
+ Elf_Scn *oscn, size_t oshnum, size_t oshstrndx,
+ size_t debuglink)
+{
+ size_t n = symtab_count_leading_section_symbols (elf, oscn, oshnum,
+ elf_getdata (scn, NULL));
+
+ if (n == oshnum)
+ return add_new_section_symbols (oscn, n, elf, rel, scn, shnum);
+
+ if (n == oshstrndx || (n == debuglink && n == oshstrndx - 1))
+ return add_new_section_symbols (oscn, n, elf, rel, scn, shstrndx);
+
+ return NULL;
+}
+
+struct section
+{
+ Elf_Scn *scn;
+ const char *name;
+ Elf_Scn *outscn;
+ Dwelf_Strent *strent;
+ GElf_Shdr shdr;
+};
+
+static int
+compare_alloc_sections (const struct section *s1, const struct section *s2,
+ bool rel)
+{
+ if (!rel)
+ {
+ /* Sort by address. */
+ if (s1->shdr.sh_addr < s2->shdr.sh_addr)
+ return -1;
+ if (s1->shdr.sh_addr > s2->shdr.sh_addr)
+ return 1;
+ }
+
+ /* At the same address, preserve original section order. */
+ return (ssize_t) elf_ndxscn (s1->scn) - (ssize_t) elf_ndxscn (s2->scn);
+}
+
+static int
+compare_unalloc_sections (const GElf_Shdr *shdr1, const GElf_Shdr *shdr2,
+ const char *name1, const char *name2)
+{
+ /* Sort by sh_flags as an arbitrary ordering. */
+ if (shdr1->sh_flags < shdr2->sh_flags)
+ return -1;
+ if (shdr1->sh_flags > shdr2->sh_flags)
+ return 1;
+
+ /* Sort by name as last resort. */
+ return strcmp (name1, name2);
+}
+
+static int
+compare_sections (const void *a, const void *b, bool rel)
+{
+ const struct section *s1 = a;
+ const struct section *s2 = b;
+
+ /* Sort all non-allocated sections last. */
+ if ((s1->shdr.sh_flags ^ s2->shdr.sh_flags) & SHF_ALLOC)
+ return (s1->shdr.sh_flags & SHF_ALLOC) ? -1 : 1;
+
+ return ((s1->shdr.sh_flags & SHF_ALLOC)
+ ? compare_alloc_sections (s1, s2, rel)
+ : compare_unalloc_sections (&s1->shdr, &s2->shdr,
+ s1->name, s2->name));
+}
+
+static int
+compare_sections_rel (const void *a, const void *b)
+{
+ return compare_sections (a, b, true);
+}
+
+static int
+compare_sections_nonrel (const void *a, const void *b)
+{
+ return compare_sections (a, b, false);
+}
+
+
+struct symbol
+{
+ size_t *map;
+
+ union
+ {
+ const char *name;
+ Dwelf_Strent *strent;
+ };
+ union
+ {
+ struct
+ {
+ GElf_Addr value;
+ GElf_Xword size;
+ GElf_Word shndx;
+ union
+ {
+ struct
+ {
+ uint8_t info;
+ uint8_t other;
+ } info;
+ int16_t compare;
+ };
+ };
+
+ /* For a symbol discarded after first sort, this matches its better's
+ map pointer. */
+ size_t *duplicate;
+ };
+};
+
+/* Collect input symbols into our internal form. */
+static void
+collect_symbols (Elf *outelf, bool rel, Elf_Scn *symscn, Elf_Scn *strscn,
+ const size_t nent, const GElf_Addr bias,
+ const size_t scnmap[], struct symbol *table, size_t *map,
+ struct section *split_bss)
+{
+ Elf_Data *symdata = elf_getdata (symscn, NULL);
+ Elf_Data *strdata = elf_getdata (strscn, NULL);
+ Elf_Data *shndxdata = NULL; /* XXX */
+
+ for (size_t i = 1; i < nent; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx = SHN_UNDEF;
+ GElf_Sym *sym = gelf_getsymshndx (symdata, shndxdata, i,
+ &sym_mem, &shndx);
+ ELF_CHECK (sym != NULL, _("cannot get symbol table entry: %s"));
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = sym->st_shndx;
+
+ if (sym->st_name >= strdata->d_size)
+ error (EXIT_FAILURE, 0,
+ _("invalid string offset in symbol [%zu]"), i);
+
+ struct symbol *s = &table[i - 1];
+ s->map = &map[i - 1];
+ s->name = strdata->d_buf + sym->st_name;
+ s->value = sym->st_value + bias;
+ s->size = sym->st_size;
+ s->shndx = shndx;
+ s->info.info = sym->st_info;
+ s->info.other = sym->st_other;
+
+ if (scnmap != NULL && shndx != SHN_UNDEF && shndx < SHN_LORESERVE)
+ s->shndx = scnmap[shndx - 1];
+
+ if (GELF_ST_TYPE (s->info.info) == STT_SECTION && !rel)
+ {
+ /* Update the value to match the output section. */
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (elf_getscn (outelf, s->shndx),
+ &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+ s->value = shdr->sh_addr;
+ }
+ else if (split_bss != NULL
+ && s->value < split_bss->shdr.sh_addr
+ && s->value >= split_bss[-1].shdr.sh_addr
+ && shndx == elf_ndxscn (split_bss->outscn))
+ /* This symbol was in .bss and was split into .dynbss. */
+ s->shndx = elf_ndxscn (split_bss[-1].outscn);
+ }
+}
+
+
+#define CMP(value) \
+ if (s1->value < s2->value) \
+ return -1; \
+ if (s1->value > s2->value) \
+ return 1
+
+/* Compare symbols with a consistent ordering,
+ but one only meaningful for equality. */
+static int
+compare_symbols (const void *a, const void *b)
+{
+ const struct symbol *s1 = a;
+ const struct symbol *s2 = b;
+
+ CMP (value);
+ CMP (size);
+ CMP (shndx);
+
+ return (s1->compare - s2->compare) ?: strcmp (s1->name, s2->name);
+}
+
+/* Compare symbols for output order after slots have been assigned. */
+static int
+compare_symbols_output (const void *a, const void *b)
+{
+ const struct symbol *s1 = a;
+ const struct symbol *s2 = b;
+ int cmp;
+
+ /* Sort discarded symbols last. */
+ cmp = (s1->name == NULL) - (s2->name == NULL);
+
+ if (cmp == 0)
+ /* Local symbols must come first. */
+ cmp = ((GELF_ST_BIND (s2->info.info) == STB_LOCAL)
+ - (GELF_ST_BIND (s1->info.info) == STB_LOCAL));
+
+ if (cmp == 0)
+ /* binutils always puts section symbols first. */
+ cmp = ((GELF_ST_TYPE (s2->info.info) == STT_SECTION)
+ - (GELF_ST_TYPE (s1->info.info) == STT_SECTION));
+
+ if (cmp == 0)
+ {
+ if (GELF_ST_TYPE (s1->info.info) == STT_SECTION)
+ {
+ /* binutils always puts section symbols in section index order. */
+ CMP (shndx);
+ else
+ assert (s1 == s2);
+ }
+
+ /* Nothing really matters, so preserve the original order. */
+ CMP (map);
+ else
+ assert (s1 == s2);
+ }
+
+ return cmp;
+}
+
+#undef CMP
+
+/* Return true if the flags of the sections match, ignoring the SHF_INFO_LINK
+ flag if the section contains relocation information. */
+static bool
+sections_flags_match (Elf64_Xword sh_flags1, Elf64_Xword sh_flags2,
+ Elf64_Word sh_type)
+{
+ if (sh_type == SHT_REL || sh_type == SHT_RELA)
+ {
+ sh_flags1 &= ~SHF_INFO_LINK;
+ sh_flags2 &= ~SHF_INFO_LINK;
+ }
+
+ return sh_flags1 == sh_flags2;
+}
+
+/* Return true iff the flags, size, and name match. */
+static bool
+sections_match (const struct section *sections, size_t i,
+ const GElf_Shdr *shdr, const char *name)
+{
+ return (sections_flags_match (sections[i].shdr.sh_flags, shdr->sh_flags,
+ sections[i].shdr.sh_type)
+ && (sections[i].shdr.sh_size == shdr->sh_size
+ || (sections[i].shdr.sh_size < shdr->sh_size
+ && section_can_shrink (§ions[i].shdr)))
+ && !strcmp (sections[i].name, name));
+}
+
+/* Locate a matching allocated section in SECTIONS. */
+static struct section *
+find_alloc_section (const GElf_Shdr *shdr, GElf_Addr bias, const char *name,
+ struct section sections[], size_t nalloc)
+{
+ const GElf_Addr addr = shdr->sh_addr + bias;
+ size_t l = 0, u = nalloc;
+ while (l < u)
+ {
+ size_t i = (l + u) / 2;
+ if (addr < sections[i].shdr.sh_addr)
+ u = i;
+ else if (addr > sections[i].shdr.sh_addr)
+ l = i + 1;
+ else
+ {
+ /* We've found allocated sections with this address.
+ Find one with matching size, flags, and name. */
+ while (i > 0 && sections[i - 1].shdr.sh_addr == addr)
+ --i;
+ for (; i < nalloc && sections[i].shdr.sh_addr == addr;
+ ++i)
+ if (sections_match (sections, i, shdr, name))
+ return §ions[i];
+ break;
+ }
+ }
+ return NULL;
+}
+
+static inline const char *
+get_section_name (size_t ndx, const GElf_Shdr *shdr, const Elf_Data *shstrtab)
+{
+ if (shdr->sh_name >= shstrtab->d_size)
+ error (EXIT_FAILURE, 0, _("cannot read section [%zu] name: %s"),
+ ndx, elf_errmsg (-1));
+ return shstrtab->d_buf + shdr->sh_name;
+}
+
+/* Fix things up when prelink has moved some allocated sections around
+ and the debuginfo file's section headers no longer match up.
+ This fills in SECTIONS[0..NALLOC-1].outscn or exits.
+ If there was a .bss section that was split into two sections
+ with the new one preceding it in sh_addr, we return that pointer. */
+static struct section *
+find_alloc_sections_prelink (Elf *debug, Elf_Data *debug_shstrtab,
+ Elf *main, const GElf_Ehdr *main_ehdr,
+ Elf_Data *main_shstrtab, GElf_Addr bias,
+ struct section *sections,
+ size_t nalloc, size_t nsections)
+{
+ Elf_Scn *undo = NULL;
+ for (size_t i = nalloc; i < nsections; ++i)
+ {
+ const struct section *sec = §ions[i];
+ if (sec->shdr.sh_type == SHT_PROGBITS
+ && !(sec->shdr.sh_flags & SHF_ALLOC)
+ && !strcmp (sec->name, ".gnu.prelink_undo"))
+ {
+ undo = sec->scn;
+ break;
+ }
+ }
+
+ /* Find the original allocated sections before prelinking. */
+ struct section *undo_sections = NULL;
+ size_t undo_nalloc = 0;
+ if (undo != NULL)
+ {
+ /* Clear assignments that might have been bogus. */
+ for (size_t i = 0; i < nalloc; ++i)
+ sections[i].outscn = NULL;
+
+ Elf_Data *undodata = elf_rawdata (undo, NULL);
+ ELF_CHECK (undodata != NULL,
+ _("cannot read '.gnu.prelink_undo' section: %s"));
+
+ union
+ {
+ Elf32_Ehdr e32;
+ Elf64_Ehdr e64;
+ } ehdr;
+ Elf_Data dst =
+ {
+ .d_buf = &ehdr,
+ .d_size = sizeof ehdr,
+ .d_type = ELF_T_EHDR,
+ .d_version = EV_CURRENT
+ };
+ Elf_Data src = *undodata;
+ src.d_size = gelf_fsize (main, ELF_T_EHDR, 1, EV_CURRENT);
+ src.d_type = ELF_T_EHDR;
+ ELF_CHECK (gelf_xlatetom (main, &dst, &src,
+ main_ehdr->e_ident[EI_DATA]) != NULL,
+ _("cannot read '.gnu.prelink_undo' section: %s"));
+
+ uint_fast16_t phnum;
+ uint_fast16_t shnum;
+ if (ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32)
+ {
+ phnum = ehdr.e32.e_phnum;
+ shnum = ehdr.e32.e_shnum;
+ }
+ else
+ {
+ phnum = ehdr.e64.e_phnum;
+ shnum = ehdr.e64.e_shnum;
+ }
+
+ bool class32 = ehdr.e32.e_ident[EI_CLASS] == ELFCLASS32;
+ size_t shsize = class32 ? sizeof (Elf32_Shdr) : sizeof (Elf64_Shdr);
+ if (unlikely (shnum == 0 || shnum > SIZE_MAX / shsize + 1))
+ error (EXIT_FAILURE, 0, _("overflow with shnum = %zu in '%s' section"),
+ (size_t) shnum, ".gnu.prelink_undo");
+
+ --shnum;
+
+ size_t phsize = gelf_fsize (main, ELF_T_PHDR, phnum, EV_CURRENT);
+ src.d_buf += src.d_size + phsize;
+ src.d_size = gelf_fsize (main, ELF_T_SHDR, shnum, EV_CURRENT);
+ src.d_type = ELF_T_SHDR;
+ if ((size_t) (src.d_buf - undodata->d_buf) > undodata->d_size
+ || undodata->d_size - (src.d_buf - undodata->d_buf) != src.d_size)
+ error (EXIT_FAILURE, 0, _("invalid contents in '%s' section"),
+ ".gnu.prelink_undo");
+
+ const size_t shdr_bytes = shnum * shsize;
+ void *shdr = xmalloc (shdr_bytes);
+ dst.d_buf = shdr;
+ dst.d_size = shdr_bytes;
+ ELF_CHECK (gelf_xlatetom (main, &dst, &src,
+ main_ehdr->e_ident[EI_DATA]) != NULL,
+ _("cannot read '.gnu.prelink_undo' section: %s"));
+
+ undo_sections = xmalloc (shnum * sizeof undo_sections[0]);
+ for (size_t i = 0; i < shnum; ++i)
+ {
+ struct section *sec = &undo_sections[undo_nalloc];
+ Elf32_Shdr (*s32)[shnum] = shdr;
+ Elf64_Shdr (*s64)[shnum] = shdr;
+ if (class32)
+ {
+#define COPY(field) sec->shdr.field = (*s32)[i].field
+ COPY (sh_name);
+ COPY (sh_type);
+ COPY (sh_flags);
+ COPY (sh_addr);
+ COPY (sh_offset);
+ COPY (sh_size);
+ COPY (sh_link);
+ COPY (sh_info);
+ COPY (sh_addralign);
+ COPY (sh_entsize);
+#undef COPY
+ }
+ else
+ sec->shdr = (*s64)[i];
+ if (sec->shdr.sh_flags & SHF_ALLOC)
+ {
+ sec->shdr.sh_addr += bias;
+ sec->name = get_section_name (i + 1, &sec->shdr, main_shstrtab);
+ sec->scn = elf_getscn (main, i + 1); /* Really just for ndx. */
+ sec->outscn = NULL;
+ sec->strent = NULL;
+ ++undo_nalloc;
+ }
+ }
+ qsort (undo_sections, undo_nalloc,
+ sizeof undo_sections[0], compare_sections_nonrel);
+ free (shdr);
+ }
+
+ bool fail = false;
+ inline void check_match (bool match, Elf_Scn *scn, const char *name)
+ {
+ if (!match)
+ {
+ fail = true;
+ error (0, 0, _("cannot find matching section for [%zu] '%s'"),
+ elf_ndxscn (scn), name);
+ }
+ }
+
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (debug, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ if (!(shdr->sh_flags & SHF_ALLOC))
+ continue;
+
+ const char *name = get_section_name (elf_ndxscn (scn), shdr,
+ debug_shstrtab);
+
+ if (undo_sections != NULL)
+ {
+ struct section *sec = find_alloc_section (shdr, 0, name,
+ undo_sections,
+ undo_nalloc);
+ if (sec != NULL)
+ {
+ sec->outscn = scn;
+ continue;
+ }
+ }
+
+ /* If there is no prelink info, we are just here to find
+ the sections to give error messages about. */
+ for (size_t i = 0; shdr != NULL && i < nalloc; ++i)
+ if (sections[i].outscn == scn)
+ shdr = NULL;
+ check_match (shdr == NULL, scn, name);
+ }
+
+ if (fail)
+ exit (EXIT_FAILURE);
+
+ /* Now we have lined up output sections for each of the original sections
+ before prelinking. Translate those to the prelinked sections.
+ This matches what prelink's undo_sections does. */
+ struct section *split_bss = NULL;
+ for (size_t i = 0; i < undo_nalloc; ++i)
+ {
+ const struct section *undo_sec = &undo_sections[i];
+
+ const char *name = undo_sec->name;
+ scn = undo_sec->scn; /* This is just for elf_ndxscn. */
+
+ for (size_t j = 0; j < nalloc; ++j)
+ {
+ struct section *sec = §ions[j];
+#define RELA_SCALED(field) \
+ (2 * sec->shdr.field == 3 * undo_sec->shdr.field)
+ if (sec->outscn == NULL
+ && sec->shdr.sh_name == undo_sec->shdr.sh_name
+ && sec->shdr.sh_flags == undo_sec->shdr.sh_flags
+ && sec->shdr.sh_addralign == undo_sec->shdr.sh_addralign
+ && (((sec->shdr.sh_type == undo_sec->shdr.sh_type
+ && sec->shdr.sh_entsize == undo_sec->shdr.sh_entsize
+ && (sec->shdr.sh_size == undo_sec->shdr.sh_size
+ || (sec->shdr.sh_size > undo_sec->shdr.sh_size
+ && main_ehdr->e_type == ET_EXEC
+ && !strcmp (sec->name, ".dynstr"))))
+ || (sec->shdr.sh_size == undo_sec->shdr.sh_size
+ && ((sec->shdr.sh_entsize == undo_sec->shdr.sh_entsize
+ && undo_sec->shdr.sh_type == SHT_NOBITS)
+ || undo_sec->shdr.sh_type == SHT_PROGBITS)
+ && !strcmp (sec->name, ".plt")))
+ || (sec->shdr.sh_type == SHT_RELA
+ && undo_sec->shdr.sh_type == SHT_REL
+ && RELA_SCALED (sh_entsize) && RELA_SCALED (sh_size))
+ || (sec->shdr.sh_entsize == undo_sec->shdr.sh_entsize
+ && (sec->shdr.sh_type == undo_sec->shdr.sh_type
+ || (sec->shdr.sh_type == SHT_PROGBITS
+ && undo_sec->shdr.sh_type == SHT_NOBITS))
+ && sec->shdr.sh_size <= undo_sec->shdr.sh_size
+ && (!strcmp (sec->name, ".bss")
+ || !strcmp (sec->name, ".sbss"))
+ && (sec->shdr.sh_size == undo_sec->shdr.sh_size
+ || (split_bss = sec) > sections))))
+ {
+ sec->outscn = undo_sec->outscn;
+ undo_sec = NULL;
+ break;
+ }
+ }
+
+ check_match (undo_sec == NULL, scn, name);
+ }
+
+ free (undo_sections);
+
+ if (fail)
+ exit (EXIT_FAILURE);
+
+ return split_bss;
+}
+
+/* Create new .shstrtab contents, subroutine of copy_elided_sections.
+ This can't be open coded there and still use variable-length auto arrays,
+ since the end of our block would free other VLAs too. */
+static Elf_Data *
+new_shstrtab (Elf *unstripped, size_t unstripped_shnum,
+ Elf_Data *shstrtab, size_t unstripped_shstrndx,
+ struct section *sections, size_t stripped_shnum,
+ Dwelf_Strtab *strtab)
+{
+ if (strtab == NULL)
+ return NULL;
+
+ Dwelf_Strent *unstripped_strent[unstripped_shnum - 1];
+ memset (unstripped_strent, 0, sizeof unstripped_strent);
+ for (struct section *sec = sections;
+ sec < §ions[stripped_shnum - 1];
+ ++sec)
+ if (sec->outscn != NULL)
+ {
+ if (sec->strent == NULL)
+ {
+ sec->strent = dwelf_strtab_add (strtab, sec->name);
+ ELF_CHECK (sec->strent != NULL,
+ _("cannot add section name to string table: %s"));
+ }
+ unstripped_strent[elf_ndxscn (sec->outscn) - 1] = sec->strent;
+ }
+
+ /* Add names of sections we aren't touching. */
+ for (size_t i = 0; i < unstripped_shnum - 1; ++i)
+ if (unstripped_strent[i] == NULL)
+ {
+ Elf_Scn *scn = elf_getscn (unstripped, i + 1);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ const char *name = get_section_name (i + 1, shdr, shstrtab);
+ unstripped_strent[i] = dwelf_strtab_add (strtab, name);
+ ELF_CHECK (unstripped_strent[i] != NULL,
+ _("cannot add section name to string table: %s"));
+ }
+ else
+ unstripped_strent[i] = NULL;
+
+ /* Now finalize the string table so we can get offsets. */
+ Elf_Data *strtab_data = elf_getdata (elf_getscn (unstripped,
+ unstripped_shstrndx), NULL);
+ ELF_CHECK (elf_flagdata (strtab_data, ELF_C_SET, ELF_F_DIRTY),
+ _("cannot update section header string table data: %s"));
+ if (dwelf_strtab_finalize (strtab, strtab_data) == NULL)
+ error (EXIT_FAILURE, 0, "Not enough memory to create string table");
+
+ /* Update the sh_name fields of sections we aren't modifying later. */
+ for (size_t i = 0; i < unstripped_shnum - 1; ++i)
+ if (unstripped_strent[i] != NULL)
+ {
+ Elf_Scn *scn = elf_getscn (unstripped, i + 1);
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ shdr->sh_name = dwelf_strent_off (unstripped_strent[i]);
+ if (i + 1 == unstripped_shstrndx)
+ shdr->sh_size = strtab_data->d_size;
+ update_shdr (scn, shdr);
+ }
+
+ return strtab_data;
+}
+
+/* Fill in any SHT_NOBITS sections in UNSTRIPPED by
+ copying their contents and sh_type from STRIPPED. */
+static void
+copy_elided_sections (Elf *unstripped, Elf *stripped,
+ const GElf_Ehdr *stripped_ehdr, GElf_Addr bias)
+{
+ size_t unstripped_shstrndx;
+ ELF_CHECK (elf_getshdrstrndx (unstripped, &unstripped_shstrndx) == 0,
+ _("cannot get section header string table section index: %s"));
+
+ size_t stripped_shstrndx;
+ ELF_CHECK (elf_getshdrstrndx (stripped, &stripped_shstrndx) == 0,
+ _("cannot get section header string table section index: %s"));
+
+ size_t unstripped_shnum;
+ ELF_CHECK (elf_getshdrnum (unstripped, &unstripped_shnum) == 0,
+ _("cannot get section count: %s"));
+
+ size_t stripped_shnum;
+ ELF_CHECK (elf_getshdrnum (stripped, &stripped_shnum) == 0,
+ _("cannot get section count: %s"));
+
+ if (unlikely (stripped_shnum > unstripped_shnum))
+ error (EXIT_FAILURE, 0, _("\
+more sections in stripped file than debug file -- arguments reversed?"));
+
+ /* Cache the stripped file's section details. */
+ struct section sections[stripped_shnum - 1];
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (stripped, scn)) != NULL)
+ {
+ size_t i = elf_ndxscn (scn) - 1;
+ GElf_Shdr *shdr = gelf_getshdr (scn, §ions[i].shdr);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+ sections[i].name = elf_strptr (stripped, stripped_shstrndx,
+ shdr->sh_name);
+ if (sections[i].name == NULL)
+ error (EXIT_FAILURE, 0, _("cannot read section [%zu] name: %s"),
+ elf_ndxscn (scn), elf_errmsg (-1));
+ sections[i].scn = scn;
+ sections[i].outscn = NULL;
+ sections[i].strent = NULL;
+ }
+
+ const struct section *stripped_symtab = NULL;
+
+ /* Sort the sections, allocated by address and others after. */
+ qsort (sections, stripped_shnum - 1, sizeof sections[0],
+ stripped_ehdr->e_type == ET_REL
+ ? compare_sections_rel : compare_sections_nonrel);
+ size_t nalloc = stripped_shnum - 1;
+ while (nalloc > 0 && !(sections[nalloc - 1].shdr.sh_flags & SHF_ALLOC))
+ {
+ --nalloc;
+ if (sections[nalloc].shdr.sh_type == SHT_SYMTAB)
+ stripped_symtab = §ions[nalloc];
+ }
+
+ /* Locate a matching unallocated section in SECTIONS. */
+ inline struct section *find_unalloc_section (const GElf_Shdr *shdr,
+ const char *name)
+ {
+ size_t l = nalloc, u = stripped_shnum - 1;
+ while (l < u)
+ {
+ size_t i = (l + u) / 2;
+ struct section *sec = §ions[i];
+ int cmp = compare_unalloc_sections (shdr, &sec->shdr,
+ name, sec->name);
+ if (cmp < 0)
+ u = i;
+ else if (cmp > 0)
+ l = i + 1;
+ else
+ return sec;
+ }
+ return NULL;
+ }
+
+ Elf_Data *shstrtab = elf_getdata (elf_getscn (unstripped,
+ unstripped_shstrndx), NULL);
+ ELF_CHECK (shstrtab != NULL,
+ _("cannot read section header string table: %s"));
+
+ /* Match each debuginfo section with its corresponding stripped section. */
+ bool check_prelink = false;
+ Elf_Scn *unstripped_symtab = NULL;
+ size_t unstripped_strndx = 0;
+ size_t alloc_avail = 0;
+ scn = NULL;
+ while ((scn = elf_nextscn (unstripped, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ if (shdr->sh_type == SHT_SYMTAB)
+ {
+ unstripped_symtab = scn;
+ unstripped_strndx = shdr->sh_link;
+ continue;
+ }
+
+ const size_t ndx = elf_ndxscn (scn);
+ if (ndx == unstripped_shstrndx || ndx == unstripped_strndx)
+ continue;
+
+ const char *name = get_section_name (ndx, shdr, shstrtab);
+
+ struct section *sec = NULL;
+ if (shdr->sh_flags & SHF_ALLOC)
+ {
+ if (stripped_ehdr->e_type != ET_REL)
+ {
+ /* Look for the section that matches. */
+ sec = find_alloc_section (shdr, bias, name, sections, nalloc);
+ if (sec == NULL)
+ {
+ /* We couldn't figure it out. It may be a prelink issue. */
+ check_prelink = true;
+ continue;
+ }
+ }
+ else
+ {
+ /* The sh_addr of allocated sections does not help us,
+ but the order usually matches. */
+ if (likely (sections_match (sections, alloc_avail, shdr, name)))
+ sec = §ions[alloc_avail++];
+ else
+ for (size_t i = alloc_avail + 1; i < nalloc; ++i)
+ if (sections_match (sections, i, shdr, name))
+ {
+ sec = §ions[i];
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* Look for the section that matches. */
+ sec = find_unalloc_section (shdr, name);
+ if (sec == NULL)
+ {
+ /* An additional unallocated section is fine if not SHT_NOBITS.
+ We looked it up anyway in case it's an unallocated section
+ copied in both files (e.g. SHT_NOTE), and don't keep both. */
+ if (shdr->sh_type != SHT_NOBITS)
+ continue;
+
+ /* Somehow some old .debug files wound up with SHT_NOBITS
+ .comment sections, so let those pass. */
+ if (!strcmp (name, ".comment"))
+ continue;
+ }
+ }
+
+ if (sec == NULL)
+ error (EXIT_FAILURE, 0,
+ _("cannot find matching section for [%zu] '%s'"),
+ elf_ndxscn (scn), name);
+
+ sec->outscn = scn;
+ }
+
+ /* If that failed due to changes made by prelink, we take another tack.
+ We keep track of a .bss section that was partly split into .dynbss
+ so that collect_symbols can update symbols' st_shndx fields. */
+ struct section *split_bss = NULL;
+ if (check_prelink)
+ {
+ Elf_Data *data = elf_getdata (elf_getscn (stripped, stripped_shstrndx),
+ NULL);
+ ELF_CHECK (data != NULL,
+ _("cannot read section header string table: %s"));
+ split_bss = find_alloc_sections_prelink (unstripped, shstrtab,
+ stripped, stripped_ehdr,
+ data, bias, sections,
+ nalloc, stripped_shnum - 1);
+ }
+
+ /* Make sure each main file section has a place to go. */
+ const struct section *stripped_dynsym = NULL;
+ size_t debuglink = SHN_UNDEF;
+ size_t ndx_section[stripped_shnum - 1];
+ Dwelf_Strtab *strtab = NULL;
+ for (struct section *sec = sections;
+ sec < §ions[stripped_shnum - 1];
+ ++sec)
+ {
+ size_t secndx = elf_ndxscn (sec->scn);
+
+ if (sec->outscn == NULL)
+ {
+ /* We didn't find any corresponding section for this. */
+
+ if (secndx == stripped_shstrndx)
+ {
+ /* We only need one .shstrtab. */
+ ndx_section[secndx - 1] = unstripped_shstrndx;
+ continue;
+ }
+
+ if (unstripped_symtab != NULL && sec == stripped_symtab)
+ {
+ /* We don't need a second symbol table. */
+ ndx_section[secndx - 1] = elf_ndxscn (unstripped_symtab);
+ continue;
+ }
+
+ if (unstripped_symtab != NULL && stripped_symtab != NULL
+ && secndx == stripped_symtab->shdr.sh_link
+ && unstripped_strndx != 0)
+ {
+ /* ... nor its string table. */
+ ndx_section[secndx - 1] = unstripped_strndx;
+ continue;
+ }
+
+ if (!(sec->shdr.sh_flags & SHF_ALLOC)
+ && !strcmp (sec->name, ".gnu_debuglink"))
+ {
+ /* This was created by stripping. We don't want it. */
+ debuglink = secndx;
+ ndx_section[secndx - 1] = SHN_UNDEF;
+ continue;
+ }
+
+ sec->outscn = elf_newscn (unstripped);
+ Elf_Data *newdata = elf_newdata (sec->outscn);
+ ELF_CHECK (newdata != NULL && gelf_update_shdr (sec->outscn,
+ &sec->shdr),
+ _("cannot add new section: %s"));
+
+ if (strtab == NULL)
+ strtab = dwelf_strtab_init (true);
+ sec->strent = dwelf_strtab_add (strtab, sec->name);
+ ELF_CHECK (sec->strent != NULL,
+ _("cannot add section name to string table: %s"));
+ }
+
+ /* Cache the mapping of original section indices to output sections. */
+ ndx_section[secndx - 1] = elf_ndxscn (sec->outscn);
+ }
+
+ /* We added some sections, so we need a new shstrtab. */
+ Elf_Data *strtab_data = new_shstrtab (unstripped, unstripped_shnum,
+ shstrtab, unstripped_shstrndx,
+ sections, stripped_shnum,
+ strtab);
+
+ /* Get the updated section count. */
+ ELF_CHECK (elf_getshdrnum (unstripped, &unstripped_shnum) == 0,
+ _("cannot get section count: %s"));
+
+ bool placed[unstripped_shnum - 1];
+ memset (placed, 0, sizeof placed);
+
+ /* Now update the output sections and copy in their data. */
+ GElf_Off offset = 0;
+ for (const struct section *sec = sections;
+ sec < §ions[stripped_shnum - 1];
+ ++sec)
+ if (sec->outscn != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (sec->outscn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ /* In an ET_REL file under --relocate, the sh_addr of SHF_ALLOC
+ sections will have been set nonzero by relocation. This
+ touched the shdrs of whichever file had the symtab. sh_addr
+ is still zero in the corresponding shdr. The relocated
+ address is what we want to use. */
+ if (stripped_ehdr->e_type != ET_REL
+ || !(shdr_mem.sh_flags & SHF_ALLOC)
+ || shdr_mem.sh_addr == 0)
+ shdr_mem.sh_addr = sec->shdr.sh_addr;
+
+ shdr_mem.sh_type = sec->shdr.sh_type;
+ shdr_mem.sh_size = sec->shdr.sh_size;
+ shdr_mem.sh_info = sec->shdr.sh_info;
+ shdr_mem.sh_link = sec->shdr.sh_link;
+
+ /* Buggy binutils objdump might have stripped the SHF_INFO_LINK
+ put it back if necessary. */
+ if ((sec->shdr.sh_type == SHT_REL || sec->shdr.sh_type == SHT_RELA)
+ && sec->shdr.sh_flags != shdr_mem.sh_flags
+ && (sec->shdr.sh_flags & SHF_INFO_LINK) != 0)
+ shdr_mem.sh_flags |= SHF_INFO_LINK;
+
+ if (sec->shdr.sh_link != SHN_UNDEF)
+ shdr_mem.sh_link = ndx_section[sec->shdr.sh_link - 1];
+ if (SH_INFO_LINK_P (&sec->shdr) && sec->shdr.sh_info != 0)
+ shdr_mem.sh_info = ndx_section[sec->shdr.sh_info - 1];
+
+ if (strtab != NULL)
+ shdr_mem.sh_name = dwelf_strent_off (sec->strent);
+
+ Elf_Data *indata = elf_getdata (sec->scn, NULL);
+ ELF_CHECK (indata != NULL, _("cannot get section data: %s"));
+ Elf_Data *outdata = elf_getdata (sec->outscn, NULL);
+ ELF_CHECK (outdata != NULL, _("cannot copy section data: %s"));
+ *outdata = *indata;
+ elf_flagdata (outdata, ELF_C_SET, ELF_F_DIRTY);
+
+ /* Preserve the file layout of the allocated sections. */
+ if (stripped_ehdr->e_type != ET_REL && (shdr_mem.sh_flags & SHF_ALLOC))
+ {
+ shdr_mem.sh_offset = sec->shdr.sh_offset;
+ placed[elf_ndxscn (sec->outscn) - 1] = true;
+
+ const GElf_Off end_offset = (shdr_mem.sh_offset
+ + (shdr_mem.sh_type == SHT_NOBITS
+ ? 0 : shdr_mem.sh_size));
+ if (end_offset > offset)
+ offset = end_offset;
+ }
+
+ update_shdr (sec->outscn, &shdr_mem);
+
+ if (shdr_mem.sh_type == SHT_SYMTAB || shdr_mem.sh_type == SHT_DYNSYM)
+ {
+ /* We must adjust all the section indices in the symbol table. */
+
+ Elf_Data *shndxdata = NULL; /* XXX */
+
+ for (size_t i = 1; i < shdr_mem.sh_size / shdr_mem.sh_entsize; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx = SHN_UNDEF;
+ GElf_Sym *sym = gelf_getsymshndx (outdata, shndxdata,
+ i, &sym_mem, &shndx);
+ ELF_CHECK (sym != NULL,
+ _("cannot get symbol table entry: %s"));
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = sym->st_shndx;
+
+ if (shndx != SHN_UNDEF && shndx < SHN_LORESERVE)
+ {
+ if (shndx >= stripped_shnum)
+ error (EXIT_FAILURE, 0,
+ _("symbol [%zu] has invalid section index"), i);
+
+ shndx = ndx_section[shndx - 1];
+ if (shndx < SHN_LORESERVE)
+ {
+ sym->st_shndx = shndx;
+ shndx = SHN_UNDEF;
+ }
+ else
+ sym->st_shndx = SHN_XINDEX;
+
+ ELF_CHECK (gelf_update_symshndx (outdata, shndxdata,
+ i, sym, shndx),
+ _("cannot update symbol table: %s"));
+ }
+ }
+
+ if (shdr_mem.sh_type == SHT_SYMTAB)
+ stripped_symtab = sec;
+ if (shdr_mem.sh_type == SHT_DYNSYM)
+ stripped_dynsym = sec;
+ }
+ }
+
+ /* We may need to update the symbol table. */
+ Elf_Data *symdata = NULL;
+ Dwelf_Strtab *symstrtab = NULL;
+ Elf_Data *symstrdata = NULL;
+ if (unstripped_symtab != NULL && (stripped_symtab != NULL
+ || check_prelink /* Section adjustments. */
+ || (stripped_ehdr->e_type != ET_REL
+ && bias != 0)))
+ {
+ /* Merge the stripped file's symbol table into the unstripped one. */
+ const size_t stripped_nsym = (stripped_symtab == NULL ? 1
+ : (stripped_symtab->shdr.sh_size
+ / stripped_symtab->shdr.sh_entsize));
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (unstripped_symtab, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+ const size_t unstripped_nsym = shdr->sh_size / shdr->sh_entsize;
+
+ /* First collect all the symbols from both tables. */
+
+ const size_t total_syms = stripped_nsym - 1 + unstripped_nsym - 1;
+ struct symbol symbols[total_syms];
+ size_t symndx_map[total_syms];
+
+ if (stripped_symtab != NULL)
+ collect_symbols (unstripped, stripped_ehdr->e_type == ET_REL,
+ stripped_symtab->scn,
+ elf_getscn (stripped, stripped_symtab->shdr.sh_link),
+ stripped_nsym, 0, ndx_section,
+ symbols, symndx_map, NULL);
+
+ Elf_Scn *unstripped_strtab = elf_getscn (unstripped, shdr->sh_link);
+ collect_symbols (unstripped, stripped_ehdr->e_type == ET_REL,
+ unstripped_symtab, unstripped_strtab, unstripped_nsym,
+ stripped_ehdr->e_type == ET_REL ? 0 : bias, NULL,
+ &symbols[stripped_nsym - 1],
+ &symndx_map[stripped_nsym - 1], split_bss);
+
+ /* Next, sort our array of all symbols. */
+ qsort (symbols, total_syms, sizeof symbols[0], compare_symbols);
+
+ /* Now we can weed out the duplicates. Assign remaining symbols
+ new slots, collecting a map from old indices to new. */
+ size_t nsym = 0;
+ for (struct symbol *s = symbols; s < &symbols[total_syms]; ++s)
+ {
+ /* Skip a section symbol for a removed section. */
+ if (s->shndx == SHN_UNDEF
+ && GELF_ST_TYPE (s->info.info) == STT_SECTION)
+ {
+ s->name = NULL; /* Mark as discarded. */
+ *s->map = STN_UNDEF;
+ s->duplicate = NULL;
+ continue;
+ }
+
+ struct symbol *n = s;
+ while (n + 1 < &symbols[total_syms] && !compare_symbols (s, n + 1))
+ ++n;
+
+ while (s < n)
+ {
+ /* This is a duplicate. Its twin will get the next slot. */
+ s->name = NULL; /* Mark as discarded. */
+ s->duplicate = n->map;
+ ++s;
+ }
+
+ /* Allocate the next slot. */
+ *s->map = ++nsym;
+ }
+
+ /* Now we sort again, to determine the order in the output. */
+ qsort (symbols, total_syms, sizeof symbols[0], compare_symbols_output);
+
+ if (nsym < total_syms)
+ /* The discarded symbols are now at the end of the table. */
+ assert (symbols[nsym].name == NULL);
+
+ /* Now a final pass updates the map with the final order,
+ and builds up the new string table. */
+ symstrtab = dwelf_strtab_init (true);
+ for (size_t i = 0; i < nsym; ++i)
+ {
+ assert (symbols[i].name != NULL);
+ assert (*symbols[i].map != 0);
+ *symbols[i].map = 1 + i;
+ symbols[i].strent = dwelf_strtab_add (symstrtab, symbols[i].name);
+ }
+
+ /* Scan the discarded symbols too, just to update their slots
+ in SYMNDX_MAP to refer to their live duplicates. */
+ for (size_t i = nsym; i < total_syms; ++i)
+ {
+ assert (symbols[i].name == NULL);
+ if (symbols[i].duplicate == NULL)
+ assert (*symbols[i].map == STN_UNDEF);
+ else
+ {
+ assert (*symbols[i].duplicate != STN_UNDEF);
+ *symbols[i].map = *symbols[i].duplicate;
+ }
+ }
+
+ /* Now we are ready to write the new symbol table. */
+ symdata = elf_getdata (unstripped_symtab, NULL);
+ symstrdata = elf_getdata (unstripped_strtab, NULL);
+ Elf_Data *shndxdata = NULL; /* XXX */
+
+ /* If symtab and the section header table share the string table
+ add the section names to the strtab and then (after finalizing)
+ fixup the section header sh_names. Also dispose of the old data. */
+ Dwelf_Strent *unstripped_strent[unstripped_shnum - 1];
+ if (unstripped_shstrndx == elf_ndxscn (unstripped_strtab))
+ {
+ for (size_t i = 0; i < unstripped_shnum - 1; ++i)
+ {
+ Elf_Scn *sec = elf_getscn (unstripped, i + 1);
+ GElf_Shdr mem;
+ GElf_Shdr *hdr = gelf_getshdr (sec, &mem);
+ const char *name = get_section_name (i + 1, hdr, shstrtab);
+ unstripped_strent[i] = dwelf_strtab_add (symstrtab, name);
+ ELF_CHECK (unstripped_strent[i] != NULL,
+ _("cannot add section name to string table: %s"));
+ }
+
+ if (strtab != NULL)
+ {
+ dwelf_strtab_free (strtab);
+ free (strtab_data->d_buf);
+ strtab = NULL;
+ }
+ }
+
+ if (dwelf_strtab_finalize (symstrtab, symstrdata) == NULL)
+ error (EXIT_FAILURE, 0, "Not enough memory to create symbol table");
+
+ elf_flagdata (symstrdata, ELF_C_SET, ELF_F_DIRTY);
+
+ /* And update the section header names if necessary. */
+ if (unstripped_shstrndx == elf_ndxscn (unstripped_strtab))
+ {
+ for (size_t i = 0; i < unstripped_shnum - 1; ++i)
+ {
+ Elf_Scn *sec = elf_getscn (unstripped, i + 1);
+ GElf_Shdr mem;
+ GElf_Shdr *hdr = gelf_getshdr (sec, &mem);
+ shdr->sh_name = dwelf_strent_off (unstripped_strent[i]);
+ update_shdr (sec, hdr);
+ }
+ }
+
+ /* Now update the symtab shdr. Reload symtab shdr because sh_name
+ might have changed above. */
+ shdr = gelf_getshdr (unstripped_symtab, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ shdr->sh_size = symdata->d_size = (1 + nsym) * shdr->sh_entsize;
+ symdata->d_buf = xmalloc (symdata->d_size);
+ record_new_data (symdata->d_buf);
+
+ GElf_Sym sym;
+ memset (&sym, 0, sizeof sym);
+ ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, 0, &sym, SHN_UNDEF),
+ _("cannot update symbol table: %s"));
+
+ shdr->sh_info = 1;
+ for (size_t i = 0; i < nsym; ++i)
+ {
+ struct symbol *s = &symbols[i];
+
+ /* Fill in the symbol details. */
+ sym.st_name = dwelf_strent_off (s->strent);
+ sym.st_value = s->value; /* Already biased to output address. */
+ sym.st_size = s->size;
+ sym.st_shndx = s->shndx; /* Already mapped to output index. */
+ sym.st_info = s->info.info;
+ sym.st_other = s->info.other;
+
+ /* Keep track of the number of leading local symbols. */
+ if (GELF_ST_BIND (sym.st_info) == STB_LOCAL)
+ {
+ assert (shdr->sh_info == 1 + i);
+ shdr->sh_info = 1 + i + 1;
+ }
+
+ ELF_CHECK (gelf_update_symshndx (symdata, shndxdata, 1 + i,
+ &sym, SHN_UNDEF),
+ _("cannot update symbol table: %s"));
+
+ }
+ elf_flagdata (symdata, ELF_C_SET, ELF_F_DIRTY);
+ update_shdr (unstripped_symtab, shdr);
+
+ if (stripped_symtab != NULL)
+ {
+ /* Adjust any relocations referring to the old symbol table. */
+ const size_t old_sh_link = elf_ndxscn (stripped_symtab->scn);
+ for (const struct section *sec = sections;
+ sec < §ions[stripped_shnum - 1];
+ ++sec)
+ if (sec->outscn != NULL && sec->shdr.sh_link == old_sh_link)
+ adjust_relocs (sec->outscn, sec->scn, &sec->shdr,
+ symndx_map, shdr);
+ }
+
+ /* Also adjust references to the other old symbol table. */
+ adjust_all_relocs (unstripped, unstripped_symtab, shdr,
+ &symndx_map[stripped_nsym - 1]);
+ }
+ else if (stripped_symtab != NULL && stripped_shnum != unstripped_shnum)
+ check_symtab_section_symbols (unstripped,
+ stripped_ehdr->e_type == ET_REL,
+ stripped_symtab->scn,
+ unstripped_shnum, unstripped_shstrndx,
+ stripped_symtab->outscn,
+ stripped_shnum, stripped_shstrndx,
+ debuglink);
+
+ if (stripped_dynsym != NULL)
+ (void) check_symtab_section_symbols (unstripped,
+ stripped_ehdr->e_type == ET_REL,
+ stripped_dynsym->outscn,
+ unstripped_shnum,
+ unstripped_shstrndx,
+ stripped_dynsym->scn, stripped_shnum,
+ stripped_shstrndx, debuglink);
+
+ /* We need to preserve the layout of the stripped file so the
+ phdrs will match up. This requires us to do our own layout of
+ the added sections. We do manual layout even for ET_REL just
+ so we can try to match what the original probably had. */
+
+ elf_flagelf (unstripped, ELF_C_SET, ELF_F_LAYOUT);
+
+ if (offset == 0)
+ /* For ET_REL we are starting the layout from scratch. */
+ offset = gelf_fsize (unstripped, ELF_T_EHDR, 1, EV_CURRENT);
+
+ bool skip_reloc = false;
+ do
+ {
+ skip_reloc = !skip_reloc;
+ for (size_t i = 0; i < unstripped_shnum - 1; ++i)
+ if (!placed[i])
+ {
+ scn = elf_getscn (unstripped, 1 + i);
+
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ ELF_CHECK (shdr != NULL, _("cannot get section header: %s"));
+
+ /* We must make sure we have read in the data of all sections
+ beforehand and marked them to be written out. When we're
+ modifying the existing file in place, we might overwrite
+ this part of the file before we get to handling the section. */
+
+ ELF_CHECK (elf_flagdata (elf_getdata (scn, NULL),
+ ELF_C_SET, ELF_F_DIRTY),
+ _("cannot read section data: %s"));
+
+ if (skip_reloc
+ && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
+ continue;
+
+ GElf_Off align = shdr->sh_addralign ?: 1;
+ offset = (offset + align - 1) & -align;
+ shdr->sh_offset = offset;
+ if (shdr->sh_type != SHT_NOBITS)
+ offset += shdr->sh_size;
+
+ update_shdr (scn, shdr);
+
+ if (unstripped_shstrndx == 1 + i)
+ {
+ /* Place the section headers immediately after
+ .shstrtab, and update the ELF header. */
+
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (unstripped, &ehdr_mem);
+ ELF_CHECK (ehdr != NULL, _("cannot get ELF header: %s"));
+
+ GElf_Off sh_align = gelf_getclass (unstripped) * 4;
+ offset = (offset + sh_align - 1) & -sh_align;
+ ehdr->e_shnum = unstripped_shnum;
+ ehdr->e_shoff = offset;
+ offset += unstripped_shnum * ehdr->e_shentsize;
+ ELF_CHECK (gelf_update_ehdr (unstripped, ehdr),
+ _("cannot update ELF header: %s"));
+ }
+
+ placed[i] = true;
+ }
+ }
+ while (skip_reloc);
+
+ size_t phnum;
+ ELF_CHECK (elf_getphdrnum (stripped, &phnum) == 0,
+ _("cannot get number of program headers: %s"));
+
+ if (phnum > 0)
+ ELF_CHECK (gelf_newphdr (unstripped, phnum),
+ _("cannot create program headers: %s"));
+
+ /* Copy each program header from the stripped file. */
+ for (size_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (stripped, i, &phdr_mem);
+ ELF_CHECK (phdr != NULL, _("cannot get program header: %s"));
+
+ ELF_CHECK (gelf_update_phdr (unstripped, i, phdr),
+ _("cannot update program header: %s"));
+ }
+
+ /* Finally, write out the file. */
+ ELF_CHECK (elf_update (unstripped, ELF_C_WRITE) > 0,
+ _("cannot write output file: %s"));
+
+ if (strtab != NULL)
+ {
+ dwelf_strtab_free (strtab);
+ free (strtab_data->d_buf);
+ }
+
+ if (symstrtab != NULL)
+ {
+ dwelf_strtab_free (symstrtab);
+ free (symstrdata->d_buf);
+ }
+ free_new_data ();
+}
+
+/* Process one pair of files, already opened. */
+static void
+handle_file (const char *output_file, bool create_dirs,
+ Elf *stripped, const GElf_Ehdr *stripped_ehdr,
+ Elf *unstripped)
+{
+ size_t phnum;
+ ELF_CHECK (elf_getphdrnum (stripped, &phnum) == 0,
+ _("cannot get number of program headers: %s"));
+
+ /* Determine the address bias between the debuginfo file and the main
+ file, which may have been modified by prelinking. */
+ GElf_Addr bias = 0;
+ if (unstripped != NULL)
+ for (size_t i = 0; i < phnum; ++i)
+ {
+ GElf_Phdr phdr_mem;
+ GElf_Phdr *phdr = gelf_getphdr (stripped, i, &phdr_mem);
+ ELF_CHECK (phdr != NULL, _("cannot get program header: %s"));
+ if (phdr->p_type == PT_LOAD)
+ {
+ GElf_Phdr unstripped_phdr_mem;
+ GElf_Phdr *unstripped_phdr = gelf_getphdr (unstripped, i,
+ &unstripped_phdr_mem);
+ ELF_CHECK (unstripped_phdr != NULL,
+ _("cannot get program header: %s"));
+ bias = phdr->p_vaddr - unstripped_phdr->p_vaddr;
+ break;
+ }
+ }
+
+ /* One day we could adjust all the DWARF data (like prelink itself does). */
+ if (bias != 0)
+ {
+ if (output_file == NULL)
+ error (0, 0, _("\
+DWARF data not adjusted for prelinking bias; consider prelink -u"));
+ else
+ error (0, 0, _("\
+DWARF data in '%s' not adjusted for prelinking bias; consider prelink -u"),
+ output_file);
+ }
+
+ if (output_file == NULL)
+ /* Modify the unstripped file in place. */
+ copy_elided_sections (unstripped, stripped, stripped_ehdr, bias);
+ else
+ {
+ if (create_dirs)
+ make_directories (output_file);
+
+ /* Copy the unstripped file and then modify it. */
+ int outfd = open (output_file, O_RDWR | O_CREAT,
+ stripped_ehdr->e_type == ET_REL ? 0666 : 0777);
+ if (outfd < 0)
+ error (EXIT_FAILURE, errno, _("cannot open '%s'"), output_file);
+ Elf *outelf = elf_begin (outfd, ELF_C_WRITE, NULL);
+ ELF_CHECK (outelf != NULL, _("cannot create ELF descriptor: %s"));
+
+ if (unstripped == NULL)
+ {
+ /* Actually, we are just copying out the main file as it is. */
+ copy_elf (outelf, stripped);
+ if (stripped_ehdr->e_type != ET_REL)
+ elf_flagelf (outelf, ELF_C_SET, ELF_F_LAYOUT);
+ ELF_CHECK (elf_update (outelf, ELF_C_WRITE) > 0,
+ _("cannot write output file: %s"));
+ }
+ else
+ {
+ copy_elf (outelf, unstripped);
+ copy_elided_sections (outelf, stripped, stripped_ehdr, bias);
+ }
+
+ elf_end (outelf);
+ close (outfd);
+ }
+}
+
+static int
+open_file (const char *file, bool writable)
+{
+ int fd = open (file, writable ? O_RDWR : O_RDONLY);
+ if (fd < 0)
+ error (EXIT_FAILURE, errno, _("cannot open '%s'"), file);
+ return fd;
+}
+
+/* Handle a pair of files we need to open by name. */
+static void
+handle_explicit_files (const char *output_file, bool create_dirs, bool force,
+ const char *stripped_file, const char *unstripped_file)
+{
+
+ /* Warn, and exit if not forced to continue, if some ELF header
+ sanity check for the stripped and unstripped files failed. */
+ void warn (const char *msg)
+ {
+ error (force ? 0 : EXIT_FAILURE, 0, "%s'%s' and '%s' %s%s.",
+ force ? _("WARNING: ") : "",
+ stripped_file, unstripped_file, msg,
+ force ? "" : _(", use --force"));
+ }
+
+ int stripped_fd = open_file (stripped_file, false);
+ Elf *stripped = elf_begin (stripped_fd, ELF_C_READ, NULL);
+ GElf_Ehdr stripped_ehdr;
+ ELF_CHECK (gelf_getehdr (stripped, &stripped_ehdr),
+ _("cannot create ELF descriptor: %s"));
+
+ int unstripped_fd = -1;
+ Elf *unstripped = NULL;
+ if (unstripped_file != NULL)
+ {
+ unstripped_fd = open_file (unstripped_file, output_file == NULL);
+ unstripped = elf_begin (unstripped_fd,
+ (output_file == NULL ? ELF_C_RDWR : ELF_C_READ),
+ NULL);
+ GElf_Ehdr unstripped_ehdr;
+ ELF_CHECK (gelf_getehdr (unstripped, &unstripped_ehdr),
+ _("cannot create ELF descriptor: %s"));
+
+ if (memcmp (stripped_ehdr.e_ident,
+ unstripped_ehdr.e_ident, EI_NIDENT) != 0)
+ warn (_("ELF header identification (e_ident) different"));
+
+ if (stripped_ehdr.e_type != unstripped_ehdr.e_type)
+ warn (_("ELF header type (e_type) different"));
+
+ if (stripped_ehdr.e_machine != unstripped_ehdr.e_machine)
+ warn (_("ELF header machine type (e_machine) different"));
+
+ if (stripped_ehdr.e_phnum < unstripped_ehdr.e_phnum)
+ warn (_("stripped program header (e_phnum) smaller than unstripped"));
+ }
+
+ handle_file (output_file, create_dirs, stripped, &stripped_ehdr, unstripped);
+
+ elf_end (stripped);
+ close (stripped_fd);
+
+ elf_end (unstripped);
+ close (unstripped_fd);
+}
+
+
+/* Handle a pair of files opened implicitly by libdwfl for one module. */
+static void
+handle_dwfl_module (const char *output_file, bool create_dirs, bool force,
+ Dwfl_Module *mod, bool all, bool ignore, bool relocate)
+{
+ GElf_Addr bias;
+ Elf *stripped = dwfl_module_getelf (mod, &bias);
+ if (stripped == NULL)
+ {
+ if (ignore)
+ return;
+
+ const char *file;
+ const char *modname = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, &file, NULL);
+ if (file == NULL)
+ error (EXIT_FAILURE, 0,
+ _("cannot find stripped file for module '%s': %s"),
+ modname, dwfl_errmsg (-1));
+ else
+ error (EXIT_FAILURE, 0,
+ _("cannot open stripped file '%s' for module '%s': %s"),
+ modname, file, dwfl_errmsg (-1));
+ }
+
+ Elf *debug = dwarf_getelf (dwfl_module_getdwarf (mod, &bias));
+ if (debug == NULL && !all)
+ {
+ if (ignore)
+ return;
+
+ const char *file;
+ const char *modname = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, NULL, &file);
+ if (file == NULL)
+ error (EXIT_FAILURE, 0,
+ _("cannot find debug file for module '%s': %s"),
+ modname, dwfl_errmsg (-1));
+ else
+ error (EXIT_FAILURE, 0,
+ _("cannot open debug file '%s' for module '%s': %s"),
+ modname, file, dwfl_errmsg (-1));
+ }
+
+ if (debug == stripped)
+ {
+ if (all)
+ debug = NULL;
+ else
+ {
+ const char *file;
+ const char *modname = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, &file, NULL);
+ error (EXIT_FAILURE, 0, _("module '%s' file '%s' is not stripped"),
+ modname, file);
+ }
+ }
+
+ GElf_Ehdr stripped_ehdr;
+ ELF_CHECK (gelf_getehdr (stripped, &stripped_ehdr),
+ _("cannot create ELF descriptor: %s"));
+
+ if (stripped_ehdr.e_type == ET_REL)
+ {
+ if (!relocate)
+ {
+ /* We can't use the Elf handles already open,
+ because the DWARF sections have been relocated. */
+
+ const char *stripped_file = NULL;
+ const char *unstripped_file = NULL;
+ (void) dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL,
+ &stripped_file, &unstripped_file);
+
+ handle_explicit_files (output_file, create_dirs, force,
+ stripped_file, unstripped_file);
+ return;
+ }
+
+ /* Relocation is what we want! This ensures that all sections that can
+ get sh_addr values assigned have them, even ones not used in DWARF.
+ They might still be used in the symbol table. */
+ if (dwfl_module_relocations (mod) < 0)
+ error (EXIT_FAILURE, 0,
+ _("cannot cache section addresses for module '%s': %s"),
+ dwfl_module_info (mod, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ dwfl_errmsg (-1));
+ }
+
+ handle_file (output_file, create_dirs, stripped, &stripped_ehdr, debug);
+}
+
+/* Handle one module being written to the output directory. */
+static void
+handle_output_dir_module (const char *output_dir, Dwfl_Module *mod, bool force,
+ bool all, bool ignore, bool modnames, bool relocate)
+{
+ if (! modnames)
+ {
+ /* Make sure we've searched for the ELF file. */
+ GElf_Addr bias;
+ (void) dwfl_module_getelf (mod, &bias);
+ }
+
+ const char *file;
+ const char *name = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, &file, NULL);
+
+ if (file == NULL && ignore)
+ return;
+
+ char *output_file;
+ if (asprintf (&output_file, "%s/%s", output_dir, modnames ? name : file) < 0)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+
+ handle_dwfl_module (output_file, true, force, mod, all, ignore, relocate);
+}
+
+
+static void
+list_module (Dwfl_Module *mod)
+{
+ /* Make sure we have searched for the files. */
+ GElf_Addr bias;
+ bool have_elf = dwfl_module_getelf (mod, &bias) != NULL;
+ bool have_dwarf = dwfl_module_getdwarf (mod, &bias) != NULL;
+
+ const char *file;
+ const char *debug;
+ Dwarf_Addr start;
+ Dwarf_Addr end;
+ const char *name = dwfl_module_info (mod, NULL, &start, &end,
+ NULL, NULL, &file, &debug);
+ if (file != NULL && debug != NULL && (debug == file || !strcmp (debug, file)))
+ debug = ".";
+
+ const unsigned char *id;
+ GElf_Addr id_vaddr;
+ int id_len = dwfl_module_build_id (mod, &id, &id_vaddr);
+
+ printf ("%#" PRIx64 "+%#" PRIx64 " ", start, end - start);
+
+ if (id_len > 0)
+ {
+ do
+ printf ("%02" PRIx8, *id++);
+ while (--id_len > 0);
+ if (id_vaddr != 0)
+ printf ("@%#" PRIx64, id_vaddr);
+ }
+ else
+ putchar ('-');
+
+ printf (" %s %s %s\n",
+ file ?: have_elf ? "." : "-",
+ debug ?: have_dwarf ? "." : "-",
+ name);
+}
+
+
+struct match_module_info
+{
+ char **patterns;
+ Dwfl_Module *found;
+ bool match_files;
+};
+
+static int
+match_module (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *name,
+ Dwarf_Addr start __attribute__ ((unused)),
+ void *arg)
+{
+ struct match_module_info *info = arg;
+
+ if (info->patterns[0] == NULL) /* Match all. */
+ {
+ match:
+ info->found = mod;
+ return DWARF_CB_ABORT;
+ }
+
+ if (info->match_files)
+ {
+ /* Make sure we've searched for the ELF file. */
+ GElf_Addr bias;
+ (void) dwfl_module_getelf (mod, &bias);
+
+ const char *file;
+ const char *check = dwfl_module_info (mod, NULL, NULL, NULL,
+ NULL, NULL, &file, NULL);
+ assert (check == name);
+ if (file == NULL)
+ return DWARF_CB_OK;
+
+ name = file;
+ }
+
+ for (char **p = info->patterns; *p != NULL; ++p)
+ if (fnmatch (*p, name, 0) == 0)
+ goto match;
+
+ return DWARF_CB_OK;
+}
+
+/* Handle files opened implicitly via libdwfl. */
+static void
+handle_implicit_modules (const struct arg_info *info)
+{
+ struct match_module_info mmi = { info->args, NULL, info->match_files };
+ inline ptrdiff_t next (ptrdiff_t offset)
+ {
+ return dwfl_getmodules (info->dwfl, &match_module, &mmi, offset);
+ }
+ ptrdiff_t offset = next (0);
+ if (offset == 0)
+ error (EXIT_FAILURE, 0, _("no matching modules found"));
+
+ if (info->list)
+ do
+ list_module (mmi.found);
+ while ((offset = next (offset)) > 0);
+ else if (info->output_dir == NULL)
+ {
+ if (next (offset) != 0)
+ error (EXIT_FAILURE, 0, _("matched more than one module"));
+ handle_dwfl_module (info->output_file, false, info->force, mmi.found,
+ info->all, info->ignore, info->relocate);
+ }
+ else
+ do
+ handle_output_dir_module (info->output_dir, mmi.found, info->force,
+ info->all, info->ignore,
+ info->modnames, info->relocate);
+ while ((offset = next (offset)) > 0);
+}
+
+int
+main (int argc, char **argv)
+{
+ /* We use no threads here which can interfere with handling a stream. */
+ __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+ __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+ __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ setlocale (LC_ALL, "");
+
+ /* Make sure the message catalog can be found. */
+ bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
+
+ /* Initialize the message catalog. */
+ textdomain (PACKAGE_TARNAME);
+
+ /* Parse and process arguments. */
+ const struct argp_child argp_children[] =
+ {
+ {
+ .argp = dwfl_standard_argp (),
+ .header = N_("Input selection options:"),
+ .group = 1,
+ },
+ { .argp = NULL },
+ };
+ const struct argp argp =
+ {
+ .options = options,
+ .parser = parse_opt,
+ .children = argp_children,
+ .args_doc = N_("STRIPPED-FILE DEBUG-FILE\n[MODULE...]"),
+ .doc = N_("\
+Combine stripped files with separate symbols and debug information.\n\
+\n\
+The first form puts the result in DEBUG-FILE if -o was not given.\n\
+\n\
+MODULE arguments give file name patterns matching modules to process.\n\
+With -f these match the file name of the main (stripped) file \
+(slashes are never special), otherwise they match the simple module names. \
+With no arguments, process all modules found.\n\
+\n\
+Multiple modules are written to files under OUTPUT-DIRECTORY, \
+creating subdirectories as needed. \
+With -m these files have simple module names, otherwise they have the \
+name of the main file complete with directory underneath OUTPUT-DIRECTORY.\n\
+\n\
+With -n no files are written, but one line to standard output for each module:\
+\n\tSTART+SIZE BUILDID FILE DEBUGFILE MODULENAME\n\
+START and SIZE are hexadecimal giving the address bounds of the module. \
+BUILDID is hexadecimal for the build ID bits, or - if no ID is known; \
+the hexadecimal may be followed by @0xADDR giving the address where the \
+ID resides if that is known. \
+FILE is the file name found for the module, or - if none was found, \
+or . if an ELF image is available but not from any named file. \
+DEBUGFILE is the separate debuginfo file name, \
+or - if no debuginfo was found, or . if FILE contains the debug information.\
+")
+ };
+
+ int remaining;
+ struct arg_info info = { .args = NULL };
+ error_t result = argp_parse (&argp, argc, argv, 0, &remaining, &info);
+ if (result == ENOSYS)
+ assert (info.dwfl == NULL);
+ else if (result)
+ return EXIT_FAILURE;
+ assert (info.args != NULL);
+
+ /* Tell the library which version we are expecting. */
+ elf_version (EV_CURRENT);
+
+ if (info.dwfl == NULL)
+ {
+ assert (result == ENOSYS);
+
+ if (info.output_dir != NULL)
+ {
+ char *file;
+ if (asprintf (&file, "%s/%s", info.output_dir, info.args[0]) < 0)
+ error (EXIT_FAILURE, 0, _("memory exhausted"));
+ handle_explicit_files (file, true, info.force,
+ info.args[0], info.args[1]);
+ free (file);
+ }
+ else
+ handle_explicit_files (info.output_file, false, info.force,
+ info.args[0], info.args[1]);
+ }
+ else
+ {
+ /* parse_opt checked this. */
+ assert (info.output_file != NULL || info.output_dir != NULL || info.list);
+
+ handle_implicit_modules (&info);
+
+ dwfl_end (info.dwfl);
+ }
+
+ return 0;
+}
+
+
+#include "debugpred.h"