Add ability for readelf to parse archives
authorNick Clifton <nickc@redhat.com>
Mon, 11 Aug 2003 09:15:55 +0000 (09:15 +0000)
committerNick Clifton <nickc@redhat.com>
Mon, 11 Aug 2003 09:15:55 +0000 (09:15 +0000)
binutils/ChangeLog
binutils/Makefile.am
binutils/Makefile.in
binutils/NEWS
binutils/doc/binutils.texi
binutils/readelf.c

index 7b8db96932f9240b6a92cd3d5f099ecb64eb0b5f..e0e1d138483b9cedeb98633d6edfd2269955df92 100644 (file)
@@ -1,3 +1,25 @@
+2003-08-11  Ian Lance Taylor <ian@airs.com>
+
+       * readelf.c: Add ability to read archives.
+       (archive_file_offset): New variable.
+       (archive_file_size): New variable.
+       (get_data): Include archive_file_offset in file offset
+       calculation when fseeking.
+       (process_program_headers): Likewise.
+       (process_symbol_table): Likewise.
+       (process_dynamic_segment): Handle computation of end of file
+       position when the file is in an archive.
+       (process_object): New function.  Contains the body of
+       process_file().
+       (process_archive): New function.  Call process_object on each
+       member of an archive.
+       (process_file): Detect archives and handle appropriately.
+       * Makefile.am: Add dependency on aout/ar.h for readelf.c
+       * Makefile.in: Regenerate.
+       * NEWS: Document readelf's new ability.
+       * doc/binutils: Alter text to say that readelf supports archives
+       and 64-bit ELF files.
+       
 2003-08-08  Nick Clifton  <nickc@redhat.com>
 
        * po/fr.po: Updated French translation.
index 1d5273852cf5c6642360658a9ac22df208211782..63174e41ad22588e8b290f76ded2c02282fbc702 100644 (file)
@@ -477,7 +477,7 @@ readelf.o: readelf.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \
   $(INCDIR)/elf/sparc.h $(INCDIR)/elf/v850.h $(INCDIR)/elf/vax.h \
   $(INCDIR)/elf/x86-64.h $(INCDIR)/elf/xstormy16.h $(INCDIR)/elf/iq2000.h \
   bucomm.h config.h $(INCDIR)/bin-bugs.h $(INCDIR)/fopen-same.h \
-  unwind-ia64.h
+  unwind-ia64.h $(INCDIR)/aout/ar.h
 rename.o: rename.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \
   $(INCDIR)/symcat.h bucomm.h config.h $(INCDIR)/bin-bugs.h \
   $(INCDIR)/fopen-same.h
index 7005f08d64f807229ad27d3ac481cb03a4ecd44f..96c9176f835f9d3b7e5ee8783903e38ca677a1ef 100644 (file)
@@ -1,4 +1,4 @@
-# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am
+# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
 
 # Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
 # This Makefile.in is free software; the Free Software Foundation
@@ -442,7 +442,7 @@ configure.in deflex.c defparse.c nlmheader.c rclex.c rcparse.c
 
 DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
 
-TAR = tar
+TAR = gtar
 GZIP_ENV = --best
 SOURCES = $(nlmconv_SOURCES) $(srconv_SOURCES) $(sysdump_SOURCES) $(coffdump_SOURCES) $(dlltool_SOURCES) $(windres_SOURCES) $(dllwrap_SOURCES) $(size_SOURCES) $(objdump_SOURCES) $(ar_SOURCES) $(strings_SOURCES) $(ranlib_SOURCES) $(objcopy_SOURCES) $(addr2line_SOURCES) $(readelf_SOURCES) $(nm_new_SOURCES) $(strip_new_SOURCES) $(cxxfilt_SOURCES)
 OBJECTS = $(nlmconv_OBJECTS) $(srconv_OBJECTS) $(sysdump_OBJECTS) $(coffdump_OBJECTS) $(dlltool_OBJECTS) $(windres_OBJECTS) $(dllwrap_OBJECTS) $(size_OBJECTS) $(objdump_OBJECTS) $(ar_OBJECTS) $(strings_OBJECTS) $(ranlib_OBJECTS) $(objcopy_OBJECTS) $(addr2line_OBJECTS) $(readelf_OBJECTS) $(nm_new_OBJECTS) $(strip_new_OBJECTS) $(cxxfilt_OBJECTS)
@@ -870,7 +870,7 @@ distclean-generic:
        -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
 
 maintainer-clean-generic:
-       -test -z "arlex.cdeflex.crclex.carparse.harparse.cdefparse.hdefparse.cnlmheader.hnlmheader.crcparse.hrcparse.c" || rm -f arlex.c deflex.c rclex.c arparse.h arparse.c defparse.h defparse.c nlmheader.h nlmheader.c rcparse.h rcparse.c
+       -test -z "arlexldeflexlrclexlarparseharparsecdefparsehdefparsecnlmheaderhnlmheadercrcparsehrcparsec" || rm -f arlexl deflexl rclexl arparseh arparsec defparseh defparsec nlmheaderh nlmheaderc rcparseh rcparsec
 mostlyclean-am:  mostlyclean-hdr mostlyclean-binPROGRAMS \
                mostlyclean-noinstPROGRAMS mostlyclean-compile \
                mostlyclean-libtool mostlyclean-tags \
@@ -1205,7 +1205,7 @@ readelf.o: readelf.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \
   $(INCDIR)/elf/sparc.h $(INCDIR)/elf/v850.h $(INCDIR)/elf/vax.h \
   $(INCDIR)/elf/x86-64.h $(INCDIR)/elf/xstormy16.h $(INCDIR)/elf/iq2000.h \
   bucomm.h config.h $(INCDIR)/bin-bugs.h $(INCDIR)/fopen-same.h \
-  unwind-ia64.h
+  unwind-ia64.h $(INCDIR)/aout/ar.h
 rename.o: rename.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \
   $(INCDIR)/symcat.h bucomm.h config.h $(INCDIR)/bin-bugs.h \
   $(INCDIR)/fopen-same.h
index 0a8c0cfa0fcbdb587776514c243636c8bb92a1b2..3fa22e818b5eb1e748686e17ab6589756ea97d7a 100644 (file)
@@ -1,5 +1,7 @@
 -*- text -*-
 
+* readelf can now parse archives.
+
 * objdump now accepts --debugging-tags to print the debug information in a
   format compatible with ctags tool.
 
index f86169d652b74d812e2663ff70a11b6a30a27766..ce9dcf0e17e01b31af743c5ae19a218c8947a797 100644 (file)
@@ -3078,9 +3078,8 @@ readelf [@option{-a}|@option{--all}]
 @command{readelf} displays information about one or more ELF format object
 files.  The options control what particular information to display.
 
-@var{elffile}@dots{} are the object files to be examined.  At the
-moment, @command{readelf} does not support examining archives, nor does it
-support examining 64 bit ELF files.
+@var{elffile}@dots{} are the object files to be examined.  32-bit and
+64-bit ELF files are supported, as are archives containing ELF files.
 
 @c man end
 
index b2366a6a45f056499b1c70a9c70e9ff1fcedb6d7..25f5de67ea3c9e6851b6dce57a1dc2d5b1aa3bb6 100644 (file)
 #include "elf/iq2000.h"
 #include "elf/xtensa.h"
 
+#include "aout/ar.h"
+
 #include "bucomm.h"
 #include "getopt.h"
 #include "libiberty.h"
 
 char *program_name = "readelf";
+long archive_file_offset;
+unsigned long archive_file_size;
 unsigned long dynamic_addr;
 bfd_size_type dynamic_size;
 char *dynamic_strings;
@@ -244,9 +248,10 @@ get_data (void *var, FILE *file, long offset, size_t size, const char *reason)
   if (size == 0)
     return NULL;
 
-  if (fseek (file, offset, SEEK_SET))
+  if (fseek (file, archive_file_offset + offset, SEEK_SET))
     {
-      error (_("Unable to seek to 0x%x for %s\n"), offset, reason);
+      error (_("Unable to seek to 0x%x for %s\n"),
+            archive_file_offset + offset, reason);
       return NULL;
     }
 
@@ -3064,7 +3069,8 @@ process_program_headers (FILE *file)
          break;
 
        case PT_INTERP:
-         if (fseek (file, (long) segment->p_offset, SEEK_SET))
+         if (fseek (file, archive_file_offset + (long) segment->p_offset,
+                    SEEK_SET))
            error (_("Unable to find program interpreter name\n"));
          else
            {
@@ -4503,10 +4509,16 @@ process_dynamic_segment (FILE *file)
             should work.  */
          section.sh_offset = offset_from_vma (file, entry->d_un.d_val, 0);
 
-         if (fseek (file, 0, SEEK_END))
-           error (_("Unable to seek to end of file!"));
+         if (archive_file_offset != 0)
+           section.sh_size = archive_file_size - section.sh_offset;
+         else
+           {
+             if (fseek (file, 0, SEEK_END))
+               error (_("Unable to seek to end of file!"));
+
+             section.sh_size = ftell (file) - section.sh_offset;
+           }
 
-         section.sh_size = ftell (file) - section.sh_offset;
          if (is_32bit_elf)
            section.sh_entsize = sizeof (Elf32_External_Sym);
          else
@@ -4544,9 +4556,15 @@ process_dynamic_segment (FILE *file)
             should work.  */
 
          offset = offset_from_vma (file, entry->d_un.d_val, 0);
-         if (fseek (file, 0, SEEK_END))
-           error (_("Unable to seek to end of file\n"));
-         str_tab_len = ftell (file) - offset;
+
+         if (archive_file_offset != 0)
+           str_tab_len = archive_file_size - offset;
+         else
+           {
+             if (fseek (file, 0, SEEK_END))
+               error (_("Unable to seek to end of file\n"));
+             str_tab_len = ftell (file) - offset;
+           }
 
          if (str_tab_len < 1)
            {
@@ -5598,8 +5616,10 @@ process_symbol_table (FILE *file)
   if (dynamic_info[DT_HASH] && ((do_using_dynamic && dynamic_strings != NULL)
                                || do_histogram))
     {
-      if (fseek (file, offset_from_vma (file, dynamic_info[DT_HASH],
-                                       sizeof nb + sizeof nc),
+      if (fseek (file,
+                (archive_file_offset
+                 + offset_from_vma (file, dynamic_info[DT_HASH],
+                                    sizeof nb + sizeof nc)),
                 SEEK_SET))
        {
          error (_("Unable to seek to start of dynamic information"));
@@ -10142,30 +10162,18 @@ get_file_header (FILE *file)
   return 1;
 }
 
+/* Process one ELF object file according to the command line options.
+   This file may actually be stored in an archive.  The file is
+   positioned at the start of the ELF object.  */
+
 static int
-process_file (char *file_name)
+process_object (char *file_name, FILE *file)
 {
-  FILE *file;
-  struct stat statbuf;
   unsigned int i;
 
-  if (stat (file_name, & statbuf) < 0)
-    {
-      error (_("Cannot stat input file %s.\n"), file_name);
-      return 1;
-    }
-
-  file = fopen (file_name, "rb");
-  if (file == NULL)
-    {
-      error (_("Input file %s not found.\n"), file_name);
-      return 1;
-    }
-
   if (! get_file_header (file))
     {
       error (_("%s: Failed to read file header\n"), file_name);
-      fclose (file);
       return 1;
     }
 
@@ -10181,10 +10189,7 @@ process_file (char *file_name)
     printf (_("\nFile: %s\n"), file_name);
 
   if (! process_file_header ())
-    {
-      fclose (file);
-      return 1;
-    }
+    return 1;
 
   if (! process_section_headers (file))
     {
@@ -10217,8 +10222,6 @@ process_file (char *file_name)
 
   process_arch_specific (file);
 
-  fclose (file);
-
   if (program_headers)
     {
       free (program_headers);
@@ -10260,6 +10263,210 @@ process_file (char *file_name)
   return 0;
 }
 
+/* Process an ELF archive.  The file is positioned just after the
+   ARMAG string.  */
+
+static int
+process_archive (char *file_name, FILE *file)
+{
+  struct ar_hdr arhdr;
+  size_t got;
+  unsigned long size;
+  char *longnames = NULL;
+  unsigned long longnames_size = 0;
+  size_t file_name_size;
+
+  show_name = 1;
+
+  got = fread (&arhdr, 1, sizeof arhdr, file);
+  if (got != sizeof arhdr)
+    {
+      if (got == 0)
+       return 0;
+
+      error (_("%s: failed to read archive header\n"), file_name);
+      return 1;
+    }
+
+  if (memcmp (arhdr.ar_name, "/               ", 16) == 0)
+    {
+      /* 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)
+       {
+         error (_("%s: failed to skip archive symbol table\n"), file_name);
+         return 1;
+       }
+
+      got = fread (&arhdr, 1, sizeof arhdr, file);
+      if (got != sizeof arhdr)
+       {
+         if (got == 0)
+           return 0;
+
+         error (_("%s: failed to read archive header\n"), file_name);
+         return 1;
+       }
+    }
+
+  if (memcmp (arhdr.ar_name, "//              ", 16) == 0)
+    {
+      /* This is the archive string table holding long member
+        names.  */
+
+      longnames_size = strtoul (arhdr.ar_size, NULL, 10);
+
+      longnames = malloc (longnames_size);
+      if (longnames == NULL)
+       {
+         error (_("Out of memory\n"));
+         return 1;
+       }
+
+      if (fread (longnames, longnames_size, 1, file) != 1)
+       {
+         error(_("%s: failed to read string table\n"), file_name);
+         return 1;
+       }
+
+      if ((longnames_size & 1) != 0)
+       getc (file);
+
+      got = fread (&arhdr, 1, sizeof arhdr, file);
+      if (got != sizeof arhdr)
+       {
+         if (got == 0)
+           return 0;
+
+         error (_("%s: failed to read archive header\n"), file_name);
+         return 1;
+       }
+    }
+
+  file_name_size = strlen (file_name);
+
+  while (1)
+    {
+      char *name;
+      char *nameend;
+      char *namealc;
+
+      if (arhdr.ar_name[0] == '/')
+       {
+         unsigned long off;
+
+         off = strtoul (arhdr.ar_name + 1, NULL, 10);
+         if (off >= longnames_size)
+           {
+             error (_("%s: invalid archive string table offset %lu\n"), off);
+             return 1;
+           }
+
+         name = longnames + off;
+         nameend = memchr (name, '/', longnames_size - off);
+       }
+      else
+       {
+         name = arhdr.ar_name;
+         nameend = memchr (name, '/', 16);
+       }
+
+      if (nameend == NULL)
+       {
+         error (_("%s: bad archive file name\n"));
+         return 1;
+       }
+
+      namealc = malloc (file_name_size + (nameend - name) + 3);
+      if (namealc == NULL)
+       {
+         error (_("Out of memory\n"));
+         return 1;
+       }
+
+      memcpy (namealc, file_name, file_name_size);
+      namealc[file_name_size] = '(';
+      memcpy (namealc + file_name_size + 1, name, nameend - name);
+      namealc[file_name_size + 1 + (nameend - name)] = ')';
+      namealc[file_name_size + 2 + (nameend - name)] = '\0';
+
+      archive_file_offset = ftell (file);
+      archive_file_size = strtoul (arhdr.ar_size, NULL, 10);
+
+      process_object (namealc, file);
+
+      free (namealc);
+
+      if (fseek (file,
+                (archive_file_offset
+                 + archive_file_size
+                 + (archive_file_size & 1)),
+                SEEK_SET) != 0)
+       {
+         error (_("%s: failed to seek to next archive header\n"), file_name);
+         return 1;
+       }
+
+      got = fread (&arhdr, 1, sizeof arhdr, file);
+      if (got != sizeof arhdr)
+       {
+         if (got == 0)
+           return 0;
+
+         error (_("%s: failed to read archive header\n"), file_name);
+         return 1;
+       }
+    }
+
+  if (longnames != 0)
+    free (longnames);
+
+  return 0;
+}
+
+static int
+process_file (char *file_name)
+{
+  FILE *file;
+  struct stat statbuf;
+  char armag[SARMAG];
+  int ret;
+
+  if (stat (file_name, &statbuf) < 0)
+    {
+      error (_("Cannot stat input file %s.\n"), file_name);
+      return 1;
+    }
+
+  file = fopen (file_name, "rb");
+  if (file == NULL)
+    {
+      error (_("Input file %s not found.\n"), file_name);
+      return 1;
+    }
+
+  if (fread (armag, SARMAG, 1, file) != 1)
+    {
+      error (_("%s: Failed to read file header\n"), file_name);
+      fclose (file);
+      return 1;
+    }
+
+  if (memcmp (armag, ARMAG, SARMAG) == 0)
+    ret = process_archive (file_name, file);
+  else
+    {
+      rewind (file);
+      archive_file_size = archive_file_offset = 0;
+      ret = process_object (file_name, file);
+    }
+
+  fclose (file);
+
+  return ret;
+}
+
 #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