]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - binutils/readelf.c
* elf32-ppc.c (ppc_elf_merge_obj_attributes): Add support for
[thirdparty/binutils-gdb.git] / binutils / readelf.c
index e166dca7c427513dc39e2e2b6ef091d85e39ca88..0ffc22b051300fa03250ea86adf590851db0685d 100644 (file)
@@ -202,6 +202,7 @@ static int do_histogram;
 static int do_debugging;
 static int do_arch;
 static int do_notes;
+static int do_archive_index;
 static int is_32bit_elf;
 
 struct group_list
@@ -383,7 +384,7 @@ byte_put_little_endian (unsigned char *field, bfd_vma value, int size)
     }
 }
 
-#if defined BFD64 && !BFD_HOST_64BIT_LONG
+#if defined BFD64 && !BFD_HOST_64BIT_LONG && !BFD_HOST_64BIT_LONG_LONG
 static int
 print_dec_vma (bfd_vma vma, int is_signed)
 {
@@ -491,6 +492,8 @@ print_vma (bfd_vma vma, print_mode mode)
        case HEX:
 #if BFD_HOST_64BIT_LONG
          return nc + printf ("%lx", vma);
+#elif BFD_HOST_64BIT_LONG_LONG
+         return nc + printf ("%llx", vma);
 #else
          return nc + print_hex_vma (vma);
 #endif
@@ -498,6 +501,8 @@ print_vma (bfd_vma vma, print_mode mode)
        case DEC:
 #if BFD_HOST_64BIT_LONG
          return printf ("%ld", vma);
+#elif BFD_HOST_64BIT_LONG_LONG
+         return printf ("%lld", vma);
 #else
          return print_dec_vma (vma, 1);
 #endif
@@ -508,6 +513,11 @@ print_vma (bfd_vma vma, print_mode mode)
            return printf ("%5ld", vma);
          else
            return printf ("%#lx", vma);
+#elif BFD_HOST_64BIT_LONG_LONG
+         if (vma <= 99999)
+           return printf ("%5lld", vma);
+         else
+           return printf ("%#llx", vma);
 #else
          if (vma <= 99999)
            return printf ("%5ld", _bfd_int64_low (vma));
@@ -518,6 +528,8 @@ print_vma (bfd_vma vma, print_mode mode)
        case UNSIGNED:
 #if BFD_HOST_64BIT_LONG
          return printf ("%lu", vma);
+#elif BFD_HOST_64BIT_LONG_LONG
+         return printf ("%llu", vma);
 #else
          return print_dec_vma (vma, 0);
 #endif
@@ -953,15 +965,23 @@ dump_relocations (FILE *file,
 
       if (is_32bit_elf)
        {
-#ifdef _bfd_int64_low
-         printf ("%8.8lx  %8.8lx ", _bfd_int64_low (offset), _bfd_int64_low (info));
-#else
-         printf ("%8.8lx  %8.8lx ", offset, info);
-#endif
+         printf ("%8.8lx  %8.8lx ",
+                 (unsigned long) offset & 0xffffffff,
+                 (unsigned long) info & 0xffffffff);
        }
       else
        {
-#ifdef _bfd_int64_low
+#if BFD_HOST_64BIT_LONG
+         printf (do_wide
+                 ? "%16.16lx  %16.16lx "
+                 : "%12.12lx  %12.12lx ",
+                 offset, info);
+#elif BFD_HOST_64BIT_LONG_LONG
+         printf (do_wide
+                 ? "%16.16llx  %16.16llx "
+                 : "%12.12llx  %12.12llx ",
+                 offset, info);
+#else
          printf (do_wide
                  ? "%8.8lx%8.8lx  %8.8lx%8.8lx "
                  : "%4.4lx%8.8lx  %4.4lx%8.8lx ",
@@ -969,11 +989,6 @@ dump_relocations (FILE *file,
                  _bfd_int64_low (offset),
                  _bfd_int64_high (info),
                  _bfd_int64_low (info));
-#else
-         printf (do_wide
-                 ? "%16.16lx  %16.16lx "
-                 : "%12.12lx  %12.12lx ",
-                 offset, info);
 #endif
        }
 
@@ -1203,11 +1218,7 @@ dump_relocations (FILE *file,
        }
 
       if (rtype == NULL)
-#ifdef _bfd_int64_low
-       printf (_("unrecognized: %-7lx"), _bfd_int64_low (type));
-#else
-       printf (_("unrecognized: %-7lx"), type);
-#endif
+       printf (_("unrecognized: %-7lx"), (unsigned long) type & 0xffffffff);
       else
        printf (do_wide ? "%-22.22s" : "%-17.17s", rtype);
 
@@ -1323,22 +1334,16 @@ dump_relocations (FILE *file,
          printf ("                    Type2: ");
 
          if (rtype2 == NULL)
-#ifdef _bfd_int64_low
-           printf (_("unrecognized: %-7lx"), _bfd_int64_low (type2));
-#else
-           printf (_("unrecognized: %-7lx"), type2);
-#endif
+           printf (_("unrecognized: %-7lx"),
+                   (unsigned long) type2 & 0xffffffff);
          else
            printf ("%-17.17s", rtype2);
 
          printf ("\n                    Type3: ");
 
          if (rtype3 == NULL)
-#ifdef _bfd_int64_low
-           printf (_("unrecognized: %-7lx"), _bfd_int64_low (type3));
-#else
-           printf (_("unrecognized: %-7lx"), type3);
-#endif
+           printf (_("unrecognized: %-7lx"),
+                   (unsigned long) type3 & 0xffffffff);
          else
            printf ("%-17.17s", rtype3);
 
@@ -2751,6 +2756,7 @@ static struct option options[] =
   {"version-info",     no_argument, 0, 'V'},
   {"use-dynamic",      no_argument, 0, 'D'},
   {"unwind",          no_argument, 0, 'u'},
+  {"archive-index",    no_argument, 0, 'c'},
   {"hex-dump",        required_argument, 0, 'x'},
   {"debug-dump",       optional_argument, 0, OPTION_DEBUG_DUMP},
   {"string-dump",      required_argument, 0, 'p'},
@@ -2787,6 +2793,7 @@ usage (FILE *stream)
   -d --dynamic           Display the dynamic section (if present)\n\
   -V --version-info      Display the version sections (if present)\n\
   -A --arch-specific     Display architecture specific information (if any).\n\
+  -c --archive-index     Display the symbol/file index in an archive\n\
   -D --use-dynamic       Use the dynamic section info when displaying symbols\n\
   -x --hex-dump=<number|name>\n\
                          Dump the contents of section <number|name> as bytes\n\
@@ -2877,7 +2884,7 @@ parse_args (int argc, char **argv)
     usage (stderr);
 
   while ((c = getopt_long
-         (argc, argv, "ersuahnldSDAINtgw::x:i:vVWHp:", options, NULL)) != EOF)
+         (argc, argv, "ADHINSVWacdeghi:lnp:rstuvw::x:", options, NULL)) != EOF)
     {
       char *cp;
       int section;
@@ -2951,6 +2958,9 @@ parse_args (int argc, char **argv)
        case 'n':
          do_notes++;
          break;
+       case 'c':
+         do_archive_index++;
+         break;
        case 'x':
          do_dump++;
          section = strtoul (optarg, & cp, 0);
@@ -3139,7 +3149,7 @@ parse_args (int argc, char **argv)
   if (!do_dynamic && !do_syms && !do_reloc && !do_unwind && !do_sections
       && !do_segments && !do_header && !do_dump && !do_version
       && !do_histogram && !do_debugging && !do_arch && !do_notes
-      && !do_section_groups)
+      && !do_section_groups && !do_archive_index)
     usage (stderr);
   else if (argc < 3)
     {
@@ -3520,6 +3530,11 @@ process_program_headers (FILE *file)
          if (dynamic_addr)
            error (_("more than one dynamic segment\n"));
 
+         /* By default, assume that the .dynamic section is the first
+            section in the DYNAMIC segment.  */
+         dynamic_addr = segment->p_offset;
+         dynamic_size = segment->p_filesz;
+
          /* Try to locate the .dynamic section. If there is
             a section header table, we can easily locate it.  */
          if (section_headers != NULL)
@@ -3534,23 +3549,21 @@ process_program_headers (FILE *file)
                }
 
              if (sec->sh_type == SHT_NOBITS)
-               break;
+               {
+                 dynamic_size = 0;
+                 break;
+               }
 
              dynamic_addr = sec->sh_offset;
              dynamic_size = sec->sh_size;
 
              if (dynamic_addr < segment->p_offset
                  || dynamic_addr > segment->p_offset + segment->p_filesz)
-               warn (_("the .dynamic section is not contained within the dynamic segment\n"));
+               warn (_("the .dynamic section is not contained"
+                       " within the dynamic segment\n"));
              else if (dynamic_addr > segment->p_offset)
-               warn (_("the .dynamic section is not the first section in the dynamic segment.\n"));
-           }
-         else
-           {
-             /* Otherwise, we can only assume that the .dynamic
-                section is the first section in the DYNAMIC segment.  */
-             dynamic_addr = segment->p_offset;
-             dynamic_size = segment->p_filesz;
+               warn (_("the .dynamic section is not the first section"
+                       " in the dynamic segment.\n"));
            }
          break;
 
@@ -6454,6 +6467,7 @@ process_version_sections (FILE *file)
            Elf_External_Verdef *edefs;
            unsigned int idx;
            unsigned int cnt;
+           char *endbuf;
 
            found = 1;
 
@@ -6473,6 +6487,7 @@ process_version_sections (FILE *file)
            edefs = get_data (NULL, file, section->sh_offset, 1,
                              section->sh_size,
                              _("version definition section"));
+           endbuf = (char *) edefs + section->sh_size;
            if (!edefs)
              break;
 
@@ -6487,6 +6502,8 @@ process_version_sections (FILE *file)
                int isum;
 
                vstart = ((char *) edefs) + idx;
+               if (vstart + sizeof (*edef) > endbuf)
+                 break;
 
                edef = (Elf_External_Verdef *) vstart;
 
@@ -6524,6 +6541,8 @@ process_version_sections (FILE *file)
                    vstart += aux.vda_next;
 
                    eaux = (Elf_External_Verdaux *) vstart;
+                   if (vstart + sizeof (*eaux) > endbuf)
+                     break;
 
                    aux.vda_name = BYTE_GET (eaux->vda_name);
                    aux.vda_next = BYTE_GET (eaux->vda_next);
@@ -6535,9 +6554,13 @@ process_version_sections (FILE *file)
                      printf (_("  %#06x: Parent %d, name index: %ld\n"),
                              isum, j, aux.vda_name);
                  }
+               if (j < ent.vd_cnt)
+                 printf (_("  Version def aux past end of section\n"));
 
                idx += ent.vd_next;
              }
+           if (cnt < section->sh_info)
+             printf (_("  Version definition past end of section\n"));
 
            free (edefs);
          }
@@ -6548,6 +6571,7 @@ process_version_sections (FILE *file)
            Elf_External_Verneed *eneed;
            unsigned int idx;
            unsigned int cnt;
+           char *endbuf;
 
            found = 1;
 
@@ -6566,6 +6590,7 @@ process_version_sections (FILE *file)
            eneed = get_data (NULL, file, section->sh_offset, 1,
                              section->sh_size,
                              _("version need section"));
+           endbuf = (char *) eneed + section->sh_size;
            if (!eneed)
              break;
 
@@ -6578,6 +6603,8 @@ process_version_sections (FILE *file)
                char *vstart;
 
                vstart = ((char *) eneed) + idx;
+               if (vstart + sizeof (*entry) > endbuf)
+                 break;
 
                entry = (Elf_External_Verneed *) vstart;
 
@@ -6603,6 +6630,8 @@ process_version_sections (FILE *file)
                    Elf_External_Vernaux *eaux;
                    Elf_Internal_Vernaux aux;
 
+                   if (vstart + sizeof (*eaux) > endbuf)
+                     break;
                    eaux = (Elf_External_Vernaux *) vstart;
 
                    aux.vna_hash  = BYTE_GET (eaux->vna_hash);
@@ -6624,9 +6653,13 @@ process_version_sections (FILE *file)
                    isum   += aux.vna_next;
                    vstart += aux.vna_next;
                  }
+               if (j < ent.vn_cnt)
+                 printf (_("  Version need aux past end of section\n"));
 
                idx += ent.vn_next;
              }
+           if (cnt < section->sh_info)
+             printf (_("  Version need past end of section\n"));
 
            free (eneed);
          }
@@ -6771,7 +6804,10 @@ process_version_sections (FILE *file)
                                {
                                  ivna.vna_name = BYTE_GET (evna.vna_name);
 
-                                 name = strtab + ivna.vna_name;
+                                 if (ivna.vna_name >= string_sec->sh_size)
+                                   name = _("*invalid*");
+                                 else
+                                   name = strtab + ivna.vna_name;
                                  nn += printf ("(%s%-*s",
                                                name,
                                                12 - (int) strlen (name),
@@ -6823,7 +6859,10 @@ process_version_sections (FILE *file)
 
                              ivda.vda_name = BYTE_GET (evda.vda_name);
 
-                             name = strtab + ivda.vda_name;
+                             if (ivda.vda_name >= string_sec->sh_size)
+                               name = _("*invalid*");
+                             else
+                               name = strtab + ivda.vda_name;
                              nn += printf ("(%s%-*s",
                                            name,
                                            12 - (int) strlen (name),
@@ -7048,6 +7087,39 @@ get_dynamic_data (FILE *file, unsigned int number, unsigned int ent_size)
   return i_data;
 }
 
+static void
+print_dynamic_symbol (bfd_vma si, unsigned long hn)
+{
+  Elf_Internal_Sym *psym;
+  int n;
+
+  psym = dynamic_symbols + si;
+
+  n = print_vma (si, DEC_5);
+  if (n < 5)
+    fputs ("     " + n, stdout);
+  printf (" %3lu: ", hn);
+  print_vma (psym->st_value, LONG_HEX);
+  putchar (' ');
+  print_vma (psym->st_size, DEC_5);
+
+  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));
+  else
+    printf (" <corrupt: %14ld>", psym->st_name);
+  putchar ('\n');
+}
+
 /* Dump the symbol table.  */
 static int
 process_symbol_table (FILE *file)
@@ -7060,12 +7132,14 @@ process_symbol_table (FILE *file)
   bfd_vma ngnubuckets = 0;
   bfd_vma *gnubuckets = NULL;
   bfd_vma *gnuchains = NULL;
+  bfd_vma gnusymidx = 0;
 
   if (! do_syms && !do_histogram)
     return 1;
 
-  if (dynamic_info[DT_HASH] && ((do_using_dynamic && dynamic_strings != NULL)
-                               || do_histogram))
+  if (dynamic_info[DT_HASH]
+      && (do_histogram
+         || (do_using_dynamic && dynamic_strings != NULL)))
     {
       unsigned char nb[8];
       unsigned char nc[8];
@@ -7109,54 +7183,157 @@ process_symbol_table (FILE *file)
        return 0;
     }
 
-  if (do_syms
-      && dynamic_info[DT_HASH] && do_using_dynamic && dynamic_strings != NULL)
+  if (dynamic_info_DT_GNU_HASH
+      && (do_histogram
+         || (do_using_dynamic && dynamic_strings != NULL)))
     {
-      unsigned long hn;
-      bfd_vma si;
+      unsigned char nb[16];
+      bfd_vma i, maxchain = 0xffffffff, bitmaskwords;
+      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\n"));
+         return 0;
+       }
 
-      printf (_("\nSymbol table for image:\n"));
+      if (fread (nb, 16, 1, file) != 1)
+       {
+         error (_("Failed to read in number of buckets\n"));
+         return 0;
+       }
+
+      ngnubuckets = byte_get (nb, 4);
+      gnusymidx = byte_get (nb + 4, 4);
+      bitmaskwords = byte_get (nb + 8, 4);
+      buckets_vma = dynamic_info_DT_GNU_HASH + 16;
       if (is_32bit_elf)
-       printf (_("  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name\n"));
+       buckets_vma += bitmaskwords * 4;
       else
-       printf (_("  Num Buc:    Value          Size   Type   Bind Vis      Ndx Name\n"));
+       buckets_vma += bitmaskwords * 8;
 
-      for (hn = 0; hn < nbuckets; hn++)
+      if (fseek (file,
+                (archive_file_offset
+                 + offset_from_vma (file, buckets_vma, 4)),
+                SEEK_SET))
        {
-         if (! buckets[hn])
-           continue;
+         error (_("Unable to seek to start of dynamic information\n"));
+         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] < gnusymidx)
+             return 0;
+
+           if (maxchain == 0xffffffff || gnubuckets[i] > maxchain)
+             maxchain = gnubuckets[i];
+         }
+
+      if (maxchain == 0xffffffff)
+       return 0;
+
+      maxchain -= gnusymidx;
 
-         for (si = buckets[hn]; si < nchains && si > 0; si = chains[si])
+      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\n"));
+         return 0;
+       }
+
+      do
+       {
+         if (fread (nb, 4, 1, file) != 1)
            {
-             Elf_Internal_Sym *psym;
-             int n;
+             error (_("Failed to determine last chain length\n"));
+             return 0;
+           }
 
-             psym = dynamic_symbols + si;
+         if (maxchain + 1 == 0)
+           return 0;
 
-             n = print_vma (si, DEC_5);
-             if (n < 5)
-               fputs ("     " + n, stdout);
-             printf (" %3lu: ", hn);
-             print_vma (psym->st_value, LONG_HEX);
-             putchar (' ');
-             print_vma (psym->st_size, DEC_5);
+         ++maxchain;
+       }
+      while ((byte_get (nb, 4) & 1) == 0);
 
-             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));
-             else
-               printf (" <corrupt: %14ld>", psym->st_name);
-             putchar ('\n');
+      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\n"));
+         return 0;
+       }
+
+      gnuchains = get_dynamic_data (file, maxchain, 4);
+
+      if (gnuchains == NULL)
+       return 0;
+    }
+
+  if ((dynamic_info[DT_HASH] || dynamic_info_DT_GNU_HASH)
+      && do_syms
+      && do_using_dynamic
+      && dynamic_strings != NULL)
+    {
+      unsigned long hn;
+
+      if (dynamic_info[DT_HASH])
+       {
+         bfd_vma si;
+
+         printf (_("\nSymbol table for image:\n"));
+         if (is_32bit_elf)
+           printf (_("  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name\n"));
+         else
+           printf (_("  Num Buc:    Value          Size   Type   Bind Vis      Ndx Name\n"));
+
+         for (hn = 0; hn < nbuckets; hn++)
+           {
+             if (! buckets[hn])
+               continue;
+
+             for (si = buckets[hn]; si < nchains && si > 0; si = chains[si])
+               print_dynamic_symbol (si, hn);
            }
        }
+
+      if (dynamic_info_DT_GNU_HASH)
+       {
+         printf (_("\nSymbol table of `.gnu.hash' for image:\n"));
+         if (is_32bit_elf)
+           printf (_("  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name\n"));
+         else
+           printf (_("  Num Buc:    Value          Size   Type   Bind Vis      Ndx Name\n"));
+
+         for (hn = 0; hn < ngnubuckets; ++hn)
+           if (gnubuckets[hn] != 0)
+             {
+               bfd_vma si = gnubuckets[hn];
+               bfd_vma off = si - gnusymidx;
+
+               do
+                 {
+                   print_dynamic_symbol (si, hn);
+                   si++;
+                 }
+               while ((gnuchains[off++] & 1) == 0);
+             }
+       }
     }
   else if (do_syms && !do_using_dynamic)
     {
@@ -7441,108 +7618,12 @@ process_symbol_table (FILE *file)
 
   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\n"));
-         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\n"));
-         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\n"));
-         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\n"));
-         return 0;
-       }
-
-      gnuchains = get_dynamic_data (file, maxchain, 4);
-
-      if (gnuchains == NULL)
-       return 0;
 
       lengths = calloc (ngnubuckets, sizeof (*lengths));
       if (lengths == NULL)
@@ -7560,7 +7641,7 @@ process_symbol_table (FILE *file)
          {
            bfd_vma off, length = 1;
 
-           for (off = gnubuckets[hn] - symidx;
+           for (off = gnubuckets[hn] - gnusymidx;
                 (gnuchains[off] & 1) == 0; ++off)
              ++length;
            lengths[hn] = length;
@@ -7742,7 +7823,7 @@ dump_section_as_strings (Elf_Internal_Shdr *section, FILE *file)
 
       if (data < end)
        {
-         printf ("  [%6zx]  %s\n", data - start, data);
+         printf ("  [%6tx]  %s\n", data - start, data);
          data += strlen (data);
          some_strings_shown = TRUE;
        }
@@ -8472,6 +8553,32 @@ display_power_gnu_attribute (unsigned char *p, int tag)
       return p;
    }
 
+  if (tag == Tag_GNU_Power_ABI_Vector)
+    {
+      val = read_uleb128 (p, &len);
+      p += len;
+      printf ("  Tag_GNU_Power_ABI_Vector: ");
+      switch (val)
+       {
+       case 0:
+         printf ("Any\n");
+         break;
+       case 1:
+         printf ("Generic\n");
+         break;
+       case 2:
+         printf ("AltiVec\n");
+         break;
+       case 3:
+         printf ("SPE\n");
+         break;
+       default:
+         printf ("??? (%d)\n", val);
+         break;
+       }
+      return p;
+   }
+
   if (tag & 1)
     type = 1; /* String.  */
   else
@@ -9202,6 +9309,8 @@ get_note_type (unsigned e_type)
        return _("NT_TASKSTRUCT (task structure)");
       case NT_PRXFPREG:
        return _("NT_PRXFPREG (user_xfpregs structure)");
+      case NT_PPC_VMX:
+       return _("NT_PPC_VMX (ppc Altivec registers)");
       case NT_PSTATUS:
        return _("NT_PSTATUS (pstatus structure)");
       case NT_FPREGS:
@@ -9766,8 +9875,8 @@ process_object (char *file_name, FILE *file)
   return 0;
 }
 
-/* Process an ELF archive.  The file is positioned just after the
-   ARMAG string.  */
+/* Process an ELF archive.
+   On entry the file is positioned just after the ARMAG string.  */
 
 static int
 process_archive (char *file_name, FILE *file)
@@ -9775,6 +9884,10 @@ process_archive (char *file_name, FILE *file)
   struct ar_hdr arhdr;
   size_t got;
   unsigned long size;
+  unsigned long index_num = 0;
+  unsigned long *index_array = NULL;
+  char *sym_table = NULL;
+  unsigned long sym_size = 0;
   char *longnames = NULL;
   unsigned long longnames_size = 0;
   size_t file_name_size;
@@ -9792,28 +9905,124 @@ process_archive (char *file_name, FILE *file)
       return 1;
     }
 
+  /* See if this is the archive symbol table.  */
   if (const_strneq (arhdr.ar_name, "/               ")
       || const_strneq (arhdr.ar_name, "/SYM64/         "))
     {
-      /* This is the archive symbol table.  Skip it.
-        FIXME: We should have an option to dump it.  */
       size = strtoul (arhdr.ar_size, NULL, 10);
-      if (fseek (file, size + (size & 1), SEEK_CUR) != 0)
+      size = size + (size & 1);
+
+      if (do_archive_index)
+       {
+         unsigned long i;
+         /* A buffer used to hold numbers read in from an archive index.
+            These are always 4 bytes long and stored in big-endian format.  */
+#define SIZEOF_AR_INDEX_NUMBERS 4
+         unsigned char integer_buffer[SIZEOF_AR_INDEX_NUMBERS];
+         unsigned char * index_buffer;
+
+         /* Check the size of the archive index.  */
+         if (size < SIZEOF_AR_INDEX_NUMBERS)
+           {
+             error (_("%s: the archive index is empty\n"), file_name);
+             return 1;
+           }
+
+         /* Read the numer of entries in the archive index.  */
+         got = fread (integer_buffer, 1, sizeof integer_buffer, file);
+         if (got != sizeof (integer_buffer))
+           {
+             error (_("%s: failed to read archive index\n"), file_name);
+             return 1;
+           }
+         index_num = byte_get_big_endian (integer_buffer, sizeof integer_buffer);
+         size -= SIZEOF_AR_INDEX_NUMBERS;
+
+         /* Read in the archive index.  */
+         if (size < index_num * SIZEOF_AR_INDEX_NUMBERS)
+           {
+             error (_("%s: the archive index is supposed to have %ld entries, but the size in the header is too small\n"),
+                    file_name, index_num);
+             return 1;
+           }
+         index_buffer = malloc (index_num * SIZEOF_AR_INDEX_NUMBERS);
+         if (index_buffer == NULL)
+           {
+             error (_("Out of memory whilst trying to read archive symbol index\n"));
+             return 1;
+           }
+         got = fread (index_buffer, SIZEOF_AR_INDEX_NUMBERS, index_num, file);
+         if (got != index_num)
+           {
+             free (index_buffer);
+             error (_("%s: failed to read archive index\n"), file_name);
+             ret = 1;
+             goto out;
+           }
+         size -= index_num * SIZEOF_AR_INDEX_NUMBERS;
+
+         /* Convert the index numbers into the host's numeric format.  */
+         index_array = malloc (index_num * sizeof (* index_array));      
+         if (index_array == NULL)
+           {
+             free (index_buffer);
+             error (_("Out of memory whilst trying to convert the archive symbol index\n"));
+             return 1;
+           }
+
+         for (i = 0; i < index_num; i++)
+           index_array[i] = byte_get_big_endian ((unsigned char *)(index_buffer + (i * SIZEOF_AR_INDEX_NUMBERS)),
+                                                 SIZEOF_AR_INDEX_NUMBERS);
+         free (index_buffer);
+
+         /* The remaining space in the header is taken up by the symbol table.  */
+         if (size < 1)
+           {
+             error (_("%s: the archive has an index but no symbols\n"), file_name);
+             ret = 1;
+             goto out;
+           }
+         sym_table = malloc (size);
+         sym_size = size;
+         if (sym_table == NULL)
+           {
+             error (_("Out of memory whilst trying to read archive index symbol table\n"));
+             ret = 1;
+             goto out;
+           }
+         got = fread (sym_table, 1, size, file);
+         if (got != size)
+           {
+             error (_("%s: failed to read archive index symbol table\n"), file_name);
+             ret = 1;
+             goto out;
+           }     
+       }
+      else
        {
-         error (_("%s: failed to skip archive symbol table\n"), file_name);
-         return 1;
+         if (fseek (file, size, SEEK_CUR) != 0)
+           {
+             error (_("%s: failed to skip archive symbol table\n"), file_name);
+             return 1;
+           }
        }
 
-      got = fread (&arhdr, 1, sizeof arhdr, file);
+      got = fread (& arhdr, 1, sizeof arhdr, file);
       if (got != sizeof arhdr)
        {
          if (got == 0)
-           return 0;
+           {
+             ret = 0;
+             goto out;
+           }
 
-         error (_("%s: failed to read archive header\n"), file_name);
-         return 1;
+         error (_("%s: failed to read archive header following archive index\n"), file_name);
+         ret = 1;
+         goto out;
        }
     }
+  else if (do_archive_index)
+    printf (_("%s has no archive index\n"), file_name);
 
   if (const_strneq (arhdr.ar_name, "//              "))
     {
@@ -9821,35 +10030,120 @@ process_archive (char *file_name, FILE *file)
         names.  */
 
       longnames_size = strtoul (arhdr.ar_size, NULL, 10);
-
       longnames = malloc (longnames_size);
       if (longnames == NULL)
        {
-         error (_("Out of memory\n"));
-         return 1;
+         error (_("Out of memory reading long symbol names in archive\n"));
+         ret = 1;
+         goto out;
        }
 
       if (fread (longnames, longnames_size, 1, file) != 1)
        {
          free (longnames);
-         error (_("%s: failed to read string table\n"), file_name);
-         return 1;
+         error (_("%s: failed to read long symbol name string table\n"), file_name);
+         ret = 1;
+         goto out;
        }
 
       if ((longnames_size & 1) != 0)
        getc (file);
 
-      got = fread (&arhdr, 1, sizeof arhdr, file);
+      got = fread (& arhdr, 1, sizeof arhdr, file);
       if (got != sizeof arhdr)
        {
-         free (longnames);
-
          if (got == 0)
-           return 0;
+           ret = 0;
+         else
+           {
+             error (_("%s: failed to read archive header following long symbol names\n"), file_name);
+             ret = 1;
+           }
+         goto out;
+       }
+    }
 
-         error (_("%s: failed to read archive header\n"), file_name);
-         return 1;
+  if (do_archive_index)
+    {
+      if (sym_table == NULL)
+       error (_("%s: unable to dump the index as none was found\n"), file_name);
+      else
+       {
+         unsigned int i, j, k, l;
+         char elf_name[16];
+         unsigned long current_pos;
+
+         printf (_("Index of archive %s: (%ld entries, 0x%lx bytes in the symbol table)\n"),
+                 file_name, index_num, sym_size);
+         current_pos = ftell (file);
+
+         for (i = l = 0; i < index_num; i++)
+           {
+             if ((i == 0) || ((i > 0) && (index_array[i] != index_array[i - 1])))
+               {
+                 if (fseek (file, index_array[i], SEEK_SET) != 0)
+                   {
+                     error (_("%s: failed to seek to next file name\n"), file_name);
+                     ret = 1;
+                     goto out;
+                   }
+                 got = fread (elf_name, 1, 16, file);
+                 if (got != 16)
+                   {
+                     error (_("%s: failed to read file name\n"), file_name);
+                     ret = 1;
+                     goto out;
+                   }
+
+                 if (elf_name[0] == '/')
+                   {
+                     /* We have a long name.  */
+                     k = j = strtoul (elf_name + 1, NULL, 10);
+                     while ((j < longnames_size) && (longnames[j] != '/'))
+                       j++;
+                     longnames[j] = '\0';
+                     printf (_("Binary %s contains:\n"), longnames + k);
+                     longnames[j] = '/';
+                   }
+                 else
+                   {
+                     j = 0;
+                     while ((elf_name[j] != '/') && (j < 16))
+                       j++;
+                     elf_name[j] = '\0';
+                     printf(_("Binary %s contains:\n"), elf_name);
+                   }
+               }
+             if (l >= sym_size)
+               {
+                 error (_("%s: end of the symbol table reached before the end of the index\n"),
+                        file_name);
+                 break;                         
+               }
+             printf ("\t%s\n", sym_table + l);
+             l += strlen (sym_table + l) + 1;
+           }
+
+         if (l < sym_size)
+           error (_("%s: symbols remain in the index symbol table, but without corresponding entries in the index table\n"),
+                  file_name);
+
+         free (index_array);
+         index_array = NULL;
+         free (sym_table);
+         sym_table = NULL;
+         if (fseek (file, current_pos, SEEK_SET) != 0)
+           {
+             error (_("%s: failed to seek back to start of object files in the archive\n"), file_name);
+             return 1;
+           }
        }
+
+      if (!do_dynamic && !do_syms && !do_reloc && !do_unwind && !do_sections
+         && !do_segments && !do_header && !do_dump && !do_version
+         && !do_histogram && !do_debugging && !do_arch && !do_notes
+         && !do_section_groups)
+       return 0; /* Archive index only.  */
     }
 
   file_name_size = strlen (file_name);
@@ -9933,7 +10227,12 @@ process_archive (char *file_name, FILE *file)
        }
     }
 
-  if (longnames != 0)
+ out:
+  if (index_array != NULL)
+    free (index_array);
+  if (sym_table != NULL)
+    free (sym_table);
+  if (longnames != NULL)
     free (longnames);
 
   return ret;
@@ -9972,7 +10271,7 @@ process_file (char *file_name)
 
   if (fread (armag, SARMAG, 1, file) != 1)
     {
-      error (_("%s: Failed to read file header\n"), file_name);
+      error (_("%s: Failed to read file's magic number\n"), file_name);
       fclose (file);
       return 1;
     }
@@ -9981,6 +10280,10 @@ process_file (char *file_name)
     ret = process_archive (file_name, file);
   else
     {
+      if (do_archive_index)
+       error (_("File %s is not an archive so its index cannot be displayed.\n"),
+              file_name);
+
       rewind (file);
       archive_file_size = archive_file_offset = 0;
       ret = process_object (file_name, file);