summaryrefslogtreecommitdiffstats
path: root/src/src/readelf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/src/readelf.c')
-rw-r--r--src/src/readelf.c8499
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 = &regs[reg];
+ info->regloc = &reglocs[i];
+ info->regno = reg;
+ info->set = register_info (ebl, reg, &reglocs[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 < &regs[maxnreg] && a->regloc != NULL
+ && b < &regs[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] = &regs[0];
+ size_t nsets = 1;
+ for (int i = 1; i <= maxreg; ++i)
+ if (regs[i].regloc != NULL && !same_set (&regs[i], &regs[i - 1]))
+ sets[nsets++] = &regs[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,
+ &regs_offset, &nregloc, &reglocs, &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"