diff options
Diffstat (limited to 'src/src/readelf.c')
-rw-r--r-- | src/src/readelf.c | 8499 |
1 files changed, 8499 insertions, 0 deletions
diff --git a/src/src/readelf.c b/src/src/readelf.c new file mode 100644 index 00000000..88766889 --- /dev/null +++ b/src/src/readelf.c @@ -0,0 +1,8499 @@ +/* Print information from ELF file in human-readable form. + Copyright (C) 1999-2012 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 1999. + + Red Hat elfutils 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; version 2 of the License. + + Red Hat 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 Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + <http://www.openinventionnetwork.com>. */ + +#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 <time.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <system.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. */ +static void print_version (FILE *stream, struct argp_state *state); +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_("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-headers", 'S', NULL, 0, N_("Display the sections' headers"), 0 }, + { "sections", 'S', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 }, + { "symbols", 's', NULL, 0, N_("Display the symbol table"), 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, frame, gdb_index, info, loc, line, ranges, pubnames, str, " + "macinfo, 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 }, + { "wide", 'W', NULL, 0, + N_("Ignored for compatibility (lines always wide)"), 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 +}; + + +/* 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; + +/* 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; + +/* 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_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) +} 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; + 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, "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, "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, "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; + } + /* Fall through. */ + case 'x': + add_dump_section (arg, false); + any_control_option = true; + break; + case 'N': + print_address_names = false; + 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; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) +{ + fprintf (stream, "readelf (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf (stream, gettext ("\ +Copyright (C) %s Red Hat, Inc.\n\ +This is free software; see the source for copying conditions. There is NO\n\ +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ +"), "2012"); + fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); +} + + +/* 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. */ +static int +find_no_debuginfo (Dwfl_Module *mod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *modname __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + const char *file_name __attribute__ ((unused)), + const char *debuglink_file __attribute__ ((unused)), + GElf_Word debuglink_crc __attribute__ ((unused)), + char **debuginfo_file_name __attribute__ ((unused))) +{ + return -1; +} + +/* 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; + + /* 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 stat64 st; + if (fstat64 (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); +} + + +/* 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. + So we need to get a fresh Elf handle on the file to display those. */ + bool print_unrelocated = (print_section_header + || print_relocations + || dump_data_sections != NULL + || print_notes); + + Elf *pure_elf = NULL; + Ebl *pure_ebl = ebl; + if (ehdr->e_type == ET_REL && print_unrelocated) + { + /* Read the file afresh. */ + off64_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 "???"; + } +} + + +/* 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")); + + 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_ORDERED) + *cp++ = 'O'; + if (shdr->sh_flags & SHF_EXCLUDE) + *cp++ = 'E'; + *cp = '\0'; + + char buf[128]; + printf ("[%2zu] %-20s %-12s %0*" PRIx64 " %0*" PRIx64 " %0*" PRIx64 + " %2" PRId64 " %-5s %2" PRId32 " %3" PRId32 + " %2" PRId64 "\n", + cnt, + elf_strptr (ebl->elf, shstrndx, shdr->sh_name) + ?: "<corrupt>", + 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); + } + + fputc_unlocked ('\n', stdout); +} + + +/* Print the program header. */ +static void +print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) +{ + if (ehdr->e_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) + { + /* We can show the user the name of the interpreter. */ + size_t maxsize; + char *filedata = elf_rawfile (ebl->elf, &maxsize); + + if (filedata != NULL && phdr->p_offset < maxsize) + 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 = NULL; + for (cnt2 = 0; cnt2 < phnum; ++cnt2) + { + GElf_Phdr phdr2_mem; + 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; + 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), + elf_strptr (ebl->elf, symshdr->sh_link, + gelf_getsym (symdata, shdr->sh_info, &sym_mem)->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) + 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; + Elf_Data *data; + size_t cnt; + size_t shstrndx; + + /* 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")); + + 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 / shdr->sh_entsize), + (unsigned long int) (shdr->sh_size / shdr->sh_entsize), + class == ELFCLASS32 ? 10 : 18, shdr->sh_addr, + shdr->sh_offset, + (int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &glink)->sh_name)); + fputs_unlocked (gettext (" Type Value\n"), stdout); + + for (cnt = 0; cnt < shdr->sh_size / shdr->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); + int nentries = shdr->sh_size / shdr->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 + { + destshdr = gelf_getshdr (elf_getscn (ebl->elf, + sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx), + &destshdr_mem); + + if (unlikely (destshdr == 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, destshdr->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); + int nentries = shdr->sh_size / shdr->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")); + + 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); + 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 + { + destshdr = gelf_getshdr (elf_getscn (ebl->elf, + sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx), + &destshdr_mem); + + if (unlikely (shdr == 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, destshdr->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) + 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")); + + /* 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); + GElf_Shdr glink; + 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, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &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; + 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, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &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); + + auxoffset += aux->vna_next; + } + + /* Find the next offset. */ + 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")); + + int class = gelf_getclass (ebl->elf); + GElf_Shdr glink; + 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, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &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)); + + auxoffset += aux->vda_next; + } + + /* Find the next offset. */ + 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)); + + 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)); + + auxoffset += aux->vna_next; + } + + 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 *)); + filename = (const char **) alloca (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); + GElf_Verdaux auxmem; + GElf_Verdaux *aux = gelf_getverdaux (defdata, + offset + def->vd_aux, + &auxmem); + if (unlikely (def == NULL || aux == NULL)) + break; + + vername[def->vd_ndx & 0x7fff] + = elf_strptr (ebl->elf, defshdr->sh_link, aux->vda_name); + filename[def->vd_ndx & 0x7fff] = NULL; + + 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); + + auxoffset += aux->vna_next; + } + + offset += need->vn_next; + } + } + } + else + { + vername = NULL; + nvername = 1; + filename = NULL; + } + + /* Print the header. */ + GElf_Shdr glink; + 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 / shdr->sh_entsize), + (unsigned int) elf_ndxscn (scn), + elf_strptr (ebl->elf, shstrndx, shdr->sh_name), + (int) (shdr->sh_size / shdr->sh_entsize), + class == ELFCLASS32 ? 10 : 18, shdr->sh_addr, + shdr->sh_offset, + (unsigned int) shdr->sh_link, + elf_strptr (ebl->elf, shstrndx, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &glink)->sh_name)); + + /* Now we can finally look at the actual contents of this section. */ + for (unsigned int cnt = 0; cnt < shdr->sh_size / shdr->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' : ' ', + (unsigned int) (*sym & 0x7fff) < nvername + ? vername[*sym & 0x7fff] : "???"); + if ((unsigned int) (*sym & 0x7fff) < nvername + && 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; + 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, + gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &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; + } + + Elf32_Word nbucket = ((Elf32_Word *) data->d_buf)[0]; + Elf32_Word nchain = ((Elf32_Word *) data->d_buf)[1]; + 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; + } + + Elf64_Xword nbucket = ((Elf64_Xword *) data->d_buf)[0]; + Elf64_Xword nchain = ((Elf64_Xword *) data->d_buf)[1]; + 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) +{ + 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; + } + + 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. */ + Elf32_Word bitmask_words = ((Elf32_Word *) data->d_buf)[2]; + if (gelf_getclass (ebl->elf) == ELFCLASS64) + bitmask_words *= 2; + + Elf32_Word shift = ((Elf32_Word *) data->d_buf)[3]; + + uint32_t *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; + } + 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) + { + 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) + { + int nentries = shdr->sh_size / shdr->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 (data == NULL) + return; + + const unsigned char *p = data->d_buf; + + 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; + } + + while (left () >= 4) + { + uint32_t len; + memcpy (&len, p, sizeof len); + + if (MY_ELFDATA != ehdr->e_ident[EI_DATA]) + CONVERT (len); + + if (unlikely (len > left ())) + break; + + const unsigned char *name = p + sizeof len; + p += len; + + unsigned const char *q = memchr (name, '\0', len); + if (unlikely (q == NULL)) + continue; + ++q; + + printf (gettext (" %-13s %4" PRIu32 "\n"), name, len); + + if (shdr->sh_type != SHT_GNU_ATTRIBUTES + || (q - name == sizeof "gnu" + && !memcmp (name, "gnu", sizeof "gnu"))) + while (q < p) + { + const unsigned char *const sub = q; + + unsigned int subsection_tag; + get_uleb128 (subsection_tag, q); + 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); + + if (unlikely (p - sub < (ptrdiff_t) subsection_len)) + break; + + const unsigned char *r = q + sizeof subsection_len; + q = sub + subsection_len; + + switch (subsection_tag) + { + default: + 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); + if (unlikely (r >= q)) + break; + + uint64_t value = 0; + const char *string = NULL; + if (tag == 32 || (tag & 1) == 0) + { + get_uleb128 (value, r); + if (r > q) + break; + } + if (tag == 32 || (tag & 1) != 0) + { + 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 + { + assert (tag != 32); + 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) +{ + /* See if there is a name we can give for this address. */ + GElf_Sym sym; + const char *name = print_address_names + ? dwfl_module_addrsym (dwflmod, address, &sym, NULL) : NULL; + if (name != NULL) + sym.st_value = address - sym.st_value; + + /* 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. */ + const char *scn = (i < 0 ? NULL + : dwfl_module_relocation_info (dwflmod, i, NULL)); + + char *result; + if ((name != NULL + ? (sym.st_value != 0 + ? (scn != NULL + ? (address_size == 0 + ? asprintf (&result, + gettext ("%s+%#" PRIx64 " <%s+%#" PRIx64 ">"), + scn, address, name, sym.st_value) + : asprintf (&result, + gettext ("%s+%#0*" PRIx64 " <%s+%#" PRIx64 ">"), + scn, 2 + address_size * 2, address, + name, sym.st_value)) + : (address_size == 0 + ? asprintf (&result, + gettext ("%#" PRIx64 " <%s+%#" PRIx64 ">"), + address, name, sym.st_value) + : asprintf (&result, + gettext ("%#0*" PRIx64 " <%s+%#" PRIx64 ">"), + 2 + address_size * 2, address, + name, sym.st_value))) + : (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) +{ + static const char *const known_tags[] = + { + [DW_TAG_array_type] = "array_type", + [DW_TAG_class_type] = "class_type", + [DW_TAG_entry_point] = "entry_point", + [DW_TAG_enumeration_type] = "enumeration_type", + [DW_TAG_formal_parameter] = "formal_parameter", + [DW_TAG_imported_declaration] = "imported_declaration", + [DW_TAG_label] = "label", + [DW_TAG_lexical_block] = "lexical_block", + [DW_TAG_member] = "member", + [DW_TAG_pointer_type] = "pointer_type", + [DW_TAG_reference_type] = "reference_type", + [DW_TAG_compile_unit] = "compile_unit", + [DW_TAG_string_type] = "string_type", + [DW_TAG_structure_type] = "structure_type", + [DW_TAG_subroutine_type] = "subroutine_type", + [DW_TAG_typedef] = "typedef", + [DW_TAG_union_type] = "union_type", + [DW_TAG_unspecified_parameters] = "unspecified_parameters", + [DW_TAG_variant] = "variant", + [DW_TAG_common_block] = "common_block", + [DW_TAG_common_inclusion] = "common_inclusion", + [DW_TAG_inheritance] = "inheritance", + [DW_TAG_inlined_subroutine] = "inlined_subroutine", + [DW_TAG_module] = "module", + [DW_TAG_ptr_to_member_type] = "ptr_to_member_type", + [DW_TAG_set_type] = "set_type", + [DW_TAG_subrange_type] = "subrange_type", + [DW_TAG_with_stmt] = "with_stmt", + [DW_TAG_access_declaration] = "access_declaration", + [DW_TAG_base_type] = "base_type", + [DW_TAG_catch_block] = "catch_block", + [DW_TAG_const_type] = "const_type", + [DW_TAG_constant] = "constant", + [DW_TAG_enumerator] = "enumerator", + [DW_TAG_file_type] = "file_type", + [DW_TAG_friend] = "friend", + [DW_TAG_namelist] = "namelist", + [DW_TAG_namelist_item] = "namelist_item", + [DW_TAG_packed_type] = "packed_type", + [DW_TAG_subprogram] = "subprogram", + [DW_TAG_template_type_parameter] = "template_type_parameter", + [DW_TAG_template_value_parameter] = "template_value_parameter", + [DW_TAG_thrown_type] = "thrown_type", + [DW_TAG_try_block] = "try_block", + [DW_TAG_variant_part] = "variant_part", + [DW_TAG_variable] = "variable", + [DW_TAG_volatile_type] = "volatile_type", + [DW_TAG_dwarf_procedure] = "dwarf_procedure", + [DW_TAG_restrict_type] = "restrict_type", + [DW_TAG_interface_type] = "interface_type", + [DW_TAG_namespace] = "namespace", + [DW_TAG_imported_module] = "imported_module", + [DW_TAG_unspecified_type] = "unspecified_type", + [DW_TAG_partial_unit] = "partial_unit", + [DW_TAG_imported_unit] = "imported_unit", + [DW_TAG_mutable_type] = "mutable_type", + [DW_TAG_condition] = "condition", + [DW_TAG_shared_type] = "shared_type", + [DW_TAG_type_unit] = "type_unit", + [DW_TAG_rvalue_reference_type] = "rvalue_reference_type", + [DW_TAG_template_alias] = "template_alias", + }; + const unsigned int nknown_tags = (sizeof (known_tags) + / sizeof (known_tags[0])); + static char buf[40]; + const char *result = NULL; + + if (likely (tag < nknown_tags)) + result = known_tags[tag]; + + if (unlikely (result == NULL)) + /* There are a few known extensions. */ + switch (tag) + { + case DW_TAG_MIPS_loop: + result = "MIPS_loop"; + break; + + case DW_TAG_format_label: + result = "format_label"; + break; + + case DW_TAG_function_template: + result = "function_template"; + break; + + case DW_TAG_class_template: + result = "class_template"; + break; + + case DW_TAG_GNU_BINCL: + result = "GNU_BINCL"; + break; + + case DW_TAG_GNU_EINCL: + result = "GNU_EINCL"; + break; + + case DW_TAG_GNU_template_template_param: + result = "GNU_template_template_param"; + break; + + case DW_TAG_GNU_template_parameter_pack: + result = "GNU_template_parameter_pack"; + break; + + case DW_TAG_GNU_formal_parameter_pack: + result = "GNU_formal_parameter_pack"; + break; + + case DW_TAG_GNU_call_site: + result = "GNU_call_site"; + break; + + case DW_TAG_GNU_call_site_parameter: + result = "GNU_call_site_parameter"; + break; + + default: + if (tag < DW_TAG_lo_user) + snprintf (buf, sizeof buf, gettext ("unknown tag %hx"), tag); + else + snprintf (buf, sizeof buf, gettext ("unknown user tag %hx"), tag); + result = buf; + break; + } + + return result; +} + + +static const char * +dwarf_attr_string (unsigned int attrnum) +{ + static const char *const known_attrs[] = + { + [DW_AT_sibling] = "sibling", + [DW_AT_location] = "location", + [DW_AT_name] = "name", + [DW_AT_ordering] = "ordering", + [DW_AT_subscr_data] = "subscr_data", + [DW_AT_byte_size] = "byte_size", + [DW_AT_bit_offset] = "bit_offset", + [DW_AT_bit_size] = "bit_size", + [DW_AT_element_list] = "element_list", + [DW_AT_stmt_list] = "stmt_list", + [DW_AT_low_pc] = "low_pc", + [DW_AT_high_pc] = "high_pc", + [DW_AT_language] = "language", + [DW_AT_member] = "member", + [DW_AT_discr] = "discr", + [DW_AT_discr_value] = "discr_value", + [DW_AT_visibility] = "visibility", + [DW_AT_import] = "import", + [DW_AT_string_length] = "string_length", + [DW_AT_common_reference] = "common_reference", + [DW_AT_comp_dir] = "comp_dir", + [DW_AT_const_value] = "const_value", + [DW_AT_containing_type] = "containing_type", + [DW_AT_default_value] = "default_value", + [DW_AT_inline] = "inline", + [DW_AT_is_optional] = "is_optional", + [DW_AT_lower_bound] = "lower_bound", + [DW_AT_producer] = "producer", + [DW_AT_prototyped] = "prototyped", + [DW_AT_return_addr] = "return_addr", + [DW_AT_start_scope] = "start_scope", + [DW_AT_bit_stride] = "bit_stride", + [DW_AT_upper_bound] = "upper_bound", + [DW_AT_abstract_origin] = "abstract_origin", + [DW_AT_accessibility] = "accessibility", + [DW_AT_address_class] = "address_class", + [DW_AT_artificial] = "artificial", + [DW_AT_base_types] = "base_types", + [DW_AT_calling_convention] = "calling_convention", + [DW_AT_count] = "count", + [DW_AT_data_member_location] = "data_member_location", + [DW_AT_decl_column] = "decl_column", + [DW_AT_decl_file] = "decl_file", + [DW_AT_decl_line] = "decl_line", + [DW_AT_declaration] = "declaration", + [DW_AT_discr_list] = "discr_list", + [DW_AT_encoding] = "encoding", + [DW_AT_external] = "external", + [DW_AT_frame_base] = "frame_base", + [DW_AT_friend] = "friend", + [DW_AT_identifier_case] = "identifier_case", + [DW_AT_macro_info] = "macro_info", + [DW_AT_namelist_item] = "namelist_item", + [DW_AT_priority] = "priority", + [DW_AT_segment] = "segment", + [DW_AT_specification] = "specification", + [DW_AT_static_link] = "static_link", + [DW_AT_type] = "type", + [DW_AT_use_location] = "use_location", + [DW_AT_variable_parameter] = "variable_parameter", + [DW_AT_virtuality] = "virtuality", + [DW_AT_vtable_elem_location] = "vtable_elem_location", + [DW_AT_allocated] = "allocated", + [DW_AT_associated] = "associated", + [DW_AT_data_location] = "data_location", + [DW_AT_byte_stride] = "byte_stride", + [DW_AT_entry_pc] = "entry_pc", + [DW_AT_use_UTF8] = "use_UTF8", + [DW_AT_extension] = "extension", + [DW_AT_ranges] = "ranges", + [DW_AT_trampoline] = "trampoline", + [DW_AT_call_column] = "call_column", + [DW_AT_call_file] = "call_file", + [DW_AT_call_line] = "call_line", + [DW_AT_description] = "description", + [DW_AT_binary_scale] = "binary_scale", + [DW_AT_decimal_scale] = "decimal_scale", + [DW_AT_small] = "small", + [DW_AT_decimal_sign] = "decimal_sign", + [DW_AT_digit_count] = "digit_count", + [DW_AT_picture_string] = "picture_string", + [DW_AT_mutable] = "mutable", + [DW_AT_threads_scaled] = "threads_scaled", + [DW_AT_explicit] = "explicit", + [DW_AT_object_pointer] = "object_pointer", + [DW_AT_endianity] = "endianity", + [DW_AT_elemental] = "elemental", + [DW_AT_pure] = "pure", + [DW_AT_recursive] = "recursive", + [DW_AT_signature] = "signature", + [DW_AT_main_subprogram] = "main_subprogram", + [DW_AT_data_bit_offset] = "data_bit_offset", + [DW_AT_const_expr] = "const_expr", + [DW_AT_enum_class] = "enum_class", + [DW_AT_linkage_name] = "linkage_name", + }; + const unsigned int nknown_attrs = (sizeof (known_attrs) + / sizeof (known_attrs[0])); + static char buf[40]; + const char *result = NULL; + + if (likely (attrnum < nknown_attrs)) + result = known_attrs[attrnum]; + + if (unlikely (result == NULL)) + /* There are a few known extensions. */ + switch (attrnum) + { + case DW_AT_MIPS_fde: + result = "MIPS_fde"; + break; + + case DW_AT_MIPS_loop_begin: + result = "MIPS_loop_begin"; + break; + + case DW_AT_MIPS_tail_loop_begin: + result = "MIPS_tail_loop_begin"; + break; + + case DW_AT_MIPS_epilog_begin: + result = "MIPS_epilog_begin"; + break; + + case DW_AT_MIPS_loop_unroll_factor: + result = "MIPS_loop_unroll_factor"; + break; + + case DW_AT_MIPS_software_pipeline_depth: + result = "MIPS_software_pipeline_depth"; + break; + + case DW_AT_MIPS_linkage_name: + result = "MIPS_linkage_name"; + break; + + case DW_AT_MIPS_stride: + result = "MIPS_stride"; + break; + + case DW_AT_MIPS_abstract_name: + result = "MIPS_abstract_name"; + break; + + case DW_AT_MIPS_clone_origin: + result = "MIPS_clone_origin"; + break; + + case DW_AT_MIPS_has_inlines: + result = "MIPS_has_inlines"; + break; + + case DW_AT_MIPS_stride_byte: + result = "MIPS_stride_byte"; + break; + + case DW_AT_MIPS_stride_elem: + result = "MIPS_stride_elem"; + break; + + case DW_AT_MIPS_ptr_dopetype: + result = "MIPS_ptr_dopetype"; + break; + + case DW_AT_MIPS_allocatable_dopetype: + result = "MIPS_allocatable_dopetype"; + break; + + case DW_AT_MIPS_assumed_shape_dopetype: + result = "MIPS_assumed_shape_dopetype"; + break; + + case DW_AT_MIPS_assumed_size: + result = "MIPS_assumed_size"; + break; + + case DW_AT_sf_names: + result = "sf_names"; + break; + + case DW_AT_src_info: + result = "src_info"; + break; + + case DW_AT_mac_info: + result = "mac_info"; + break; + + case DW_AT_src_coords: + result = "src_coords"; + break; + + case DW_AT_body_begin: + result = "body_begin"; + break; + + case DW_AT_body_end: + result = "body_end"; + break; + + case DW_AT_GNU_vector: + result = "GNU_vector"; + break; + + case DW_AT_GNU_guarded_by: + result = "GNU_guarded_by"; + break; + + case DW_AT_GNU_pt_guarded_by: + result = "GNU_pt_guarded_by"; + break; + + case DW_AT_GNU_guarded: + result = "GNU_guarded"; + break; + + case DW_AT_GNU_pt_guarded: + result = "GNU_pt_guarded"; + break; + + case DW_AT_GNU_locks_excluded: + result = "GNU_locks_excluded"; + break; + + case DW_AT_GNU_exclusive_locks_required: + result = "GNU_exclusive_locks_required"; + break; + + case DW_AT_GNU_shared_locks_required: + result = "GNU_shared_locks_required"; + break; + + case DW_AT_GNU_odr_signature: + result = "GNU_odr_signature"; + break; + + case DW_AT_GNU_template_name: + result = "GNU_template_name"; + break; + + case DW_AT_GNU_call_site_value: + result = "GNU_call_site_value"; + break; + + case DW_AT_GNU_call_site_data_value: + result = "GNU_call_site_data_value"; + break; + + case DW_AT_GNU_call_site_target: + result = "GNU_call_site_target"; + break; + + case DW_AT_GNU_call_site_target_clobbered: + result = "GNU_call_site_target_clobbered"; + break; + + case DW_AT_GNU_tail_call: + result = "GNU_tail_call"; + break; + + case DW_AT_GNU_all_tail_call_sites: + result = "GNU_all_tail_call_sites"; + break; + + case DW_AT_GNU_all_call_sites: + result = "GNU_all_call_sites"; + break; + + case DW_AT_GNU_all_source_call_sites: + result = "GNU_all_source_call_sites"; + break; + + default: + if (attrnum < DW_AT_lo_user) + snprintf (buf, sizeof buf, gettext ("unknown attribute %hx"), + attrnum); + else + snprintf (buf, sizeof buf, gettext ("unknown user attribute %hx"), + attrnum); + result = buf; + break; + } + + return result; +} + + +static const char * +dwarf_form_string (unsigned int form) +{ + static const char *const known_forms[] = + { + [DW_FORM_addr] = "addr", + [DW_FORM_block2] = "block2", + [DW_FORM_block4] = "block4", + [DW_FORM_data2] = "data2", + [DW_FORM_data4] = "data4", + [DW_FORM_data8] = "data8", + [DW_FORM_string] = "string", + [DW_FORM_block] = "block", + [DW_FORM_block1] = "block1", + [DW_FORM_data1] = "data1", + [DW_FORM_flag] = "flag", + [DW_FORM_sdata] = "sdata", + [DW_FORM_strp] = "strp", + [DW_FORM_udata] = "udata", + [DW_FORM_ref_addr] = "ref_addr", + [DW_FORM_ref1] = "ref1", + [DW_FORM_ref2] = "ref2", + [DW_FORM_ref4] = "ref4", + [DW_FORM_ref8] = "ref8", + [DW_FORM_ref_udata] = "ref_udata", + [DW_FORM_indirect] = "indirect", + [DW_FORM_sec_offset] = "sec_offset", + [DW_FORM_exprloc] = "exprloc", + [DW_FORM_flag_present] = "flag_present", + [DW_FORM_ref_sig8] = "ref_sig8", + }; + const unsigned int nknown_forms = (sizeof (known_forms) + / sizeof (known_forms[0])); + static char buf[40]; + const char *result = NULL; + + if (likely (form < nknown_forms)) + result = known_forms[form]; + + if (unlikely (result == NULL)) + { + snprintf (buf, sizeof buf, gettext ("unknown form %#" PRIx64), + (uint64_t) form); + result = buf; + } + + return result; +} + + +static const char * +dwarf_lang_string (unsigned int lang) +{ + static const char *const known[] = + { + [DW_LANG_C89] = "ISO C89", + [DW_LANG_C] = "C", + [DW_LANG_Ada83] = "Ada83", + [DW_LANG_C_plus_plus] = "C++", + [DW_LANG_Cobol74] = "Cobol74", + [DW_LANG_Cobol85] = "Cobol85", + [DW_LANG_Fortran77] = "Fortran77", + [DW_LANG_Fortran90] = "Fortran90", + [DW_LANG_Pascal83] = "Pascal83", + [DW_LANG_Modula2] = "Modula2", + [DW_LANG_Java] = "Java", + [DW_LANG_C99] = "ISO C99", + [DW_LANG_Ada95] = "Ada95", + [DW_LANG_Fortran95] = "Fortran95", + [DW_LANG_PL1] = "PL1", + [DW_LANG_Objc] = "Objective C", + [DW_LANG_ObjC_plus_plus] = "Objective C++", + [DW_LANG_UPC] = "UPC", + [DW_LANG_D] = "D", + }; + + if (likely (lang < sizeof (known) / sizeof (known[0]))) + return known[lang]; + else if (lang == DW_LANG_Mips_Assembler) + /* This language tag is used for assembler in general. */ + return "Assembler"; + + if (lang >= DW_LANG_lo_user && lang <= DW_LANG_hi_user) + { + static char buf[30]; + snprintf (buf, sizeof (buf), "lo_user+%u", lang - DW_LANG_lo_user); + return buf; + } + + return "???"; +} + + +static const char * +dwarf_inline_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_INL_not_inlined] = "not_inlined", + [DW_INL_inlined] = "inlined", + [DW_INL_declared_not_inlined] = "declared_not_inlined", + [DW_INL_declared_inlined] = "declared_inlined" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_encoding_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_ATE_void] = "void", + [DW_ATE_address] = "address", + [DW_ATE_boolean] = "boolean", + [DW_ATE_complex_float] = "complex_float", + [DW_ATE_float] = "float", + [DW_ATE_signed] = "signed", + [DW_ATE_signed_char] = "signed_char", + [DW_ATE_unsigned] = "unsigned", + [DW_ATE_unsigned_char] = "unsigned_char", + [DW_ATE_imaginary_float] = "imaginary_float", + [DW_ATE_packed_decimal] = "packed_decimal", + [DW_ATE_numeric_string] = "numeric_string", + [DW_ATE_edited] = "edited", + [DW_ATE_signed_fixed] = "signed_fixed", + [DW_ATE_unsigned_fixed] = "unsigned_fixed", + [DW_ATE_decimal_float] = "decimal_float", + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + if (code >= DW_ATE_lo_user && code <= DW_ATE_hi_user) + { + static char buf[30]; + snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_ATE_lo_user); + return buf; + } + + return "???"; +} + + +static const char * +dwarf_access_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_ACCESS_public] = "public", + [DW_ACCESS_protected] = "protected", + [DW_ACCESS_private] = "private" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_visibility_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_VIS_local] = "local", + [DW_VIS_exported] = "exported", + [DW_VIS_qualified] = "qualified" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_virtuality_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_VIRTUALITY_none] = "none", + [DW_VIRTUALITY_virtual] = "virtual", + [DW_VIRTUALITY_pure_virtual] = "pure_virtual" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_identifier_case_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_ID_case_sensitive] = "sensitive", + [DW_ID_up_case] = "up_case", + [DW_ID_down_case] = "down_case", + [DW_ID_case_insensitive] = "insensitive" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_calling_convention_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_CC_normal] = "normal", + [DW_CC_program] = "program", + [DW_CC_nocall] = "nocall", + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + if (code >= DW_CC_lo_user && code <= DW_CC_hi_user) + { + static char buf[30]; + snprintf (buf, sizeof (buf), "lo_user+%u", code - DW_CC_lo_user); + return buf; + } + + return "???"; +} + + +static const char * +dwarf_ordering_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_ORD_row_major] = "row_major", + [DW_ORD_col_major] = "col_major" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +static const char * +dwarf_discr_list_string (unsigned int code) +{ + static const char *const known[] = + { + [DW_DSC_label] = "label", + [DW_DSC_range] = "range" + }; + + if (likely (code < sizeof (known) / sizeof (known[0]))) + return known[code]; + + return "???"; +} + + +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, + Dwarf_Word len, const unsigned char *data) +{ + const unsigned int ref_size = vers < 3 ? addrsize : offset_size; + + static const char *const known[] = + { + [DW_OP_addr] = "addr", + [DW_OP_deref] = "deref", + [DW_OP_const1u] = "const1u", + [DW_OP_const1s] = "const1s", + [DW_OP_const2u] = "const2u", + [DW_OP_const2s] = "const2s", + [DW_OP_const4u] = "const4u", + [DW_OP_const4s] = "const4s", + [DW_OP_const8u] = "const8u", + [DW_OP_const8s] = "const8s", + [DW_OP_constu] = "constu", + [DW_OP_consts] = "consts", + [DW_OP_dup] = "dup", + [DW_OP_drop] = "drop", + [DW_OP_over] = "over", + [DW_OP_pick] = "pick", + [DW_OP_swap] = "swap", + [DW_OP_rot] = "rot", + [DW_OP_xderef] = "xderef", + [DW_OP_abs] = "abs", + [DW_OP_and] = "and", + [DW_OP_div] = "div", + [DW_OP_minus] = "minus", + [DW_OP_mod] = "mod", + [DW_OP_mul] = "mul", + [DW_OP_neg] = "neg", + [DW_OP_not] = "not", + [DW_OP_or] = "or", + [DW_OP_plus] = "plus", + [DW_OP_plus_uconst] = "plus_uconst", + [DW_OP_shl] = "shl", + [DW_OP_shr] = "shr", + [DW_OP_shra] = "shra", + [DW_OP_xor] = "xor", + [DW_OP_bra] = "bra", + [DW_OP_eq] = "eq", + [DW_OP_ge] = "ge", + [DW_OP_gt] = "gt", + [DW_OP_le] = "le", + [DW_OP_lt] = "lt", + [DW_OP_ne] = "ne", + [DW_OP_skip] = "skip", + [DW_OP_lit0] = "lit0", + [DW_OP_lit1] = "lit1", + [DW_OP_lit2] = "lit2", + [DW_OP_lit3] = "lit3", + [DW_OP_lit4] = "lit4", + [DW_OP_lit5] = "lit5", + [DW_OP_lit6] = "lit6", + [DW_OP_lit7] = "lit7", + [DW_OP_lit8] = "lit8", + [DW_OP_lit9] = "lit9", + [DW_OP_lit10] = "lit10", + [DW_OP_lit11] = "lit11", + [DW_OP_lit12] = "lit12", + [DW_OP_lit13] = "lit13", + [DW_OP_lit14] = "lit14", + [DW_OP_lit15] = "lit15", + [DW_OP_lit16] = "lit16", + [DW_OP_lit17] = "lit17", + [DW_OP_lit18] = "lit18", + [DW_OP_lit19] = "lit19", + [DW_OP_lit20] = "lit20", + [DW_OP_lit21] = "lit21", + [DW_OP_lit22] = "lit22", + [DW_OP_lit23] = "lit23", + [DW_OP_lit24] = "lit24", + [DW_OP_lit25] = "lit25", + [DW_OP_lit26] = "lit26", + [DW_OP_lit27] = "lit27", + [DW_OP_lit28] = "lit28", + [DW_OP_lit29] = "lit29", + [DW_OP_lit30] = "lit30", + [DW_OP_lit31] = "lit31", + [DW_OP_reg0] = "reg0", + [DW_OP_reg1] = "reg1", + [DW_OP_reg2] = "reg2", + [DW_OP_reg3] = "reg3", + [DW_OP_reg4] = "reg4", + [DW_OP_reg5] = "reg5", + [DW_OP_reg6] = "reg6", + [DW_OP_reg7] = "reg7", + [DW_OP_reg8] = "reg8", + [DW_OP_reg9] = "reg9", + [DW_OP_reg10] = "reg10", + [DW_OP_reg11] = "reg11", + [DW_OP_reg12] = "reg12", + [DW_OP_reg13] = "reg13", + [DW_OP_reg14] = "reg14", + [DW_OP_reg15] = "reg15", + [DW_OP_reg16] = "reg16", + [DW_OP_reg17] = "reg17", + [DW_OP_reg18] = "reg18", + [DW_OP_reg19] = "reg19", + [DW_OP_reg20] = "reg20", + [DW_OP_reg21] = "reg21", + [DW_OP_reg22] = "reg22", + [DW_OP_reg23] = "reg23", + [DW_OP_reg24] = "reg24", + [DW_OP_reg25] = "reg25", + [DW_OP_reg26] = "reg26", + [DW_OP_reg27] = "reg27", + [DW_OP_reg28] = "reg28", + [DW_OP_reg29] = "reg29", + [DW_OP_reg30] = "reg30", + [DW_OP_reg31] = "reg31", + [DW_OP_breg0] = "breg0", + [DW_OP_breg1] = "breg1", + [DW_OP_breg2] = "breg2", + [DW_OP_breg3] = "breg3", + [DW_OP_breg4] = "breg4", + [DW_OP_breg5] = "breg5", + [DW_OP_breg6] = "breg6", + [DW_OP_breg7] = "breg7", + [DW_OP_breg8] = "breg8", + [DW_OP_breg9] = "breg9", + [DW_OP_breg10] = "breg10", + [DW_OP_breg11] = "breg11", + [DW_OP_breg12] = "breg12", + [DW_OP_breg13] = "breg13", + [DW_OP_breg14] = "breg14", + [DW_OP_breg15] = "breg15", + [DW_OP_breg16] = "breg16", + [DW_OP_breg17] = "breg17", + [DW_OP_breg18] = "breg18", + [DW_OP_breg19] = "breg19", + [DW_OP_breg20] = "breg20", + [DW_OP_breg21] = "breg21", + [DW_OP_breg22] = "breg22", + [DW_OP_breg23] = "breg23", + [DW_OP_breg24] = "breg24", + [DW_OP_breg25] = "breg25", + [DW_OP_breg26] = "breg26", + [DW_OP_breg27] = "breg27", + [DW_OP_breg28] = "breg28", + [DW_OP_breg29] = "breg29", + [DW_OP_breg30] = "breg30", + [DW_OP_breg31] = "breg31", + [DW_OP_regx] = "regx", + [DW_OP_fbreg] = "fbreg", + [DW_OP_bregx] = "bregx", + [DW_OP_piece] = "piece", + [DW_OP_deref_size] = "deref_size", + [DW_OP_xderef_size] = "xderef_size", + [DW_OP_nop] = "nop", + [DW_OP_push_object_address] = "push_object_address", + [DW_OP_call2] = "call2", + [DW_OP_call4] = "call4", + [DW_OP_call_ref] = "call_ref", + [DW_OP_form_tls_address] = "form_tls_address", + [DW_OP_call_frame_cfa] = "call_frame_cfa", + [DW_OP_bit_piece] = "bit_piece", + [DW_OP_implicit_value] = "implicit_value", + [DW_OP_stack_value] = "stack_value", + [DW_OP_GNU_implicit_pointer] = "GNU_implicit_pointer", + [DW_OP_GNU_entry_value] = "GNU_entry_value", + [DW_OP_GNU_const_type] = "GNU_const_type", + [DW_OP_GNU_regval_type] = "GNU_regval_type", + [DW_OP_GNU_deref_type] = "GNU_deref_type", + [DW_OP_GNU_convert] = "GNU_convert", + [DW_OP_GNU_reinterpret] = "GNU_reinterpret", + }; + + 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++; + + switch (op) + { + case DW_OP_addr:; + /* Address operand. */ + Dwarf_Word addr; + NEED (addrsize); + if (addrsize == 4) + addr = read_4ubyte_unaligned (dbg, data); + else + { + assert (addrsize == 8); + addr = read_8ubyte_unaligned (dbg, data); + } + data += addrsize; + CONSUME (addrsize); + + char *a = format_dwarf_addr (dwflmod, 0, addr); + printf ("%*s[%4" PRIuMAX "] %s %s\n", + indent, "", (uintmax_t) offset, known[op], a); + free (a); + + offset += 1 + addrsize; + break; + + case DW_OP_call_ref: + /* Offset operand. */ + NEED (ref_size); + if (ref_size == 4) + addr = read_4ubyte_unaligned (dbg, data); + else + { + assert (ref_size == 8); + addr = read_8ubyte_unaligned (dbg, data); + } + data += ref_size; + CONSUME (ref_size); + + printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n", + indent, "", (uintmax_t) offset, + known[op], (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[%4" PRIuMAX "] %s %" PRIu8 "\n", + indent, "", (uintmax_t) offset, + known[op], *((uint8_t *) data)); + ++data; + --len; + offset += 2; + break; + + case DW_OP_const2u: + NEED (2); + // XXX value might be modified by relocation + printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n", + indent, "", (uintmax_t) offset, + known[op], 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[%4" PRIuMAX "] %s %" PRIu32 "\n", + indent, "", (uintmax_t) offset, + known[op], 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[%4" PRIuMAX "] %s %" PRIu64 "\n", + indent, "", (uintmax_t) offset, + known[op], 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[%4" PRIuMAX "] %s %" PRId8 "\n", + indent, "", (uintmax_t) offset, + known[op], *((int8_t *) data)); + ++data; + --len; + offset += 2; + break; + + case DW_OP_const2s: + NEED (2); + // XXX value might be modified by relocation + printf ("%*s[%4" PRIuMAX "] %s %" PRId16 "\n", + indent, "", (uintmax_t) offset, + known[op], 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[%4" PRIuMAX "] %s %" PRId32 "\n", + indent, "", (uintmax_t) offset, + known[op], 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[%4" PRIuMAX "] %s %" PRId64 "\n", + indent, "", (uintmax_t) offset, + known[op], 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); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n", + indent, "", (uintmax_t) offset, known[op], uleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_bit_piece: + start = data; + uint64_t uleb2; + NEED (2); + get_uleb128 (uleb, data); /* XXX check overrun */ + get_uleb128 (uleb2, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 ", %" PRIu64 "\n", + indent, "", (uintmax_t) offset, known[op], 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); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n", + indent, "", (uintmax_t) offset, known[op], sleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_bregx: + start = data; + NEED (2); + get_uleb128 (uleb, data); /* XXX check overrun */ + get_sleb128 (sleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 " %" PRId64 "\n", + indent, "", (uintmax_t) offset, known[op], uleb, sleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_call2: + NEED (2); + printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n", + indent, "", (uintmax_t) offset, known[op], + read_2ubyte_unaligned (dbg, data)); + CONSUME (2); + offset += 3; + break; + + case DW_OP_call4: + NEED (4); + printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n", + indent, "", (uintmax_t) offset, known[op], + read_4ubyte_unaligned (dbg, data)); + CONSUME (4); + offset += 5; + break; + + case DW_OP_skip: + case DW_OP_bra: + NEED (2); + printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n", + indent, "", (uintmax_t) offset, known[op], + (uintmax_t) (offset + read_2sbyte_unaligned (dbg, data))); + CONSUME (2); + data += 2; + offset += 3; + break; + + case DW_OP_implicit_value: + start = data; + NEED (1); + get_uleb128 (uleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s: ", + indent, "", (uintmax_t) offset, known[op]); + 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 + 1); + if (ref_size == 4) + addr = read_4ubyte_unaligned (dbg, data); + else + { + assert (ref_size == 8); + addr = read_8ubyte_unaligned (dbg, data); + } + data += ref_size; + /* Byte offset operand. */ + get_sleb128 (sleb, data); /* XXX check overrun */ + + printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX ", %+" PRId64 "\n", + indent, "", (intmax_t) offset, + known[op], (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); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s:\n", + indent, "", (uintmax_t) offset, known[op]); + NEED (uleb); + print_ops (dwflmod, dbg, indent + 6, indent + 6, vers, + addrsize, offset_size, uleb, data); + data += uleb; + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_GNU_const_type: + /* DIE offset, size plus block. */ + start = data; + NEED (2); + get_uleb128 (uleb, data); /* XXX check overrun */ + uint8_t usize = *(uint8_t *) data++; + NEED (usize); + printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "] ", + indent, "", (uintmax_t) offset, known[op], uleb); + print_block (usize, data); + data += usize; + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_GNU_regval_type: + start = data; + NEED (2); + get_uleb128 (uleb, data); /* XXX check overrun */ + get_uleb128 (uleb2, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 " %#" PRIx64 "\n", + indent, "", (uintmax_t) offset, known[op], uleb, uleb2); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_GNU_deref_type: + start = data; + NEED (2); + usize = *(uint8_t *) data++; + get_uleb128 (uleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s %" PRIu8 " [%6" PRIxMAX "]\n", + indent, "", (uintmax_t) offset, + known[op], usize, uleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + case DW_OP_GNU_convert: + case DW_OP_GNU_reinterpret: + start = data; + NEED (1); + get_uleb128 (uleb, data); /* XXX check overrun */ + printf ("%*s[%4" PRIuMAX "] %s [%6" PRIxMAX "]\n", + indent, "", (uintmax_t) offset, known[op], uleb); + CONSUME (data - start); + offset += 1 + (data - start); + break; + + default: + /* No Operand. */ + if (op < sizeof known / sizeof known[0] && known[op] != NULL) + printf ("%*s[%4" PRIuMAX "] %s\n", + indent, "", (uintmax_t) offset, known[op]); + else + printf ("%*s[%4" PRIuMAX "] %#x\n", + indent, "", (uintmax_t) offset, op); + ++offset; + break; + } + + indent = indentrest; + continue; + + invalid: + printf (gettext ("%*s[%4" PRIuMAX "] %s <TRUNCATED>\n"), + indent, "", (uintmax_t) offset, known[op]); + break; + } +} + + +struct listptr +{ + Dwarf_Off offset:(64 - 3); + bool addr64:1; + bool dwarf64:1; + bool warned:1; +}; + +#define listptr_offset_size(p) ((p)->dwarf64 ? 8 : 4) +#define listptr_address_size(p) ((p)->addr64 ? 8 : 4) + +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); + } + } + + 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; +} + +static void +notice_listptr (enum section_e section, struct listptr_table *table, + uint_fast8_t address_size, uint_fast8_t offset_size, + 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 + }; + assert (p->offset == offset); + } +} + +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, + 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); + + 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) +{ + 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 < dbg->sectiondata[IDX_debug_abbrev]->d_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_string (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_string (name), dwarf_form_string (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_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), + 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; + } + + 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_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 = elf_rawdata (scn, NULL); + + 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; + unsigned char *const endp = (unsigned char *) data->d_buf + data->d_size; + unsigned char *readp = data->d_buf; + while (readp < endp) + { + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + + if (first && skip_listptr_hole (&known_rangelistptr, &listptr_idx, + &address_size, NULL, + offset, &readp, endp)) + continue; + + if (unlikely (data->d_size - offset < 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); + printf (gettext (" [%6tx] base address %s\n"), offset, b); + free (b); + } + else if (begin == 0 && end == 0) /* End of list entry. */ + { + if (first) + printf (gettext (" [%6tx] empty list\n"), offset); + first = true; + } + else + { + char *b = format_dwarf_addr (dwflmod, address_size, begin); + char *e = format_dwarf_addr (dwflmod, address_size, end); + /* We have an address range entry. */ + if (first) /* First address range entry in a list. */ + printf (gettext (" [%6tx] %s..%s\n"), offset, b, e); + else + printf (gettext (" %s..%s\n"), b, 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) + { + snprintf (name, REGNAMESZ, "reg%u", loc->regno); + if (bits != NULL) + *bits = loc->bits; + if (type != NULL) + *type = DW_ATE_unsigned; + set = "??? unrecognized"; + } + else + { + if (bits != NULL && *bits <= 0) + *bits = loc->bits; + if (type != NULL && *type == DW_ATE_void) + *type = DW_ATE_unsigned; + + } + return set; +} + +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, + 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: + // XXX overflow check + get_uleb128 (op1, readp); + op1 += vma_base; + printf (" set_loc %" PRIu64 "\n", op1 * code_align); + break; + case DW_CFA_advance_loc1: + printf (" advance_loc1 %u to %#" PRIx64 "\n", + *readp, pc += *readp * code_align); + ++readp; + break; + case DW_CFA_advance_loc2: + 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: + 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: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); + printf (" offset_extended r%" PRIu64 " (%s) at cfa%+" PRId64 + "\n", + op1, regname (op1), op2 * data_align); + break; + case DW_CFA_restore_extended: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" restore_extended r%" PRIu64 " (%s)\n", + op1, regname (op1)); + break; + case DW_CFA_undefined: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" undefined r%" PRIu64 " (%s)\n", op1, regname (op1)); + break; + case DW_CFA_same_value: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" same_value r%" PRIu64 " (%s)\n", op1, regname (op1)); + break; + case DW_CFA_register: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); + 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: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); + printf (" def_cfa r%" PRIu64 " (%s) at offset %" PRIu64 "\n", + op1, regname (op1), op2); + break; + case DW_CFA_def_cfa_register: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" def_cfa_register r%" PRIu64 " (%s)\n", + op1, regname (op1)); + break; + case DW_CFA_def_cfa_offset: + // XXX overflow check + get_uleb128 (op1, readp); + printf (" def_cfa_offset %" PRIu64 "\n", op1); + break; + case DW_CFA_def_cfa_expression: + // XXX overflow check + get_uleb128 (op1, readp); /* Length of DW_FORM_block. */ + printf (" def_cfa_expression %" PRIu64 "\n", op1); + print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, op1, readp); + readp += op1; + break; + case DW_CFA_expression: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); /* Length of DW_FORM_block. */ + printf (" expression r%" PRIu64 " (%s) \n", + op1, regname (op1)); + print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, op2, readp); + readp += op2; + break; + case DW_CFA_offset_extended_sf: + // XXX overflow check + get_uleb128 (op1, readp); + get_sleb128 (sop2, readp); + printf (" offset_extended_sf r%" PRIu64 " (%s) at cfa%+" + PRId64 "\n", + op1, regname (op1), sop2 * data_align); + break; + case DW_CFA_def_cfa_sf: + // XXX overflow check + get_uleb128 (op1, readp); + get_sleb128 (sop2, readp); + 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: + // XXX overflow check + get_sleb128 (sop1, readp); + printf (" def_cfa_offset_sf %" PRId64 "\n", sop1 * data_align); + break; + case DW_CFA_val_offset: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); + printf (" val_offset %" PRIu64 " at offset %" PRIu64 "\n", + op1, op2 * data_align); + break; + case DW_CFA_val_offset_sf: + // XXX overflow check + get_uleb128 (op1, readp); + get_sleb128 (sop2, readp); + printf (" val_offset_sf %" PRIu64 " at offset %" PRId64 "\n", + op1, sop2 * data_align); + break; + case DW_CFA_val_expression: + // XXX overflow check + get_uleb128 (op1, readp); + get_uleb128 (op2, readp); /* Length of DW_FORM_block. */ + printf (" val_expression r%" PRIu64 " (%s)\n", + op1, regname (op1)); + print_ops (dwflmod, dbg, 10, 10, version, ptr_size, 0, op2, readp); + readp += op2; + break; + case DW_CFA_MIPS_advance_loc8: + 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: + // XXX overflow check + get_uleb128 (op1, readp); + 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; + // XXX overflow check + get_uleb128 (offset, readp); + 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 2: + return 2; + case 3: + return 4; + case 4: + return 8; + default: + 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 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: + // XXX buffer overrun check + get_uleb128 (*res, readp); + break; + case DW_EH_PE_sleb128: + // XXX buffer overrun check + get_sleb128 (*res, readp); + 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_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); + + Elf_Data *data = elf_rawdata (scn, NULL); + + if (unlikely (data == NULL)) + { + error (0, 0, gettext ("cannot get %s content: %s"), + scnname, elf_errmsg (-1)); + return; + } + bool is_eh_frame = strcmp (scnname, ".eh_frame") == 0; + + 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; + } + + 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++; + } + + // XXX Check overflow + get_uleb128 (code_alignment_factor, readp); + // XXX Check overflow + get_sleb128 (data_alignment_factor, readp); + + /* 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 (unlikely (version == 1)) + return_address_register = *readp++; + else + // XXX Check overflow + get_uleb128 (return_address_register, readp); + + 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); + + if (augmentationlen > (size_t) (dataend - readp)) + error (1, 0, gettext ("invalid augmentation length")); + + const char *hdr = "Augmentation data:"; + const char *cp = augmentation + 1; + while (*cp != '\0') + { + 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 + ? start - (ptrdiff_t) cie_id == cie->cie_offset + : (ptrdiff_t) cie_id == 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_ubyte_unaligned_inc (ptr_size, dbg, readp); + Dwarf_Word address_range + = read_ubyte_unaligned_inc (ptr_size, dbg, readp); + + char *a = format_dwarf_addr (dwflmod, cie->address_size, + 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; + get_uleb128 (augmentationlen, readp); + + if (augmentationlen > 0) + { + const char *hdr = "Augmentation data:"; + const char *cp = cie->augmentation + 1; + unsigned int u = 0; + while (*cp != '\0') + { + 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. */ + print_cfa_program (readp, cieend, vma_base, code_alignment_factor, + data_alignment_factor, version, ptr_size, + dwflmod, ebl, dbg); + readp = cieend; + } +} + + +struct attrcb_args +{ + Dwfl_Module *dwflmod; + Dwarf *dbg; + int level; + bool silent; + unsigned int version; + unsigned int addrsize; + unsigned int offset_size; + Dwarf_Off cu_offset; +}; + + +static int +attr_callback (Dwarf_Attribute *attrp, void *arg) +{ + struct attrcb_args *cbargs = (struct attrcb_args *) arg; + const int level = cbargs->level; + + unsigned int attr = dwarf_whatattr (attrp); + if (unlikely (attr == 0)) + { + if (!cbargs->silent) + error (0, 0, gettext ("cannot get attribute code: %s"), + dwarf_errmsg (-1)); + return DWARF_CB_ABORT; + } + + unsigned int form = dwarf_whatform (attrp); + if (unlikely (form == 0)) + { + if (!cbargs->silent) + error (0, 0, gettext ("cannot get attribute form: %s"), + 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 ("cannot get attribute value: %s"), + dwarf_errmsg (-1)); + return DWARF_CB_ABORT; + } + char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize, addr); + printf (" %*s%-20s (%s) %s\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), a); + free (a); + } + break; + + case DW_FORM_indirect: + case DW_FORM_strp: + case DW_FORM_string: + 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_string (attr), + dwarf_form_string (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:; + 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_string (attr), + dwarf_form_string (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_string (attr), + dwarf_form_string (form), + 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_string (attr), + dwarf_form_string (form), (uintmax_t) num); + return DWARF_CB_OK; + } + /* else 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: + notice_listptr (section_loc, &known_loclistptr, + cbargs->addrsize, cbargs->offset_size, num); + if (!cbargs->silent) + printf (" %*s%-20s (%s) location list [%6" PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), (uintmax_t) num); + return DWARF_CB_OK; + + case DW_AT_ranges: + notice_listptr (section_ranges, &known_rangelistptr, + cbargs->addrsize, cbargs->offset_size, num); + if (!cbargs->silent) + printf (" %*s%-20s (%s) range list [%6" PRIxMAX "]\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), (uintmax_t) num); + return DWARF_CB_OK; + + case DW_AT_language: + valuestr = dwarf_lang_string (num); + break; + case DW_AT_encoding: + valuestr = dwarf_encoding_string (num); + break; + case DW_AT_accessibility: + valuestr = dwarf_access_string (num); + break; + case DW_AT_visibility: + valuestr = dwarf_visibility_string (num); + break; + case DW_AT_virtuality: + valuestr = dwarf_virtuality_string (num); + break; + case DW_AT_identifier_case: + valuestr = dwarf_identifier_case_string (num); + break; + case DW_AT_calling_convention: + valuestr = dwarf_calling_convention_string (num); + break; + case DW_AT_inline: + valuestr = dwarf_inline_string (num); + break; + case DW_AT_ordering: + valuestr = dwarf_ordering_string (num); + break; + case DW_AT_discr_list: + valuestr = dwarf_discr_list_string (num); + break; + default: + /* Nothing. */ + break; + } + + if (cbargs->silent) + break; + + if (valuestr == NULL) + printf (" %*s%-20s (%s) %" PRIuMAX "\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), (uintmax_t) num); + else + printf (" %*s%-20s (%s) %s (%" PRIuMAX ")\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), valuestr, (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_string (attr), + dwarf_form_string (form), nl_langinfo (flag ? YESSTR : NOSTR)); + break; + + case DW_FORM_flag_present: + if (cbargs->silent) + break; + printf (" %*s%-20s (%s) %s\n", + (int) (level * 2), "", dwarf_attr_string (attr), + dwarf_form_string (form), nl_langinfo (YESSTR)); + 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_string (attr), + dwarf_form_string (form)); + + switch (attr) + { + default: + if (form != DW_FORM_exprloc) + { + print_block (block.length, block.data); + break; + } + /* Fall through. */ + + 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, + block.length, block.data); + break; + } + break; + + default: + if (cbargs->silent) + break; + printf (" %*s%-20s (form: %#x) ???\n", + (int) (level * 2), "", dwarf_attr_string (attr), + (int) 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, + .cu_offset = offset + }; + + 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; + } + + 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 %" PRIu64 + " in section '%s': %s"), + (uint64_t) offset, secname, dwarf_errmsg (-1)); + goto do_return; + } + + if (!silent) + printf (" [%6" PRIx64 "] %*s%s\n", + (uint64_t) offset, (int) (level * 2), "", + dwarf_tag_string (tag)); + + /* Print the attribute values. */ + args.level = 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_debug_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"), + 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 = elf_getdata (scn, NULL); + 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); + + 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 (unit_length < 2 + length + 5 * 1 + || unlikely (linep + unit_length > lineendp)) + 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; + get_uleb128 (diridx, linep); + + /* Next comes the modification time. */ + unsigned int mtime; + get_uleb128 (mtime, linep); + + /* Finally the length of the file. */ + unsigned int fsize; + get_uleb128 (fsize, linep); + + 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; + } + + 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)) + { + /* 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); + 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 (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); + 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; + get_uleb128 (diridx, linep); + Dwarf_Word mtime; + get_uleb128 (mtime, linep); + Dwarf_Word filelength; + get_uleb128 (filelength, linep); + + 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); + 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); + advance_pc (u128); + { + char *a = format_dwarf_addr (dwflmod, 0, 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); + 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); + 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); + 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. */ + advance_pc ((255 - opcode_base) / line_range); + { + char *a = format_dwarf_addr (dwflmod, 0, 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); + 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); + 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); + 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 = elf_rawdata (scn, NULL); + + 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; + unsigned char *readp = data->d_buf; + unsigned char *const endp = (unsigned char *) data->d_buf + data->d_size; + while (readp < endp) + { + ptrdiff_t offset = readp - (unsigned char *) data->d_buf; + + if (first && skip_listptr_hole (&known_loclistptr, &listptr_idx, + &address_size, &offset_size, + offset, &readp, endp)) + continue; + + if (unlikely (data->d_size - offset < 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); + printf (gettext (" [%6tx] base address %s\n"), offset, b); + free (b); + } + 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); + + char *b = format_dwarf_addr (dwflmod, address_size, begin); + char *e = format_dwarf_addr (dwflmod, address_size, end); + + if (first) /* First entry in a list. */ + printf (gettext (" [%6tx] %s..%s"), offset, b, e); + else + printf (gettext (" %s..%s"), b, e); + + free (b); + free (e); + + if (endp - readp <= (ptrdiff_t) len) + { + fputs (gettext (" <INVALID DATA>\n"), stdout); + break; + } + + print_ops (dwflmod, dbg, 1, 18 + (address_size * 4), + 3 /*XXX*/, address_size, offset_size, 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 = elf_getdata (scn, NULL); + 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; + 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); + + 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); + get_uleb128 (u128_2, readp); + + /* 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; + + 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; + } + } +} + + +/* 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]->d_size; + + /* 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); + printf (" TType base offset: %#x\n", 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); + + 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); + 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); + } + assert (readp == action_table); + + unsigned int max_ar_filter = 0; + if (max_action > 0) + { + puts ("\n Action table:"); + + const unsigned char *const action_table_end + = action_table + max_action + 1; + + u = 0; + do + { + int ar_filter; + get_sleb128 (ar_filter, readp); + if (ar_filter > 0 && (unsigned int) ar_filter > max_ar_filter) + max_ar_filter = ar_filter; + int ar_disp; + get_sleb128 (ar_disp, readp); + + 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) + { + puts ("\n TType table:"); + + // XXX Not *4, size of encoding; + switch (ttype_encoding & 7) + { + case DW_EH_PE_udata2: + case DW_EH_PE_sdata2: + readp = ttype_base - max_ar_filter * 2; + break; + case DW_EH_PE_udata4: + case DW_EH_PE_sdata4: + readp = ttype_base - max_ar_filter * 4; + break; + case DW_EH_PE_udata8: + case DW_EH_PE_sdata8: + readp = ttype_base - max_ar_filter * 8; + break; + default: + error (1, 0, gettext ("invalid TType encoding")); + } + + 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. + if (vers < 4 || vers > 5) + { + 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); + + readp = data->d_buf + cu_off; + + const unsigned char *nextp = data->d_buf + tu_off; + size_t nr = (nextp - readp) / 16; + + printf (gettext ("\n CU list at offset %#" PRIx32 + " contains %zu entries:\n"), + cu_off, nr); + + size_t n = 0; + while (readp + 16 <= dataend && n < 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; + nr = (nextp - readp) / 24; + + printf (gettext ("\n TU list at offset %#" PRIx32 + " contains %zu entries:\n"), + tu_off, nr); + + n = 0; + while (readp + 24 <= dataend && n < 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; + nr = (nextp - readp) / 20; + + printf (gettext ("\n Address list at offset %#" PRIx32 + " contains %zu entries:\n"), + addr_off, nr); + + n = 0; + while (readp + 20 <= dataend && n < 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); + char *h = format_dwarf_addr (dwflmod, 8, high - 1); + printf (" [%4zu] %s..%s, CU index: %5" PRId32 "\n", + n, l, h, idx); + n++; + } + + readp = data->d_buf + sym_off; + nextp = data->d_buf + const_off; + nr = (nextp - readp) / 8; + + printf (gettext ("\n Symbol table at offset %#" PRIx32 + " contains %zu slots:\n"), + addr_off, nr); + + n = 0; + while (readp + 8 <= dataend && n < 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 = data->d_buf + const_off + name; + if (unlikely (sym > dataend)) + goto invalid_data; + + printf (" [%4zu] symbol: %s, CUs: ", n, sym); + + const unsigned char *readcus = data->d_buf + const_off + vector; + if (unlikely (readcus + 8 > dataend)) + goto invalid_data; + + uint32_t cus = read_4ubyte_unaligned (dbg, readcus); + while (cus--) + { + uint32_t cu; + readcus += 4; + cu = read_4ubyte_unaligned (dbg, readcus); + printf ("%" PRId32 "%s", cu, ((cus > 0) ? ", " : "")); + } + 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)); + if ((print_debug_sections & section_exception) == 0) + return; + 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 (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); + int n; + + for (n = 0; n < ndebug_sections; ++n) + if (strcmp (name, debug_sections[n].name) == 0 +#if USE_ZLIB + || (name[0] == '.' && name[1] == 'z' + && debug_sections[n].name[1] == 'd' + && strcmp (&name[2], &debug_sections[n].name[1]) == 0) +#endif + ) + { + 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 ITEM_WRAP_COLUMN 150 +#define REGISTER_WRAP_COLUMN 75 + +/* Print "NAME: FORMAT", wrapping when FORMAT_MAX chars of FORMAT would + make the line exceed ITEM_WRAP_COLUMN. Unpadded numbers look better + for the core items. But we do not want the line breaks to depend on + the particular values. */ +static unsigned int +__attribute__ ((format (printf, 7, 8))) +print_core_item (unsigned int colno, char sep, unsigned int wrap, + size_t name_width, const char *name, + size_t format_max, const char *format, ...) +{ + size_t len = strlen (name); + if (name_width < len) + name_width = len; + + size_t n = name_width + sizeof ": " - 1 + format_max; + + 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", name, (int) (name_width - len), ""); + + va_list ap; + va_start (ap, format); + vprintf (format, ap); + va_end (ap); + + 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; + +#define TYPES \ + DO_TYPE (BYTE, Byte, "0x%.2" PRIx8, "%" PRId8, 4); \ + DO_TYPE (HALF, Half, "0x%.4" PRIx16, "%" PRId16, 6); \ + DO_TYPE (WORD, Word, "0x%.8" PRIx32, "%" PRId32, 11); \ + DO_TYPE (SWORD, Sword, "%" PRId32, "%" PRId32, 11); \ + DO_TYPE (XWORD, Xword, "0x%.16" PRIx64, "%" PRId64, 20); \ + DO_TYPE (SXWORD, Sxword, "%" PRId64, "%" PRId64, 20) + +#define DO_TYPE(NAME, Name, hex, dec, max) GElf_##Name Name[count] + union { TYPES; } value; +#undef DO_TYPE + + void *data = &value; + 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, max) \ + case ELF_T_##NAME: \ + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, \ + 0, item->name, max, 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, max) \ + case ELF_T_##NAME: \ + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, \ + 0, item->name, max, 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]; + 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; + 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; + unsigned int run = 0; + while (w != 0) + { + int n = ffs (w); + w >>= n; + bit += n; + + if (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 && lastbit + 1 != nbits) + p += sprintf (p, "-%u", nbits - bias); + + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, 0, item->name, + 4 + nbits * 4, + negate ? "~<%s>" : "<%s>", printed); + } + break; + + case 'T': + case (char) ('T'|0x80): + assert (count == 2); + Dwarf_Word sec; + Dwarf_Word usec; + size_t maxfmt = 7; + switch (type) + { +#define DO_TYPE(NAME, Name, hex, dec, max) \ + case ELF_T_##NAME: \ + sec = value.Name[0]; \ + usec = value.Name[1]; \ + maxfmt += max; \ + 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, ',', ITEM_WRAP_COLUMN, 0, item->name, + maxfmt, "%" PRIu64 ".%.6" PRIu64, sec, usec); + break; + + case 'c': + assert (count == 1); + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, 0, item->name, + 1, "%c", value.Byte[0]); + break; + + case 's': + colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, 0, item->name, + count, "%.*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 = ITEM_WRAP_COLUMN; + 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; + + /* 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. */ + unsigned int colno = 0; + + const void *last = desc; + if (nitems == 1) + { + size_t size = descsz; + colno = handle_core_item (core, sorted_items[0], desc, colno, &size); + if (size == 0) + return colno; + desc += descsz - size; + descsz = size; + } + + 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 = ITEM_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, 4); \ + BITS (16, HALF, "%6" PRId16, "0x%.4" PRIx16, 6); \ + BITS (32, WORD, "%11" PRId32, " 0x%.8" PRIx32, 11); \ + BITS (64, XWORD, "%20" PRId64, " 0x%.16" PRIx64, 20) + +#define BITS(bits, xtype, sfmt, ufmt, max) \ + 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, max) \ + case bits: \ + desc = convert (core, ELF_T_##xtype, 1, &value, desc, 0); \ + if (type == DW_ATE_signed) \ + colno = print_core_item (colno, ' ', REGISTER_WRAP_COLUMN, \ + maxregname, name, \ + max, sfmt, value.b##bits##s); \ + else \ + colno = print_core_item (colno, ' ', REGISTER_WRAP_COLUMN, \ + maxregname, name, \ + max, 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, ' ', REGISTER_WRAP_COLUMN, + maxregname, name, + 34, "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, ' ', REGISTER_WRAP_COLUMN, + maxregname, name, + 2 + sizeof hex - 1, "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 = REGISTER_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; + } + /* Fall through */ + 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 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 = 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 + 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 (EXIT_FAILURE, 0, + gettext ("cannot get content of note section: %s"), + 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 + { + 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 + { + 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); + 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 + { + 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 + { + printf (gettext ("\nString section [%Zu] '%s' contains %" PRIu64 + " bytes at offset %#0" PRIx64 ":\n"), + elf_ndxscn (scn), name, + shdr->sh_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; + 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" |