2011-05-09 Paul Brook <paul@codesourcery.com>
[binutils-gdb.git] / bfd / elf32-m68k.c
index 2957db0740bb1a36fa42131da585b752af346bdf..b4f7bc2c94c9346a2b7a3e4fd522fe1e4c41bccf 100644 (file)
@@ -1,6 +1,6 @@
 /* Motorola 68k series support for 32-bit ELF
    Copyright 1993, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
-   2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+   2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -374,13 +374,17 @@ static reloc_howto_type howto_table[] = {
 };
 
 static void
-rtype_to_howto (abfd, cache_ptr, dst)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     arelent *cache_ptr;
-     Elf_Internal_Rela *dst;
+rtype_to_howto (bfd *abfd, arelent *cache_ptr, Elf_Internal_Rela *dst)
 {
-  BFD_ASSERT (ELF32_R_TYPE(dst->r_info) < (unsigned int) R_68K_max);
-  cache_ptr->howto = &howto_table[ELF32_R_TYPE(dst->r_info)];
+  unsigned int indx = ELF32_R_TYPE (dst->r_info);
+
+  if (indx >= (unsigned int) R_68K_max)
+    {
+      (*_bfd_error_handler) (_("%B: invalid relocation type %d"),
+                            abfd, (int) indx);
+      indx = R_68K_NONE;
+    }
+  cache_ptr->howto = &howto_table[indx];
 }
 
 #define elf_info_to_howto rtype_to_howto
@@ -465,6 +469,7 @@ reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
 #define bfd_elf32_bfd_reloc_type_lookup reloc_type_lookup
 #define bfd_elf32_bfd_reloc_name_lookup reloc_name_lookup
 #define ELF_ARCH bfd_arch_m68k
+#define ELF_TARGET_ID M68K_ELF_DATA
 \f
 /* Functions for the m68k ELF linker.  */
 
@@ -933,7 +938,8 @@ struct elf_m68k_link_hash_table
 /* Get the m68k ELF linker hash table from a link_info structure.  */
 
 #define elf_m68k_hash_table(p) \
-  ((struct elf_m68k_link_hash_table *) (p)->hash)
+  (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
+  == M68K_ELF_DATA ? ((struct elf_m68k_link_hash_table *) ((p)->hash)) : NULL)
 
 /* Shortcut to multi-GOT data.  */
 #define elf_m68k_multi_got(INFO) (&elf_m68k_hash_table (INFO)->multi_got_)
@@ -941,10 +947,9 @@ struct elf_m68k_link_hash_table
 /* Create an entry in an m68k ELF linker hash table.  */
 
 static struct bfd_hash_entry *
-elf_m68k_link_hash_newfunc (entry, table, string)
-     struct bfd_hash_entry *entry;
-     struct bfd_hash_table *table;
-     const char *string;
+elf_m68k_link_hash_newfunc (struct bfd_hash_entry *entry,
+                           struct bfd_hash_table *table,
+                           const char *string)
 {
   struct bfd_hash_entry *ret = entry;
 
@@ -971,8 +976,7 @@ elf_m68k_link_hash_newfunc (entry, table, string)
 /* Create an m68k ELF linker hash table.  */
 
 static struct bfd_link_hash_table *
-elf_m68k_link_hash_table_create (abfd)
-     bfd *abfd;
+elf_m68k_link_hash_table_create (bfd *abfd)
 {
   struct elf_m68k_link_hash_table *ret;
   bfd_size_type amt = sizeof (struct elf_m68k_link_hash_table);
@@ -983,7 +987,8 @@ elf_m68k_link_hash_table_create (abfd)
 
   if (!_bfd_elf_link_hash_table_init (&ret->root, abfd,
                                      elf_m68k_link_hash_newfunc,
-                                     sizeof (struct elf_m68k_link_hash_entry)))
+                                     sizeof (struct elf_m68k_link_hash_entry),
+                                     M68K_ELF_DATA))
     {
       free (ret);
       return NULL;
@@ -1076,7 +1081,68 @@ elf32_m68k_object_p (bfd *abfd)
   return TRUE;
 }
 
+/* Somewhat reverse of elf32_m68k_object_p, this sets the e_flag
+   field based on the machine number.  */
+
+static void
+elf_m68k_final_write_processing (bfd *abfd,
+                                bfd_boolean linker ATTRIBUTE_UNUSED)
+{
+  int mach = bfd_get_mach (abfd);
+  unsigned long e_flags = elf_elfheader (abfd)->e_flags;
+
+  if (!e_flags)
+    {
+      unsigned int arch_mask;
+
+      arch_mask = bfd_m68k_mach_to_features (mach);
+
+      if (arch_mask & m68000)
+       e_flags = EF_M68K_M68000;
+      else if (arch_mask & cpu32)
+       e_flags = EF_M68K_CPU32;
+      else if (arch_mask & fido_a)
+       e_flags = EF_M68K_FIDO;
+      else
+       {
+         switch (arch_mask
+                 & (mcfisa_a | mcfisa_aa | mcfisa_b | mcfisa_c | mcfhwdiv | mcfusp))
+           {
+           case mcfisa_a:
+             e_flags |= EF_M68K_CF_ISA_A_NODIV;
+             break;
+           case mcfisa_a | mcfhwdiv:
+             e_flags |= EF_M68K_CF_ISA_A;
+             break;
+           case mcfisa_a | mcfisa_aa | mcfhwdiv | mcfusp:
+             e_flags |= EF_M68K_CF_ISA_A_PLUS;
+             break;
+           case mcfisa_a | mcfisa_b | mcfhwdiv:
+             e_flags |= EF_M68K_CF_ISA_B_NOUSP;
+             break;
+           case mcfisa_a | mcfisa_b | mcfhwdiv | mcfusp:
+             e_flags |= EF_M68K_CF_ISA_B;
+             break;
+           case mcfisa_a | mcfisa_c | mcfhwdiv | mcfusp:
+             e_flags |= EF_M68K_CF_ISA_C;
+             break;
+           case mcfisa_a | mcfisa_c | mcfusp:
+             e_flags |= EF_M68K_CF_ISA_C_NODIV;
+             break;
+           }
+         if (arch_mask & mcfmac)
+           e_flags |= EF_M68K_CF_MAC;
+         else if (arch_mask & mcfemac)
+           e_flags |= EF_M68K_CF_EMAC;
+         if (arch_mask & cfloat)
+           e_flags |= EF_M68K_CF_FLOAT | EF_M68K_CFV4E;
+       }
+      elf_elfheader (abfd)->e_flags = e_flags;
+    }
+}
+
 /* Keep m68k-specific flags in the ELF header.  */
+
 static bfd_boolean
 elf32_m68k_set_private_flags (abfd, flags)
      bfd *abfd;
@@ -1228,6 +1294,9 @@ elf32_m68k_print_private_bfd_data (bfd *abfd, void * ptr)
            case EF_M68K_CF_EMAC:
              mac = "emac";
              break;
+           case EF_M68K_CF_EMAC_B:
+             mac = "emac_b";
+             break;
            }
          if (mac)
            fprintf (file, " [%s]", mac);
@@ -1531,7 +1600,7 @@ elf_m68k_get_got_entry (struct elf_m68k_got *got,
 static enum elf_m68k_reloc_type
 elf_m68k_update_got_entry_type (struct elf_m68k_got *got,
                                enum elf_m68k_reloc_type was,
-                               enum elf_m68k_reloc_type new)
+                               enum elf_m68k_reloc_type new_reloc)
 {
   enum elf_m68k_got_offset_size was_size;
   enum elf_m68k_got_offset_size new_size;
@@ -1543,20 +1612,20 @@ elf_m68k_update_got_entry_type (struct elf_m68k_got *got,
       /* Update all got->n_slots counters, including n_slots[R_32].  */
       was_size = R_LAST;
 
-      was = new;
+      was = new_reloc;
     }
   else
     {
       /* !!! We, probably, should emit an error rather then fail on assert
         in such a case.  */
       BFD_ASSERT (elf_m68k_reloc_got_type (was)
-                 == elf_m68k_reloc_got_type (new));
+                 == elf_m68k_reloc_got_type (new_reloc));
 
       was_size = elf_m68k_reloc_got_offset_size (was);
     }
 
-  new_size = elf_m68k_reloc_got_offset_size (new);
-  n_slots = elf_m68k_reloc_got_n_slots (new);
+  new_size = elf_m68k_reloc_got_offset_size (new_reloc);
+  n_slots = elf_m68k_reloc_got_n_slots (new_reloc);
 
   while (was_size > new_size)
     {
@@ -1564,10 +1633,10 @@ elf_m68k_update_got_entry_type (struct elf_m68k_got *got,
       got->n_slots[was_size] += n_slots;
     }
 
-  if (new > was)
+  if (new_reloc > was)
     /* Relocations are ordered from bigger got offset size to lesser,
        so choose the relocation type with lesser offset size.  */
-    was = new;
+    was = new_reloc;
 
   return was;
 }
@@ -2487,6 +2556,10 @@ elf_m68k_copy_indirect_symbol (struct bfd_link_info *info,
   dir = elf_m68k_hash_entry (_dir);
   ind = elf_m68k_hash_entry (_ind);
 
+  /* Any absolute non-dynamic relocations against an indirect or weak
+     definition will be against the target symbol.  */
+  _dir->non_got_ref |= _ind->non_got_ref;
+
   /* We might have a direct symbol already having entries in the GOTs.
      Update its key only in case indirect symbol has GOT entries and
      assert that both indirect and direct symbols don't have GOT entries
@@ -2581,6 +2654,14 @@ elf_m68k_check_relocs (abfd, info, sec, relocs)
        case R_68K_TLS_IE16:
        case R_68K_TLS_IE32:
 
+       case R_68K_TLS_TPREL32:
+       case R_68K_TLS_DTPREL32:
+
+         if (ELF32_R_TYPE (rel->r_info) == R_68K_TLS_TPREL32
+             && info->shared)
+           /* Do the special chorus for libraries with static TLS.  */
+           info->flags |= DF_STATIC_TLS;
+
          /* This symbol requires a global offset table entry.  */
 
          if (dynobj == NULL)
@@ -2739,6 +2820,10 @@ elf_m68k_check_relocs (abfd, info, sec, relocs)
              /* Make sure a plt entry is created for this symbol if it
                 turns out to be a function defined by a dynamic object.  */
              h->plt.refcount++;
+
+             if (!info->shared)
+               /* This symbol needs a non-GOT reference.  */
+               h->non_got_ref = 1;
            }
 
          /* If we are creating a shared library, we need to copy the
@@ -2887,8 +2972,6 @@ elf_m68k_gc_sweep_hook (bfd *abfd,
   struct elf_link_hash_entry **sym_hashes;
   const Elf_Internal_Rela *rel, *relend;
   bfd *dynobj;
-  asection *sgot;
-  asection *srelgot;
   struct elf_m68k_got *got;
 
   if (info->relocatable)
@@ -2900,9 +2983,6 @@ elf_m68k_gc_sweep_hook (bfd *abfd,
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
-
-  sgot = bfd_get_section_by_name (dynobj, ".got");
-  srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
   got = NULL;
 
   relend = relocs + sec->reloc_count;
@@ -2946,6 +3026,9 @@ elf_m68k_gc_sweep_hook (bfd *abfd,
        case R_68K_TLS_IE16:
        case R_68K_TLS_IE32:
 
+       case R_68K_TLS_TPREL32:
+       case R_68K_TLS_DTPREL32:
+
          if (got == NULL)
            {
              got = elf_m68k_get_bfd2got_entry (elf_m68k_multi_got (info),
@@ -3159,6 +3242,11 @@ elf_m68k_adjust_dynamic_symbol (info, h)
   if (info->shared)
     return TRUE;
 
+  /* If there are no references to this symbol that do not use the
+     GOT, we don't need to generate a copy reloc.  */
+  if (!h->non_got_ref)
+    return TRUE;
+
   if (h->size == 0)
     {
       (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"),
@@ -3422,9 +3510,11 @@ elf_m68k_install_rela (bfd *output_bfd,
   bfd_elf32_swap_reloca_out (output_bfd, rela, loc);
 }
 
-/* Return the base VMA address which should be subtracted from real addresses
-   when resolving @dtpoff relocation.
-   This is PT_TLS segment p_vaddr.  */
+/* Find the base offsets for thread-local storage in this object,
+   for GD/LD and IE/LE respectively.  */
+
+#define DTP_OFFSET 0x8000
+#define TP_OFFSET  0x7000
 
 static bfd_vma
 dtpoff_base (struct bfd_link_info *info)
@@ -3432,23 +3522,116 @@ dtpoff_base (struct bfd_link_info *info)
   /* If tls_sec is NULL, we should have signalled an error already.  */
   if (elf_hash_table (info)->tls_sec == NULL)
     return 0;
-  return elf_hash_table (info)->tls_sec->vma;
+  return elf_hash_table (info)->tls_sec->vma + DTP_OFFSET;
 }
 
-/* Return the relocation value for @tpoff relocation
-   if STT_TLS virtual address is ADDRESS.  */
-
 static bfd_vma
-tpoff (struct bfd_link_info *info, bfd_vma address)
+tpoff_base (struct bfd_link_info *info)
 {
-  struct elf_link_hash_table *htab = elf_hash_table (info);
-  bfd_vma base;
-
   /* If tls_sec is NULL, we should have signalled an error already.  */
-  if (htab->tls_sec == NULL)
+  if (elf_hash_table (info)->tls_sec == NULL)
     return 0;
-  base = align_power ((bfd_vma) 8, htab->tls_sec->alignment_power);
-  return address - htab->tls_sec->vma + base;
+  return elf_hash_table (info)->tls_sec->vma + TP_OFFSET;
+}
+
+/* Output necessary relocation to handle a symbol during static link.
+   This function is called from elf_m68k_relocate_section.  */
+
+static void
+elf_m68k_init_got_entry_static (struct bfd_link_info *info,
+                               bfd *output_bfd,
+                               enum elf_m68k_reloc_type r_type,
+                               asection *sgot,
+                               bfd_vma got_entry_offset,
+                               bfd_vma relocation)
+{
+  switch (elf_m68k_reloc_got_type (r_type))
+    {
+    case R_68K_GOT32O:
+      bfd_put_32 (output_bfd, relocation, sgot->contents + got_entry_offset);
+      break;
+
+    case R_68K_TLS_GD32:
+      /* We know the offset within the module,
+        put it into the second GOT slot.  */
+      bfd_put_32 (output_bfd, relocation - dtpoff_base (info),
+                 sgot->contents + got_entry_offset + 4);
+      /* FALLTHRU */
+
+    case R_68K_TLS_LDM32:
+      /* Mark it as belonging to module 1, the executable.  */
+      bfd_put_32 (output_bfd, 1, sgot->contents + got_entry_offset);
+      break;
+
+    case R_68K_TLS_IE32:
+      bfd_put_32 (output_bfd, relocation - tpoff_base (info),
+                 sgot->contents + got_entry_offset);
+      break;
+
+    default:
+      BFD_ASSERT (FALSE);
+    }
+}
+
+/* Output necessary relocation to handle a local symbol
+   during dynamic link.
+   This function is called either from elf_m68k_relocate_section
+   or from elf_m68k_finish_dynamic_symbol.  */
+
+static void
+elf_m68k_init_got_entry_local_shared (struct bfd_link_info *info,
+                                     bfd *output_bfd,
+                                     enum elf_m68k_reloc_type r_type,
+                                     asection *sgot,
+                                     bfd_vma got_entry_offset,
+                                     bfd_vma relocation,
+                                     asection *srela)
+{
+  Elf_Internal_Rela outrel;
+
+  switch (elf_m68k_reloc_got_type (r_type))
+    {
+    case R_68K_GOT32O:
+      /* Emit RELATIVE relocation to initialize GOT slot
+        at run-time.  */
+      outrel.r_info = ELF32_R_INFO (0, R_68K_RELATIVE);
+      outrel.r_addend = relocation;
+      break;
+
+    case R_68K_TLS_GD32:
+      /* We know the offset within the module,
+        put it into the second GOT slot.  */
+      bfd_put_32 (output_bfd, relocation - dtpoff_base (info),
+                 sgot->contents + got_entry_offset + 4);
+      /* FALLTHRU */
+
+    case R_68K_TLS_LDM32:
+      /* We don't know the module number,
+        create a relocation for it.  */
+      outrel.r_info = ELF32_R_INFO (0, R_68K_TLS_DTPMOD32);
+      outrel.r_addend = 0;
+      break;
+
+    case R_68K_TLS_IE32:
+      /* Emit TPREL relocation to initialize GOT slot
+        at run-time.  */
+      outrel.r_info = ELF32_R_INFO (0, R_68K_TLS_TPREL32);
+      outrel.r_addend = relocation - elf_hash_table (info)->tls_sec->vma;
+      break;
+
+    default:
+      BFD_ASSERT (FALSE);
+    }
+
+  /* Offset of the GOT entry.  */
+  outrel.r_offset = (sgot->output_section->vma
+                    + sgot->output_offset
+                    + got_entry_offset);
+
+  /* Install one of the above relocations.  */
+  elf_m68k_install_rela (output_bfd, srela, &outrel);
+
+  bfd_put_32 (output_bfd, outrel.r_addend, sgot->contents + got_entry_offset);
 }
 
 /* Relocate an M68K ELF section.  */
@@ -3471,6 +3654,7 @@ elf_m68k_relocate_section (output_bfd, info, input_bfd, input_section,
   asection *sgot;
   asection *splt;
   asection *sreloc;
+  asection *srela;
   struct elf_m68k_got *got;
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
@@ -3482,6 +3666,7 @@ elf_m68k_relocate_section (output_bfd, info, input_bfd, input_section,
   sgot = NULL;
   splt = NULL;
   sreloc = NULL;
+  srela = NULL;
 
   got = NULL;
 
@@ -3531,15 +3716,8 @@ elf_m68k_relocate_section (output_bfd, info, input_bfd, input_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;
-       }
+       RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+                                        rel, relend, howto, contents);
 
       if (info->relocatable)
        continue;
@@ -3680,7 +3858,7 @@ elf_m68k_relocate_section (output_bfd, info, input_bfd, input_section,
                        /* 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
+                          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
@@ -3690,26 +3868,12 @@ elf_m68k_relocate_section (output_bfd, info, input_bfd, input_section,
                           relocation entry to initialize the value.  This
                           is done in the finish_dynamic_symbol routine.  */
 
-                       if (elf_m68k_reloc_got_type (r_type) == R_68K_GOT32O)
-                         bfd_put_32 (output_bfd, relocation,
-                                     sgot->contents + off);
-                       else if (elf_m68k_reloc_got_type (r_type)
-                                == R_68K_TLS_GD32)
-                         /* Mark it as belonging to module 1,
-                            the executable.  */
-                         {
-                           bfd_put_32 (output_bfd, 1,
-                                       sgot->contents + off);
-                           bfd_put_32 (output_bfd, (relocation
-                                                    - dtpoff_base (info)),
-                                       sgot->contents + off + 4);
-                         }
-                       else if (elf_m68k_reloc_got_type (r_type)
-                                == R_68K_TLS_IE32)
-                         bfd_put_32 (output_bfd, tpoff (info, relocation),
-                                     sgot->contents + off);
-                       else
-                         BFD_ASSERT (FALSE);
+                       elf_m68k_init_got_entry_static (info,
+                                                       output_bfd,
+                                                       r_type,
+                                                       sgot,
+                                                       off,
+                                                       relocation);
 
                        *off_ptr |= 1;
                      }
@@ -3717,103 +3881,32 @@ elf_m68k_relocate_section (output_bfd, info, input_bfd, input_section,
                      unresolved_reloc = FALSE;
                  }
                else if (info->shared) /* && h == NULL */
+                 /* Process local symbol during dynamic link.  */
                  {
-                   asection *srela;
-                   Elf_Internal_Rela outrel;
-
-                   srela = bfd_get_section_by_name (dynobj, ".rela.got");
-                   BFD_ASSERT (srela != NULL);
-
-                   if (elf_m68k_reloc_got_type (r_type) == R_68K_GOT32O)
-                     {
-                       /* Emit RELATIVE relocation to initialize GOT slot
-                          at run-time.  */
-                       outrel.r_info = ELF32_R_INFO (0, R_68K_RELATIVE);
-                       outrel.r_addend = relocation;
-                       outrel.r_offset = (sgot->output_section->vma
-                                          + sgot->output_offset
-                                          + off);
-
-                       elf_m68k_install_rela (output_bfd, srela, &outrel);
-                     }
-                   else if (elf_m68k_reloc_got_type (r_type)
-                            == R_68K_TLS_LDM32)
+                   if (srela == NULL)
                      {
-                       /* If we don't know the module number, create
-                          a relocation for it.  */
-                       outrel.r_info = ELF32_R_INFO (0, R_68K_TLS_DTPMOD32);
-                       outrel.r_addend = 0;
-                       outrel.r_offset = (sgot->output_section->vma
-                                          + sgot->output_offset
-                                          + off);
-
-                       elf_m68k_install_rela (output_bfd, srela, &outrel);
+                       srela = bfd_get_section_by_name (dynobj, ".rela.got");
+                       BFD_ASSERT (srela != NULL);
                      }
-                   else if (elf_m68k_reloc_got_type (r_type)
-                            == R_68K_TLS_GD32)
-                     {
-                       /* If we don't know the module number, create
-                          a relocation for it.  */
-                       outrel.r_info = ELF32_R_INFO (0, R_68K_TLS_DTPMOD32);
-                       outrel.r_addend = 0;
-                       outrel.r_offset = (sgot->output_section->vma
-                                          + sgot->output_offset
-                                          + off);
-
-                       elf_m68k_install_rela (output_bfd, srela, &outrel);
-
-                       bfd_put_32 (output_bfd, (relocation
-                                                - dtpoff_base (info)),
-                                   sgot->contents + off + 4);
-                     }
-                   else if (elf_m68k_reloc_got_type (r_type)
-                            == R_68K_TLS_IE32)
-                     {
-                       outrel.r_info = ELF32_R_INFO (0, R_68K_TLS_TPREL32);
-                       outrel.r_addend = relocation - dtpoff_base (info);
-                       outrel.r_offset = (sgot->output_section->vma
-                                          + sgot->output_offset
-                                          + off);
 
-                       elf_m68k_install_rela (output_bfd, srela, &outrel);
-                     }
-                   else
-                     BFD_ASSERT (FALSE);
-
-                   bfd_put_32 (output_bfd, outrel.r_addend,
-                               sgot->contents + off);
+                   elf_m68k_init_got_entry_local_shared (info,
+                                                         output_bfd,
+                                                         r_type,
+                                                         sgot,
+                                                         off,
+                                                         relocation,
+                                                         srela);
 
                    *off_ptr |= 1;
                  }
                else /* h == NULL && !info->shared */
                  {
-                   if (elf_m68k_reloc_got_type (r_type) == R_68K_GOT32O)
-                     bfd_put_32 (output_bfd, relocation,
-                                 sgot->contents + off);
-                   else if (elf_m68k_reloc_got_type (r_type)
-                            == R_68K_TLS_LDM32)
-                     /* If this is a static link, put the number of the
-                        only module in the GOT slot.  */
-                     bfd_put_32 (output_bfd, 1, sgot->contents + off);
-                   else if (elf_m68k_reloc_got_type (r_type)
-                            == R_68K_TLS_GD32)
-                     {
-                       /* If we are not emitting relocations for a
-                          general dynamic reference, then we must be in a
-                          static link or an executable link with the
-                          symbol binding locally.  Mark it as belonging
-                          to module 1, the executable.  */
-                       bfd_put_32 (output_bfd, 1, sgot->contents + off);
-                       bfd_put_32 (output_bfd, (relocation
-                                                - dtpoff_base (info)),
-                                   sgot->contents + off + 4);
-                     }
-                   else if (elf_m68k_reloc_got_type (r_type)
-                            == R_68K_TLS_IE32)
-                     bfd_put_32 (output_bfd, tpoff (info, relocation),
-                                 sgot->contents + off);
-                   else
-                     BFD_ASSERT (FALSE);
+                   elf_m68k_init_got_entry_static (info,
+                                                   output_bfd,
+                                                   r_type,
+                                                   sgot,
+                                                   off,
+                                                   relocation);
 
                    *off_ptr |= 1;
                  }
@@ -3843,7 +3936,6 @@ elf_m68k_relocate_section (output_bfd, info, input_bfd, input_section,
                  }
 
                /* This relocation does not use the addend.  */
-               BFD_ASSERT (rel->r_addend == 0);
                rel->r_addend = 0;
              }
            else
@@ -3871,7 +3963,7 @@ elf_m68k_relocate_section (output_bfd, info, input_bfd, input_section,
              return FALSE;
            }
          else
-           relocation = tpoff (info, relocation);
+           relocation -= tpoff_base (info);
 
          break;
 
@@ -3935,7 +4027,7 @@ elf_m68k_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_68K_PC16:
        case R_68K_PC32:
          if (info->shared
-             && r_symndx != 0
+             && r_symndx != STN_UNDEF
              && (input_section->flags & SEC_ALLOC) != 0
              && (h == NULL
                  || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
@@ -4070,7 +4162,7 @@ elf_m68k_relocate_section (output_bfd, info, input_bfd, input_section,
          return FALSE;
        }
 
-      if (r_symndx != 0
+      if (r_symndx != STN_UNDEF
          && r_type != R_68K_NONE
          && (h == NULL
              || h->root.type == bfd_link_hash_defined
@@ -4270,15 +4362,12 @@ elf_m68k_finish_dynamic_symbol (output_bfd, info, h, sym)
 
       while (got_entry != NULL)
        {
-         Elf_Internal_Rela rela;
+         enum elf_m68k_reloc_type r_type;
          bfd_vma got_entry_offset;
 
+         r_type = got_entry->key_.type;
          got_entry_offset = got_entry->u.s2.offset &~ (bfd_vma) 1;
 
-         rela.r_offset = (sgot->output_section->vma
-                          + sgot->output_offset
-                          + got_entry_offset);
-
          /* If this is a -Bsymbolic link, and the symbol is defined
             locally, we just want to emit a RELATIVE reloc.  Likewise if
             the symbol was forced to be local because of a version file.
@@ -4287,33 +4376,48 @@ elf_m68k_finish_dynamic_symbol (output_bfd, info, h, sym)
          if (info->shared
              && SYMBOL_REFERENCES_LOCAL (info, h))
            {
-             rela.r_addend = bfd_get_signed_32 (output_bfd,
-                                                (sgot->contents
-                                                 + got_entry_offset));
+             bfd_vma relocation;
 
-             switch (elf_m68k_reloc_got_type (got_entry->key_.type))
+             relocation = bfd_get_signed_32 (output_bfd,
+                                             (sgot->contents
+                                              + got_entry_offset));
+
+             /* Undo TP bias.  */
+             switch (elf_m68k_reloc_got_type (r_type))
                {
                case R_68K_GOT32O:
-                 rela.r_info = ELF32_R_INFO (0, R_68K_RELATIVE);
+               case R_68K_TLS_LDM32:
                  break;
 
                case R_68K_TLS_GD32:
-                 rela.r_info = ELF32_R_INFO (0, R_68K_TLS_DTPMOD32);
+                 /* The value for this relocation is actually put in
+                    the second GOT slot.  */
+                 relocation = bfd_get_signed_32 (output_bfd,
+                                                 (sgot->contents
+                                                  + got_entry_offset + 4));
+                 relocation += dtpoff_base (info);
                  break;
 
                case R_68K_TLS_IE32:
-                 rela.r_info = ELF32_R_INFO (0, R_68K_TLS_TPREL32);
+                 relocation += tpoff_base (info);
                  break;
 
                default:
                  BFD_ASSERT (FALSE);
-                 break;
                }
 
-               elf_m68k_install_rela (output_bfd, srela, &rela);
+             elf_m68k_init_got_entry_local_shared (info,
+                                                   output_bfd,
+                                                   r_type,
+                                                   sgot,
+                                                   got_entry_offset,
+                                                   relocation,
+                                                   srela);
            }
          else
            {
+             Elf_Internal_Rela rela;
+
              /* Put zeros to GOT slots that will be initialized
                 at run-time.  */
              {
@@ -4327,8 +4431,11 @@ elf_m68k_finish_dynamic_symbol (output_bfd, info, h, sym)
              }
 
              rela.r_addend = 0;
+             rela.r_offset = (sgot->output_section->vma
+                              + sgot->output_offset
+                              + got_entry_offset);
 
-             switch (elf_m68k_reloc_got_type (got_entry->key_.type))
+             switch (elf_m68k_reloc_got_type (r_type))
                {
                case R_68K_GOT32O:
                  rela.r_info = ELF32_R_INFO (h->dynindx, R_68K_GLOB_DAT);
@@ -4637,34 +4744,44 @@ void
 bfd_elf_m68k_set_target_options (struct bfd_link_info *info, int got_handling)
 {
   struct elf_m68k_link_hash_table *htab;
-
-  htab = elf_m68k_hash_table (info);
+  bfd_boolean use_neg_got_offsets_p;
+  bfd_boolean allow_multigot_p;
+  bfd_boolean local_gp_p;
 
   switch (got_handling)
     {
     case 0:
       /* --got=single.  */
-      htab->local_gp_p = FALSE;
-      htab->use_neg_got_offsets_p = FALSE;
-      htab->allow_multigot_p = FALSE;
+      local_gp_p = FALSE;
+      use_neg_got_offsets_p = FALSE;
+      allow_multigot_p = FALSE;
       break;
 
     case 1:
       /* --got=negative.  */
-      htab->local_gp_p = TRUE;
-      htab->use_neg_got_offsets_p = TRUE;
-      htab->allow_multigot_p = FALSE;
+      local_gp_p = TRUE;
+      use_neg_got_offsets_p = TRUE;
+      allow_multigot_p = FALSE;
       break;
 
     case 2:
       /* --got=multigot.  */
-      htab->local_gp_p = TRUE;
-      htab->use_neg_got_offsets_p = TRUE;
-      htab->allow_multigot_p = TRUE;
+      local_gp_p = TRUE;
+      use_neg_got_offsets_p = TRUE;
+      allow_multigot_p = TRUE;
       break;
 
     default:
       BFD_ASSERT (FALSE);
+      return;
+    }
+
+  htab = elf_m68k_hash_table (info);
+  if (htab != NULL)
+    {
+      htab->local_gp_p = local_gp_p;
+      htab->use_neg_got_offsets_p = use_neg_got_offsets_p;
+      htab->allow_multigot_p = allow_multigot_p;
     }
 }
 
@@ -4715,6 +4832,7 @@ elf_m68k_plt_sym_val (bfd_vma i, const asection *plt,
                                        elf_m68k_adjust_dynamic_symbol
 #define elf_backend_size_dynamic_sections \
                                        elf_m68k_size_dynamic_sections
+#define elf_backend_final_write_processing     elf_m68k_final_write_processing
 #define elf_backend_init_index_section _bfd_elf_init_1_index_section
 #define elf_backend_relocate_section   elf_m68k_relocate_section
 #define elf_backend_finish_dynamic_symbol \