blob: 2797a8496a4a6a2382d520bc1f95764eb7482b6d [file] [log] [blame]
/* Print information from ELF file in human-readable form.
Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008 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 "../libelf/common.h"
#include "../libebl/libeblP.h"
#include "../libdw/libdwP.h"
#include "../libdwfl/libdwflP.h"
#include "../libdw/memory-access.h"
/* Name and version of program. */
static void print_version (FILE *stream, struct argp_state *state);
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 print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr);
static void dump_data (Ebl *ebl);
static void dump_strings (Ebl *ebl);
static void print_strings (Ebl *ebl);
static void dump_archive_index (Elf *, const char *);
int
main (int argc, char *argv[])
{
/* Set locale. */
setlocale (LC_ALL, "");
/* Initialize the message catalog. */
textdomain (PACKAGE_TARNAME);
/* Parse and process arguments. */
int remaining;
argp_parse (&argp, argc, argv, 0, &remaining, NULL);
/* Before we start tell the ELF library which version we are using. */
elf_version (EV_CURRENT);
/* Now process all the files given at the command line. */
bool only_one = remaining + 1 == argc;
do
{
/* Open the file. */
int fd = open (argv[remaining], O_RDONLY);
if (fd == -1)
{
error (0, errno, gettext ("cannot open input file"));
continue;
}
process_file (fd, argv[remaining], only_one);
close (fd);
}
while (++remaining < argc);
return error_message_count != 0;
}
/* Handle program arguments. */
static error_t
parse_opt (int key, char *arg,
struct argp_state *state __attribute__ ((unused)))
{
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)
{
if (*(bool *) arg)
return DWARF_CB_ABORT;
*(bool *) arg = true;
return DWARF_CB_OK;
}
struct process_dwflmod_args
{
int fd;
bool only_one;
};
static int
process_dwflmod (Dwfl_Module *dwflmod,
void **userdata __attribute__ ((unused)),
const char *name __attribute__ ((unused)),
Dwarf_Addr base __attribute__ ((unused)),
void *arg)
{
const struct process_dwflmod_args *a = arg;
/* Print the file name. */
if (!a->only_one)
{
const char *fname;
dwfl_module_info (dwflmod, NULL, NULL, NULL, NULL, NULL, &fname, NULL);
printf ("\n%s:\n\n", fname);
}
process_elf_file (dwflmod, a->fd);
return DWARF_CB_OK;
}
/* Stub libdwfl callback, only the ELF handle already open is ever used. */
static int
find_no_debuginfo (Dwfl_Module *mod __attribute__ ((unused)),
void **userdata __attribute__ ((unused)),
const char *modname __attribute__ ((unused)),
Dwarf_Addr base __attribute__ ((unused)),
const char *file_name __attribute__ ((unused)),
const char *debuglink_file __attribute__ ((unused)),
GElf_Word debuglink_crc __attribute__ ((unused)),
char **debuginfo_file_name __attribute__ ((unused)))
{
return -1;
}
/* Process one input file. */
static void
process_file (int fd, const char *fname, bool only_one)
{
if (print_archive_index)
check_archive_index (fd, fname, only_one);
if (!any_control_option)
return;
/* Duplicate an fd for dwfl_report_offline to swallow. */
int dwfl_fd = dup (fd);
if (unlikely (dwfl_fd < 0))
error (EXIT_FAILURE, errno, "dup");
/* Use libdwfl in a trivial way to open the libdw handle for us.
This takes care of applying relocations to DWARF data in ET_REL files. */
static const Dwfl_Callbacks callbacks =
{
.section_address = dwfl_offline_section_address,
.find_debuginfo = find_no_debuginfo
};
Dwfl *dwfl = dwfl_begin (&callbacks);
if (likely (dwfl != NULL))
/* Let 0 be the logical address of the file (or first in archive). */
dwfl->offline_next_address = 0;
if (dwfl_report_offline (dwfl, fname, fname, dwfl_fd) == NULL)
{
struct stat64 st;
if (fstat64 (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. */
bool seen = false;
only_one = dwfl_getmodules (dwfl, &count_dwflmod, &seen, 0) == 0;
}
/* Process the one or more modules gleaned from this file. */
struct process_dwflmod_args a = { .fd = fd, .only_one = only_one };
dwfl_getmodules (dwfl, &process_dwflmod, &a, 0);
}
dwfl_end (dwfl);
}
/* Process one ELF file. */
static void
process_elf_file (Dwfl_Module *dwflmod, int fd)
{
GElf_Addr dwflbias;
Elf *elf = dwfl_module_getelf (dwflmod, &dwflbias);
GElf_Ehdr ehdr_mem;
GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
if (ehdr == NULL)
{
elf_error:
error (0, 0, gettext ("cannot read ELF header: %s"), elf_errmsg (-1));
return;
}
Ebl *ebl = ebl_openbackend (elf);
if (unlikely (ebl == NULL))
{
ebl_error:
error (0, errno, gettext ("cannot create EBL handle"));
return;
}
/* Determine the number of sections. */
if (unlikely (elf_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 (print_arch)
print_attributes (ebl, ehdr);
if (dump_data_sections != NULL)
dump_data (pure_ebl);
if (string_sections != NULL)
dump_strings (ebl);
if (print_debug_sections != 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),