/* readelf.c -- display contents of an ELF format file
- Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
Free Software Foundation, Inc.
Originally developed by Eric Youngdale <eric@andante.jic.com>
#include <stdio.h>
#include <time.h>
+/* for PATH_MAX */
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef PATH_MAX
+/* for MAXPATHLEN */
+# ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+# endif
+# ifndef PATH_MAX
+# ifdef MAXPATHLEN
+# define PATH_MAX MAXPATHLEN
+# else
+# define PATH_MAX 1024
+# endif
+# endif
+#endif
+
#if __GNUC__ >= 2
/* Define BFD64 here, even if our default architecture is 32 bit ELF
as this will allow us to read in and parse 64bit and 32bit ELF files.
#include "elf/external.h"
#include "elf/internal.h"
+
+/* Included here, before RELOC_MACROS_GEN_FUNC is defined, so that
+ we can obtain the H8 reloc numbers. We need these for the
+ get_reloc_size() function. We include h8.h again after defining
+ RELOC_MACROS_GEN_FUNC so that we get the naming function as well. */
+
+#include "elf/h8.h"
+#undef _ELF_H8_H
+
+/* Undo the effects of #including reloc-macros.h. */
+
+#undef START_RELOC_NUMBERS
+#undef RELOC_NUMBER
+#undef FAKE_RELOC
+#undef EMPTY_RELOC
+#undef END_RELOC_NUMBERS
+#undef _RELOC_MACROS_H
+
/* The following headers use the elf/reloc-macros.h file to
automatically generate relocation recognition functions
such as elf_mips_reloc_type() */
#include "elf/avr.h"
#include "elf/bfin.h"
#include "elf/cris.h"
+#include "elf/crx.h"
#include "elf/d10v.h"
#include "elf/d30v.h"
#include "elf/dlx.h"
#include "elf/i960.h"
#include "elf/ia64.h"
#include "elf/ip2k.h"
+#include "elf/iq2000.h"
#include "elf/m32c.h"
#include "elf/m32r.h"
#include "elf/m68k.h"
#include "elf/m68hc11.h"
#include "elf/mcore.h"
+#include "elf/mep.h"
#include "elf/mips.h"
#include "elf/mmix.h"
#include "elf/mn10200.h"
#include "elf/mn10300.h"
-#include "elf/ms1.h"
+#include "elf/mt.h"
#include "elf/msp430.h"
#include "elf/or32.h"
#include "elf/pj.h"
#include "elf/ppc.h"
#include "elf/ppc64.h"
#include "elf/s390.h"
+#include "elf/score.h"
#include "elf/sh.h"
#include "elf/sparc.h"
+#include "elf/spu.h"
#include "elf/v850.h"
#include "elf/vax.h"
#include "elf/x86-64.h"
#include "elf/xstormy16.h"
-#include "elf/crx.h"
-#include "elf/iq2000.h"
#include "elf/xtensa.h"
#include "aout/ar.h"
static Elf_Internal_Syminfo *dynamic_syminfo;
static unsigned long dynamic_syminfo_offset;
static unsigned int dynamic_syminfo_nent;
-static char program_interpreter[64];
+static char program_interpreter[PATH_MAX];
static bfd_vma dynamic_info[DT_JMPREL + 1];
+static bfd_vma dynamic_info_DT_GNU_HASH;
static bfd_vma version_info[16];
static Elf_Internal_Ehdr elf_header;
static Elf_Internal_Shdr *section_headers;
static struct group *section_groups;
static struct group **section_headers_groups;
+/* A linked list of the section names for which dumps were requested
+ by name. */
+struct dump_list_entry
+{
+ char *name;
+ int type;
+ struct dump_list_entry *next;
+};
+static struct dump_list_entry *dump_sects_byname;
+
/* A dynamic array of flags indicating for which sections a hex dump
has been requested (via the -x switch) and/or a disassembly dump
(via the -i switch). */
/* A dynamic array of flags indicating for which sections a dump of
some kind has been requested. It is reset on a per-object file
- basis and then initialised from the cmdline_dump_sects array and
- the results of interpreting the -w switch. */
+ basis and then initialised from the cmdline_dump_sects array,
+ the results of interpreting the -w switch, and the
+ dump_sects_byname list. */
char *dump_sects = NULL;
unsigned int num_dump_sects = 0;
#define UNKNOWN -1
-#define SECTION_NAME(X) ((X) == NULL ? "<none>" : \
- ((X)->sh_name >= string_table_length \
- ? "<corrupt>" : string_table + (X)->sh_name))
+#define SECTION_NAME(X) \
+ ((X) == NULL ? "<none>" \
+ : string_table == NULL ? "<no-name>" \
+ : ((X)->sh_name >= string_table_length ? "<corrupt>" \
+ : string_table + (X)->sh_name))
/* Given st_shndx I, map to section_headers index. */
#define SECTION_HEADER_INDEX(I) \
/* This is just a bit of syntatic sugar. */
#define streq(a,b) (strcmp ((a), (b)) == 0)
#define strneq(a,b,n) (strncmp ((a), (b), (n)) == 0)
+#define const_strneq(a,b) (strncmp ((a), (b), sizeof (b) - 1) == 0)
\f
static void *
get_data (void *var, FILE *file, long offset, size_t size, size_t nmemb,
switch (e_machine)
{
/* Targets that use REL relocations. */
- case EM_ARM:
case EM_386:
case EM_486:
case EM_960:
- case EM_DLX:
- case EM_OPENRISC:
- case EM_OR32:
- case EM_CYGNUS_M32R:
+ case EM_ARM:
case EM_D10V:
case EM_CYGNUS_D10V:
+ case EM_DLX:
case EM_MIPS:
case EM_MIPS_RS3_LE:
+ case EM_CYGNUS_M32R:
+ case EM_OPENRISC:
+ case EM_OR32:
+ case EM_SCORE:
return FALSE;
/* Targets that use RELA relocations. */
case EM_68K:
- case EM_H8_300:
- case EM_H8_300H:
- case EM_H8S:
- case EM_SPARC32PLUS:
- case EM_SPARCV9:
- case EM_SPARC:
- case EM_PPC:
- case EM_PPC64:
- case EM_V850:
- case EM_CYGNUS_V850:
+ case EM_860:
+ case EM_ALPHA:
+ case EM_ALTERA_NIOS2:
+ case EM_AVR:
+ case EM_AVR_OLD:
+ case EM_BLACKFIN:
+ case EM_CRIS:
+ case EM_CRX:
case EM_D30V:
case EM_CYGNUS_D30V:
- case EM_MN10200:
- case EM_CYGNUS_MN10200:
- case EM_MN10300:
- case EM_CYGNUS_MN10300:
case EM_FR30:
case EM_CYGNUS_FR30:
case EM_CYGNUS_FRV:
- case EM_SH:
- case EM_ALPHA:
- case EM_MCORE:
+ case EM_H8S:
+ case EM_H8_300:
+ case EM_H8_300H:
case EM_IA_64:
- case EM_AVR:
- case EM_AVR_OLD:
- case EM_CRIS:
- case EM_860:
- case EM_X86_64:
- case EM_S390:
- case EM_S390_OLD:
+ case EM_IP2K:
+ case EM_IP2K_OLD:
+ case EM_IQ2000:
+ case EM_M32C:
+ case EM_M32R:
+ case EM_MCORE:
+ case EM_CYGNUS_MEP:
case EM_MMIX:
+ case EM_MN10200:
+ case EM_CYGNUS_MN10200:
+ case EM_MN10300:
+ case EM_CYGNUS_MN10300:
case EM_MSP430:
case EM_MSP430_OLD:
- case EM_XSTORMY16:
- case EM_CRX:
+ case EM_MT:
+ case EM_NIOS32:
+ case EM_PPC64:
+ case EM_PPC:
+ case EM_S390:
+ case EM_S390_OLD:
+ case EM_SH:
+ case EM_SPARC:
+ case EM_SPARC32PLUS:
+ case EM_SPARCV9:
+ case EM_SPU:
+ case EM_V850:
+ case EM_CYGNUS_V850:
case EM_VAX:
- case EM_IP2K:
- case EM_IP2K_OLD:
- case EM_IQ2000:
+ case EM_X86_64:
+ case EM_XSTORMY16:
case EM_XTENSA:
case EM_XTENSA_OLD:
- case EM_M32R:
- case EM_M32C:
- case EM_MS1:
- case EM_BLACKFIN:
return TRUE;
+ case EM_68HC05:
+ case EM_68HC08:
+ case EM_68HC11:
+ case EM_68HC16:
+ case EM_FX66:
+ case EM_ME16:
case EM_MMA:
- case EM_PCP:
case EM_NCPU:
case EM_NDR1:
- case EM_STARCORE:
- case EM_ME16:
+ case EM_PCP:
case EM_ST100:
- case EM_TINYJ:
- case EM_FX66:
- case EM_ST9PLUS:
+ case EM_ST19:
case EM_ST7:
- case EM_68HC16:
- case EM_68HC11:
- case EM_68HC08:
- case EM_68HC05:
+ case EM_ST9PLUS:
+ case EM_STARCORE:
case EM_SVX:
- case EM_ST19:
+ case EM_TINYJ:
default:
warn (_("Don't know about relocations on this machine architecture\n"));
return FALSE;
rtype = elf_sparc_reloc_type (type);
break;
+ case EM_SPU:
+ rtype = elf_spu_reloc_type (type);
+ break;
+
case EM_V850:
case EM_CYGNUS_V850:
rtype = v850_reloc_type (type);
rtype = elf_s390_reloc_type (type);
break;
+ case EM_SCORE:
+ rtype = elf_score_reloc_type (type);
+ break;
+
case EM_XSTORMY16:
rtype = elf_xstormy16_reloc_type (type);
break;
rtype = elf_m32c_reloc_type (type);
break;
- case EM_MS1:
- rtype = elf_ms1_reloc_type (type);
+ case EM_MT:
+ rtype = elf_mt_reloc_type (type);
break;
case EM_BLACKFIN:
rtype = elf_bfin_reloc_type (type);
break;
+ case EM_CYGNUS_MEP:
+ rtype = elf_mep_reloc_type (type);
+ break;
}
if (rtype == NULL)
printf (do_wide ? "%-22.22s" : "%-17.17s", rtype);
if (elf_header.e_machine == EM_ALPHA
+ && rtype != NULL
&& streq (rtype, "R_ALPHA_LITUSE")
&& is_rela)
{
sec_name = "ABS";
else if (psym->st_shndx == SHN_COMMON)
sec_name = "COMMON";
+ else if (elf_header.e_machine == EM_MIPS
+ && psym->st_shndx == SHN_MIPS_SCOMMON)
+ sec_name = "SCOMMON";
+ else if (elf_header.e_machine == EM_MIPS
+ && psym->st_shndx == SHN_MIPS_SUNDEFINED)
+ sec_name = "SUNDEF";
else if (elf_header.e_machine == EM_X86_64
&& psym->st_shndx == SHN_X86_64_LCOMMON)
sec_name = "LARGE_COMMON";
print_vma (rels[i].r_addend, LONG_HEX);
}
- if (elf_header.e_machine == EM_SPARCV9 && streq (rtype, "R_SPARC_OLO10"))
+ if (elf_header.e_machine == EM_SPARCV9
+ && rtype != NULL
+ && streq (rtype, "R_SPARC_OLO10"))
printf (" + %lx", (unsigned long) ELF64_R_TYPE_DATA (info));
putchar ('\n');
}
}
+static const char *
+get_score_dynamic_type (unsigned long type)
+{
+ switch (type)
+ {
+ case DT_SCORE_BASE_ADDRESS: return "SCORE_BASE_ADDRESS";
+ case DT_SCORE_LOCAL_GOTNO: return "SCORE_LOCAL_GOTNO";
+ case DT_SCORE_SYMTABNO: return "SCORE_SYMTABNO";
+ case DT_SCORE_GOTSYM: return "SCORE_GOTSYM";
+ case DT_SCORE_UNREFEXTNO: return "SCORE_UNREFEXTNO";
+ case DT_SCORE_HIPAGENO: return "SCORE_HIPAGENO";
+ default:
+ return NULL;
+ }
+}
+
+
static const char *
get_dynamic_type (unsigned long type)
{
case DT_VERSYM: return "VERSYM";
+ case DT_TLSDESC_GOT: return "TLSDESC_GOT";
+ case DT_TLSDESC_PLT: return "TLSDESC_PLT";
case DT_RELACOUNT: return "RELACOUNT";
case DT_RELCOUNT: return "RELCOUNT";
case DT_FLAGS_1: return "FLAGS_1";
case DT_GNU_CONFLICTSZ: return "GNU_CONFLICTSZ";
case DT_GNU_LIBLIST: return "GNU_LIBLIST";
case DT_GNU_LIBLISTSZ: return "GNU_LIBLISTSZ";
+ case DT_GNU_HASH: return "GNU_HASH";
default:
if ((type >= DT_LOPROC) && (type <= DT_HIPROC))
case EM_ALPHA:
result = get_alpha_dynamic_type (type);
break;
+ case EM_SCORE:
+ result = get_score_dynamic_type (type);
+ break;
default:
result = NULL;
break;
case EM_NONE: return _("None");
case EM_M32: return "WE32100";
case EM_SPARC: return "Sparc";
+ case EM_SPU: return "SPU";
case EM_386: return "Intel 80386";
case EM_68K: return "MC68000";
case EM_88K: return "MC88000";
case EM_X86_64: return "Advanced Micro Devices X86-64";
case EM_S390_OLD:
case EM_S390: return "IBM S/390";
+ case EM_SCORE: return "SUNPLUS S+Core";
case EM_XSTORMY16: return "Sanyo Xstormy16 CPU core";
case EM_OPENRISC:
case EM_OR32: return "OpenRISC";
case EM_XTENSA_OLD:
case EM_XTENSA: return "Tensilica Xtensa Processor";
case EM_M32C: return "Renesas M32c";
- case EM_MS1: return "Morpho Techologies MS1 processor";
+ case EM_MT: return "Morpho Techologies MT processor";
+ case EM_BLACKFIN: return "Analog Devices Blackfin";
+ case EM_NIOS32: return "Altera Nios";
+ case EM_ALTERA_NIOS2: return "Altera Nios II";
+ case EM_XC16X: return "Infineon Technologies xc16x";
+ case EM_CYGNUS_MEP: return "Toshiba MeP Media Engine";
default:
snprintf (buff, sizeof (buff), _("<unknown>: %x"), e_machine);
return buff;
case EF_ARM_EABI_VER4:
strcat (buf, ", Version4 EABI");
+ goto eabi;
+
+ case EF_ARM_EABI_VER5:
+ strcat (buf, ", Version5 EABI");
+ eabi:
while (e_flags)
{
unsigned flag;
break;
case EM_68K:
- if (e_flags & EF_CPU32)
- strcat (buf, ", cpu32");
- if (e_flags & EF_M68000)
+ if ((e_flags & EF_M68K_ARCH_MASK) == EF_M68K_M68000)
strcat (buf, ", m68000");
+ else if ((e_flags & EF_M68K_ARCH_MASK) == EF_M68K_CPU32)
+ strcat (buf, ", cpu32");
+ else if ((e_flags & EF_M68K_ARCH_MASK) == EF_M68K_FIDO)
+ strcat (buf, ", fido_a");
+ else
+ {
+ char const *isa = _("unknown");
+ char const *mac = _("unknown mac");
+ char const *additional = NULL;
+
+ switch (e_flags & EF_M68K_CF_ISA_MASK)
+ {
+ case EF_M68K_CF_ISA_A_NODIV:
+ isa = "A";
+ additional = ", nodiv";
+ break;
+ case EF_M68K_CF_ISA_A:
+ isa = "A";
+ break;
+ case EF_M68K_CF_ISA_A_PLUS:
+ isa = "A+";
+ break;
+ case EF_M68K_CF_ISA_B_NOUSP:
+ isa = "B";
+ additional = ", nousp";
+ break;
+ case EF_M68K_CF_ISA_B:
+ isa = "B";
+ break;
+ }
+ strcat (buf, ", cf, isa ");
+ strcat (buf, isa);
+ if (additional)
+ strcat (buf, additional);
+ if (e_flags & EF_M68K_CF_FLOAT)
+ strcat (buf, ", float");
+ switch (e_flags & EF_M68K_CF_MAC_MASK)
+ {
+ case 0:
+ mac = NULL;
+ break;
+ case EF_M68K_CF_MAC:
+ mac = "mac";
+ break;
+ case EF_M68K_CF_EMAC:
+ mac = "emac";
+ break;
+ }
+ if (mac)
+ {
+ strcat (buf, ", ");
+ strcat (buf, mac);
+ }
+ }
break;
case EM_PPC:
case EM_CYGNUS_M32R:
if ((e_flags & EF_M32R_ARCH) == E_M32R_ARCH)
strcat (buf, ", m32r");
-
break;
case EM_MIPS:
case EF_SH4_NOFPU: strcat (buf, ", sh4-nofpu"); break;
case EF_SH4A_NOFPU: strcat (buf, ", sh4a-nofpu"); break;
case EF_SH2A_NOFPU: strcat (buf, ", sh2a-nofpu"); break;
+ case EF_SH3_NOMMU: strcat (buf, ", sh3-nommu"); break;
+ case EF_SH4_NOMMU_NOFPU: strcat (buf, ", sh4-nommu-nofpu"); break;
+ case EF_SH2A_SH4_NOFPU: strcat (buf, ", sh2a-nofpu-or-sh4-nommu-nofpu"); break;
+ case EF_SH2A_SH3_NOFPU: strcat (buf, ", sh2a-nofpu-or-sh3-nommu"); break;
+ case EF_SH2A_SH4: strcat (buf, ", sh2a-or-sh4"); break;
+ case EF_SH2A_SH3E: strcat (buf, ", sh2a-or-sh3e"); break;
default: strcat (buf, ", unknown ISA"); break;
}
case SHT_INIT_ARRAY: return "INIT_ARRAY";
case SHT_FINI_ARRAY: return "FINI_ARRAY";
case SHT_PREINIT_ARRAY: return "PREINIT_ARRAY";
+ case SHT_GNU_HASH: return "GNU_HASH";
case SHT_GROUP: return "GROUP";
case SHT_SYMTAB_SHNDX: return "SYMTAB SECTION INDICIES";
case SHT_GNU_verdef: return "VERDEF";
};
static void
-usage (void)
+usage (FILE *stream)
{
- fprintf (stdout, _("Usage: readelf <option(s)> elf-file(s)\n"));
- fprintf (stdout, _(" Display information about the contents of ELF format files\n"));
- fprintf (stdout, _(" Options are:\n\
+ fprintf (stream, _("Usage: readelf <option(s)> elf-file(s)\n"));
+ fprintf (stream, _(" Display information about the contents of ELF format files\n"));
+ fprintf (stream, _(" Options are:\n\
-a --all Equivalent to: -h -l -S -s -r -d -V -A -I\n\
-h --file-header Display the ELF file header\n\
-l --program-headers Display the program headers\n\
--debug-dump[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str,=loc,=Ranges]\n\
Display the contents of DWARF2 debug sections\n"));
#ifdef SUPPORT_DISASSEMBLY
- fprintf (stdout, _("\
+ fprintf (stream, _("\
-i --instruction-dump=<number>\n\
Disassemble the contents of section <number>\n"));
#endif
- fprintf (stdout, _("\
+ fprintf (stream, _("\
-I --histogram Display histogram of bucket list lengths\n\
-W --wide Allow output width to exceed 80 characters\n\
+ @<file> Read options from <file>\n\
-H --help Display this information\n\
-v --version Display the version number of readelf\n"));
- fprintf (stdout, _("Report bugs to %s\n"), REPORT_BUGS_TO);
+
+ if (REPORT_BUGS_TO[0] && stream == stdout)
+ fprintf (stdout, _("Report bugs to %s\n"), REPORT_BUGS_TO);
- exit (0);
+ exit (stream == stdout ? 0 : 1);
}
/* Record the fact that the user wants the contents of section number
return;
}
+/* Request a dump by section name. */
+
+static void
+request_dump_byname (const char *section, int type)
+{
+ struct dump_list_entry *new_request;
+
+ new_request = malloc (sizeof (struct dump_list_entry));
+ if (!new_request)
+ error (_("Out of memory allocating dump request table."));
+
+ new_request->name = strdup (section);
+ if (!new_request->name)
+ error (_("Out of memory allocating dump request table."));
+
+ new_request->type = type;
+
+ new_request->next = dump_sects_byname;
+ dump_sects_byname = new_request;
+}
+
static void
parse_args (int argc, char **argv)
{
int c;
if (argc < 2)
- usage ();
+ usage (stderr);
while ((c = getopt_long
(argc, argv, "ersuahnldSDAINtgw::x:i:vVWH", options, NULL)) != EOF)
/* Long options. */
break;
case 'H':
- usage ();
+ usage (stdout);
break;
case 'a':
do_dump++;
section = strtoul (optarg, & cp, 0);
if (! *cp && section >= 0)
- {
- request_dump (section, HEX_DUMP);
- break;
- }
- goto oops;
+ request_dump (section, HEX_DUMP);
+ else
+ request_dump_byname (optarg, HEX_DUMP);
+ break;
case 'w':
do_dump++;
if (optarg == 0)
do_wide++;
break;
default:
+#ifdef SUPPORT_DISASSEMBLY
oops:
+#endif
/* xgettext:c-format */
error (_("Invalid option '-%c'\n"), c);
/* Drop through. */
case '?':
- usage ();
+ usage (stderr);
}
}
&& !do_segments && !do_header && !do_dump && !do_version
&& !do_histogram && !do_debugging && !do_arch && !do_notes
&& !do_section_groups)
- usage ();
+ usage (stderr);
else if (argc < 3)
{
warn (_("Nothing to do.\n"));
- usage ();
+ usage (stderr);
}
}
(long) elf_header.e_shstrndx);
if (section_headers != NULL && elf_header.e_shstrndx == SHN_XINDEX)
printf (" (%ld)", (long) section_headers[0].sh_link);
+ else if (elf_header.e_shstrndx != SHN_UNDEF
+ && (elf_header.e_shstrndx >= elf_header.e_shnum
+ || (elf_header.e_shstrndx >= SHN_LORESERVE
+ && elf_header.e_shstrndx <= SHN_HIRESERVE)))
+ printf (" <corrupt: out of range>");
putc ('\n', stdout);
}
elf_header.e_shnum = section_headers[0].sh_size;
if (elf_header.e_shstrndx == SHN_XINDEX)
elf_header.e_shstrndx = section_headers[0].sh_link;
+ else if (elf_header.e_shstrndx != SHN_UNDEF
+ && (elf_header.e_shstrndx >= elf_header.e_shnum
+ || (elf_header.e_shstrndx >= SHN_LORESERVE
+ && elf_header.e_shstrndx <= SHN_HIRESERVE)))
+ elf_header.e_shstrndx = SHN_UNDEF;
free (section_headers);
section_headers = NULL;
}
error (_("Unable to find program interpreter name\n"));
else
{
+ char fmt [32];
+ int ret = snprintf (fmt, sizeof (fmt), "%%%ds", PATH_MAX);
+
+ if (ret >= (int) sizeof (fmt) || ret < 0)
+ error (_("Internal error: failed to create format string to display program interpreter"));
+
program_interpreter[0] = 0;
- fscanf (file, "%63s", program_interpreter);
+ if (fscanf (file, fmt, program_interpreter) <= 0)
+ error (_("Unable to read program interpreter name\n"));
if (do_segments)
printf (_("\n [Requesting program interpreter: %s]"),
for (j = 1; j < elf_header.e_shnum; j++, section++)
{
- if (section->sh_size > 0
- /* Compare allocated sections by VMA, unallocated
- sections by file offset. */
- && (section->sh_flags & SHF_ALLOC
- ? (section->sh_addr >= segment->p_vaddr
- && section->sh_addr + section->sh_size
- <= segment->p_vaddr + segment->p_memsz)
- : ((bfd_vma) section->sh_offset >= segment->p_offset
- && (section->sh_offset + section->sh_size
- <= segment->p_offset + segment->p_filesz)))
- /* .tbss is special. It doesn't contribute memory space
- to normal segments. */
- && (!((section->sh_flags & SHF_TLS) != 0
- && section->sh_type == SHT_NOBITS)
- || segment->p_type == PT_TLS))
+ if (ELF_IS_SECTION_IN_SEGMENT_MEMORY(section, segment))
printf ("%s ", SECTION_NAME (section));
}
return 0;
/* Read in the string table, so that we have names to display. */
- if (SECTION_HEADER_INDEX (elf_header.e_shstrndx) < elf_header.e_shnum)
+ if (elf_header.e_shstrndx != SHN_UNDEF
+ && SECTION_HEADER_INDEX (elf_header.e_shstrndx) < elf_header.e_shnum)
{
section = SECTION_HEADER (elf_header.e_shstrndx);
&& find_section (".gcc_compiled_long32") == NULL)
eh_addr_size = 8;
break;
+
+ case EM_H8_300:
+ case EM_H8_300H:
+ switch (elf_header.e_flags & EF_H8_MACH)
+ {
+ case E_H8_MACH_H8300:
+ case E_H8_MACH_H8300HN:
+ case E_H8_MACH_H8300SN:
+ case E_H8_MACH_H8300SXN:
+ eh_addr_size = 2;
+ break;
+ case E_H8_MACH_H8300H:
+ case E_H8_MACH_H8300S:
+ case E_H8_MACH_H8300SX:
+ eh_addr_size = 4;
+ break;
+ }
}
#define CHECK_ENTSIZE_VALUES(section, i, size32, size64) \
|| do_debug_lines || do_debug_pubnames || do_debug_aranges
|| do_debug_frames || do_debug_macinfo || do_debug_str
|| do_debug_loc || do_debug_ranges)
- && strneq (name, ".debug_", 7))
+ && const_strneq (name, ".debug_"))
{
name += 7;
}
/* linkonce section to be combined with .debug_info at link time. */
else if ((do_debugging || do_debug_info)
- && strneq (name, ".gnu.linkonce.wi.", 17))
+ && const_strneq (name, ".gnu.linkonce.wi."))
request_dump (i, DEBUG_DUMP);
else if (do_debug_frames && streq (name, ".eh_frame"))
request_dump (i, DEBUG_DUMP);
bfd_vma offset;
};
+#define ABSADDR(a) \
+ ((a).section \
+ ? section_headers [(a).section].sh_addr + (a).offset \
+ : (a).offset)
+
struct ia64_unw_aux_info
{
struct ia64_unw_table_entry
printf ("], info at +0x%lx\n",
(unsigned long) (tp->info.offset - aux->seg_base));
- head = aux->info + (tp->info.offset - aux->info_addr);
+ head = aux->info + (ABSADDR (tp->info) - aux->info_addr);
stamp = byte_get ((unsigned char *) head, sizeof (stamp));
printf (" v%u, flags=0x%lx (%s%s), len=%lu bytes\n",
sym = aux->symtab + ELF64_R_SYM (rp->r_info);
}
- if (! strneq (relname, "R_IA64_SEGREL", 13))
+ if (! const_strneq (relname, "R_IA64_SEGREL"))
{
warn (_("Skipping unexpected relocation type %s\n"), relname);
continue;
}
/* R_PARISC_SEGREL32 or R_PARISC_SEGREL64. */
- if (strncmp (relname, "R_PARISC_SEGREL", 15) != 0)
+ if (! const_strneq (relname, "R_PARISC_SEGREL"))
{
warn (_("Skipping unexpected relocation type %s\n"), relname);
continue;
}
break;
+ case DT_GNU_HASH:
+ dynamic_info_DT_GNU_HASH = entry->d_un.d_val;
+ if (do_dynamic)
+ {
+ print_vma (entry->d_un.d_val, PREFIX_HEX);
+ putchar ('\n');
+ }
+ break;
+
default:
if ((entry->d_tag >= DT_VERSYM) && (entry->d_tag <= DT_VERNEEDNUM))
version_info[DT_VERSIONTAGIDX (entry->d_tag)] =
case STT_FILE: return "FILE";
case STT_COMMON: return "COMMON";
case STT_TLS: return "TLS";
+ case STT_RELC: return "RELC";
+ case STT_SRELC: return "SRELC";
default:
if (type >= STT_LOPROC && type <= STT_HIPROC)
{
}
}
+static const char *
+get_mips_symbol_other (unsigned int other)
+{
+ switch (other)
+ {
+ case STO_OPTIONAL: return "OPTIONAL";
+ case STO_MIPS16: return "MIPS16";
+ default: return NULL;
+ }
+}
+
+static const char *
+get_symbol_other (unsigned int other)
+{
+ const char * result = NULL;
+ static char buff [32];
+
+ if (other == 0)
+ return "";
+
+ switch (elf_header.e_machine)
+ {
+ case EM_MIPS:
+ result = get_mips_symbol_other (other);
+ default:
+ break;
+ }
+
+ if (result)
+ return result;
+
+ snprintf (buff, sizeof buff, _("<other>: %x"), other);
+ return buff;
+}
+
static const char *
get_symbol_index_type (unsigned int type)
{
else if (elf_header.e_machine == EM_X86_64
&& type == SHN_X86_64_LCOMMON)
return "LARGE_COM";
+ else if (type == SHN_MIPS_SCOMMON
+ && elf_header.e_machine == EM_MIPS)
+ return "SCOM";
+ else if (type == SHN_MIPS_SUNDEFINED
+ && elf_header.e_machine == EM_MIPS)
+ return "SUND";
else if (type >= SHN_LOPROC && type <= SHN_HIPROC)
sprintf (buff, "PRC[0x%04x]", type);
else if (type >= SHN_LOOS && type <= SHN_HIOS)
bfd_vma nchains = 0;
bfd_vma *buckets = NULL;
bfd_vma *chains = NULL;
+ bfd_vma ngnubuckets = 0;
+ bfd_vma *gnubuckets = NULL;
+ bfd_vma *gnuchains = NULL;
if (! do_syms && !do_histogram)
return 1;
printf (" %6s", get_symbol_type (ELF_ST_TYPE (psym->st_info)));
printf (" %6s", get_symbol_binding (ELF_ST_BIND (psym->st_info)));
printf (" %3s", get_symbol_visibility (ELF_ST_VISIBILITY (psym->st_other)));
+ /* Check to see if any other bits in the st_other field are set.
+ Note - displaying this information disrupts the layout of the
+ table being generated, but for the moment this case is very rare. */
+ if (psym->st_other ^ ELF_ST_VISIBILITY (psym->st_other))
+ printf (" [%s] ", get_symbol_other (psym->st_other ^ ELF_ST_VISIBILITY (psym->st_other)));
printf (" %3.3s ", get_symbol_index_type (psym->st_shndx));
if (VALID_DYNAMIC_NAME (psym->st_name))
print_symbol (25, GET_DYNAMIC_NAME (psym->st_name));
printf (" %-7s", get_symbol_type (ELF_ST_TYPE (psym->st_info)));
printf (" %-6s", get_symbol_binding (ELF_ST_BIND (psym->st_info)));
printf (" %-3s", get_symbol_visibility (ELF_ST_VISIBILITY (psym->st_other)));
+ /* Check to see if any other bits in the st_other field are set.
+ Note - displaying this information disrupts the layout of the
+ table being generated, but for the moment this case is very rare. */
+ if (psym->st_other ^ ELF_ST_VISIBILITY (psym->st_other))
+ printf (" [%s] ", get_symbol_other (psym->st_other ^ ELF_ST_VISIBILITY (psym->st_other)));
printf (" %4s ", get_symbol_index_type (psym->st_shndx));
print_symbol (25, psym->st_name < strtab_size
? strtab + psym->st_name : "<corrupt>");
free (chains);
}
+ if (do_histogram && dynamic_info_DT_GNU_HASH)
+ {
+ unsigned char nb[16];
+ bfd_vma i, maxchain = 0xffffffff, symidx, bitmaskwords;
+ unsigned long *lengths;
+ unsigned long *counts;
+ unsigned long hn;
+ unsigned long maxlength = 0;
+ unsigned long nzero_counts = 0;
+ unsigned long nsyms = 0;
+ bfd_vma buckets_vma;
+
+ if (fseek (file,
+ (archive_file_offset
+ + offset_from_vma (file, dynamic_info_DT_GNU_HASH,
+ sizeof nb)),
+ SEEK_SET))
+ {
+ error (_("Unable to seek to start of dynamic information"));
+ return 0;
+ }
+
+ if (fread (nb, 16, 1, file) != 1)
+ {
+ error (_("Failed to read in number of buckets\n"));
+ return 0;
+ }
+
+ ngnubuckets = byte_get (nb, 4);
+ symidx = byte_get (nb + 4, 4);
+ bitmaskwords = byte_get (nb + 8, 4);
+ buckets_vma = dynamic_info_DT_GNU_HASH + 16;
+ if (is_32bit_elf)
+ buckets_vma += bitmaskwords * 4;
+ else
+ buckets_vma += bitmaskwords * 8;
+
+ if (fseek (file,
+ (archive_file_offset
+ + offset_from_vma (file, buckets_vma, 4)),
+ SEEK_SET))
+ {
+ error (_("Unable to seek to start of dynamic information"));
+ return 0;
+ }
+
+ gnubuckets = get_dynamic_data (file, ngnubuckets, 4);
+
+ if (gnubuckets == NULL)
+ return 0;
+
+ for (i = 0; i < ngnubuckets; i++)
+ if (gnubuckets[i] != 0)
+ {
+ if (gnubuckets[i] < symidx)
+ return 0;
+
+ if (maxchain == 0xffffffff || gnubuckets[i] > maxchain)
+ maxchain = gnubuckets[i];
+ }
+
+ if (maxchain == 0xffffffff)
+ return 0;
+
+ maxchain -= symidx;
+
+ if (fseek (file,
+ (archive_file_offset
+ + offset_from_vma (file, buckets_vma
+ + 4 * (ngnubuckets + maxchain), 4)),
+ SEEK_SET))
+ {
+ error (_("Unable to seek to start of dynamic information"));
+ return 0;
+ }
+
+ do
+ {
+ if (fread (nb, 4, 1, file) != 1)
+ {
+ error (_("Failed to determine last chain length\n"));
+ return 0;
+ }
+
+ if (maxchain + 1 == 0)
+ return 0;
+
+ ++maxchain;
+ }
+ while ((byte_get (nb, 4) & 1) == 0);
+
+ if (fseek (file,
+ (archive_file_offset
+ + offset_from_vma (file, buckets_vma + 4 * ngnubuckets, 4)),
+ SEEK_SET))
+ {
+ error (_("Unable to seek to start of dynamic information"));
+ return 0;
+ }
+
+ gnuchains = get_dynamic_data (file, maxchain, 4);
+
+ if (gnuchains == NULL)
+ return 0;
+
+ lengths = calloc (ngnubuckets, sizeof (*lengths));
+ if (lengths == NULL)
+ {
+ error (_("Out of memory"));
+ return 0;
+ }
+
+ printf (_("\nHistogram for `.gnu.hash' bucket list length (total of %lu buckets):\n"),
+ (unsigned long) ngnubuckets);
+ printf (_(" Length Number %% of total Coverage\n"));
+
+ for (hn = 0; hn < ngnubuckets; ++hn)
+ if (gnubuckets[hn] != 0)
+ {
+ bfd_vma off, length = 1;
+
+ for (off = gnubuckets[hn] - symidx;
+ (gnuchains[off] & 1) == 0; ++off)
+ ++length;
+ lengths[hn] = length;
+ if (length > maxlength)
+ maxlength = length;
+ nsyms += length;
+ }
+
+ counts = calloc (maxlength + 1, sizeof (*counts));
+ if (counts == NULL)
+ {
+ error (_("Out of memory"));
+ return 0;
+ }
+
+ for (hn = 0; hn < ngnubuckets; ++hn)
+ ++counts[lengths[hn]];
+
+ if (ngnubuckets > 0)
+ {
+ unsigned long j;
+ printf (" 0 %-10lu (%5.1f%%)\n",
+ counts[0], (counts[0] * 100.0) / ngnubuckets);
+ for (j = 1; j <= maxlength; ++j)
+ {
+ nzero_counts += counts[j] * j;
+ printf ("%7lu %-10lu (%5.1f%%) %5.1f%%\n",
+ j, counts[j], (counts[j] * 100.0) / ngnubuckets,
+ (nzero_counts * 100.0) / nsyms);
+ }
+ }
+
+ free (counts);
+ free (lengths);
+ free (gnubuckets);
+ free (gnuchains);
+ }
+
return 1;
}
static int
dump_section (Elf_Internal_Shdr *section, FILE *file)
{
+ Elf_Internal_Shdr *relsec;
bfd_size_type bytes;
bfd_vma addr;
unsigned char *data;
if (!start)
return 0;
+ /* If the section being dumped has relocations against it the user might
+ be expecting these relocations to have been applied. Check for this
+ case and issue a warning message in order to avoid confusion.
+ FIXME: Maybe we ought to have an option that dumps a section with
+ relocs applied ? */
+ for (relsec = section_headers;
+ relsec < section_headers + elf_header.e_shnum;
+ ++relsec)
+ {
+ if (relsec->sh_type != SHT_RELA
+ || SECTION_HEADER_INDEX (relsec->sh_info) >= elf_header.e_shnum
+ || SECTION_HEADER (relsec->sh_info) != section
+ || relsec->sh_size == 0
+ || SECTION_HEADER_INDEX (relsec->sh_link) >= elf_header.e_shnum)
+ continue;
+
+ printf (_(" NOTE: This section has relocations against it, but these have NOT been applied to this dump.\n"));
+ break;
+ }
+
data = start;
while (bytes)
printf (" 0x%8.8lx ", (unsigned long) addr);
- switch (elf_header.e_ident[EI_DATA])
+ for (j = 0; j < 16; j++)
{
- default:
- case ELFDATA2LSB:
- for (j = 15; j >= 0; j --)
- {
- if (j < lbytes)
- printf ("%2.2x", data[j]);
- else
- printf (" ");
-
- if (!(j & 0x3))
- printf (" ");
- }
- break;
-
- case ELFDATA2MSB:
- for (j = 0; j < 16; j++)
- {
- if (j < lbytes)
- printf ("%2.2x", data[j]);
- else
- printf (" ");
+ if (j < lbytes)
+ printf ("%2.2x", data[j]);
+ else
+ printf (" ");
- if ((j & 3) == 3)
- printf (" ");
- }
- break;
+ if ((j & 3) == 3)
+ printf (" ");
}
for (j = 0; j < lbytes; j++)
free (start);
+ putchar ('\n');
return 1;
}
+/* Return the number of bytes affected by a given reloc.
+ This information is architecture and reloc dependent.
+ Returns 4 by default, although this is not always correct.
+ It should return 0 if a decision cannot be made.
+ FIXME: This is not the correct way to solve this problem.
+ The proper way is to have target specific reloc sizing functions
+ created by the reloc-macros.h header, in the same way that it
+ already creates the reloc naming functions. */
+
+static unsigned int
+get_reloc_size (Elf_Internal_Rela * reloc)
+{
+ switch (elf_header.e_machine)
+ {
+ case EM_H8S:
+ case EM_H8_300:
+ case EM_H8_300H:
+ case EM_H8_500:
+ switch (ELF32_R_TYPE (reloc->r_info))
+ {
+ /* PR gas/3800 - without this information we do not correctly
+ decode the debug information generated by the h8300 assembler. */
+ case R_H8_DIR16:
+ return 2;
+ default:
+ return 4;
+ }
+ default:
+ /* FIXME: We need to extend this switch statement to cope with other
+ architecture's relocs. (When those relocs are used against debug
+ sections, and when their size is not 4). But see the multiple
+ inclusions of <elf/h8.h> for an example of the hoops that we need
+ to jump through in order to obtain the reloc numbers. */
+ return 4;
+ }
+}
+
/* Apply addends of RELA relocations. */
static int
{
Elf_Internal_Shdr *relsec;
unsigned char *end = start + section->sh_size;
- /* FIXME: The relocation field size is relocation type dependent. */
- unsigned int reloc_size = 4;
if (!is_relocatable)
return 1;
- if (section->sh_size < reloc_size)
- return 1;
-
for (relsec = section_headers;
relsec < section_headers + elf_header.e_shnum;
++relsec)
for (rp = rela; rp < rela + nrelas; ++rp)
{
unsigned char *loc;
+ unsigned int reloc_size;
+
+ reloc_size = get_reloc_size (rp);
+ if (reloc_size == 0)
+ {
+ warn (_("skipping relocation of unknown size against offset 0x%lx in section %s\n"),
+ (unsigned long) rp->r_offset,
+ SECTION_NAME (section));
+ continue;
+ }
loc = start + rp->r_offset;
if ((loc + reloc_size) > end)
if (ELF32_R_SYM (rp->r_info) != 0
&& ELF32_ST_TYPE (sym->st_info) != STT_SECTION
+ /* Relocations against symbols without type can happen.
+ Gcc -feliminate-dwarf2-dups may generate symbols
+ without type for debug info. */
+ && ELF32_ST_TYPE (sym->st_info) != STT_NOTYPE
/* Relocations against object symbols can happen,
eg when referencing a global array. For an
example of this see the _clz.o binary in libgcc.a. */
if (ELF64_R_SYM (rp->r_info) != 0
&& ELF64_ST_TYPE (sym->st_info) != STT_SECTION
+ && ELF64_ST_TYPE (sym->st_info) != STT_NOTYPE
&& ELF64_ST_TYPE (sym->st_info) != STT_OBJECT)
{
warn (_("skipping unexpected symbol type %s in relocation in section .rela.%s\n"),
return 0;
}
- if (strneq (name, ".gnu.linkonce.wi.", 17))
+ if (const_strneq (name, ".gnu.linkonce.wi."))
name = ".debug_info";
/* See if we know how to display the contents of this section. */
return result;
}
+/* Set DUMP_SECTS for all sections where dumps were requested
+ based on section name. */
+
+static void
+initialise_dumps_byname (void)
+{
+ struct dump_list_entry *cur;
+
+ for (cur = dump_sects_byname; cur; cur = cur->next)
+ {
+ unsigned int i;
+ int any;
+
+ for (i = 0, any = 0; i < elf_header.e_shnum; i++)
+ if (streq (SECTION_NAME (section_headers + i), cur->name))
+ {
+ request_dump (i, cur->type);
+ any = 1;
+ }
+
+ if (!any)
+ warn (_("Section '%s' was not dumped because it does not exist!\n"),
+ cur->name);
+ }
+}
+
static void
process_section_contents (FILE *file)
{
if (! do_dump)
return;
+ initialise_dumps_byname ();
+
for (i = 0, section = section_headers;
i < elf_header.e_shnum && i < num_dump_sects;
i++, section++)
static const char *arm_attr_tag_ARM_ISA_use[] = {"No", "Yes"};
static const char *arm_attr_tag_THUMB_ISA_use[] =
{"No", "Thumb-1", "Thumb-2"};
-static const char *arm_attr_tag_VFP_arch[] = {"No", "VFPv1", "VFPv2"};
+/* FIXME: VFPv3 encoding was extrapolated! */
+static const char *arm_attr_tag_VFP_arch[] = {"No", "VFPv1", "VFPv2", "VFPv3"};
static const char *arm_attr_tag_WMMX_arch[] = {"No", "WMMXv1"};
static const char *arm_attr_tag_NEON_arch[] = {"No", "NEONv1"};
static const char *arm_attr_tag_ABI_PCS_config[] =
#define LOOKUP(id, name) \
{id, #name, 0x80 | ARRAY_SIZE(arm_attr_tag_##name), arm_attr_tag_##name}
-static arm_attr_public_tag arm_attr_public_tags[] =
+static arm_attr_public_tag arm_attr_public_tags[] =
{
{4, "CPU_raw_name", 1, NULL},
{5, "CPU_name", 1, NULL},
contents = get_data (NULL, file, sect->sh_offset, 1, sect->sh_size,
_("attributes"));
-
+
if (!contents)
continue;
p = contents;
{
printf (_("Unknown format '%c'\n"), *p);
}
-
+
free(contents);
}
return 1;
note type strings. */
nt = get_note_type (pnote->type);
- else if (strneq (pnote->namedata, "NetBSD-CORE", 11))
+ else if (const_strneq (pnote->namedata, "NetBSD-CORE"))
/* NetBSD-specific core file notes. */
nt = get_netbsd_elfcore_note_type (pnote->type);
assert (num_dump_sects >= num_cmdline_dump_sects);
memcpy (dump_sects, cmdline_dump_sects, num_cmdline_dump_sects);
}
-
+
if (! process_file_header ())
return 1;
return 1;
}
- if (memcmp (arhdr.ar_name, "/ ", 16) == 0)
+ if (const_strneq (arhdr.ar_name, "/ "))
{
/* This is the archive symbol table. Skip it.
FIXME: We should have an option to dump it. */
}
}
- if (memcmp (arhdr.ar_name, "// ", 16) == 0)
+ if (const_strneq (arhdr.ar_name, "// "))
{
/* This is the archive string table holding long member
names. */
archive_file_offset = ftell (file);
archive_file_size = strtoul (arhdr.ar_size, NULL, 10);
-
+
ret |= process_object (namealc, file);
free (namealc);