2255 lines
54 KiB
C
2255 lines
54 KiB
C
/* readelf.c -- display contents of an ELF format file
|
||
Copyright (C) 1998 Free Software Foundation, Inc.
|
||
|
||
Originally developed by Eric Youngdale <eric@andante.jic.com>
|
||
Modifications by Nick Clifton <nickc@cygnus.com>
|
||
|
||
This file is part of GNU Binutils.
|
||
|
||
This program 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 2 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program 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, write to the Free Software
|
||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||
02111-1307, USA. */
|
||
|
||
|
||
#include <assert.h>
|
||
#include <sys/mman.h>
|
||
#include <fcntl.h>
|
||
#include <stdlib.h>
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <sys/stat.h>
|
||
#include <unistd.h>
|
||
#include <fcntl.h>
|
||
#include <sys/mman.h>
|
||
#include <errno.h>
|
||
|
||
#include "readelf.h"
|
||
#include "getopt.h"
|
||
#include "bucomm.h"
|
||
|
||
#ifdef ANSI_PROTOTYPES
|
||
#include <stdarg.h>
|
||
#else
|
||
#include <varargs.h>
|
||
#endif
|
||
|
||
unsigned int dynamic_addr;
|
||
unsigned int dynamic_size;
|
||
char * pint = "";
|
||
char * program_name = "readelf";
|
||
|
||
int dynamic_info [DT_JMPREL + 1];
|
||
int version_info [16];
|
||
|
||
int must_swap = 0;
|
||
|
||
unsigned int rel_size;
|
||
int loadaddr = -1;
|
||
|
||
unsigned int rela_addr;
|
||
unsigned int rela_size;
|
||
char * strtab;
|
||
int symtab_index;
|
||
int lastmapped;
|
||
char * header;
|
||
|
||
Elf_Dyn * dpnt;
|
||
Elf_Rel * rpnt;
|
||
Elf_Shdr * elf_sections;
|
||
Elf_Ehdr * epnt;
|
||
Elf_Sym * symtab;
|
||
|
||
int show_name;
|
||
int do_dynamic;
|
||
int do_syms;
|
||
int do_reloc;
|
||
int do_section;
|
||
int do_load;
|
||
int do_using_dynamic;
|
||
int do_header;
|
||
int do_dump;
|
||
int do_version;
|
||
long int expected_endian;
|
||
|
||
char * dyntype[] =
|
||
{
|
||
"NULL", "NEEDED","PLTRELSZ","PLTGOT","HASH","STRTAB","SYMTAB","RELA",
|
||
"RELASZ","RELAENT","STRSZ","SYMENT","INIT","FINI","SONAME","RPATH",
|
||
"SYMBOLIC","REL","RELSZ","RELENT","PLTREL","DEBUG","TEXTREL","JMPREL"
|
||
};
|
||
|
||
char * vertype[] =
|
||
{
|
||
"VERNEEDNUM", "VERNEED", "VERDEFNUM", "VERDEF",
|
||
"", "", "", "", "", "", "", "", "", "", "", "VERSYM"
|
||
};
|
||
|
||
char * filtertype[] =
|
||
{
|
||
"FILTER", "USED", "AUXILIARY"
|
||
};
|
||
|
||
|
||
char * sttinfo[] = {"NOTYPE","OBJECT","FUNC","SECTION","FILE"};
|
||
char * stbinfo[] = {"LOCAL","GLOBAL","WEAK"};
|
||
|
||
#define SECTION_NAME(X) (& header [lastmapped + (X)->sh_name])
|
||
|
||
#define NUM_DUMP_SECTS 100
|
||
char dump_sects [NUM_DUMP_SECTS];
|
||
#define HEX_DUMP 1
|
||
#define DISASS_DUMP 2
|
||
|
||
/* Forward declarations for dumb compilers. */
|
||
static char * get_i386_rel_type PARAMS ((bfd_vma rtype));
|
||
static char * get_m68k_rel_type PARAMS ((bfd_vma rtype));
|
||
static char * get_sparc_rel_type PARAMS ((bfd_vma rtype));
|
||
static char * get_m32r_rel_type PARAMS ((bfd_vma rtype));
|
||
static char * get_v850_rel_type PARAMS ((bfd_vma rtype));
|
||
static char * get_d10v_rel_type PARAMS ((bfd_vma rtype));
|
||
static char * get_d30v_rel_type PARAMS ((bfd_vma rtype));
|
||
static char * get_sh_rel_type PARAMS ((bfd_vma rtype));
|
||
static char * get_mn10300_rel_type PARAMS ((bfd_vma rtype));
|
||
static char * get_mn10200_rel_type PARAMS ((bfd_vma rtype));
|
||
static void dump_relocations PARAMS ((Elf_Rel * rpnt, int rel_size));
|
||
static char * get_file_type PARAMS ((unsigned short e_type));
|
||
static char * get_machine_name PARAMS ((unsigned short e_machine));
|
||
static char * get_segment_type PARAMS ((unsigned long p_type));
|
||
static char * get_section_type_name PARAMS ((unsigned int sh_type));
|
||
static void usage PARAMS ((void));
|
||
static void parse_args PARAMS ((int argc, char ** argv));
|
||
static int process_elf_header PARAMS ((void));
|
||
static void process_program_headers PARAMS ((void));
|
||
static void process_section_headers PARAMS ((void));
|
||
static void process_dynamic_segment PARAMS ((void));
|
||
static void process_symbol_table PARAMS ((void));
|
||
static void process_section_contents PARAMS ((void));
|
||
static void process_file PARAMS ((char * file_name));
|
||
|
||
|
||
#define SWAP2(val) ( (((val) << 8) & (0xff << 8)) \
|
||
| (((val) >> 8) & (0xff << 0)))
|
||
|
||
#define SWAP4(val) ( (((val) << 24) & (0xff << 24)) \
|
||
| (((val) << 8) & (0xff << 16)) \
|
||
| (((val) >> 8) & (0xff << 8)) \
|
||
| (((val) >> 24) & (0xff << 0)))
|
||
|
||
/* Warning: This macro assumes 8 bits in a char. */
|
||
#define BYTE_SWAP(pointer, field) \
|
||
if (sizeof ((pointer)->field) == 2) \
|
||
{ \
|
||
unsigned short val = (pointer)->field ; \
|
||
new_header->field = SWAP2 (val); \
|
||
} \
|
||
else if (sizeof ((pointer)->field) != 4) \
|
||
abort (); \
|
||
else \
|
||
{ \
|
||
unsigned long val = (pointer)->field ; \
|
||
new_header->field = SWAP4 (val); \
|
||
}
|
||
|
||
|
||
#ifdef ANSI_PROTOTYPES
|
||
static void
|
||
error (const char * message, ...)
|
||
{
|
||
va_list args;
|
||
|
||
fprintf (stderr, _("%s: Error: "), program_name);
|
||
va_start (args, message);
|
||
vfprintf (stderr, message, args);
|
||
va_end (args);
|
||
return;
|
||
}
|
||
|
||
static void
|
||
warn (const char * message, ...)
|
||
{
|
||
va_list args;
|
||
|
||
fprintf (stderr, _("%s: Warning: "), program_name);
|
||
va_start (args, message);
|
||
vfprintf (stderr, message, args);
|
||
va_end (args);
|
||
return;
|
||
}
|
||
#else
|
||
static void
|
||
error (va_alist)
|
||
{
|
||
char * message;
|
||
va_list args;
|
||
|
||
fprintf (stderr, _("%s: Error: "), program_name);
|
||
va_start (args);
|
||
message = va_arg (args, char *);
|
||
vfprintf (stderr, message, args);
|
||
va_end (args);
|
||
return;
|
||
}
|
||
|
||
static void
|
||
warn (va_alist)
|
||
va_dcl;
|
||
{
|
||
char * message;
|
||
va_list args;
|
||
|
||
fprintf (stderr, _("%s: Warning: "), program_name);
|
||
va_start (args);
|
||
message = va_arg (args, char *);
|
||
vfprintf (stderr, message, args);
|
||
va_end (args);
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
|
||
static char *
|
||
get_i386_rel_type (rtype)
|
||
bfd_vma rtype;
|
||
{
|
||
switch (rtype)
|
||
{
|
||
case 0: return "R_386_NONE";
|
||
case 1: return "R_386_32";
|
||
case 2: return "R_386_PC32";
|
||
case 3: return "R_386_GOT32";
|
||
case 4: return "R_386_PLT32";
|
||
case 5: return "R_386_COPY";
|
||
case 6: return "R_386_GLOB_DAT";
|
||
case 7: return "R_386_JMP_SLOT";
|
||
case 8: return "R_386_RELATIVE";
|
||
case 9: return "R_386_GOTOFF";
|
||
case 10: return "R_386_GOTPC";
|
||
case 20: return "R_386_16";
|
||
case 21: return "R_386_PC16";
|
||
case 22: return "R_386_PC8";
|
||
case 23: return "R_386_max";
|
||
default: return _("*INVALID*");
|
||
}
|
||
}
|
||
|
||
static char *
|
||
get_m68k_rel_type (rtype)
|
||
bfd_vma rtype;
|
||
{
|
||
switch (rtype)
|
||
{
|
||
case 0: return "R_68K_NONE";
|
||
case 1: return "R_68K_32";
|
||
case 2: return "R_68K_16";
|
||
case 3: return "R_68K_8";
|
||
case 4: return "R_68K_PC32";
|
||
case 5: return "R_68K_PC16";
|
||
case 6: return "R_68K_PC8";
|
||
case 7: return "R_68K_GOT32";
|
||
case 8: return "R_68K_GOT16";
|
||
case 9: return "R_68K_GOT8";
|
||
case 10: return "R_68K_GOT32O";
|
||
case 11: return "R_68K_GOT16O";
|
||
case 12: return "R_68K_GOT8O";
|
||
case 13: return "R_68K_PLT32";
|
||
case 14: return "R_68K_PLT16";
|
||
case 15: return "R_68K_PLT8";
|
||
case 16: return "R_68K_PLT32O";
|
||
case 17: return "R_68K_PLT16O";
|
||
case 18: return "R_68K_PLT8O";
|
||
case 19: return "R_68K_COPY";
|
||
case 20: return "R_68K_GLOB_DAT";
|
||
case 21: return "R_68K_JMP_SLOT";
|
||
case 22: return "R_68K_RELATIVE";
|
||
default: return _("*INVALID*");
|
||
}
|
||
}
|
||
|
||
|
||
static char *
|
||
get_sparc_rel_type (rtype)
|
||
bfd_vma rtype;
|
||
{
|
||
switch (rtype)
|
||
{
|
||
case 0: return "R_SPARC_NONE";
|
||
case 1: return "R_SPARC_8";
|
||
case 2: return "R_SPARC_16";
|
||
case 3: return "R_SPARC_32";
|
||
case 4: return "R_SPARC_DISP8";
|
||
case 5: return "R_SPARC_DISP16";
|
||
case 6: return "R_SPARC_DISP32";
|
||
case 7: return "R_SPARC_WDISP30";
|
||
case 8: return "R_SPARC_WDISP22";
|
||
case 9: return "R_SPARC_HI22";
|
||
case 10: return "R_SPARC_22";
|
||
case 11: return "R_SPARC_13";
|
||
case 12: return "R_SPARC_LO10";
|
||
case 13: return "R_SPARC_GOT10";
|
||
case 14: return "R_SPARC_GOT13";
|
||
case 15: return "R_SPARC_GOT22";
|
||
case 16: return "R_SPARC_PC10";
|
||
case 17: return "R_SPARC_PC22";
|
||
case 18: return "R_SPARC_WPLT30";
|
||
case 19: return "R_SPARC_COPY";
|
||
case 20: return "R_SPARC_GLOB_DAT";
|
||
case 21: return "R_SPARC_JMP_SLOT";
|
||
case 22: return "R_SPARC_RELATIVE";
|
||
case 23: return "R_SPARC_UA32";
|
||
case 24: return "R_SPARC_10";
|
||
case 25: return "R_SPARC_11";
|
||
case 26: return "R_SPARC_64";
|
||
case 27: return "R_SPARC_OLO10";
|
||
case 28: return "R_SPARC_HH22";
|
||
case 29: return "R_SPARC_HM10";
|
||
case 30: return "R_SPARC_LM22";
|
||
case 31: return "R_SPARC_PC_HH22";
|
||
case 32: return "R_SPARC_PC_HM10";
|
||
case 33: return "R_SPARC_PC_LM22";
|
||
case 34: return "R_SPARC_WDISP16";
|
||
case 35: return "R_SPARC_WDISP19";
|
||
case 36: return "R_SPARC_UNUSED_42";
|
||
case 37: return "R_SPARC_7";
|
||
case 38: return "R_SPARC_5";
|
||
case 39: return "R_SPARC_6";
|
||
case 40: return "R_SPARC_DISP64";
|
||
case 41: return "R_SPARC_PLT64";
|
||
case 42: return "R_SPARC_HIX22";
|
||
case 43: return "R_SPARC_LOX10";
|
||
case 44: return "R_SPARC_H44";
|
||
case 45: return "R_SPARC_M44";
|
||
case 46: return "R_SPARC_L44";
|
||
case 47: return "R_SPARC_REGISTER";
|
||
case 48: return "R_SPARC_UA64";
|
||
case 49: return "R_SPARC_UA16";
|
||
case 50: return "R_SPARC_32LE";
|
||
default: return _("*INVALID*");
|
||
}
|
||
}
|
||
|
||
|
||
static char *
|
||
get_m32r_rel_type (rtype)
|
||
bfd_vma rtype;
|
||
{
|
||
switch (rtype)
|
||
{
|
||
case 0: return "R_M32R_NONE";
|
||
case 1: return "R_M32R_16";
|
||
case 2: return "R_M32R_32";
|
||
case 3: return "R_M32R_24";
|
||
case 4: return "R_M32R_10_PCREL";
|
||
case 5: return "R_M32R_18_PCREL";
|
||
case 6: return "R_M32R_26_PCREL";
|
||
case 7: return "R_M32R_HI16_ULO";
|
||
case 8: return "R_M32R_HI16_SLO";
|
||
case 9: return "R_M32R_LO16";
|
||
case 10: return "R_M32R_SDA16";
|
||
default: return _("*INVALID*");
|
||
}
|
||
}
|
||
|
||
|
||
static char *
|
||
get_v850_rel_type (rtype)
|
||
bfd_vma rtype;
|
||
{
|
||
switch (rtype)
|
||
{
|
||
case 0: return "R_V850_NONE";
|
||
case 1: return "R_V850_9_PCREL";
|
||
case 2: return "R_V850_22_PCREL";
|
||
case 3: return "R_V850_HI16_S";
|
||
case 4: return "R_V850_HI16";
|
||
case 5: return "R_V850_LO16";
|
||
case 6: return "R_V850_32";
|
||
case 7: return "R_V850_16";
|
||
case 8: return "R_V850_8";
|
||
case 9: return "R_V850_SDA_16_16_OFFSET";
|
||
case 10: return "R_V850_SDA_15_16_OFFSET";
|
||
case 11: return "R_V850_ZDA_16_16_OFFSET";
|
||
case 12: return "R_V850_ZDA_15_16_OFFSET";
|
||
case 13: return "R_V850_TDA_6_8_OFFSET";
|
||
case 14: return "R_V850_TDA_7_8_OFFSET";
|
||
case 15: return "R_V850_TDA_7_7_OFFSET";
|
||
case 16: return "R_V850_TDA_16_16_OFFSET";
|
||
/* start-sanitize-v850e */
|
||
case 17: return "R_V850_TDA_4_5_OFFSET";
|
||
case 18: return "R_V850_TDA_4_4_OFFSET";
|
||
case 19: return "R_V850_SDA_16_16_SPLIT_OFFSET";
|
||
case 20: return "R_V850_ZDA_16_16_SPLIT_OFFSET";
|
||
case 21: return "R_V850_CALLT_6_7_OFFSET";
|
||
case 22: return "R_V850_CALLT_16_16_OFFSET";
|
||
/* end-sanitize-v850e */
|
||
default: return _("*INVALID*");
|
||
}
|
||
}
|
||
|
||
|
||
static char *
|
||
get_d10v_rel_type (rtype)
|
||
bfd_vma rtype;
|
||
{
|
||
switch (rtype)
|
||
{
|
||
case 0: return "R_D10V_NONE";
|
||
case 1: return "R_D10V_10_PCREL_R";
|
||
case 2: return "R_D10V_10_PCREL_L";
|
||
case 3: return "R_D10V_16";
|
||
case 4: return "R_D10V_18";
|
||
case 5: return "R_D10V_18_PCREL";
|
||
case 6: return "R_D10V_32";
|
||
default: return _("*INVALID*");
|
||
}
|
||
}
|
||
|
||
|
||
static char *
|
||
get_d30v_rel_type (rtype)
|
||
bfd_vma rtype;
|
||
{
|
||
switch (rtype)
|
||
{
|
||
case 0: return "R_D30V_NONE";
|
||
case 1: return "R_D30V_6";
|
||
case 2: return "R_D30V_9_PCREL";
|
||
case 3: return "R_D30V_9_PCREL_R";
|
||
case 4: return "R_D30V_15";
|
||
case 5: return "R_D30V_15_PCREL";
|
||
case 6: return "R_D30V_15_PCREL_R";
|
||
case 7: return "R_D30V_21";
|
||
case 8: return "R_D30V_21_PCREL";
|
||
case 9: return "R_D30V_21_PCREL_R";
|
||
case 10: return "R_D30V_32";
|
||
case 11: return "R_D30V_32_PCREL";
|
||
case 12: return "R_D30V_32_NORMAL";
|
||
default: return _("*INVALID*");
|
||
}
|
||
}
|
||
|
||
|
||
static char *
|
||
get_sh_rel_type (rtype)
|
||
bfd_vma rtype;
|
||
{
|
||
switch (rtype)
|
||
{
|
||
case 0: return "R_SH_NONE";
|
||
case 1: return "R_SH_DIR32";
|
||
case 2: return "R_SH_REL32";
|
||
case 3: return "R_SH_DIR8WPN";
|
||
case 4: return "R_SH_IND12W";
|
||
case 5: return "R_SH_DIR8WPL";
|
||
case 6: return "R_SH_DIR8WPZ";
|
||
case 7: return "R_SH_DIR8BP";
|
||
case 8: return "R_SH_DIR8W";
|
||
case 9: return "R_SH_DIR8L";
|
||
case 25: return "R_SH_SWITCH16";
|
||
case 26: return "R_SH_SWITCH32";
|
||
case 27: return "R_SH_USES";
|
||
case 28: return "R_SH_COUNT";
|
||
case 29: return "R_SH_ALIGN";
|
||
case 30: return "R_SH_CODE";
|
||
case 31: return "R_SH_DATA";
|
||
case 32: return "R_SH_LABEL";
|
||
default: return _("*INVALID*");
|
||
}
|
||
}
|
||
|
||
|
||
static char *
|
||
get_mn10300_rel_type (rtype)
|
||
bfd_vma rtype;
|
||
{
|
||
switch (rtype)
|
||
{
|
||
case 0: return "R_MN10300_NONE";
|
||
case 1: return "R_MN10300_32";
|
||
case 2: return "R_MN10300_16";
|
||
case 3: return "R_MN10300_8";
|
||
case 4: return "R_MN10300_PCREL32";
|
||
case 5: return "R_MN10300_PCREL16";
|
||
case 6: return "R_MN10300_PCREL8";
|
||
default: return _("*INVALID*");
|
||
}
|
||
}
|
||
|
||
|
||
static char *
|
||
get_mn10200_rel_type (rtype)
|
||
bfd_vma rtype;
|
||
{
|
||
switch (rtype)
|
||
{
|
||
case 0: return "R_MN10200_NONE";
|
||
case 1: return "R_MN10200_32";
|
||
case 2: return "R_MN10200_16";
|
||
case 3: return "R_MN10200_8";
|
||
case 4: return "R_MN10200_24";
|
||
case 5: return "R_MN10200_PCREL8";
|
||
case 6: return "R_MN10200_PCREL16";
|
||
case 7: return "R_MN10200_PCREL24";
|
||
default: return _("*INVALID*");
|
||
}
|
||
}
|
||
|
||
|
||
static void
|
||
dump_relocations (rpnt, rel_size)
|
||
Elf_Rel * rpnt;
|
||
int rel_size;
|
||
{
|
||
int i;
|
||
int is_rela;
|
||
Elf_Rela * rapnt;
|
||
Elf_Rela * relocs = NULL;
|
||
|
||
|
||
rapnt = (Elf_Rela *) rpnt;
|
||
|
||
/* Compute number of relocations. */
|
||
switch (epnt->e_machine)
|
||
{
|
||
case EM_386:
|
||
case EM_486:
|
||
case EM_CYGNUS_M32R:
|
||
case EM_CYGNUS_D10V:
|
||
rel_size = rel_size / sizeof (Elf_Rel);
|
||
|
||
if (must_swap)
|
||
{
|
||
Elf_Rel * new_header = malloc (sizeof (* new_header) * rel_size);
|
||
|
||
if (new_header == NULL)
|
||
{
|
||
error (_("out of memory\n"));
|
||
return;
|
||
}
|
||
|
||
memcpy (new_header, rpnt, sizeof (* new_header) * rel_size);
|
||
|
||
rpnt = new_header;
|
||
relocs = (Elf_Rela *) new_header;
|
||
|
||
for (i = 0; i < rel_size; i++)
|
||
{
|
||
BYTE_SWAP (rpnt + i, r_offset);
|
||
BYTE_SWAP (rpnt + i, r_info);
|
||
|
||
new_header ++;
|
||
}
|
||
}
|
||
|
||
is_rela = 0;
|
||
break;
|
||
|
||
case EM_68K:
|
||
case EM_SPARC:
|
||
case EM_CYGNUS_V850:
|
||
case EM_CYGNUS_D30V:
|
||
case EM_CYGNUS_MN10200:
|
||
case EM_CYGNUS_MN10300:
|
||
case EM_SH:
|
||
rel_size = rel_size / sizeof (Elf_Rela);
|
||
|
||
if (must_swap)
|
||
{
|
||
Elf_Rela * new_header = malloc (sizeof (* new_header) * rel_size);
|
||
|
||
if (new_header == NULL)
|
||
{
|
||
error (_("out of memory\n"));
|
||
return;
|
||
}
|
||
|
||
memcpy (new_header, rpnt, sizeof (* new_header) * rel_size);
|
||
|
||
relocs = rapnt = new_header;
|
||
|
||
for (i = rel_size; i--;)
|
||
{
|
||
BYTE_SWAP (new_header, r_offset);
|
||
BYTE_SWAP (new_header, r_info);
|
||
BYTE_SWAP (new_header, r_addend);
|
||
|
||
new_header ++;
|
||
}
|
||
}
|
||
|
||
is_rela = 1;
|
||
break;
|
||
|
||
default:
|
||
warn (_("Don't know about relocations on this machine architecture\n"));
|
||
return;
|
||
}
|
||
|
||
if (is_rela)
|
||
printf (_(" Offset Value Type Symbol's Value Symbol Name Addend\n"));
|
||
else
|
||
printf (_(" Offset Value Type Symbol's Value Symbol Name\n"));
|
||
|
||
for (i = 0; i < rel_size; i++)
|
||
{
|
||
char * rtype;
|
||
|
||
if (is_rela)
|
||
rpnt = (Elf_Rel *) rapnt;
|
||
|
||
printf (" %5.5x %5.5x ", rpnt->r_offset, rpnt->r_info);
|
||
|
||
switch (epnt->e_machine)
|
||
{
|
||
case EM_CYGNUS_M32R:
|
||
rtype = get_m32r_rel_type (ELF32_R_TYPE (rpnt->r_info));
|
||
break;
|
||
|
||
case EM_386:
|
||
case EM_486:
|
||
rtype = get_i386_rel_type (ELF32_R_TYPE (rpnt->r_info));
|
||
break;
|
||
|
||
case EM_68K:
|
||
rtype = get_m68k_rel_type (ELF32_R_TYPE (rpnt->r_info));
|
||
break;
|
||
|
||
case EM_SPARC:
|
||
rtype = get_sparc_rel_type (ELF32_R_TYPE (rapnt->r_info));
|
||
break;
|
||
|
||
case EM_CYGNUS_V850:
|
||
rtype = get_v850_rel_type (ELF32_R_TYPE (rpnt->r_info));
|
||
break;
|
||
|
||
case EM_CYGNUS_D10V:
|
||
rtype = get_d10v_rel_type (ELF32_R_TYPE (rpnt->r_info));
|
||
break;
|
||
|
||
case EM_CYGNUS_D30V:
|
||
rtype = get_d30v_rel_type (ELF32_R_TYPE (rpnt->r_info));
|
||
break;
|
||
|
||
case EM_SH:
|
||
rtype = get_sh_rel_type (ELF32_R_TYPE (rpnt->r_info));
|
||
break;
|
||
|
||
case EM_CYGNUS_MN10300:
|
||
rtype = get_mn10300_rel_type (ELF32_R_TYPE (rpnt->r_info));
|
||
break;
|
||
|
||
case EM_CYGNUS_MN10200:
|
||
rtype = get_mn10200_rel_type (ELF32_R_TYPE (rpnt->r_info));
|
||
break;
|
||
}
|
||
|
||
printf ("%-18s", rtype);
|
||
|
||
symtab_index = ELF32_R_SYM (rpnt->r_info);
|
||
|
||
if (symtab_index)
|
||
{
|
||
Elf_Sym ssym;
|
||
Elf_Sym * psym;
|
||
|
||
psym = symtab + symtab_index;
|
||
|
||
if (must_swap)
|
||
{
|
||
Elf_Sym * new_header = & ssym;
|
||
|
||
ssym = * psym;
|
||
|
||
BYTE_SWAP (psym, st_name);
|
||
BYTE_SWAP (psym, st_value);
|
||
/* BYTE_SWAP (psym, st_size); */
|
||
BYTE_SWAP (psym, st_shndx);
|
||
|
||
psym = new_header;
|
||
}
|
||
|
||
if (psym->st_name == 0)
|
||
printf (" %08x %-15s", psym->st_value,
|
||
SECTION_NAME (elf_sections + psym->st_shndx));
|
||
else
|
||
printf (" %08x %-15s", psym->st_value, strtab + psym->st_name);
|
||
|
||
if (is_rela)
|
||
printf (" + %x", rapnt->r_addend);
|
||
}
|
||
|
||
putchar ('\n');
|
||
rapnt ++;
|
||
rpnt ++;
|
||
}
|
||
|
||
if (relocs != NULL)
|
||
free (relocs);
|
||
}
|
||
|
||
static char *
|
||
get_file_type (e_type)
|
||
unsigned short e_type;
|
||
{
|
||
static char buff [32];
|
||
|
||
switch (e_type)
|
||
{
|
||
case ET_NONE: return _("None");
|
||
case ET_REL: return _("Relocatable file");
|
||
case ET_EXEC: return _("Executable file");
|
||
case ET_DYN: return _("Shared object file");
|
||
case ET_CORE: return _("Core file");
|
||
|
||
default:
|
||
if ((e_type >= ET_LOPROC) && (e_type <= ET_HIPROC))
|
||
sprintf (buff, _("Processor Specific: (%x)"), e_type);
|
||
else
|
||
sprintf (buff, _("<unknown>: %x"), e_type);
|
||
return buff;
|
||
}
|
||
}
|
||
|
||
static char *
|
||
get_machine_name (e_machine)
|
||
unsigned short e_machine;
|
||
{
|
||
static char buff [32];
|
||
|
||
switch (e_machine)
|
||
{
|
||
case EM_NONE: return _("None");
|
||
case EM_M32: return "WE32100";
|
||
case EM_SPARC: return "Sparc";
|
||
case EM_386: return "80386";
|
||
case EM_68K: return "MC68000";
|
||
case EM_88K: return "MC88000";
|
||
case EM_486: return "Intel 80486";
|
||
case EM_860: return "Intel 80860";
|
||
case EM_MIPS: return "MIPS R3000 big-endian";
|
||
case EM_S370: return "Amdahl";
|
||
case EM_MIPS_RS4_BE: return "MIPS R400 big-endian";
|
||
case EM_PARISC: return "HPPA";
|
||
case EM_SPARC32PLUS: return "Sparc v8+" ;
|
||
case EM_PPC: return "Power PCC";
|
||
case EM_SPARCV9: return "Sparc v9";
|
||
case EM_ARM: return "ARM";
|
||
case EM_SH: return "Hitachi SH";
|
||
case EM_ALPHA: return "Alpha";
|
||
case EM_CYGNUS_D10V: return "d10v";
|
||
case EM_CYGNUS_D30V: return "d30v";
|
||
case EM_CYGNUS_M32R: return "M32r";
|
||
case EM_CYGNUS_V850: return "v850";
|
||
case EM_CYGNUS_MN10300: return "mn10300";
|
||
case EM_CYGNUS_MN10200: return "mn10200";
|
||
|
||
default:
|
||
sprintf (buff, _("<unknown>: %x"), e_machine);
|
||
return buff;
|
||
}
|
||
}
|
||
|
||
static char *
|
||
get_segment_type (p_type)
|
||
unsigned long p_type;
|
||
{
|
||
static char buff [32];
|
||
|
||
switch (p_type)
|
||
{
|
||
case PT_NULL: return _("Unused");
|
||
case PT_LOAD: return _("Loadable");
|
||
case PT_DYNAMIC: return _("Dynamic link info");
|
||
case PT_INTERP: return _("Interpreter");
|
||
case PT_NOTE: return _("Auxillary Info");
|
||
case PT_SHLIB: return _("Shared Library");
|
||
case PT_PHDR: return _("Program Headers");
|
||
|
||
default:
|
||
if ((p_type >= PT_LOPROC) && (p_type <= PT_HIPROC))
|
||
return _("processor specific");
|
||
else
|
||
{
|
||
sprintf (buff, _("<unknown>: %x"), p_type);
|
||
return buff;
|
||
}
|
||
}
|
||
}
|
||
|
||
static char *
|
||
get_section_type_name (sh_type)
|
||
unsigned int sh_type;
|
||
{
|
||
static char buff [32];
|
||
|
||
switch (sh_type)
|
||
{
|
||
case SHT_NULL: return _("Unused");
|
||
case SHT_PROGBITS: return _("Program data");
|
||
case SHT_SYMTAB: return _("Symbol table");
|
||
case SHT_STRTAB: return _("String table");
|
||
case SHT_RELA: return _("Relocs, addends");
|
||
case SHT_HASH: return _("Symbol hash table");
|
||
case SHT_DYNAMIC: return _("Dynamic linking info");
|
||
case SHT_NOTE: return _("Notes");
|
||
case SHT_NOBITS: return _("Space, no data");
|
||
case SHT_REL: return _("Relocs, no addends");
|
||
case SHT_SHLIB: return _("Shared Library info");
|
||
case SHT_DYNSYM: return _("Dynamic linker symbols");
|
||
case SHT_GNU_verdef: return _("Version definition");
|
||
case SHT_GNU_verneed: return _("Version needs");
|
||
case SHT_GNU_versym: return _("Version symbols");
|
||
case 0x6ffffff0: return "VERSYM";
|
||
case 0x6ffffffc: return "VERDEF";
|
||
case 0x7ffffffd: return "AUXILIARY";
|
||
case 0x7fffffff: return "FILTER";
|
||
|
||
default:
|
||
if ((sh_type >= SHT_LOPROC) && (sh_type <= SHT_HIPROC))
|
||
return _("processor specific");
|
||
else if ((sh_type >= SHT_LOUSER) && (sh_type <= SHT_HIUSER))
|
||
return _("application specific");
|
||
else
|
||
{
|
||
sprintf (buff, _("<unknown>: %x"), sh_type);
|
||
return buff;
|
||
}
|
||
}
|
||
}
|
||
|
||
struct option options [] =
|
||
{
|
||
{"all", no_argument, 0, 'a'},
|
||
{"file-header", no_argument, 0, 'h'},
|
||
{"program-headers", no_argument, 0, 'l'},
|
||
{"segments", no_argument, 0, 'l'},
|
||
{"sections", no_argument, 0, 'S'},
|
||
{"symbols", no_argument, 0, 's'},
|
||
{"relocs", no_argument, 0, 'r'},
|
||
{"dynamic", no_argument, 0, 'd'},
|
||
{"version-info", no_argument, 0, 'V'},
|
||
{"use-dynamic", no_argument, 0, 'D'},
|
||
|
||
{"hex-dump", required_argument, 0, 'x'},
|
||
#ifdef SUPPORT_DISASSEMBLY
|
||
{"instruction-dump", required_argument, 0, 'i'},
|
||
#endif
|
||
|
||
{"version", no_argument, 0, 'v'},
|
||
{"help", no_argument, 0, 'H'},
|
||
|
||
{0, no_argument, 0, 0}
|
||
};
|
||
|
||
static void
|
||
usage ()
|
||
{
|
||
fprintf (stderr, _("Usage: readelf {options} elf-file(s)\n"));
|
||
fprintf (stderr, _(" Options are:\n"));
|
||
fprintf (stderr, _(" -a or --all Display all the information\n"));
|
||
fprintf (stderr, _(" -h or --file-header Display the ELF file header\n"));
|
||
fprintf (stderr, _(" -l or --program-headers or --segments\n"));
|
||
fprintf (stderr, _(" Display the program headers\n"));
|
||
fprintf (stderr, _(" -S or --sections Display the sections' headers\n"));
|
||
fprintf (stderr, _(" -s or --symbols Display the symbol table\n"));
|
||
fprintf (stderr, _(" -r or --relocs Display the relocations (if present)\n"));
|
||
fprintf (stderr, _(" -d or --dynamic Display the dynamic section (if present)\n"));
|
||
fprintf (stderr, _(" -V or --version-info Display the version sections (if present)\n"));
|
||
fprintf (stderr, _(" -D or --use-dynamic Use the dynamic section info when displaying symbols\n"));
|
||
fprintf (stderr, _(" -x <number> or --hex-dump=<number>\n"));
|
||
fprintf (stderr, _(" Dump the contents of section <number>\n"));
|
||
#ifdef SUPPORT_DISASSEMBLY
|
||
fprintf (stderr, _(" -i <number> or --instruction-dump=<number>\n"));
|
||
fprintf (stderr, _(" Disassemble the contents of section <number>\n"));
|
||
#endif
|
||
fprintf (stderr, _(" -v or --version Display the version number of readelf\n"));
|
||
fprintf (stderr, _(" -H or --help Display this information\n"));
|
||
|
||
exit (0);
|
||
}
|
||
|
||
static void
|
||
parse_args (argc, argv)
|
||
int argc;
|
||
char ** argv;
|
||
{
|
||
char c;
|
||
|
||
if (argc < 2)
|
||
usage ();
|
||
|
||
while ((c = getopt_long
|
||
(argc, argv, "rsahldSDx:i:vV", options, NULL)) != EOF)
|
||
{
|
||
char * cp;
|
||
int section;
|
||
|
||
switch (c)
|
||
{
|
||
case 'H':
|
||
usage ();
|
||
break;
|
||
|
||
case 'a':
|
||
do_syms++;
|
||
do_reloc++;
|
||
do_dynamic++;
|
||
do_header++;
|
||
do_section++;
|
||
do_load++;
|
||
do_version++;
|
||
break;
|
||
case 'D':
|
||
do_using_dynamic++;
|
||
break;
|
||
case 'r':
|
||
do_reloc++;
|
||
break;
|
||
case 'h':
|
||
do_header++;
|
||
break;
|
||
case 'l':
|
||
do_load++;
|
||
break;
|
||
case 's':
|
||
do_syms++;
|
||
break;
|
||
case 'S':
|
||
do_section++;
|
||
break;
|
||
case 'd':
|
||
do_dynamic++;
|
||
break;
|
||
case 'x':
|
||
do_dump ++;
|
||
section = strtoul (optarg, & cp, 0);
|
||
if (! * cp && section >= 0 && section < NUM_DUMP_SECTS)
|
||
{
|
||
dump_sects [section] |= HEX_DUMP;
|
||
break;
|
||
}
|
||
goto oops;
|
||
#ifdef SUPPORT_DISASSEMBLY
|
||
case 'i':
|
||
do_dump ++;
|
||
section = strtoul (optarg, & cp, 0);
|
||
if (! * cp && section >= 0 && section < NUM_DUMP_SECTS)
|
||
{
|
||
dump_sects [section] |= DISASS_DUMP;
|
||
break;
|
||
}
|
||
goto oops;
|
||
#endif
|
||
case 'v':
|
||
print_version (program_name);
|
||
break;
|
||
case 'V':
|
||
do_version ++;
|
||
break;
|
||
default:
|
||
oops:
|
||
/* xgettext:c-format */
|
||
error (_("Invalid option '-%c'\n"), c);
|
||
/* Drop through. */
|
||
case '?':
|
||
usage ();
|
||
}
|
||
}
|
||
|
||
if (!do_dynamic && !do_syms && !do_reloc && !do_section
|
||
&& !do_load && !do_header && !do_dump && !do_version)
|
||
usage ();
|
||
else if (argc < 3)
|
||
warn (_("Nothing to do.\n"));
|
||
}
|
||
|
||
static int
|
||
process_elf_header ()
|
||
{
|
||
if ( epnt->e_ident [EI_MAG0] != ELFMAG0
|
||
|| epnt->e_ident [EI_MAG1] != ELFMAG1
|
||
|| epnt->e_ident [EI_MAG2] != ELFMAG2
|
||
|| epnt->e_ident [EI_MAG3] != ELFMAG3)
|
||
{
|
||
error (_("Not an ELF file - it has the wrong magic bytes at the start\n"));
|
||
return 0;
|
||
}
|
||
|
||
if (epnt->e_ident [EI_CLASS] != ELFCLASS32)
|
||
{
|
||
error (_("Not a 32 bit ELF file\n"));
|
||
return 0;
|
||
}
|
||
|
||
if (epnt->e_ident [EI_DATA] != expected_endian)
|
||
must_swap = 1;
|
||
|
||
if (must_swap)
|
||
{
|
||
Elf_Ehdr * new_header = malloc (sizeof (* new_header));
|
||
|
||
if (new_header == NULL)
|
||
{
|
||
error (_("out of memory\n"));
|
||
return 0;
|
||
}
|
||
|
||
memcpy (new_header, epnt, sizeof (* new_header));
|
||
|
||
BYTE_SWAP (epnt, e_type);
|
||
BYTE_SWAP (epnt, e_machine);
|
||
BYTE_SWAP (epnt, e_version);
|
||
BYTE_SWAP (epnt, e_entry);
|
||
BYTE_SWAP (epnt, e_phoff);
|
||
BYTE_SWAP (epnt, e_shoff);
|
||
BYTE_SWAP (epnt, e_flags);
|
||
BYTE_SWAP (epnt, e_ehsize);
|
||
BYTE_SWAP (epnt, e_phentsize);
|
||
BYTE_SWAP (epnt, e_phnum);
|
||
BYTE_SWAP (epnt, e_shentsize);
|
||
BYTE_SWAP (epnt, e_shnum);
|
||
BYTE_SWAP (epnt, e_shstrndx);
|
||
|
||
epnt = new_header;
|
||
}
|
||
|
||
if (do_header)
|
||
{
|
||
int i;
|
||
|
||
printf (_("ELF Header....\n"));
|
||
printf (_(" Magic: "));
|
||
for (i = 0; i < EI_NIDENT; i ++)
|
||
printf ("%2.2x ", epnt->e_ident [i]);
|
||
printf ("\n");
|
||
printf (_(" Type: %s\n"), get_file_type (epnt->e_type));
|
||
printf (_(" Machine: %s\n"), get_machine_name (epnt->e_machine));
|
||
printf (_(" Version: %x\n"), epnt->e_version);
|
||
printf (_(" Entry point address: %x\n"), epnt->e_entry);
|
||
printf (_(" Start of program headers: %d (bytes into file)\n"), epnt->e_phoff);
|
||
printf (_(" Start of section headers: %d (bytes into file)\n"), epnt->e_shoff);
|
||
printf (_(" Flags: %x\n"), epnt->e_flags);
|
||
printf (_(" Size of this header: %d (bytes)\n"), epnt->e_ehsize);
|
||
printf (_(" Size of program headers: %d (bytes)\n"), epnt->e_phentsize);
|
||
printf (_(" Number of program headers: %d\n"), epnt->e_phnum);
|
||
printf (_(" Size of section headers: %d (bytes)\n"), epnt->e_shentsize);
|
||
printf (_(" Number of section headers: %d\n"), epnt->e_shnum);
|
||
printf (_(" Section header string table index: %d\n"), epnt->e_shstrndx);
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
|
||
static void
|
||
process_program_headers ()
|
||
{
|
||
Elf_Phdr * elf_segments;
|
||
Elf_Phdr * ppnt;
|
||
int i;
|
||
|
||
if (epnt->e_phnum == 0)
|
||
{
|
||
if (do_load)
|
||
printf (_("\nThere are no program headers in this file\n"));
|
||
return;
|
||
}
|
||
|
||
if (do_load && !do_header)
|
||
{
|
||
printf (_("\nElf file is %s\n"), get_file_type (epnt->e_type));
|
||
printf (_("Entry point 0x%x\n"), epnt->e_entry);
|
||
printf (_("There are %d program headers, starting at offset %x:\n"),
|
||
epnt->e_phnum, epnt->e_phoff);
|
||
}
|
||
|
||
if (must_swap)
|
||
{
|
||
Elf_Phdr * new_header = malloc (sizeof (* new_header) * epnt->e_phnum);
|
||
|
||
if (new_header == NULL)
|
||
{
|
||
error (_("out of memory\n"));
|
||
return;
|
||
}
|
||
|
||
memcpy (new_header, & header [epnt->e_phoff],
|
||
sizeof (* new_header) * epnt->e_phnum);
|
||
|
||
elf_segments = ppnt = new_header;
|
||
|
||
for (i = 0; i < epnt->e_phnum; i++)
|
||
{
|
||
BYTE_SWAP (ppnt + i, p_type);
|
||
BYTE_SWAP (ppnt + i, p_flags);
|
||
BYTE_SWAP (ppnt + i, p_offset);
|
||
BYTE_SWAP (ppnt + i, p_vaddr);
|
||
BYTE_SWAP (ppnt + i, p_paddr);
|
||
BYTE_SWAP (ppnt + i, p_filesz);
|
||
BYTE_SWAP (ppnt + i, p_memsz);
|
||
BYTE_SWAP (ppnt + i, p_align);
|
||
|
||
new_header ++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ppnt = (Elf_Phdr *) & header [epnt->e_phoff];
|
||
elf_segments = NULL;
|
||
}
|
||
|
||
if (do_load)
|
||
{
|
||
printf (_("\nProgram Header%s....\n"), epnt->e_phnum > 1 ? "s" : "");
|
||
printf (_(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align\n"));
|
||
}
|
||
|
||
loadaddr = -1;
|
||
dynamic_addr = 0;
|
||
|
||
for (i = 0; i < epnt->e_phnum; i++)
|
||
{
|
||
if (loadaddr == -1 && ppnt->p_type == PT_LOAD)
|
||
loadaddr = (ppnt->p_vaddr & 0xfffff000)
|
||
- (ppnt->p_offset & 0xfffff000);
|
||
|
||
if (do_load)
|
||
{
|
||
printf (" %-10s ", get_segment_type (ppnt->p_type));
|
||
printf ("0x%5.5x ",ppnt->p_offset);
|
||
printf ("0x%8.8x ",ppnt->p_vaddr);
|
||
printf ("0x%8.8x ",ppnt->p_paddr);
|
||
printf ("0x%5.5x 0x%5.5x ",ppnt->p_filesz, ppnt->p_memsz);
|
||
printf ("%c%c%c ",
|
||
(ppnt->p_flags & 4 ? 'R' : ' '),
|
||
(ppnt->p_flags & 2 ? 'W' : ' '),
|
||
(ppnt->p_flags & 1 ? 'E' : ' '));
|
||
printf ("%#x", ppnt->p_align);
|
||
}
|
||
|
||
if (ppnt->p_type == PT_DYNAMIC)
|
||
{
|
||
if (dynamic_addr)
|
||
error (_("more than one dynamic section\n"));
|
||
|
||
dynamic_addr = ppnt->p_offset;
|
||
dynamic_size = ppnt->p_filesz;
|
||
}
|
||
|
||
if (ppnt->p_type == PT_INTERP)
|
||
{
|
||
if (do_load)
|
||
printf (_("\nRequesting program interpreter [%s]"),
|
||
& header [ppnt->p_offset]);
|
||
pint = strdup (& header [ppnt->p_offset]);
|
||
}
|
||
|
||
if (do_load)
|
||
putc ('\n', stdout);
|
||
|
||
ppnt ++;
|
||
}
|
||
|
||
if (do_load)
|
||
{
|
||
printf (_("\n Section to Segment mapping:\n"));
|
||
printf (_(" Segment Sections...\n"));
|
||
|
||
if (elf_segments)
|
||
ppnt = elf_segments;
|
||
else
|
||
ppnt = (Elf_Phdr *) & header [epnt->e_phoff];
|
||
|
||
for (i = 0; i < epnt->e_phnum; i++, ppnt++)
|
||
{
|
||
int j;
|
||
Elf_Shdr * spnt;
|
||
|
||
printf (" %2.2d ", i);
|
||
|
||
spnt = (Elf_Shdr *) & header [epnt->e_shoff];
|
||
|
||
if (must_swap)
|
||
{
|
||
lastmapped = SWAP4 (spnt[epnt->e_shstrndx].sh_offset);
|
||
|
||
for (j = 0; j < epnt->e_shnum; j++)
|
||
{
|
||
bfd_vma addr;
|
||
bfd_size_type size;
|
||
unsigned int name;
|
||
|
||
addr = SWAP4 (spnt[j].sh_addr);
|
||
size = SWAP4 (spnt[j].sh_size);
|
||
name = SWAP4 (spnt[j].sh_name);
|
||
|
||
if (size > 0
|
||
&& (addr >= ppnt->p_vaddr)
|
||
&& (addr + size) <= (ppnt->p_vaddr + ppnt->p_memsz))
|
||
printf ("%s ", header + lastmapped + name);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
lastmapped = spnt[epnt->e_shstrndx].sh_offset;
|
||
|
||
for (j = 0; j < epnt->e_shnum; j++, spnt++)
|
||
{
|
||
if (spnt->sh_size > 0
|
||
&& (spnt->sh_addr >= ppnt->p_vaddr)
|
||
&& (spnt->sh_addr + spnt->sh_size)
|
||
<= (ppnt->p_vaddr + ppnt->p_memsz))
|
||
printf ("%s ", SECTION_NAME (spnt));
|
||
}
|
||
}
|
||
|
||
putc ('\n',stdout);
|
||
}
|
||
}
|
||
|
||
if (elf_segments)
|
||
free (elf_segments);
|
||
}
|
||
|
||
|
||
static void
|
||
process_section_headers ()
|
||
{
|
||
Elf_Shdr * spnt;
|
||
int i;
|
||
|
||
if (must_swap)
|
||
{
|
||
Elf_Shdr * new_header = malloc (sizeof (* new_header) * epnt->e_shnum);
|
||
|
||
if (new_header == NULL)
|
||
{
|
||
error (_("out of memory\n"));
|
||
return;
|
||
}
|
||
|
||
memcpy (new_header, & header [epnt->e_shoff],
|
||
sizeof (* new_header) * epnt->e_shnum);
|
||
|
||
elf_sections = spnt = new_header;
|
||
|
||
for (i = 0; i < epnt->e_shnum; i++)
|
||
{
|
||
BYTE_SWAP (spnt + i, sh_name);
|
||
BYTE_SWAP (spnt + i, sh_type);
|
||
BYTE_SWAP (spnt + i, sh_flags);
|
||
BYTE_SWAP (spnt + i, sh_addr);
|
||
BYTE_SWAP (spnt + i, sh_offset);
|
||
BYTE_SWAP (spnt + i, sh_size);
|
||
BYTE_SWAP (spnt + i, sh_link);
|
||
BYTE_SWAP (spnt + i, sh_info);
|
||
BYTE_SWAP (spnt + i, sh_addralign);
|
||
BYTE_SWAP (spnt + i, sh_entsize);
|
||
|
||
new_header ++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
elf_sections = spnt = (Elf_Shdr *) & header [epnt->e_shoff];
|
||
}
|
||
|
||
spnt += epnt->e_shstrndx;
|
||
lastmapped = spnt->sh_offset;
|
||
spnt = elf_sections;
|
||
|
||
if (! do_section || (epnt->e_shnum == 0))
|
||
return;
|
||
|
||
if (! do_header)
|
||
printf (_("There are %d section headers, starting at offset %x:\n"),
|
||
epnt->e_shnum, epnt->e_shoff);
|
||
|
||
printf (_("\nSection Header%s....\n"), epnt->e_shnum > 1 ? "s" : "");
|
||
printf (_(" [Nr] Name Type Addr Off Size ES Flg Lk In Al\n"));
|
||
|
||
for (i = 0; i < epnt->e_shnum; i++)
|
||
{
|
||
printf (" [%2d] %-14s", i, SECTION_NAME (spnt));
|
||
|
||
printf (" %-18s ",get_section_type_name (spnt->sh_type));
|
||
printf ( "%8.8x %6.6x %6.6x %2.2x",
|
||
spnt->sh_addr,
|
||
spnt->sh_offset,
|
||
spnt->sh_size,
|
||
spnt->sh_entsize);
|
||
|
||
printf (" %c%c%c %2d %2x %d \n",
|
||
(spnt->sh_flags & 1 ? 'W' : ' '),
|
||
(spnt->sh_flags & 2 ? 'A' : ' '),
|
||
(spnt->sh_flags & 4 ? 'X' : ' '),
|
||
spnt->sh_link,
|
||
spnt->sh_info,
|
||
spnt->sh_addralign);
|
||
spnt ++;
|
||
}
|
||
}
|
||
|
||
/* Parse the dynamic segment */
|
||
static void
|
||
process_dynamic_segment ()
|
||
{
|
||
Elf_Dyn * elf_dynamic;
|
||
unsigned int i;
|
||
|
||
dynamic_size = dynamic_size / sizeof (Elf_Dyn);
|
||
|
||
if (must_swap)
|
||
{
|
||
Elf_Dyn * new_header = malloc (sizeof (* new_header) * dynamic_size);
|
||
|
||
if (new_header == NULL)
|
||
{
|
||
error (_("out of memory\n"));
|
||
return;
|
||
}
|
||
|
||
memcpy (new_header, & header [dynamic_addr],
|
||
sizeof (* new_header) * dynamic_size);
|
||
|
||
elf_dynamic = dpnt = new_header;
|
||
|
||
for (i = 0; i < dynamic_size; i++)
|
||
{
|
||
BYTE_SWAP (dpnt + i, d_tag);
|
||
BYTE_SWAP (dpnt + i, d_un.d_ptr);
|
||
|
||
new_header ++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
dpnt = (Elf_Dyn *) & header [dynamic_addr];
|
||
elf_dynamic = NULL;
|
||
}
|
||
|
||
/* Find symtab. */
|
||
for (i = 0; i < dynamic_size; ++i, ++dpnt)
|
||
if (dpnt->d_tag == DT_SYMTAB)
|
||
{
|
||
dynamic_info [DT_SYMTAB] = dpnt->d_un.d_val;
|
||
symtab = (Elf_Sym *) (header - loadaddr
|
||
+ dynamic_info[DT_SYMTAB]);
|
||
}
|
||
else if (dpnt->d_tag == DT_STRTAB)
|
||
{
|
||
dynamic_info [DT_STRTAB] = dpnt->d_un.d_val;
|
||
strtab = (char *) (header - loadaddr + dynamic_info[DT_STRTAB]);
|
||
}
|
||
|
||
if (do_dynamic && dynamic_addr)
|
||
{
|
||
printf (_("\n Dynamic section data: %x, %d entries\n"),
|
||
dynamic_addr, dynamic_size );
|
||
}
|
||
|
||
for (i = 0; i < dynamic_size; i++)
|
||
{
|
||
if (do_dynamic)
|
||
printf (_(" Tag: %#10x: "), dpnt->d_tag);
|
||
|
||
switch (dpnt->d_tag)
|
||
{
|
||
case DT_AUXILIARY:
|
||
case DT_FILTER:
|
||
if (do_dynamic)
|
||
{
|
||
printf ("(%-11s)", filtertype [DT_FILTER - dpnt->d_tag]);
|
||
|
||
if (dynamic_info [DT_STRTAB])
|
||
{
|
||
if (dpnt->d_tag == DT_AUXILIARY)
|
||
printf (_("Auxiliary library"));
|
||
else
|
||
printf (_("Filter library"));
|
||
|
||
printf (": [%s]\n", (dpnt->d_un.d_val + strtab));
|
||
}
|
||
else
|
||
printf (_("Value %x\n"), dpnt->d_un.d_val);
|
||
}
|
||
break;
|
||
|
||
case DT_NULL :
|
||
case DT_NEEDED :
|
||
case DT_PLTRELSZ:
|
||
case DT_PLTGOT :
|
||
case DT_HASH :
|
||
case DT_STRTAB :
|
||
case DT_SYMTAB :
|
||
case DT_RELA :
|
||
case DT_RELASZ :
|
||
case DT_RELAENT :
|
||
case DT_STRSZ :
|
||
case DT_SYMENT :
|
||
case DT_INIT :
|
||
case DT_FINI :
|
||
case DT_SONAME :
|
||
case DT_RPATH :
|
||
case DT_SYMBOLIC:
|
||
case DT_REL :
|
||
case DT_RELSZ :
|
||
case DT_RELENT :
|
||
case DT_PLTREL :
|
||
case DT_DEBUG :
|
||
case DT_TEXTREL :
|
||
case DT_JMPREL :
|
||
dynamic_info [dpnt->d_tag] = dpnt->d_un.d_val;
|
||
|
||
if (do_dynamic)
|
||
{
|
||
printf ("(%-11s)", dyntype [dpnt->d_tag]);
|
||
|
||
if (dynamic_info [DT_STRTAB])
|
||
{
|
||
switch (dpnt->d_tag)
|
||
{
|
||
case DT_NEEDED:
|
||
printf (_("Shared library: [%s]\n"),
|
||
(dpnt->d_un.d_val + strtab));
|
||
|
||
if (strcmp (dpnt->d_un.d_val + strtab, pint))
|
||
printf ("\n");
|
||
else
|
||
printf (_(" program interpreter\n"));
|
||
break;
|
||
|
||
case DT_SONAME:
|
||
printf (_("Library soname: [%s]\n"),
|
||
(dpnt->d_un.d_val + strtab));
|
||
break;
|
||
|
||
case DT_RPATH:
|
||
printf (_("Library rpath: [%s]\n"),
|
||
(dpnt->d_un.d_val + strtab));
|
||
break;
|
||
|
||
default:
|
||
printf (_("Value %x\n"), dpnt->d_un.d_val);
|
||
}
|
||
}
|
||
else
|
||
printf (_("Value %x\n"), dpnt->d_un.d_val);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
if ((dpnt->d_tag >= DT_VERSYM) && (dpnt->d_tag <= DT_VERNEEDNUM))
|
||
{
|
||
version_info [DT_VERSIONTAGIDX (dpnt->d_tag)] = dpnt->d_un.d_val;
|
||
|
||
if (do_dynamic)
|
||
printf (_("(%-11s) Value %#x\n"),
|
||
vertype [DT_VERSIONTAGIDX (dpnt->d_tag)],
|
||
dpnt->d_un.d_ptr);
|
||
}
|
||
else
|
||
warn (_("<Invalid> Value %#x\n"), dpnt->d_un.d_ptr);
|
||
break;
|
||
}
|
||
|
||
dpnt ++;
|
||
}
|
||
|
||
if (do_reloc)
|
||
{
|
||
if (do_using_dynamic)
|
||
{
|
||
if (dynamic_info [DT_REL])
|
||
{
|
||
rpnt = (Elf_Rel *) (header + dynamic_info [DT_REL] - loadaddr);
|
||
rel_size = dynamic_info [DT_RELSZ];
|
||
if (rel_size)
|
||
{
|
||
printf (_("\nRelocation section data: %x %x\n"),
|
||
dynamic_info[DT_REL], rel_size);
|
||
dump_relocations (rpnt, rel_size);
|
||
}
|
||
else
|
||
printf (_("\nNo Relocations in this file\n"));
|
||
}
|
||
|
||
if (dynamic_info[DT_RELA])
|
||
{
|
||
rpnt = (Elf_Rel *) (header + dynamic_info[DT_RELA] - loadaddr);
|
||
rel_size = dynamic_info[DT_RELASZ];
|
||
if (rel_size)
|
||
{
|
||
printf (_("\nRelocation section data: %x %x\n"),
|
||
dynamic_info[DT_RELA], rel_size);
|
||
dump_relocations (rpnt, rel_size);
|
||
}
|
||
else
|
||
printf (_("\nNo Relocations in this file\n"));
|
||
}
|
||
|
||
if (dynamic_info[DT_JMPREL])
|
||
{
|
||
rpnt = (Elf_Rel *) (header + dynamic_info[DT_JMPREL]
|
||
- loadaddr);
|
||
rel_size = dynamic_info[DT_PLTRELSZ];
|
||
if (rel_size)
|
||
{
|
||
printf (_("\nJumptable Relocation section data: %x %x\n"),
|
||
dynamic_info[DT_JMPREL], rel_size);
|
||
dump_relocations (rpnt, rel_size);
|
||
}
|
||
else
|
||
printf (_("\nNo Relocations in this file\n"));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Elf_Shdr * spnt;
|
||
|
||
spnt = elf_sections;
|
||
|
||
for (i = 0; i < epnt->e_shnum; i++, spnt++)
|
||
{
|
||
Elf_Shdr * symsec;
|
||
|
||
|
||
if (spnt->sh_type != SHT_RELA && spnt->sh_type != SHT_REL)
|
||
continue;
|
||
|
||
rpnt = (Elf_Rel *) (header + spnt->sh_offset);
|
||
|
||
rel_size = spnt->sh_size;
|
||
|
||
if (rel_size)
|
||
{
|
||
printf (_("\nRelocation section data: %s (%#x entries)\n"),
|
||
SECTION_NAME (spnt), rel_size / spnt->sh_entsize);
|
||
|
||
symsec = & elf_sections [spnt->sh_link];
|
||
symtab = (Elf_Sym *) (header + symsec->sh_offset);
|
||
strtab = (char *) (header
|
||
+ elf_sections [symsec->sh_link].sh_offset);
|
||
|
||
dump_relocations (rpnt, rel_size);
|
||
}
|
||
else
|
||
printf (_("\nNo Relocations in this file\n"));
|
||
}
|
||
}
|
||
}
|
||
|
||
if (elf_dynamic)
|
||
free (elf_dynamic);
|
||
}
|
||
|
||
/* Dump the symbol table */
|
||
static void
|
||
process_symbol_table ()
|
||
{
|
||
char * pnt;
|
||
int i;
|
||
Elf_Shdr * spnt;
|
||
|
||
if (! do_syms)
|
||
return;
|
||
|
||
if (dynamic_info [DT_HASH] && do_using_dynamic)
|
||
{
|
||
int nbucket;
|
||
int nchain;
|
||
int * elf_buckets;
|
||
int * chains;
|
||
int hn;
|
||
int si;
|
||
int * hash_addr;
|
||
|
||
hash_addr = (int *) (dynamic_info [DT_HASH] + header - loadaddr);
|
||
|
||
nbucket = *hash_addr++;
|
||
nchain = *hash_addr++;
|
||
elf_buckets = hash_addr;
|
||
hash_addr += nbucket;
|
||
chains = hash_addr;
|
||
|
||
printf (_("\n Symbol table for image\n"));
|
||
printf (_(" Num Buc: Value Size Type Bind Ot Ndx Name\n"));
|
||
|
||
for (hn = 0; hn < nbucket; hn++)
|
||
{
|
||
if (! elf_buckets [hn])
|
||
continue;
|
||
|
||
for (si = elf_buckets[hn]; si; si = chains[si])
|
||
{
|
||
pnt = strtab + symtab[si].st_name;
|
||
|
||
printf ("%3d %3d: %8x %5d %6s %6s %2d ", si, hn,
|
||
symtab[si].st_value,
|
||
symtab[si].st_size,
|
||
sttinfo [ELF_ST_TYPE (symtab[si].st_info)],
|
||
stbinfo [ELF_ST_BIND (symtab[si].st_info)],
|
||
symtab[si].st_other);
|
||
|
||
if (symtab[si].st_shndx == 0)
|
||
printf ("UND");
|
||
else if ((symtab[si].st_shndx & 0xffff) == 0xfff1)
|
||
printf ("ABS");
|
||
else if ((symtab[si].st_shndx & 0xffff) == 0xfff2)
|
||
printf ("COM");
|
||
else
|
||
printf ("%3d", symtab[si].st_shndx);
|
||
printf (" %s\n", pnt);
|
||
}
|
||
}
|
||
}
|
||
else if (!do_using_dynamic)
|
||
{
|
||
int i;
|
||
unsigned short * vers_addr;
|
||
|
||
spnt = elf_sections;
|
||
vers_addr = (short *) (version_info [DT_VERNEEDNUM - DT_VERSYM]
|
||
+ header - loadaddr);
|
||
|
||
for (i = 0; i < epnt->e_shnum; i++, spnt++)
|
||
{
|
||
unsigned int si;
|
||
|
||
if (spnt->sh_type != SHT_SYMTAB && spnt->sh_type != SHT_DYNSYM)
|
||
continue;
|
||
|
||
printf (_("\nSymbol data for: %s\n"), SECTION_NAME (spnt));
|
||
printf (_(" Num: Value Size Type Bind Ot Ndx Name\n"));
|
||
|
||
symtab = (Elf_Sym *) (header + spnt->sh_offset);
|
||
strtab = (char *) (header + elf_sections [spnt->sh_link].sh_offset);
|
||
|
||
for (si = 0; si < spnt->sh_size / spnt->sh_entsize; si++)
|
||
{
|
||
Elf_Sym ssym;
|
||
Elf_Sym * psym;
|
||
Elf_Sym * new_header;
|
||
|
||
psym = symtab + si;
|
||
|
||
if (must_swap)
|
||
{
|
||
ssym = * psym;
|
||
|
||
new_header = & ssym;
|
||
|
||
BYTE_SWAP (psym, st_name);
|
||
BYTE_SWAP (psym, st_value);
|
||
BYTE_SWAP (psym, st_size);
|
||
BYTE_SWAP (psym, st_shndx);
|
||
|
||
psym = new_header;
|
||
}
|
||
|
||
pnt = strtab + psym->st_name;
|
||
|
||
printf (" %3d: %8x %5d %-7s %-6s %2d ", si,
|
||
psym->st_value,
|
||
psym->st_size,
|
||
sttinfo [ELF_ST_TYPE (psym->st_info)],
|
||
stbinfo [ELF_ST_BIND (psym->st_info)],
|
||
psym->st_other);
|
||
|
||
if (psym->st_shndx == 0)
|
||
printf ("UND");
|
||
else if ((psym->st_shndx & 0xffff) == 0xfff1)
|
||
printf ("ABS");
|
||
else if ((psym->st_shndx & 0xffff) == 0xfff2)
|
||
printf ("COM");
|
||
else
|
||
printf ("%3d", psym->st_shndx);
|
||
printf (" %s", pnt);
|
||
|
||
if (spnt->sh_type == SHT_DYNSYM &&
|
||
version_info [DT_VERSIONTAGIDX (DT_VERSYM)] != 0 &&
|
||
((vers_addr[si] & 0x8000) || vers_addr[si] > 1))
|
||
{
|
||
Elf_Vernaux * a;
|
||
|
||
if (elf_sections [psym->st_shndx].sh_type == SHT_NOBITS
|
||
|| psym->st_shndx == SHN_UNDEF)
|
||
{
|
||
Elf_Verneed * v;
|
||
|
||
/* We must test both. */
|
||
v = (Elf_Verneed *)
|
||
(version_info [DT_VERSIONTAGIDX (DT_VERNEED)]
|
||
+ header - loadaddr);
|
||
|
||
for (;;)
|
||
{
|
||
a = (Elf_Vernaux *)((char *)v + v->vn_aux);
|
||
|
||
while (a->vna_other != vers_addr[si]
|
||
&& a->vna_next != 0)
|
||
a = (Elf_Vernaux *)((char *)a + a->vna_next);
|
||
|
||
if (a->vna_other == vers_addr[si])
|
||
break;
|
||
|
||
if (v->vn_next == 0)
|
||
{
|
||
if (elf_sections [psym->st_shndx].sh_type
|
||
!= SHT_NOBITS)
|
||
error (_("bad dynamic symbol"));
|
||
|
||
a = NULL;
|
||
break;
|
||
}
|
||
|
||
v = (Elf_Verneed *)((char *)v + v->vn_next);
|
||
}
|
||
|
||
if (a != NULL)
|
||
printf ("@%s (%d)", strtab + a->vna_name, a->vna_other);
|
||
}
|
||
else if ((elf_sections [psym->st_shndx].sh_type
|
||
== SHT_NOBITS && a == NULL)
|
||
|| psym->st_shndx != SHN_UNDEF)
|
||
{
|
||
Elf_Verdef * v;
|
||
Elf_Verdaux * b;
|
||
|
||
v = (Elf_Verdef *)
|
||
(version_info [DT_VERSIONTAGIDX (DT_VERDEF)]
|
||
+ header - loadaddr);
|
||
|
||
if (vers_addr[si] == 0x8001)
|
||
pnt = "";
|
||
else
|
||
{
|
||
while (v->vd_ndx != (vers_addr [si] & 0x7fff))
|
||
v = (Elf_Verdef *)((char *)v + v->vd_next);
|
||
|
||
b = (Elf_Verdaux *) ((char *)v + v->vd_aux);
|
||
|
||
if (psym->st_name != b->vda_name)
|
||
pnt = strtab + b->vda_name;
|
||
else
|
||
pnt = NULL;
|
||
}
|
||
|
||
if (pnt)
|
||
printf ((vers_addr [si] & 0x8000)
|
||
? "@%s" : "@@%s", pnt);
|
||
}
|
||
}
|
||
|
||
puts ("");
|
||
}
|
||
}
|
||
}
|
||
|
||
if (! do_version)
|
||
return;
|
||
|
||
spnt = elf_sections;
|
||
|
||
for (i = 0; i < epnt->e_shnum; i++, spnt++)
|
||
{
|
||
if (spnt->sh_type == SHT_GNU_verdef)
|
||
{
|
||
Elf_Shdr * dspnt = &elf_sections[spnt->sh_link];
|
||
unsigned int idx;
|
||
unsigned int cnt;
|
||
|
||
strtab = (char *) (header - loadaddr + dynamic_info[DT_STRTAB]);
|
||
|
||
printf (_("\n Version definitions:%s (%#0x entries)\n"),
|
||
SECTION_NAME(spnt), spnt->sh_info);
|
||
printf (_("Addr: %#08x Offset: %#08x Link: %x (%s)\n"),
|
||
spnt->sh_addr, spnt->sh_offset, spnt->sh_link,
|
||
SECTION_NAME(dspnt));
|
||
|
||
for (idx = cnt = 0; cnt < spnt->sh_info; ++cnt)
|
||
{
|
||
Elf_Verdef * ent = (Elf_Verdef *)
|
||
((char *) header + spnt->sh_offset + idx);
|
||
Elf_Verdaux * aux = (Elf_Verdaux *)
|
||
((char *) ent + ent->vd_aux);
|
||
int j, isum;
|
||
|
||
printf (_("%#06x: Rev: %d Flags: "), idx, ent->vd_version);
|
||
|
||
if (ent->vd_flags == 0)
|
||
printf (_("none"));
|
||
else
|
||
{
|
||
int f = 1;
|
||
if (ent->vd_flags & 0x1)
|
||
{
|
||
printf (_("BASE"));
|
||
f = 0;
|
||
}
|
||
if (ent->vd_flags & 0x2)
|
||
{
|
||
printf (_("%sWEAK"), f ? "" : "|");
|
||
f = 0;
|
||
}
|
||
}
|
||
printf (_(" Index: %d Cnt: %d Name: %s\n"),
|
||
ent->vd_ndx, ent->vd_cnt, strtab + aux->vda_name);
|
||
j = 1;
|
||
isum = idx + ent->vd_aux;
|
||
while (j < ent->vd_cnt)
|
||
{
|
||
isum += aux->vda_next;
|
||
aux = (Elf_Verdaux *)((char *)aux + aux->vda_next);
|
||
printf (_(" %#06x: Parent %d: %s\n"), isum, j,
|
||
strtab + aux->vda_name);
|
||
++j;
|
||
}
|
||
|
||
idx += ent->vd_next;
|
||
}
|
||
}
|
||
|
||
if (spnt->sh_type == SHT_GNU_verneed)
|
||
{
|
||
Elf_Shdr * dspnt = &elf_sections[spnt->sh_link];
|
||
unsigned int idx;
|
||
unsigned int cnt;
|
||
|
||
strtab = (char *) (header - loadaddr + dynamic_info[DT_STRTAB]);
|
||
printf (_("\n Needed versions:%s (%#0x entries)\n"),
|
||
SECTION_NAME (spnt), spnt->sh_info);
|
||
printf (_("Addr: %#08x Offset: %#08x Link: %x (%s)\n"),
|
||
spnt->sh_addr, spnt->sh_offset, spnt->sh_link,
|
||
SECTION_NAME (dspnt));
|
||
|
||
for (idx = cnt = 0; cnt < spnt->sh_info; ++cnt)
|
||
{
|
||
Elf_Verneed * ent = (Elf_Verneed *)
|
||
((char *) header + spnt->sh_offset + idx);
|
||
Elf_Vernaux * aux = (Elf_Vernaux *)
|
||
((char *) ent + ent->vn_aux);
|
||
int j, isum;
|
||
|
||
printf (_("%#06x: Version: %d File: %s Cnt: %d\n"),
|
||
idx, ent->vn_version, strtab + ent->vn_file,ent->vn_cnt);
|
||
|
||
for (j = 0, isum = idx + ent->vn_aux; j < ent->vn_cnt; ++j)
|
||
{
|
||
printf (_(" %#06x: Name: %s Flags: %s Version: %d\n"),
|
||
isum, strtab+aux->vna_name,
|
||
aux->vna_flags & 0x2 ? "WEAK" : "none",
|
||
aux->vna_other);
|
||
isum += aux->vna_next;
|
||
aux = (Elf_Vernaux *)((char *) aux + aux->vna_next);
|
||
}
|
||
|
||
idx += ent->vn_next;
|
||
}
|
||
}
|
||
|
||
if (spnt->sh_type == SHT_GNU_versym)
|
||
{
|
||
Elf_Shdr * dspnt = &elf_sections[spnt->sh_link];
|
||
int total = spnt->sh_size / spnt->sh_entsize;
|
||
int cnt;
|
||
unsigned short * p = (short *)
|
||
(version_info[DT_VERNEEDNUM - DT_VERSYM] + header - loadaddr);
|
||
|
||
symtab = (Elf_Sym *) (header + dspnt->sh_offset);
|
||
strtab = (char *) (header + elf_sections[dspnt->sh_link].sh_offset);
|
||
|
||
printf (_("\n Version symbols:%s (%#0x entries)\n"),
|
||
SECTION_NAME (spnt), total);
|
||
printf (_("Addr: %#08x Offset: %#08x Link: %x (%s)\n"),
|
||
spnt->sh_addr, spnt->sh_offset, spnt->sh_link,
|
||
SECTION_NAME (dspnt));
|
||
|
||
for (cnt = 0; cnt < total; cnt += 4)
|
||
{
|
||
int j, nn;
|
||
|
||
printf ("%#08x:", cnt);
|
||
|
||
for (j = 0; (j < 4) && (cnt + j) < total; ++j)
|
||
switch (p[cnt + j])
|
||
{
|
||
case 0:
|
||
printf (" 0 (*local*) ");
|
||
break;
|
||
case 1:
|
||
printf (" 1 (*global*) ");
|
||
break;
|
||
default:
|
||
nn = printf ("%4x%c", p[cnt + j] & 0x7fff,
|
||
p[cnt + j] & 0x8000 ? 'h' : ' ');
|
||
if (elf_sections[symtab[cnt + j].st_shndx].sh_type
|
||
== SHT_NOBITS)
|
||
{
|
||
/* We must test both. */
|
||
Elf_Verneed * v = (Elf_Verneed *)
|
||
(version_info [DT_VERNEEDNUM - DT_VERNEED]
|
||
+ header - loadaddr);
|
||
Elf_Vernaux * a = NULL;
|
||
|
||
for (;;)
|
||
{
|
||
a = (Elf_Vernaux *)((char *) v + v->vn_aux);
|
||
|
||
while (a->vna_other != p[cnt + j]
|
||
&& a->vna_next != 0)
|
||
a = (Elf_Vernaux *)((char *) a + a->vna_next);
|
||
|
||
if (a->vna_other == p[cnt + j])
|
||
break;
|
||
|
||
if (v->vn_next == 0)
|
||
{
|
||
a = NULL;
|
||
break;
|
||
}
|
||
|
||
v = (Elf_Verneed *)((char *)v + v->vn_next);
|
||
}
|
||
|
||
if (a != NULL)
|
||
nn += printf ("(%s)", strtab + a->vna_name);
|
||
else
|
||
{
|
||
Elf_Verdef * v = (Elf_Verdef *)
|
||
(version_info [DT_VERNEEDNUM - DT_VERDEF]
|
||
+ header - loadaddr);
|
||
Elf_Verdaux * a;
|
||
|
||
if (p[cnt + j] == 0x8001)
|
||
pnt = "";
|
||
else
|
||
{
|
||
while (v->vd_ndx != (p[cnt + j]&0x7fff))
|
||
v = (Elf_Verdef *)((char *)v + v->vd_next);
|
||
|
||
a = (Elf_Verdaux *) ((char *) v + v->vd_aux);
|
||
pnt = strtab + a->vda_name;
|
||
}
|
||
|
||
if (pnt)
|
||
nn += printf ("(%s)", pnt);
|
||
}
|
||
|
||
if (nn <16)
|
||
printf ("%*c", 16 - nn, ' ');
|
||
}
|
||
else if (symtab[cnt + j].st_shndx ==SHN_UNDEF)
|
||
{
|
||
Elf_Verneed * v = (Elf_Verneed *)
|
||
(version_info [DT_VERNEEDNUM - DT_VERNEED]
|
||
+ header - loadaddr);
|
||
Elf_Vernaux * a;
|
||
|
||
for (;;)
|
||
{
|
||
a = (Elf_Vernaux *)((char *) v + v->vn_aux);
|
||
|
||
while (a->vna_other != p[cnt + j]
|
||
&& a->vna_next != 0)
|
||
a = (Elf_Vernaux *)((char *)a + a->vna_next);
|
||
|
||
if (a->vna_other == p[cnt + j])
|
||
break;
|
||
|
||
v = (Elf_Verneed *)((char *) v + v->vn_next);
|
||
}
|
||
|
||
nn += printf ("(%s)", strtab + a->vna_name);
|
||
|
||
if (nn <16)
|
||
printf ("%*c", 16 - nn, ' ');
|
||
}
|
||
else
|
||
{
|
||
Elf_Verdef * v = (Elf_Verdef *)
|
||
(version_info [DT_VERNEEDNUM - DT_VERDEF]
|
||
+ header - loadaddr);
|
||
Elf_Verdaux * a;
|
||
|
||
if (p[cnt + j] == 0x8001)
|
||
pnt = "";
|
||
else
|
||
{
|
||
while (v->vd_ndx != (p[cnt + j] & 0x7fff))
|
||
v = (Elf_Verdef *)((char *) v + v->vd_next);
|
||
|
||
a = (Elf_Verdaux *) ((char *) v + v->vd_aux);
|
||
pnt = strtab + a->vda_name;
|
||
}
|
||
|
||
if (pnt)
|
||
nn += printf ("(%s)", pnt);
|
||
|
||
if (nn <16)
|
||
printf ("%*c", 16 - nn, ' ');
|
||
}
|
||
}
|
||
|
||
printf ("\n");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
process_section_contents ()
|
||
{
|
||
Elf_Shdr * spnt;
|
||
int i;
|
||
|
||
if (! do_dump)
|
||
return;
|
||
|
||
spnt = elf_sections;
|
||
|
||
for (i = 0; i < epnt->e_shnum; i++, spnt++)
|
||
{
|
||
int bytes;
|
||
int addr;
|
||
int lbytes;
|
||
unsigned char * my_addr;
|
||
|
||
#ifdef SUPPORT_DISASSEMBLY
|
||
/* See if we need an assembly dump of this section */
|
||
|
||
if ((i < NUM_DUMP_SECTS) && (dump_sects[i] & DISASS_DUMP))
|
||
{
|
||
printf (_("\nAssembly dump of section %s\n"), SECTION_NAME (spnt));
|
||
|
||
bytes = spnt->sh_size;
|
||
addr = spnt->sh_addr;
|
||
my_addr = (unsigned char *) (header + spnt->sh_offset);
|
||
|
||
while (bytes > 0)
|
||
{
|
||
printf ("0x%8.8x ", addr);
|
||
|
||
switch (epnt->e_machine)
|
||
{
|
||
case EM_386:
|
||
case EM_486:
|
||
lbytes = db_disasm ((unsigned int) my_addr, 0, 0) -
|
||
((unsigned int) my_addr);
|
||
break;
|
||
case EM_68K:
|
||
lbytes = (m68k_disass ((unsigned int) my_addr, addr)
|
||
- (unsigned int) my_addr);
|
||
break;
|
||
default:
|
||
warn (_("Unable to disassemble code for this platform\n"));
|
||
return;
|
||
}
|
||
|
||
addr += lbytes;
|
||
my_addr += lbytes;
|
||
bytes -= lbytes;
|
||
|
||
printf ("\n");
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/* OK, see if we need a hex dump of this section. */
|
||
if ((i < NUM_DUMP_SECTS) && (dump_sects[i] & HEX_DUMP))
|
||
{
|
||
int j;
|
||
int k;
|
||
|
||
printf (_("\nHex dump of section %s\n"), SECTION_NAME (spnt));
|
||
|
||
bytes = spnt->sh_size;
|
||
addr = spnt->sh_addr;
|
||
my_addr = (unsigned char *) (header + spnt->sh_offset);
|
||
|
||
while (bytes)
|
||
{
|
||
lbytes = (bytes > 16 ? 16 : bytes);
|
||
|
||
printf ("0x%8.8x ",addr);
|
||
|
||
switch (epnt->e_ident [EI_DATA])
|
||
{
|
||
case ELFDATA2LSB:
|
||
for (j = 15; j >= 0; j --)
|
||
{
|
||
if (j < lbytes)
|
||
printf ("%2.2x", my_addr[j]);
|
||
else
|
||
printf (" ");
|
||
|
||
if (!(j & 0x3))
|
||
printf (" ");
|
||
}
|
||
break;
|
||
|
||
case ELFDATA2MSB:
|
||
for (j = 0; j < 16; j++)
|
||
{
|
||
if (j < lbytes)
|
||
printf ("%2.2x", my_addr[j]);
|
||
else
|
||
printf (" ");
|
||
|
||
if ((j & 3) == 3)
|
||
printf (" ");
|
||
}
|
||
break;
|
||
}
|
||
|
||
for (j = 0; j < lbytes; j++)
|
||
{
|
||
k = my_addr [j];
|
||
if (k >= ' ' && k < 0x80)
|
||
printf ("%c", k);
|
||
else
|
||
printf (".");
|
||
}
|
||
|
||
printf ("\n");
|
||
|
||
my_addr += lbytes;
|
||
addr += lbytes;
|
||
bytes -= lbytes;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
process_file (file_name)
|
||
char * file_name;
|
||
{
|
||
int fd;
|
||
struct stat statbuf;
|
||
|
||
must_swap = 0;
|
||
|
||
fd = open (file_name, O_RDONLY);
|
||
if (fd == -1)
|
||
{
|
||
error (_("Input file %s not found.\n"), file_name);
|
||
return;
|
||
}
|
||
|
||
if (fstat (fd, & statbuf) < 0)
|
||
{
|
||
error (_("Cannot stat input file %s.\n"), file_name);
|
||
close (fd);
|
||
return;
|
||
}
|
||
|
||
header = mmap (0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||
|
||
if ((header == (char *) -1) || (header == NULL))
|
||
{
|
||
error (_("Cannot mmap %s: %s\n"), file_name, strerror (errno));
|
||
close (fd);
|
||
return;
|
||
}
|
||
|
||
close (fd);
|
||
|
||
epnt = (Elf_Ehdr *) header;
|
||
|
||
if (show_name)
|
||
printf (_("\nFile: %s\n"), file_name);
|
||
|
||
if (! process_elf_header ())
|
||
{
|
||
munmap (header, statbuf.st_size);
|
||
return;
|
||
}
|
||
|
||
process_program_headers ();
|
||
|
||
if (loadaddr == -1)
|
||
{
|
||
/* Very strange. */
|
||
loadaddr = 0;
|
||
}
|
||
|
||
process_section_headers ();
|
||
|
||
process_dynamic_segment ();
|
||
|
||
process_symbol_table ();
|
||
|
||
process_section_contents ();
|
||
|
||
munmap (header, statbuf.st_size);
|
||
|
||
if (must_swap)
|
||
{
|
||
if (epnt)
|
||
{
|
||
free (epnt);
|
||
epnt = NULL;
|
||
}
|
||
|
||
if (elf_sections)
|
||
{
|
||
free (elf_sections);
|
||
elf_sections = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef SUPPORT_DISASSEMBLY
|
||
/* Needed by the i386 disassembler. For extra credit, someone could
|
||
fix this so that we insert symbolic addresses here, esp for GOT/PLT
|
||
symbols */
|
||
|
||
void
|
||
print_address (unsigned int addr, FILE * outfile)
|
||
{
|
||
fprintf (outfile,"0x%8.8x", addr);
|
||
}
|
||
|
||
/* Needed by the i386 disassembler. */
|
||
void
|
||
db_task_printsym (unsigned int addr)
|
||
{
|
||
print_address (addr, stderr);
|
||
}
|
||
#endif
|
||
|
||
int
|
||
main (argc, argv)
|
||
int argc;
|
||
char ** argv;
|
||
{
|
||
parse_args (argc, argv);
|
||
|
||
expected_endian = 0x12345678;
|
||
|
||
if (* ((char *) & expected_endian) == 0x12)
|
||
expected_endian = ELFDATA2MSB;
|
||
else
|
||
expected_endian = ELFDATA2LSB;
|
||
|
||
if (optind < (argc - 1))
|
||
show_name = 1;
|
||
|
||
while (optind < argc)
|
||
process_file (argv [optind ++]);
|
||
|
||
return 0;
|
||
}
|