[patch] Add thin archive support to readelf

Cary Coutant ccoutant@google.com
Fri Feb 27 22:37:00 GMT 2009


I promised a long time ago to provide a patch that adds thin archive
support to readelf. Here it is at long last; sorry for taking so
long.0

-cary


	* readelf.c (adjust_relative_path): New function.
	(struct archive_info): New type.
	(setup_archive): New function.
	(release_archive): New function.
	(setup_nested_archive): New function.
	(get_archive_member_name): New function.
	(get_archive_member_name_at): New function.
	(make_qualified_name): New function.
	(process_archive): Factor out code for reading archive index and
	long filename table to setup_archive.  Add support for thin archives.
-------------- next part --------------
Index: readelf.c
===================================================================
RCS file: /cvs/src/src/binutils/readelf.c,v
retrieving revision 1.438
diff -u -p -d -r1.438 readelf.c
--- readelf.c	23 Feb 2009 19:01:47 -0000	1.438
+++ readelf.c	27 Feb 2009 22:20:31 -0000
@@ -145,6 +145,7 @@
 #include "getopt.h"
 #include "libiberty.h"
 #include "safe-ctype.h"
+#include "filenames.h"
 
 char *program_name = "readelf";
 int do_wide;
@@ -10534,28 +10535,98 @@ process_object (char *file_name, FILE *f
   return 0;
 }
 
-/* Process an ELF archive.
-   On entry the file is positioned just after the ARMAG string.  */
+/* Return the path name for a proxy entry in a thin archive, adjusted relative
+   to the path name of the thin archive itself if necessary.  Always returns
+   a pointer to malloc'ed memory.  */
+
+static char*
+adjust_relative_path(char* file_name, char* name, int name_len)
+{
+  char *member_file_name;
+  const char *base_name = lbasename (file_name);
+
+  /* This is a proxy entry for a thin archive member.
+     If the extended name table contains an absolute path
+     name, or if the archive is in the current directory,
+     use the path name as given.  Otherwise, we need to
+     find the member relative to the directory where the
+     archive is located.  */
+  if (IS_ABSOLUTE_PATH (name) || base_name == file_name)
+    {
+      member_file_name = malloc (name_len + 1);
+      if (member_file_name == NULL)
+        {
+          error (_("Out of memory\n"));
+          return NULL;
+        }
+      memcpy (member_file_name, name, name_len);
+      member_file_name[name_len] = '\0';
+    }
+  else
+    {
+      /* Concatenate the path components of the archive file name
+         to the relative path name from the extended name table.  */
+      size_t prefix_len = base_name - file_name;
+      member_file_name = malloc (prefix_len + name_len + 1);
+      if (member_file_name == NULL)
+        {
+          error (_("Out of memory\n"));
+          return NULL;
+        }
+      memcpy (member_file_name, file_name, prefix_len);
+      memcpy (member_file_name + prefix_len, name, name_len);
+      member_file_name[prefix_len + name_len] = '\0';
+    }
+  return member_file_name;
+}
+
+/* Structure to hold information about an archive file.  */
+
+struct archive_info
+{
+  char *file_name;                      /* Archive file name.  */
+  FILE *file;                           /* Open file descriptor.  */
+  unsigned long index_num;              /* Number of symbols in table.  */
+  unsigned long *index_array;           /* The array of member offsets.  */
+  char *sym_table;                      /* The symbol table.  */
+  unsigned long sym_size;               /* Size of the symbol table.  */
+  char *longnames;                      /* The long file names table.  */
+  unsigned long longnames_size;         /* Size of the long file names table.  */
+  unsigned long nested_member_origin;   /* Origin in the nested archive of the current member.  */
+  unsigned long next_arhdr_offset;      /* Offset of the next archive header.  */
+  bfd_boolean is_thin_archive;          /* TRUE if this is a thin archive.  */
+  struct ar_hdr arhdr;                  /* Current archive header.  */
+};
+
+/* Read the symbol table and long-name table from an archive.  */
 
 static int
-process_archive (char *file_name, FILE *file)
+setup_archive (struct archive_info *arch, char *file_name, FILE *file,
+               bfd_boolean is_thin_archive, bfd_boolean read_symbols)
 {
-  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;
-  int ret;
 
-  show_name = 1;
+  arch->file_name = strdup (file_name);
+  arch->file = file;
+  arch->index_num = 0;
+  arch->index_array = NULL;
+  arch->sym_table = NULL;
+  arch->sym_size = 0;
+  arch->longnames = NULL;
+  arch->longnames_size = 0;
+  arch->nested_member_origin = 0;
+  arch->is_thin_archive = is_thin_archive;
+  arch->next_arhdr_offset = SARMAG;
 
-  got = fread (&arhdr, 1, sizeof arhdr, file);
-  if (got != sizeof arhdr)
+  /* Read the first archive member header.  */
+  if (fseek (file, SARMAG, SEEK_SET) != 0)
+    {
+      error (_("%s: failed to seek to first archive header\n"), file_name);
+      return 1;
+    }
+  got = fread (&arch->arhdr, 1, sizeof arch->arhdr, file);
+  if (got != sizeof arch->arhdr)
     {
       if (got == 0)
 	return 0;
@@ -10565,13 +10636,15 @@ process_archive (char *file_name, FILE *
     }
 
   /* See if this is the archive symbol table.  */
-  if (const_strneq (arhdr.ar_name, "/               ")
-      || const_strneq (arhdr.ar_name, "/SYM64/         "))
+  if (const_strneq (arch->arhdr.ar_name, "/               ")
+      || const_strneq (arch->arhdr.ar_name, "/SYM64/         "))
     {
-      size = strtoul (arhdr.ar_size, NULL, 10);
+      size = strtoul (arch->arhdr.ar_size, NULL, 10);
       size = size + (size & 1);
 
-      if (do_archive_index)
+      arch->next_arhdr_offset += sizeof arch->arhdr + size;
+
+      if (read_symbols)
 	{
 	  unsigned long i;
 	  /* A buffer used to hold numbers read in from an archive index.
@@ -10594,67 +10667,63 @@ process_archive (char *file_name, FILE *
 	      error (_("%s: failed to read archive index\n"), file_name);
 	      return 1;
 	    }
-	  index_num = byte_get_big_endian (integer_buffer, sizeof integer_buffer);
+	  arch->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)
+	  if (size < arch->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);
+		     file_name, arch->index_num);
 	      return 1;
 	    }
-	  index_buffer = malloc (index_num * SIZEOF_AR_INDEX_NUMBERS);
+	  index_buffer = malloc (arch->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)
+	  got = fread (index_buffer, SIZEOF_AR_INDEX_NUMBERS, arch->index_num, file);
+	  if (got != arch->index_num)
 	    {
 	      free (index_buffer);
 	      error (_("%s: failed to read archive index\n"), file_name);
-	      ret = 1;
-	      goto out;
+	      return 1;
 	    }
-	  size -= index_num * SIZEOF_AR_INDEX_NUMBERS;
+	  size -= arch->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)
+	  arch->index_array = malloc (arch->index_num * sizeof (* arch->index_array));
+	  if (arch->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);
+	  for (i = 0; i < arch->index_num; i++)
+	    arch->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;
+	      return 1;
 	    }
-	  sym_table = malloc (size);
-	  sym_size = size;
-	  if (sym_table == NULL)
+	  arch->sym_table = malloc (size);
+	  arch->sym_size = size;
+	  if (arch->sym_table == NULL)
 	    {
 	      error (_("Out of memory whilst trying to read archive index symbol table\n"));
-	      ret = 1;
-	      goto out;
+	      return 1;
 	    }
-	  got = fread (sym_table, 1, size, file);
+	  got = fread (arch->sym_table, 1, size, file);
 	  if (got != size)
 	    {
 	      error (_("%s: failed to read archive index symbol table\n"), file_name);
-	      ret = 1;
-	      goto out;
+	      return 1;
 	    }
   	}
       else
@@ -10666,135 +10735,299 @@ process_archive (char *file_name, FILE *
 	    }
 	}
 
-      got = fread (& arhdr, 1, sizeof arhdr, file);
-      if (got != sizeof arhdr)
+      /* Read the next archive header.  */
+      got = fread (&arch->arhdr, 1, sizeof arch->arhdr, file);
+      if (got != sizeof arch->arhdr)
 	{
 	  if (got == 0)
-	    {
-	      ret = 0;
-	      goto out;
-	    }
-
+            return 0;
 	  error (_("%s: failed to read archive header following archive index\n"), file_name);
-	  ret = 1;
-	  goto out;
+	  return 1;
 	}
     }
-  else if (do_archive_index)
+  else if (read_symbols)
     printf (_("%s has no archive index\n"), file_name);
 
-  if (const_strneq (arhdr.ar_name, "//              "))
+  if (const_strneq (arch->arhdr.ar_name, "//              "))
     {
-      /* This is the archive string table holding long member
-	 names.  */
+      /* This is the archive string table holding long member names.  */
+      arch->longnames_size = strtoul (arch->arhdr.ar_size, NULL, 10);
+      arch->next_arhdr_offset += sizeof arch->arhdr + arch->longnames_size;
 
-      longnames_size = strtoul (arhdr.ar_size, NULL, 10);
-      longnames = malloc (longnames_size);
-      if (longnames == NULL)
+      arch->longnames = malloc (arch->longnames_size);
+      if (arch->longnames == NULL)
 	{
 	  error (_("Out of memory reading long symbol names in archive\n"));
-	  ret = 1;
-	  goto out;
+	  return 1;
 	}
 
-      if (fread (longnames, longnames_size, 1, file) != 1)
+      if (fread (arch->longnames, arch->longnames_size, 1, file) != 1)
 	{
-	  free (longnames);
+	  free (arch->longnames);
+	  arch->longnames = NULL;
 	  error (_("%s: failed to read long symbol name string table\n"), file_name);
-	  ret = 1;
-	  goto out;
+	  return 1;
 	}
 
-      if ((longnames_size & 1) != 0)
+      if ((arch->longnames_size & 1) != 0)
 	getc (file);
+    }
 
-      got = fread (& arhdr, 1, sizeof arhdr, file);
-      if (got != sizeof arhdr)
-	{
-	  if (got == 0)
-	    ret = 0;
-	  else
-	    {
-	      error (_("%s: failed to read archive header following long symbol names\n"), file_name);
-	      ret = 1;
-	    }
-	  goto out;
-	}
+  return 0;
+}
+
+/* Release the memory used for the archive information.  */
+
+static void
+release_archive (struct archive_info *arch)
+{
+  if (arch->file_name != NULL)
+    free (arch->file_name);
+  if (arch->index_array != NULL)
+    free (arch->index_array);
+  if (arch->sym_table != NULL)
+    free (arch->sym_table);
+  if (arch->longnames != NULL)
+    free (arch->longnames);
+}
+
+/* Open and setup a nested archive, if not already open.  */
+
+static int
+setup_nested_archive (struct archive_info *nested_arch, char *member_file_name)
+{
+  FILE *member_file;
+
+  /* Have we already setup this archive?  */
+  if (nested_arch->file_name != NULL
+      && streq (nested_arch->file_name, member_file_name))
+    return 0;
+
+  /* Close previous file and discard cached information.  */
+  if (nested_arch->file != NULL)
+    fclose (nested_arch->file);
+  release_archive (nested_arch);
+
+  member_file = fopen (member_file_name, "rb");
+  if (member_file == NULL)
+    return 1;
+  return setup_archive (nested_arch, member_file_name, member_file, FALSE, FALSE);
+}
+
+static char* get_archive_member_name_at(struct archive_info *arch,
+                                        unsigned long offset,
+                                        struct archive_info *nested_arch);
+
+/* Get the name of an archive member from the current archive header.
+   For simple names, this will modify the ar_name field of the current
+   archive header.  For long names, it will return a pointer to the
+   longnames table.  For nested archives, it will open the nested archive
+   and get the name recursively.  NESTED_ARCH is a single-entry cache so
+   we don't keep rereading the same information from a nested archive.  */
+
+static char*
+get_archive_member_name(struct archive_info *arch,
+                        struct archive_info *nested_arch)
+{
+  unsigned long j, k;
+
+  if (arch->arhdr.ar_name[0] == '/')
+    {
+      /* We have a long name.  */
+      char* endp;
+      char *member_file_name;
+      char *member_name;
+
+      arch->nested_member_origin = 0;
+      k = j = strtoul (arch->arhdr.ar_name + 1, &endp, 10);
+      if (arch->is_thin_archive && endp != NULL && *endp == ':')
+        arch->nested_member_origin = strtoul (endp + 1, NULL, 10);
+
+      while ((j < arch->longnames_size)
+             && (arch->longnames[j] != '\n')
+             && (arch->longnames[j] != '\0'))
+        j++;
+      if (arch->longnames[j-1] == '/')
+        j--;
+      arch->longnames[j] = '\0';
+
+      if (!arch->is_thin_archive || arch->nested_member_origin == 0)
+        return arch->longnames + k;
+
+      /* This is a proxy for a member of a nested archive.
+         Find the name of the member in that archive.  */
+      member_file_name = adjust_relative_path (arch->file_name, arch->longnames + k, j - k);
+      if (member_file_name != NULL
+          && setup_nested_archive (nested_arch, member_file_name) == 0
+          && (member_name = get_archive_member_name_at (nested_arch, arch->nested_member_origin, NULL)) != NULL)
+        {
+          free (member_file_name);
+          return member_name;
+        }
+      free (member_file_name);
+
+      /* Last resort: just return the name of the nested archive.  */
+      return arch->longnames + k;
+    }
+
+  /* We have a normal (short) name.  */
+  j = 0;
+  while ((arch->arhdr.ar_name[j] != '/') && (j < 16))
+    j++;
+  arch->arhdr.ar_name[j] = '\0';
+  return arch->arhdr.ar_name;
+}
+
+/* Get the name of an archive member at a given OFFSET within an archive ARCH.  */
+
+static char*
+get_archive_member_name_at(struct archive_info *arch,
+                           unsigned long offset,
+                           struct archive_info *nested_arch)
+{
+  size_t got;
+
+  if (fseek (arch->file, offset, SEEK_SET) != 0)
+    {
+      error (_("%s: failed to seek to next file name\n"), arch->file_name);
+      return NULL;
+    }
+  got = fread (&arch->arhdr, 1, sizeof arch->arhdr, arch->file);
+  if (got != sizeof arch->arhdr)
+    {
+      error (_("%s: failed to read archive header\n"), arch->file_name);
+      return NULL;
+    }
+  if (memcmp (arch->arhdr.ar_fmag, ARFMAG, 2) != 0)
+    {
+      error (_("%s: did not find a valid archive header\n"), arch->file_name);
+      return NULL;
+    }
+
+  return get_archive_member_name (arch, nested_arch);
+}
+
+/* Construct a string showing the name of the archive member, qualified
+   with the name of the containing archive file.  For thin archives, we
+   use square brackets to denote the indirection.  For nested archives,
+   we show the qualified name of the external member inside the square
+   brackets (e.g., "thin.a[normal.a(foo.o)]").  */
+
+static char*
+make_qualified_name (struct archive_info *arch,
+                     struct archive_info *nested_arch,
+                     char *member_name)
+{
+  size_t len;
+  char *name;
+
+  len = strlen (arch->file_name) + strlen (member_name) + 3;
+  if (arch->is_thin_archive && arch->nested_member_origin != 0)
+    len += strlen (nested_arch->file_name) + 2;
+
+  name = malloc (len);
+  if (name == NULL)
+    {
+      error (_("Out of memory\n"));
+      return NULL;
+    }
+
+  if (arch->is_thin_archive && arch->nested_member_origin != 0)
+    snprintf (name, len, "%s[%s(%s)]", arch->file_name, nested_arch->file_name, member_name);
+  else if (arch->is_thin_archive)
+    snprintf (name, len, "%s[%s]", arch->file_name, member_name);
+  else
+    snprintf (name, len, "%s(%s)", arch->file_name, member_name);
+
+  return name;
+}
+
+/* Process an ELF archive.
+   On entry the file is positioned just after the ARMAG string.  */
+
+static int
+process_archive (char *file_name, FILE *file, bfd_boolean is_thin_archive)
+{
+  struct archive_info arch;
+  struct archive_info nested_arch;
+  size_t got;
+  size_t file_name_size;
+  int ret;
+
+  show_name = 1;
+
+  /* The ARCH structure is used to hold information about this archive.  */
+  arch.file_name = NULL;
+  arch.file = NULL;
+  arch.index_array = NULL;
+  arch.sym_table = NULL;
+  arch.longnames = NULL;
+
+  /* The NESTED_ARCH structure is used as a single-item cache of information
+     about a nested archive (when members of a thin archive reside within
+     another regular archive file).  */
+  nested_arch.file_name = NULL;
+  nested_arch.file = NULL;
+  nested_arch.index_array = NULL;
+  nested_arch.sym_table = NULL;
+  nested_arch.longnames = NULL;
+
+  if (setup_archive (&arch, file_name, file, is_thin_archive, do_archive_index) != 0)
+    {
+      ret = 1;
+      goto out;
     }
 
   if (do_archive_index)
     {
-      if (sym_table == NULL)
+      if (arch.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 int i, l;
 	  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);
+		  file_name, arch.index_num, arch.sym_size);
 	  current_pos = ftell (file);
 
-	  for (i = l = 0; i < index_num; i++)
+	  for (i = l = 0; i < arch.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 ((i == 0) || ((i > 0) && (arch.index_array[i] != arch.index_array[i - 1])))
+	        {
+	          char *member_name = get_archive_member_name_at (&arch, arch.index_array[i], &nested_arch);
+                  if (member_name != NULL)
+                    {
+	              char *qualified_name = make_qualified_name (&arch, &nested_arch, member_name);
+                      if (qualified_name != NULL)
+                        {
+		          printf(_("Binary %s contains:\n"), qualified_name);
+		          free (qualified_name);
+		        }
 		    }
 		}
-	      if (l >= sym_size)
+		
+	      if (l >= arch.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;
+	      printf ("\t%s\n", arch.sym_table + l);
+	      l += strlen (arch.sym_table + l) + 1;
 	    }
 
-	  if (l < sym_size)
+          if (l & 01)
+            ++l;
+	  if (l < arch.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;
+	      ret = 1;
+	      goto out;
 	    }
 	}
 
@@ -10802,7 +11035,10 @@ process_archive (char *file_name, FILE *
 	  && !do_segments && !do_header && !do_dump && !do_version
 	  && !do_histogram && !do_debugging && !do_arch && !do_notes
 	  && !do_section_groups)
-	return 0; /* Archive index only.  */
+	{
+	  ret = 0; /* Archive index only.  */
+	  goto out;
+	}
     }
 
   file_name_size = strlen (file_name);
@@ -10811,88 +11047,113 @@ process_archive (char *file_name, FILE *
   while (1)
     {
       char *name;
-      char *nameend;
-      char *namealc;
+      size_t namelen;
+      char *qualified_name;
 
-      if (arhdr.ar_name[0] == '/')
-	{
-	  unsigned long off;
+      /* Read the next archive header.  */
+      if (fseek (file, arch.next_arhdr_offset, SEEK_SET) != 0)
+        {
+          error (_("%s: failed to seek to next archive header\n"), file_name);
+          return 1;
+        }
+      got = fread (&arch.arhdr, 1, sizeof arch.arhdr, file);
+      if (got != sizeof arch.arhdr)
+        {
+          if (got == 0)
+	    break;
+          error (_("%s: failed to read archive header\n"), file_name);
+          ret = 1;
+          break;
+        }
+      if (memcmp (arch.arhdr.ar_fmag, ARFMAG, 2) != 0)
+        {
+          error (_("%s: did not find a valid archive header\n"), arch.file_name);
+          ret = 1;
+          break;
+        }
 
-	  off = strtoul (arhdr.ar_name + 1, NULL, 10);
-	  if (off >= longnames_size)
-	    {
-	      error (_("%s: invalid archive string table offset %lu\n"), file_name, off);
-	      ret = 1;
-	      break;
-	    }
+      arch.next_arhdr_offset += sizeof arch.arhdr;
 
-	  name = longnames + off;
-	  nameend = memchr (name, '/', longnames_size - off);
-	}
-      else
-	{
-	  name = arhdr.ar_name;
-	  nameend = memchr (name, '/', 16);
-	}
+      archive_file_size = strtoul (arch.arhdr.ar_size, NULL, 10);
+      if (archive_file_size & 01)
+        ++archive_file_size;
 
-      if (nameend == NULL)
+      name = get_archive_member_name (&arch, &nested_arch);
+      if (name == NULL)
 	{
 	  error (_("%s: bad archive file name\n"), file_name);
 	  ret = 1;
 	  break;
 	}
+      namelen = strlen (name);
 
-      namealc = malloc (file_name_size + (nameend - name) + 3);
-      if (namealc == NULL)
+      qualified_name = make_qualified_name (&arch, &nested_arch, name);
+      if (qualified_name == NULL)
 	{
-	  error (_("Out of memory\n"));
+	  error (_("%s: bad archive file name\n"), file_name);
 	  ret = 1;
 	  break;
 	}
 
-      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';
+      if (is_thin_archive && arch.nested_member_origin == 0)
+        {
+          /* This is a proxy for an external member of a thin archive.  */
+          FILE *member_file;
+          char *member_file_name = adjust_relative_path (file_name, name, namelen);
+          if (member_file_name == NULL)
+            {
+              ret = 1;
+              break;
+            }
 
-      archive_file_offset = ftell (file);
-      archive_file_size = strtoul (arhdr.ar_size, NULL, 10);
+          member_file = fopen (member_file_name, "rb");
+          if (member_file == NULL)
+            {
+              error (_("Input file '%s' is not readable.\n"), member_file_name);
+              free (member_file_name);
+              ret = 1;
+              break;
+            }
 
-      ret |= process_object (namealc, file);
+          archive_file_offset = arch.nested_member_origin;
 
-      free (namealc);
+          ret |= process_object (qualified_name, member_file);
+          
+          fclose (member_file);
+          free (member_file_name);
+        }
+      else if (is_thin_archive)
+        {
+          /* This is a proxy for a member of a nested archive.  */
+          archive_file_offset = arch.nested_member_origin + sizeof arch.arhdr;
 
-      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);
-	  ret = 1;
-	  break;
-	}
+          /* The nested archive file will have been opened and setup by
+             get_archive_member_name.  */
+          if (fseek (nested_arch.file, archive_file_offset, SEEK_SET) != 0)
+            {
+              error (_("%s: failed to seek to archive member.\n"), nested_arch.file_name);
+              ret = 1;
+              break;
+            }
 
-      got = fread (&arhdr, 1, sizeof arhdr, file);
-      if (got != sizeof arhdr)
-	{
-	  if (got == 0)
-	    break;
+          ret |= process_object (qualified_name, nested_arch.file);
+        }
+      else
+        {
+          archive_file_offset = arch.next_arhdr_offset;
+          arch.next_arhdr_offset += archive_file_size;
 
-	  error (_("%s: failed to read archive header\n"), file_name);
-	  ret = 1;
-	  break;
-	}
+          ret |= process_object (qualified_name, file);
+        }
+
+      free (qualified_name);
     }
 
  out:
-  if (index_array != NULL)
-    free (index_array);
-  if (sym_table != NULL)
-    free (sym_table);
-  if (longnames != NULL)
-    free (longnames);
+  if (nested_arch.file != NULL)
+    fclose (nested_arch.file);
+  release_archive (&nested_arch);
+  release_archive (&arch);
 
   return ret;
 }
@@ -10936,7 +11197,9 @@ process_file (char *file_name)
     }
 
   if (memcmp (armag, ARMAG, SARMAG) == 0)
-    ret = process_archive (file_name, file);
+    ret = process_archive (file_name, file, FALSE);
+  else if (memcmp (armag, ARMAGT, SARMAG) == 0)
+    ret = process_archive (file_name, file, TRUE);
   else
     {
       if (do_archive_index)


More information about the Binutils mailing list