Remove ppc860, ppc750cl, ppc7450 insns from common ppc.
[binutils-gdb.git] / bfd / elflink.c
index 0cd85f1d94c28db63dea3e16ed98452d0763ae35..1c0861ba3aa54a1b121d6a635fe6abb8deb4e135 100644 (file)
@@ -54,6 +54,47 @@ struct elf_find_verdep_info
 static bfd_boolean _bfd_elf_fix_symbol_flags
   (struct elf_link_hash_entry *, struct elf_info_failed *);
 
+asection *
+_bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie,
+                            unsigned long r_symndx,
+                            bfd_boolean discard)
+{
+  if (r_symndx >= cookie->locsymcount
+      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+    {
+      struct elf_link_hash_entry *h;
+
+      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
+
+      while (h->root.type == bfd_link_hash_indirect
+            || h->root.type == bfd_link_hash_warning)
+       h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+      if ((h->root.type == bfd_link_hash_defined
+          || h->root.type == bfd_link_hash_defweak)
+          && discarded_section (h->root.u.def.section))
+        return h->root.u.def.section;
+      else
+       return NULL;
+    }
+  else
+    {
+      /* It's not a relocation against a global symbol,
+        but it could be a relocation against a local
+        symbol for a discarded section.  */
+      asection *isec;
+      Elf_Internal_Sym *isym;
+
+      /* Need to: get the symbol; get the section.  */
+      isym = &cookie->locsyms[r_symndx];
+      isec = bfd_section_from_elf_index (cookie->abfd, isym->st_shndx);
+      if (isec != NULL
+         && discard ? discarded_section (isec) : 1)
+       return isec;
+     }
+  return NULL;
+}
+
 /* Define a symbol in a dynamic linkage section.  */
 
 struct elf_link_hash_entry *
@@ -77,9 +118,9 @@ _bfd_elf_define_linkage_sym (bfd *abfd,
     }
 
   bh = &h->root;
+  bed = get_elf_backend_data (abfd);
   if (!_bfd_generic_link_add_one_symbol (info, abfd, name, BSF_GLOBAL,
-                                        sec, 0, NULL, FALSE,
-                                        get_elf_backend_data (abfd)->collect,
+                                        sec, 0, NULL, FALSE, bed->collect,
                                         &bh))
     return NULL;
   h = (struct elf_link_hash_entry *) bh;
@@ -90,7 +131,6 @@ _bfd_elf_define_linkage_sym (bfd *abfd,
   if (ELF_ST_VISIBILITY (h->other) != STV_INTERNAL)
     h->other = (h->other & ~ELF_ST_VISIBILITY (-1)) | STV_HIDDEN;
 
-  bed = get_elf_backend_data (abfd);
   (*bed->elf_backend_hide_symbol) (info, h, TRUE);
   return h;
 }
@@ -845,7 +885,7 @@ _bfd_elf_link_renumber_dynsyms (bfd *output_bfd,
 
 static void
 elf_merge_st_other (bfd *abfd, struct elf_link_hash_entry *h,
-                   const Elf_Internal_Sym *isym,
+                   const Elf_Internal_Sym *isym, asection *sec,
                    bfd_boolean definition, bfd_boolean dynamic)
 {
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
@@ -866,7 +906,9 @@ elf_merge_st_other (bfd *abfd, struct elf_link_hash_entry *h,
       if (symvis - 1 < hvis - 1)
        h->other = symvis | (h->other & ~ELF_ST_VISIBILITY (-1));
     }
-  else if (definition && ELF_ST_VISIBILITY (isym->st_other) != STV_DEFAULT)
+  else if (definition
+          && ELF_ST_VISIBILITY (isym->st_other) != STV_DEFAULT
+          && (sec->flags & SEC_READONLY) == 0)
     h->protected_def = 1;
 }
 
@@ -1418,7 +1460,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
       /* Merge st_other.  If the symbol already has a dynamic index,
         but visibility says it should not be visible, turn it into a
         local symbol.  */
-      elf_merge_st_other (abfd, h, sym, newdef, newdyn);
+      elf_merge_st_other (abfd, h, sym, sec, newdef, newdyn);
       if (h->dynindx != -1)
        switch (ELF_ST_VISIBILITY (h->other))
          {
@@ -2672,14 +2714,14 @@ _bfd_elf_adjust_dynamic_copy (struct bfd_link_info *info,
   /* Increment the size of DYNBSS to make room for the symbol.  */
   dynbss->size += h->size;
 
-  if (h->protected_def)
-    {
-      info->callbacks->einfo
-       (_("%P: copy reloc against protected `%T' is invalid\n"),
-        h->root.root.string);
-      bfd_set_error (bfd_error_bad_value);
-      return FALSE;
-    }
+  /* No error if extern_protected_data is true.  */
+  if (h->protected_def
+      && (!info->extern_protected_data
+         || (info->extern_protected_data < 0
+             && !get_elf_backend_data (dynbss->owner)->extern_protected_data)))
+    info->callbacks->einfo
+      (_("%P: copy reloc against protected `%T' is dangerous\n"),
+       h->root.root.string);
 
   return TRUE;
 }
@@ -2836,8 +2878,12 @@ _bfd_elf_symbol_refs_local_p (struct elf_link_hash_entry *h,
 
   bed = get_elf_backend_data (hash_table->dynobj);
 
-  /* STV_PROTECTED non-function symbols are local.  */
-  if (!bed->is_function_type (h->type))
+  /* If extern_protected_data is false, STV_PROTECTED non-function
+     symbols are local.  */
+  if ((!info->extern_protected_data
+       || (info->extern_protected_data < 0
+          && !bed->extern_protected_data))
+      && !bed->is_function_type (h->type))
     return TRUE;
 
   /* Function pointer equality tests may require that STV_PROTECTED
@@ -2936,6 +2982,10 @@ elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef)
   if (abfd == NULL)
     return FALSE;
 
+  /* Return FALSE if the object has been claimed by plugin.  */
+  if (abfd->plugin_format == bfd_plugin_yes)
+    return FALSE;
+
   if (! bfd_check_format (abfd, bfd_object))
     return FALSE;
 
@@ -4356,7 +4406,7 @@ error_free_dyn:
            }
 
          /* Merge st_other field.  */
-         elf_merge_st_other (abfd, h, isym, definition, dynamic);
+         elf_merge_st_other (abfd, h, isym, sec, definition, dynamic);
 
          /* We don't want to make debug symbol dynamic.  */
          if (definition && (sec->flags & SEC_DEBUGGING) && !info->relocatable)
@@ -7394,7 +7444,7 @@ struct elf_final_link_info
   /* Output BFD.  */
   bfd *output_bfd;
   /* Symbol string table.  */
-  struct bfd_strtab_hash *symstrtab;
+  struct elf_strtab_hash *symstrtab;
   /* .dynsym section.  */
   asection *dynsym_sec;
   /* .hash section.  */
@@ -7421,16 +7471,8 @@ struct elf_final_link_info
   /* Array large enough to hold a section pointer for each local
      symbol of any input BFD.  */
   asection **sections;
-  /* Buffer to hold swapped out symbols.  */
-  bfd_byte *symbuf;
-  /* And one for symbol section indices.  */
+  /* Buffer for SHT_SYMTAB_SHNDX section.  */
   Elf_External_Sym_Shndx *symshndxbuf;
-  /* Number of swapped out symbols in buffer.  */
-  size_t symbuf_count;
-  /* Number of symbols which fit in symbuf.  */
-  size_t symbuf_size;
-  /* And same for symshndxbuf.  */
-  size_t shndxbuf_size;
   /* Number of STT_FILE syms seen.  */
   size_t filesym_count;
 };
@@ -7791,28 +7833,34 @@ put_value (bfd_vma size,
 {
   location += (size - chunksz);
 
-  for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8))
+  for (; size; size -= chunksz, location -= chunksz)
     {
       switch (chunksz)
        {
-       default:
-       case 0:
-         abort ();
        case 1:
          bfd_put_8 (input_bfd, x, location);
+         x >>= 8;
          break;
        case 2:
          bfd_put_16 (input_bfd, x, location);
+         x >>= 16;
          break;
        case 4:
          bfd_put_32 (input_bfd, x, location);
+         /* Computed this way because x >>= 32 is undefined if x is a 32-bit value.  */
+         x >>= 16;
+         x >>= 16;
          break;
-       case 8:
 #ifdef BFD64
+       case 8:
          bfd_put_64 (input_bfd, x, location);
-#else
-         abort ();
+         /* Computed this way because x >>= 64 is undefined if x is a 64-bit value.  */
+         x >>= 32;
+         x >>= 32;
+         break;
 #endif
+       default:
+         abort ();
          break;
        }
     }
@@ -8514,47 +8562,21 @@ elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
   return ret;
 }
 
-/* Flush the output symbols to the file.  */
-
-static bfd_boolean
-elf_link_flush_output_syms (struct elf_final_link_info *flinfo,
-                           const struct elf_backend_data *bed)
-{
-  if (flinfo->symbuf_count > 0)
-    {
-      Elf_Internal_Shdr *hdr;
-      file_ptr pos;
-      bfd_size_type amt;
-
-      hdr = &elf_tdata (flinfo->output_bfd)->symtab_hdr;
-      pos = hdr->sh_offset + hdr->sh_size;
-      amt = flinfo->symbuf_count * bed->s->sizeof_sym;
-      if (bfd_seek (flinfo->output_bfd, pos, SEEK_SET) != 0
-         || bfd_bwrite (flinfo->symbuf, amt, flinfo->output_bfd) != amt)
-       return FALSE;
-
-      hdr->sh_size += amt;
-      flinfo->symbuf_count = 0;
-    }
-
-  return TRUE;
-}
-
-/* Add a symbol to the output symbol table.  */
+/* Add a symbol to the output symbol string table.  */
 
 static int
-elf_link_output_sym (struct elf_final_link_info *flinfo,
-                    const char *name,
-                    Elf_Internal_Sym *elfsym,
-                    asection *input_sec,
-                    struct elf_link_hash_entry *h)
-{
-  bfd_byte *dest;
-  Elf_External_Sym_Shndx *destshndx;
+elf_link_output_symstrtab (struct elf_final_link_info *flinfo,
+                          const char *name,
+                          Elf_Internal_Sym *elfsym,
+                          asection *input_sec,
+                          struct elf_link_hash_entry *h)
+{
   int (*output_symbol_hook)
     (struct bfd_link_info *, const char *, Elf_Internal_Sym *, asection *,
      struct elf_link_hash_entry *);
+  struct elf_link_hash_table *hash_table;
   const struct elf_backend_data *bed;
+  bfd_size_type strtabsize;
 
   BFD_ASSERT (elf_onesymtab (flinfo->output_bfd));
 
@@ -8567,49 +8589,119 @@ elf_link_output_sym (struct elf_final_link_info *flinfo,
        return ret;
     }
 
-  if (name == NULL || *name == '\0')
-    elfsym->st_name = 0;
-  else if (input_sec->flags & SEC_EXCLUDE)
-    elfsym->st_name = 0;
+  if (name == NULL
+      || *name == '\0'
+      || (input_sec->flags & SEC_EXCLUDE))
+    elfsym->st_name = (unsigned long) -1;
   else
     {
-      elfsym->st_name = (unsigned long) _bfd_stringtab_add (flinfo->symstrtab,
-                                                           name, TRUE, FALSE);
+      /* Call _bfd_elf_strtab_offset after _bfd_elf_strtab_finalize
+        to get the final offset for st_name.  */
+      elfsym->st_name
+       = (unsigned long) _bfd_elf_strtab_add (flinfo->symstrtab,
+                                              name, FALSE);
       if (elfsym->st_name == (unsigned long) -1)
        return 0;
     }
 
-  if (flinfo->symbuf_count >= flinfo->symbuf_size)
+  hash_table = elf_hash_table (flinfo->info);
+  strtabsize = hash_table->strtabsize;
+  if (strtabsize <= hash_table->strtabcount)
     {
-      if (! elf_link_flush_output_syms (flinfo, bed))
+      strtabsize += strtabsize;
+      hash_table->strtabsize = strtabsize;
+      strtabsize *= sizeof (*hash_table->strtab);
+      hash_table->strtab
+       = (struct elf_sym_strtab *) bfd_realloc (hash_table->strtab,
+                                                strtabsize);
+      if (hash_table->strtab == NULL)
        return 0;
     }
+  hash_table->strtab[hash_table->strtabcount].sym = *elfsym;
+  hash_table->strtab[hash_table->strtabcount].dest_index
+    = hash_table->strtabcount;
+  hash_table->strtab[hash_table->strtabcount].destshndx_index
+    = flinfo->symshndxbuf ? bfd_get_symcount (flinfo->output_bfd) : 0;
+
+  bfd_get_symcount (flinfo->output_bfd) += 1;
+  hash_table->strtabcount += 1;
+
+  return 1;
+}
 
-  dest = flinfo->symbuf + flinfo->symbuf_count * bed->s->sizeof_sym;
-  destshndx = flinfo->symshndxbuf;
-  if (destshndx != NULL)
+/* Swap symbols out to the symbol table and flush the output symbols to
+   the file.  */
+
+static bfd_boolean
+elf_link_swap_symbols_out (struct elf_final_link_info *flinfo)
+{
+  struct elf_link_hash_table *hash_table = elf_hash_table (flinfo->info);
+  bfd_size_type amt, i;
+  const struct elf_backend_data *bed;
+  bfd_byte *symbuf;
+  Elf_Internal_Shdr *hdr;
+  file_ptr pos;
+  bfd_boolean ret;
+
+  if (!hash_table->strtabcount)
+    return TRUE;
+
+  BFD_ASSERT (elf_onesymtab (flinfo->output_bfd));
+
+  bed = get_elf_backend_data (flinfo->output_bfd);
+
+  amt = bed->s->sizeof_sym * hash_table->strtabcount;
+  symbuf = (bfd_byte *) bfd_malloc (amt);
+  if (symbuf == NULL)
+    return FALSE;
+  
+  if (flinfo->symshndxbuf)
     {
-      if (bfd_get_symcount (flinfo->output_bfd) >= flinfo->shndxbuf_size)
+      amt = (sizeof (Elf_External_Sym_Shndx)
+            * (bfd_get_symcount (flinfo->output_bfd)));
+      flinfo->symshndxbuf = (Elf_External_Sym_Shndx *) bfd_zmalloc (amt);
+      if (flinfo->symshndxbuf == NULL)
        {
-         bfd_size_type amt;
-
-         amt = flinfo->shndxbuf_size * sizeof (Elf_External_Sym_Shndx);
-         destshndx = (Elf_External_Sym_Shndx *) bfd_realloc (destshndx,
-                                                             amt * 2);
-         if (destshndx == NULL)
-           return 0;
-         flinfo->symshndxbuf = destshndx;
-         memset ((char *) destshndx + amt, 0, amt);
-         flinfo->shndxbuf_size *= 2;
+         free (symbuf);
+         return FALSE;
        }
-      destshndx += bfd_get_symcount (flinfo->output_bfd);
     }
 
-  bed->s->swap_symbol_out (flinfo->output_bfd, elfsym, dest, destshndx);
-  flinfo->symbuf_count += 1;
-  bfd_get_symcount (flinfo->output_bfd) += 1;
+  for (i = 0; i < hash_table->strtabcount; i++)
+    {
+      struct elf_sym_strtab *elfsym = &hash_table->strtab[i];
+      if (elfsym->sym.st_name == (unsigned long) -1)
+       elfsym->sym.st_name = 0;
+      else
+       elfsym->sym.st_name
+         = (unsigned long) _bfd_elf_strtab_offset (flinfo->symstrtab,
+                                                   elfsym->sym.st_name);
+      bed->s->swap_symbol_out (flinfo->output_bfd, &elfsym->sym,
+                              ((bfd_byte *) symbuf
+                               + (elfsym->dest_index
+                                  * bed->s->sizeof_sym)),
+                              (flinfo->symshndxbuf
+                               + elfsym->destshndx_index));
+    }
+
+  hdr = &elf_tdata (flinfo->output_bfd)->symtab_hdr;
+  pos = hdr->sh_offset + hdr->sh_size;
+  amt = hash_table->strtabcount * bed->s->sizeof_sym;
+  if (bfd_seek (flinfo->output_bfd, pos, SEEK_SET) == 0
+      && bfd_bwrite (symbuf, amt, flinfo->output_bfd) == amt)
+    {
+      hdr->sh_size += amt;
+      ret = TRUE;
+    }
+  else
+    ret = FALSE;
 
-  return 1;
+  free (symbuf);
+
+  free (hash_table->strtab);
+  hash_table->strtab = NULL;
+
+  return ret;
 }
 
 /* Return TRUE if the dynamic symbol SYM in ABFD is supported.  */
@@ -9235,15 +9327,16 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data)
       memset (&fsym, 0, sizeof (fsym));
       fsym.st_info = ELF_ST_INFO (STB_LOCAL, STT_FILE);
       fsym.st_shndx = SHN_ABS;
-      if (!elf_link_output_sym (eoinfo->flinfo, NULL, &fsym,
-                               bfd_und_section_ptr, NULL))
+      if (!elf_link_output_symstrtab (eoinfo->flinfo, NULL, &fsym,
+                                     bfd_und_section_ptr, NULL))
        return FALSE;
 
       eoinfo->file_sym_done = TRUE;
     }
 
   indx = bfd_get_symcount (flinfo->output_bfd);
-  ret = elf_link_output_sym (flinfo, h->root.root.string, &sym, input_sec, h);
+  ret = elf_link_output_symstrtab (flinfo, h->root.root.string, &sym,
+                                  input_sec, h);
   if (ret == 0)
     {
       eoinfo->failed = TRUE;
@@ -9269,6 +9362,7 @@ elf_section_ignore_discarded_relocs (asection *sec)
     {
     case SEC_INFO_TYPE_STABS:
     case SEC_INFO_TYPE_EH_FRAME:
+    case SEC_INFO_TYPE_EH_FRAME_ENTRY:
       return TRUE;
     default:
       break;
@@ -9536,10 +9630,11 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
          memset (&osym, 0, sizeof (osym));
          osym.st_info = ELF_ST_INFO (STB_LOCAL, STT_FILE);
          osym.st_shndx = SHN_ABS;
-         if (!elf_link_output_sym (flinfo,
-                                   (input_bfd->lto_output ? NULL
-                                    : input_bfd->filename),
-                                   &osym, bfd_abs_section_ptr, NULL))
+         if (!elf_link_output_symstrtab (flinfo,
+                                         (input_bfd->lto_output ? NULL
+                                          : input_bfd->filename),
+                                         &osym, bfd_abs_section_ptr,
+                                         NULL))
            return FALSE;
        }
 
@@ -9571,7 +9666,7 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
        }
 
       indx = bfd_get_symcount (output_bfd);
-      ret = elf_link_output_sym (flinfo, name, &osym, isec, NULL);
+      ret = elf_link_output_symstrtab (flinfo, name, &osym, isec, NULL);
       if (ret == 0)
        return FALSE;
       else if (ret == 1)
@@ -9654,7 +9749,8 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
                  sym.st_value += o->output_offset;
 
                  indx = bfd_get_symcount (output_bfd);
-                 ret = elf_link_output_sym (flinfo, name, &sym, o, NULL);
+                 ret = elf_link_output_symstrtab (flinfo, name, &sym, o,
+                                                  NULL);
                  if (ret == 0)
                    return FALSE;
                  else if (ret == 1)
@@ -10129,8 +10225,9 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
                            }
 
                          indx = bfd_get_symcount (output_bfd);
-                         ret = elf_link_output_sym (flinfo, name, &sym, sec,
-                                                    NULL);
+                         ret = elf_link_output_symstrtab (flinfo, name,
+                                                          &sym, sec,
+                                                          NULL);
                          if (ret == 0)
                            return FALSE;
                          else if (ret == 1)
@@ -10200,6 +10297,14 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
              return FALSE;
          }
          break;
+       case SEC_INFO_TYPE_EH_FRAME_ENTRY:
+         {
+           if (! _bfd_elf_write_section_eh_frame_entry (output_bfd,
+                                                        flinfo->info,
+                                                        o, contents))
+             return FALSE;
+         }
+         break;
        default:
          {
            /* FIXME: octets_per_byte.  */
@@ -10569,7 +10674,7 @@ elf_final_link_free (bfd *obfd, struct elf_final_link_info *flinfo)
   asection *o;
 
   if (flinfo->symstrtab != NULL)
-    _bfd_stringtab_free (flinfo->symstrtab);
+    _bfd_elf_strtab_free (flinfo->symstrtab);
   if (flinfo->contents != NULL)
     free (flinfo->contents);
   if (flinfo->external_relocs != NULL)
@@ -10586,8 +10691,6 @@ elf_final_link_free (bfd *obfd, struct elf_final_link_info *flinfo)
     free (flinfo->indices);
   if (flinfo->sections != NULL)
     free (flinfo->sections);
-  if (flinfo->symbuf != NULL)
-    free (flinfo->symbuf);
   if (flinfo->symshndxbuf != NULL)
     free (flinfo->symshndxbuf);
   for (o = obfd->sections; o != NULL; o = o->next)
@@ -10645,7 +10748,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
   flinfo.info = info;
   flinfo.output_bfd = abfd;
-  flinfo.symstrtab = _bfd_elf_stringtab_init ();
+  flinfo.symstrtab = _bfd_elf_strtab_init ();
   if (flinfo.symstrtab == NULL)
     return FALSE;
 
@@ -10672,10 +10775,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   flinfo.internal_syms = NULL;
   flinfo.indices = NULL;
   flinfo.sections = NULL;
-  flinfo.symbuf = NULL;
   flinfo.symshndxbuf = NULL;
-  flinfo.symbuf_count = 0;
-  flinfo.shndxbuf_size = 0;
   flinfo.filesym_count = 0;
 
   /* The object attributes have been merged.  Remove the input
@@ -10875,6 +10975,21 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
         to count upwards while actually outputting the relocations.  */
       esdo->rel.count = 0;
       esdo->rela.count = 0;
+
+      if (esdo->this_hdr.sh_offset == (file_ptr) -1)
+       {
+         /* Cache the section contents so that they can be compressed
+            later.  Use bfd_malloc since it will be freed by
+            bfd_compress_section_contents.  */
+         unsigned char *contents = esdo->this_hdr.contents;
+         if ((o->flags & SEC_ELF_COMPRESS) == 0 || contents != NULL)
+           abort ();
+         contents
+           = (unsigned char *) bfd_malloc (esdo->this_hdr.sh_size);
+         if (contents == NULL)
+           goto error_return;
+         esdo->this_hdr.contents = contents;
+       }
     }
 
   /* We have now assigned file positions for all the sections except
@@ -10892,27 +11007,18 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   /* sh_offset is set just below.  */
   symtab_hdr->sh_addralign = (bfd_vma) 1 << bed->s->log_file_align;
 
-  /* Allocate a buffer to hold swapped out symbols.  This is to avoid
-     continuously seeking to the right position in the file.  */
-  if (! info->keep_memory || max_sym_count < 20)
-    flinfo.symbuf_size = 20;
-  else
-    flinfo.symbuf_size = max_sym_count;
-  amt = flinfo.symbuf_size;
-  amt *= bed->s->sizeof_sym;
-  flinfo.symbuf = (bfd_byte *) bfd_malloc (amt);
-  if (flinfo.symbuf == NULL)
+  if (max_sym_count < 20)
+    max_sym_count = 20;
+  elf_hash_table (info)->strtabsize = max_sym_count;
+  amt = max_sym_count * sizeof (struct elf_sym_strtab);
+  elf_hash_table (info)->strtab
+    = (struct elf_sym_strtab *) bfd_malloc (amt);
+  if (elf_hash_table (info)->strtab == NULL)
     goto error_return;
-  if (elf_numsections (abfd) > (SHN_LORESERVE & 0xFFFF))
-    {
-      /* Wild guess at number of output symbols.  realloc'd as needed.  */
-      amt = 2 * max_sym_count + elf_numsections (abfd) + 1000;
-      flinfo.shndxbuf_size = amt;
-      amt *= sizeof (Elf_External_Sym_Shndx);
-      flinfo.symshndxbuf = (Elf_External_Sym_Shndx *) bfd_zmalloc (amt);
-      if (flinfo.symshndxbuf == NULL)
-       goto error_return;
-    }
+  /* The real buffer will be allocated in elf_link_swap_symbols_out.  */
+  flinfo.symshndxbuf
+    = (elf_numsections (abfd) > (SHN_LORESERVE & 0xFFFF)
+       ? (Elf_External_Sym_Shndx *) -1 : NULL);
 
   if (info->strip != strip_all || emit_relocs)
     {
@@ -10932,8 +11038,8 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
       elfsym.st_other = 0;
       elfsym.st_shndx = SHN_UNDEF;
       elfsym.st_target_internal = 0;
-      if (elf_link_output_sym (&flinfo, NULL, &elfsym, bfd_und_section_ptr,
-                              NULL) != 1)
+      if (elf_link_output_symstrtab (&flinfo, NULL, &elfsym,
+                                    bfd_und_section_ptr, NULL) != 1)
        goto error_return;
 
       /* Output a symbol for each section.  We output these even if we are
@@ -10956,7 +11062,8 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
              elfsym.st_shndx = i;
              if (!info->relocatable)
                elfsym.st_value = o->vma;
-             if (elf_link_output_sym (&flinfo, NULL, &elfsym, o, NULL) != 1)
+             if (elf_link_output_symstrtab (&flinfo, NULL, &elfsym, o,
+                                            NULL) != 1)
                goto error_return;
            }
        }
@@ -11055,6 +11162,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
        return FALSE;
     }
 
+  if (!_bfd_elf_fixup_eh_frame_hdr (info))
+    return FALSE;
+
   /* Since ELF permits relocations to be against local symbols, we
      must have the local symbols available when we do the relocations.
      Since we would rather only read the local symbols once, and we
@@ -11170,7 +11280,8 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
         struct elf_link_hash_entry *);
 
       if (! ((*bed->elf_backend_output_arch_local_syms)
-            (abfd, info, &flinfo, (out_sym_func) elf_link_output_sym)))
+            (abfd, info, &flinfo,
+             (out_sym_func) elf_link_output_symstrtab)))
        return FALSE;
     }
 
@@ -11281,12 +11392,16 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
         struct elf_link_hash_entry *);
 
       if (! ((*bed->elf_backend_output_arch_syms)
-            (abfd, info, &flinfo, (out_sym_func) elf_link_output_sym)))
+            (abfd, info, &flinfo,
+             (out_sym_func) elf_link_output_symstrtab)))
        return FALSE;
     }
 
-  /* Flush all symbols to the file.  */
-  if (! elf_link_flush_output_syms (&flinfo, bed))
+  /* Finalize the .strtab section.  */
+  _bfd_elf_strtab_finalize (flinfo.symstrtab);
+
+  /* Swap out the .strtab section. */
+  if (!elf_link_swap_symbols_out (&flinfo))
     return FALSE;
 
   /* Now we know the size of the symtab section.  */
@@ -11319,7 +11434,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
       symstrtab_hdr->sh_type = SHT_STRTAB;
       symstrtab_hdr->sh_flags = 0;
       symstrtab_hdr->sh_addr = 0;
-      symstrtab_hdr->sh_size = _bfd_stringtab_size (flinfo.symstrtab);
+      symstrtab_hdr->sh_size = _bfd_elf_strtab_size (flinfo.symstrtab);
       symstrtab_hdr->sh_entsize = 0;
       symstrtab_hdr->sh_link = 0;
       symstrtab_hdr->sh_info = 0;
@@ -11331,7 +11446,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
       elf_next_file_pos (abfd) = off;
 
       if (bfd_seek (abfd, symstrtab_hdr->sh_offset, SEEK_SET) != 0
-         || ! _bfd_stringtab_emit (abfd, flinfo.symstrtab))
+         || ! _bfd_elf_strtab_emit (abfd, flinfo.symstrtab))
        return FALSE;
     }
 
@@ -11964,6 +12079,11 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
        }
     }
 
+  eh_frame = elf_section_eh_frame_entry (sec);
+  if (ret && eh_frame && !eh_frame->gc_mark)
+    if (!_bfd_elf_gc_mark (info, eh_frame, gc_mark_hook))
+      ret = FALSE;
+
   return ret;
 }
 
@@ -12094,7 +12214,6 @@ _bfd_elf_gc_mark_extra_sections (struct bfd_link_info *info,
                                isec->name, ilen) == 0)
                  {
                    dsec->gc_mark = 0;
-                   break;
                  }
              }
          }
@@ -12152,7 +12271,8 @@ elf_gc_sweep (bfd *abfd, struct bfd_link_info *info)
     {
       asection *o;
 
-      if (bfd_get_flavour (sub) != bfd_target_elf_flavour)
+      if (bfd_get_flavour (sub) != bfd_target_elf_flavour
+         || !(*bed->relocs_compatible) (sub->xvec, abfd->xvec))
        continue;
 
       for (o = sub->sections; o != NULL; o = o->next)
@@ -12379,6 +12499,36 @@ _bfd_elf_gc_keep (struct bfd_link_info *info)
     }
 }
 
+bfd_boolean
+bfd_elf_parse_eh_frame_entries (bfd *abfd ATTRIBUTE_UNUSED,
+                               struct bfd_link_info *info)
+{
+  bfd *ibfd = info->input_bfds;
+
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      asection *sec;
+      struct elf_reloc_cookie cookie;
+
+      if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
+       continue;
+
+      if (!init_reloc_cookie (&cookie, info, ibfd))
+       return FALSE;
+
+      for (sec = ibfd->sections; sec; sec = sec->next)
+       {
+         if (CONST_STRNEQ (bfd_section_name (ibfd, sec), ".eh_frame_entry")
+             && init_reloc_cookie_rels (&cookie, info, ibfd, sec))
+           {
+             _bfd_elf_parse_eh_frame_entry (info, sec, &cookie);
+             fini_reloc_cookie_rels (&cookie, sec);
+           }
+       }
+    }
+  return TRUE;
+}
+
 /* Do mark and sweep of unused sections.  */
 
 bfd_boolean
@@ -12402,7 +12552,9 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
 
   /* Try to parse each bfd's .eh_frame section.  Point elf_eh_frame_section
      at the .eh_frame section if we can mark the FDEs individually.  */
-  for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
+  for (sub = info->input_bfds;
+       info->eh_frame_hdr_type != COMPACT_EH_HDR && sub != NULL;
+       sub = sub->link.next)
     {
       asection *sec;
       struct elf_reloc_cookie cookie;
@@ -12439,7 +12591,8 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
     {
       asection *o;
 
-      if (bfd_get_flavour (sub) != bfd_target_elf_flavour)
+      if (bfd_get_flavour (sub) != bfd_target_elf_flavour
+         || !(*bed->relocs_compatible) (sub->xvec, abfd->xvec))
        continue;
 
       /* Start at sections marked with SEC_KEEP (ref _bfd_elf_gc_keep).
@@ -12905,7 +13058,9 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
        }
     }
 
-  o = bfd_get_section_by_name (output_bfd, ".eh_frame");
+  o = NULL;
+  if (info->eh_frame_hdr_type != COMPACT_EH_HDR)
+    o = bfd_get_section_by_name (output_bfd, ".eh_frame");
   if (o != NULL)
     {
       asection *i;
@@ -12953,7 +13108,10 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
        }
     }
 
-  if (info->eh_frame_hdr
+  if (info->eh_frame_hdr_type == COMPACT_EH_HDR)
+    _bfd_elf_end_eh_frame_parsing (info);
+
+  if (info->eh_frame_hdr_type
       && !info->relocatable
       && _bfd_elf_discard_section_eh_frame_hdr (output_bfd, info))
     changed = 1;
@@ -13259,7 +13417,7 @@ _bfd_elf_copy_link_hash_symbol_type (bfd *abfd,
   ehdest->target_internal = ehsrc->target_internal;
 
   isym.st_other = ehsrc->other;
-  elf_merge_st_other (abfd, ehdest, &isym, TRUE, FALSE);
+  elf_merge_st_other (abfd, ehdest, &isym, NULL, TRUE, FALSE);
 }
 
 /* Append a RELA relocation REL to section S in BFD.  */