blob: 4c20c38ec65212430f1aca5e775d966a67a1edfd [file] [log] [blame]
/* Print information from ELF file in human-readable form.
Copyright (C) 1999-2018 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
elfutils is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <argp.h>
#include <assert.h>
#include <ctype.h>
#include <dwarf.h>
#include <errno.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 <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <libeu.h>
#include <system.h>
#include <printversion.h>
#include "../libelf/libelfP.h"
#include "../libelf/common.h"
#include "../libebl/libeblP.h"
#include "../libdwelf/libdwelf.h"
#include "../libdw/libdwP.h"
#include "../libdwfl/libdwflP.h"
#include "../libdw/memory-access.h"
#include "../libdw/known-dwarf.h"
#ifdef __linux__
#define CORE_SIGILL SIGILL
#define CORE_SIGBUS SIGBUS
#define CORE_SIGFPE SIGFPE
#define CORE_SIGSEGV SIGSEGV
#define CORE_SI_USER SI_USER
#else
/* We want the linux version of those as that is what shows up in the core files. */
#define CORE_SIGILL 4 /* Illegal instruction (ANSI). */
#define CORE_SIGBUS 7 /* BUS error (4.2 BSD). */
#define CORE_SIGFPE 8 /* Floating-point exception (ANSI). */
#define CORE_SIGSEGV 11 /* Segmentation violation (ANSI). */
#define CORE_SI_USER 0 /* Sent by kill, sigsend. */
#endif
/* Name and version of program. */
ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
/* Bug report address. */
ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
/* argp key value for --elf-section, non-ascii. */
#define ELF_INPUT_SECTION 256
/* argp key value for --dwarf-skeleton, non-ascii. */
#define DWARF_SKELETON 257
/* Terrible hack for hooking unrelated skeleton/split compile units,
see __libdw_link_skel_split in print_debug. */
static bool do_not_close_dwfl = false;
/* Definitions of arguments for argp functions. */
static const struct argp_option options[] =
{
{ NULL, 0, NULL, 0, N_("ELF input selection:"), 0 },
{ "elf-section", ELF_INPUT_SECTION, "SECTION", OPTION_ARG_OPTIONAL,
N_("Use the named SECTION (default .gnu_debugdata) as (compressed) ELF "
"input data"), 0 },
{ "dwarf-skeleton", DWARF_SKELETON, "FILE", 0,
N_("Used with -w to find the skeleton Compile Units in FILE associated "
"with the Split Compile units in a .dwo input file"), 0 },
{ NULL, 0, NULL, 0, N_("ELF output selection:"), 0 },
{ "all", 'a', NULL, 0,
N_("All these plus -p .strtab -p .dynstr -p .comment"), 0 },
{ "dynamic", 'd', NULL, 0, N_("Display the dynamic segment"), 0 },
{ "file-header", 'h', NULL, 0, N_("Display the ELF file header"), 0 },
{ "histogram", 'I', NULL, 0,
N_("Display histogram of bucket list lengths"), 0 },
{ "program-headers", 'l', NULL, 0, N_("Display the program headers"), 0 },
{ "segments", 'l', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 },
{ "relocs", 'r', NULL, 0, N_("Display relocations"), 0 },
{ "section-groups", 'g', NULL, 0, N_("Display the section groups"), 0 },
{ "section-headers", 'S', NULL, 0, N_("Display the sections' headers"), 0 },
{ "sections", 'S', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 },
{ "symbols", 's', "SECTION", OPTION_ARG_OPTIONAL,
N_("Display the symbol table sections"), 0 },
{ "version-info", 'V', NULL, 0, N_("Display versioning information"), 0 },
{ "notes", 'n', NULL, 0, N_("Display the ELF notes"), 0 },
{ "arch-specific", 'A', NULL, 0,
N_("Display architecture specific information, if any"), 0 },
{ "exception", 'e', NULL, 0,
N_("Display sections for exception handling"), 0 },
{ NULL, 0, NULL, 0, N_("Additional output selection:"), 0 },
{ "debug-dump", 'w', "SECTION", OPTION_ARG_OPTIONAL,
N_("Display DWARF section content. SECTION can be one of abbrev, addr, "
"aranges, decodedaranges, frame, gdb_index, info, info+, loc, line, "
"decodedline, ranges, pubnames, str, macinfo, macro or exception"), 0 },
{ "hex-dump", 'x', "SECTION", 0,
N_("Dump the uninterpreted contents of SECTION, by number or name"), 0 },
{ "strings", 'p', "SECTION", OPTION_ARG_OPTIONAL,
N_("Print string contents of sections"), 0 },
{ "string-dump", 'p', NULL, OPTION_ALIAS | OPTION_HIDDEN, NULL, 0 },
{ "archive-index", 'c', NULL, 0,
N_("Display the symbol index of an archive"), 0 },
{ NULL, 0, NULL, 0, N_("Output control:"), 0 },
{ "numeric-addresses", 'N', NULL, 0,
N_("Do not find symbol names for addresses in DWARF data"), 0 },
{ "unresolved-address-offsets", 'U', NULL, 0,
N_("Display just offsets instead of resolving values to addresses in DWARF data"), 0 },
{ "wide", 'W', NULL, 0,
N_("Ignored for compatibility (lines always wide)"), 0 },
{ "decompress", 'z', NULL, 0,
N_("Show compression information for compressed sections (when used with -S); decompress section before dumping data (when used with -p or -x)"), 0 },
{ NULL, 0, NULL, 0, NULL, 0 }
};
/* Short description of program. */
static const char doc[] = N_("\
Print information from ELF file in human-readable form.");
/* Strings for arguments in help texts. */
static const char args_doc[] = N_("FILE...");
/* Prototype for option handler. */
static error_t parse_opt (int key, char *arg, struct argp_state *state);
/* Data structure to communicate with argp functions. */
static struct argp argp =
{
options, parse_opt, args_doc, doc, NULL, NULL, NULL
};
/* If non-null, the section from which we should read to (compressed) ELF. */
static const char *elf_input_section = NULL;
/* If non-null, the file that contains the skeleton CUs. */
static const char *dwarf_skeleton = NULL;
/* Flags set by the option controlling the output. */
/* True if dynamic segment should be printed. */
static bool print_dynamic_table;
/* True if the file header should be printed. */
static bool print_file_header;
/* True if the program headers should be printed. */
static bool print_program_header;
/* True if relocations should be printed. */
static bool print_relocations;
/* True if the section headers should be printed. */
static bool print_section_header;
/* True if the symbol table should be printed. */
static bool print_symbol_table;
/* A specific section name, or NULL to print all symbol tables. */
static char *symbol_table_section;
/* True if the version information should be printed. */
static bool print_version_info;
/* True if section groups should be printed. */
static bool print_section_groups;
/* True if bucket list length histogram should be printed. */
static bool print_histogram;
/* True if the architecture specific data should be printed. */
static bool print_arch;
/* True if note section content should be printed. */
static bool print_notes;
/* True if SHF_STRINGS section content should be printed. */
static bool print_string_sections;
/* True if archive index should be printed. */
static bool print_archive_index;
/* True if any of the control options except print_archive_index is set. */
static bool any_control_option;
/* True if we should print addresses from DWARF in symbolic form. */
static bool print_address_names = true;
/* True if we should print raw values instead of relativized addresses. */
static bool print_unresolved_addresses = false;
/* True if we should print the .debug_aranges section using libdw. */
static bool decodedaranges = false;
/* True if we should print the .debug_aranges section using libdw. */
static bool decodedline = false;
/* True if we want to show more information about compressed sections. */
static bool print_decompress = false;
/* True if we want to show split compile units for debug_info skeletons. */
static bool show_split_units = false;
/* Select printing of debugging sections. */
static enum section_e
{
section_abbrev = 1, /* .debug_abbrev */
section_aranges = 2, /* .debug_aranges */
section_frame = 4, /* .debug_frame or .eh_frame & al. */
section_info = 8, /* .debug_info, (implies .debug_types) */
section_line = 16, /* .debug_line */
section_loc = 32, /* .debug_loc */
section_pubnames = 64, /* .debug_pubnames */
section_str = 128, /* .debug_str */
section_macinfo = 256, /* .debug_macinfo */
section_ranges = 512, /* .debug_ranges */
section_exception = 1024, /* .eh_frame & al. */
section_gdb_index = 2048, /* .gdb_index */
section_macro = 4096, /* .debug_macro */
section_addr = 8192, /* .debug_addr */
section_types = 16384, /* .debug_types (implied by .debug_info) */
section_all = (section_abbrev | section_aranges | section_frame
| section_info | section_line | section_loc
| section_pubnames | section_str | section_macinfo
| section_ranges | section_exception | section_gdb_index
| section_macro | section_addr | section_types)
} print_debug_sections, implicit_debug_sections;
/* Select hex dumping of sections. */
static struct section_argument *dump_data_sections;
static struct section_argument **dump_data_sections_tail = &dump_data_sections;
/* Select string dumping of sections. */
static struct section_argument *string_sections;
static struct section_argument **string_sections_tail = &string_sections;
struct section_argument
{
struct section_argument *next;
const char *arg;
bool implicit;
};
/* Numbers of sections and program headers in the file. */
static size_t shnum;
static size_t phnum;
/* Declarations of local functions. */
static void process_file (int fd, const char *fname, bool only_one);
static void process_elf_file (Dwfl_Module *dwflmod, int fd);
static void print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr);
static void print_shdr (Ebl *ebl, GElf_Ehdr *ehdr);
static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr);
static void print_scngrp (Ebl *ebl);
static void print_dynamic (Ebl *ebl);
static void print_relocs (Ebl *ebl, GElf_Ehdr *ehdr);
static void handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
GElf_Shdr *shdr);
static void handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn,
GElf_Shdr *shdr);
static void print_symtab (Ebl *ebl, int type);
static void handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr);
static void print_verinfo (Ebl *ebl);
static void handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr);
static void handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr);
static void handle_versym (Ebl *ebl, Elf_Scn *scn,
GElf_Shdr *shdr);
static void print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr);
static void handle_hash (Ebl *ebl);
static void handle_notes (Ebl *ebl, GElf_Ehdr *ehdr);
static void print_liblist (Ebl *ebl);
static void print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr);
static void dump_data (Ebl *ebl);
static void dump_strings (Ebl *ebl);
static void print_strings (Ebl *ebl);
static void dump_archive_index (Elf *, const char *);
/* Looked up once with gettext in main. */
static char *yes_str;
static char *no_str;
static void
cleanup_list (struct section_argument *list)
{
while (list != NULL)
{
struct section_argument *a = list;
list = a->next;
free (a);
}
}
int
main (int argc, char *argv[])
{
/* We use no threads here which can interfere with handling a stream. */
(void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
/* Set locale. */
setlocale (LC_ALL, "");
/* Initialize the message catalog. */
textdomain (PACKAGE_TARNAME);
/* Look up once. */
yes_str = gettext ("yes");
no_str = gettext ("no");
/* 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);
cleanup_list (dump_data_sections);
cleanup_list (string_sections);
return error_message_count != 0;
}
/* Handle program arguments. */
static error_t
parse_opt (int key, char *arg,
struct argp_state *state __attribute__ ((unused)))
{
void add_dump_section (const char *name, bool implicit)
{
struct section_argument *a = xmalloc (sizeof *a);
a->arg = name;
a->next = NULL;
a->implicit = implicit;
struct section_argument ***tailp
= key == 'x' ? &dump_data_sections_tail : &string_sections_tail;
**tailp = a;
*tailp = &a->next;
}
switch (key)
{
case 'a':
print_file_header = true;
print_program_header = true;
print_relocations = true;
print_section_header = true;
print_symbol_table = true;
print_version_info = true;
print_dynamic_table = true;
print_section_groups = true;
print_histogram = true;
print_arch = true;
print_notes = true;
implicit_debug_sections |= section_exception;
add_dump_section (".strtab", true);
add_dump_section (".dynstr", true);
add_dump_section (".comment", true);
any_control_option = true;
break;
case 'A':
print_arch = true;
any_control_option = true;
break;
case 'd':
print_dynamic_table = true;
any_control_option = true;
break;
case 'e':
print_debug_sections |= section_exception;
any_control_option = true;
break;
case 'g':
print_section_groups = true;
any_control_option = true;
break;
case 'h':
print_file_header = true;
any_control_option = true;
break;
case 'I':
print_histogram = true;
any_control_option = true;
break;
case 'l':
print_program_header = true;
any_control_option = true;
break;
case 'n':
print_notes = true;
any_control_option = true;
break;
case 'r':
print_relocations = true;
any_control_option = true;
break;
case 'S':
print_section_header = true;
any_control_option = true;
break;
case 's':
print_symbol_table = true;
any_control_option = true;
symbol_table_section = arg;
break;
case 'V':
print_version_info = true;
any_control_option = true;
break;
case 'c':
print_archive_index = true;
break;
case 'w':
if (arg == NULL)
{
print_debug_sections = section_all;
implicit_debug_sections = section_info;
show_split_units = true;
}
else if (strcmp (arg, "abbrev") == 0)
print_debug_sections |= section_abbrev;
else if (strcmp (arg, "addr") == 0)
{
print_debug_sections |= section_addr;
implicit_debug_sections |= section_info;
}
else if (strcmp (arg, "aranges") == 0)
print_debug_sections |= section_aranges;
else if (strcmp (arg, "decodedaranges") == 0)
{
print_debug_sections |= section_aranges;
decodedaranges = true;
}
else if (strcmp (arg, "ranges") == 0)
{
print_debug_sections |= section_ranges;
implicit_debug_sections |= section_info;
}
else if (strcmp (arg, "frame") == 0 || strcmp (arg, "frames") == 0)
print_debug_sections |= section_frame;
else if (strcmp (arg, "info") == 0)
{
print_debug_sections |= section_info;
print_debug_sections |= section_types;
}
else if (strcmp (arg, "info+") == 0)
{
print_debug_sections |= section_info;
print_debug_sections |= section_types;
show_split_units = true;
}
else if (strcmp (arg, "loc") == 0)
{
print_debug_sections |= section_loc;
implicit_debug_sections |= section_info;
}
else if (strcmp (arg, "line") == 0)
print_debug_sections |= section_line;
else if (strcmp (arg, "decodedline") == 0)
{
print_debug_sections |= section_line;
decodedline = true;
}
else if (strcmp (arg, "pubnames") == 0)
print_debug_sections |= section_pubnames;
else if (strcmp (arg, "str") == 0)
{
print_debug_sections |= section_str;
/* For mapping string offset tables to CUs. */
implicit_debug_sections |= section_info;
}
else if (strcmp (arg, "macinfo") == 0)
print_debug_sections |= section_macinfo;
else if (strcmp (arg, "macro") == 0)
print_debug_sections |= section_macro;
else if (strcmp (arg, "exception") == 0)
print_debug_sections |= section_exception;
else if (strcmp (arg, "gdb_index") == 0)
print_debug_sections |= section_gdb_index;
else
{
fprintf (stderr, gettext ("Unknown DWARF debug section `%s'.\n"),
arg);
argp_help (&argp, stderr, ARGP_HELP_SEE,
program_invocation_short_name);
exit (1);
}
any_control_option = true;
break;
case 'p':
any_control_option = true;
if (arg == NULL)
{
print_string_sections = true;
break;
}
FALLTHROUGH;
case 'x':
add_dump_section (arg, false);
any_control_option = true;
break;
case 'N':
print_address_names = false;
break;
case 'U':
print_unresolved_addresses = true;
break;
case ARGP_KEY_NO_ARGS:
fputs (gettext ("Missing file name.\n"), stderr);
goto do_argp_help;
case ARGP_KEY_FINI:
if (! any_control_option && ! print_archive_index)
{
fputs (gettext ("No operation specified.\n"), stderr);
do_argp_help:
argp_help (&argp, stderr, ARGP_HELP_SEE,
program_invocation_short_name);
exit (EXIT_FAILURE);
}
break;
case 'W': /* Ignored. */
break;
case 'z':
print_decompress = true;
break;
case ELF_INPUT_SECTION:
if (arg == NULL)
elf_input_section = ".gnu_debugdata";
else
elf_input_section = arg;
break;
case DWARF_SKELETON:
dwarf_skeleton = arg;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
/* Create a file descriptor to read the data from the
elf_input_section given a file descriptor to an ELF file. */
static int
open_input_section (int fd)
{
size_t shnums;
size_t cnt;
size_t shstrndx;
Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
if (elf == NULL)
{
error (0, 0, gettext ("cannot generate Elf descriptor: %s"),
elf_errmsg (-1));
return -1;
}
if (elf_getshdrnum (elf, &shnums) < 0)
{
error (0, 0, gettext ("cannot determine number of sections: %s"),
elf_errmsg (-1));
open_error:
elf_end (elf);
return -1;
}
if (elf_getshdrstrndx (elf, &shstrndx) < 0)
{
error (0, 0, gettext ("cannot get section header string table index"));
goto open_error;
}
for (cnt = 0; cnt < shnums; ++cnt)
{
Elf_Scn *scn = elf_getscn (elf, cnt);
if (scn == NULL)
{
error (0, 0, gettext ("cannot get section: %s"),
elf_errmsg (-1));
goto open_error;
}
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (unlikely (shdr == NULL))
{
error (0, 0, gettext ("cannot get section header: %s"),
elf_errmsg (-1));
goto open_error;
}
const char *sname = elf_strptr (elf, shstrndx, shdr->sh_name);
if (sname == NULL)
{
error (0, 0, gettext ("cannot get section name"));
goto open_error;
}
if (strcmp (sname, elf_input_section) == 0)
{
Elf_Data *data = elf_rawdata (scn, NULL);
if (data == NULL)
{
error (0, 0, gettext ("cannot get %s content: %s"),
sname, elf_errmsg (-1));
goto open_error;
}
/* Create (and immediately unlink) a temporary file to store
section data in to create a file descriptor for it. */
const char *tmpdir = getenv ("TMPDIR") ?: P_tmpdir;
static const char suffix[] = "/readelfXXXXXX";
int tmplen = strlen (tmpdir) + sizeof (suffix);
char *tempname = alloca (tmplen);
sprintf (tempname, "%s%s", tmpdir, suffix);
int sfd = mkstemp (tempname);
if (sfd == -1)
{
error (0, 0, gettext ("cannot create temp file '%s'"),
tempname);
goto open_error;
}
unlink (tempname);
ssize_t size = data->d_size;
if (write_retry (sfd, data->d_buf, size) != size)
{
error (0, 0, gettext ("cannot write section data"));
goto open_error;
}
if (elf_end (elf) != 0)
{
error (0, 0, gettext ("error while closing Elf descriptor: %s"),
elf_errmsg (-1));
return -1;
}
if (lseek (sfd, 0, SEEK_SET) == -1)
{
error (0, 0, gettext ("error while rewinding file descriptor"));
return -1;
}
return sfd;
}
}
/* Named section not found. */
if (elf_end (elf) != 0)
error (0, 0, gettext ("error while closing Elf descriptor: %s"),
elf_errmsg (-1));
return -1;
}
/* Check if the file is an archive, and if so dump its index. */
static void
check_archive_index (int fd, const char *fname, bool only_one)
{
/* Create an `Elf' descriptor. */
Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
if (elf == NULL)
error (0, 0, gettext ("cannot generate Elf descriptor: %s"),
elf_errmsg (-1));
else
{
if (elf_kind (elf) == ELF_K_AR)
{
if (!only_one)
printf ("\n%s:\n\n", fname);
dump_archive_index (elf, fname);
}
else
error (0, 0,
gettext ("'%s' is not an archive, cannot print archive index"),
fname);
/* Now we can close the descriptor. */
if (elf_end (elf) != 0)
error (0, 0, gettext ("error while closing Elf descriptor: %s"),
elf_errmsg (-1));
}
}
/* Trivial callback used for checking if we opened an archive. */
static int
count_dwflmod (Dwfl_Module *dwflmod __attribute__ ((unused)),
void **userdata __attribute__ ((unused)),
const char *name __attribute__ ((unused)),
Dwarf_Addr base __attribute__ ((unused)),
void *arg)
{
if (*(bool *) arg)
return DWARF_CB_ABORT;
*(bool *) arg = true;
return DWARF_CB_OK;
}
struct process_dwflmod_args
{
int fd;
bool only_one;
};
static int
process_dwflmod (Dwfl_Module *dwflmod,
void **userdata __attribute__ ((unused)),
const char *name __attribute__ ((unused)),
Dwarf_Addr base __attribute__ ((unused)),
void *arg)
{
const struct process_dwflmod_args *a = arg;
/* Print the file name. */
if (!a->only_one)
{
const char *fname;
dwfl_module_info (dwflmod, NULL, NULL, NULL, NULL, NULL, &fname, NULL);
printf ("\n%s:\n\n", fname);
}
process_elf_file (dwflmod, a->fd);
return DWARF_CB_OK;
}
/* Stub libdwfl callback, only the ELF handle already open is ever used.
Only used for finding the alternate debug file if the Dwarf comes from
the main file. We are not interested in separate debuginfo. */
static int
find_no_debuginfo (Dwfl_Module *mod,
void **userdata,
const char *modname,
Dwarf_Addr base,
const char *file_name,
const char *debuglink_file,
GElf_Word debuglink_crc,
char **debuginfo_file_name)
{
Dwarf_Addr dwbias;
dwfl_module_info (mod, NULL, NULL, NULL, &dwbias, NULL, NULL, NULL);
/* We are only interested if the Dwarf has been setup on the main
elf file but is only missing the alternate debug link. If dwbias
hasn't even been setup, this is searching for separate debuginfo
for the main elf. We don't care in that case. */
if (dwbias == (Dwarf_Addr) -1)
return -1;
return dwfl_standard_find_debuginfo (mod, userdata, modname, base,
file_name, debuglink_file,
debuglink_crc, debuginfo_file_name);
}
static Dwfl *
create_dwfl (int fd, const char *fname)
{
/* Duplicate an fd for dwfl_report_offline to swallow. */
int dwfl_fd = dup (fd);
if (unlikely (dwfl_fd < 0))
error (EXIT_FAILURE, errno, "dup");
/* Use libdwfl in a trivial way to open the libdw handle for us.
This takes care of applying relocations to DWARF data in ET_REL files. */
static const Dwfl_Callbacks callbacks =
{
.section_address = dwfl_offline_section_address,
.find_debuginfo = find_no_debuginfo
};
Dwfl *dwfl = dwfl_begin (&callbacks);
if (likely (dwfl != NULL))
/* Let 0 be the logical address of the file (or first in archive). */
dwfl->offline_next_address = 0;
if (dwfl_report_offline (dwfl, fname, fname, dwfl_fd) == NULL)
{
struct stat st;
if (fstat (dwfl_fd, &st) != 0)
error (0, errno, gettext ("cannot stat input file"));
else if (unlikely (st.st_size == 0))
error (0, 0, gettext ("input file is empty"));
else
error (0, 0, gettext ("failed reading '%s': %s"),
fname, dwfl_errmsg (-1));
close (dwfl_fd); /* Consumed on success, not on failure. */
dwfl = NULL;
}
else
dwfl_report_end (dwfl, NULL, NULL);
return dwfl;
}
/* Process one input file. */
static void
process_file (int fd, const char *fname, bool only_one)
{
if (print_archive_index)
check_archive_index (fd, fname, only_one);
if (!any_control_option)
return;
if (elf_input_section != NULL)
{
/* Replace fname and fd with section content. */
char *fnname = alloca (strlen (fname) + strlen (elf_input_section) + 2);
sprintf (fnname, "%s:%s", fname, elf_input_section);
fd = open_input_section (fd);
if (fd == -1)
{
error (0, 0, gettext ("No such section '%s' in '%s'"),
elf_input_section, fname);
return;
}
fname = fnname;
}
Dwfl *dwfl = create_dwfl (fd, fname);
if (dwfl != 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);
}
/* Terrible hack for hooking unrelated skeleton/split compile units,
see __libdw_link_skel_split in print_debug. */
if (! do_not_close_dwfl)
dwfl_end (dwfl);
/* Need to close the replaced fd if we created it. Caller takes
care of original. */
if (elf_input_section != NULL)
close (fd);
}
/* Check whether there are any compressed sections in the ELF file. */
static bool
elf_contains_chdrs (Elf *elf)
{
Elf_Scn *scn = NULL;
while ((scn = elf_nextscn (elf, scn)) != NULL)
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr != NULL && (shdr->sh_flags & SHF_COMPRESSED) != 0)
return true;
}
return false;
}
/* Process one ELF file. */
static void
process_elf_file (Dwfl_Module *dwflmod, int fd)
{
GElf_Addr dwflbias;
Elf *elf = dwfl_module_getelf (dwflmod, &dwflbias);
GElf_Ehdr ehdr_mem;
GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
if (ehdr == NULL)
{
error (0, 0, gettext ("cannot read ELF header: %s"), elf_errmsg (-1));
return;
}
Ebl *ebl = ebl_openbackend (elf);
if (unlikely (ebl == NULL))
{
ebl_error:
error (0, errno, gettext ("cannot create EBL handle"));
return;
}
/* Determine the number of sections. */
if (unlikely (elf_getshdrnum (ebl->elf, &shnum) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot determine number of sections: %s"),
elf_errmsg (-1));
/* Determine the number of phdrs. */
if (unlikely (elf_getphdrnum (ebl->elf, &phnum) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot determine number of program headers: %s"),
elf_errmsg (-1));
/* For an ET_REL file, libdwfl has adjusted the in-core shdrs and
may have applied relocation to some sections. If there are any
compressed sections, any pass (or libdw/libdwfl) might have
uncompressed them. So we need to get a fresh Elf handle on the
file to display those. */
bool print_unchanged = ((print_section_header
|| print_relocations
|| dump_data_sections != NULL
|| print_notes)
&& (ehdr->e_type == ET_REL
|| elf_contains_chdrs (ebl->elf)));
Elf *pure_elf = NULL;
Ebl *pure_ebl = ebl;
if (print_unchanged)
{
/* Read the file afresh. */
off_t aroff = elf_getaroff (elf);
pure_elf = dwelf_elf_begin (fd);
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)
{
error (0, 0, gettext ("cannot read ELF: %s"), elf_errmsg (-1));
return;
}
pure_ebl = ebl_openbackend (pure_elf);
if (pure_ebl == NULL)
goto ebl_error;
}
if (print_file_header)
print_ehdr (ebl, ehdr);
if (print_section_header)
print_shdr (pure_ebl, ehdr);
if (print_program_header)
print_phdr (ebl, ehdr);
if (print_section_groups)
print_scngrp (ebl);
if (print_dynamic_table)
print_dynamic (ebl);
if (print_relocations)
print_relocs (pure_ebl, ehdr);
if (print_histogram)
handle_hash (ebl);
if (print_symbol_table)
print_symtab (ebl, SHT_DYNSYM);
if (print_version_info)
print_verinfo (ebl);
if (print_symbol_table)
print_symtab (ebl, SHT_SYMTAB);
if (print_arch)
print_liblist (ebl);
if (print_arch)
print_attributes (ebl, ehdr);
if (dump_data_sections != NULL)
dump_data (pure_ebl);
if (string_sections != NULL)
dump_strings (ebl);
if ((print_debug_sections | implicit_debug_sections) != 0)
print_debug (dwflmod, ebl, ehdr);
if (print_notes)
handle_notes (pure_ebl, ehdr);
if (print_string_sections)
print_strings (ebl);
ebl_closebackend (ebl);
if (pure_ebl != ebl)
{
ebl_closebackend (pure_ebl);
elf_end (pure_elf);
}
}
/* Print file type. */
static void
print_file_type (unsigned short int e_type)
{
if (likely (e_type <= ET_CORE))
{
static const char *const knowntypes[] =
{
N_("NONE (None)"),
N_("REL (Relocatable file)"),
N_("EXEC (Executable file)"),
N_("DYN (Shared object file)"),
N_("CORE (Core file)")
};
puts (gettext (knowntypes[e_type]));
}
else if (e_type >= ET_LOOS && e_type <= ET_HIOS)
printf (gettext ("OS Specific: (%x)\n"), e_type);
else if (e_type >= ET_LOPROC /* && e_type <= ET_HIPROC always true */)
printf (gettext ("Processor Specific: (%x)\n"), e_type);
else
puts ("???");
}
/* Print ELF header. */
static void
print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr)
{
fputs_unlocked (gettext ("ELF Header:\n Magic: "), stdout);
for (size_t cnt = 0; cnt < EI_NIDENT; ++cnt)
printf (" %02hhx", ehdr->e_ident[cnt]);
printf (gettext ("\n Class: %s\n"),
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? "ELF32"
: ehdr->e_ident[EI_CLASS] == ELFCLASS64 ? "ELF64"
: "\?\?\?");
printf (gettext (" Data: %s\n"),
ehdr->e_ident[EI_DATA] == ELFDATA2LSB
? "2's complement, little endian"
: ehdr->e_ident[EI_DATA] == ELFDATA2MSB
? "2's complement, big endian" : "\?\?\?");
printf (gettext (" Ident Version: %hhd %s\n"),
ehdr->e_ident[EI_VERSION],
ehdr->e_ident[EI_VERSION] == EV_CURRENT ? gettext ("(current)")
: "(\?\?\?)");
char buf[512];
printf (gettext (" OS/ABI: %s\n"),
ebl_osabi_name (ebl, ehdr->e_ident[EI_OSABI], buf, sizeof (buf)));
printf (gettext (" ABI Version: %hhd\n"),
ehdr->e_ident[EI_ABIVERSION]);
fputs_unlocked (gettext (" Type: "), stdout);
print_file_type (ehdr->e_type);
printf (gettext (" Machine: %s\n"), ebl->name);
printf (gettext (" Version: %d %s\n"),
ehdr->e_version,
ehdr->e_version == EV_CURRENT ? gettext ("(current)") : "(\?\?\?)");
printf (gettext (" Entry point address: %#" PRIx64 "\n"),
ehdr->e_entry);
printf (gettext (" Start of program headers: %" PRId64 " %s\n"),
ehdr->e_phoff, gettext ("(bytes into file)"));
printf (gettext (" Start of section headers: %" PRId64 " %s\n"),
ehdr->e_shoff, gettext ("(bytes into file)"));
printf (gettext (" Flags: %s\n"),
ebl_machine_flag_name (ebl, ehdr->e_flags, buf, sizeof (buf)));
printf (gettext (" Size of this header: %" PRId16 " %s\n"),
ehdr->e_ehsize, gettext ("(bytes)"));
printf (gettext (" Size of program header entries: %" PRId16 " %s\n"),
ehdr->e_phentsize, gettext ("(bytes)"));
printf (gettext (" Number of program headers entries: %" PRId16),
ehdr->e_phnum);
if (ehdr->e_phnum == PN_XNUM)
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
if (shdr != NULL)
printf (gettext (" (%" PRIu32 " in [0].sh_info)"),
(uint32_t) shdr->sh_info);
else
fputs_unlocked (gettext (" ([0] not available)"), stdout);
}
fputc_unlocked ('\n', stdout);
printf (gettext (" Size of section header entries: %" PRId16 " %s\n"),
ehdr->e_shentsize, gettext ("(bytes)"));
printf (gettext (" Number of section headers entries: %" PRId16),
ehdr->e_shnum);
if (ehdr->e_shnum == 0)
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
if (shdr != NULL)
printf (gettext (" (%" PRIu32 " in [0].sh_size)"),
(uint32_t) shdr->sh_size);
else
fputs_unlocked (gettext (" ([0] not available)"), stdout);
}
fputc_unlocked ('\n', stdout);
if (unlikely (ehdr->e_shstrndx == SHN_XINDEX))
{
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem);
if (shdr != NULL)
/* We managed to get the zeroth section. */
snprintf (buf, sizeof (buf), gettext (" (%" PRIu32 " in [0].sh_link)"),
(uint32_t) shdr->sh_link);
else
{
strncpy (buf, gettext (" ([0] not available)"), sizeof (buf));
buf[sizeof (buf) - 1] = '\0';
}
printf (gettext (" Section header string table index: XINDEX%s\n\n"),
buf);
}
else
printf (gettext (" Section header string table index: %" PRId16 "\n\n"),
ehdr->e_shstrndx);
}
static const char *
get_visibility_type (int value)
{
switch (value)
{
case STV_DEFAULT:
return "DEFAULT";
case STV_INTERNAL:
return "INTERNAL";
case STV_HIDDEN:
return "HIDDEN";
case STV_PROTECTED:
return "PROTECTED";
default:
return "???";
}
}
static const char *
elf_ch_type_name (unsigned int code)
{
if (code == 0)
return "NONE";
if (code == ELFCOMPRESS_ZLIB)
return "ZLIB";
return "UNKNOWN";
}
/* Print the section headers. */
static void
print_shdr (Ebl *ebl, GElf_Ehdr *ehdr)
{
size_t cnt;
size_t shstrndx;
if (! print_file_header)
{
size_t sections;
if (unlikely (elf_getshdrnum (ebl->elf, &sections) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot get number of sections: %s"),
elf_errmsg (-1));
printf (gettext ("\
There are %zd section headers, starting at offset %#" PRIx64 ":\n\
\n"),
sections, ehdr->e_shoff);
}
/* Get the section header string table index. */
if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot get section header string table index: %s"),
elf_errmsg (-1));
puts (gettext ("Section Headers:"));
if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
puts (gettext ("[Nr] Name Type Addr Off Size ES Flags Lk Inf Al"));
else
puts (gettext ("[Nr] Name Type Addr Off Size ES Flags Lk Inf Al"));
if (print_decompress)
{
if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
puts (gettext (" [Compression Size Al]"));
else
puts (gettext (" [Compression Size Al]"));
}
for (cnt = 0; cnt < shnum; ++cnt)
{
Elf_Scn *scn = elf_getscn (ebl->elf, cnt);
if (unlikely (scn == NULL))
error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"),
elf_errmsg (-1));
/* Get the section header. */
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (unlikely (shdr == NULL))
error (EXIT_FAILURE, 0, gettext ("cannot get section header: %s"),
elf_errmsg (-1));
char flagbuf[20];
char *cp = flagbuf;
if (shdr->sh_flags & SHF_WRITE)
*cp++ = 'W';
if (shdr->sh_flags & SHF_ALLOC)
*cp++ = 'A';
if (shdr->sh_flags & SHF_EXECINSTR)
*cp++ = 'X';
if (shdr->sh_flags & SHF_MERGE)
*cp++ = 'M';
if (shdr->sh_flags & SHF_STRINGS)
*cp++ = 'S';
if (shdr->sh_flags & SHF_INFO_LINK)
*cp++ = 'I';
if (shdr->sh_flags & SHF_LINK_ORDER)
*cp++ = 'L';
if (shdr->sh_flags & SHF_OS_NONCONFORMING)
*cp++ = 'N';
if (shdr->sh_flags & SHF_GROUP)
*cp++ = 'G';
if (shdr->sh_flags & SHF_TLS)
*cp++ = 'T';
if (shdr->sh_flags & SHF_COMPRESSED)
*cp++ = 'C';
if (shdr->sh_flags & SHF_ORDERED)
*cp++ = 'O';
if (shdr->sh_flags & SHF_EXCLUDE)
*cp++ = 'E';
*cp = '\0';
const char *sname;
char buf[128];
sname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name) ?: "<corrupt>";
printf ("[%2zu] %-20s %-12s %0*" PRIx64 " %0*" PRIx64 " %0*" PRIx64
" %2" PRId64 " %-5s %2" PRId32 " %3" PRId32
" %2" PRId64 "\n",
cnt, sname,
ebl_section_type_name (ebl, shdr->sh_type, buf, sizeof (buf)),
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, shdr->sh_addr,
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_offset,
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_size,
shdr->sh_entsize, flagbuf, shdr->sh_link, shdr->sh_info,
shdr->sh_addralign);
if (print_decompress)
{
if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
{
GElf_Chdr chdr;
if (gelf_getchdr (scn, &chdr) != NULL)
printf (" [ELF %s (%" PRId32 ") %0*" PRIx64
" %2" PRId64 "]\n",
elf_ch_type_name (chdr.ch_type),
chdr.ch_type,
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8,
chdr.ch_size, chdr.ch_addralign);
else
error (0, 0,
gettext ("bad compression header for section %zd: %s"),
elf_ndxscn (scn), elf_errmsg (-1));
}
else if (strncmp(".zdebug", sname, strlen (".zdebug")) == 0)
{
ssize_t size;
if ((size = dwelf_scn_gnu_compressed_size (scn)) >= 0)
printf (" [GNU ZLIB %0*zx ]\n",
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, size);
else
error (0, 0,
gettext ("bad gnu compressed size for section %zd: %s"),
elf_ndxscn (scn), elf_errmsg (-1));
}
}
}
fputc_unlocked ('\n', stdout);
}
/* Print the program header. */
static void
print_phdr (Ebl *ebl, GElf_Ehdr *ehdr)
{
if (phnum == 0)
/* No program header, this is OK in relocatable objects. */
return;
puts (gettext ("Program Headers:"));
if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
puts (gettext ("\
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align"));
else
puts (gettext ("\
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align"));
/* Process all program headers. */
bool has_relro = false;
GElf_Addr relro_from = 0;
GElf_Addr relro_to = 0;
for (size_t cnt = 0; cnt < phnum; ++cnt)
{
char buf[128];
GElf_Phdr mem;
GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &mem);
/* If for some reason the header cannot be returned show this. */
if (unlikely (phdr == NULL))
{
puts (" ???");
continue;
}
printf (" %-14s 0x%06" PRIx64 " 0x%0*" PRIx64 " 0x%0*" PRIx64
" 0x%06" PRIx64 " 0x%06" PRIx64 " %c%c%c 0x%" PRIx64 "\n",
ebl_segment_type_name (ebl, phdr->p_type, buf, sizeof (buf)),
phdr->p_offset,
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, phdr->p_vaddr,
ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, phdr->p_paddr,
phdr->p_filesz,
phdr->p_memsz,
phdr->p_flags & PF_R ? 'R' : ' ',
phdr->p_flags & PF_W ? 'W' : ' ',
phdr->p_flags & PF_X ? 'E' : ' ',
phdr->p_align);
if (phdr->p_type == PT_INTERP)
{
/* If we are sure the file offset is valid then we can show
the user the name of the interpreter. We check whether
there is a section at the file offset. Normally there
would be a section called ".interp". But in separate
.debug files it is a NOBITS section (and so doesn't match
with gelf_offscn). Which probably means the offset is
not valid another reason could be because the ELF file
just doesn't contain any section headers, in that case
just play it safe and don't display anything. */
Elf_Scn *scn = gelf_offscn (ebl->elf, phdr->p_offset);
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
size_t maxsize;
char *filedata = elf_rawfile (ebl->elf, &maxsize);
if (shdr != NULL && shdr->sh_type == SHT_PROGBITS
&& filedata != NULL && phdr->p_offset < maxsize
&& phdr->p_filesz <= maxsize - phdr->p_offset
&& memchr (filedata + phdr->p_offset, '\0',
phdr->p_filesz) != NULL)
printf (gettext ("\t[Requesting program interpreter: %s]\n"),
filedata + phdr->p_offset);
}
else if (phdr->p_type == PT_GNU_RELRO)
{
has_relro = true;
relro_from = phdr->p_vaddr;
relro_to = relro_from + phdr->p_memsz;
}
}
size_t sections;
if (unlikely (elf_getshdrnum (ebl->elf, &sections) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot get number of sections: %s"),
elf_errmsg (-1));
if (sections == 0)
/* No sections in the file. Punt. */
return;
/* Get the section header string table index. */
size_t shstrndx;
if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot get section header string table index"));
puts (gettext ("\n Section to Segment mapping:\n Segment Sections..."));
for (size_t cnt = 0; cnt < phnum; ++cnt)
{
/* Print the segment number. */
printf (" %2.2zu ", cnt);
GElf_Phdr phdr_mem;
GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &phdr_mem);
/* This must not happen. */
if (unlikely (phdr == NULL))
error (EXIT_FAILURE, 0, gettext ("cannot get program header: %s"),
elf_errmsg (-1));
/* Iterate over the sections. */
bool in_relro = false;
bool in_ro = false;
for (size_t inner = 1; inner < shnum; ++inner)
{
Elf_Scn *scn = elf_getscn (ebl->elf, inner);
/* This should not happen. */
if (unlikely (scn == NULL))
error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"),
elf_errmsg (-1));
/* Get the section header. */
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (unlikely (shdr == NULL))
error (EXIT_FAILURE, 0,
gettext ("cannot get section header: %s"),
elf_errmsg (-1));
if (shdr->sh_size > 0
/* Compare allocated sections by VMA, unallocated
sections by file offset. */
&& (shdr->sh_flags & SHF_ALLOC
? (shdr->sh_addr >= phdr->p_vaddr
&& (shdr->sh_addr + shdr->sh_size
<= phdr->p_vaddr + phdr->p_memsz))
: (shdr->sh_offset >= phdr->p_offset
&& (shdr->sh_offset + shdr->sh_size
<= phdr->p_offset + phdr->p_filesz))))
{
if (has_relro && !in_relro
&& shdr->sh_addr >= relro_from
&& shdr->sh_addr + shdr->sh_size <= relro_to)
{
fputs_unlocked (" [RELRO:", stdout);
in_relro = true;
}
else if (has_relro && in_relro && shdr->sh_addr >= relro_to)
{
fputs_unlocked ("]", stdout);
in_relro = false;
}
else if (has_relro && in_relro
&& shdr->sh_addr + shdr->sh_size > relro_to)
fputs_unlocked ("] <RELRO:", stdout);
else if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_W) == 0)
{
if (!in_ro)
{
fputs_unlocked (" [RO:", stdout);
in_ro = true;
}
}
else
{
/* Determine the segment this section is part of. */
size_t cnt2;
GElf_Phdr phdr2_mem;
GElf_Phdr *phdr2 = NULL;
for (cnt2 = 0; cnt2 < phnum; ++cnt2)
{
phdr2 = gelf_getphdr (ebl->elf, cnt2, &phdr2_mem);
if (phdr2 != NULL && phdr2->p_type == PT_LOAD
&& shdr->sh_addr >= phdr2->p_vaddr
&& (shdr->sh_addr + shdr->sh_size
<= phdr2->p_vaddr + phdr2->p_memsz))
break;
}
if (cnt2 < phnum)
{
if ((phdr2->p_flags & PF_W) == 0 && !in_ro)
{
fputs_unlocked (" [RO:", stdout);
in_ro = true;
}
else if ((phdr2->p_flags & PF_W) != 0 && in_ro)
{
fputs_unlocked ("]", stdout);
in_ro = false;
}
}
}
printf (" %s",
elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
/* Signal that this sectin is only partially covered. */
if (has_relro && in_relro
&& shdr->sh_addr + shdr->sh_size > relro_to)
{
fputs_unlocked (">", stdout);
in_relro = false;
}
}
}
if (in_relro || in_ro)
fputs_unlocked ("]", stdout);
/* Finish the line. */
fputc_unlocked ('\n', stdout);
}
}
static const char *
section_name (Ebl *ebl, GElf_Shdr *shdr)
{
size_t shstrndx;
if (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)
return "???";
return elf_strptr (ebl->elf, shstrndx, shdr->sh_name) ?: "???";
}
static void
handle_scngrp (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
{
/* Get the data of the section. */
Elf_Data *data = elf_getdata (scn, NULL);
Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
GElf_Shdr symshdr_mem;
GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
Elf_Data *symdata = elf_getdata (symscn, NULL);
if (data == NULL || data->d_size < sizeof (Elf32_Word) || symshdr == NULL
|| symdata == NULL)
return;
/* Get the section header string table index. */
size_t shstrndx;
if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot get section header string table index"));
Elf32_Word *grpref = (Elf32_Word *) data->d_buf;
GElf_Sym sym_mem;
GElf_Sym *sym = gelf_getsym (symdata, shdr->sh_info, &sym_mem);
printf ((grpref[0] & GRP_COMDAT)
? ngettext ("\
\nCOMDAT section group [%2zu] '%s' with signature '%s' contains %zu entry:\n",
"\
\nCOMDAT section group [%2zu] '%s' with signature '%s' contains %zu entries:\n",
data->d_size / sizeof (Elf32_Word) - 1)
: ngettext ("\
\nSection group [%2zu] '%s' with signature '%s' contains %zu entry:\n", "\
\nSection group [%2zu] '%s' with signature '%s' contains %zu entries:\n",
data->d_size / sizeof (Elf32_Word) - 1),
elf_ndxscn (scn),
elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
(sym == NULL ? NULL
: elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name))
?: gettext ("<INVALID SYMBOL>"),
data->d_size / sizeof (Elf32_Word) - 1);
for (size_t cnt = 1; cnt < data->d_size / sizeof (Elf32_Word); ++cnt)
{
GElf_Shdr grpshdr_mem;
GElf_Shdr *grpshdr = gelf_getshdr (elf_getscn (ebl->elf, grpref[cnt]),
&grpshdr_mem);
const char *str;
printf (" [%2u] %s\n",
grpref[cnt],
grpshdr != NULL
&& (str = elf_strptr (ebl->elf, shstrndx, grpshdr->sh_name))
? str : gettext ("<INVALID SECTION>"));
}
}
static void
print_scngrp (Ebl *ebl)
{
/* Find all relocation sections and handle them. */
Elf_Scn *scn = NULL;
while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
{
/* Handle the section if it is a symbol table. */
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr != NULL && shdr->sh_type == SHT_GROUP)
{
if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
{
if (elf_compress (scn, 0, 0) < 0)
printf ("WARNING: %s [%zd]\n",
gettext ("Couldn't uncompress section"),
elf_ndxscn (scn));
shdr = gelf_getshdr (scn, &shdr_mem);
if (unlikely (shdr == NULL))
error (EXIT_FAILURE, 0,
gettext ("cannot get section [%zd] header: %s"),
elf_ndxscn (scn),
elf_errmsg (-1));
}
handle_scngrp (ebl, scn, shdr);
}
}
}
static const struct flags
{
int mask;
const char *str;
} dt_flags[] =
{
{ DF_ORIGIN, "ORIGIN" },
{ DF_SYMBOLIC, "SYMBOLIC" },
{ DF_TEXTREL, "TEXTREL" },
{ DF_BIND_NOW, "BIND_NOW" },
{ DF_STATIC_TLS, "STATIC_TLS" }
};
static const int ndt_flags = sizeof (dt_flags) / sizeof (dt_flags[0]);
static const struct flags dt_flags_1[] =
{
{ DF_1_NOW, "NOW" },
{ DF_1_GLOBAL, "GLOBAL" },
{ DF_1_GROUP, "GROUP" },
{ DF_1_NODELETE, "NODELETE" },
{ DF_1_LOADFLTR, "LOADFLTR" },
{ DF_1_INITFIRST, "INITFIRST" },
{ DF_1_NOOPEN, "NOOPEN" },
{ DF_1_ORIGIN, "ORIGIN" },
{ DF_1_DIRECT, "DIRECT" },
{ DF_1_TRANS, "TRANS" },
{ DF_1_INTERPOSE, "INTERPOSE" },
{ DF_1_NODEFLIB, "NODEFLIB" },
{ DF_1_NODUMP, "NODUMP" },
{ DF_1_CONFALT, "CONFALT" },
{ DF_1_ENDFILTEE, "ENDFILTEE" },
{ DF_1_DISPRELDNE, "DISPRELDNE" },
{ DF_1_DISPRELPND, "DISPRELPND" },
};
static const int ndt_flags_1 = sizeof (dt_flags_1) / sizeof (dt_flags_1[0]);
static const struct flags dt_feature_1[] =
{
{ DTF_1_PARINIT, "PARINIT" },
{ DTF_1_CONFEXP, "CONFEXP" }
};
static const int ndt_feature_1 = (sizeof (dt_feature_1)
/ sizeof (dt_feature_1[0]));
static const struct flags dt_posflag_1[] =
{
{ DF_P1_LAZYLOAD, "LAZYLOAD" },
{ DF_P1_GROUPPERM, "GROUPPERM" }
};
static const int ndt_posflag_1 = (sizeof (dt_posflag_1)
/ sizeof (dt_posflag_1[0]));
static void
print_flags (int class, GElf_Xword d_val, const struct flags *flags,
int nflags)
{
bool first = true;
int cnt;
for (cnt = 0; cnt < nflags; ++cnt)
if (d_val & flags[cnt].mask)
{
if (!first)
putchar_unlocked (' ');
fputs_unlocked (flags[cnt].str, stdout);
d_val &= ~flags[cnt].mask;
first = false;
}
if (d_val != 0)
{
if (!first)
putchar_unlocked (' ');
printf ("%#0*" PRIx64, class == ELFCLASS32 ? 10 : 18, d_val);
}
putchar_unlocked ('\n');
}
static void
print_dt_flags (int class, GElf_Xword d_val)
{
print_flags (class, d_val, dt_flags, ndt_flags);
}
static void
print_dt_flags_1 (int class, GElf_Xword d_val)
{
print_flags (class, d_val, dt_flags_1, ndt_flags_1);
}
static void
print_dt_feature_1 (int class, GElf_Xword d_val)
{
print_flags (class, d_val, dt_feature_1, ndt_feature_1);
}
static void
print_dt_posflag_1 (int class, GElf_Xword d_val)
{
print_flags (class, d_val, dt_posflag_1, ndt_posflag_1);
}
static void
handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
{
int class = gelf_getclass (ebl->elf);
GElf_Shdr glink_mem;
GElf_Shdr *glink;
Elf_Data *data;
size_t cnt;
size_t shstrndx;
size_t sh_entsize;
/* Get the data of the section. */
data = elf_getdata (scn, NULL);
if (data == NULL)
return;
/* Get the section header string table index. */
if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot get section header string table index"));
sh_entsize = gelf_fsize (ebl->elf, ELF_T_DYN, 1, EV_CURRENT);
glink = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &glink_mem);
if (glink == NULL)
error (EXIT_FAILURE, 0, gettext ("invalid sh_link value in section %zu"),
elf_ndxscn (scn));
printf (ngettext ("\
\nDynamic segment contains %lu entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n",
"\
\nDynamic segment contains %lu entries:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n",
shdr->sh_size / sh_entsize),
(unsigned long int) (shdr->sh_size / sh_entsize),
class == ELFCLASS32 ? 10 : 18, shdr->sh_addr,
shdr->sh_offset,
(int) shdr->sh_link,
elf_strptr (ebl->elf, shstrndx, glink->sh_name));
fputs_unlocked (gettext (" Type Value\n"), stdout);
for (cnt = 0; cnt < shdr->sh_size / sh_entsize; ++cnt)
{
GElf_Dyn dynmem;
GElf_Dyn *dyn = gelf_getdyn (data, cnt, &dynmem);
if (dyn == NULL)
break;
char buf[64];
printf (" %-17s ",
ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, sizeof (buf)));
switch (dyn->d_tag)
{
case DT_NULL:
case DT_DEBUG:
case DT_BIND_NOW:
case DT_TEXTREL:
/* No further output. */
fputc_unlocked ('\n', stdout);
break;
case DT_NEEDED:
printf (gettext ("Shared library: [%s]\n"),
elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val));
break;
case DT_SONAME:
printf (gettext ("Library soname: [%s]\n"),
elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val));
break;
case DT_RPATH:
printf (gettext ("Library rpath: [%s]\n"),
elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val));
break;
case DT_RUNPATH:
printf (gettext ("Library runpath: [%s]\n"),
elf_strptr (ebl->elf, shdr->sh_link, dyn->d_un.d_val));
break;
case DT_PLTRELSZ:
case DT_RELASZ:
case DT_STRSZ:
case DT_RELSZ:
case DT_RELAENT:
case DT_SYMENT:
case DT_RELENT:
case DT_PLTPADSZ:
case DT_MOVEENT:
case DT_MOVESZ:
case DT_INIT_ARRAYSZ:
case DT_FINI_ARRAYSZ:
case DT_SYMINSZ:
case DT_SYMINENT:
case DT_GNU_CONFLICTSZ:
case DT_GNU_LIBLISTSZ:
printf (gettext ("%" PRId64 " (bytes)\n"), dyn->d_un.d_val);
break;
case DT_VERDEFNUM:
case DT_VERNEEDNUM:
case DT_RELACOUNT:
case DT_RELCOUNT:
printf ("%" PRId64 "\n", dyn->d_un.d_val);
break;
case DT_PLTREL:;
const char *tagname = ebl_dynamic_tag_name (ebl, dyn->d_un.d_val,
NULL, 0);
puts (tagname ?: "???");
break;
case DT_FLAGS:
print_dt_flags (class, dyn->d_un.d_val);
break;
case DT_FLAGS_1:
print_dt_flags_1 (class, dyn->d_un.d_val);
break;
case DT_FEATURE_1:
print_dt_feature_1 (class, dyn->d_un.d_val);
break;
case DT_POSFLAG_1:
print_dt_posflag_1 (class, dyn->d_un.d_val);
break;
default:
printf ("%#0*" PRIx64 "\n",
class == ELFCLASS32 ? 10 : 18, dyn->d_un.d_val);
break;
}
}
}
/* Print the dynamic segment. */
static void
print_dynamic (Ebl *ebl)
{
for (size_t i = 0; i < phnum; ++i)
{
GElf_Phdr phdr_mem;
GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem);
if (phdr != NULL && phdr->p_type == PT_DYNAMIC)
{
Elf_Scn *scn = gelf_offscn (ebl->elf, phdr->p_offset);
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC)
handle_dynamic (ebl, scn, shdr);
break;
}
}
}
/* Print relocations. */
static void
print_relocs (Ebl *ebl, GElf_Ehdr *ehdr)
{
/* Find all relocation sections and handle them. */
Elf_Scn *scn = NULL;
while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
{
/* Handle the section if it is a symbol table. */
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (likely (shdr != NULL))
{
if (shdr->sh_type == SHT_REL)
handle_relocs_rel (ebl, ehdr, scn, shdr);
else if (shdr->sh_type == SHT_RELA)
handle_relocs_rela (ebl, ehdr, scn, shdr);
}
}
}
/* Handle a relocation section. */
static void
handle_relocs_rel (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr)
{
int class = gelf_getclass (ebl->elf);
size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_REL, 1, EV_CURRENT);
int nentries = shdr->sh_size / sh_entsize;
/* Get the data of the section. */
Elf_Data *data = elf_getdata (scn, NULL);
if (data == NULL)
return;
/* Get the symbol table information. */
Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
GElf_Shdr symshdr_mem;
GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
Elf_Data *symdata = elf_getdata (symscn, NULL);
/* Get the section header of the section the relocations are for. */
GElf_Shdr destshdr_mem;
GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info),
&destshdr_mem);
if (unlikely (symshdr == NULL || symdata == NULL || destshdr == NULL))
{
printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"),
shdr->sh_offset);
return;
}
/* Search for the optional extended section index table. */
Elf_Data *xndxdata = NULL;
int xndxscnidx = elf_scnshndx (scn);
if (unlikely (xndxscnidx > 0))
xndxdata = elf_getdata (elf_getscn (ebl->elf, xndxscnidx), NULL);
/* Get the section header string table index. */
size_t shstrndx;
if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot get section header string table index"));
if (shdr->sh_info != 0)
printf (ngettext ("\
\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
"\
\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n",
nentries),
elf_ndxscn (scn),
elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
(unsigned int) shdr->sh_info,
elf_strptr (ebl->elf, shstrndx, destshdr->sh_name),
shdr->sh_offset,
nentries);
else
/* The .rel.dyn section does not refer to a specific section but
instead of section index zero. Do not try to print a section
name. */
printf (ngettext ("\
\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
"\
\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n",
nentries),
(unsigned int) elf_ndxscn (scn),
elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
shdr->sh_offset,
nentries);
fputs_unlocked (class == ELFCLASS32
? gettext ("\
Offset Type Value Name\n")
: gettext ("\
Offset Type Value Name\n"),
stdout);
int is_statically_linked = 0;
for (int cnt = 0; cnt < nentries; ++cnt)
{
GElf_Rel relmem;
GElf_Rel *rel = gelf_getrel (data, cnt, &relmem);
if (likely (rel != NULL))
{
char buf[128];
GElf_Sym symmem;
Elf32_Word xndx;
GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata,
GELF_R_SYM (rel->r_info),
&symmem, &xndx);
if (unlikely (sym == NULL))
{
/* As a special case we have to handle relocations in static
executables. This only happens for IRELATIVE relocations
(so far). There is no symbol table. */
if (is_statically_linked == 0)
{
/* Find the program header and look for a PT_INTERP entry. */
is_statically_linked = -1;
if (ehdr->e_type == ET_EXEC)
{
is_statically_linked = 1;
for (size_t inner = 0; inner < phnum; ++inner)
{
GElf_Phdr phdr_mem;
GElf_Phdr *phdr = gelf_getphdr (ebl->elf, inner,
&phdr_mem);
if (phdr != NULL && phdr->p_type == PT_INTERP)
{
is_statically_linked = -1;
break;
}
}
}
}
if (is_statically_linked > 0 && shdr->sh_link == 0)
printf ("\
%#0*" PRIx64 " %-20s %*s %s\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
/* Avoid the leading R_ which isn't carrying any
information. */
? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
buf, sizeof (buf)) + 2
: gettext ("<INVALID RELOC>"),
class == ELFCLASS32 ? 10 : 18, "",
elf_strptr (ebl->elf, shstrndx, destshdr->sh_name));
else
printf (" %#0*" PRIx64 " %-20s <%s %ld>\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
/* Avoid the leading R_ which isn't carrying any
information. */
? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
buf, sizeof (buf)) + 2
: gettext ("<INVALID RELOC>"),
gettext ("INVALID SYMBOL"),
(long int) GELF_R_SYM (rel->r_info));
}
else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION)
printf (" %#0*" PRIx64 " %-20s %#0*" PRIx64 " %s\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
likely (ebl_reloc_type_check (ebl,
GELF_R_TYPE (rel->r_info)))
/* Avoid the leading R_ which isn't carrying any
information. */
? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
buf, sizeof (buf)) + 2
: gettext ("<INVALID RELOC>"),
class == ELFCLASS32 ? 10 : 18, sym->st_value,
elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name));
else
{
/* This is a relocation against a STT_SECTION symbol. */
GElf_Shdr secshdr_mem;
GElf_Shdr *secshdr;
secshdr = gelf_getshdr (elf_getscn (ebl->elf,
sym->st_shndx == SHN_XINDEX
? xndx : sym->st_shndx),
&secshdr_mem);
if (unlikely (secshdr == NULL))
printf (" %#0*" PRIx64 " %-20s <%s %ld>\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
/* Avoid the leading R_ which isn't carrying any
information. */
? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
buf, sizeof (buf)) + 2
: gettext ("<INVALID RELOC>"),
gettext ("INVALID SECTION"),
(long int) (sym->st_shndx == SHN_XINDEX
? xndx : sym->st_shndx));
else
printf (" %#0*" PRIx64 " %-20s %#0*" PRIx64 " %s\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
/* Avoid the leading R_ which isn't carrying any
information. */
? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
buf, sizeof (buf)) + 2
: gettext ("<INVALID RELOC>"),
class == ELFCLASS32 ? 10 : 18, sym->st_value,
elf_strptr (ebl->elf, shstrndx, secshdr->sh_name));
}
}
}
}
/* Handle a relocation section. */
static void
handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr)
{
int class = gelf_getclass (ebl->elf);
size_t sh_entsize = gelf_fsize (ebl->elf, ELF_T_RELA, 1, EV_CURRENT);
int nentries = shdr->sh_size / sh_entsize;
/* Get the data of the section. */
Elf_Data *data = elf_getdata (scn, NULL);
if (data == NULL)
return;
/* Get the symbol table information. */
Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link);
GElf_Shdr symshdr_mem;
GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
Elf_Data *symdata = elf_getdata (symscn, NULL);
/* Get the section header of the section the relocations are for. */
GElf_Shdr destshdr_mem;
GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info),
&destshdr_mem);
if (unlikely (symshdr == NULL || symdata == NULL || destshdr == NULL))
{
printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"),
shdr->sh_offset);
return;
}
/* Search for the optional extended section index table. */
Elf_Data *xndxdata = NULL;
int xndxscnidx = elf_scnshndx (scn);
if (unlikely (xndxscnidx > 0))
xndxdata = elf_getdata (elf_getscn (ebl->elf, xndxscnidx), NULL);
/* Get the section header string table index. */
size_t shstrndx;
if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot get section header string table index"));
if (shdr->sh_info != 0)
printf (ngettext ("\
\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
"\
\nRelocation section [%2zu] '%s' for section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n",
nentries),
elf_ndxscn (scn),
elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
(unsigned int) shdr->sh_info,
elf_strptr (ebl->elf, shstrndx, destshdr->sh_name),
shdr->sh_offset,
nentries);
else
/* The .rela.dyn section does not refer to a specific section but
instead of section index zero. Do not try to print a section
name. */
printf (ngettext ("\
\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entry:\n",
"\
\nRelocation section [%2u] '%s' at offset %#0" PRIx64 " contains %d entries:\n",
nentries),
(unsigned int) elf_ndxscn (scn),
elf_strptr (ebl->elf, shstrndx, shdr->sh_name),
shdr->sh_offset,
nentries);
fputs_unlocked (class == ELFCLASS32
? gettext ("\
Offset Type Value Addend Name\n")
: gettext ("\
Offset Type Value Addend Name\n"),
stdout);
int is_statically_linked = 0;
for (int cnt = 0; cnt < nentries; ++cnt)
{
GElf_Rela relmem;
GElf_Rela *rel = gelf_getrela (data, cnt, &relmem);
if (likely (rel != NULL))
{
char buf[64];
GElf_Sym symmem;
Elf32_Word xndx;
GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata,
GELF_R_SYM (rel->r_info),
&symmem, &xndx);
if (unlikely (sym == NULL))
{
/* As a special case we have to handle relocations in static
executables. This only happens for IRELATIVE relocations
(so far). There is no symbol table. */
if (is_statically_linked == 0)
{
/* Find the program header and look for a PT_INTERP entry. */
is_statically_linked = -1;
if (ehdr->e_type == ET_EXEC)
{
is_statically_linked = 1;
for (size_t inner = 0; inner < phnum; ++inner)
{
GElf_Phdr phdr_mem;
GElf_Phdr *phdr = gelf_getphdr (ebl->elf, inner,
&phdr_mem);
if (phdr != NULL && phdr->p_type == PT_INTERP)
{
is_statically_linked = -1;
break;
}
}
}
}
if (is_statically_linked > 0 && shdr->sh_link == 0)
printf ("\
%#0*" PRIx64 " %-15s %*s %#6" PRIx64 " %s\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
/* Avoid the leading R_ which isn't carrying any
information. */
? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
buf, sizeof (buf)) + 2
: gettext ("<INVALID RELOC>"),
class == ELFCLASS32 ? 10 : 18, "",
rel->r_addend,
elf_strptr (ebl->elf, shstrndx, destshdr->sh_name));
else
printf (" %#0*" PRIx64 " %-15s <%s %ld>\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
/* Avoid the leading R_ which isn't carrying any
information. */
? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
buf, sizeof (buf)) + 2
: gettext ("<INVALID RELOC>"),
gettext ("INVALID SYMBOL"),
(long int) GELF_R_SYM (rel->r_info));
}
else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION)
printf ("\
%#0*" PRIx64 " %-15s %#0*" PRIx64 " %+6" PRId64 " %s\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
likely (ebl_reloc_type_check (ebl,
GELF_R_TYPE (rel->r_info)))
/* Avoid the leading R_ which isn't carrying any
information. */
? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
buf, sizeof (buf)) + 2
: gettext ("<INVALID RELOC>"),
class == ELFCLASS32 ? 10 : 18, sym->st_value,
rel->r_addend,
elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name));
else
{
/* This is a relocation against a STT_SECTION symbol. */
GElf_Shdr secshdr_mem;
GElf_Shdr *secshdr;
secshdr = gelf_getshdr (elf_getscn (ebl->elf,
sym->st_shndx == SHN_XINDEX
? xndx : sym->st_shndx),
&secshdr_mem);
if (unlikely (secshdr == NULL))
printf (" %#0*" PRIx64 " %-15s <%s %ld>\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
/* Avoid the leading R_ which isn't carrying any
information. */
? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
buf, sizeof (buf)) + 2
: gettext ("<INVALID RELOC>"),
gettext ("INVALID SECTION"),
(long int) (sym->st_shndx == SHN_XINDEX
? xndx : sym->st_shndx));
else
printf ("\
%#0*" PRIx64 " %-15s %#0*" PRIx64 " %+6" PRId64 " %s\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
/* Avoid the leading R_ which isn't carrying any
information. */
? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info),
buf, sizeof (buf)) + 2
: gettext ("<INVALID RELOC>"),
class == ELFCLASS32 ? 10 : 18, sym->st_value,
rel->r_addend,
elf_strptr (ebl->elf, shstrndx, secshdr->sh_name));
}
}
}
}
/* Print the program header. */
static void
print_symtab (Ebl *ebl, int type)
{
/* Find the symbol table(s). For this we have to search through the
section table. */
Elf_Scn *scn = NULL;
while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
{
/* Handle the section if it is a symbol table. */
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
if (shdr != NULL && shdr->sh_type == (GElf_Word) type)
{
if (symbol_table_section != NULL)
{
/* Get the section header string table index. */
size_t shstrndx;
const char *sname;
if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot get section header string table index"));
sname = elf_strptr (ebl->elf, shstrndx, shdr->sh_name);
if (sname == NULL || strcmp (sname, symbol_table_section) != 0)
continue;
}
if ((shdr->sh_flags & SHF_COMPRESSED) != 0)
{
if (elf_compress (scn, 0, 0) < 0)
printf ("WARNING: %s [%zd]\n",
gettext ("Couldn't uncompress section"),
elf_ndxscn (scn));
shdr = gelf_getshdr (scn, &shdr_mem);
if (unlikely (shdr == NULL))
error (EXIT_FAILURE, 0,
gettext ("cannot get section [%zd] header: %s"),
elf_ndxscn (scn), elf_errmsg (-1));
}
handle_symtab (ebl, scn, shdr);
}
}
}
static void
handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr)
{
Elf_Data *versym_data = NULL;
Elf_Data *verneed_data = NULL;
Elf_Data *verdef_data = NULL;
Elf_Data *xndx_data = NULL;
int class = gelf_getclass (ebl->elf);
Elf32_Word verneed_stridx = 0;
Elf32_Word verdef_stridx = 0;
/* Get the data of the section. */
Elf_Data *data = elf_getdata (scn, NULL);
if (data == NULL)
return;
/* Find out whether we have other sections we might need. */
Elf_Scn *runscn = NULL;
while ((runscn = elf_nextscn (ebl->elf, runscn)) != NULL)
{
GElf_Shdr runshdr_mem;
GElf_Shdr *runshdr = gelf_getshdr (runscn, &runshdr_mem);
if (likely (runshdr != NULL))
{
if (runshdr->sh_type == SHT_GNU_versym
&& runshdr->sh_link == elf_ndxscn (scn))
/* Bingo, found the version information. Now get the data. */
versym_data = elf_getdata (runscn, NULL);
else if (runshdr->sh_type == SHT_GNU_verneed)
{
/* This is the information about the needed versions. */
verneed_data = elf_getdata (runscn, NULL);
verneed_stridx = runshdr->sh_link;
}
else if (runshdr->sh_type == SHT_GNU_verdef)
{
/* This is the information about the defined versions. */
verdef_data = elf_getdata (runscn, NULL);
verdef_stridx = runshdr->sh_link;
}
else if (runshdr->sh_type == SHT_SYMTAB_SHNDX
&& runshdr->sh_link == elf_ndxscn (scn))
/* Extended section index. */
xndx_data = elf_getdata (runscn, NULL);
}
}
/* Get the section header string table index. */
size_t shstrndx;
if (unlikely (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0))
error (EXIT_FAILURE, 0,
gettext ("cannot get section header string table index"));
GElf_Shdr glink_mem;
GElf_Shdr *glink = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link),
&glink_mem);
if (glink == NULL)
error (EXIT_FAILURE, 0, gettext ("invalid sh_link value in section %zu"),
elf_ndxscn (scn));
/* Now we can compute the number of entries in the section. */
unsigned int nsyms = data->d_size / (class == ELFCLASS32
? sizeof (Elf32_Sym)
: sizeof (Elf64_Sym));
printf (ngettext ("\nSymbol table [%2u] '%s' contains %u entry:\n",
"\nSymbol table [%2u] '%s' contains %u entries:\n",
nsyms),
(unsigned int) elf_ndxscn (scn),
elf_strptr (ebl->elf, shstrndx, shdr->sh_name), nsyms);
printf (ngettext (" %lu local symbol String table: [%2u] '%s'\n",
" %lu local symbols String table: [%2u] '%s'\n",
shdr->sh_info),
(unsigned long int) shdr->sh_info,
(unsigned int) shdr->sh_link,
elf_strptr (ebl->elf, shstrndx, glink->sh_name));
fputs_unlocked (class == ELFCLASS32
? gettext ("\
Num: Value Size Type Bind Vis Ndx Name\n")
: gettext ("\
Num: Value Size Type Bind Vis Ndx Name\n"),
stdout);
for (unsigned int cnt = 0; cnt < nsyms; ++cnt)
{
char typebuf[64];
char bindbuf[64];
char scnbuf[64];
Elf32_Word xndx;
GElf_Sym sym_mem;
GElf_Sym *sym = gelf_getsymshndx (data, xndx_data, cnt, &sym_mem, &xndx);
if (unlikely (sym == NULL))
continue;
/* Determine the real section index. */
if (likely (sym->st_shndx != SHN_XINDEX))
xndx = sym->st_shndx;
printf (gettext ("\
%5u: %0*" PRIx64 " %6" PRId64 " %-7s %-6s %-9s %6s %s"),
cnt,
class == ELFCLASS32 ? 8 : 16,
sym->st_value,
sym->st_size,
ebl_symbol_type_name (ebl, GELF_ST_TYPE (sym->st_info),
typebuf, sizeof (typebuf)),
ebl_symbol_binding_name (ebl, GELF_ST_BIND (sym->st_info),
bindbuf, sizeof (bindbuf)),
get_visibility_type (GELF_ST_VISIBILITY (sym->st_other)),
ebl_section_name (ebl, sym->st_shndx, xndx, scnbuf,
sizeof (scnbuf), NULL, shnum),
elf_strptr (ebl->elf, shdr->sh_link, sym->st_name));
if (versym_data != NULL)
{
/* Get the version information. */
GElf_Versym versym_mem;
GElf_Versym *versym = gelf_getversym (versym_data, cnt, &versym_mem);
if (versym != NULL && ((*versym & 0x8000) != 0 || *versym > 1))
{
bool is_nobits = false;
bool check_def = xndx != SHN_UNDEF;
if (xndx < SHN_LORESERVE || sym->st_shndx == SHN_XINDEX)
{
GElf_Shdr symshdr_mem;
GElf_Shdr *symshdr =
gelf_getshdr (elf_getscn (ebl->elf, xndx), &symshdr_mem);
is_nobits = (symshdr != NULL
&& symshdr->sh_type == SHT_NOBITS);
}
if (is_nobits || ! check_def)
{
/* We must test both. */
GElf_Vernaux vernaux_mem;
GElf_Vernaux *vernaux = NULL;
size_t vn_offset = 0;
GElf_Verneed verneed_mem;
GElf_Verneed *verneed = gelf_getverneed (verneed_data, 0,
&verneed_mem);
while (verneed != NULL)
{
size_t vna_offset = vn_offset;
vernaux = gelf_getvernaux (verneed_data,
vna_offset += verneed->vn_aux,
&vernaux_mem);
while (vernaux != NULL
&& vernaux->vna_other != *versym
&& vernaux->vna_next != 0)
{
/* Update the offset. */
vna_offset += vernaux->vna_next;
vernaux = (vernaux->vna_next == 0
? NULL
: gelf_getvernaux (verneed_data,
vna_offset,
&vernaux_mem));
}
/* Check whether we found the version. */
if (vernaux != NULL && vernaux->vna_other == *versym)
/* Found it. */
break;
vn_offset += verneed->vn_next;
verneed = (verneed->vn_next == 0
? NULL
: gelf_getverneed (verneed_data, vn_offset,
&verneed_mem));
}
if (vernaux != NULL && vernaux->vna_other == *versym)
{
printf ("@%s (%u)",
elf_strptr (ebl->elf, verneed_stridx,
vernaux->vna_name),
(unsigned int) vernaux->vna_other);
check_def = 0;
}
else if (unlikely (! is_nobits))
error (0, 0, gettext ("bad dynamic symbol"));