* elf32-bfin.c (bfin_check_relocs, bfin_relocate_section,
authorBernd Schmidt <bernds@codesourcery.com>
Wed, 16 Jul 2008 15:27:05 +0000 (15:27 +0000)
committerBernd Schmidt <bernds@codesourcery.com>
Wed, 16 Jul 2008 15:27:05 +0000 (15:27 +0000)
bfin_final_link_relocate, bfin_gc_mark_hook, bfin_gc_sweep_hook,
ELF_DYNAMIC_INTERPRETER, DEFAULT_STACK_SIZE,
struct _bfinfdpic_dynamic_got_info): Moved around to keep FD-PIC code
separate from non-FD-PIC.

bfd/ChangeLog
bfd/elf32-bfin.c

index 443786bc313016ce577cbd0e86cc6433a3c7c77e..bcb636ea35b8c3f4a7dcb23442400297f3ce40b0 100644 (file)
@@ -1,3 +1,11 @@
+2008-07-16  Bernd Schmidt  <bernd.schmidt@analog.com>
+
+       * elf32-bfin.c (bfin_check_relocs, bfin_relocate_section,
+       bfin_final_link_relocate, bfin_gc_mark_hook, bfin_gc_sweep_hook,
+       ELF_DYNAMIC_INTERPRETER, DEFAULT_STACK_SIZE,
+       struct _bfinfdpic_dynamic_got_info): Moved around to keep FD-PIC code
+       separate from non-FD-PIC.
+
 2008-07-14  DJ Delorie  <dj@redhat.com>
 
        * elf-m10300.c (mn10300_elf_final_link_relocate): Correct overflow
index b899b6e9eea0709dd755e45f52a7a9c720ce6666..a5c24003d665db4f293c4a18782e0e0117443da0 100644 (file)
@@ -1131,2012 +1131,2114 @@ bfin_is_local_label_name (
 
   return _bfd_elf_is_local_label_name (abfd, label);
 }
+\f
+/* Look through the relocs for a section during the first phase, and
+   allocate space in the global offset table or procedure linkage
+   table.  */
 
-extern const bfd_target bfd_elf32_bfinfdpic_vec;
-#define IS_FDPIC(bfd) ((bfd)->xvec == &bfd_elf32_bfinfdpic_vec)
-
-/* An extension of the elf hash table data structure, containing some
-   additional Blackfin-specific data.  */
-struct bfinfdpic_elf_link_hash_table
+static bfd_boolean
+bfin_check_relocs (bfd * abfd,
+                  struct bfd_link_info *info,
+                  asection *sec,
+                   const Elf_Internal_Rela *relocs)
 {
-  struct elf_link_hash_table elf;
-
-  /* A pointer to the .got section.  */
+  bfd *dynobj;
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  bfd_signed_vma *local_got_refcounts;
+  const Elf_Internal_Rela *rel;
+  const Elf_Internal_Rela *rel_end;
   asection *sgot;
-  /* A pointer to the .rel.got section.  */
-  asection *sgotrel;
-  /* A pointer to the .rofixup section.  */
-  asection *sgotfixup;
-  /* A pointer to the .plt section.  */
-  asection *splt;
-  /* A pointer to the .rel.plt section.  */
-  asection *spltrel;
-  /* GOT base offset.  */
-  bfd_vma got0;
-  /* Location of the first non-lazy PLT entry, i.e., the number of
-     bytes taken by lazy PLT entries.  */
-  bfd_vma plt0;
-  /* A hash table holding information about which symbols were
-     referenced with which PIC-related relocations.  */
-  struct htab *relocs_info;
-};
-
-/* Get the Blackfin ELF linker hash table from a link_info structure.  */
-
-#define bfinfdpic_hash_table(info) \
-  ((struct bfinfdpic_elf_link_hash_table *) ((info)->hash))
-
-#define bfinfdpic_got_section(info) \
-  (bfinfdpic_hash_table (info)->sgot)
-#define bfinfdpic_gotrel_section(info) \
-  (bfinfdpic_hash_table (info)->sgotrel)
-#define bfinfdpic_gotfixup_section(info) \
-  (bfinfdpic_hash_table (info)->sgotfixup)
-#define bfinfdpic_plt_section(info) \
-  (bfinfdpic_hash_table (info)->splt)
-#define bfinfdpic_pltrel_section(info) \
-  (bfinfdpic_hash_table (info)->spltrel)
-#define bfinfdpic_relocs_info(info) \
-  (bfinfdpic_hash_table (info)->relocs_info)
-#define bfinfdpic_got_initial_offset(info) \
-  (bfinfdpic_hash_table (info)->got0)
-#define bfinfdpic_plt_initial_offset(info) \
-  (bfinfdpic_hash_table (info)->plt0)
-
-/* Create a Blackfin ELF linker hash table.  */
+  asection *srelgot;
+  asection *sreloc;
+  if (info->relocatable)
+    return TRUE;
 
-static struct bfd_link_hash_table *
-bfinfdpic_elf_link_hash_table_create (bfd *abfd)
-{
-  struct bfinfdpic_elf_link_hash_table *ret;
-  bfd_size_type amt = sizeof (struct bfinfdpic_elf_link_hash_table);
+  dynobj = elf_hash_table (info)->dynobj;
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (abfd);
+  local_got_refcounts = elf_local_got_refcounts (abfd);
 
-  ret = bfd_zalloc (abfd, amt);
-  if (ret == NULL)
-    return NULL;
+  sgot = NULL;
+  srelgot = NULL;
+  sreloc = NULL;
 
-  if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
-                                     _bfd_elf_link_hash_newfunc,
-                                     sizeof (struct elf_link_hash_entry)))
+  rel_end = relocs + sec->reloc_count;
+  for (rel = relocs; rel < rel_end; rel++)
     {
-      free (ret);
-      return NULL;
-    }
+      unsigned long r_symndx;
+      struct elf_link_hash_entry *h;
 
-  return &ret->elf.root;
-}
+      r_symndx = ELF32_R_SYM (rel->r_info);
+      if (r_symndx < symtab_hdr->sh_info)
+       h = NULL;
+      else
+       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 
-/* Decide whether a reference to a symbol can be resolved locally or
-   not.  If the symbol is protected, we want the local address, but
-   its function descriptor must be assigned by the dynamic linker.  */
-#define BFINFDPIC_SYM_LOCAL(INFO, H) \
-  (_bfd_elf_symbol_refs_local_p ((H), (INFO), 1) \
-   || ! elf_hash_table (INFO)->dynamic_sections_created)
-#define BFINFDPIC_FUNCDESC_LOCAL(INFO, H) \
-  ((H)->dynindx == -1 || ! elf_hash_table (INFO)->dynamic_sections_created)
+      switch (ELF32_R_TYPE (rel->r_info))
+       {
+       /* This relocation describes the C++ object vtable hierarchy.
+           Reconstruct it for later use during GC.  */
+        case R_BFIN_GNU_VTINHERIT:
+          if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+            return FALSE;
+          break;
 
-/* This structure collects information on what kind of GOT, PLT or
-   function descriptors are required by relocations that reference a
-   certain symbol.  */
-struct bfinfdpic_relocs_info
-{
-  /* The index of the symbol, as stored in the relocation r_info, if
-     we have a local symbol; -1 otherwise.  */
-  long symndx;
-  union
-  {
-    /* The input bfd in which the symbol is defined, if it's a local
-       symbol.  */
-    bfd *abfd;
-    /* If symndx == -1, the hash table entry corresponding to a global
-       symbol (even if it turns out to bind locally, in which case it
-       should ideally be replaced with section's symndx + addend).  */
-    struct elf_link_hash_entry *h;
-  } d;
-  /* The addend of the relocation that references the symbol.  */
-  bfd_vma addend;
+        /* This relocation describes which C++ vtable entries
+           are actually used.  Record for later use during GC.  */
+        case R_BFIN_GNU_VTENTRY:
+          BFD_ASSERT (h != NULL);
+          if (h != NULL
+              && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+            return FALSE;
+          break;
 
-  /* The fields above are used to identify an entry.  The fields below
-     contain information on how an entry is used and, later on, which
-     locations it was assigned.  */
-  /* The following 2 fields record whether the symbol+addend above was
-     ever referenced with a GOT relocation.  The 17M4 suffix indicates a
-     GOT17M4 relocation; hilo is used for GOTLO/GOTHI pairs.  */
-  unsigned got17m4;
-  unsigned gothilo;
-  /* Whether a FUNCDESC relocation references symbol+addend.  */
-  unsigned fd;
-  /* Whether a FUNCDESC_GOT relocation references symbol+addend.  */
-  unsigned fdgot17m4;
-  unsigned fdgothilo;
-  /* Whether a FUNCDESC_GOTOFF relocation references symbol+addend.  */
-  unsigned fdgoff17m4;
-  unsigned fdgoffhilo;
-  /* Whether symbol+addend is referenced with GOTOFF17M4, GOTOFFLO or
-     GOTOFFHI relocations.  The addend doesn't really matter, since we
-     envision that this will only be used to check whether the symbol
-     is mapped to the same segment as the got.  */
-  unsigned gotoff;
-  /* Whether symbol+addend is referenced by a LABEL24 relocation.  */
-  unsigned call;
-  /* Whether symbol+addend is referenced by a 32 or FUNCDESC_VALUE
-     relocation.  */
-  unsigned sym;
-  /* Whether we need a PLT entry for a symbol.  Should be implied by
-     something like:
-     (call && symndx == -1 && ! BFINFDPIC_SYM_LOCAL (info, d.h))  */
-  unsigned plt:1;
-  /* Whether a function descriptor should be created in this link unit
-     for symbol+addend.  Should be implied by something like:
-     (plt || fdgotoff17m4 || fdgotofflohi
-      || ((fd || fdgot17m4 || fdgothilo)
-          && (symndx != -1 || BFINFDPIC_FUNCDESC_LOCAL (info, d.h))))  */
-  unsigned privfd:1;
-  /* Whether a lazy PLT entry is needed for this symbol+addend.
-     Should be implied by something like:
-     (privfd && symndx == -1 && ! BFINFDPIC_SYM_LOCAL (info, d.h)
-      && ! (info->flags & DF_BIND_NOW))  */
-  unsigned lazyplt:1;
-  /* Whether we've already emitted GOT relocations and PLT entries as
-     needed for this symbol.  */
-  unsigned done:1;
+       case R_got:
+         if (h != NULL
+             && strcmp (h->root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0)
+           break;
+         /* Fall through.  */
 
-  /* The number of R_byte4_data, R_BFIN_FUNCDESC and R_BFIN_FUNCDESC_VALUE
-     relocations referencing the symbol.  */
-  unsigned relocs32, relocsfd, relocsfdv;
+         if (dynobj == NULL)
+           {
+             /* Create the .got section.  */
+             elf_hash_table (info)->dynobj = dynobj = abfd;
+             if (!_bfd_elf_create_got_section (dynobj, info))
+               return FALSE;
+           }
 
-  /* The number of .rofixups entries and dynamic relocations allocated
-     for this symbol, minus any that might have already been used.  */
-  unsigned fixups, dynrelocs;
+         if (sgot == NULL)
+           {
+             sgot = bfd_get_section_by_name (dynobj, ".got");
+             BFD_ASSERT (sgot != NULL);
+           }
 
-  /* The offsets of the GOT entries assigned to symbol+addend, to the
-     function descriptor's address, and to a function descriptor,
-     respectively.  Should be zero if unassigned.  The offsets are
-     counted from the value that will be assigned to the PIC register,
-     not from the beginning of the .got section.  */
-  bfd_signed_vma got_entry, fdgot_entry, fd_entry;
-  /* The offsets of the PLT entries assigned to symbol+addend,
-     non-lazy and lazy, respectively.  If unassigned, should be
-     (bfd_vma)-1.  */
-  bfd_vma plt_entry, lzplt_entry;
-};
+         if (srelgot == NULL && (h != NULL || info->shared))
+           {
+             srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
+             if (srelgot == NULL)
+               {
+                 flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+                                   | SEC_IN_MEMORY | SEC_LINKER_CREATED
+                                   | SEC_READONLY);
+                 srelgot = bfd_make_section_with_flags (dynobj, ".rela.got",
+                                                        flags);
+                 if (srelgot == NULL
+                     || !bfd_set_section_alignment (dynobj, srelgot, 2))
+                   return FALSE;
+               }
+           }
 
-/* Compute a hash with the key fields of an bfinfdpic_relocs_info entry.  */
-static hashval_t
-bfinfdpic_relocs_info_hash (const void *entry_)
-{
-  const struct bfinfdpic_relocs_info *entry = entry_;
+         if (h != NULL)
+           {
+             if (h->got.refcount == 0)
+               {
+                 /* Make sure this symbol is output as a dynamic symbol.  */
+                 if (h->dynindx == -1 && !h->forced_local)
+                   {
+                     if (!bfd_elf_link_record_dynamic_symbol (info, h))
+                       return FALSE;
+                   }
 
-  return (entry->symndx == -1
-         ? (long) entry->d.h->root.root.hash
-         : entry->symndx + (long) entry->d.abfd->id * 257) + entry->addend;
-}
+                 /* Allocate space in the .got section.  */
+                 sgot->size += 4;
+                 /* Allocate relocation space.  */
+                 srelgot->size += sizeof (Elf32_External_Rela);
+               }
+             h->got.refcount++;
+           }
+         else
+           {
+             /* This is a global offset table entry for a local symbol.  */
+             if (local_got_refcounts == NULL)
+               {
+                 bfd_size_type size;
 
-/* Test whether the key fields of two bfinfdpic_relocs_info entries are
-   identical.  */
-static int
-bfinfdpic_relocs_info_eq (const void *entry1, const void *entry2)
-{
-  const struct bfinfdpic_relocs_info *e1 = entry1;
-  const struct bfinfdpic_relocs_info *e2 = entry2;
+                 size = symtab_hdr->sh_info;
+                 size *= sizeof (bfd_signed_vma);
+                 local_got_refcounts = ((bfd_signed_vma *)
+                                        bfd_zalloc (abfd, size));
+                 if (local_got_refcounts == NULL)
+                   return FALSE;
+                 elf_local_got_refcounts (abfd) = local_got_refcounts;
+               }
+             if (local_got_refcounts[r_symndx] == 0)
+               {
+                 sgot->size += 4;
+                 if (info->shared)
+                   {
+                     /* If we are generating a shared object, we need to
+                        output a R_68K_RELATIVE reloc so that the dynamic
+                        linker can adjust this GOT entry.  */
+                     srelgot->size += sizeof (Elf32_External_Rela);
+                   }
+               }
+             local_got_refcounts[r_symndx]++;
+           }
+         break;
 
-  return e1->symndx == e2->symndx && e1->addend == e2->addend
-    && (e1->symndx == -1 ? e1->d.h == e2->d.h : e1->d.abfd == e2->d.abfd);
+       default:
+         break;
+       }
+    }
+
+  return TRUE;
 }
 
-/* Find or create an entry in a hash table HT that matches the key
-   fields of the given ENTRY.  If it's not found, memory for a new
-   entry is allocated in ABFD's obstack.  */
-static struct bfinfdpic_relocs_info *
-bfinfdpic_relocs_info_find (struct htab *ht,
-                          bfd *abfd,
-                          const struct bfinfdpic_relocs_info *entry,
-                          enum insert_option insert)
+static enum elf_reloc_type_class
+elf32_bfin_reloc_type_class (const Elf_Internal_Rela * rela)
 {
-  struct bfinfdpic_relocs_info **loc =
-    (struct bfinfdpic_relocs_info **) htab_find_slot (ht, entry, insert);
+  switch ((int) ELF32_R_TYPE (rela->r_info))
+    {
+    default:
+      return reloc_class_normal;
+    }
+}
+\f
+static bfd_reloc_status_type
+bfin_final_link_relocate (Elf_Internal_Rela *rel, reloc_howto_type *howto,
+                         bfd *input_bfd, asection *input_section,
+                         bfd_byte *contents, bfd_vma address,
+                         bfd_vma value, bfd_vma addend)
+{
+  int r_type = ELF32_R_TYPE (rel->r_info);
 
-  if (! loc)
-    return NULL;
+  if (r_type == R_pcrel24 || r_type == R_pcrel24_jump_l)
+    {
+      bfd_reloc_status_type r = bfd_reloc_ok;
+      bfd_vma x;
 
-  if (*loc)
-    return *loc;
+      if (address > bfd_get_section_limit (input_bfd, input_section))
+       return bfd_reloc_outofrange;
 
-  *loc = bfd_zalloc (abfd, sizeof (**loc));
+      value += addend;
 
-  if (! *loc)
-    return *loc;
+      /* Perform usual pc-relative correction.  */
+      value -= input_section->output_section->vma + input_section->output_offset;
+      value -= address;
 
-  (*loc)->symndx = entry->symndx;
-  (*loc)->d = entry->d;
-  (*loc)->addend = entry->addend;
-  (*loc)->plt_entry = (bfd_vma)-1;
-  (*loc)->lzplt_entry = (bfd_vma)-1;
+      /* We are getting reloc_entry->address 2 byte off from
+        the start of instruction. Assuming absolute postion
+        of the reloc data. But, following code had been written assuming
+        reloc address is starting at begining of instruction.
+        To compensate that I have increased the value of
+        relocation by 1 (effectively 2) and used the addr -2 instead of addr.  */
 
-  return *loc;
-}
+      value += 2;
+      address -= 2;
 
-/* Obtain the address of the entry in HT associated with H's symbol +
-   addend, creating a new entry if none existed.  ABFD is only used
-   for memory allocation purposes.  */
-inline static struct bfinfdpic_relocs_info *
-bfinfdpic_relocs_info_for_global (struct htab *ht,
-                                bfd *abfd,
-                                struct elf_link_hash_entry *h,
-                                bfd_vma addend,
-                                enum insert_option insert)
-{
-  struct bfinfdpic_relocs_info entry;
+      if ((value & 0xFF000000) != 0
+         && (value & 0xFF000000) != 0xFF000000)
+       r = bfd_reloc_overflow;
 
-  entry.symndx = -1;
-  entry.d.h = h;
-  entry.addend = addend;
+      value >>= 1;
 
-  return bfinfdpic_relocs_info_find (ht, abfd, &entry, insert);
-}
+      x = bfd_get_16 (input_bfd, contents + address);
+      x = (x & 0xff00) | ((value >> 16) & 0xff);
+      bfd_put_16 (input_bfd, x, contents + address);
 
-/* Obtain the address of the entry in HT associated with the SYMNDXth
-   local symbol of the input bfd ABFD, plus the addend, creating a new
-   entry if none existed.  */
-inline static struct bfinfdpic_relocs_info *
-bfinfdpic_relocs_info_for_local (struct htab *ht,
-                               bfd *abfd,
-                               long symndx,
-                               bfd_vma addend,
-                               enum insert_option insert)
-{
-  struct bfinfdpic_relocs_info entry;
+      x = bfd_get_16 (input_bfd, contents + address + 2);
+      x = value & 0xFFFF;
+      bfd_put_16 (input_bfd, x, contents + address + 2);
+      return r;
+    }
 
-  entry.symndx = symndx;
-  entry.d.abfd = abfd;
-  entry.addend = addend;
+  return _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
+                                  rel->r_offset, value, addend);
 
-  return bfinfdpic_relocs_info_find (ht, abfd, &entry, insert);
 }
 
-/* Merge fields set by check_relocs() of two entries that end up being
-   mapped to the same (presumably global) symbol.  */
-
-inline static void
-bfinfdpic_pic_merge_early_relocs_info (struct bfinfdpic_relocs_info *e2,
-                                     struct bfinfdpic_relocs_info const *e1)
+static bfd_boolean
+bfin_relocate_section (bfd * output_bfd,
+                      struct bfd_link_info *info,
+                      bfd * input_bfd,
+                      asection * input_section,
+                      bfd_byte * contents,
+                      Elf_Internal_Rela * relocs,
+                      Elf_Internal_Sym * local_syms,
+                      asection ** local_sections)
 {
-  e2->got17m4 |= e1->got17m4;
-  e2->gothilo |= e1->gothilo;
-  e2->fd |= e1->fd;
-  e2->fdgot17m4 |= e1->fdgot17m4;
-  e2->fdgothilo |= e1->fdgothilo;
-  e2->fdgoff17m4 |= e1->fdgoff17m4;
-  e2->fdgoffhilo |= e1->fdgoffhilo;
-  e2->gotoff |= e1->gotoff;
-  e2->call |= e1->call;
-  e2->sym |= e1->sym;
-}
+  bfd *dynobj;
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  bfd_vma *local_got_offsets;
+  asection *sgot;
+  asection *sreloc;
+  Elf_Internal_Rela *rel;
+  Elf_Internal_Rela *relend;
+  int i = 0;
 
-/* Every block of 65535 lazy PLT entries shares a single call to the
-   resolver, inserted in the 32768th lazy PLT entry (i.e., entry #
-   32767, counting from 0).  All other lazy PLT entries branch to it
-   in a single instruction.  */
+  dynobj = elf_hash_table (info)->dynobj;
+  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (input_bfd);
+  local_got_offsets = elf_local_got_offsets (input_bfd);
 
-#define LZPLT_RESOLVER_EXTRA 10
-#define LZPLT_NORMAL_SIZE 6
-#define LZPLT_ENTRIES 1362
+  sgot = NULL;
+  sreloc = NULL;
 
-#define BFINFDPIC_LZPLT_BLOCK_SIZE ((bfd_vma) LZPLT_NORMAL_SIZE * LZPLT_ENTRIES + LZPLT_RESOLVER_EXTRA)
-#define BFINFDPIC_LZPLT_RESOLV_LOC (LZPLT_NORMAL_SIZE * LZPLT_ENTRIES / 2)
+  rel = relocs;
+  relend = relocs + input_section->reloc_count;
+  for (; rel < relend; rel++, i++)
+    {
+      int r_type;
+      reloc_howto_type *howto;
+      unsigned long r_symndx;
+      struct elf_link_hash_entry *h;
+      Elf_Internal_Sym *sym;
+      asection *sec;
+      bfd_vma relocation = 0;
+      bfd_boolean unresolved_reloc;
+      bfd_reloc_status_type r;
+      bfd_vma address;
 
-/* Add a dynamic relocation to the SRELOC section.  */
+      r_type = ELF32_R_TYPE (rel->r_info);
+      if (r_type < 0 || r_type >= 243)
+       {
+         bfd_set_error (bfd_error_bad_value);
+         return FALSE;
+       }
 
-inline static bfd_vma
-_bfinfdpic_add_dyn_reloc (bfd *output_bfd, asection *sreloc, bfd_vma offset,
-                        int reloc_type, long dynindx, bfd_vma addend,
-                        struct bfinfdpic_relocs_info *entry)
-{
-  Elf_Internal_Rela outrel;
-  bfd_vma reloc_offset;
+      if (r_type == R_BFIN_GNU_VTENTRY
+          || r_type == R_BFIN_GNU_VTINHERIT)
+       continue;
 
-  outrel.r_offset = offset;
-  outrel.r_info = ELF32_R_INFO (dynindx, reloc_type);
-  outrel.r_addend = addend;
+      howto = bfin_reloc_type_lookup (input_bfd, r_type);
+      if (howto == NULL)
+       {
+         bfd_set_error (bfd_error_bad_value);
+         return FALSE;
+       }
+      r_symndx = ELF32_R_SYM (rel->r_info);
 
-  reloc_offset = sreloc->reloc_count * sizeof (Elf32_External_Rel);
-  BFD_ASSERT (reloc_offset < sreloc->size);
-  bfd_elf32_swap_reloc_out (output_bfd, &outrel,
-                           sreloc->contents + reloc_offset);
-  sreloc->reloc_count++;
+      h = NULL;
+      sym = NULL;
+      sec = NULL;
+      unresolved_reloc = FALSE;
 
-  /* If the entry's index is zero, this relocation was probably to a
-     linkonce section that got discarded.  We reserved a dynamic
-     relocation, but it was for another entry than the one we got at
-     the time of emitting the relocation.  Unfortunately there's no
-     simple way for us to catch this situation, since the relocation
-     is cleared right before calling relocate_section, at which point
-     we no longer know what the relocation used to point to.  */
-  if (entry->symndx)
-    {
-      BFD_ASSERT (entry->dynrelocs > 0);
-      entry->dynrelocs--;
-    }
+      if (r_symndx < symtab_hdr->sh_info)
+       {
+         sym = local_syms + r_symndx;
+         sec = local_sections[r_symndx];
+         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+       }
+      else
+       {
+         bfd_boolean warned;
 
-  return reloc_offset;
-}
+         RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
+                                  r_symndx, symtab_hdr, sym_hashes,
+                                  h, sec, relocation,
+                                  unresolved_reloc, warned);
+       }
 
-/* Add a fixup to the ROFIXUP section.  */
+      if (sec != NULL && elf_discarded_section (sec))
+       {
+         /* For relocs against symbols from removed linkonce sections,
+            or sections discarded by a linker script, we just want the
+            section contents zeroed.  Avoid any special processing.  */
+         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
+         rel->r_info = 0;
+         rel->r_addend = 0;
+         continue;
+       }
 
-static bfd_vma
-_bfinfdpic_add_rofixup (bfd *output_bfd, asection *rofixup, bfd_vma offset,
-                      struct bfinfdpic_relocs_info *entry)
-{
-  bfd_vma fixup_offset;
+      if (info->relocatable)
+       continue;
 
-  if (rofixup->flags & SEC_EXCLUDE)
-    return -1;
+      address = rel->r_offset;
 
-  fixup_offset = rofixup->reloc_count * 4;
-  if (rofixup->contents)
-    {
-      BFD_ASSERT (fixup_offset < rofixup->size);
-      bfd_put_32 (output_bfd, offset, rofixup->contents + fixup_offset);
-    }
-  rofixup->reloc_count++;
+      /* Then, process normally.  */
+      switch (r_type)
+       {
+       case R_BFIN_GNU_VTINHERIT:
+       case R_BFIN_GNU_VTENTRY:
+         return bfd_reloc_ok;
 
-  if (entry && entry->symndx)
-    {
-      /* See discussion about symndx == 0 in _bfinfdpic_add_dyn_reloc
-        above.  */
-      BFD_ASSERT (entry->fixups > 0);
-      entry->fixups--;
-    }
+       case R_got:
+         /* Relocation is to the address of the entry for this symbol
+            in the global offset table.  */
+         if (h != NULL
+             && strcmp (h->root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0)
+           goto do_default;
+         /* Fall through.  */
+         /* Relocation is the offset of the entry for this symbol in
+            the global offset table.  */
 
-  return fixup_offset;
-}
+         {
+           bfd_vma off;
 
-/* Find the segment number in which OSEC, and output section, is
-   located.  */
+         if (dynobj == NULL)
+           {
+             /* Create the .got section.  */
+             elf_hash_table (info)->dynobj = dynobj = output_bfd;
+             if (!_bfd_elf_create_got_section (dynobj, info))
+               return FALSE;
+           }
 
-static unsigned
-_bfinfdpic_osec_to_segment (bfd *output_bfd, asection *osec)
-{
-  Elf_Internal_Phdr *p = _bfd_elf_find_segment_containing_section (output_bfd, osec);
+           if (sgot == NULL)
+             {
+               sgot = bfd_get_section_by_name (dynobj, ".got");
+               BFD_ASSERT (sgot != NULL);
+             }
 
-  return (p != NULL) ? p - elf_tdata (output_bfd)->phdr : -1;
-}
+           if (h != NULL)
+             {
+               bfd_boolean dyn;
 
-inline static bfd_boolean
-_bfinfdpic_osec_readonly_p (bfd *output_bfd, asection *osec)
-{
-  unsigned seg = _bfinfdpic_osec_to_segment (output_bfd, osec);
+               off = h->got.offset;
+               BFD_ASSERT (off != (bfd_vma) - 1);
+               dyn = elf_hash_table (info)->dynamic_sections_created;
 
-  return ! (elf_tdata (output_bfd)->phdr[seg].p_flags & PF_W);
-}
+               if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+                   || (info->shared
+                       && (info->symbolic
+                           || h->dynindx == -1
+                           || h->forced_local)
+                       && h->def_regular))
+                 {
+                   /* This is actually a static link, or it is a
+                      -Bsymbolic link and the symbol is defined
+                      locally, or the symbol was forced to be local
+                      because of a version file..  We must initialize
+                      this entry in the global offset table.  Since
+                      the offset must always be a multiple of 4, we
+                      use the least significant bit to record whether
+                      we have initialized it already.
 
-/* Generate relocations for GOT entries, function descriptors, and
-   code for PLT and lazy PLT entries.  */
+                      When doing a dynamic link, we create a .rela.got
+                      relocation entry to initialize the value.  This
+                      is done in the finish_dynamic_symbol routine.  */
+                   if ((off & 1) != 0)
+                     off &= ~1;
+                   else
+                     {
+                       bfd_put_32 (output_bfd, relocation,
+                                   sgot->contents + off);
+                       h->got.offset |= 1;
+                     }
+                 }
+               else
+                 unresolved_reloc = FALSE;
+             }
+           else
+             {
+               BFD_ASSERT (local_got_offsets != NULL);
+               off = local_got_offsets[r_symndx];
+               BFD_ASSERT (off != (bfd_vma) - 1);
 
-inline static bfd_boolean
-_bfinfdpic_emit_got_relocs_plt_entries (struct bfinfdpic_relocs_info *entry,
-                                       bfd *output_bfd,
-                                       struct bfd_link_info *info,
-                                       asection *sec,
-                                       Elf_Internal_Sym *sym,
-                                       bfd_vma addend)
+               /* The offset must always be a multiple of 4.  We use
+                  the least significant bit to record whether we have
+                  already generated the necessary reloc.  */
+               if ((off & 1) != 0)
+                 off &= ~1;
+               else
+                 {
+                   bfd_put_32 (output_bfd, relocation, sgot->contents + off);
 
-{
-  bfd_vma fd_lazy_rel_offset = (bfd_vma)-1;
-  int dynindx = -1;
+                   if (info->shared)
+                     {
+                       asection *s;
+                       Elf_Internal_Rela outrel;
+                       bfd_byte *loc;
 
-  if (entry->done)
-    return TRUE;
-  entry->done = 1;
+                       s = bfd_get_section_by_name (dynobj, ".rela.got");
+                       BFD_ASSERT (s != NULL);
 
-  if (entry->got_entry || entry->fdgot_entry || entry->fd_entry)
-    {
-      /* If the symbol is dynamic, consider it for dynamic
-        relocations, otherwise decay to section + offset.  */
-      if (entry->symndx == -1 && entry->d.h->dynindx != -1)
-       dynindx = entry->d.h->dynindx;
-      else
-       {
-         if (sec->output_section
-             && ! bfd_is_abs_section (sec->output_section)
-             && ! bfd_is_und_section (sec->output_section))
-           dynindx = elf_section_data (sec->output_section)->dynindx;
-         else
-           dynindx = 0;
-       }
-    }
+                       outrel.r_offset = (sgot->output_section->vma
+                                          + sgot->output_offset + off);
+                       outrel.r_info =
+                         ELF32_R_INFO (0, R_pcrel24);
+                       outrel.r_addend = relocation;
+                       loc = s->contents;
+                       loc +=
+                         s->reloc_count++ * sizeof (Elf32_External_Rela);
+                       bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+                     }
 
-  /* Generate relocation for GOT entry pointing to the symbol.  */
-  if (entry->got_entry)
-    {
-      int idx = dynindx;
-      bfd_vma ad = addend;
+                   local_got_offsets[r_symndx] |= 1;
+                 }
+             }
 
-      /* If the symbol is dynamic but binds locally, use
-        section+offset.  */
-      if (sec && (entry->symndx != -1
-                 || BFINFDPIC_SYM_LOCAL (info, entry->d.h)))
-       {
-         if (entry->symndx == -1)
-           ad += entry->d.h->root.u.def.value;
-         else
-           ad += sym->st_value;
-         ad += sec->output_offset;
-         if (sec->output_section && elf_section_data (sec->output_section))
-           idx = elf_section_data (sec->output_section)->dynindx;
-         else
-           idx = 0;
+           relocation = sgot->output_offset + off;
+           rel->r_addend = 0;
+            /* bfin : preg = [preg + 17bitdiv4offset] relocation is div by 4.  */
+            relocation /= 4;
+         }
+         goto do_default;
+
+       default:
+       do_default:
+         r = bfin_final_link_relocate (rel, howto, input_bfd, input_section,
+                                       contents, address,
+                                       relocation, rel->r_addend);
+
+         break;
        }
 
-      /* If we're linking an executable at a fixed address, we can
-        omit the dynamic relocation as long as the symbol is local to
-        this module.  */
-      if (info->executable && !info->pie
-         && (entry->symndx != -1
-             || BFINFDPIC_SYM_LOCAL (info, entry->d.h)))
+      /* Dynamic relocs are not propagated for SEC_DEBUGGING sections
+         because such sections are not SEC_ALLOC and thus ld.so will
+         not process them.  */
+      if (unresolved_reloc
+         && !((input_section->flags & SEC_DEBUGGING) != 0 && h->def_dynamic))
        {
-         if (sec)
-           ad += sec->output_section->vma;
-         if (entry->symndx != -1
-             || entry->d.h->root.type != bfd_link_hash_undefweak)
-           _bfinfdpic_add_rofixup (output_bfd,
-                                  bfinfdpic_gotfixup_section (info),
-                                  bfinfdpic_got_section (info)->output_section
-                                  ->vma
-                                  + bfinfdpic_got_section (info)->output_offset
-                                  + bfinfdpic_got_initial_offset (info)
-                                  + entry->got_entry, entry);
+         (*_bfd_error_handler)
+           (_("%B(%A+0x%lx): unresolvable relocation against symbol `%s'"),
+            input_bfd,
+            input_section, (long) rel->r_offset, h->root.root.string);
+         return FALSE;
        }
-      else
-       _bfinfdpic_add_dyn_reloc (output_bfd, bfinfdpic_gotrel_section (info),
-                                _bfd_elf_section_offset
-                                (output_bfd, info,
-                                 bfinfdpic_got_section (info),
-                                 bfinfdpic_got_initial_offset (info)
-                                 + entry->got_entry)
-                                + bfinfdpic_got_section (info)
-                                ->output_section->vma
-                                + bfinfdpic_got_section (info)->output_offset,
-                                R_byte4_data, idx, ad, entry);
-
-      bfd_put_32 (output_bfd, ad,
-                 bfinfdpic_got_section (info)->contents
-                 + bfinfdpic_got_initial_offset (info)
-                 + entry->got_entry);
-    }
-
-  /* Generate relocation for GOT entry pointing to a canonical
-     function descriptor.  */
-  if (entry->fdgot_entry)
-    {
-      int reloc, idx;
-      bfd_vma ad = 0;
 
-      if (! (entry->symndx == -1
-            && entry->d.h->root.type == bfd_link_hash_undefweak
-            && BFINFDPIC_SYM_LOCAL (info, entry->d.h)))
+      if (r != bfd_reloc_ok)
        {
-         /* If the symbol is dynamic and there may be dynamic symbol
-            resolution because we are, or are linked with, a shared
-            library, emit a FUNCDESC relocation such that the dynamic
-            linker will allocate the function descriptor.  If the
-            symbol needs a non-local function descriptor but binds
-            locally (e.g., its visibility is protected, emit a
-            dynamic relocation decayed to section+offset.  */
-         if (entry->symndx == -1
-             && ! BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h)
-             && BFINFDPIC_SYM_LOCAL (info, entry->d.h)
-             && !(info->executable && !info->pie))
+         const char *name;
+
+         if (h != NULL)
+           name = h->root.root.string;
+         else
            {
-             reloc = R_BFIN_FUNCDESC;
-             idx = elf_section_data (entry->d.h->root.u.def.section
-                                     ->output_section)->dynindx;
-             ad = entry->d.h->root.u.def.section->output_offset
-               + entry->d.h->root.u.def.value;
+             name = bfd_elf_string_from_elf_section (input_bfd,
+                                                     symtab_hdr->sh_link,
+                                                     sym->st_name);
+             if (name == NULL)
+               return FALSE;
+             if (*name == '\0')
+               name = bfd_section_name (input_bfd, sec);
            }
-         else if (entry->symndx == -1
-                  && ! BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h))
+
+         if (r == bfd_reloc_overflow)
            {
-             reloc = R_BFIN_FUNCDESC;
-             idx = dynindx;
-             ad = addend;
-             if (ad)
+             if (!(info->callbacks->reloc_overflow
+                   (info, (h ? &h->root : NULL), name, howto->name,
+                    (bfd_vma) 0, input_bfd, input_section, rel->r_offset)))
                return FALSE;
            }
          else
            {
-             /* Otherwise, we know we have a private function descriptor,
-                so reference it directly.  */
-             if (elf_hash_table (info)->dynamic_sections_created)
-               BFD_ASSERT (entry->privfd);
-             reloc = R_byte4_data;
-             idx = elf_section_data (bfinfdpic_got_section (info)
-                                     ->output_section)->dynindx;
-             ad = bfinfdpic_got_section (info)->output_offset
-               + bfinfdpic_got_initial_offset (info) + entry->fd_entry;
-           }
-
-         /* If there is room for dynamic symbol resolution, emit the
-            dynamic relocation.  However, if we're linking an
-            executable at a fixed location, we won't have emitted a
-            dynamic symbol entry for the got section, so idx will be
-            zero, which means we can and should compute the address
-            of the private descriptor ourselves.  */
-         if (info->executable && !info->pie
-             && (entry->symndx != -1
-                 || BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h)))
-           {
-             ad += bfinfdpic_got_section (info)->output_section->vma;
-             _bfinfdpic_add_rofixup (output_bfd,
-                                    bfinfdpic_gotfixup_section (info),
-                                    bfinfdpic_got_section (info)
-                                    ->output_section->vma
-                                    + bfinfdpic_got_section (info)
-                                    ->output_offset
-                                    + bfinfdpic_got_initial_offset (info)
-                                    + entry->fdgot_entry, entry);
+             (*_bfd_error_handler)
+               (_("%B(%A+0x%lx): reloc against `%s': error %d"),
+                input_bfd, input_section,
+                (long) rel->r_offset, name, (int) r);
+             return FALSE;
            }
-         else
-           _bfinfdpic_add_dyn_reloc (output_bfd,
-                                    bfinfdpic_gotrel_section (info),
-                                    _bfd_elf_section_offset
-                                    (output_bfd, info,
-                                     bfinfdpic_got_section (info),
-                                     bfinfdpic_got_initial_offset (info)
-                                     + entry->fdgot_entry)
-                                    + bfinfdpic_got_section (info)
-                                    ->output_section->vma
-                                    + bfinfdpic_got_section (info)
-                                    ->output_offset,
-                                    reloc, idx, ad, entry);
        }
-
-      bfd_put_32 (output_bfd, ad,
-                 bfinfdpic_got_section (info)->contents
-                 + bfinfdpic_got_initial_offset (info)
-                 + entry->fdgot_entry);
     }
 
-  /* Generate relocation to fill in a private function descriptor in
-     the GOT.  */
-  if (entry->fd_entry)
-    {
-      int idx = dynindx;
-      bfd_vma ad = addend;
-      bfd_vma ofst;
-      long lowword, highword;
+  return TRUE;
+}
 
-      /* If the symbol is dynamic but binds locally, use
-        section+offset.  */
-      if (sec && (entry->symndx != -1
-                 || BFINFDPIC_SYM_LOCAL (info, entry->d.h)))
-       {
-         if (entry->symndx == -1)
-           ad += entry->d.h->root.u.def.value;
-         else
-           ad += sym->st_value;
-         ad += sec->output_offset;
-         if (sec->output_section && elf_section_data (sec->output_section))
-           idx = elf_section_data (sec->output_section)->dynindx;
-         else
-           idx = 0;
-       }
+static asection *
+bfin_gc_mark_hook (asection * sec,
+                  struct bfd_link_info *info,
+                  Elf_Internal_Rela * rel,
+                  struct elf_link_hash_entry *h,
+                   Elf_Internal_Sym * sym)
+{
+  if (h != NULL)
+    switch (ELF32_R_TYPE (rel->r_info))
+      {
+      case R_BFIN_GNU_VTINHERIT:
+      case R_BFIN_GNU_VTENTRY:
+       return NULL;
+      }
 
-      /* If we're linking an executable at a fixed address, we can
-        omit the dynamic relocation as long as the symbol is local to
-        this module.  */
-      if (info->executable && !info->pie
-         && (entry->symndx != -1 || BFINFDPIC_SYM_LOCAL (info, entry->d.h)))
-       {
-         if (sec)
-           ad += sec->output_section->vma;
-         ofst = 0;
-         if (entry->symndx != -1
-             || entry->d.h->root.type != bfd_link_hash_undefweak)
-           {
-             _bfinfdpic_add_rofixup (output_bfd,
-                                    bfinfdpic_gotfixup_section (info),
-                                    bfinfdpic_got_section (info)
-                                    ->output_section->vma
-                                    + bfinfdpic_got_section (info)
-                                    ->output_offset
-                                    + bfinfdpic_got_initial_offset (info)
-                                    + entry->fd_entry, entry);
-             _bfinfdpic_add_rofixup (output_bfd,
-                                    bfinfdpic_gotfixup_section (info),
-                                    bfinfdpic_got_section (info)
-                                    ->output_section->vma
-                                    + bfinfdpic_got_section (info)
-                                    ->output_offset
-                                    + bfinfdpic_got_initial_offset (info)
-                                    + entry->fd_entry + 4, entry);
-           }
-       }
-      else
-       {
-         ofst
-           = _bfinfdpic_add_dyn_reloc (output_bfd,
-                                       entry->lazyplt
-                                       ? bfinfdpic_pltrel_section (info)
-                                       : bfinfdpic_gotrel_section (info),
-                                       _bfd_elf_section_offset
-                                       (output_bfd, info,
-                                        bfinfdpic_got_section (info),
-                                        bfinfdpic_got_initial_offset (info)
-                                        + entry->fd_entry)
-                                       + bfinfdpic_got_section (info)
-                                       ->output_section->vma
-                                       + bfinfdpic_got_section (info)
-                                       ->output_offset,
-                                       R_BFIN_FUNCDESC_VALUE, idx, ad, entry);
-       }
+  return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
+}
 
-      /* If we've omitted the dynamic relocation, just emit the fixed
-        addresses of the symbol and of the local GOT base offset.  */
-      if (info->executable && !info->pie && sec && sec->output_section)
-       {
-         lowword = ad;
-         highword = bfinfdpic_got_section (info)->output_section->vma
-           + bfinfdpic_got_section (info)->output_offset
-           + bfinfdpic_got_initial_offset (info);
-       }
-      else if (entry->lazyplt)
-       {
-         if (ad)
-           return FALSE;
+/* Update the got entry reference counts for the section being removed.  */
 
-         fd_lazy_rel_offset = ofst;
+static bfd_boolean
+bfin_gc_sweep_hook (bfd * abfd,
+                   struct bfd_link_info *info,
+                   asection * sec,
+                    const Elf_Internal_Rela * relocs)
+{
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  bfd_signed_vma *local_got_refcounts;
+  const Elf_Internal_Rela *rel, *relend;
+  bfd *dynobj;
+  asection *sgot;
+  asection *srelgot;
 
-         /* A function descriptor used for lazy or local resolving is
-            initialized such that its high word contains the output
-            section index in which the PLT entries are located, and
-            the low word contains the address of the lazy PLT entry
-            entry point, that must be within the memory region
-            assigned to that section.  */
-         lowword = entry->lzplt_entry + 4
-           + bfinfdpic_plt_section (info)->output_offset
-           + bfinfdpic_plt_section (info)->output_section->vma;
-         highword = _bfinfdpic_osec_to_segment
-           (output_bfd, bfinfdpic_plt_section (info)->output_section);
-       }
-      else
-       {
-         /* A function descriptor for a local function gets the index
-            of the section.  For a non-local function, it's
-            disregarded.  */
-         lowword = ad;
-         if (entry->symndx == -1 && entry->d.h->dynindx != -1
-             && entry->d.h->dynindx == idx)
-           highword = 0;
-         else
-           highword = _bfinfdpic_osec_to_segment
-             (output_bfd, sec->output_section);
-       }
-
-      bfd_put_32 (output_bfd, lowword,
-                 bfinfdpic_got_section (info)->contents
-                 + bfinfdpic_got_initial_offset (info)
-                 + entry->fd_entry);
-      bfd_put_32 (output_bfd, highword,
-                 bfinfdpic_got_section (info)->contents
-                 + bfinfdpic_got_initial_offset (info)
-                 + entry->fd_entry + 4);
-    }
-
-  /* Generate code for the PLT entry.  */
-  if (entry->plt_entry != (bfd_vma) -1)
-    {
-      bfd_byte *plt_code = bfinfdpic_plt_section (info)->contents
-       + entry->plt_entry;
-
-      BFD_ASSERT (entry->fd_entry);
-
-      /* Figure out what kind of PLT entry we need, depending on the
-        location of the function descriptor within the GOT.  */
-      if (entry->fd_entry >= -(1 << (18 - 1))
-         && entry->fd_entry + 4 < (1 << (18 - 1)))
-       {
-         /* P1 = [P3 + fd_entry]; P3 = [P3 + fd_entry + 4] */
-         bfd_put_32 (output_bfd,
-                     0xe519 | ((entry->fd_entry << 14) & 0xFFFF0000),
-                     plt_code);
-         bfd_put_32 (output_bfd,
-                     0xe51b | (((entry->fd_entry + 4) << 14) & 0xFFFF0000),
-                     plt_code + 4);
-         plt_code += 8;
-       }
-      else
-       {
-         /* P1.L = fd_entry; P1.H = fd_entry;
-            P3 = P3 + P1;
-            P1 = [P3];
-            P3 = [P3 + 4];  */
-         bfd_put_32 (output_bfd,
-                     0xe109 | (entry->fd_entry << 16),
-                     plt_code);
-         bfd_put_32 (output_bfd,
-                     0xe149 | (entry->fd_entry & 0xFFFF0000),
-                     plt_code + 4);
-         bfd_put_16 (output_bfd, 0x5ad9, plt_code + 8);
-         bfd_put_16 (output_bfd, 0x9159, plt_code + 10);
-         bfd_put_16 (output_bfd, 0xac5b, plt_code + 12);
-         plt_code += 14;
-       }
-      /* JUMP (P1) */
-      bfd_put_16 (output_bfd, 0x0051, plt_code);
-    }
-
-  /* Generate code for the lazy PLT entry.  */
-  if (entry->lzplt_entry != (bfd_vma) -1)
-    {
-      bfd_byte *lzplt_code = bfinfdpic_plt_section (info)->contents
-       + entry->lzplt_entry;
-      bfd_vma resolverStub_addr;
-
-      bfd_put_32 (output_bfd, fd_lazy_rel_offset, lzplt_code);
-      lzplt_code += 4;
-
-      resolverStub_addr = entry->lzplt_entry / BFINFDPIC_LZPLT_BLOCK_SIZE
-       * BFINFDPIC_LZPLT_BLOCK_SIZE + BFINFDPIC_LZPLT_RESOLV_LOC;
-      if (resolverStub_addr >= bfinfdpic_plt_initial_offset (info))
-       resolverStub_addr = bfinfdpic_plt_initial_offset (info) - LZPLT_NORMAL_SIZE - LZPLT_RESOLVER_EXTRA;
-
-      if (entry->lzplt_entry == resolverStub_addr)
-       {
-         /* This is a lazy PLT entry that includes a resolver call.
-            P2 = [P3];
-            R3 = [P3 + 4];
-            JUMP (P2);  */
-         bfd_put_32 (output_bfd,
-                     0xa05b915a,
-                     lzplt_code);
-         bfd_put_16 (output_bfd, 0x0052, lzplt_code + 4);
-       }
-      else
-       {
-         /* JUMP.S  resolverStub */
-         bfd_put_16 (output_bfd,
-                     0x2000
-                     | (((resolverStub_addr - entry->lzplt_entry)
-                         / 2) & (((bfd_vma)1 << 12) - 1)),
-                     lzplt_code);
-       }
-    }
-
-  return TRUE;
-}
-
-
-/* Look through the relocs for a section during the first phase, and
-   allocate space in the global offset table or procedure linkage
-   table.  */
-
-static bfd_boolean
-bfin_check_relocs (bfd * abfd,
-                  struct bfd_link_info *info,
-                  asection *sec,
-                   const Elf_Internal_Rela *relocs)
-{
-  bfd *dynobj;
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes;
-  bfd_signed_vma *local_got_refcounts;
-  const Elf_Internal_Rela *rel;
-  const Elf_Internal_Rela *rel_end;
-  asection *sgot;
-  asection *srelgot;
-  asection *sreloc;
-  if (info->relocatable)
+  dynobj = elf_hash_table (info)->dynobj;
+  if (dynobj == NULL)
     return TRUE;
 
-  dynobj = elf_hash_table (info)->dynobj;
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
   local_got_refcounts = elf_local_got_refcounts (abfd);
 
-  sgot = NULL;
-  srelgot = NULL;
-  sreloc = NULL;
+  sgot = bfd_get_section_by_name (dynobj, ".got");
+  srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
 
-  rel_end = relocs + sec->reloc_count;
-  for (rel = relocs; rel < rel_end; rel++)
+  relend = relocs + sec->reloc_count;
+  for (rel = relocs; rel < relend; rel++)
     {
       unsigned long r_symndx;
       struct elf_link_hash_entry *h;
 
-      r_symndx = ELF32_R_SYM (rel->r_info);
-      if (r_symndx < symtab_hdr->sh_info)
-       h = NULL;
-      else
-       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-
       switch (ELF32_R_TYPE (rel->r_info))
        {
-       /* This relocation describes the C++ object vtable hierarchy.
-           Reconstruct it for later use during GC.  */
-        case R_BFIN_GNU_VTINHERIT:
-          if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
-            return FALSE;
-          break;
-
-        /* This relocation describes which C++ vtable entries
-           are actually used.  Record for later use during GC.  */
-        case R_BFIN_GNU_VTENTRY:
-          BFD_ASSERT (h != NULL);
-          if (h != NULL
-              && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
-            return FALSE;
-          break;
-
        case R_got:
-         if (h != NULL
-             && strcmp (h->root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0)
-           break;
-         /* Fall through.  */
-
-         if (dynobj == NULL)
-           {
-             /* Create the .got section.  */
-             elf_hash_table (info)->dynobj = dynobj = abfd;
-             if (!_bfd_elf_create_got_section (dynobj, info))
-               return FALSE;
-           }
-
-         if (sgot == NULL)
-           {
-             sgot = bfd_get_section_by_name (dynobj, ".got");
-             BFD_ASSERT (sgot != NULL);
-           }
-
-         if (srelgot == NULL && (h != NULL || info->shared))
-           {
-             srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
-             if (srelgot == NULL)
-               {
-                 flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
-                                   | SEC_IN_MEMORY | SEC_LINKER_CREATED
-                                   | SEC_READONLY);
-                 srelgot = bfd_make_section_with_flags (dynobj, ".rela.got",
-                                                        flags);
-                 if (srelgot == NULL
-                     || !bfd_set_section_alignment (dynobj, srelgot, 2))
-                   return FALSE;
-               }
-           }
-
-         if (h != NULL)
+         r_symndx = ELF32_R_SYM (rel->r_info);
+         if (r_symndx >= symtab_hdr->sh_info)
            {
-             if (h->got.refcount == 0)
+             h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+             if (h->got.refcount > 0)
                {
-                 /* Make sure this symbol is output as a dynamic symbol.  */
-                 if (h->dynindx == -1 && !h->forced_local)
+                 --h->got.refcount;
+                 if (h->got.refcount == 0)
                    {
-                     if (!bfd_elf_link_record_dynamic_symbol (info, h))
-                       return FALSE;
+                     /* We don't need the .got entry any more.  */
+                     sgot->size -= 4;
+                     srelgot->size -= sizeof (Elf32_External_Rela);
                    }
-
-                 /* Allocate space in the .got section.  */
-                 sgot->size += 4;
-                 /* Allocate relocation space.  */
-                 srelgot->size += sizeof (Elf32_External_Rela);
                }
-             h->got.refcount++;
            }
-         else
+         else if (local_got_refcounts != NULL)
            {
-             /* This is a global offset table entry for a local symbol.  */
-             if (local_got_refcounts == NULL)
-               {
-                 bfd_size_type size;
-
-                 size = symtab_hdr->sh_info;
-                 size *= sizeof (bfd_signed_vma);
-                 local_got_refcounts = ((bfd_signed_vma *)
-                                        bfd_zalloc (abfd, size));
-                 if (local_got_refcounts == NULL)
-                   return FALSE;
-                 elf_local_got_refcounts (abfd) = local_got_refcounts;
-               }
-             if (local_got_refcounts[r_symndx] == 0)
+             if (local_got_refcounts[r_symndx] > 0)
                {
-                 sgot->size += 4;
-                 if (info->shared)
+                 --local_got_refcounts[r_symndx];
+                 if (local_got_refcounts[r_symndx] == 0)
                    {
-                     /* If we are generating a shared object, we need to
-                        output a R_68K_RELATIVE reloc so that the dynamic
-                        linker can adjust this GOT entry.  */
-                     srelgot->size += sizeof (Elf32_External_Rela);
+                     /* We don't need the .got entry any more.  */
+                     sgot->size -= 4;
+                     if (info->shared)
+                       srelgot->size -= sizeof (Elf32_External_Rela);
                    }
                }
-             local_got_refcounts[r_symndx]++;
            }
          break;
-
        default:
          break;
        }
     }
-
   return TRUE;
 }
+\f
+extern const bfd_target bfd_elf32_bfinfdpic_vec;
+#define IS_FDPIC(bfd) ((bfd)->xvec == &bfd_elf32_bfinfdpic_vec)
 
-static enum elf_reloc_type_class
-elf32_bfin_reloc_type_class (const Elf_Internal_Rela * rela)
-{
-  switch ((int) ELF32_R_TYPE (rela->r_info))
-    {
-    default:
-      return reloc_class_normal;
-    }
-}
-\f
-static bfd_reloc_status_type
-bfin_final_link_relocate (Elf_Internal_Rela *rel, reloc_howto_type *howto,
-                         bfd *input_bfd, asection *input_section,
-                         bfd_byte *contents, bfd_vma address,
-                         bfd_vma value, bfd_vma addend)
+/* An extension of the elf hash table data structure, containing some
+   additional Blackfin-specific data.  */
+struct bfinfdpic_elf_link_hash_table
 {
-  int r_type = ELF32_R_TYPE (rel->r_info);
+  struct elf_link_hash_table elf;
 
-  if (r_type == R_pcrel24 || r_type == R_pcrel24_jump_l)
-    {
-      bfd_reloc_status_type r = bfd_reloc_ok;
-      bfd_vma x;
+  /* A pointer to the .got section.  */
+  asection *sgot;
+  /* A pointer to the .rel.got section.  */
+  asection *sgotrel;
+  /* A pointer to the .rofixup section.  */
+  asection *sgotfixup;
+  /* A pointer to the .plt section.  */
+  asection *splt;
+  /* A pointer to the .rel.plt section.  */
+  asection *spltrel;
+  /* GOT base offset.  */
+  bfd_vma got0;
+  /* Location of the first non-lazy PLT entry, i.e., the number of
+     bytes taken by lazy PLT entries.  */
+  bfd_vma plt0;
+  /* A hash table holding information about which symbols were
+     referenced with which PIC-related relocations.  */
+  struct htab *relocs_info;
+};
 
-      if (address > bfd_get_section_limit (input_bfd, input_section))
-       return bfd_reloc_outofrange;
+/* Get the Blackfin ELF linker hash table from a link_info structure.  */
 
-      value += addend;
+#define bfinfdpic_hash_table(info) \
+  ((struct bfinfdpic_elf_link_hash_table *) ((info)->hash))
 
-      /* Perform usual pc-relative correction.  */
-      value -= input_section->output_section->vma + input_section->output_offset;
-      value -= address;
+#define bfinfdpic_got_section(info) \
+  (bfinfdpic_hash_table (info)->sgot)
+#define bfinfdpic_gotrel_section(info) \
+  (bfinfdpic_hash_table (info)->sgotrel)
+#define bfinfdpic_gotfixup_section(info) \
+  (bfinfdpic_hash_table (info)->sgotfixup)
+#define bfinfdpic_plt_section(info) \
+  (bfinfdpic_hash_table (info)->splt)
+#define bfinfdpic_pltrel_section(info) \
+  (bfinfdpic_hash_table (info)->spltrel)
+#define bfinfdpic_relocs_info(info) \
+  (bfinfdpic_hash_table (info)->relocs_info)
+#define bfinfdpic_got_initial_offset(info) \
+  (bfinfdpic_hash_table (info)->got0)
+#define bfinfdpic_plt_initial_offset(info) \
+  (bfinfdpic_hash_table (info)->plt0)
 
-      /* We are getting reloc_entry->address 2 byte off from
-        the start of instruction. Assuming absolute postion
-        of the reloc data. But, following code had been written assuming
-        reloc address is starting at begining of instruction.
-        To compensate that I have increased the value of
-        relocation by 1 (effectively 2) and used the addr -2 instead of addr.  */
+/* The name of the dynamic interpreter.  This is put in the .interp
+   section.  */
 
-      value += 2;
-      address -= 2;
+#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1"
 
-      if ((value & 0xFF000000) != 0
-         && (value & 0xFF000000) != 0xFF000000)
-       r = bfd_reloc_overflow;
+#define DEFAULT_STACK_SIZE 0x20000
 
-      value >>= 1;
+/* This structure is used to collect the number of entries present in
+   each addressable range of the got.  */
+struct _bfinfdpic_dynamic_got_info
+{
+  /* Several bits of information about the current link.  */
+  struct bfd_link_info *info;
+  /* Total size needed for GOT entries within the 18- or 32-bit
+     ranges.  */
+  bfd_vma got17m4, gothilo;
+  /* Total size needed for function descriptor entries within the 18-
+     or 32-bit ranges.  */
+  bfd_vma fd17m4, fdhilo;
+  /* Total size needed function descriptor entries referenced in PLT
+     entries, that would be profitable to place in offsets close to
+     the PIC register.  */
+  bfd_vma fdplt;
+  /* Total size needed by lazy PLT entries.  */
+  bfd_vma lzplt;
+  /* Number of relocations carried over from input object files.  */
+  unsigned long relocs;
+  /* Number of fixups introduced by relocations in input object files.  */
+  unsigned long fixups;
+};
 
-      x = bfd_get_16 (input_bfd, contents + address);
-      x = (x & 0xff00) | ((value >> 16) & 0xff);
-      bfd_put_16 (input_bfd, x, contents + address);
+/* Create a Blackfin ELF linker hash table.  */
 
-      x = bfd_get_16 (input_bfd, contents + address + 2);
-      x = value & 0xFFFF;
-      bfd_put_16 (input_bfd, x, contents + address + 2);
-      return r;
-    }
+static struct bfd_link_hash_table *
+bfinfdpic_elf_link_hash_table_create (bfd *abfd)
+{
+  struct bfinfdpic_elf_link_hash_table *ret;
+  bfd_size_type amt = sizeof (struct bfinfdpic_elf_link_hash_table);
 
-  return _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
-                                  rel->r_offset, value, addend);
+  ret = bfd_zalloc (abfd, amt);
+  if (ret == NULL)
+    return NULL;
 
-}
+  if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
+                                     _bfd_elf_link_hash_newfunc,
+                                     sizeof (struct elf_link_hash_entry)))
+    {
+      free (ret);
+      return NULL;
+    }
 
-\f
-/* Relocate an Blackfin ELF section.
+  return &ret->elf.root;
+}
 
-   The RELOCATE_SECTION function is called by the new ELF backend linker
-   to handle the relocations for a section.
+/* Decide whether a reference to a symbol can be resolved locally or
+   not.  If the symbol is protected, we want the local address, but
+   its function descriptor must be assigned by the dynamic linker.  */
+#define BFINFDPIC_SYM_LOCAL(INFO, H) \
+  (_bfd_elf_symbol_refs_local_p ((H), (INFO), 1) \
+   || ! elf_hash_table (INFO)->dynamic_sections_created)
+#define BFINFDPIC_FUNCDESC_LOCAL(INFO, H) \
+  ((H)->dynindx == -1 || ! elf_hash_table (INFO)->dynamic_sections_created)
 
-   The relocs are always passed as Rela structures; if the section
-   actually uses Rel structures, the r_addend field will always be
-   zero.
+/* This structure collects information on what kind of GOT, PLT or
+   function descriptors are required by relocations that reference a
+   certain symbol.  */
+struct bfinfdpic_relocs_info
+{
+  /* The index of the symbol, as stored in the relocation r_info, if
+     we have a local symbol; -1 otherwise.  */
+  long symndx;
+  union
+  {
+    /* The input bfd in which the symbol is defined, if it's a local
+       symbol.  */
+    bfd *abfd;
+    /* If symndx == -1, the hash table entry corresponding to a global
+       symbol (even if it turns out to bind locally, in which case it
+       should ideally be replaced with section's symndx + addend).  */
+    struct elf_link_hash_entry *h;
+  } d;
+  /* The addend of the relocation that references the symbol.  */
+  bfd_vma addend;
 
-   This function is responsible for adjusting the section contents as
-   necessary, and (if using Rela relocs and generating a relocatable
-   output file) adjusting the reloc addend as necessary.
+  /* The fields above are used to identify an entry.  The fields below
+     contain information on how an entry is used and, later on, which
+     locations it was assigned.  */
+  /* The following 2 fields record whether the symbol+addend above was
+     ever referenced with a GOT relocation.  The 17M4 suffix indicates a
+     GOT17M4 relocation; hilo is used for GOTLO/GOTHI pairs.  */
+  unsigned got17m4;
+  unsigned gothilo;
+  /* Whether a FUNCDESC relocation references symbol+addend.  */
+  unsigned fd;
+  /* Whether a FUNCDESC_GOT relocation references symbol+addend.  */
+  unsigned fdgot17m4;
+  unsigned fdgothilo;
+  /* Whether a FUNCDESC_GOTOFF relocation references symbol+addend.  */
+  unsigned fdgoff17m4;
+  unsigned fdgoffhilo;
+  /* Whether symbol+addend is referenced with GOTOFF17M4, GOTOFFLO or
+     GOTOFFHI relocations.  The addend doesn't really matter, since we
+     envision that this will only be used to check whether the symbol
+     is mapped to the same segment as the got.  */
+  unsigned gotoff;
+  /* Whether symbol+addend is referenced by a LABEL24 relocation.  */
+  unsigned call;
+  /* Whether symbol+addend is referenced by a 32 or FUNCDESC_VALUE
+     relocation.  */
+  unsigned sym;
+  /* Whether we need a PLT entry for a symbol.  Should be implied by
+     something like:
+     (call && symndx == -1 && ! BFINFDPIC_SYM_LOCAL (info, d.h))  */
+  unsigned plt:1;
+  /* Whether a function descriptor should be created in this link unit
+     for symbol+addend.  Should be implied by something like:
+     (plt || fdgotoff17m4 || fdgotofflohi
+      || ((fd || fdgot17m4 || fdgothilo)
+          && (symndx != -1 || BFINFDPIC_FUNCDESC_LOCAL (info, d.h))))  */
+  unsigned privfd:1;
+  /* Whether a lazy PLT entry is needed for this symbol+addend.
+     Should be implied by something like:
+     (privfd && symndx == -1 && ! BFINFDPIC_SYM_LOCAL (info, d.h)
+      && ! (info->flags & DF_BIND_NOW))  */
+  unsigned lazyplt:1;
+  /* Whether we've already emitted GOT relocations and PLT entries as
+     needed for this symbol.  */
+  unsigned done:1;
 
-   This function does not have to worry about setting the reloc
-   address or the reloc symbol index.
+  /* The number of R_byte4_data, R_BFIN_FUNCDESC and R_BFIN_FUNCDESC_VALUE
+     relocations referencing the symbol.  */
+  unsigned relocs32, relocsfd, relocsfdv;
 
-   LOCAL_SYMS is a pointer to the swapped in local symbols.
+  /* The number of .rofixups entries and dynamic relocations allocated
+     for this symbol, minus any that might have already been used.  */
+  unsigned fixups, dynrelocs;
 
-   LOCAL_SECTIONS is an array giving the section in the input file
-   corresponding to the st_shndx field of each local symbol.
+  /* The offsets of the GOT entries assigned to symbol+addend, to the
+     function descriptor's address, and to a function descriptor,
+     respectively.  Should be zero if unassigned.  The offsets are
+     counted from the value that will be assigned to the PIC register,
+     not from the beginning of the .got section.  */
+  bfd_signed_vma got_entry, fdgot_entry, fd_entry;
+  /* The offsets of the PLT entries assigned to symbol+addend,
+     non-lazy and lazy, respectively.  If unassigned, should be
+     (bfd_vma)-1.  */
+  bfd_vma plt_entry, lzplt_entry;
+};
 
-   The global hash table entry for the global symbols can be found
-   via elf_sym_hashes (input_bfd).
+/* Compute a hash with the key fields of an bfinfdpic_relocs_info entry.  */
+static hashval_t
+bfinfdpic_relocs_info_hash (const void *entry_)
+{
+  const struct bfinfdpic_relocs_info *entry = entry_;
 
-   When generating relocatable output, this function must handle
-   STB_LOCAL/STT_SECTION symbols specially.  The output symbol is
-   going to be the section symbol corresponding to the output
-   section, which means that the addend must be adjusted
-   accordingly.  */
+  return (entry->symndx == -1
+         ? (long) entry->d.h->root.root.hash
+         : entry->symndx + (long) entry->d.abfd->id * 257) + entry->addend;
+}
 
-static bfd_boolean
-bfinfdpic_relocate_section (bfd * output_bfd,
-                           struct bfd_link_info *info,
-                           bfd * input_bfd,
-                           asection * input_section,
-                           bfd_byte * contents,
-                           Elf_Internal_Rela * relocs,
-                           Elf_Internal_Sym * local_syms,
-                           asection ** local_sections)
+/* Test whether the key fields of two bfinfdpic_relocs_info entries are
+   identical.  */
+static int
+bfinfdpic_relocs_info_eq (const void *entry1, const void *entry2)
 {
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes;
-  Elf_Internal_Rela *rel;
-  Elf_Internal_Rela *relend;
-  unsigned isec_segment, got_segment, plt_segment,
-    check_segment[2];
-  int silence_segment_error = !(info->shared || info->pie);
+  const struct bfinfdpic_relocs_info *e1 = entry1;
+  const struct bfinfdpic_relocs_info *e2 = entry2;
 
-  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
-  sym_hashes = elf_sym_hashes (input_bfd);
-  relend     = relocs + input_section->reloc_count;
+  return e1->symndx == e2->symndx && e1->addend == e2->addend
+    && (e1->symndx == -1 ? e1->d.h == e2->d.h : e1->d.abfd == e2->d.abfd);
+}
 
-  isec_segment = _bfinfdpic_osec_to_segment (output_bfd,
-                                            input_section->output_section);
-  if (IS_FDPIC (output_bfd) && bfinfdpic_got_section (info))
-    got_segment = _bfinfdpic_osec_to_segment (output_bfd,
-                                             bfinfdpic_got_section (info)
-                                             ->output_section);
-  else
-    got_segment = -1;
-  if (IS_FDPIC (output_bfd) && elf_hash_table (info)->dynamic_sections_created)
-    plt_segment = _bfinfdpic_osec_to_segment (output_bfd,
-                                             bfinfdpic_plt_section (info)
-                                             ->output_section);
-  else
-    plt_segment = -1;
+/* Find or create an entry in a hash table HT that matches the key
+   fields of the given ENTRY.  If it's not found, memory for a new
+   entry is allocated in ABFD's obstack.  */
+static struct bfinfdpic_relocs_info *
+bfinfdpic_relocs_info_find (struct htab *ht,
+                          bfd *abfd,
+                          const struct bfinfdpic_relocs_info *entry,
+                          enum insert_option insert)
+{
+  struct bfinfdpic_relocs_info **loc =
+    (struct bfinfdpic_relocs_info **) htab_find_slot (ht, entry, insert);
 
-  for (rel = relocs; rel < relend; rel ++)
-    {
-      reloc_howto_type *howto;
-      unsigned long r_symndx;
-      Elf_Internal_Sym *sym;
-      asection *sec;
-      struct elf_link_hash_entry *h;
-      bfd_vma relocation;
-      bfd_reloc_status_type r;
-      const char * name = NULL;
-      int r_type;
-      asection *osec;
-      struct bfinfdpic_relocs_info *picrel;
-      bfd_vma orig_addend = rel->r_addend;
+  if (! loc)
+    return NULL;
 
-      r_type = ELF32_R_TYPE (rel->r_info);
+  if (*loc)
+    return *loc;
 
-      if (r_type == R_BFIN_GNU_VTINHERIT
-         || r_type == R_BFIN_GNU_VTENTRY)
-       continue;
+  *loc = bfd_zalloc (abfd, sizeof (**loc));
 
-      r_symndx = ELF32_R_SYM (rel->r_info);
-      howto = bfin_reloc_type_lookup (input_bfd, r_type);
-      if (howto == NULL)
-       {
-         bfd_set_error (bfd_error_bad_value);
-         return FALSE;
-       }
+  if (! *loc)
+    return *loc;
 
-      h      = NULL;
-      sym    = NULL;
-      sec    = NULL;
+  (*loc)->symndx = entry->symndx;
+  (*loc)->d = entry->d;
+  (*loc)->addend = entry->addend;
+  (*loc)->plt_entry = (bfd_vma)-1;
+  (*loc)->lzplt_entry = (bfd_vma)-1;
 
-      if (r_symndx < symtab_hdr->sh_info)
-       {
-         sym = local_syms + r_symndx;
-         osec = sec = local_sections [r_symndx];
-         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+  return *loc;
+}
 
-         name = bfd_elf_string_from_elf_section
-           (input_bfd, symtab_hdr->sh_link, sym->st_name);
-         name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name;
-       }
-      else
-       {
-         bfd_boolean warned;
-         bfd_boolean unresolved_reloc;
+/* Obtain the address of the entry in HT associated with H's symbol +
+   addend, creating a new entry if none existed.  ABFD is only used
+   for memory allocation purposes.  */
+inline static struct bfinfdpic_relocs_info *
+bfinfdpic_relocs_info_for_global (struct htab *ht,
+                                bfd *abfd,
+                                struct elf_link_hash_entry *h,
+                                bfd_vma addend,
+                                enum insert_option insert)
+{
+  struct bfinfdpic_relocs_info entry;
 
-         RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
-                                  r_symndx, symtab_hdr, sym_hashes,
-                                  h, sec, relocation,
-                                  unresolved_reloc, warned);
-         osec = sec;
-       }
+  entry.symndx = -1;
+  entry.d.h = h;
+  entry.addend = addend;
 
-      if (sec != NULL && elf_discarded_section (sec))
-       {
-         /* For relocs against symbols from removed linkonce sections,
-            or sections discarded by a linker script, we just want the
-            section contents zeroed.  Avoid any special processing.  */
-         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
-         rel->r_info = 0;
-         rel->r_addend = 0;
-         continue;
-       }
+  return bfinfdpic_relocs_info_find (ht, abfd, &entry, insert);
+}
 
-      if (info->relocatable)
-       continue;
+/* Obtain the address of the entry in HT associated with the SYMNDXth
+   local symbol of the input bfd ABFD, plus the addend, creating a new
+   entry if none existed.  */
+inline static struct bfinfdpic_relocs_info *
+bfinfdpic_relocs_info_for_local (struct htab *ht,
+                               bfd *abfd,
+                               long symndx,
+                               bfd_vma addend,
+                               enum insert_option insert)
+{
+  struct bfinfdpic_relocs_info entry;
 
-      if (h != NULL
-         && (h->root.type == bfd_link_hash_defined
-             || h->root.type == bfd_link_hash_defweak)
-         && !BFINFDPIC_SYM_LOCAL (info, h))
-       {
-         osec = sec = NULL;
-         relocation = 0;
-       }
+  entry.symndx = symndx;
+  entry.d.abfd = abfd;
+  entry.addend = addend;
 
-      switch (r_type)
-       {
-       case R_pcrel24:
-       case R_pcrel24_jump_l:
-       case R_byte4_data:
-         if (! IS_FDPIC (output_bfd))
-           goto non_fdpic;
+  return bfinfdpic_relocs_info_find (ht, abfd, &entry, insert);
+}
 
-       case R_BFIN_GOT17M4:
-       case R_BFIN_GOTHI:
-       case R_BFIN_GOTLO:
-       case R_BFIN_FUNCDESC_GOT17M4:
-       case R_BFIN_FUNCDESC_GOTHI:
-       case R_BFIN_FUNCDESC_GOTLO:
-       case R_BFIN_GOTOFF17M4:
-       case R_BFIN_GOTOFFHI:
-       case R_BFIN_GOTOFFLO:
-       case R_BFIN_FUNCDESC_GOTOFF17M4:
-       case R_BFIN_FUNCDESC_GOTOFFHI:
-       case R_BFIN_FUNCDESC_GOTOFFLO:
-       case R_BFIN_FUNCDESC:
-       case R_BFIN_FUNCDESC_VALUE:
-         if (h != NULL)
-           picrel = bfinfdpic_relocs_info_for_global (bfinfdpic_relocs_info
-                                                      (info), input_bfd, h,
-                                                      orig_addend, INSERT);
-         else
-           /* In order to find the entry we created before, we must
-              use the original addend, not the one that may have been
-              modified by _bfd_elf_rela_local_sym().  */
-           picrel = bfinfdpic_relocs_info_for_local (bfinfdpic_relocs_info
-                                                     (info), input_bfd, r_symndx,
-                                                     orig_addend, INSERT);
-         if (! picrel)
-           return FALSE;
+/* Merge fields set by check_relocs() of two entries that end up being
+   mapped to the same (presumably global) symbol.  */
 
-         if (!_bfinfdpic_emit_got_relocs_plt_entries (picrel, output_bfd, info,
-                                                      osec, sym,
-                                                      rel->r_addend))
-           {
-             (*_bfd_error_handler)
-               (_("%B: relocation at `%A+0x%x' references symbol `%s' with nonzero addend"),
-                input_bfd, input_section, rel->r_offset, name);
-             return FALSE;
+inline static void
+bfinfdpic_pic_merge_early_relocs_info (struct bfinfdpic_relocs_info *e2,
+                                     struct bfinfdpic_relocs_info const *e1)
+{
+  e2->got17m4 |= e1->got17m4;
+  e2->gothilo |= e1->gothilo;
+  e2->fd |= e1->fd;
+  e2->fdgot17m4 |= e1->fdgot17m4;
+  e2->fdgothilo |= e1->fdgothilo;
+  e2->fdgoff17m4 |= e1->fdgoff17m4;
+  e2->fdgoffhilo |= e1->fdgoffhilo;
+  e2->gotoff |= e1->gotoff;
+  e2->call |= e1->call;
+  e2->sym |= e1->sym;
+}
 
-           }
+/* Every block of 65535 lazy PLT entries shares a single call to the
+   resolver, inserted in the 32768th lazy PLT entry (i.e., entry #
+   32767, counting from 0).  All other lazy PLT entries branch to it
+   in a single instruction.  */
 
-         break;
+#define LZPLT_RESOLVER_EXTRA 10
+#define LZPLT_NORMAL_SIZE 6
+#define LZPLT_ENTRIES 1362
 
-       default:
-       non_fdpic:
-         picrel = NULL;
-         if (h && ! BFINFDPIC_SYM_LOCAL (info, h))
-           {
-             info->callbacks->warning
-               (info, _("relocation references symbol not defined in the module"),
-                name, input_bfd, input_section, rel->r_offset);
-             return FALSE;
-           }
-         break;
-       }
+#define BFINFDPIC_LZPLT_BLOCK_SIZE ((bfd_vma) LZPLT_NORMAL_SIZE * LZPLT_ENTRIES + LZPLT_RESOLVER_EXTRA)
+#define BFINFDPIC_LZPLT_RESOLV_LOC (LZPLT_NORMAL_SIZE * LZPLT_ENTRIES / 2)
 
-      switch (r_type)
-       {
-       case R_pcrel24:
-       case R_pcrel24_jump_l:
-         check_segment[0] = isec_segment;
-         if (! IS_FDPIC (output_bfd))
-           check_segment[1] = isec_segment;
-         else if (picrel->plt)
-           {
-             relocation = bfinfdpic_plt_section (info)->output_section->vma
-               + bfinfdpic_plt_section (info)->output_offset
-               + picrel->plt_entry;
-             check_segment[1] = plt_segment;
-           }
-         /* We don't want to warn on calls to undefined weak symbols,
-            as calls to them must be protected by non-NULL tests
-            anyway, and unprotected calls would invoke undefined
-            behavior.  */
-         else if (picrel->symndx == -1
-                  && picrel->d.h->root.type == bfd_link_hash_undefweak)
-           check_segment[1] = check_segment[0];
-         else
-           check_segment[1] = sec
-             ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section)
-             : (unsigned)-1;
-         break;
+/* Add a dynamic relocation to the SRELOC section.  */
 
-       case R_BFIN_GOT17M4:
-       case R_BFIN_GOTHI:
-       case R_BFIN_GOTLO:
-         relocation = picrel->got_entry;
-         check_segment[0] = check_segment[1] = got_segment;
-         break;
+inline static bfd_vma
+_bfinfdpic_add_dyn_reloc (bfd *output_bfd, asection *sreloc, bfd_vma offset,
+                        int reloc_type, long dynindx, bfd_vma addend,
+                        struct bfinfdpic_relocs_info *entry)
+{
+  Elf_Internal_Rela outrel;
+  bfd_vma reloc_offset;
 
-       case R_BFIN_FUNCDESC_GOT17M4:
-       case R_BFIN_FUNCDESC_GOTHI:
-       case R_BFIN_FUNCDESC_GOTLO:
-         relocation = picrel->fdgot_entry;
-         check_segment[0] = check_segment[1] = got_segment;
-         break;
+  outrel.r_offset = offset;
+  outrel.r_info = ELF32_R_INFO (dynindx, reloc_type);
+  outrel.r_addend = addend;
 
-       case R_BFIN_GOTOFFHI:
-       case R_BFIN_GOTOFF17M4:
-       case R_BFIN_GOTOFFLO:
-         relocation -= bfinfdpic_got_section (info)->output_section->vma
-           + bfinfdpic_got_section (info)->output_offset
-           + bfinfdpic_got_initial_offset (info);
-         check_segment[0] = got_segment;
-         check_segment[1] = sec
-           ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section)
-           : (unsigned)-1;
-         break;
+  reloc_offset = sreloc->reloc_count * sizeof (Elf32_External_Rel);
+  BFD_ASSERT (reloc_offset < sreloc->size);
+  bfd_elf32_swap_reloc_out (output_bfd, &outrel,
+                           sreloc->contents + reloc_offset);
+  sreloc->reloc_count++;
 
-       case R_BFIN_FUNCDESC_GOTOFF17M4:
-       case R_BFIN_FUNCDESC_GOTOFFHI:
-       case R_BFIN_FUNCDESC_GOTOFFLO:
-         relocation = picrel->fd_entry;
-         check_segment[0] = check_segment[1] = got_segment;
-         break;
+  /* If the entry's index is zero, this relocation was probably to a
+     linkonce section that got discarded.  We reserved a dynamic
+     relocation, but it was for another entry than the one we got at
+     the time of emitting the relocation.  Unfortunately there's no
+     simple way for us to catch this situation, since the relocation
+     is cleared right before calling relocate_section, at which point
+     we no longer know what the relocation used to point to.  */
+  if (entry->symndx)
+    {
+      BFD_ASSERT (entry->dynrelocs > 0);
+      entry->dynrelocs--;
+    }
 
-       case R_BFIN_FUNCDESC:
-         {
-           int dynindx;
-           bfd_vma addend = rel->r_addend;
+  return reloc_offset;
+}
 
-           if (! (h && h->root.type == bfd_link_hash_undefweak
-                  && BFINFDPIC_SYM_LOCAL (info, h)))
-             {
-               /* If the symbol is dynamic and there may be dynamic
-                  symbol resolution because we are or are linked with a
-                  shared library, emit a FUNCDESC relocation such that
-                  the dynamic linker will allocate the function
-                  descriptor.  If the symbol needs a non-local function
-                  descriptor but binds locally (e.g., its visibility is
-                  protected, emit a dynamic relocation decayed to
-                  section+offset.  */
-               if (h && ! BFINFDPIC_FUNCDESC_LOCAL (info, h)
-                   && BFINFDPIC_SYM_LOCAL (info, h)
-                   && !(info->executable && !info->pie))
-                 {
-                   dynindx = elf_section_data (h->root.u.def.section
-                                               ->output_section)->dynindx;
-                   addend += h->root.u.def.section->output_offset
-                     + h->root.u.def.value;
-                 }
-               else if (h && ! BFINFDPIC_FUNCDESC_LOCAL (info, h))
-                 {
-                   if (addend)
-                     {
-                       info->callbacks->warning
-                         (info, _("R_BFIN_FUNCDESC references dynamic symbol with nonzero addend"),
-                          name, input_bfd, input_section, rel->r_offset);
-                       return FALSE;
-                     }
-                   dynindx = h->dynindx;
-                 }
-               else
-                 {
-                   /* Otherwise, we know we have a private function
-                      descriptor, so reference it directly.  */
-                   BFD_ASSERT (picrel->privfd);
-                   r_type = R_byte4_data;
-                   dynindx = elf_section_data (bfinfdpic_got_section (info)
-                                               ->output_section)->dynindx;
-                   addend = bfinfdpic_got_section (info)->output_offset
-                     + bfinfdpic_got_initial_offset (info)
-                     + picrel->fd_entry;
-                 }
+/* Add a fixup to the ROFIXUP section.  */
 
-               /* If there is room for dynamic symbol resolution, emit
-                  the dynamic relocation.  However, if we're linking an
-                  executable at a fixed location, we won't have emitted a
-                  dynamic symbol entry for the got section, so idx will
-                  be zero, which means we can and should compute the
-                  address of the private descriptor ourselves.  */
-               if (info->executable && !info->pie
-                   && (!h || BFINFDPIC_FUNCDESC_LOCAL (info, h)))
-                 {
-                   addend += bfinfdpic_got_section (info)->output_section->vma;
-                   if ((bfd_get_section_flags (output_bfd,
-                                               input_section->output_section)
-                        & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
-                     {
-                       if (_bfinfdpic_osec_readonly_p (output_bfd,
-                                                      input_section
-                                                      ->output_section))
-                         {
-                           info->callbacks->warning
-                             (info,
-                              _("cannot emit fixups in read-only section"),
-                              name, input_bfd, input_section, rel->r_offset);
-                           return FALSE;
-                         }
-                       _bfinfdpic_add_rofixup (output_bfd,
-                                              bfinfdpic_gotfixup_section
-                                              (info),
-                                              _bfd_elf_section_offset
-                                              (output_bfd, info,
-                                               input_section, rel->r_offset)
-                                              + input_section
-                                              ->output_section->vma
-                                              + input_section->output_offset,
-                                              picrel);
-                     }
-                 }
-               else if ((bfd_get_section_flags (output_bfd,
-                                                input_section->output_section)
-                         & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
-                 {
-                   bfd_vma offset;
+static bfd_vma
+_bfinfdpic_add_rofixup (bfd *output_bfd, asection *rofixup, bfd_vma offset,
+                      struct bfinfdpic_relocs_info *entry)
+{
+  bfd_vma fixup_offset;
 
-                   if (_bfinfdpic_osec_readonly_p (output_bfd,
-                                                  input_section
-                                                  ->output_section))
-                     {
-                       info->callbacks->warning
-                         (info,
-                          _("cannot emit dynamic relocations in read-only section"),
-                          name, input_bfd, input_section, rel->r_offset);
-                       return FALSE;
-                     }
-                   offset = _bfd_elf_section_offset (output_bfd, info,
-                                                     input_section, rel->r_offset);
-                   /* Only output a reloc for a not deleted entry.  */
-                   if (offset >= (bfd_vma) -2)
-                     _bfinfdpic_add_dyn_reloc (output_bfd,
-                                               bfinfdpic_gotrel_section (info),
-                                               0,
-                                               R_unused0,
-                                               dynindx, addend, picrel);
-                   else
-                     _bfinfdpic_add_dyn_reloc (output_bfd,
-                                               bfinfdpic_gotrel_section (info),
-                                               offset + input_section
-                                               ->output_section->vma
-                                               + input_section->output_offset,
-                                               r_type,
-                                               dynindx, addend, picrel);
-                 }
-               else
-                 addend += bfinfdpic_got_section (info)->output_section->vma;
-             }
+  if (rofixup->flags & SEC_EXCLUDE)
+    return -1;
 
-           /* We want the addend in-place because dynamic
-              relocations are REL.  Setting relocation to it should
-              arrange for it to be installed.  */
-           relocation = addend - rel->r_addend;
-         }
-         check_segment[0] = check_segment[1] = got_segment;
-         break;
+  fixup_offset = rofixup->reloc_count * 4;
+  if (rofixup->contents)
+    {
+      BFD_ASSERT (fixup_offset < rofixup->size);
+      bfd_put_32 (output_bfd, offset, rofixup->contents + fixup_offset);
+    }
+  rofixup->reloc_count++;
 
-       case R_byte4_data:
-         if (! IS_FDPIC (output_bfd))
-           {
-             check_segment[0] = check_segment[1] = -1;
-             break;
-           }
-         /* Fall through.  */
-       case R_BFIN_FUNCDESC_VALUE:
-         {
-           int dynindx;
-           bfd_vma addend = rel->r_addend;
-           bfd_vma offset;
-           offset = _bfd_elf_section_offset (output_bfd, info,
-                                             input_section, rel->r_offset);
+  if (entry && entry->symndx)
+    {
+      /* See discussion about symndx == 0 in _bfinfdpic_add_dyn_reloc
+        above.  */
+      BFD_ASSERT (entry->fixups > 0);
+      entry->fixups--;
+    }
 
-           /* If the symbol is dynamic but binds locally, use
-              section+offset.  */
-           if (h && ! BFINFDPIC_SYM_LOCAL (info, h))
-             {
-               if (addend && r_type == R_BFIN_FUNCDESC_VALUE)
-                 {
-                   info->callbacks->warning
-                     (info, _("R_BFIN_FUNCDESC_VALUE references dynamic symbol with nonzero addend"),
-                      name, input_bfd, input_section, rel->r_offset);
-                   return FALSE;
-                 }
-               dynindx = h->dynindx;
-             }
-           else
-             {
-               if (h)
-                 addend += h->root.u.def.value;
-               else
-                 addend += sym->st_value;
-               if (osec)
-                 addend += osec->output_offset;
-               if (osec && osec->output_section
-                   && ! bfd_is_abs_section (osec->output_section)
-                   && ! bfd_is_und_section (osec->output_section))
-                 dynindx = elf_section_data (osec->output_section)->dynindx;
-               else
-                 dynindx = 0;
-             }
+  return fixup_offset;
+}
 
-           /* If we're linking an executable at a fixed address, we
-              can omit the dynamic relocation as long as the symbol
-              is defined in the current link unit (which is implied
-              by its output section not being NULL).  */
-           if (info->executable && !info->pie
-               && (!h || BFINFDPIC_SYM_LOCAL (info, h)))
-             {
-               if (osec)
-                 addend += osec->output_section->vma;
-               if (IS_FDPIC (input_bfd)
-                   && (bfd_get_section_flags (output_bfd,
-                                              input_section->output_section)
-                       & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
-                 {
-                   if (_bfinfdpic_osec_readonly_p (output_bfd,
-                                                  input_section
-                                                  ->output_section))
-                     {
-                       info->callbacks->warning
-                         (info,
-                          _("cannot emit fixups in read-only section"),
-                          name, input_bfd, input_section, rel->r_offset);
-                       return FALSE;
-                     }
-                   if (!h || h->root.type != bfd_link_hash_undefweak)
-                     {
-                       /* Only output a reloc for a not deleted entry.  */
-                       if (offset >= (bfd_vma)-2)
-                         _bfinfdpic_add_rofixup (output_bfd,
-                                                 bfinfdpic_gotfixup_section
-                                                 (info), -1, picrel);
-                       else
-                         _bfinfdpic_add_rofixup (output_bfd,
-                                                 bfinfdpic_gotfixup_section
-                                                 (info),
-                                                 offset + input_section
-                                                 ->output_section->vma
-                                                 + input_section->output_offset,
-                                                 picrel);
+/* Find the segment number in which OSEC, and output section, is
+   located.  */
 
-                       if (r_type == R_BFIN_FUNCDESC_VALUE)
-                         {
-                           if (offset >= (bfd_vma)-2)
-                             _bfinfdpic_add_rofixup
-                               (output_bfd,
-                                bfinfdpic_gotfixup_section (info),
-                                -1, picrel);
-                           else
-                             _bfinfdpic_add_rofixup
-                               (output_bfd,
-                                bfinfdpic_gotfixup_section (info),
-                                offset + input_section->output_section->vma
-                                + input_section->output_offset + 4, picrel);
-                         }
-                     }
-                 }
-             }
-           else
-             {
-               if ((bfd_get_section_flags (output_bfd,
-                                           input_section->output_section)
-                    & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
-                 {
-                   if (_bfinfdpic_osec_readonly_p (output_bfd,
-                                                  input_section
-                                                  ->output_section))
-                     {
-                       info->callbacks->warning
-                         (info,
-                          _("cannot emit dynamic relocations in read-only section"),
-                          name, input_bfd, input_section, rel->r_offset);
-                       return FALSE;
-                     }
-                   /* Only output a reloc for a not deleted entry.  */
-                   if (offset >= (bfd_vma)-2)
-                     _bfinfdpic_add_dyn_reloc (output_bfd,
-                                               bfinfdpic_gotrel_section (info),
-                                               0, R_unused0, dynindx, addend, picrel);
-                   else
-                     _bfinfdpic_add_dyn_reloc (output_bfd,
-                                               bfinfdpic_gotrel_section (info),
-                                               offset
-                                               + input_section
-                                               ->output_section->vma
-                                               + input_section->output_offset,
-                                               r_type, dynindx, addend, picrel);
-                 }
-               else if (osec)
-                 addend += osec->output_section->vma;
-               /* We want the addend in-place because dynamic
-                  relocations are REL.  Setting relocation to it
-                  should arrange for it to be installed.  */
-               relocation = addend - rel->r_addend;
-             }
+static unsigned
+_bfinfdpic_osec_to_segment (bfd *output_bfd, asection *osec)
+{
+  Elf_Internal_Phdr *p = _bfd_elf_find_segment_containing_section (output_bfd, osec);
 
-           if (r_type == R_BFIN_FUNCDESC_VALUE && offset < (bfd_vma)-2)
-             {
-               /* If we've omitted the dynamic relocation, just emit
-                  the fixed addresses of the symbol and of the local
-                  GOT base offset.  */
-               if (info->executable && !info->pie
-                   && (!h || BFINFDPIC_SYM_LOCAL (info, h)))
-                 bfd_put_32 (output_bfd,
-                             bfinfdpic_got_section (info)->output_section->vma
-                             + bfinfdpic_got_section (info)->output_offset
-                             + bfinfdpic_got_initial_offset (info),
-                             contents + rel->r_offset + 4);
-               else
-                 /* A function descriptor used for lazy or local
-                    resolving is initialized such that its high word
-                    contains the output section index in which the
-                    PLT entries are located, and the low word
-                    contains the offset of the lazy PLT entry entry
-                    point into that section.  */
-                 bfd_put_32 (output_bfd,
-                             h && ! BFINFDPIC_SYM_LOCAL (info, h)
-                             ? 0
-                             : _bfinfdpic_osec_to_segment (output_bfd,
-                                                           sec
-                                                           ->output_section),
-                             contents + rel->r_offset + 4);
-             }
-         }
-         check_segment[0] = check_segment[1] = got_segment;
-         break;
+  return (p != NULL) ? p - elf_tdata (output_bfd)->phdr : -1;
+}
+
+inline static bfd_boolean
+_bfinfdpic_osec_readonly_p (bfd *output_bfd, asection *osec)
+{
+  unsigned seg = _bfinfdpic_osec_to_segment (output_bfd, osec);
+
+  return ! (elf_tdata (output_bfd)->phdr[seg].p_flags & PF_W);
+}
+
+/* Generate relocations for GOT entries, function descriptors, and
+   code for PLT and lazy PLT entries.  */
+
+inline static bfd_boolean
+_bfinfdpic_emit_got_relocs_plt_entries (struct bfinfdpic_relocs_info *entry,
+                                       bfd *output_bfd,
+                                       struct bfd_link_info *info,
+                                       asection *sec,
+                                       Elf_Internal_Sym *sym,
+                                       bfd_vma addend)
+
+{
+  bfd_vma fd_lazy_rel_offset = (bfd_vma)-1;
+  int dynindx = -1;
+
+  if (entry->done)
+    return TRUE;
+  entry->done = 1;
+
+  if (entry->got_entry || entry->fdgot_entry || entry->fd_entry)
+    {
+      /* If the symbol is dynamic, consider it for dynamic
+        relocations, otherwise decay to section + offset.  */
+      if (entry->symndx == -1 && entry->d.h->dynindx != -1)
+       dynindx = entry->d.h->dynindx;
+      else
+       {
+         if (sec->output_section
+             && ! bfd_is_abs_section (sec->output_section)
+             && ! bfd_is_und_section (sec->output_section))
+           dynindx = elf_section_data (sec->output_section)->dynindx;
+         else
+           dynindx = 0;
+       }
+    }
+
+  /* Generate relocation for GOT entry pointing to the symbol.  */
+  if (entry->got_entry)
+    {
+      int idx = dynindx;
+      bfd_vma ad = addend;
+
+      /* If the symbol is dynamic but binds locally, use
+        section+offset.  */
+      if (sec && (entry->symndx != -1
+                 || BFINFDPIC_SYM_LOCAL (info, entry->d.h)))
+       {
+         if (entry->symndx == -1)
+           ad += entry->d.h->root.u.def.value;
+         else
+           ad += sym->st_value;
+         ad += sec->output_offset;
+         if (sec->output_section && elf_section_data (sec->output_section))
+           idx = elf_section_data (sec->output_section)->dynindx;
+         else
+           idx = 0;
+       }
+
+      /* If we're linking an executable at a fixed address, we can
+        omit the dynamic relocation as long as the symbol is local to
+        this module.  */
+      if (info->executable && !info->pie
+         && (entry->symndx != -1
+             || BFINFDPIC_SYM_LOCAL (info, entry->d.h)))
+       {
+         if (sec)
+           ad += sec->output_section->vma;
+         if (entry->symndx != -1
+             || entry->d.h->root.type != bfd_link_hash_undefweak)
+           _bfinfdpic_add_rofixup (output_bfd,
+                                  bfinfdpic_gotfixup_section (info),
+                                  bfinfdpic_got_section (info)->output_section
+                                  ->vma
+                                  + bfinfdpic_got_section (info)->output_offset
+                                  + bfinfdpic_got_initial_offset (info)
+                                  + entry->got_entry, entry);
+       }
+      else
+       _bfinfdpic_add_dyn_reloc (output_bfd, bfinfdpic_gotrel_section (info),
+                                _bfd_elf_section_offset
+                                (output_bfd, info,
+                                 bfinfdpic_got_section (info),
+                                 bfinfdpic_got_initial_offset (info)
+                                 + entry->got_entry)
+                                + bfinfdpic_got_section (info)
+                                ->output_section->vma
+                                + bfinfdpic_got_section (info)->output_offset,
+                                R_byte4_data, idx, ad, entry);
+
+      bfd_put_32 (output_bfd, ad,
+                 bfinfdpic_got_section (info)->contents
+                 + bfinfdpic_got_initial_offset (info)
+                 + entry->got_entry);
+    }
+
+  /* Generate relocation for GOT entry pointing to a canonical
+     function descriptor.  */
+  if (entry->fdgot_entry)
+    {
+      int reloc, idx;
+      bfd_vma ad = 0;
+
+      if (! (entry->symndx == -1
+            && entry->d.h->root.type == bfd_link_hash_undefweak
+            && BFINFDPIC_SYM_LOCAL (info, entry->d.h)))
+       {
+         /* If the symbol is dynamic and there may be dynamic symbol
+            resolution because we are, or are linked with, a shared
+            library, emit a FUNCDESC relocation such that the dynamic
+            linker will allocate the function descriptor.  If the
+            symbol needs a non-local function descriptor but binds
+            locally (e.g., its visibility is protected, emit a
+            dynamic relocation decayed to section+offset.  */
+         if (entry->symndx == -1
+             && ! BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h)
+             && BFINFDPIC_SYM_LOCAL (info, entry->d.h)
+             && !(info->executable && !info->pie))
+           {
+             reloc = R_BFIN_FUNCDESC;
+             idx = elf_section_data (entry->d.h->root.u.def.section
+                                     ->output_section)->dynindx;
+             ad = entry->d.h->root.u.def.section->output_offset
+               + entry->d.h->root.u.def.value;
+           }
+         else if (entry->symndx == -1
+                  && ! BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h))
+           {
+             reloc = R_BFIN_FUNCDESC;
+             idx = dynindx;
+             ad = addend;
+             if (ad)
+               return FALSE;
+           }
+         else
+           {
+             /* Otherwise, we know we have a private function descriptor,
+                so reference it directly.  */
+             if (elf_hash_table (info)->dynamic_sections_created)
+               BFD_ASSERT (entry->privfd);
+             reloc = R_byte4_data;
+             idx = elf_section_data (bfinfdpic_got_section (info)
+                                     ->output_section)->dynindx;
+             ad = bfinfdpic_got_section (info)->output_offset
+               + bfinfdpic_got_initial_offset (info) + entry->fd_entry;
+           }
+
+         /* If there is room for dynamic symbol resolution, emit the
+            dynamic relocation.  However, if we're linking an
+            executable at a fixed location, we won't have emitted a
+            dynamic symbol entry for the got section, so idx will be
+            zero, which means we can and should compute the address
+            of the private descriptor ourselves.  */
+         if (info->executable && !info->pie
+             && (entry->symndx != -1
+                 || BFINFDPIC_FUNCDESC_LOCAL (info, entry->d.h)))
+           {
+             ad += bfinfdpic_got_section (info)->output_section->vma;
+             _bfinfdpic_add_rofixup (output_bfd,
+                                    bfinfdpic_gotfixup_section (info),
+                                    bfinfdpic_got_section (info)
+                                    ->output_section->vma
+                                    + bfinfdpic_got_section (info)
+                                    ->output_offset
+                                    + bfinfdpic_got_initial_offset (info)
+                                    + entry->fdgot_entry, entry);
+           }
+         else
+           _bfinfdpic_add_dyn_reloc (output_bfd,
+                                    bfinfdpic_gotrel_section (info),
+                                    _bfd_elf_section_offset
+                                    (output_bfd, info,
+                                     bfinfdpic_got_section (info),
+                                     bfinfdpic_got_initial_offset (info)
+                                     + entry->fdgot_entry)
+                                    + bfinfdpic_got_section (info)
+                                    ->output_section->vma
+                                    + bfinfdpic_got_section (info)
+                                    ->output_offset,
+                                    reloc, idx, ad, entry);
+       }
 
-       default:
-         check_segment[0] = isec_segment;
-         check_segment[1] = sec
-           ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section)
-           : (unsigned)-1;
-         break;
+      bfd_put_32 (output_bfd, ad,
+                 bfinfdpic_got_section (info)->contents
+                 + bfinfdpic_got_initial_offset (info)
+                 + entry->fdgot_entry);
+    }
+
+  /* Generate relocation to fill in a private function descriptor in
+     the GOT.  */
+  if (entry->fd_entry)
+    {
+      int idx = dynindx;
+      bfd_vma ad = addend;
+      bfd_vma ofst;
+      long lowword, highword;
+
+      /* If the symbol is dynamic but binds locally, use
+        section+offset.  */
+      if (sec && (entry->symndx != -1
+                 || BFINFDPIC_SYM_LOCAL (info, entry->d.h)))
+       {
+         if (entry->symndx == -1)
+           ad += entry->d.h->root.u.def.value;
+         else
+           ad += sym->st_value;
+         ad += sec->output_offset;
+         if (sec->output_section && elf_section_data (sec->output_section))
+           idx = elf_section_data (sec->output_section)->dynindx;
+         else
+           idx = 0;
        }
 
-      if (check_segment[0] != check_segment[1] && IS_FDPIC (output_bfd))
+      /* If we're linking an executable at a fixed address, we can
+        omit the dynamic relocation as long as the symbol is local to
+        this module.  */
+      if (info->executable && !info->pie
+         && (entry->symndx != -1 || BFINFDPIC_SYM_LOCAL (info, entry->d.h)))
        {
-#if 1 /* If you take this out, remove the #error from fdpic-static-6.d
-        in the ld testsuite.  */
-         /* This helps catch problems in GCC while we can't do more
-            than static linking.  The idea is to test whether the
-            input file basename is crt0.o only once.  */
-         if (silence_segment_error == 1)
-           silence_segment_error =
-             (strlen (input_bfd->filename) == 6
-              && strcmp (input_bfd->filename, "crt0.o") == 0)
-             || (strlen (input_bfd->filename) > 6
-                 && strcmp (input_bfd->filename
-                            + strlen (input_bfd->filename) - 7,
-                            "/crt0.o") == 0)
-             ? -1 : 0;
-#endif
-         if (!silence_segment_error
-             /* We don't want duplicate errors for undefined
-                symbols.  */
-             && !(picrel && picrel->symndx == -1
-                  && picrel->d.h->root.type == bfd_link_hash_undefined))
-           info->callbacks->warning
-             (info,
-              (info->shared || info->pie)
-              ? _("relocations between different segments are not supported")
-              : _("warning: relocation references a different segment"),
-              name, input_bfd, input_section, rel->r_offset);
-         if (!silence_segment_error && (info->shared || info->pie))
-           return FALSE;
-         elf_elfheader (output_bfd)->e_flags |= EF_BFIN_PIC;
+         if (sec)
+           ad += sec->output_section->vma;
+         ofst = 0;
+         if (entry->symndx != -1
+             || entry->d.h->root.type != bfd_link_hash_undefweak)
+           {
+             _bfinfdpic_add_rofixup (output_bfd,
+                                    bfinfdpic_gotfixup_section (info),
+                                    bfinfdpic_got_section (info)
+                                    ->output_section->vma
+                                    + bfinfdpic_got_section (info)
+                                    ->output_offset
+                                    + bfinfdpic_got_initial_offset (info)
+                                    + entry->fd_entry, entry);
+             _bfinfdpic_add_rofixup (output_bfd,
+                                    bfinfdpic_gotfixup_section (info),
+                                    bfinfdpic_got_section (info)
+                                    ->output_section->vma
+                                    + bfinfdpic_got_section (info)
+                                    ->output_offset
+                                    + bfinfdpic_got_initial_offset (info)
+                                    + entry->fd_entry + 4, entry);
+           }
+       }
+      else
+       {
+         ofst
+           = _bfinfdpic_add_dyn_reloc (output_bfd,
+                                       entry->lazyplt
+                                       ? bfinfdpic_pltrel_section (info)
+                                       : bfinfdpic_gotrel_section (info),
+                                       _bfd_elf_section_offset
+                                       (output_bfd, info,
+                                        bfinfdpic_got_section (info),
+                                        bfinfdpic_got_initial_offset (info)
+                                        + entry->fd_entry)
+                                       + bfinfdpic_got_section (info)
+                                       ->output_section->vma
+                                       + bfinfdpic_got_section (info)
+                                       ->output_offset,
+                                       R_BFIN_FUNCDESC_VALUE, idx, ad, entry);
        }
 
-      switch (r_type)
+      /* If we've omitted the dynamic relocation, just emit the fixed
+        addresses of the symbol and of the local GOT base offset.  */
+      if (info->executable && !info->pie && sec && sec->output_section)
        {
-       case R_BFIN_GOTOFFHI:
-         /* We need the addend to be applied before we shift the
-            value right.  */
-         relocation += rel->r_addend;
-         /* Fall through.  */
-       case R_BFIN_GOTHI:
-       case R_BFIN_FUNCDESC_GOTHI:
-       case R_BFIN_FUNCDESC_GOTOFFHI:
-         relocation >>= 16;
-         /* Fall through.  */
+         lowword = ad;
+         highword = bfinfdpic_got_section (info)->output_section->vma
+           + bfinfdpic_got_section (info)->output_offset
+           + bfinfdpic_got_initial_offset (info);
+       }
+      else if (entry->lazyplt)
+       {
+         if (ad)
+           return FALSE;
 
-       case R_BFIN_GOTLO:
-       case R_BFIN_FUNCDESC_GOTLO:
-       case R_BFIN_GOTOFFLO:
-       case R_BFIN_FUNCDESC_GOTOFFLO:
-         relocation &= 0xffff;
-         break;
+         fd_lazy_rel_offset = ofst;
 
-       default:
-         break;
+         /* A function descriptor used for lazy or local resolving is
+            initialized such that its high word contains the output
+            section index in which the PLT entries are located, and
+            the low word contains the address of the lazy PLT entry
+            entry point, that must be within the memory region
+            assigned to that section.  */
+         lowword = entry->lzplt_entry + 4
+           + bfinfdpic_plt_section (info)->output_offset
+           + bfinfdpic_plt_section (info)->output_section->vma;
+         highword = _bfinfdpic_osec_to_segment
+           (output_bfd, bfinfdpic_plt_section (info)->output_section);
+       }
+      else
+       {
+         /* A function descriptor for a local function gets the index
+            of the section.  For a non-local function, it's
+            disregarded.  */
+         lowword = ad;
+         if (entry->symndx == -1 && entry->d.h->dynindx != -1
+             && entry->d.h->dynindx == idx)
+           highword = 0;
+         else
+           highword = _bfinfdpic_osec_to_segment
+             (output_bfd, sec->output_section);
        }
 
-      switch (r_type)
+      bfd_put_32 (output_bfd, lowword,
+                 bfinfdpic_got_section (info)->contents
+                 + bfinfdpic_got_initial_offset (info)
+                 + entry->fd_entry);
+      bfd_put_32 (output_bfd, highword,
+                 bfinfdpic_got_section (info)->contents
+                 + bfinfdpic_got_initial_offset (info)
+                 + entry->fd_entry + 4);
+    }
+
+  /* Generate code for the PLT entry.  */
+  if (entry->plt_entry != (bfd_vma) -1)
+    {
+      bfd_byte *plt_code = bfinfdpic_plt_section (info)->contents
+       + entry->plt_entry;
+
+      BFD_ASSERT (entry->fd_entry);
+
+      /* Figure out what kind of PLT entry we need, depending on the
+        location of the function descriptor within the GOT.  */
+      if (entry->fd_entry >= -(1 << (18 - 1))
+         && entry->fd_entry + 4 < (1 << (18 - 1)))
        {
-       case R_pcrel24:
-       case R_pcrel24_jump_l:
-         if (! IS_FDPIC (output_bfd) || ! picrel->plt)
-           break;
-         /* Fall through.  */
+         /* P1 = [P3 + fd_entry]; P3 = [P3 + fd_entry + 4] */
+         bfd_put_32 (output_bfd,
+                     0xe519 | ((entry->fd_entry << 14) & 0xFFFF0000),
+                     plt_code);
+         bfd_put_32 (output_bfd,
+                     0xe51b | (((entry->fd_entry + 4) << 14) & 0xFFFF0000),
+                     plt_code + 4);
+         plt_code += 8;
+       }
+      else
+       {
+         /* P1.L = fd_entry; P1.H = fd_entry;
+            P3 = P3 + P1;
+            P1 = [P3];
+            P3 = [P3 + 4];  */
+         bfd_put_32 (output_bfd,
+                     0xe109 | (entry->fd_entry << 16),
+                     plt_code);
+         bfd_put_32 (output_bfd,
+                     0xe149 | (entry->fd_entry & 0xFFFF0000),
+                     plt_code + 4);
+         bfd_put_16 (output_bfd, 0x5ad9, plt_code + 8);
+         bfd_put_16 (output_bfd, 0x9159, plt_code + 10);
+         bfd_put_16 (output_bfd, 0xac5b, plt_code + 12);
+         plt_code += 14;
+       }
+      /* JUMP (P1) */
+      bfd_put_16 (output_bfd, 0x0051, plt_code);
+    }
 
-         /* When referencing a GOT entry, a function descriptor or a
-            PLT, we don't want the addend to apply to the reference,
-            but rather to the referenced symbol.  The actual entry
-            will have already been created taking the addend into
-            account, so cancel it out here.  */
-       case R_BFIN_GOT17M4:
-       case R_BFIN_GOTHI:
-       case R_BFIN_GOTLO:
-       case R_BFIN_FUNCDESC_GOT17M4:
-       case R_BFIN_FUNCDESC_GOTHI:
-       case R_BFIN_FUNCDESC_GOTLO:
-       case R_BFIN_FUNCDESC_GOTOFF17M4:
-       case R_BFIN_FUNCDESC_GOTOFFHI:
-       case R_BFIN_FUNCDESC_GOTOFFLO:
-         /* Note that we only want GOTOFFHI, not GOTOFFLO or GOTOFF17M4
-            here, since we do want to apply the addend to the others.
-            Note that we've applied the addend to GOTOFFHI before we
-            shifted it right.  */
-       case R_BFIN_GOTOFFHI:
-         relocation -= rel->r_addend;
-         break;
+  /* Generate code for the lazy PLT entry.  */
+  if (entry->lzplt_entry != (bfd_vma) -1)
+    {
+      bfd_byte *lzplt_code = bfinfdpic_plt_section (info)->contents
+       + entry->lzplt_entry;
+      bfd_vma resolverStub_addr;
 
-       default:
-         break;
-       }
+      bfd_put_32 (output_bfd, fd_lazy_rel_offset, lzplt_code);
+      lzplt_code += 4;
 
-      r = bfin_final_link_relocate (rel, howto, input_bfd, input_section,
-                                   contents, rel->r_offset,
-                                   relocation, rel->r_addend);
+      resolverStub_addr = entry->lzplt_entry / BFINFDPIC_LZPLT_BLOCK_SIZE
+       * BFINFDPIC_LZPLT_BLOCK_SIZE + BFINFDPIC_LZPLT_RESOLV_LOC;
+      if (resolverStub_addr >= bfinfdpic_plt_initial_offset (info))
+       resolverStub_addr = bfinfdpic_plt_initial_offset (info) - LZPLT_NORMAL_SIZE - LZPLT_RESOLVER_EXTRA;
 
-      if (r != bfd_reloc_ok)
+      if (entry->lzplt_entry == resolverStub_addr)
        {
-         const char * msg = (const char *) NULL;
+         /* This is a lazy PLT entry that includes a resolver call.
+            P2 = [P3];
+            R3 = [P3 + 4];
+            JUMP (P2);  */
+         bfd_put_32 (output_bfd,
+                     0xa05b915a,
+                     lzplt_code);
+         bfd_put_16 (output_bfd, 0x0052, lzplt_code + 4);
+       }
+      else
+       {
+         /* JUMP.S  resolverStub */
+         bfd_put_16 (output_bfd,
+                     0x2000
+                     | (((resolverStub_addr - entry->lzplt_entry)
+                         / 2) & (((bfd_vma)1 << 12) - 1)),
+                     lzplt_code);
+       }
+    }
 
-         switch (r)
-           {
-           case bfd_reloc_overflow:
-             r = info->callbacks->reloc_overflow
-               (info, (h ? &h->root : NULL), name, howto->name,
-                (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
-             break;
+  return TRUE;
+}
+\f
+/* Relocate an Blackfin ELF section.
 
-           case bfd_reloc_undefined:
-             r = info->callbacks->undefined_symbol
-               (info, name, input_bfd, input_section, rel->r_offset, TRUE);
-             break;
+   The RELOCATE_SECTION function is called by the new ELF backend linker
+   to handle the relocations for a section.
 
-           case bfd_reloc_outofrange:
-             msg = _("internal error: out of range error");
-             break;
+   The relocs are always passed as Rela structures; if the section
+   actually uses Rel structures, the r_addend field will always be
+   zero.
 
-           case bfd_reloc_notsupported:
-             msg = _("internal error: unsupported relocation error");
-             break;
+   This function is responsible for adjusting the section contents as
+   necessary, and (if using Rela relocs and generating a relocatable
+   output file) adjusting the reloc addend as necessary.
 
-           case bfd_reloc_dangerous:
-             msg = _("internal error: dangerous relocation");
-             break;
+   This function does not have to worry about setting the reloc
+   address or the reloc symbol index.
 
-           default:
-             msg = _("internal error: unknown error");
-             break;
-           }
+   LOCAL_SYMS is a pointer to the swapped in local symbols.
 
-         if (msg)
-           r = info->callbacks->warning
-             (info, msg, name, input_bfd, input_section, rel->r_offset);
+   LOCAL_SECTIONS is an array giving the section in the input file
+   corresponding to the st_shndx field of each local symbol.
 
-         if (! r)
-           return FALSE;
-       }
-    }
+   The global hash table entry for the global symbols can be found
+   via elf_sym_hashes (input_bfd).
 
-  return TRUE;
-}
+   When generating relocatable output, this function must handle
+   STB_LOCAL/STT_SECTION symbols specially.  The output symbol is
+   going to be the section symbol corresponding to the output
+   section, which means that the addend must be adjusted
+   accordingly.  */
 
 static bfd_boolean
-bfin_relocate_section (bfd * output_bfd,
-                      struct bfd_link_info *info,
-                      bfd * input_bfd,
-                      asection * input_section,
-                      bfd_byte * contents,
-                      Elf_Internal_Rela * relocs,
-                      Elf_Internal_Sym * local_syms,
-                      asection ** local_sections)
+bfinfdpic_relocate_section (bfd * output_bfd,
+                           struct bfd_link_info *info,
+                           bfd * input_bfd,
+                           asection * input_section,
+                           bfd_byte * contents,
+                           Elf_Internal_Rela * relocs,
+                           Elf_Internal_Sym * local_syms,
+                           asection ** local_sections)
 {
-  bfd *dynobj;
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
-  bfd_vma *local_got_offsets;
-  asection *sgot;
-  asection *sreloc;
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
-  int i = 0;
+  unsigned isec_segment, got_segment, plt_segment,
+    check_segment[2];
+  int silence_segment_error = !(info->shared || info->pie);
 
-  dynobj = elf_hash_table (info)->dynobj;
-  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (input_bfd);
-  local_got_offsets = elf_local_got_offsets (input_bfd);
+  relend     = relocs + input_section->reloc_count;
 
-  sgot = NULL;
-  sreloc = NULL;
+  isec_segment = _bfinfdpic_osec_to_segment (output_bfd,
+                                            input_section->output_section);
+  if (IS_FDPIC (output_bfd) && bfinfdpic_got_section (info))
+    got_segment = _bfinfdpic_osec_to_segment (output_bfd,
+                                             bfinfdpic_got_section (info)
+                                             ->output_section);
+  else
+    got_segment = -1;
+  if (IS_FDPIC (output_bfd) && elf_hash_table (info)->dynamic_sections_created)
+    plt_segment = _bfinfdpic_osec_to_segment (output_bfd,
+                                             bfinfdpic_plt_section (info)
+                                             ->output_section);
+  else
+    plt_segment = -1;
 
-  rel = relocs;
-  relend = relocs + input_section->reloc_count;
-  for (; rel < relend; rel++, i++)
+  for (rel = relocs; rel < relend; rel ++)
     {
-      int r_type;
       reloc_howto_type *howto;
       unsigned long r_symndx;
-      struct elf_link_hash_entry *h;
       Elf_Internal_Sym *sym;
       asection *sec;
-      bfd_vma relocation = 0;
-      bfd_boolean unresolved_reloc;
+      struct elf_link_hash_entry *h;
+      bfd_vma relocation;
       bfd_reloc_status_type r;
-      bfd_vma address;
+      const char * name = NULL;
+      int r_type;
+      asection *osec;
+      struct bfinfdpic_relocs_info *picrel;
+      bfd_vma orig_addend = rel->r_addend;
 
       r_type = ELF32_R_TYPE (rel->r_info);
-      if (r_type < 0 || r_type >= 243)
-       {
-         bfd_set_error (bfd_error_bad_value);
-         return FALSE;
-       }
 
-      if (r_type == R_BFIN_GNU_VTENTRY
-          || r_type == R_BFIN_GNU_VTINHERIT)
+      if (r_type == R_BFIN_GNU_VTINHERIT
+         || r_type == R_BFIN_GNU_VTENTRY)
        continue;
 
+      r_symndx = ELF32_R_SYM (rel->r_info);
       howto = bfin_reloc_type_lookup (input_bfd, r_type);
       if (howto == NULL)
        {
          bfd_set_error (bfd_error_bad_value);
          return FALSE;
        }
-      r_symndx = ELF32_R_SYM (rel->r_info);
 
-      h = NULL;
-      sym = NULL;
-      sec = NULL;
-      unresolved_reloc = FALSE;
+      h      = NULL;
+      sym    = NULL;
+      sec    = NULL;
+
+      if (r_symndx < symtab_hdr->sh_info)
+       {
+         sym = local_syms + r_symndx;
+         osec = sec = local_sections [r_symndx];
+         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+
+         name = bfd_elf_string_from_elf_section
+           (input_bfd, symtab_hdr->sh_link, sym->st_name);
+         name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name;
+       }
+      else
+       {
+         bfd_boolean warned;
+         bfd_boolean unresolved_reloc;
+
+         RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
+                                  r_symndx, symtab_hdr, sym_hashes,
+                                  h, sec, relocation,
+                                  unresolved_reloc, warned);
+         osec = sec;
+       }
+
+      if (sec != NULL && elf_discarded_section (sec))
+       {
+         /* For relocs against symbols from removed linkonce sections,
+            or sections discarded by a linker script, we just want the
+            section contents zeroed.  Avoid any special processing.  */
+         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
+         rel->r_info = 0;
+         rel->r_addend = 0;
+         continue;
+       }
+
+      if (info->relocatable)
+       continue;
+
+      if (h != NULL
+         && (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+         && !BFINFDPIC_SYM_LOCAL (info, h))
+       {
+         osec = sec = NULL;
+         relocation = 0;
+       }
+
+      switch (r_type)
+       {
+       case R_pcrel24:
+       case R_pcrel24_jump_l:
+       case R_byte4_data:
+         if (! IS_FDPIC (output_bfd))
+           goto non_fdpic;
+
+       case R_BFIN_GOT17M4:
+       case R_BFIN_GOTHI:
+       case R_BFIN_GOTLO:
+       case R_BFIN_FUNCDESC_GOT17M4:
+       case R_BFIN_FUNCDESC_GOTHI:
+       case R_BFIN_FUNCDESC_GOTLO:
+       case R_BFIN_GOTOFF17M4:
+       case R_BFIN_GOTOFFHI:
+       case R_BFIN_GOTOFFLO:
+       case R_BFIN_FUNCDESC_GOTOFF17M4:
+       case R_BFIN_FUNCDESC_GOTOFFHI:
+       case R_BFIN_FUNCDESC_GOTOFFLO:
+       case R_BFIN_FUNCDESC:
+       case R_BFIN_FUNCDESC_VALUE:
+         if (h != NULL)
+           picrel = bfinfdpic_relocs_info_for_global (bfinfdpic_relocs_info
+                                                      (info), input_bfd, h,
+                                                      orig_addend, INSERT);
+         else
+           /* In order to find the entry we created before, we must
+              use the original addend, not the one that may have been
+              modified by _bfd_elf_rela_local_sym().  */
+           picrel = bfinfdpic_relocs_info_for_local (bfinfdpic_relocs_info
+                                                     (info), input_bfd, r_symndx,
+                                                     orig_addend, INSERT);
+         if (! picrel)
+           return FALSE;
+
+         if (!_bfinfdpic_emit_got_relocs_plt_entries (picrel, output_bfd, info,
+                                                      osec, sym,
+                                                      rel->r_addend))
+           {
+             (*_bfd_error_handler)
+               (_("%B: relocation at `%A+0x%x' references symbol `%s' with nonzero addend"),
+                input_bfd, input_section, rel->r_offset, name);
+             return FALSE;
+
+           }
 
-      if (r_symndx < symtab_hdr->sh_info)
-       {
-         sym = local_syms + r_symndx;
-         sec = local_sections[r_symndx];
-         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
-       }
-      else
-       {
-         bfd_boolean warned;
+         break;
 
-         RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
-                                  r_symndx, symtab_hdr, sym_hashes,
-                                  h, sec, relocation,
-                                  unresolved_reloc, warned);
+       default:
+       non_fdpic:
+         picrel = NULL;
+         if (h && ! BFINFDPIC_SYM_LOCAL (info, h))
+           {
+             info->callbacks->warning
+               (info, _("relocation references symbol not defined in the module"),
+                name, input_bfd, input_section, rel->r_offset);
+             return FALSE;
+           }
+         break;
        }
 
-      if (sec != NULL && elf_discarded_section (sec))
+      switch (r_type)
        {
-         /* For relocs against symbols from removed linkonce sections,
-            or sections discarded by a linker script, we just want the
-            section contents zeroed.  Avoid any special processing.  */
-         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
-         rel->r_info = 0;
-         rel->r_addend = 0;
-         continue;
-       }
+       case R_pcrel24:
+       case R_pcrel24_jump_l:
+         check_segment[0] = isec_segment;
+         if (! IS_FDPIC (output_bfd))
+           check_segment[1] = isec_segment;
+         else if (picrel->plt)
+           {
+             relocation = bfinfdpic_plt_section (info)->output_section->vma
+               + bfinfdpic_plt_section (info)->output_offset
+               + picrel->plt_entry;
+             check_segment[1] = plt_segment;
+           }
+         /* We don't want to warn on calls to undefined weak symbols,
+            as calls to them must be protected by non-NULL tests
+            anyway, and unprotected calls would invoke undefined
+            behavior.  */
+         else if (picrel->symndx == -1
+                  && picrel->d.h->root.type == bfd_link_hash_undefweak)
+           check_segment[1] = check_segment[0];
+         else
+           check_segment[1] = sec
+             ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section)
+             : (unsigned)-1;
+         break;
 
-      if (info->relocatable)
-       continue;
+       case R_BFIN_GOT17M4:
+       case R_BFIN_GOTHI:
+       case R_BFIN_GOTLO:
+         relocation = picrel->got_entry;
+         check_segment[0] = check_segment[1] = got_segment;
+         break;
 
-      address = rel->r_offset;
+       case R_BFIN_FUNCDESC_GOT17M4:
+       case R_BFIN_FUNCDESC_GOTHI:
+       case R_BFIN_FUNCDESC_GOTLO:
+         relocation = picrel->fdgot_entry;
+         check_segment[0] = check_segment[1] = got_segment;
+         break;
 
-      /* Then, process normally.  */
-      switch (r_type)
-       {
-       case R_BFIN_GNU_VTINHERIT:
-       case R_BFIN_GNU_VTENTRY:
-         return bfd_reloc_ok;
+       case R_BFIN_GOTOFFHI:
+       case R_BFIN_GOTOFF17M4:
+       case R_BFIN_GOTOFFLO:
+         relocation -= bfinfdpic_got_section (info)->output_section->vma
+           + bfinfdpic_got_section (info)->output_offset
+           + bfinfdpic_got_initial_offset (info);
+         check_segment[0] = got_segment;
+         check_segment[1] = sec
+           ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section)
+           : (unsigned)-1;
+         break;
 
-       case R_got:
-         /* Relocation is to the address of the entry for this symbol
-            in the global offset table.  */
-         if (h != NULL
-             && strcmp (h->root.root.string, "__GLOBAL_OFFSET_TABLE_") == 0)
-           goto do_default;
-         /* Fall through.  */
-         /* Relocation is the offset of the entry for this symbol in
-            the global offset table.  */
+       case R_BFIN_FUNCDESC_GOTOFF17M4:
+       case R_BFIN_FUNCDESC_GOTOFFHI:
+       case R_BFIN_FUNCDESC_GOTOFFLO:
+         relocation = picrel->fd_entry;
+         check_segment[0] = check_segment[1] = got_segment;
+         break;
 
+       case R_BFIN_FUNCDESC:
          {
-           bfd_vma off;
+           int dynindx;
+           bfd_vma addend = rel->r_addend;
 
-         if (dynobj == NULL)
+           if (! (h && h->root.type == bfd_link_hash_undefweak
+                  && BFINFDPIC_SYM_LOCAL (info, h)))
+             {
+               /* If the symbol is dynamic and there may be dynamic
+                  symbol resolution because we are or are linked with a
+                  shared library, emit a FUNCDESC relocation such that
+                  the dynamic linker will allocate the function
+                  descriptor.  If the symbol needs a non-local function
+                  descriptor but binds locally (e.g., its visibility is
+                  protected, emit a dynamic relocation decayed to
+                  section+offset.  */
+               if (h && ! BFINFDPIC_FUNCDESC_LOCAL (info, h)
+                   && BFINFDPIC_SYM_LOCAL (info, h)
+                   && !(info->executable && !info->pie))
+                 {
+                   dynindx = elf_section_data (h->root.u.def.section
+                                               ->output_section)->dynindx;
+                   addend += h->root.u.def.section->output_offset
+                     + h->root.u.def.value;
+                 }
+               else if (h && ! BFINFDPIC_FUNCDESC_LOCAL (info, h))
+                 {
+                   if (addend)
+                     {
+                       info->callbacks->warning
+                         (info, _("R_BFIN_FUNCDESC references dynamic symbol with nonzero addend"),
+                          name, input_bfd, input_section, rel->r_offset);
+                       return FALSE;
+                     }
+                   dynindx = h->dynindx;
+                 }
+               else
+                 {
+                   /* Otherwise, we know we have a private function
+                      descriptor, so reference it directly.  */
+                   BFD_ASSERT (picrel->privfd);
+                   r_type = R_byte4_data;
+                   dynindx = elf_section_data (bfinfdpic_got_section (info)
+                                               ->output_section)->dynindx;
+                   addend = bfinfdpic_got_section (info)->output_offset
+                     + bfinfdpic_got_initial_offset (info)
+                     + picrel->fd_entry;
+                 }
+
+               /* If there is room for dynamic symbol resolution, emit
+                  the dynamic relocation.  However, if we're linking an
+                  executable at a fixed location, we won't have emitted a
+                  dynamic symbol entry for the got section, so idx will
+                  be zero, which means we can and should compute the
+                  address of the private descriptor ourselves.  */
+               if (info->executable && !info->pie
+                   && (!h || BFINFDPIC_FUNCDESC_LOCAL (info, h)))
+                 {
+                   addend += bfinfdpic_got_section (info)->output_section->vma;
+                   if ((bfd_get_section_flags (output_bfd,
+                                               input_section->output_section)
+                        & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
+                     {
+                       if (_bfinfdpic_osec_readonly_p (output_bfd,
+                                                      input_section
+                                                      ->output_section))
+                         {
+                           info->callbacks->warning
+                             (info,
+                              _("cannot emit fixups in read-only section"),
+                              name, input_bfd, input_section, rel->r_offset);
+                           return FALSE;
+                         }
+                       _bfinfdpic_add_rofixup (output_bfd,
+                                              bfinfdpic_gotfixup_section
+                                              (info),
+                                              _bfd_elf_section_offset
+                                              (output_bfd, info,
+                                               input_section, rel->r_offset)
+                                              + input_section
+                                              ->output_section->vma
+                                              + input_section->output_offset,
+                                              picrel);
+                     }
+                 }
+               else if ((bfd_get_section_flags (output_bfd,
+                                                input_section->output_section)
+                         & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
+                 {
+                   bfd_vma offset;
+
+                   if (_bfinfdpic_osec_readonly_p (output_bfd,
+                                                  input_section
+                                                  ->output_section))
+                     {
+                       info->callbacks->warning
+                         (info,
+                          _("cannot emit dynamic relocations in read-only section"),
+                          name, input_bfd, input_section, rel->r_offset);
+                       return FALSE;
+                     }
+                   offset = _bfd_elf_section_offset (output_bfd, info,
+                                                     input_section, rel->r_offset);
+                   /* Only output a reloc for a not deleted entry.  */
+                   if (offset >= (bfd_vma) -2)
+                     _bfinfdpic_add_dyn_reloc (output_bfd,
+                                               bfinfdpic_gotrel_section (info),
+                                               0,
+                                               R_unused0,
+                                               dynindx, addend, picrel);
+                   else
+                     _bfinfdpic_add_dyn_reloc (output_bfd,
+                                               bfinfdpic_gotrel_section (info),
+                                               offset + input_section
+                                               ->output_section->vma
+                                               + input_section->output_offset,
+                                               r_type,
+                                               dynindx, addend, picrel);
+                 }
+               else
+                 addend += bfinfdpic_got_section (info)->output_section->vma;
+             }
+
+           /* We want the addend in-place because dynamic
+              relocations are REL.  Setting relocation to it should
+              arrange for it to be installed.  */
+           relocation = addend - rel->r_addend;
+         }
+         check_segment[0] = check_segment[1] = got_segment;
+         break;
+
+       case R_byte4_data:
+         if (! IS_FDPIC (output_bfd))
            {
-             /* Create the .got section.  */
-             elf_hash_table (info)->dynobj = dynobj = output_bfd;
-             if (!_bfd_elf_create_got_section (dynobj, info))
-               return FALSE;
+             check_segment[0] = check_segment[1] = -1;
+             break;
            }
+         /* Fall through.  */
+       case R_BFIN_FUNCDESC_VALUE:
+         {
+           int dynindx;
+           bfd_vma addend = rel->r_addend;
+           bfd_vma offset;
+           offset = _bfd_elf_section_offset (output_bfd, info,
+                                             input_section, rel->r_offset);
 
-           if (sgot == NULL)
+           /* If the symbol is dynamic but binds locally, use
+              section+offset.  */
+           if (h && ! BFINFDPIC_SYM_LOCAL (info, h))
              {
-               sgot = bfd_get_section_by_name (dynobj, ".got");
-               BFD_ASSERT (sgot != NULL);
+               if (addend && r_type == R_BFIN_FUNCDESC_VALUE)
+                 {
+                   info->callbacks->warning
+                     (info, _("R_BFIN_FUNCDESC_VALUE references dynamic symbol with nonzero addend"),
+                      name, input_bfd, input_section, rel->r_offset);
+                   return FALSE;
+                 }
+               dynindx = h->dynindx;
              }
-
-           if (h != NULL)
+           else
              {
-               bfd_boolean dyn;
-
-               off = h->got.offset;
-               BFD_ASSERT (off != (bfd_vma) - 1);
-               dyn = elf_hash_table (info)->dynamic_sections_created;
+               if (h)
+                 addend += h->root.u.def.value;
+               else
+                 addend += sym->st_value;
+               if (osec)
+                 addend += osec->output_offset;
+               if (osec && osec->output_section
+                   && ! bfd_is_abs_section (osec->output_section)
+                   && ! bfd_is_und_section (osec->output_section))
+                 dynindx = elf_section_data (osec->output_section)->dynindx;
+               else
+                 dynindx = 0;
+             }
 
-               if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
-                   || (info->shared
-                       && (info->symbolic
-                           || h->dynindx == -1
-                           || h->forced_local)
-                       && h->def_regular))
+           /* If we're linking an executable at a fixed address, we
+              can omit the dynamic relocation as long as the symbol
+              is defined in the current link unit (which is implied
+              by its output section not being NULL).  */
+           if (info->executable && !info->pie
+               && (!h || BFINFDPIC_SYM_LOCAL (info, h)))
+             {
+               if (osec)
+                 addend += osec->output_section->vma;
+               if (IS_FDPIC (input_bfd)
+                   && (bfd_get_section_flags (output_bfd,
+                                              input_section->output_section)
+                       & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
                  {
-                   /* This is actually a static link, or it is a
-                      -Bsymbolic link and the symbol is defined
-                      locally, or the symbol was forced to be local
-                      because of a version file..  We must initialize
-                      this entry in the global offset table.  Since
-                      the offset must always be a multiple of 4, we
-                      use the least significant bit to record whether
-                      we have initialized it already.
-
-                      When doing a dynamic link, we create a .rela.got
-                      relocation entry to initialize the value.  This
-                      is done in the finish_dynamic_symbol routine.  */
-                   if ((off & 1) != 0)
-                     off &= ~1;
-                   else
+                   if (_bfinfdpic_osec_readonly_p (output_bfd,
+                                                  input_section
+                                                  ->output_section))
                      {
-                       bfd_put_32 (output_bfd, relocation,
-                                   sgot->contents + off);
-                       h->got.offset |= 1;
+                       info->callbacks->warning
+                         (info,
+                          _("cannot emit fixups in read-only section"),
+                          name, input_bfd, input_section, rel->r_offset);
+                       return FALSE;
+                     }
+                   if (!h || h->root.type != bfd_link_hash_undefweak)
+                     {
+                       /* Only output a reloc for a not deleted entry.  */
+                       if (offset >= (bfd_vma)-2)
+                         _bfinfdpic_add_rofixup (output_bfd,
+                                                 bfinfdpic_gotfixup_section
+                                                 (info), -1, picrel);
+                       else
+                         _bfinfdpic_add_rofixup (output_bfd,
+                                                 bfinfdpic_gotfixup_section
+                                                 (info),
+                                                 offset + input_section
+                                                 ->output_section->vma
+                                                 + input_section->output_offset,
+                                                 picrel);
+
+                       if (r_type == R_BFIN_FUNCDESC_VALUE)
+                         {
+                           if (offset >= (bfd_vma)-2)
+                             _bfinfdpic_add_rofixup
+                               (output_bfd,
+                                bfinfdpic_gotfixup_section (info),
+                                -1, picrel);
+                           else
+                             _bfinfdpic_add_rofixup
+                               (output_bfd,
+                                bfinfdpic_gotfixup_section (info),
+                                offset + input_section->output_section->vma
+                                + input_section->output_offset + 4, picrel);
+                         }
                      }
                  }
-               else
-                 unresolved_reloc = FALSE;
              }
            else
              {
-               BFD_ASSERT (local_got_offsets != NULL);
-               off = local_got_offsets[r_symndx];
-               BFD_ASSERT (off != (bfd_vma) - 1);
-
-               /* The offset must always be a multiple of 4.  We use
-                  the least significant bit to record whether we have
-                  already generated the necessary reloc.  */
-               if ((off & 1) != 0)
-                 off &= ~1;
-               else
+               if ((bfd_get_section_flags (output_bfd,
+                                           input_section->output_section)
+                    & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
                  {
-                   bfd_put_32 (output_bfd, relocation, sgot->contents + off);
-
-                   if (info->shared)
+                   if (_bfinfdpic_osec_readonly_p (output_bfd,
+                                                  input_section
+                                                  ->output_section))
                      {
-                       asection *s;
-                       Elf_Internal_Rela outrel;
-                       bfd_byte *loc;
-
-                       s = bfd_get_section_by_name (dynobj, ".rela.got");
-                       BFD_ASSERT (s != NULL);
-
-                       outrel.r_offset = (sgot->output_section->vma
-                                          + sgot->output_offset + off);
-                       outrel.r_info =
-                         ELF32_R_INFO (0, R_pcrel24);
-                       outrel.r_addend = relocation;
-                       loc = s->contents;
-                       loc +=
-                         s->reloc_count++ * sizeof (Elf32_External_Rela);
-                       bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+                       info->callbacks->warning
+                         (info,
+                          _("cannot emit dynamic relocations in read-only section"),
+                          name, input_bfd, input_section, rel->r_offset);
+                       return FALSE;
                      }
-
-                   local_got_offsets[r_symndx] |= 1;
+                   /* Only output a reloc for a not deleted entry.  */
+                   if (offset >= (bfd_vma)-2)
+                     _bfinfdpic_add_dyn_reloc (output_bfd,
+                                               bfinfdpic_gotrel_section (info),
+                                               0, R_unused0, dynindx, addend, picrel);
+                   else
+                     _bfinfdpic_add_dyn_reloc (output_bfd,
+                                               bfinfdpic_gotrel_section (info),
+                                               offset
+                                               + input_section
+                                               ->output_section->vma
+                                               + input_section->output_offset,
+                                               r_type, dynindx, addend, picrel);
                  }
+               else if (osec)
+                 addend += osec->output_section->vma;
+               /* We want the addend in-place because dynamic
+                  relocations are REL.  Setting relocation to it
+                  should arrange for it to be installed.  */
+               relocation = addend - rel->r_addend;
+             }
+
+           if (r_type == R_BFIN_FUNCDESC_VALUE && offset < (bfd_vma)-2)
+             {
+               /* If we've omitted the dynamic relocation, just emit
+                  the fixed addresses of the symbol and of the local
+                  GOT base offset.  */
+               if (info->executable && !info->pie
+                   && (!h || BFINFDPIC_SYM_LOCAL (info, h)))
+                 bfd_put_32 (output_bfd,
+                             bfinfdpic_got_section (info)->output_section->vma
+                             + bfinfdpic_got_section (info)->output_offset
+                             + bfinfdpic_got_initial_offset (info),
+                             contents + rel->r_offset + 4);
+               else
+                 /* A function descriptor used for lazy or local
+                    resolving is initialized such that its high word
+                    contains the output section index in which the
+                    PLT entries are located, and the low word
+                    contains the offset of the lazy PLT entry entry
+                    point into that section.  */
+                 bfd_put_32 (output_bfd,
+                             h && ! BFINFDPIC_SYM_LOCAL (info, h)
+                             ? 0
+                             : _bfinfdpic_osec_to_segment (output_bfd,
+                                                           sec
+                                                           ->output_section),
+                             contents + rel->r_offset + 4);
              }
+         }
+         check_segment[0] = check_segment[1] = got_segment;
+         break;
+
+       default:
+         check_segment[0] = isec_segment;
+         check_segment[1] = sec
+           ? _bfinfdpic_osec_to_segment (output_bfd, sec->output_section)
+           : (unsigned)-1;
+         break;
+       }
+
+      if (check_segment[0] != check_segment[1] && IS_FDPIC (output_bfd))
+       {
+#if 1 /* If you take this out, remove the #error from fdpic-static-6.d
+        in the ld testsuite.  */
+         /* This helps catch problems in GCC while we can't do more
+            than static linking.  The idea is to test whether the
+            input file basename is crt0.o only once.  */
+         if (silence_segment_error == 1)
+           silence_segment_error =
+             (strlen (input_bfd->filename) == 6
+              && strcmp (input_bfd->filename, "crt0.o") == 0)
+             || (strlen (input_bfd->filename) > 6
+                 && strcmp (input_bfd->filename
+                            + strlen (input_bfd->filename) - 7,
+                            "/crt0.o") == 0)
+             ? -1 : 0;
+#endif
+         if (!silence_segment_error
+             /* We don't want duplicate errors for undefined
+                symbols.  */
+             && !(picrel && picrel->symndx == -1
+                  && picrel->d.h->root.type == bfd_link_hash_undefined))
+           info->callbacks->warning
+             (info,
+              (info->shared || info->pie)
+              ? _("relocations between different segments are not supported")
+              : _("warning: relocation references a different segment"),
+              name, input_bfd, input_section, rel->r_offset);
+         if (!silence_segment_error && (info->shared || info->pie))
+           return FALSE;
+         elf_elfheader (output_bfd)->e_flags |= EF_BFIN_PIC;
+       }
+
+      switch (r_type)
+       {
+       case R_BFIN_GOTOFFHI:
+         /* We need the addend to be applied before we shift the
+            value right.  */
+         relocation += rel->r_addend;
+         /* Fall through.  */
+       case R_BFIN_GOTHI:
+       case R_BFIN_FUNCDESC_GOTHI:
+       case R_BFIN_FUNCDESC_GOTOFFHI:
+         relocation >>= 16;
+         /* Fall through.  */
 
-           relocation = sgot->output_offset + off;
-           rel->r_addend = 0;
-            /* bfin : preg = [preg + 17bitdiv4offset] relocation is div by 4.  */
-            relocation /= 4;
-         }
-         goto do_default;
+       case R_BFIN_GOTLO:
+       case R_BFIN_FUNCDESC_GOTLO:
+       case R_BFIN_GOTOFFLO:
+       case R_BFIN_FUNCDESC_GOTOFFLO:
+         relocation &= 0xffff;
+         break;
 
        default:
-       do_default:
-         r = bfin_final_link_relocate (rel, howto, input_bfd, input_section,
-                                       contents, address,
-                                       relocation, rel->r_addend);
-
          break;
        }
 
-      /* Dynamic relocs are not propagated for SEC_DEBUGGING sections
-         because such sections are not SEC_ALLOC and thus ld.so will
-         not process them.  */
-      if (unresolved_reloc
-         && !((input_section->flags & SEC_DEBUGGING) != 0 && h->def_dynamic))
+      switch (r_type)
        {
-         (*_bfd_error_handler)
-           (_("%B(%A+0x%lx): unresolvable relocation against symbol `%s'"),
-            input_bfd,
-            input_section, (long) rel->r_offset, h->root.root.string);
-         return FALSE;
+       case R_pcrel24:
+       case R_pcrel24_jump_l:
+         if (! IS_FDPIC (output_bfd) || ! picrel->plt)
+           break;
+         /* Fall through.  */
+
+         /* When referencing a GOT entry, a function descriptor or a
+            PLT, we don't want the addend to apply to the reference,
+            but rather to the referenced symbol.  The actual entry
+            will have already been created taking the addend into
+            account, so cancel it out here.  */
+       case R_BFIN_GOT17M4:
+       case R_BFIN_GOTHI:
+       case R_BFIN_GOTLO:
+       case R_BFIN_FUNCDESC_GOT17M4:
+       case R_BFIN_FUNCDESC_GOTHI:
+       case R_BFIN_FUNCDESC_GOTLO:
+       case R_BFIN_FUNCDESC_GOTOFF17M4:
+       case R_BFIN_FUNCDESC_GOTOFFHI:
+       case R_BFIN_FUNCDESC_GOTOFFLO:
+         /* Note that we only want GOTOFFHI, not GOTOFFLO or GOTOFF17M4
+            here, since we do want to apply the addend to the others.
+            Note that we've applied the addend to GOTOFFHI before we
+            shifted it right.  */
+       case R_BFIN_GOTOFFHI:
+         relocation -= rel->r_addend;
+         break;
+
+       default:
+         break;
        }
 
+      r = bfin_final_link_relocate (rel, howto, input_bfd, input_section,
+                                   contents, rel->r_offset,
+                                   relocation, rel->r_addend);
+
       if (r != bfd_reloc_ok)
        {
-         const char *name;
+         const char * msg = (const char *) NULL;
 
-         if (h != NULL)
-           name = h->root.root.string;
-         else
+         switch (r)
            {
-             name = bfd_elf_string_from_elf_section (input_bfd,
-                                                     symtab_hdr->sh_link,
-                                                     sym->st_name);
-             if (name == NULL)
-               return FALSE;
-             if (*name == '\0')
-               name = bfd_section_name (input_bfd, sec);
-           }
+           case bfd_reloc_overflow:
+             r = info->callbacks->reloc_overflow
+               (info, (h ? &h->root : NULL), name, howto->name,
+                (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
+             break;
 
-         if (r == bfd_reloc_overflow)
-           {
-             if (!(info->callbacks->reloc_overflow
-                   (info, (h ? &h->root : NULL), name, howto->name,
-                    (bfd_vma) 0, input_bfd, input_section, rel->r_offset)))
-               return FALSE;
-           }
-         else
-           {
-             (*_bfd_error_handler)
-               (_("%B(%A+0x%lx): reloc against `%s': error %d"),
-                input_bfd, input_section,
-                (long) rel->r_offset, name, (int) r);
-             return FALSE;
+           case bfd_reloc_undefined:
+             r = info->callbacks->undefined_symbol
+               (info, name, input_bfd, input_section, rel->r_offset, TRUE);
+             break;
+
+           case bfd_reloc_outofrange:
+             msg = _("internal error: out of range error");
+             break;
+
+           case bfd_reloc_notsupported:
+             msg = _("internal error: unsupported relocation error");
+             break;
+
+           case bfd_reloc_dangerous:
+             msg = _("internal error: dangerous relocation");
+             break;
+
+           default:
+             msg = _("internal error: unknown error");
+             break;
            }
+
+         if (msg)
+           r = info->callbacks->warning
+             (info, msg, name, input_bfd, input_section, rel->r_offset);
+
+         if (! r)
+           return FALSE;
        }
     }
 
   return TRUE;
 }
 
-static asection *
-bfin_gc_mark_hook (asection * sec,
-                  struct bfd_link_info *info,
-                  Elf_Internal_Rela * rel,
-                  struct elf_link_hash_entry *h,
-                   Elf_Internal_Sym * sym)
-{
-  if (h != NULL)
-    switch (ELF32_R_TYPE (rel->r_info))
-      {
-      case R_BFIN_GNU_VTINHERIT:
-      case R_BFIN_GNU_VTENTRY:
-       return NULL;
-      }
-
-  return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
-}
-
 /* Update the relocation information for the relocations of the section
    being removed.  */
 
@@ -3249,79 +3351,6 @@ bfinfdpic_gc_sweep_hook (bfd *abfd,
   return TRUE;
 }
 
-/* Update the got entry reference counts for the section being removed.  */
-
-static bfd_boolean
-bfin_gc_sweep_hook (bfd * abfd,
-                   struct bfd_link_info *info,
-                   asection * sec,
-                    const Elf_Internal_Rela * relocs)
-{
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes;
-  bfd_signed_vma *local_got_refcounts;
-  const Elf_Internal_Rela *rel, *relend;
-  bfd *dynobj;
-  asection *sgot;
-  asection *srelgot;
-
-  dynobj = elf_hash_table (info)->dynobj;
-  if (dynobj == NULL)
-    return TRUE;
-
-  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-  sym_hashes = elf_sym_hashes (abfd);
-  local_got_refcounts = elf_local_got_refcounts (abfd);
-
-  sgot = bfd_get_section_by_name (dynobj, ".got");
-  srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
-
-  relend = relocs + sec->reloc_count;
-  for (rel = relocs; rel < relend; rel++)
-    {
-      unsigned long r_symndx;
-      struct elf_link_hash_entry *h;
-
-      switch (ELF32_R_TYPE (rel->r_info))
-       {
-       case R_got:
-         r_symndx = ELF32_R_SYM (rel->r_info);
-         if (r_symndx >= symtab_hdr->sh_info)
-           {
-             h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-             if (h->got.refcount > 0)
-               {
-                 --h->got.refcount;
-                 if (h->got.refcount == 0)
-                   {
-                     /* We don't need the .got entry any more.  */
-                     sgot->size -= 4;
-                     srelgot->size -= sizeof (Elf32_External_Rela);
-                   }
-               }
-           }
-         else if (local_got_refcounts != NULL)
-           {
-             if (local_got_refcounts[r_symndx] > 0)
-               {
-                 --local_got_refcounts[r_symndx];
-                 if (local_got_refcounts[r_symndx] == 0)
-                   {
-                     /* We don't need the .got entry any more.  */
-                     sgot->size -= 4;
-                     if (info->shared)
-                       srelgot->size -= sizeof (Elf32_External_Rela);
-                   }
-               }
-           }
-         break;
-       default:
-         break;
-       }
-    }
-  return TRUE;
-}
-
 /* We need dynamic symbols for every section, since segments can
    relocate independently.  */
 static bfd_boolean
@@ -3557,37 +3586,6 @@ elf32_bfinfdpic_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
   return TRUE;
 }
 
-/* The name of the dynamic interpreter.  This is put in the .interp
-   section.  */
-
-#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1"
-
-#define DEFAULT_STACK_SIZE 0x20000
-
-/* This structure is used to collect the number of entries present in
-   each addressable range of the got.  */
-struct _bfinfdpic_dynamic_got_info
-{
-  /* Several bits of information about the current link.  */
-  struct bfd_link_info *info;
-  /* Total size needed for GOT entries within the 18- or 32-bit
-     ranges.  */
-  bfd_vma got17m4, gothilo;
-  /* Total size needed for function descriptor entries within the 18-
-     or 32-bit ranges.  */
-  bfd_vma fd17m4, fdhilo;
-  /* Total size needed function descriptor entries referenced in PLT
-     entries, that would be profitable to place in offsets close to
-     the PIC register.  */
-  bfd_vma fdplt;
-  /* Total size needed by lazy PLT entries.  */
-  bfd_vma lzplt;
-  /* Number of relocations carried over from input object files.  */
-  unsigned long relocs;
-  /* Number of fixups introduced by relocations in input object files.  */
-  unsigned long fixups;
-};
-
 /* Compute the total GOT size required by each symbol in each range.
    Symbols may require up to 4 words in the GOT: an entry pointing to
    the symbol, an entry pointing to its function descriptor, and a