]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - binutils/readelf.c
include/elf/ChangeLog:
[thirdparty/binutils-gdb.git] / binutils / readelf.c
index 5f738c9335d1ef4083efccd2027806a71da823d6..f0385d0f9c82f4a40ba765cec7d3601c812d01b3 100644 (file)
@@ -1,5 +1,5 @@
 /* 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
    Free Software Foundation, Inc.
 
    Originally developed by Eric Youngdale <eric@andante.jic.com>
@@ -71,6 +71,7 @@
 #include "elf/arc.h"
 #include "elf/arm.h"
 #include "elf/avr.h"
+#include "elf/bfin.h"
 #include "elf/cris.h"
 #include "elf/d10v.h"
 #include "elf/d30v.h"
@@ -94,7 +95,7 @@
 #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"
@@ -176,6 +177,16 @@ static size_t group_count;
 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).  */
@@ -184,8 +195,9 @@ unsigned num_cmdline_dump_sects = 0;
 
 /* 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;
 
@@ -597,7 +609,10 @@ guess_is_rela (unsigned long e_machine)
     case EM_XTENSA_OLD:
     case EM_M32R:
     case EM_M32C:
-    case EM_MS1:
+    case EM_MT:
+    case EM_BLACKFIN:
+    case EM_NIOS32:
+    case EM_ALTERA_NIOS2:
       return TRUE;
 
     case EM_MMA:
@@ -1110,9 +1125,14 @@ dump_relocations (FILE *file,
          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;
+
        }
 
       if (rtype == NULL)
@@ -1460,6 +1480,8 @@ 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";
@@ -1669,7 +1691,10 @@ get_machine_name (unsigned e_machine)
     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";
     default:
       snprintf (buff, sizeof (buff), _("<unknown>: %x"), e_machine);
       return buff;
@@ -2609,6 +2634,7 @@ usage (void)
   fprintf (stdout, _("\
   -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);
@@ -2650,6 +2676,27 @@ request_dump (unsigned int section, int type)
   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)
 {
@@ -2737,11 +2784,10 @@ parse_args (int argc, char **argv)
          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)
@@ -2905,7 +2951,9 @@ parse_args (int argc, char **argv)
          do_wide++;
          break;
        default:
+#ifdef SUPPORT_DISASSEMBLY
        oops:
+#endif
          /* xgettext:c-format */
          error (_("Invalid option '-%c'\n"), c);
          /* Drop through.  */
@@ -4572,6 +4620,11 @@ struct absaddr
     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
@@ -4664,7 +4717,7 @@ dump_ia64_unwind (struct ia64_unw_aux_info *aux)
       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",
@@ -6676,6 +6729,41 @@ get_symbol_visibility (unsigned int visibility)
     }
 }
 
+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)
 {
@@ -6838,6 +6926,11 @@ process_symbol_table (FILE *file)
              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));
@@ -6905,6 +6998,11 @@ process_symbol_table (FILE *file)
              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>");
@@ -7489,6 +7587,32 @@ display_debug_section (Elf_Internal_Shdr *section, FILE *file)
   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)
 {
@@ -7498,6 +7622,8 @@ 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++)
@@ -7541,6 +7667,337 @@ process_mips_fpe_exception (int mask)
     fputs ("0", stdout);
 }
 
+/* ARM EABI attributes section.  */
+typedef struct
+{
+  int tag;
+  const char *name;
+  /* 0 = special, 1 = string, 2 = uleb123, > 0x80 == table lookup.  */
+  int type;
+  const char **table;
+} arm_attr_public_tag;
+
+static const char *arm_attr_tag_CPU_arch[] =
+  {"Pre-v4", "v4", "v4T", "v5T", "v5TE", "v5TEJ", "v6", "v6KZ", "v6T2",
+   "v6K", "v7"};
+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"};
+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[] =
+  {"None", "Bare platform", "Linux application", "Linux DSO", "PalmOS 2004",
+   "PalmOS (reserved)", "SymbianOS 2004", "SymbianOS (reserved)"};
+static const char *arm_attr_tag_ABI_PCS_R9_use[] =
+  {"V6", "SB", "TLS", "Unused"};
+static const char *arm_attr_tag_ABI_PCS_RW_data[] =
+  {"Absolute", "PC-relative", "SB-relative", "None"};
+static const char *arm_attr_tag_ABI_PCS_RO_DATA[] =
+  {"Absolute", "PC-relative", "None"};
+static const char *arm_attr_tag_ABI_PCS_GOT_use[] =
+  {"None", "direct", "GOT-indirect"};
+static const char *arm_attr_tag_ABI_PCS_wchar_t[] =
+  {"None", "??? 1", "2", "??? 3", "4"};
+static const char *arm_attr_tag_ABI_FP_rounding[] = {"Unused", "Needed"};
+static const char *arm_attr_tag_ABI_FP_denormal[] = {"Unused", "Needed"};
+static const char *arm_attr_tag_ABI_FP_exceptions[] = {"Unused", "Needed"};
+static const char *arm_attr_tag_ABI_FP_user_exceptions[] = {"Unused", "Needed"};
+static const char *arm_attr_tag_ABI_FP_number_model[] =
+  {"Unused", "Finite", "RTABI", "IEEE 754"};
+static const char *arm_attr_tag_ABI_align8_needed[] = {"No", "Yes", "4-byte"};
+static const char *arm_attr_tag_ABI_align8_preserved[] =
+  {"No", "Yes, except leaf SP", "Yes"};
+static const char *arm_attr_tag_ABI_enum_size[] =
+  {"Unused", "small", "int", "forced to int"};
+static const char *arm_attr_tag_ABI_HardFP_use[] =
+  {"As Tag_VFP_arch", "SP only", "DP only", "SP and DP"};
+static const char *arm_attr_tag_ABI_VFP_args[] =
+  {"AAPCS", "VFP registers", "custom"};
+static const char *arm_attr_tag_ABI_WMMX_args[] =
+  {"AAPCS", "WMMX registers", "custom"};
+static const char *arm_attr_tag_ABI_optimization_goals[] =
+  {"None", "Prefer Speed", "Aggressive Speed", "Prefer Size",
+    "Aggressive Size", "Prefer Debug", "Aggressive Debug"};
+static const char *arm_attr_tag_ABI_FP_optimization_goals[] =
+  {"None", "Prefer Speed", "Aggressive Speed", "Prefer Size",
+    "Aggressive Size", "Prefer Accuracy", "Aggressive Accuracy"};
+
+#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[] = 
+{
+  {4, "CPU_raw_name", 1, NULL},
+  {5, "CPU_name", 1, NULL},
+  LOOKUP(6, CPU_arch),
+  {7, "CPU_arch_profile", 0, NULL},
+  LOOKUP(8, ARM_ISA_use),
+  LOOKUP(9, THUMB_ISA_use),
+  LOOKUP(10, VFP_arch),
+  LOOKUP(11, WMMX_arch),
+  LOOKUP(12, NEON_arch),
+  LOOKUP(13, ABI_PCS_config),
+  LOOKUP(14, ABI_PCS_R9_use),
+  LOOKUP(15, ABI_PCS_RW_data),
+  LOOKUP(16, ABI_PCS_RO_DATA),
+  LOOKUP(17, ABI_PCS_GOT_use),
+  LOOKUP(18, ABI_PCS_wchar_t),
+  LOOKUP(19, ABI_FP_rounding),
+  LOOKUP(20, ABI_FP_denormal),
+  LOOKUP(21, ABI_FP_exceptions),
+  LOOKUP(22, ABI_FP_user_exceptions),
+  LOOKUP(23, ABI_FP_number_model),
+  LOOKUP(24, ABI_align8_needed),
+  LOOKUP(25, ABI_align8_preserved),
+  LOOKUP(26, ABI_enum_size),
+  LOOKUP(27, ABI_HardFP_use),
+  LOOKUP(28, ABI_VFP_args),
+  LOOKUP(29, ABI_WMMX_args),
+  LOOKUP(30, ABI_optimization_goals),
+  LOOKUP(31, ABI_FP_optimization_goals),
+  {32, "compatibility", 0, NULL}
+};
+#undef LOOKUP
+
+/* Read an unsigned LEB128 encoded value from p.  Set *PLEN to the number of
+   bytes read.  */
+static unsigned int
+read_uleb128 (unsigned char *p, unsigned int *plen)
+{
+  unsigned char c;
+  unsigned int val;
+  int shift;
+  int len;
+
+  val = 0;
+  shift = 0;
+  len = 0;
+  do
+    {
+      c = *(p++);
+      len++;
+      val |= ((unsigned int)c & 0x7f) << shift;
+      shift += 7;
+    }
+  while (c & 0x80);
+
+  *plen = len;
+  return val;
+}
+
+static unsigned char *
+display_arm_attribute (unsigned char *p)
+{
+  int tag;
+  unsigned int len;
+  int val;
+  arm_attr_public_tag *attr;
+  unsigned i;
+  int type;
+
+  tag = read_uleb128 (p, &len);
+  p += len;
+  attr = NULL;
+  for (i = 0; i < ARRAY_SIZE(arm_attr_public_tags); i++)
+    {
+      if (arm_attr_public_tags[i].tag == tag)
+       {
+         attr = &arm_attr_public_tags[i];
+         break;
+       }
+    }
+
+  if (attr)
+    {
+      printf ("  Tag_%s: ", attr->name);
+      switch (attr->type)
+       {
+       case 0:
+         switch (tag)
+           {
+           case 7: /* Tag_CPU_arch_profile.  */
+             val = read_uleb128 (p, &len);
+             p += len;
+             switch (val)
+               {
+               case 0: printf ("None\n"); break;
+               case 'A': printf ("Application\n"); break;
+               case 'R': printf ("Realtime\n"); break;
+               case 'M': printf ("Microcontroller\n"); break;
+               default: printf ("??? (%d)\n", val); break;
+               }
+             break;
+
+           case 32: /* Tag_compatibility.  */
+             val = read_uleb128 (p, &len);
+             p += len;
+             printf ("flag = %d, vendor = %s\n", val, p);
+             p += strlen((char *)p) + 1;
+             break;
+
+           default:
+             abort();
+           }
+         return p;
+
+       case 1:
+       case 2:
+         type = attr->type;
+         break;
+
+       default:
+         assert (attr->type & 0x80);
+         val = read_uleb128 (p, &len);
+         p += len;
+         type = attr->type & 0x7f;
+         if (val >= type)
+           printf ("??? (%d)\n", val);
+         else
+           printf ("%s\n", attr->table[val]);
+         return p;
+       }
+    }
+  else
+    {
+      if (tag & 1)
+       type = 1; /* String.  */
+      else
+       type = 2; /* uleb128.  */
+      printf ("  Tag_unknown_%d: ", tag);
+    }
+
+  if (type == 1)
+    {
+      printf ("\"%s\"\n", p);
+      p += strlen((char *)p) + 1;
+    }
+  else
+    {
+      val = read_uleb128 (p, &len);
+      p += len;
+      printf ("%d (0x%x)\n", val, val);
+    }
+
+  return p;
+}
+
+static int
+process_arm_specific (FILE *file)
+{
+  Elf_Internal_Shdr *sect;
+  unsigned char *contents;
+  unsigned char *p;
+  unsigned char *end;
+  bfd_vma section_len;
+  bfd_vma len;
+  unsigned i;
+
+  /* Find the section header so that we get the size.  */
+  for (i = 0, sect = section_headers;
+       i < elf_header.e_shnum;
+       i++, sect++)
+    {
+      if (sect->sh_type != SHT_ARM_ATTRIBUTES)
+       continue;
+
+      contents = get_data (NULL, file, sect->sh_offset, 1, sect->sh_size,
+                          _("attributes"));
+      
+      if (!contents)
+       continue;
+      p = contents;
+      if (*p == 'A')
+       {
+         len = sect->sh_size - 1;
+         p++;
+         while (len > 0)
+           {
+             int namelen;
+             bfd_boolean public_section;
+
+             section_len = byte_get (p, 4);
+             p += 4;
+             if (section_len > len)
+               {
+                 printf (_("ERROR: Bad section length (%d > %d)\n"),
+                         (int)section_len, (int)len);
+                 section_len = len;
+               }
+             len -= section_len;
+             printf ("Attribute Section: %s\n", p);
+             if (strcmp ((char *)p, "aeabi") == 0)
+               public_section = TRUE;
+             else
+               public_section = FALSE;
+             namelen = strlen ((char *)p) + 1;
+             p += namelen;
+             section_len -= namelen + 4;
+             while (section_len > 0)
+               {
+                 int tag = *(p++);
+                 int val;
+                 bfd_vma size;
+                 size = byte_get (p, 4);
+                 if (size > section_len)
+                   {
+                     printf (_("ERROR: Bad subsection length (%d > %d)\n"),
+                             (int)size, (int)section_len);
+                     size = section_len;
+                   }
+                 section_len -= size;
+                 end = p + size - 1;
+                 p += 4;
+                 switch (tag)
+                   {
+                   case 1:
+                     printf ("File Attributes\n");
+                     break;
+                   case 2:
+                     printf ("Section Attributes:");
+                     goto do_numlist;
+                   case 3:
+                     printf ("Symbol Attributes:");
+                   do_numlist:
+                     for (;;)
+                       {
+                         unsigned int i;
+                         val = read_uleb128 (p, &i);
+                         p += i;
+                         if (val == 0)
+                           break;
+                         printf (" %d", val);
+                       }
+                     printf ("\n");
+                     break;
+                   default:
+                     printf ("Unknown tag: %d\n", tag);
+                     public_section = FALSE;
+                     break;
+                   }
+                 if (public_section)
+                   {
+                     while (p < end)
+                       p = display_arm_attribute(p);
+                   }
+                 else
+                   {
+                     /* ??? Do something sensible, like dump hex.  */
+                     printf ("  Unknown section contexts\n");
+                     p = end;
+                   }
+               }
+           }
+       }
+      else
+       {
+         printf (_("Unknown format '%c'\n"), *p);
+       }
+       
+      free(contents);
+    }
+  return 1;
+}
+
 static int
 process_mips_specific (FILE *file)
 {
@@ -8310,6 +8767,8 @@ process_arch_specific (FILE *file)
 
   switch (elf_header.e_machine)
     {
+    case EM_ARM:
+      return process_arm_specific (file);
     case EM_MIPS:
     case EM_MIPS_RS3_LE:
       return process_mips_specific (file);
@@ -8822,6 +9281,8 @@ main (int argc, char **argv)
   bindtextdomain (PACKAGE, LOCALEDIR);
   textdomain (PACKAGE);
 
+  expandargv (&argc, &argv);
+
   parse_args (argc, argv);
 
   if (num_dump_sects > 0)