]> oss.titaniummirror.com Git - msp430-binutils.git/blobdiff - bfd/archive.c
Merge commit 'upstream/2.20'
[msp430-binutils.git] / bfd / archive.c
index e080d3a301da737cc320005239e8d584a5a7003e..36252d5794896e4ba3fa11ac9f77bdd2b0cfced7 100644 (file)
@@ -1,6 +1,6 @@
 /* BFD back-end for archive files (libraries).
    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
    Free Software Foundation, Inc.
    Written by Cygnus Support.  Mostly Gumby Henkel-Wallace's fault.
 
@@ -137,6 +137,7 @@ SUBSECTION
 #include "aout/ranlib.h"
 #include "safe-ctype.h"
 #include "hashtab.h"
+#include "filenames.h"
 
 #ifndef errno
 extern int errno;
@@ -157,7 +158,7 @@ struct ar_cache {
 #define ar_maxnamelen(abfd) ((abfd)->xvec->ar_max_namelen)
 
 #define arch_eltdata(bfd) ((struct areltdata *) ((bfd)->arelt_data))
-#define arch_hdr(bfd) ((struct ar_hdr *) arch_eltdata(bfd)->arch_header)
+#define arch_hdr(bfd) ((struct ar_hdr *) arch_eltdata (bfd)->arch_header)
 \f
 void
 _bfd_ar_spacepad (char *p, size_t n, const char *fmt, long val)
@@ -180,7 +181,7 @@ _bfd_generic_mkarchive (bfd *abfd)
 {
   bfd_size_type amt = sizeof (struct artdata);
 
-  abfd->tdata.aout_ar_data = bfd_zalloc (abfd, amt);
+  abfd->tdata.aout_ar_data = (struct artdata *) bfd_zalloc (abfd, amt);
   if (bfd_ardata (abfd) == NULL)
     return FALSE;
 
@@ -318,7 +319,7 @@ _bfd_add_bfd_to_archive_cache (bfd *arch_bfd, file_ptr filepos, bfd *new_elt)
     }
 
   /* Insert new_elt into the hash table by filepos.  */
-  cache = bfd_zalloc (arch_bfd, sizeof (struct ar_cache));
+  cache = (struct ar_cache *) bfd_zalloc (arch_bfd, sizeof (struct ar_cache));
   cache->ptr = filepos;
   cache->arbfd = new_elt;
   *htab_find_slot (hash_table, (const void *) cache, INSERT) = cache;
@@ -326,24 +327,61 @@ _bfd_add_bfd_to_archive_cache (bfd *arch_bfd, file_ptr filepos, bfd *new_elt)
   return TRUE;
 }
 \f
+static bfd *
+_bfd_find_nested_archive (bfd *arch_bfd, const char *filename)
+{
+  bfd *abfd;
+
+  for (abfd = arch_bfd->nested_archives;
+       abfd != NULL;
+       abfd = abfd->archive_next)
+    {
+      if (strcmp (filename, abfd->filename) == 0)
+        return abfd;
+    }
+  abfd = bfd_openr (filename, NULL);
+  if (abfd)
+    {
+      abfd->archive_next = arch_bfd->nested_archives;
+      arch_bfd->nested_archives = abfd;
+    }
+  return abfd;
+}
+
 /* The name begins with space.  Hence the rest of the name is an index into
    the string table.  */
 
 static char *
-get_extended_arelt_filename (bfd *arch, const char *name)
+get_extended_arelt_filename (bfd *arch, const char *name, file_ptr *originp)
 {
   unsigned long index = 0;
+  const char *endp;
 
   /* Should extract string so that I can guarantee not to overflow into
      the next region, but I'm too lazy.  */
   errno = 0;
   /* Skip first char, which is '/' in SVR4 or ' ' in some other variants.  */
-  index = strtol (name + 1, NULL, 10);
+  index = strtol (name + 1, (char **) &endp, 10);
   if (errno != 0 || index >= bfd_ardata (arch)->extended_names_size)
     {
       bfd_set_error (bfd_error_malformed_archive);
       return NULL;
     }
+  /* In a thin archive, a member of an archive-within-an-archive
+     will have the offset in the inner archive encoded here.  */
+  if (bfd_is_thin_archive (arch) && endp != NULL && *endp == ':')
+    {
+      file_ptr origin = strtol (endp + 1, NULL, 10);
+
+      if (errno != 0)
+        {
+          bfd_set_error (bfd_error_malformed_archive);
+          return NULL;
+        }
+      *originp = origin;
+    }
+  else
+    *originp = 0;
 
   return bfd_ardata (arch)->extended_names + index;
 }
@@ -376,6 +414,7 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, const char *mag)
   bfd_size_type namelen = 0;
   bfd_size_type allocsize = sizeof (struct areltdata) + sizeof (struct ar_hdr);
   char *allocptr = 0;
+  file_ptr origin = 0;
 
   if (bfd_bread (hdrp, sizeof (struct ar_hdr), abfd) != sizeof (struct ar_hdr))
     {
@@ -407,7 +446,7 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, const char *mag)
           && memchr (hdr.ar_name, '/', ar_maxnamelen (abfd)) == NULL))
       && bfd_ardata (abfd)->extended_names != NULL)
     {
-      filename = get_extended_arelt_filename (abfd, hdr.ar_name);
+      filename = get_extended_arelt_filename (abfd, hdr.ar_name, &origin);
       if (filename == NULL)
        return NULL;
     }
@@ -423,7 +462,7 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, const char *mag)
       allocsize += namelen + 1;
       parsed_size -= namelen;
 
-      allocptr = bfd_zalloc (abfd, allocsize);
+      allocptr = (char *) bfd_zalloc (abfd, allocsize);
       if (allocptr == NULL)
        return NULL;
       filename = (allocptr
@@ -444,12 +483,12 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, const char *mag)
         spaces, so only look for ' ' if we don't find '/'.  */
 
       char *e;
-      e = memchr (hdr.ar_name, '\0', ar_maxnamelen (abfd));
+      e = (char *) memchr (hdr.ar_name, '\0', ar_maxnamelen (abfd));
       if (e == NULL)
        {
-         e = memchr (hdr.ar_name, '/', ar_maxnamelen (abfd));
+         e = (char *) memchr (hdr.ar_name, '/', ar_maxnamelen (abfd));
          if (e == NULL)
-           e = memchr (hdr.ar_name, ' ', ar_maxnamelen (abfd));
+           e = (char *) memchr (hdr.ar_name, ' ', ar_maxnamelen (abfd));
        }
 
       if (e != NULL)
@@ -466,7 +505,7 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, const char *mag)
 
   if (!allocptr)
     {
-      allocptr = bfd_zalloc (abfd, allocsize);
+      allocptr = (char *) bfd_zalloc (abfd, allocsize);
       if (allocptr == NULL)
        return NULL;
     }
@@ -476,6 +515,7 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, const char *mag)
   ared->arch_header = allocptr + sizeof (struct areltdata);
   memcpy (ared->arch_header, &hdr, sizeof (struct ar_hdr));
   ared->parsed_size = parsed_size;
+  ared->origin = origin;
 
   if (filename != NULL)
     ared->filename = filename;
@@ -491,6 +531,30 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, const char *mag)
   return ared;
 }
 \f
+/* Append the relative pathname for a member of the thin archive
+   to the pathname of the directory containing the archive.  */
+
+static char *
+append_relative_path (bfd *arch, char *elt_name)
+{
+  const char *arch_name = arch->filename;
+  const char *base_name = lbasename (arch_name);
+  size_t prefix_len;
+  char *filename;
+
+  if (base_name == arch_name)
+    return elt_name;
+
+  prefix_len = base_name - arch_name;
+  filename = (char *) bfd_alloc (arch, prefix_len + strlen (elt_name) + 1);
+  if (filename == NULL)
+    return NULL;
+
+  strncpy (filename, arch_name, prefix_len);
+  strcpy (filename + prefix_len, elt_name);
+  return filename;
+}
+
 /* This is an internal function; it's mainly used when indexing
    through the archive symbol table, but also used to get the next
    element, since it handles the bookkeeping so nicely for us.  */
@@ -500,6 +564,7 @@ _bfd_get_elt_at_filepos (bfd *archive, file_ptr filepos)
 {
   struct areltdata *new_areldata;
   bfd *n_nfd;
+  char *filename;
 
   if (archive->my_archive)
     {
@@ -514,24 +579,77 @@ _bfd_get_elt_at_filepos (bfd *archive, file_ptr filepos)
   if (0 > bfd_seek (archive, filepos, SEEK_SET))
     return NULL;
 
-  if ((new_areldata = _bfd_read_ar_hdr (archive)) == NULL)
+  if ((new_areldata = (struct areltdata *) _bfd_read_ar_hdr (archive)) == NULL)
     return NULL;
 
-  n_nfd = _bfd_create_empty_archive_element_shell (archive);
+  filename = new_areldata->filename;
+
+  if (bfd_is_thin_archive (archive))
+    {
+      /* This is a proxy entry for an external file.  */
+      if (! IS_ABSOLUTE_PATH (filename))
+        {
+          filename = append_relative_path (archive, filename);
+          if (filename == NULL)
+            return NULL;
+        }
+
+      if (new_areldata->origin > 0)
+        {
+          /* This proxy entry refers to an element of a nested archive.
+             Locate the member of that archive and return a bfd for it.  */
+          bfd *ext_arch = _bfd_find_nested_archive (archive, filename);
+
+          if (ext_arch == NULL
+              || ! bfd_check_format (ext_arch, bfd_archive))
+            {
+              bfd_release (archive, new_areldata);
+              return NULL;
+            }
+          n_nfd = _bfd_get_elt_at_filepos (ext_arch, new_areldata->origin);
+          if (n_nfd == NULL)
+            {
+              bfd_release (archive, new_areldata);
+              return NULL;
+            }
+          n_nfd->proxy_origin = bfd_tell (archive);
+          return n_nfd;
+        }
+      /* It's not an element of a nested archive;
+         open the external file as a bfd.  */
+      n_nfd = bfd_openr (filename, NULL);
+    }
+  else
+    {
+      n_nfd = _bfd_create_empty_archive_element_shell (archive);
+    }
+
   if (n_nfd == NULL)
     {
       bfd_release (archive, new_areldata);
       return NULL;
     }
 
-  n_nfd->origin = bfd_tell (archive);
+  n_nfd->proxy_origin = bfd_tell (archive);
+
+  if (bfd_is_thin_archive (archive))
+    {
+      n_nfd->origin = 0;
+    }
+  else
+    {
+      n_nfd->origin = n_nfd->proxy_origin;
+      n_nfd->filename = filename;
+    }
+
   n_nfd->arelt_data = new_areldata;
-  n_nfd->filename = new_areldata->filename;
 
   if (_bfd_add_bfd_to_archive_cache (archive, filepos, n_nfd))
     return n_nfd;
 
   /* Huh?  */
+  /* FIXME:  n_nfd isn't allocated in the archive's memory pool.
+     If we reach this point, I think bfd_release will abort.  */
   bfd_release (archive, n_nfd);
   bfd_release (archive, new_areldata);
   return NULL;
@@ -568,8 +686,8 @@ DESCRIPTION
 bfd *
 bfd_openr_next_archived_file (bfd *archive, bfd *last_file)
 {
-  if ((bfd_get_format (archive) != bfd_archive) ||
-      (archive->direction == write_direction))
+  if ((bfd_get_format (archive) != bfd_archive)
+      || (archive->direction == write_direction))
     {
       bfd_set_error (bfd_error_invalid_operation);
       return NULL;
@@ -589,7 +707,9 @@ bfd_generic_openr_next_archived_file (bfd *archive, bfd *last_file)
   else
     {
       unsigned int size = arelt_size (last_file);
-      filestart = last_file->origin + size;
+      filestart = last_file->proxy_origin;
+      if (! bfd_is_thin_archive (archive))
+        filestart += size;
       if (archive->my_archive)
        filestart -= archive->origin;
       /* Pad to an even boundary...
@@ -615,14 +735,17 @@ bfd_generic_archive_p (bfd *abfd)
       return NULL;
     }
 
-  if (strncmp (armag, ARMAG, SARMAG) != 0 &&
-      strncmp (armag, ARMAGB, SARMAG) != 0)
+  bfd_is_thin_archive (abfd) = (strncmp (armag, ARMAGT, SARMAG) == 0);
+
+  if (strncmp (armag, ARMAG, SARMAG) != 0
+      && strncmp (armag, ARMAGB, SARMAG) != 0
+      && ! bfd_is_thin_archive (abfd))
     return 0;
 
   tdata_hold = bfd_ardata (abfd);
 
   amt = sizeof (struct artdata);
-  bfd_ardata (abfd) = bfd_zalloc (abfd, amt);
+  bfd_ardata (abfd) = (struct artdata *) bfd_zalloc (abfd, amt);
   if (bfd_ardata (abfd) == NULL)
     {
       bfd_ardata (abfd) = tdata_hold;
@@ -712,13 +835,13 @@ do_slurp_bsd_armap (bfd *abfd)
   bfd_size_type parsed_size, amt;
   carsym *set;
 
-  mapdata = _bfd_read_ar_hdr (abfd);
+  mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
   if (mapdata == NULL)
     return FALSE;
   parsed_size = mapdata->parsed_size;
   bfd_release (abfd, mapdata); /* Don't need it any more.  */
 
-  raw_armap = bfd_zalloc (abfd, parsed_size);
+  raw_armap = (bfd_byte *) bfd_zalloc (abfd, parsed_size);
   if (raw_armap == NULL)
     return FALSE;
 
@@ -747,7 +870,7 @@ do_slurp_bsd_armap (bfd *abfd)
                + ardata->symdef_count * BSD_SYMDEF_SIZE
                + BSD_STRING_COUNT_SIZE);
   amt = ardata->symdef_count * sizeof (carsym);
-  ardata->symdefs = bfd_alloc (abfd, amt);
+  ardata->symdefs = (struct carsym *) bfd_alloc (abfd, amt);
   if (!ardata->symdefs)
     return FALSE;
 
@@ -788,7 +911,7 @@ do_slurp_coff_armap (bfd *abfd)
   bfd_size_type carsym_size, ptrsize;
   unsigned int i;
 
-  mapdata = _bfd_read_ar_hdr (abfd);
+  mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
   if (mapdata == NULL)
     return FALSE;
   parsed_size = mapdata->parsed_size;
@@ -833,14 +956,15 @@ do_slurp_coff_armap (bfd *abfd)
   if (carsym_size + stringsize + 1 <= carsym_size)
     return FALSE;
 
-  ardata->symdefs = bfd_zalloc (abfd, carsym_size + stringsize + 1);
+  ardata->symdefs = (struct carsym *) bfd_zalloc (abfd,
+                                                  carsym_size + stringsize + 1);
   if (ardata->symdefs == NULL)
     return FALSE;
   carsyms = ardata->symdefs;
   stringbase = ((char *) ardata->symdefs) + carsym_size;
 
   /* Allocate and read in the raw offsets.  */
-  raw_armap = bfd_alloc (abfd, ptrsize);
+  raw_armap = (int *) bfd_alloc (abfd, ptrsize);
   if (raw_armap == NULL)
     goto release_symdefs;
   if (bfd_bread (raw_armap, ptrsize, abfd) != ptrsize
@@ -875,7 +999,7 @@ do_slurp_coff_armap (bfd *abfd)
     struct areltdata *tmp;
 
     bfd_seek (abfd, ardata->first_file_filepos, SEEK_SET);
-    tmp = _bfd_read_ar_hdr (abfd);
+    tmp = (struct areltdata *) _bfd_read_ar_hdr (abfd);
     if (tmp != NULL)
       {
        if (tmp->arch_header[0] == '/'
@@ -975,12 +1099,12 @@ bfd_slurp_bsd_armap_f2 (bfd *abfd)
       return TRUE;
     }
 
-  mapdata = _bfd_read_ar_hdr (abfd);
+  mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
   if (mapdata == NULL)
     return FALSE;
 
   amt = mapdata->parsed_size;
-  raw_armap = bfd_zalloc (abfd, amt);
+  raw_armap = (bfd_byte *) bfd_zalloc (abfd, amt);
   if (raw_armap == NULL)
     {
     byebye:
@@ -1016,7 +1140,7 @@ bfd_slurp_bsd_armap_f2 (bfd *abfd)
                + BSD_STRING_COUNT_SIZE);
   rbase = (bfd_byte *) stringbase + stringsize;
   amt = ardata->symdef_count * BSD_SYMDEF_SIZE;
-  ardata->symdefs = bfd_alloc (abfd, amt);
+  ardata->symdefs = (struct carsym *) bfd_alloc (abfd, amt);
   if (!ardata->symdefs)
     return FALSE;
 
@@ -1073,7 +1197,7 @@ _bfd_slurp_extended_name_table (bfd *abfd)
          return TRUE;
        }
 
-      namedata = _bfd_read_ar_hdr (abfd);
+      namedata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
       if (namedata == NULL)
        return FALSE;
 
@@ -1082,7 +1206,7 @@ _bfd_slurp_extended_name_table (bfd *abfd)
         goto byebye;
 
       bfd_ardata (abfd)->extended_names_size = amt;
-      bfd_ardata (abfd)->extended_names = bfd_zalloc (abfd, amt + 1);
+      bfd_ardata (abfd)->extended_names = (char *) bfd_zalloc (abfd, amt + 1);
       if (bfd_ardata (abfd)->extended_names == NULL)
        {
        byebye:
@@ -1110,7 +1234,7 @@ _bfd_slurp_extended_name_table (bfd *abfd)
        char *limit = temp + namedata->parsed_size;
        for (; temp < limit; ++temp)
          {
-           if (*temp == '\012')
+           if (*temp == ARFMAG[1])
              temp[temp > ext_names && temp[-1] == '/' ? -1 : 0] = '\0';
            if (*temp == '\\')
              *temp = '/';
@@ -1190,6 +1314,66 @@ normalize (bfd *abfd ATTRIBUTE_UNUSED, const char *file)
 }
 #endif
 
+/* Adjust a relative path name based on the reference path.  */
+
+static const char *
+adjust_relative_path (const char * path, const char * ref_path)
+{
+  static char *pathbuf = NULL;
+  static int pathbuf_len = 0;
+  const char *pathp = path;
+  const char *refp = ref_path;
+  int element_count = 0;
+  int len;
+  char *newp;
+
+  /* Remove common leading path elements.  */
+  for (;;)
+    {
+      const char *e1 = pathp;
+      const char *e2 = refp;
+
+      while (*e1 && ! IS_DIR_SEPARATOR (*e1))
+       ++e1;
+      while (*e2 && ! IS_DIR_SEPARATOR (*e2))
+       ++e2;
+      if (*e1 == '\0' || *e2 == '\0' || e1 - pathp != e2 - refp
+         || strncmp (pathp, refp, e1 - pathp) != 0)
+       break;
+      pathp = e1 + 1;
+      refp = e2 + 1;
+    }
+
+  /* For each leading path element in the reference path,
+     insert "../" into the path.  */
+  for (; *refp; ++refp)
+    if (IS_DIR_SEPARATOR (*refp))
+      ++element_count;
+  len = 3 * element_count + strlen (path) + 1;
+
+  if (len > pathbuf_len)
+    {
+      if (pathbuf != NULL)
+       free (pathbuf);
+      pathbuf_len = 0;
+      pathbuf = (char *) bfd_malloc (len);
+      if (pathbuf == NULL)
+       return path;
+      pathbuf_len = len;
+    }
+
+  newp = pathbuf;
+  while (element_count-- > 0)
+    {
+      /* FIXME: Support Windows style path separators as well.  */
+      strcpy (newp, "../");
+      newp += 3;
+    }
+  strcpy (newp, pathp);
+
+  return pathbuf;
+}
+
 /* Build a BFD style extended name table.  */
 
 bfd_boolean
@@ -1232,8 +1416,11 @@ _bfd_construct_extended_name_table (bfd *abfd,
   bfd_size_type total_namelen = 0;
   bfd *current;
   char *strptr;
+  const char *last_filename;
+  long last_stroff;
 
   *tablen = 0;
+  last_filename = NULL;
 
   /* Figure out how long the table should be.  */
   for (current = abfd->archive_head;
@@ -1243,6 +1430,42 @@ _bfd_construct_extended_name_table (bfd *abfd,
       const char *normal;
       unsigned int thislen;
 
+      if (bfd_is_thin_archive (abfd))
+        {
+          const char *filename = current->filename;
+
+          /* If the element being added is a member of another archive
+             (i.e., we are flattening), use the containing archive's name.  */
+          if (current->my_archive
+              && ! bfd_is_thin_archive (current->my_archive))
+            filename = current->my_archive->filename;
+
+          /* If the path is the same as the previous path seen,
+             reuse it.  This can happen when flattening a thin
+             archive that contains other archives.  */
+          if (last_filename && strcmp (last_filename, filename) == 0)
+            continue;
+
+          last_filename = filename;
+
+          /* If the path is relative, adjust it relative to
+             the containing archive. */
+          if (! IS_ABSOLUTE_PATH (filename)
+              && ! IS_ABSOLUTE_PATH (abfd->filename))
+            normal = adjust_relative_path (filename, abfd->filename);
+          else
+            normal = filename;
+
+          /* In a thin archive, always store the full pathname
+             in the extended name table.  */
+          total_namelen += strlen (normal) + 1;
+         if (trailing_slash)
+           /* Leave room for trailing slash.  */
+           ++total_namelen;
+
+          continue;
+        }
+
       normal = normalize (current, current->filename);
       if (normal == NULL)
        return FALSE;
@@ -1283,45 +1506,92 @@ _bfd_construct_extended_name_table (bfd *abfd,
   if (total_namelen == 0)
     return TRUE;
 
-  *tabloc = bfd_zalloc (abfd, total_namelen);
+  *tabloc = (char *) bfd_zalloc (abfd, total_namelen);
   if (*tabloc == NULL)
     return FALSE;
 
   *tablen = total_namelen;
   strptr = *tabloc;
 
+  last_filename = NULL;
+  last_stroff = 0;
+
   for (current = abfd->archive_head;
        current != NULL;
        current = current->archive_next)
     {
       const char *normal;
       unsigned int thislen;
-
-      normal = normalize (current, current->filename);
-      if (normal == NULL)
-       return FALSE;
+      long stroff;
+      const char *filename = current->filename;
+
+      if (bfd_is_thin_archive (abfd))
+        {
+          /* If the element being added is a member of another archive
+             (i.e., we are flattening), use the containing archive's name.  */
+          if (current->my_archive
+              && ! bfd_is_thin_archive (current->my_archive))
+            filename = current->my_archive->filename;
+          /* If the path is the same as the previous path seen,
+             reuse it.  This can happen when flattening a thin
+             archive that contains other archives.
+             If the path is relative, adjust it relative to
+             the containing archive.  */
+          if (last_filename && strcmp (last_filename, filename) == 0)
+            normal = last_filename;
+          else if (! IS_ABSOLUTE_PATH (filename)
+                   && ! IS_ABSOLUTE_PATH (abfd->filename))
+            normal = adjust_relative_path (filename, abfd->filename);
+          else
+            normal = filename;
+        }
+      else
+        {
+          normal = normalize (current, filename);
+          if (normal == NULL)
+            return FALSE;
+        }
 
       thislen = strlen (normal);
-      if (thislen > maxname)
+      if (thislen > maxname || bfd_is_thin_archive (abfd))
        {
          /* Works for now; may need to be re-engineered if we
             encounter an oddball archive format and want to
             generalise this hack.  */
          struct ar_hdr *hdr = arch_hdr (current);
-         strcpy (strptr, normal);
-         if (! trailing_slash)
-           strptr[thislen] = '\012';
-         else
-           {
-             strptr[thislen] = '/';
-             strptr[thislen + 1] = '\012';
+         if (normal == last_filename)
+           stroff = last_stroff;
+          else
+            {
+             strcpy (strptr, normal);
+             if (! trailing_slash)
+               strptr[thislen] = ARFMAG[1];
+             else
+               {
+                 strptr[thislen] = '/';
+                 strptr[thislen + 1] = ARFMAG[1];
+               }
+             stroff = strptr - *tabloc;
+             last_stroff = stroff;
            }
          hdr->ar_name[0] = ar_padchar (current);
-          _bfd_ar_spacepad (hdr->ar_name + 1, maxname - 1, "%-ld",
-                            strptr - *tabloc);
-         strptr += thislen + 1;
-         if (trailing_slash)
-           ++strptr;
+         if (bfd_is_thin_archive (abfd) && current->origin > 0)
+           {
+             int len = snprintf (hdr->ar_name + 1, maxname - 1, "%-ld:",
+                                 stroff);
+             _bfd_ar_spacepad (hdr->ar_name + 1 + len, maxname - 1 - len,
+                                "%-ld",
+                                current->origin - sizeof (struct ar_hdr));
+           }
+         else
+            _bfd_ar_spacepad (hdr->ar_name + 1, maxname - 1, "%-ld", stroff);
+          if (normal != last_filename)
+            {
+             strptr += thislen + 1;
+             if (trailing_slash)
+               ++strptr;
+              last_filename = filename;
+           }
        }
     }
 
@@ -1370,7 +1640,7 @@ bfd_ar_hdr_from_filesystem (bfd *abfd, const char *filename, bfd *member)
   if (member && (member->flags & BFD_IN_MEMORY) != 0)
     {
       /* Assume we just "made" the member, and fake it.  */
-      struct bfd_in_memory *bim = member->iostream;
+      struct bfd_in_memory *bim = (struct bfd_in_memory *) member->iostream;
       time (&status.st_mtime);
       status.st_uid = getuid ();
       status.st_gid = getgid ();
@@ -1383,8 +1653,18 @@ bfd_ar_hdr_from_filesystem (bfd *abfd, const char *filename, bfd *member)
       return NULL;
     }
 
+  /* If the caller requested that the BFD generate deterministic output,
+     fake values for modification time, UID, GID, and file mode.  */
+  if ((abfd->flags & BFD_DETERMINISTIC_OUTPUT) != 0)
+    {
+      status.st_mtime = 0;
+      status.st_uid = 0;
+      status.st_gid = 0;
+      status.st_mode = 0644;
+    }
+
   amt = sizeof (struct ar_hdr) + sizeof (struct areltdata);
-  ared = bfd_zalloc (abfd, amt);
+  ared = (struct areltdata *) bfd_zalloc (abfd, amt);
   if (ared == NULL)
     return NULL;
   hdr = (struct ar_hdr *) (((char *) ared) + sizeof (struct areltdata));
@@ -1593,6 +1873,7 @@ bfd_gnu_truncate_arname (bfd *abfd, const char *pathname, char *arhdr)
   {
     /* We could have foo/bar\\baz, or foo\\bar, or d:bar.  */
     char *bslash = strrchr (pathname, '\\');
+
     if (filename == NULL || (bslash != NULL && bslash > filename))
       filename = bslash;
     if (filename == NULL && pathname[0] != '\0' && pathname[1] == ':')
@@ -1610,7 +1891,8 @@ bfd_gnu_truncate_arname (bfd *abfd, const char *pathname, char *arhdr)
   if (length <= maxlen)
     memcpy (hdr->ar_name, filename, length);
   else
-    {                          /* pathname: meet procrustes */
+    {
+      /* pathname: meet procrustes.  */
       memcpy (hdr->ar_name, filename, maxlen);
       if ((filename[length - 2] == '.') && (filename[length - 1] == 'o'))
        {
@@ -1638,6 +1920,7 @@ _bfd_write_archive_contents (bfd *arch)
   bfd_boolean hasobjects = FALSE;
   bfd_size_type wrote;
   int tries;
+  char *armag;
 
   /* Verify the viability of all entries; if any of them live in the
      filesystem (as opposed to living in an archive open for input)
@@ -1681,7 +1964,10 @@ _bfd_write_archive_contents (bfd *arch)
 
   if (bfd_seek (arch, (file_ptr) 0, SEEK_SET) != 0)
     return FALSE;
-  wrote = bfd_bwrite (ARMAG, SARMAG, arch);
+  armag = ARMAG;
+  if (bfd_is_thin_archive (arch))
+    armag = ARMAGT;
+  wrote = bfd_bwrite (armag, SARMAG, arch);
   if (wrote != SARMAG)
     return FALSE;
 
@@ -1707,7 +1993,7 @@ _bfd_write_archive_contents (bfd *arch)
        return FALSE;
       if ((elength % 2) == 1)
        {
-         if (bfd_bwrite ("\012", 1, arch) != 1)
+         if (bfd_bwrite (&ARFMAG[1], 1, arch) != 1)
            return FALSE;
        }
     }
@@ -1724,11 +2010,15 @@ _bfd_write_archive_contents (bfd *arch)
       if (bfd_bwrite (hdr, sizeof (*hdr), arch)
          != sizeof (*hdr))
        return FALSE;
+      if (bfd_is_thin_archive (arch))
+        continue;
       if (bfd_seek (current, (file_ptr) 0, SEEK_SET) != 0)
        goto input_err;
+
       while (remaining)
        {
          unsigned int amt = DEFAULT_BUFFERSIZE;
+
          if (amt > remaining)
            amt = remaining;
          errno = 0;
@@ -1742,9 +2032,10 @@ _bfd_write_archive_contents (bfd *arch)
            return FALSE;
          remaining -= amt;
        }
+
       if ((arelt_size (current) % 2) == 1)
        {
-         if (bfd_bwrite ("\012", 1, arch) != 1)
+         if (bfd_bwrite (&ARFMAG[1], 1, arch) != 1)
            return FALSE;
        }
     }
@@ -1798,19 +2089,19 @@ _bfd_compute_and_write_armap (bfd *arch, unsigned int elength)
   elength += elength % 2;
 
   amt = orl_max * sizeof (struct orl);
-  map = bfd_malloc (amt);
+  map = (struct orl *) bfd_malloc (amt);
   if (map == NULL)
     goto error_return;
 
   /* We put the symbol names on the arch objalloc, and then discard
      them when done.  */
-  first_name = bfd_alloc (arch, 1);
+  first_name = (char *) bfd_alloc (arch, 1);
   if (first_name == NULL)
     goto error_return;
 
   /* Drop all the files called __.SYMDEF, we're going to make our own.  */
-  while (arch->archive_head &&
-        strcmp (arch->archive_head->filename, "__.SYMDEF") == 0)
+  while (arch->archive_head
+        && strcmp (arch->archive_head->filename, "__.SYMDEF") == 0)
     arch->archive_head = arch->archive_head->archive_next;
 
   /* Map over each element.  */
@@ -1836,7 +2127,7 @@ _bfd_compute_and_write_armap (bfd *arch, unsigned int elength)
                  if (syms_max > 0)
                    free (syms);
                  syms_max = storage;
-                 syms = bfd_malloc (syms_max);
+                 syms = (asymbol **) bfd_malloc (syms_max);
                  if (syms == NULL)
                    goto error_return;
                }
@@ -1851,10 +2142,10 @@ _bfd_compute_and_write_armap (bfd *arch, unsigned int elength)
                  flagword flags = (syms[src_count])->flags;
                  asection *sec = syms[src_count]->section;
 
-                 if ((flags & BSF_GLOBAL ||
-                      flags & BSF_WEAK ||
-                      flags & BSF_INDIRECT ||
-                      bfd_is_com_section (sec))
+                 if ((flags & BSF_GLOBAL
+                      || flags & BSF_WEAK
+                      || flags & BSF_INDIRECT
+                      || bfd_is_com_section (sec))
                      && ! bfd_is_und_section (sec))
                    {
                      bfd_size_type namelen;
@@ -1865,7 +2156,7 @@ _bfd_compute_and_write_armap (bfd *arch, unsigned int elength)
                        {
                          orl_max *= 2;
                          amt = orl_max * sizeof (struct orl);
-                         new_map = bfd_realloc (map, amt);
+                         new_map = (struct orl *) bfd_realloc (map, amt);
                          if (new_map == NULL)
                            goto error_return;
 
@@ -1874,10 +2165,11 @@ _bfd_compute_and_write_armap (bfd *arch, unsigned int elength)
 
                      namelen = strlen (syms[src_count]->name);
                      amt = sizeof (char *);
-                     map[orl_count].name = bfd_alloc (arch, amt);
+                     map[orl_count].name = (char **) bfd_alloc (arch, amt);
                      if (map[orl_count].name == NULL)
                        goto error_return;
-                     *(map[orl_count].name) = bfd_alloc (arch, namelen + 1);
+                     *(map[orl_count].name) = (char *) bfd_alloc (arch,
+                                                                   namelen + 1);
                      if (*(map[orl_count].name) == NULL)
                        goto error_return;
                      strcpy (*(map[orl_count].name), syms[src_count]->name);
@@ -1940,20 +2232,39 @@ bsd_write_armap (bfd *arch,
   unsigned int count;
   struct ar_hdr hdr;
   struct stat statbuf;
+  long uid, gid;
 
   firstreal = mapsize + elength + sizeof (struct ar_hdr) + SARMAG;
 
   stat (arch->filename, &statbuf);
+  if ((arch->flags & BFD_DETERMINISTIC_OUTPUT) == 0)
+    {
+      /* Remember the timestamp, to keep it holy.  But fudge it a little.  */
+      bfd_ardata (arch)->armap_timestamp = (statbuf.st_mtime
+                                            + ARMAP_TIME_OFFSET);
+      uid = getuid();
+      gid = getgid();
+    }
+  else
+    {
+      /* If deterministic, we use 0 as the timestamp in the map.
+         Some linkers may require that the archive filesystem modification
+         time is less than (or near to) the archive map timestamp.  Those
+         linkers should not be used with deterministic mode.  (GNU ld and
+         Gold do not have this restriction.)  */
+      bfd_ardata (arch)->armap_timestamp = 0;
+      uid = 0;
+      gid = 0;
+    }
+
   memset (&hdr, ' ', sizeof (struct ar_hdr));
   memcpy (hdr.ar_name, RANLIBMAG, strlen (RANLIBMAG));
-  /* Remember the timestamp, to keep it holy.  But fudge it a little.  */
-  bfd_ardata (arch)->armap_timestamp = statbuf.st_mtime + ARMAP_TIME_OFFSET;
   bfd_ardata (arch)->armap_datepos = (SARMAG
                                      + offsetof (struct ar_hdr, ar_date[0]));
   _bfd_ar_spacepad (hdr.ar_date, sizeof (hdr.ar_date), "%ld",
                     bfd_ardata (arch)->armap_timestamp);
-  _bfd_ar_spacepad (hdr.ar_uid, sizeof (hdr.ar_uid), "%ld", getuid ());
-  _bfd_ar_spacepad (hdr.ar_gid, sizeof (hdr.ar_gid), "%ld", getgid ());
+  _bfd_ar_spacepad (hdr.ar_uid, sizeof (hdr.ar_uid), "%ld", uid);
+  _bfd_ar_spacepad (hdr.ar_gid, sizeof (hdr.ar_gid), "%ld", gid);
   _bfd_ar_spacepad (hdr.ar_size, sizeof (hdr.ar_size), "%-10ld", mapsize);
   memcpy (hdr.ar_fmag, ARFMAG, 2);
   if (bfd_bwrite (&hdr, sizeof (struct ar_hdr), arch)
@@ -2021,6 +2332,10 @@ _bfd_archive_bsd_update_armap_timestamp (bfd *arch)
   struct stat archstat;
   struct ar_hdr hdr;
 
+  /* If creating deterministic archives, just leave the timestamp as-is.  */
+  if ((arch->flags & BFD_DETERMINISTIC_OUTPUT) != 0)
+    return TRUE;
+
   /* Flush writes, get last-write timestamp from file, and compare it
      to the timestamp IN the file.  */
   bfd_flush (arch);
@@ -2031,7 +2346,7 @@ _bfd_archive_bsd_update_armap_timestamp (bfd *arch)
       /* Can't read mod time for some reason.  */
       return TRUE;
     }
-  if (archstat.st_mtime <= bfd_ardata (arch)->armap_timestamp)
+  if (((long) archstat.st_mtime) <= bfd_ardata (arch)->armap_timestamp)
     /* OK by the linker's rules.  */
     return TRUE;
 
@@ -2105,7 +2420,8 @@ coff_write_armap (bfd *arch,
   _bfd_ar_spacepad (hdr.ar_size, sizeof (hdr.ar_size), "%-10ld",
                     mapsize);
   _bfd_ar_spacepad (hdr.ar_date, sizeof (hdr.ar_date), "%ld",
-                    time (NULL));
+                    ((arch->flags & BFD_DETERMINISTIC_OUTPUT) == 0
+                     ? time (NULL) : 0));
   /* This, at least, is what Intel coff sets the values to.  */
   _bfd_ar_spacepad (hdr.ar_uid, sizeof (hdr.ar_uid), "%ld", 0);
   _bfd_ar_spacepad (hdr.ar_gid, sizeof (hdr.ar_gid), "%ld", 0);
@@ -2139,10 +2455,14 @@ coff_write_armap (bfd *arch,
            return FALSE;
          count++;
        }
-      /* Add size of this archive entry.  */
-      archive_member_file_ptr += arelt_size (current) + sizeof (struct ar_hdr);
-      /* Remember aboout the even alignment.  */
-      archive_member_file_ptr += archive_member_file_ptr % 2;
+      archive_member_file_ptr += sizeof (struct ar_hdr);
+      if (! bfd_is_thin_archive (arch))
+        {
+          /* Add size of this archive entry.  */
+          archive_member_file_ptr += arelt_size (current);
+          /* Remember about the even alignment.  */
+          archive_member_file_ptr += archive_member_file_ptr % 2;
+        }
       current = current->archive_next;
     }