mirror of
https://github.com/openharmony/third_party_elfutils.git
synced 2026-07-19 19:43:34 -04:00
6178 lines
170 KiB
C
6178 lines
170 KiB
C
/* Print information from ELF file in human-readable form.
|
|
Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007 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 <system.h>
|
|
#include "../libelf/libelfP.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);
|
|
void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
|
|
|
|
/* Bug report address. */
|
|
const char *argp_program_bug_address = PACKAGE_BUGREPORT;
|
|
|
|
/* Definitions of arguments for argp functions. */
|
|
static const struct argp_option options[] =
|
|
{
|
|
{ NULL, 0, NULL, 0, N_("Output selection:"), 0 },
|
|
{ "all", 'a', NULL, 0, N_("Equivalent to: -h -l"), 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' header"), 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 },
|
|
{ "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL,
|
|
N_("Display DWARF section content. SECTION can be one of abbrev, "
|
|
"aranges, frame, info, loc, line, ranges, pubnames, str, or macinfo."),
|
|
0 },
|
|
{ "notes", 'n', NULL, 0, N_("Display the core notes"), 0 },
|
|
{ "arch-specific", 'A', NULL, 0,
|
|
N_("Display architecture specific information (if any)"), 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 },
|
|
|
|
{ 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;
|
|
|
|
/* 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 */
|
|
section_info = 8, /* .debug_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_all = (section_abbrev | section_aranges | section_frame
|
|
| section_info | section_line | section_loc
|
|
| section_pubnames | section_str | section_macinfo
|
|
| section_ranges)
|
|
} print_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;
|
|
};
|
|
|
|
/* Number of sections in the file. */
|
|
static size_t shnum;
|
|
|
|
|
|
/* 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, GElf_Ehdr *ehdr);
|
|
static void print_relocs (Ebl *ebl);
|
|
static void handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr);
|
|
static void handle_relocs_rela (Ebl *ebl, 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 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)))
|
|
{
|
|
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;
|
|
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 '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;
|
|
else if (strcmp (arg, "frame") == 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;
|
|
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
|
|
{
|
|
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':
|
|
{
|
|
struct section_argument *a = xmalloc (sizeof *a);
|
|
a->arg = arg;
|
|
a->next = NULL;
|
|
struct section_argument ***tailp
|
|
= key == 'x' ? &dump_data_sections_tail : &string_sections_tail;
|
|
**tailp = a;
|
|
*tailp = &a->next;
|
|
}
|
|
any_control_option = true;
|
|
break;
|
|
case ARGP_KEY_NO_ARGS:
|
|
fputs (gettext ("Missing file name.\n"), stderr);
|
|
goto do_argp_help;
|
|
case ARGP_KEY_FINI:
|
|
if (! any_control_option && ! print_archive_index)
|
|
{
|
|
fputs (gettext ("No operation specified.\n"), stderr);
|
|
do_argp_help:
|
|
argp_help (&argp, stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
|
|
program_invocation_short_name);
|
|
exit (1);
|
|
}
|
|
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\
|
|
"), "2008");
|
|
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)
|
|
{
|
|
*(bool *) arg = false;
|
|
return DWARF_CB_ABORT;
|
|
}
|
|
|
|
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 (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));
|
|
}
|
|
else
|
|
{
|
|
dwfl_report_end (dwfl, NULL, NULL);
|
|
|
|
if (only_one)
|
|
/* Clear ONLY_ONE if we have multiple modules, from an archive. */
|
|
dwfl_getmodules (dwfl, &count_dwflmod, &only_one, 1);
|
|
|
|
/* 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_getshnum (ebl->elf, &shnum) < 0))
|
|
error (EXIT_FAILURE, 0,
|
|
gettext ("cannot determine number of sections: %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, ehdr);
|
|
if (print_relocations)
|
|
print_relocs (pure_ebl);
|
|
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 (dump_data_sections != NULL)
|
|
dump_data (pure_ebl);
|
|
if (string_sections != NULL)
|
|
dump_strings (ebl);
|
|
if (print_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 "\n"),
|
|
ehdr->e_phnum);
|
|
|
|
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_getshstrndx (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 < ehdr->e_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;
|
|
}
|
|
}
|
|
|
|
/* Get the section header string table index. */
|
|
size_t shstrndx;
|
|
if (unlikely (elf_getshstrndx (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 < ehdr->e_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 < ehdr->e_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 < ehdr->e_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 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_getshstrndx (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_getshstrndx (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:
|
|
puts (ebl_dynamic_tag_name (ebl, dyn->d_un.d_val, NULL, 0));
|
|
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, GElf_Ehdr *ehdr)
|
|
{
|
|
for (int i = 0; i < ehdr->e_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)
|
|
{
|
|
/* 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, scn, shdr);
|
|
else if (shdr->sh_type == SHT_RELA)
|
|
handle_relocs_rela (ebl, scn, shdr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Handle a relocation section. */
|
|
static void
|
|
handle_relocs_rel (Ebl *ebl, 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_getshstrndx (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 [%2u] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
|
|
"\
|
|
\nRelocation section [%2u] '%s' for 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),
|
|
(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);
|
|
|
|
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))
|
|
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, 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_getshstrndx (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);
|
|
|
|
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))
|
|
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_getshstrndx (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_getshstrndx (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_getshstrndx (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_getshstrndx (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;
|
|
|
|
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_getshstrndx (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_getshstrndx (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 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 = dwfl_module_addrsym (dwflmod, address, &sym, 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",
|
|
};
|
|
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;
|
|
|
|
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",
|
|
};
|
|
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;
|
|
|
|
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"
|
|
};
|
|
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);
|
|
|
|
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_ops (Dwfl_Module *dwflmod, Dwarf *dbg, int indent, int indentrest,
|
|
unsigned int addrsize, Dwarf_Word len, const unsigned char *data)
|
|
{
|
|
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",
|
|
};
|
|
|
|
Dwarf_Word offset = 0;
|
|
while (len-- > 0)
|
|
{
|
|
uint_fast8_t op = *data++;
|
|
|
|
switch (op)
|
|
{
|
|
case DW_OP_call_ref:
|
|
case DW_OP_addr:;
|
|
/* Address operand. */
|
|
Dwarf_Word addr;
|
|
if (addrsize == 4)
|
|
addr = read_4ubyte_unaligned (dbg, data);
|
|
else
|
|
{
|
|
assert (addrsize == 8);
|
|
addr = read_8ubyte_unaligned (dbg, data);
|
|
}
|
|
data += addrsize;
|
|
len -= addrsize;
|
|
|
|
if (op == DW_OP_addr)
|
|
{
|
|
char *a = format_dwarf_addr (dwflmod, 0, addr);
|
|
printf ("%*s[%4" PRIuMAX "] %s %s\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", a);
|
|
free (a);
|
|
}
|
|
else
|
|
printf ("%*s[%4" PRIuMAX "] %s %#" PRIxMAX "\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", (uintmax_t) addr);
|
|
offset += 1 + addrsize;
|
|
break;
|
|
|
|
case DW_OP_deref_size:
|
|
case DW_OP_xderef_size:
|
|
case DW_OP_pick:
|
|
case DW_OP_const1u:
|
|
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:
|
|
printf ("%*s[%4" PRIuMAX "] %s %" PRIu16 "\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", read_2ubyte_unaligned (dbg, data));
|
|
len -= 2;
|
|
data += 2;
|
|
offset += 3;
|
|
break;
|
|
|
|
case DW_OP_const4u:
|
|
printf ("%*s[%4" PRIuMAX "] %s %" PRIu32 "\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", read_4ubyte_unaligned (dbg, data));
|
|
len -= 4;
|
|
data += 4;
|
|
offset += 5;
|
|
break;
|
|
|
|
case DW_OP_const8u:
|
|
printf ("%*s[%4" PRIuMAX "] %s %" PRIu64 "\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", read_8ubyte_unaligned (dbg, data));
|
|
len -= 8;
|
|
data += 8;
|
|
offset += 9;
|
|
break;
|
|
|
|
case DW_OP_const1s:
|
|
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:
|
|
printf ("%*s[%4" PRIuMAX "] %s %" PRId16 "\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", read_2sbyte_unaligned (dbg, data));
|
|
len -= 2;
|
|
data += 2;
|
|
offset += 3;
|
|
break;
|
|
|
|
case DW_OP_const4s:
|
|
printf ("%*s[%4" PRIuMAX "] %s %" PRId32 "\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", read_4sbyte_unaligned (dbg, data));
|
|
len -= 4;
|
|
data += 4;
|
|
offset += 5;
|
|
break;
|
|
|
|
case DW_OP_const8s:
|
|
printf ("%*s[%4" PRIuMAX "] %s %" PRId64 "\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", read_8sbyte_unaligned (dbg, data));
|
|
len -= 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;
|
|
unsigned int uleb;
|
|
get_uleb128 (uleb, data);
|
|
printf ("%*s[%4" PRIuMAX "] %s %u\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", uleb);
|
|
len -= data - start;
|
|
offset += 1 + (data - start);
|
|
break;
|
|
|
|
case DW_OP_bit_piece:
|
|
start = data;
|
|
unsigned int uleb2;
|
|
get_uleb128 (uleb, data);
|
|
get_uleb128 (uleb2, data);
|
|
printf ("%*s[%4" PRIuMAX "] %s %u, %u\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", uleb, uleb2);
|
|
len -= data - start;
|
|
offset += 1 + (data - start);
|
|
break;
|
|
|
|
case DW_OP_fbreg:
|
|
case DW_OP_breg0 ... DW_OP_breg31:
|
|
case DW_OP_consts:
|
|
start = data;
|
|
unsigned int sleb;
|
|
get_sleb128 (sleb, data);
|
|
printf ("%*s[%4" PRIuMAX "] %s %d\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", sleb);
|
|
len -= data - start;
|
|
offset += 1 + (data - start);
|
|
break;
|
|
|
|
case DW_OP_bregx:
|
|
start = data;
|
|
get_uleb128 (uleb, data);
|
|
get_sleb128 (sleb, data);
|
|
printf ("%*s[%4" PRIuMAX "] %s %u %d\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???", uleb, sleb);
|
|
len -= data - start;
|
|
offset += 1 + (data - start);
|
|
break;
|
|
|
|
case DW_OP_call2:
|
|
case DW_OP_call4:
|
|
case DW_OP_skip:
|
|
case DW_OP_bra:
|
|
printf ("%*s[%4" PRIuMAX "] %s %" PRIuMAX "\n",
|
|
indent, "", (uintmax_t) offset,
|
|
known[op] ?: "???",
|
|
(uintmax_t) (offset + read_2sbyte_unaligned (dbg, data)));
|
|
len -= 2;
|
|
data += 2;
|
|
offset += 3;
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
|
|
Ebl *ebl __attribute__ ((unused)),
|
|
GElf_Ehdr *ehdr __attribute__ ((unused)),
|
|
Elf_Scn *scn __attribute__ ((unused)),
|
|
GElf_Shdr *shdr, Dwarf *dbg)
|
|
{
|
|
printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n"
|
|
" [ Code]\n"),
|
|
".debug_abbrev", (uint64_t) shdr->sh_offset);
|
|
|
|
Dwarf_Off offset = 0;
|
|
while (offset < shdr->sh_size)
|
|
{
|
|
printf (gettext ("\nAbbreviation section at offset %" PRIu64 ":\n"),
|
|
offset);
|
|
|
|
while (1)
|
|
{
|
|
size_t length;
|
|
Dwarf_Abbrev abbrev;
|
|
|
|
int res = dwarf_offabbrev (dbg, offset, &length, &abbrev);
|
|
if (res != 0)
|
|
{
|
|
if (unlikely (res < 0))
|
|
{
|
|
printf (gettext ("\
|
|
*** error while reading abbreviation: %s\n"),
|
|
dwarf_errmsg (-1));
|
|
return;
|
|
}
|
|
|
|
/* This is the NUL byte at the end of the section. */
|
|
++offset;
|
|
break;
|
|
}
|
|
|
|
/* We know these calls can never fail. */
|
|
unsigned int code = dwarf_getabbrevcode (&abbrev);
|
|
unsigned int tag = dwarf_getabbrevtag (&abbrev);
|
|
int has_children = dwarf_abbrevhaschildren (&abbrev);
|
|
|
|
printf (gettext (" [%5u] offset: %" PRId64
|
|
", children: %s, tag: %s\n"),
|
|
code, (int64_t) offset,
|
|
has_children ? gettext ("yes") : gettext ("no"),
|
|
dwarf_tag_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 __attribute__ ((unused)),
|
|
GElf_Ehdr *ehdr __attribute__ ((unused)),
|
|
Elf_Scn *scn __attribute__ ((unused)),
|
|
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 '%s' at offset %#" PRIx64 " contains %zu entry:\n",
|
|
"\
|
|
\nDWARF section '%s' at offset %#" PRIx64 " contains %zu entries:\n",
|
|
cnt),
|
|
".debug_aranges", (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 __attribute__ ((unused)),
|
|
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 '%s' at offset %#" PRIx64 ":\n"),
|
|
".debug_ranges", (uint64_t) shdr->sh_offset);
|
|
|
|
size_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
|
|
|
|
bool first = true;
|
|
unsigned char *readp = data->d_buf;
|
|
while (readp < (unsigned char *) data->d_buf + data->d_size)
|
|
{
|
|
ptrdiff_t offset = readp - (unsigned char *) data->d_buf;
|
|
|
|
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. */
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
print_debug_frame_section (Dwfl_Module *dwflmod __attribute__ ((unused)),
|
|
Ebl *ebl __attribute__ ((unused)),
|
|
GElf_Ehdr *ehdr __attribute__ ((unused)),
|
|
Elf_Scn *scn __attribute__ ((unused)),
|
|
GElf_Shdr *shdr __attribute__ ((unused)),
|
|
Dwarf *dbg __attribute__ ((unused)))
|
|
{
|
|
}
|
|
|
|
|
|
struct attrcb_args
|
|
{
|
|
Dwfl_Module *dwflmod;
|
|
Dwarf *dbg;
|
|
int level;
|
|
unsigned int addrsize;
|
|
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))
|
|
{
|
|
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))
|
|
{
|
|
error (0, 0, gettext ("cannot get attribute form: %s"),
|
|
dwarf_errmsg (-1));
|
|
return DWARF_CB_ABORT;
|
|
}
|
|
|
|
switch (form)
|
|
{
|
|
case DW_FORM_addr:
|
|
{
|
|
Dwarf_Addr addr;
|
|
if (unlikely (dwarf_formaddr (attrp, &addr) != 0))
|
|
{
|
|
attrval_out:
|
|
error (0, 0, gettext ("offset: %" PRIx64 " cannot get attribute value: %s"),
|
|
attrp->valp - (unsigned char *) attrp->cu->dbg->sectiondata[IDX_debug_info]->d_buf,
|
|
dwarf_errmsg (-1));
|
|
return DWARF_CB_ABORT;
|
|
}
|
|
char *a = format_dwarf_addr (cbargs->dwflmod, cbargs->addrsize, addr);
|
|
printf (" %*s%-20s %s\n",
|
|
(int) (level * 2), "", dwarf_attr_string (attr), a);
|
|
free (a);
|
|
}
|
|
break;
|
|
|
|
case DW_FORM_indirect:
|
|
case DW_FORM_strp:
|
|
case DW_FORM_string:;
|
|
const char *str = dwarf_formstring (attrp);
|
|
if (unlikely (str == NULL))
|
|
goto attrval_out;
|
|
printf (" %*s%-20s \"%s\"\n",
|
|
(int) (level * 2), "", dwarf_attr_string (attr), 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:;
|
|
Dwarf_Die ref;
|
|
if (unlikely (dwarf_formref_die (attrp, &ref) == NULL))
|
|
goto attrval_out;
|
|
|
|
printf (" %*s%-20s [%6" PRIxMAX "]\n",
|
|
(int) (level * 2), "", dwarf_attr_string (attr),
|
|
(uintmax_t) dwarf_dieoffset (&ref));
|
|
break;
|
|
|
|
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)
|
|
{
|
|
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:
|
|
printf (" %*s%-20s location list [%6" PRIxMAX "]\n",
|
|
(int) (level * 2), "", dwarf_attr_string (attr),
|
|
(uintmax_t) num);
|
|
return DWARF_CB_OK;
|
|
|
|
case DW_AT_ranges:
|
|
printf (" %*s%-20s range list [%6" PRIxMAX "]\n",
|
|
(int) (level * 2), "", dwarf_attr_string (attr),
|
|
(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 (valuestr == NULL)
|
|
printf (" %*s%-20s %" PRIuMAX "\n",
|
|
(int) (level * 2), "", dwarf_attr_string (attr),
|
|
(uintmax_t) num);
|
|
else
|
|
printf (" %*s%-20s %s (%" PRIuMAX ")\n",
|
|
(int) (level * 2), "", dwarf_attr_string (attr),
|
|
valuestr, (uintmax_t) num);
|
|
break;
|
|
|
|
case DW_FORM_flag:;
|
|
bool flag;
|
|
if (unlikely (dwarf_formflag (attrp, &flag) != 0))
|
|
goto attrval_out;
|
|
|
|
printf (" %*s%-20s %s\n",
|
|
(int) (level * 2), "", dwarf_attr_string (attr),
|
|
nl_langinfo (flag ? YESSTR : NOSTR));
|
|
break;
|
|
|
|
case DW_FORM_block4:
|
|
case DW_FORM_block2:
|
|
case DW_FORM_block1:
|
|
case DW_FORM_block:;
|
|
Dwarf_Block block;
|
|
if (unlikely (dwarf_formblock (attrp, &block) != 0))
|
|
goto attrval_out;
|
|
|
|
printf (" %*s%-20s %" PRIxMAX " byte block\n",
|
|
(int) (level * 2), "", dwarf_attr_string (attr),
|
|
(uintmax_t) block.length);
|
|
|
|
switch (attr)
|
|
{
|
|
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:
|
|
print_ops (cbargs->dwflmod, cbargs->dbg,
|
|
12 + level * 2, 12 + level * 2,
|
|
cbargs->addrsize, block.length, block.data);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
printf (" %*s%-20s [form: %d] ???\n",
|
|
(int) (level * 2), "", dwarf_attr_string (attr),
|
|
(int) form);
|
|
break;
|
|
}
|
|
|
|
return DWARF_CB_OK;
|
|
}
|
|
|
|
|
|
static void
|
|
print_debug_info_section (Dwfl_Module *dwflmod,
|
|
Ebl *ebl __attribute__ ((unused)),
|
|
GElf_Ehdr *ehdr __attribute__ ((unused)),
|
|
Elf_Scn *scn __attribute__ ((unused)),
|
|
GElf_Shdr *shdr, Dwarf *dbg)
|
|
{
|
|
printf (gettext ("\
|
|
\nDWARF section '%s' at offset %#" PRIx64 ":\n [Offset]\n"),
|
|
".debug_info", (uint64_t) shdr->sh_offset);
|
|
|
|
/* If the section is empty we don't have to do anything. */
|
|
if (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;
|
|
next_cu:
|
|
if (dwarf_nextcu (dbg, offset, &nextcu, &cuhl, &abbroffset, &addrsize,
|
|
&offsize) != 0)
|
|
goto do_return;
|
|
|
|
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*/2, abbroffset, addrsize, offsize);
|
|
|
|
|
|
struct attrcb_args args;
|
|
args.dwflmod = dwflmod;
|
|
args.dbg = dbg;
|
|
args.addrsize = addrsize;
|
|
args.cu_offset = offset;
|
|
|
|
offset += cuhl;
|
|
|
|
int level = 0;
|
|
|
|
if (unlikely (dwarf_offdie (dbg, offset, &dies[level]) == NULL))
|
|
{
|
|
error (0, 0, gettext ("cannot get DIE at offset %" PRIu64
|
|
" in section '%s': %s"),
|
|
(uint64_t) offset, ".debug_info", dwarf_errmsg (-1));
|
|
goto do_return;
|
|
}
|
|
|
|
do
|
|
{
|
|
offset = dwarf_dieoffset (&dies[level]);
|
|
if (unlikely (offset == ~0ul))
|
|
{
|
|
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))
|
|
{
|
|
error (0, 0, gettext ("cannot get tag of DIE at offset %" PRIx64
|
|
" in section '%s': %s"),
|
|
(uint64_t) offset, ".debug_info", dwarf_errmsg (-1));
|
|
goto do_return;
|
|
}
|
|
|
|
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))
|
|
{
|
|
error (0, 0, gettext ("cannot get next DIE: %s\n"),
|
|
dwarf_errmsg (-1));
|
|
goto do_return;
|
|
}
|
|
}
|
|
else if (unlikely (res < 0))
|
|
{
|
|
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_line_section (Dwfl_Module *dwflmod, Ebl *ebl,
|
|
GElf_Ehdr *ehdr __attribute__ ((unused)),
|
|
Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
|
|
{
|
|
printf (gettext ("\
|
|
\nDWARF section '%s' at offset %#" PRIx64 ":\n"),
|
|
".debug_line", (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), ".debug_line");
|
|
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++;
|
|
|
|
/* 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"
|
|
" 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, "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), ".debug_line");
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
|
|
while (linep < lineendp)
|
|
{
|
|
unsigned int u128;
|
|
int s128;
|
|
|
|
/* Read the opcode. */
|
|
unsigned int opcode = *linep++;
|
|
|
|
/* 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);
|
|
unsigned int address_increment = (minimum_instr_len
|
|
* ((opcode - opcode_base)
|
|
/ line_range));
|
|
|
|
/* Perform the increments. */
|
|
line += line_increment;
|
|
address += address_increment;
|
|
|
|
char *a = format_dwarf_addr (dwflmod, 0, address);
|
|
printf (gettext ("\
|
|
special opcode %u: address+%u = %s, line%+d = %zu\n"),
|
|
opcode, address_increment, 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;
|
|
line = 1;
|
|
is_stmt = default_is_stmt;
|
|
break;
|
|
|
|
case DW_LNE_set_address:
|
|
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;
|
|
|
|
default:
|
|
/* Unknown, ignore it. */
|
|
puts (gettext ("unknown opcode"));
|
|
linep += len - 1;
|
|
break;
|
|
}
|
|
}
|
|
else if (opcode <= DW_LNS_set_epilogue_begin)
|
|
{
|
|
/* 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);
|
|
address += minimum_instr_len * u128;
|
|
{
|
|
char *a = format_dwarf_addr (dwflmod, 0, address);
|
|
printf (gettext ("advance address by %u to %s\n"),
|
|
u128, 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. */
|
|
u128 = (minimum_instr_len
|
|
* ((255 - opcode_base) / line_range));
|
|
address += u128;
|
|
{
|
|
char *a = format_dwarf_addr (dwflmod, 0, address);
|
|
printf (gettext ("advance address by constant %u to %s\n"),
|
|
u128, 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;
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
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 __attribute__ ((unused)),
|
|
GElf_Ehdr *ehdr __attribute__ ((unused)),
|
|
Elf_Scn *scn __attribute__ ((unused)),
|
|
GElf_Shdr *shdr,
|
|
Dwarf *dbg __attribute__ ((unused)))
|
|
{
|
|
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 '%s' at offset %#" PRIx64 ":\n"),
|
|
".debug_loc", (uint64_t) shdr->sh_offset);
|
|
|
|
size_t address_size = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
|
|
|
|
bool first = true;
|
|
unsigned char *readp = data->d_buf;
|
|
while (readp < (unsigned char *) data->d_buf + data->d_size)
|
|
{
|
|
ptrdiff_t offset = readp - (unsigned char *) data->d_buf;
|
|
|
|
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. */
|
|
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);
|
|
|
|
print_ops (dwflmod, dbg, 1, 18 + (address_size * 4),
|
|
address_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 __attribute__ ((unused)),
|
|
GElf_Ehdr *ehdr __attribute__ ((unused)),
|
|
Elf_Scn *scn, GElf_Shdr *shdr, Dwarf *dbg)
|
|
{
|
|
printf (gettext ("\
|
|
\nDWARF section '%s' at offset %#" PRIx64 ":\n"),
|
|
".debug_macinfo", (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 __attribute__ ((unused)),
|
|
GElf_Ehdr *ehdr __attribute__ ((unused)),
|
|
Elf_Scn *scn __attribute__ ((unused)),
|
|
GElf_Shdr *shdr, Dwarf *dbg)
|
|
{
|
|
printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n"),
|
|
".debug_pubnames", (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 __attribute__ ((unused)),
|
|
GElf_Ehdr *ehdr __attribute__ ((unused)),
|
|
Elf_Scn *scn __attribute__ ((unused)),
|
|
GElf_Shdr *shdr, Dwarf *dbg)
|
|
{
|
|
/* Compute floor(log16(shdr->sh_size)). */
|
|
GElf_Addr tmp = shdr->sh_size;
|
|
int digits = 1;
|
|
while (tmp >= 16)
|
|
{
|
|
++digits;
|
|
tmp >>= 4;
|
|
}
|
|
digits = MAX (4, digits);
|
|
|
|
printf (gettext ("\nDWARF section '%s' at offset %#" PRIx64 ":\n"
|
|
" %*s String\n"),
|
|
".debug_str", (uint64_t) shdr->sh_offset,
|
|
/* TRANS: the debugstr| prefix makes the string unique. */
|
|
digits + 2, sgettext ("debugstr|Offset"));
|
|
|
|
Dwarf_Off offset = 0;
|
|
while (offset < shdr->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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (dbg == NULL)
|
|
{
|
|
error (0, 0, gettext ("cannot get debug context descriptor: %s"),
|
|
dwfl_errmsg (-1));
|
|
return;
|
|
}
|
|
|
|
/* Get the section header string table index. */
|
|
size_t shstrndx;
|
|
if (unlikely (elf_getshstrndx (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 (line),
|
|
NEW_SECTION (loc),
|
|
NEW_SECTION (pubnames),
|
|
NEW_SECTION (str),
|
|
NEW_SECTION (macinfo),
|
|
NEW_SECTION (ranges),
|
|
{ ".eh_frame", section_frame, print_debug_frame_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 (print_debug_sections & debug_sections[n].bitmask)
|
|
debug_sections[n].fp (dwflmod, ebl, ehdr, scn, shdr, dbg);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#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)
|
|
{
|
|
Elf_Data valuedata =
|
|
{
|
|
.d_type = type,
|
|
.d_buf = value,
|
|
.d_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)
|
|
{
|
|
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
|
|
|
|
desc = convert (core, item->type, count, &value, desc + item->offset);
|
|
|
|
switch (item->format)
|
|
{
|
|
case 'd':
|
|
assert (count == 1);
|
|
switch (item->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 (item->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':
|
|
assert (count == 1);
|
|
Dwarf_Word bits = 0;
|
|
Dwarf_Word bit = 0;
|
|
switch (item->type)
|
|
{
|
|
#define DO_TYPE(NAME, Name, hex, dec, max) \
|
|
case ELF_T_##NAME: \
|
|
bits = value.Name[0]; \
|
|
bit = (Dwarf_Word) 1 << ((sizeof value.Name[0] * 8) - 1); \
|
|
break
|
|
TYPES;
|
|
#undef DO_TYPE
|
|
default:
|
|
abort ();
|
|
}
|
|
char printed[sizeof (Dwarf_Word) * 8 + 1];
|
|
int i = 0;
|
|
while (bit != 0)
|
|
{
|
|
printed[i++] = (bits & bit) ? '1' : '0';
|
|
bit >>= 1;
|
|
}
|
|
colno = print_core_item (colno, ',', ITEM_WRAP_COLUMN, 0, item->name,
|
|
sizeof printed - 1, "%.*s", i, printed);
|
|
break;
|
|
|
|
case 'T':
|
|
case (char) ('T'|0x80):
|
|
assert (count == 2);
|
|
Dwarf_Word sec;
|
|
Dwarf_Word usec;
|
|
size_t maxfmt = 7;
|
|
switch (item->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;
|
|
|
|
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,
|
|
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;
|
|
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);
|
|
|
|
/* Force a line break at the end of the group. */
|
|
colno = ITEM_WRAP_COLUMN;
|
|
}
|
|
|
|
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)
|
|
{
|
|
const char *pfx;
|
|
const char *set;
|
|
char name[16];
|
|
int bits;
|
|
int type;
|
|
ssize_t n = ebl_register_info (ebl, reg, name, sizeof name,
|
|
&pfx, &set, &bits, &type);
|
|
if (n <= 0)
|
|
error (EXIT_FAILURE, 0,
|
|
gettext ("unable to handle register number %d"),
|
|
regloc->regno);
|
|
|
|
#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); \
|
|
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);
|
|
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[16];
|
|
Dwarf_Half regno;
|
|
uint8_t bits;
|
|
uint8_t 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)
|
|
error (EXIT_FAILURE, 0,
|
|
gettext ("cannot register info: %s"), elf_errmsg (-1));
|
|
|
|
struct register_info regs[maxnreg];
|
|
memset (regs, 0, sizeof regs);
|
|
|
|
/* Sort to collect the sets together. */
|
|
int maxreg = 0;
|
|
for (size_t i = 0; i < nregloc; ++i)
|
|
for (int reg = reglocs[i].regno;
|
|
reg < reglocs[i].regno + reglocs[i].count;
|
|
++reg)
|
|
{
|
|
assert (reg < maxnreg);
|
|
if (reg > maxreg)
|
|
maxreg = reg;
|
|
struct register_info *info = ®s[reg];
|
|
|
|
const char *pfx;
|
|
int bits;
|
|
int type;
|
|
ssize_t n = ebl_register_info (ebl, reg, info->name, sizeof info->name,
|
|
&pfx, &info->set, &bits, &type);
|
|
if (n <= 0)
|
|
error (EXIT_FAILURE, 0,
|
|
gettext ("cannot register info: %s"), elf_errmsg (-1));
|
|
|
|
info->regloc = ®locs[i];
|
|
info->regno = reg;
|
|
info->bits = bits;
|
|
info->type = type;
|
|
}
|
|
qsort (regs, maxreg + 1, sizeof regs[0], &compare_registers);
|
|
|
|
/* Collect the unique sets and sort them. */
|
|
inline bool same_set (const struct register_info *a,
|
|
const struct register_info *b)
|
|
{
|
|
return (a < ®s[maxnreg] && a->regloc != NULL
|
|
&& b < ®s[maxnreg] && b->regloc != NULL
|
|
&& a->bits == b->bits
|
|
&& (a->set == b->set || !strcmp (a->set, b->set)));
|
|
}
|
|
struct register_info *sets[maxreg + 1];
|
|
sets[0] = ®s[0];
|
|
size_t nsets = 1;
|
|
for (int i = 1; i <= maxreg; ++i)
|
|
if (regs[i].regloc != NULL && !same_set (®s[i], ®s[i - 1]))
|
|
sets[nsets++] = ®s[i];
|
|
qsort (sets, nsets, sizeof sets[0], &compare_register_sets);
|
|
|
|
/* Write out all the sets. */
|
|
unsigned int colno = 0;
|
|
for (size_t i = 0; i < nsets; ++i)
|
|
{
|
|
/* Find the longest name of a register in this set. */
|
|
size_t maxname = 0;
|
|
const struct register_info *end;
|
|
for (end = sets[i]; same_set (sets[i], end); ++end)
|
|
{
|
|
size_t len = strlen (end->name);
|
|
if (len > maxname)
|
|
maxname = len;
|
|
}
|
|
|
|
for (const struct register_info *reg = sets[i];
|
|
reg < end;
|
|
reg += reg->regloc->count ?: 1)
|
|
colno = handle_core_register (ebl, core, maxname,
|
|
reg->regloc, desc, colno);
|
|
|
|
/* Force a line break at the end of the group. */
|
|
colno = REGISTER_WRAP_COLUMN;
|
|
}
|
|
|
|
return colno;
|
|
}
|
|
|
|
static void
|
|
handle_auxv_note (Ebl *ebl, Elf *core, GElf_Word descsz, GElf_Off desc_pos)
|
|
{
|
|
Elf_Data *data = elf_getdata_rawchunk (core, desc_pos, descsz, ELF_T_AUXV);
|
|
if (data == NULL)
|
|
elf_error:
|
|
error (EXIT_FAILURE, 0,
|
|
gettext ("cannot convert core note data: %s"), elf_errmsg (-1));
|
|
|
|
const size_t nauxv = descsz / gelf_fsize (core, ELF_T_AUXV, 1, EV_CURRENT);
|
|
for (size_t i = 0; i < nauxv; ++i)
|
|
{
|
|
GElf_auxv_t av_mem;
|
|
GElf_auxv_t *av = gelf_getauxv (data, i, &av_mem);
|
|
if (av == NULL)
|
|
goto elf_error;
|
|
|
|
const char *name;
|
|
const char *fmt;
|
|
if (ebl_auxv_info (ebl, av->a_type, &name, &fmt) == 0)
|
|
{
|
|
/* Unknown type. */
|
|
if (av->a_un.a_val == 0)
|
|
printf (" %" PRIu64 "\n", av->a_type);
|
|
else
|
|
printf (" %" PRIu64 ": %#" PRIx64 "\n",
|
|
av->a_type, av->a_un.a_val);
|
|
}
|
|
else
|
|
switch (fmt[0])
|
|
{
|
|
case '\0': /* Normally zero. */
|
|
if (av->a_un.a_val == 0)
|
|
{
|
|
printf (" %s\n", name);
|
|
break;
|
|
}
|
|
/* Fall through */
|
|
case 'x': /* hex */
|
|
case 'p': /* address */
|
|
case 's': /* address of string */
|
|
printf (" %s: %#" PRIx64 "\n", name, av->a_un.a_val);
|
|
break;
|
|
case 'u':
|
|
printf (" %s: %" PRIu64 "\n", name, av->a_un.a_val);
|
|
break;
|
|
case 'd':
|
|
printf (" %s: %" PRId64 "\n", name, av->a_un.a_val);
|
|
break;
|
|
|
|
case 'b':
|
|
printf (" %s: %#" PRIx64 " ", name, av->a_un.a_val);
|
|
GElf_Xword bit = 1;
|
|
const char *pfx = "<";
|
|
for (const char *p = fmt + 1; *p != 0; p = strchr (p, '\0') + 1)
|
|
{
|
|
if (av->a_un.a_val & bit)
|
|
{
|
|
printf ("%s%s", pfx, p);
|
|
pfx = " ";
|
|
}
|
|
bit <<= 1;
|
|
}
|
|
printf (">\n");
|
|
break;
|
|
|
|
default:
|
|
abort ();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
handle_core_note (Ebl *ebl, const GElf_Nhdr *nhdr, const 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->n_type, nhdr->n_descsz,
|
|
®s_offset, &nregloc, ®locs, &nitems, &items))
|
|
return;
|
|
|
|
unsigned int colno = handle_core_items (ebl->elf, desc, 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, 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)
|
|
handle_auxv_note (ebl, ebl->elf, nhdr.n_descsz,
|
|
start + desc_offset);
|
|
else
|
|
handle_core_note (ebl, &nhdr, 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_getshstrndx (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 < ehdr->e_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)
|
|
printf (gettext ("\nSection [%Zu] '%s' is empty.\n"),
|
|
elf_ndxscn (scn), name);
|
|
|
|
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_getshstrndx (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;
|
|
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))
|
|
break;
|
|
}
|
|
|
|
if (unlikely (scn == NULL))
|
|
{
|
|
error (0, 0, gettext ("\nsection '%s' does not exist"), a->arg);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
(*dump) (scn, &shdr_mem, name);
|
|
}
|
|
}
|
|
|
|
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_getshstrndx (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"
|