* Makefile.am (libbfd.h): Add "Extracted from.." comment.
[deliverable/binutils-gdb.git] / bfd / elf64-alpha.c
index 53ac1e79175f91ec0830c36342120f2488bee20a..c172c4f23065d2390c95eb967fdf513d7d3f491f 100644 (file)
@@ -73,7 +73,7 @@ static boolean elf64_alpha_mkobject
 static boolean elf64_alpha_object_p
   PARAMS((bfd *));
 static boolean elf64_alpha_section_from_shdr
-  PARAMS((bfd *, Elf64_Internal_Shdr *, char *));
+  PARAMS((bfd *, Elf64_Internal_Shdr *, const char *));
 static boolean elf64_alpha_section_flags
   PARAMS((flagword *, Elf64_Internal_Shdr *));
 static boolean elf64_alpha_fake_sections
@@ -106,14 +106,27 @@ static boolean elf64_alpha_calc_got_offsets_for_symbol
   PARAMS ((struct alpha_elf_link_hash_entry *, PTR));
 static void elf64_alpha_calc_got_offsets PARAMS ((struct bfd_link_info *));
 static boolean elf64_alpha_size_got_sections
-  PARAMS ((bfd *, struct bfd_link_info *));
+  PARAMS ((struct bfd_link_info *));
+static boolean elf64_alpha_size_plt_section
+  PARAMS ((struct bfd_link_info *));
+static boolean elf64_alpha_size_plt_section_1
+  PARAMS ((struct alpha_elf_link_hash_entry *, PTR));
 static boolean elf64_alpha_always_size_sections
   PARAMS ((bfd *, struct bfd_link_info *));
+static int alpha_dynamic_entries_for_reloc
+  PARAMS ((int, int, int));
 static boolean elf64_alpha_calc_dynrel_sizes
   PARAMS ((struct alpha_elf_link_hash_entry *, struct bfd_link_info *));
+static boolean elf64_alpha_size_rela_got_section
+  PARAMS ((struct bfd_link_info *));
+static boolean elf64_alpha_size_rela_got_1
+  PARAMS ((struct alpha_elf_link_hash_entry *, struct bfd_link_info *));
 static boolean elf64_alpha_add_symbol_hook
   PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Sym *,
           const char **, flagword *, asection **, bfd_vma *));
+static struct alpha_elf_got_entry *get_got_entry
+  PARAMS ((bfd *, struct alpha_elf_link_hash_entry *, unsigned long,
+          unsigned long, bfd_vma));
 static boolean elf64_alpha_check_relocs
   PARAMS((bfd *, struct bfd_link_info *, asection *sec,
          const Elf_Internal_Rela *));
@@ -121,6 +134,9 @@ static boolean elf64_alpha_adjust_dynamic_symbol
   PARAMS((struct bfd_link_info *, struct elf_link_hash_entry *));
 static boolean elf64_alpha_size_dynamic_sections
   PARAMS((bfd *, struct bfd_link_info *));
+static boolean elf64_alpha_relocate_section_r
+  PARAMS((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
+         Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
 static boolean elf64_alpha_relocate_section
   PARAMS((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
          Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
@@ -148,11 +164,15 @@ struct alpha_elf_link_hash_entry
   /* Cumulative flags for all the .got entries.  */
   int flags;
 
-  /* Contexts (LITUSE) in which a literal was referenced.  */
-#define ALPHA_ELF_LINK_HASH_LU_ADDR 0x01
-#define ALPHA_ELF_LINK_HASH_LU_MEM  0x02
-#define ALPHA_ELF_LINK_HASH_LU_BYTE 0x04
-#define ALPHA_ELF_LINK_HASH_LU_FUNC 0x08
+  /* Contexts in which a literal was referenced.  */
+#define ALPHA_ELF_LINK_HASH_LU_ADDR    0x01
+#define ALPHA_ELF_LINK_HASH_LU_MEM     0x02
+#define ALPHA_ELF_LINK_HASH_LU_BYTE    0x04
+#define ALPHA_ELF_LINK_HASH_LU_JSR     0x08
+#define ALPHA_ELF_LINK_HASH_LU_TLSGD   0x10
+#define ALPHA_ELF_LINK_HASH_LU_TLSLDM  0x20
+#define ALPHA_ELF_LINK_HASH_LU_FUNC    0x38
+#define ALPHA_ELF_LINK_HASH_TLS_IE     0x40
 
   /* Used to implement multiple .got subsections.  */
   struct alpha_elf_got_entry
@@ -168,13 +188,20 @@ struct alpha_elf_link_hash_entry
     /* the .got offset for this entry.  */
     int got_offset;
 
-    int flags;
+    /* How many references to this entry?  */
+    int use_count;
+
+    /* The relocation type of this entry.  */
+    unsigned char reloc_type;
 
-    /* Additional flags.  */
-#define ALPHA_ELF_GOT_ENTRY_RELOCS_DONE 0x10
-#define ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED 0x20
+    /* How a LITERAL is used.  */
+    unsigned char flags;
 
-    int use_count;
+    /* Have we initialized the dynamic relocation for this entry?  */
+    unsigned char reloc_done;
+
+    /* Have we adjusted this entry for SEC_MERGE?  */
+    unsigned char reloc_xlated;
   } *got_entries;
 
   /* used to count non-got, non-plt relocations for delayed sizing
@@ -324,14 +351,14 @@ elf64_alpha_bfd_link_hash_table_create (abfd)
   struct alpha_elf_link_hash_table *ret;
   bfd_size_type amt = sizeof (struct alpha_elf_link_hash_table);
 
-  ret = (struct alpha_elf_link_hash_table *) bfd_zalloc (abfd, amt);
+  ret = (struct alpha_elf_link_hash_table *) bfd_zmalloc (amt);
   if (ret == (struct alpha_elf_link_hash_table *) NULL)
     return NULL;
 
   if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
                                       elf64_alpha_link_hash_newfunc))
     {
-      bfd_release (abfd, ret);
+      free (ret);
       return NULL;
     }
 
@@ -361,12 +388,12 @@ struct alpha_elf_obj_tdata
   /* For every got, this is the section.  */
   asection *got;
 
-  /* For every got, this is it's total number of *entries*.  */
-  int total_got_entries;
+  /* For every got, this is it's total number of words.  */
+  int total_got_size;
 
-  /* For every got, this is the sum of the number of *entries* required
+  /* For every got, this is the sum of the number of words required
      to hold all of the member object's local got.  */
-  int n_local_got_entries;
+  int local_got_size;
 };
 
 #define alpha_elf_tdata(abfd) \
@@ -748,6 +775,203 @@ static reloc_howto_type elf64_alpha_howto_table[] =
         0x1fffff,              /* src_mask */
         0x1fffff,              /* dst_mask */
         true),                 /* pcrel_offset */
+
+  /* Creates a tls_index for the symbol in the got.  */
+  HOWTO (R_ALPHA_TLSGD,                /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TLSGD",               /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* Creates a tls_index for the (current) module in the got.  */
+  HOWTO (R_ALPHA_TLSLDM,       /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TLSLDM",              /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A dynamic relocation for a DTP module entry.  */
+  HOWTO (R_ALPHA_DTPMOD64,     /* type */
+        0,                     /* rightshift */
+        4,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DTPMOD64",            /* name */
+        false,                 /* partial_inplace */
+        MINUS_ONE,             /* src_mask */
+        MINUS_ONE,             /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* Creates a 64-bit offset in the got for the displacement
+     from DTP to the target.  */
+  HOWTO (R_ALPHA_GOTDTPREL,    /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "GOTDTPREL",           /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A dynamic relocation for a displacement from DTP to the target.  */
+  HOWTO (R_ALPHA_DTPREL64,     /* type */
+        0,                     /* rightshift */
+        4,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DTPREL64",            /* name */
+        false,                 /* partial_inplace */
+        MINUS_ONE,             /* src_mask */
+        MINUS_ONE,             /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* The high 16 bits of the displacement from DTP to the target.  */
+  HOWTO (R_ALPHA_DTPRELHI,     /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DTPRELHI",            /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* The low 16 bits of the displacement from DTP to the target.  */
+  HOWTO (R_ALPHA_DTPRELLO,     /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DTPRELLO",            /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A 16-bit displacement from DTP to the target.  */
+  HOWTO (R_ALPHA_DTPREL16,     /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "DTPREL16",            /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* Creates a 64-bit offset in the got for the displacement
+     from TP to the target.  */
+  HOWTO (R_ALPHA_GOTTPREL,     /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "GOTTPREL",            /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A dynamic relocation for a displacement from TP to the target.  */
+  HOWTO (R_ALPHA_TPREL64,      /* type */
+        0,                     /* rightshift */
+        4,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TPREL64",             /* name */
+        false,                 /* partial_inplace */
+        MINUS_ONE,             /* src_mask */
+        MINUS_ONE,             /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* The high 16 bits of the displacement from TP to the target.  */
+  HOWTO (R_ALPHA_TPRELHI,      /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TPRELHI",             /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* The low 16 bits of the displacement from TP to the target.  */
+  HOWTO (R_ALPHA_TPRELLO,      /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TPRELLO",             /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A 16-bit displacement from TP to the target.  */
+  HOWTO (R_ALPHA_TPREL16,      /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        0,                     /* special_function */
+        "TPREL16",             /* name */
+        false,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
 };
 
 /* A relocation function which doesn't do anything.  */
@@ -902,6 +1126,19 @@ static const struct elf_reloc_map elf64_alpha_reloc_map[] =
   {BFD_RELOC_ALPHA_GPREL_LO16,         R_ALPHA_GPRELLOW},
   {BFD_RELOC_GPREL16,                  R_ALPHA_GPREL16},
   {BFD_RELOC_ALPHA_BRSGP,              R_ALPHA_BRSGP},
+  {BFD_RELOC_ALPHA_TLSGD,              R_ALPHA_TLSGD},
+  {BFD_RELOC_ALPHA_TLSLDM,             R_ALPHA_TLSLDM},
+  {BFD_RELOC_ALPHA_DTPMOD64,           R_ALPHA_DTPMOD64},
+  {BFD_RELOC_ALPHA_GOTDTPREL16,                R_ALPHA_GOTDTPREL},
+  {BFD_RELOC_ALPHA_DTPREL64,           R_ALPHA_DTPREL64},
+  {BFD_RELOC_ALPHA_DTPREL_HI16,                R_ALPHA_DTPRELHI},
+  {BFD_RELOC_ALPHA_DTPREL_LO16,                R_ALPHA_DTPRELLO},
+  {BFD_RELOC_ALPHA_DTPREL16,           R_ALPHA_DTPREL16},
+  {BFD_RELOC_ALPHA_GOTTPREL16,         R_ALPHA_GOTTPREL},
+  {BFD_RELOC_ALPHA_TPREL64,            R_ALPHA_TPREL64},
+  {BFD_RELOC_ALPHA_TPREL_HI16,         R_ALPHA_TPRELHI},
+  {BFD_RELOC_ALPHA_TPREL_LO16,         R_ALPHA_TPRELLO},
+  {BFD_RELOC_ALPHA_TPREL16,            R_ALPHA_TPREL16},
 };
 
 /* Given a BFD reloc type, return a HOWTO structure.  */
@@ -936,6 +1173,19 @@ elf64_alpha_info_to_howto (abfd, cache_ptr, dst)
   BFD_ASSERT (r_type < (unsigned int) R_ALPHA_max);
   cache_ptr->howto = &elf64_alpha_howto_table[r_type];
 }
+
+/* These two relocations create a two-word entry in the got.  */
+#define alpha_got_entry_size(r_type) \
+  (r_type == R_ALPHA_TLSGD || r_type == R_ALPHA_TLSLDM ? 16 : 8)
+
+/* This is PT_TLS segment p_vaddr.  */
+#define alpha_get_dtprel_base(tlss) \
+  ((tlss)->start)
+
+/* Main program TLS (whose template starts at PT_TLS p_vaddr)
+   is assigned offset round(16, PT_TLS p_align).  */
+#define alpha_get_tprel_base(tlss) \
+  ((tlss)->start - align_power ((bfd_vma) 16, (tlss)->align))
 \f
 /* These functions do relaxation for Alpha ELF.
 
@@ -958,35 +1208,45 @@ elf64_alpha_info_to_howto (abfd, cache_ptr, dst)
 #define OP_BR          0x30
 #define OP_BSR         0x34
 #define INSN_UNOP      0x2ffe0000
+#define INSN_ADDQ      0x40000400
+#define INSN_RDUNIQ    0x0000009e
 
 struct alpha_relax_info
 {
   bfd *abfd;
   asection *sec;
   bfd_byte *contents;
+  Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *relocs, *relend;
   struct bfd_link_info *link_info;
-  boolean changed_contents;
-  boolean changed_relocs;
+  struct elf_link_tls_segment *tls_segment;
   bfd_vma gp;
   bfd *gotobj;
   asection *tsec;
   struct alpha_elf_link_hash_entry *h;
+  struct alpha_elf_got_entry **first_gotent;
   struct alpha_elf_got_entry *gotent;
+  boolean changed_contents;
+  boolean changed_relocs;
   unsigned char other;
 };
 
-static Elf_Internal_Rela * elf64_alpha_relax_with_lituse
-  PARAMS((struct alpha_relax_info *info, bfd_vma symval,
-          Elf_Internal_Rela *irel, Elf_Internal_Rela *irelend));
-
-static boolean elf64_alpha_relax_without_lituse
+static boolean elf64_alpha_relax_with_lituse
   PARAMS((struct alpha_relax_info *info, bfd_vma symval,
           Elf_Internal_Rela *irel));
-
 static bfd_vma elf64_alpha_relax_opt_call
   PARAMS((struct alpha_relax_info *info, bfd_vma symval));
-
+static boolean elf64_alpha_relax_got_load
+  PARAMS((struct alpha_relax_info *info, bfd_vma symval,
+          Elf_Internal_Rela *irel, unsigned long));
+static boolean elf64_alpha_relax_gprelhilo
+  PARAMS((struct alpha_relax_info *info, bfd_vma symval,
+          Elf_Internal_Rela *irel, boolean));
+static boolean elf64_alpha_relax_tls_get_addr
+  PARAMS((struct alpha_relax_info *info, bfd_vma symval,
+          Elf_Internal_Rela *irel, boolean));
+static struct elf_link_tls_segment *elf64_alpha_relax_find_tls_segment
+  PARAMS((struct alpha_relax_info *, struct elf_link_tls_segment *));
 static boolean elf64_alpha_relax_section
   PARAMS((bfd *abfd, asection *sec, struct bfd_link_info *link_info,
          boolean *again));
@@ -1007,13 +1267,13 @@ elf64_alpha_find_reloc_at_ofs (rel, relend, offset, type)
   return NULL;
 }
 
-static Elf_Internal_Rela *
-elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
+static boolean
+elf64_alpha_relax_with_lituse (info, symval, irel)
      struct alpha_relax_info *info;
      bfd_vma symval;
-     Elf_Internal_Rela *irel, *irelend;
+     Elf_Internal_Rela *irel;
 {
-  Elf_Internal_Rela *urel;
+  Elf_Internal_Rela *urel, *irelend = info->relend;
   int flags, count, i;
   bfd_signed_vma disp;
   boolean fits16;
@@ -1029,9 +1289,13 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
        ("%s: %s+0x%lx: warning: LITERAL relocation against unexpected insn",
        bfd_archive_filename (info->abfd), info->sec->name,
        (unsigned long) irel->r_offset));
-      return irel;
+      return true;
     }
 
+  /* Can't relax dynamic symbols.  */
+  if (alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info))
+    return true;
+
   /* Summarize how this particular LITERAL is used.  */
   for (urel = irel+1, flags = count = 0; urel < irelend; ++urel, ++count)
     {
@@ -1054,25 +1318,27 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
 
       switch (urel->r_addend)
        {
-       default: /* 0 = ADDRESS FORMAT */
+       case LITUSE_ALPHA_ADDR:
+       default:
          /* This type is really just a placeholder to note that all
             uses cannot be optimized, but to still allow some.  */
          all_optimized = false;
          break;
 
-       case 1: /* MEM FORMAT */
+       case LITUSE_ALPHA_BASE:
          /* We can always optimize 16-bit displacements.  */
 
          /* Extract the displacement from the instruction, sign-extending
             it if necessary, then test whether it is within 16 or 32 bits
             displacement from GP.  */
          insn_disp = insn & 0x0000ffff;
-         if (insn_disp & 0x00008000)
-           insn_disp |= 0xffff0000;  /* Negative: sign-extend.  */
+         if (insn_disp & 0x8000)
+           insn_disp |= ~0xffff;  /* Negative: sign-extend.  */
 
          xdisp = disp + insn_disp;
-         fits16 = (xdisp >= - (bfd_signed_vma) 0x00008000 && xdisp < 0x00008000);
-         fits32 = (xdisp >= - (bfd_signed_vma) 0x80000000 && xdisp < 0x7fff8000);
+         fits16 = (xdisp >= - (bfd_signed_vma) 0x8000 && xdisp < 0x8000);
+         fits32 = (xdisp >= - (bfd_signed_vma) 0x80000000
+                   && xdisp < 0x7fff8000);
 
          if (fits16)
            {
@@ -1111,7 +1377,7 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
            all_optimized = false;
          break;
 
-       case 2: /* BYTE OFFSET FORMAT */
+       case LITUSE_ALPHA_BYTOFF:
          /* We can always optimize byte instructions.  */
 
          /* FIXME: sanity check the insn for byte op.  Check that the
@@ -1129,16 +1395,20 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
          info->changed_contents = true;
          break;
 
-       case 3: /* CALL FORMAT */
+       case LITUSE_ALPHA_JSR:
+       case LITUSE_ALPHA_TLSGD:
+       case LITUSE_ALPHA_TLSLDM:
          {
-           /* If not zero, place to jump without needing pv.  */
-           bfd_vma optdest = elf64_alpha_relax_opt_call (info, symval);
-           bfd_vma org = (info->sec->output_section->vma
-                          + info->sec->output_offset
-                          + urel->r_offset + 4);
+           bfd_vma optdest, org;
            bfd_signed_vma odisp;
 
+           /* If not zero, place to jump without needing pv.  */
+           optdest = elf64_alpha_relax_opt_call (info, symval);
+           org = (info->sec->output_section->vma
+                  + info->sec->output_offset
+                  + urel->r_offset + 4);
            odisp = (optdest ? optdest : symval) - org;
+
            if (odisp >= -0x400000 && odisp < 0x400000)
              {
                Elf_Internal_Rela *xrel;
@@ -1180,10 +1450,11 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
              {
                Elf_Internal_Rela *gpdisp
                  = (elf64_alpha_find_reloc_at_ofs
-                    (irel, irelend, urel->r_offset + 4, R_ALPHA_GPDISP));
+                    (info->relocs, irelend, urel->r_offset + 4,
+                     R_ALPHA_GPDISP));
                if (gpdisp)
                  {
-                   bfd_byte *p_ldah = info->contents + gpdisp->r_offset; 
+                   bfd_byte *p_ldah = info->contents + gpdisp->r_offset;
                    bfd_byte *p_lda = p_ldah + gpdisp->r_addend;
                    unsigned int ldah = bfd_get_32 (info->abfd, p_ldah);
                    unsigned int lda = bfd_get_32 (info->abfd, p_lda);
@@ -1213,14 +1484,17 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
      got entry by one, possibly eliminating it.  */
   if (all_optimized)
     {
-      info->gotent->use_count -= 1;
-      alpha_elf_tdata (info->gotent->gotobj)->total_got_entries -= 1;
-      if (!info->h)
-       alpha_elf_tdata (info->gotent->gotobj)->n_local_got_entries -= 1;
+      if (--info->gotent->use_count == 0)
+       {
+         int sz = alpha_got_entry_size (R_ALPHA_LITERAL);
+         alpha_elf_tdata (info->gotobj)->total_got_size -= sz;
+         if (!info->h)
+           alpha_elf_tdata (info->gotobj)->local_got_size -= sz;
+       }
 
       /* If the literal instruction is no longer needed (it may have been
-        reused.  We can eliminate it.
-        ??? For now, I don't want to deal with compacting the section,
+        reused.  We can eliminate it.  */
+      /* ??? For now, I don't want to deal with compacting the section,
         so just nop it out.  */
       if (!lit_reused)
        {
@@ -1233,7 +1507,7 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
        }
     }
 
-  return irel + count;
+  return true;
 }
 
 static bfd_vma
@@ -1309,10 +1583,11 @@ elf64_alpha_relax_opt_call (info, symval)
 }
 
 static boolean
-elf64_alpha_relax_without_lituse (info, symval, irel)
+elf64_alpha_relax_got_load (info, symval, irel, r_type)
      struct alpha_relax_info *info;
      bfd_vma symval;
      Elf_Internal_Rela *irel;
+     unsigned long r_type;
 {
   unsigned int insn;
   bfd_signed_vma disp;
@@ -1322,37 +1597,74 @@ elf64_alpha_relax_without_lituse (info, symval, irel)
 
   if (insn >> 26 != OP_LDQ)
     {
+      reloc_howto_type *howto = elf64_alpha_howto_table + r_type;
       ((*_bfd_error_handler)
-       ("%s: %s+0x%lx: warning: LITERAL relocation against unexpected insn",
+       ("%s: %s+0x%lx: warning: %s relocation against unexpected insn",
        bfd_archive_filename (info->abfd), info->sec->name,
-       (unsigned long) irel->r_offset));
+       (unsigned long) irel->r_offset, howto->name));
       return true;
     }
 
-  /* So we aren't told much.  Do what we can with the address load and
-     fake the rest.  All of the optimizations here require that the
-     offset from the GP fit in 16 bits.  */
+  /* Can't relax dynamic symbols.  */
+  if (alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info))
+    return true;
 
-  disp = symval - info->gp;
-  if (disp < -0x8000 || disp >= 0x8000)
+  /* Can't use local-exec relocations in shared libraries.  */
+  if (r_type == R_ALPHA_GOTTPREL && info->link_info->shared)
     return true;
 
-  /* On the LITERAL instruction itself, consider exchanging
-     `ldq R,X(gp)' for `lda R,Y(gp)'.  */
+  if (r_type == R_ALPHA_LITERAL)
+    disp = symval - info->gp;
+  else
+    {
+      bfd_vma dtp_base, tp_base;
+
+      BFD_ASSERT (info->tls_segment != NULL);
+      dtp_base = alpha_get_dtprel_base (info->tls_segment);
+      tp_base = alpha_get_tprel_base (info->tls_segment);
+      disp = symval - (r_type == R_ALPHA_GOTDTPREL ? dtp_base : tp_base);
+    }
+
+  if (disp < -0x8000 || disp >= 0x8000)
+    return true;
 
-  insn = (OP_LDA << 26) | (insn & 0x03ff0000);
+  /* Exchange LDQ for LDA.  In the case of the TLS relocs, we're loading
+     a constant, so force the base register to be $31.  */
+  if (r_type == R_ALPHA_LITERAL)
+    insn = (OP_LDA << 26) | (insn & 0x03ff0000);
+  else
+    insn = (OP_LDA << 26) | (insn & (31 << 21)) | (31 << 16);
   bfd_put_32 (info->abfd, (bfd_vma) insn, info->contents + irel->r_offset);
   info->changed_contents = true;
 
-  irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), R_ALPHA_GPREL16);
+  switch (r_type)
+    {
+    case R_ALPHA_LITERAL:
+      r_type = R_ALPHA_GPREL16;
+      break;
+    case R_ALPHA_GOTDTPREL:
+      r_type = R_ALPHA_DTPREL16;
+      break;
+    case R_ALPHA_GOTTPREL:
+      r_type = R_ALPHA_TPREL16;
+      break;
+    default:
+      BFD_ASSERT (0);
+      return false;
+    }
+
+  irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info), r_type);
   info->changed_relocs = true;
 
   /* Reduce the use count on this got entry by one, possibly
      eliminating it.  */
-  info->gotent->use_count -= 1;
-  alpha_elf_tdata (info->gotent->gotobj)->total_got_entries -= 1;
-  if (!info->h)
-    alpha_elf_tdata (info->gotent->gotobj)->n_local_got_entries -= 1;
+  if (--info->gotent->use_count == 0)
+    {
+      int sz = alpha_got_entry_size (r_type);
+      alpha_elf_tdata (info->gotobj)->total_got_size -= sz;
+      if (!info->h)
+       alpha_elf_tdata (info->gotobj)->local_got_size -= sz;
+    }
 
   /* ??? Search forward through this basic block looking for insns
      that use the target register.  Stop after an insn modifying the
@@ -1371,39 +1683,387 @@ elf64_alpha_relax_without_lituse (info, symval, irel)
 }
 
 static boolean
-elf64_alpha_relax_section (abfd, sec, link_info, again)
-     bfd *abfd;
-     asection *sec;
-     struct bfd_link_info *link_info;
-     boolean *again;
+elf64_alpha_relax_gprelhilo (info, symval, irel, hi)
+     struct alpha_relax_info *info;
+     bfd_vma symval;
+     Elf_Internal_Rela *irel;
+     boolean hi;
 {
-  Elf_Internal_Shdr *symtab_hdr;
-  Elf_Internal_Shdr *shndx_hdr;
-  Elf_Internal_Rela *internal_relocs;
-  Elf_Internal_Rela *free_relocs = NULL;
-  Elf_Internal_Rela *irel, *irelend;
-  bfd_byte *free_contents = NULL;
-  Elf64_External_Sym *extsyms = NULL;
-  Elf64_External_Sym *free_extsyms = NULL;
-  Elf_External_Sym_Shndx *shndx_buf = NULL;
-  struct alpha_elf_got_entry **local_got_entries;
-  struct alpha_relax_info info;
+  unsigned int insn;
+  bfd_signed_vma disp;
+  bfd_byte *pos = info->contents + irel->r_offset;
 
-  /* We are not currently changing any sizes, so only one pass.  */
-  *again = false;
+  /* ??? This assumes that the compiler doesn't render
 
-  if (link_info->relocateable
-      || (sec->flags & SEC_RELOC) == 0
-      || sec->reloc_count == 0)
+       array[i]
+     as
+       ldah    t, array(gp)    !gprelhigh
+       s8addl  i, t, t
+       ldq     r, array(t)     !gprellow
+
+     which would indeed be the most efficient way to implement this.  */
+
+  return true;
+
+  disp = symval - info->gp;
+  if (disp < -0x8000 || disp >= 0x8000)
     return true;
 
-  /* If this is the first time we have been called for this section,
-     initialize the cooked size.  */
-  if (sec->_cooked_size == 0)
-    sec->_cooked_size = sec->_raw_size;
+  if (hi)
+    {
+      /* Nop out the high instruction.  */
 
-  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-  local_got_entries = alpha_elf_tdata(abfd)->local_got_entries;
+      bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, pos);
+      info->changed_contents = true;
+
+      irel->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
+      irel->r_addend = 0;
+      info->changed_relocs = true;
+    }
+  else
+    {
+      /* Adjust the low instruction to reference GP directly.  */
+
+      insn = bfd_get_32 (info->abfd, pos);
+      insn = (insn & 0xffe00000) | (29 << 16);
+      bfd_put_32 (info->abfd, (bfd_vma) insn, pos);
+      info->changed_contents = true;
+
+      irel->r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
+                                  R_ALPHA_GPREL16);
+      info->changed_relocs = true;
+    }
+
+  return true;
+}
+
+static boolean
+elf64_alpha_relax_tls_get_addr (info, symval, irel, is_gd)
+     struct alpha_relax_info *info;
+     bfd_vma symval;
+     Elf_Internal_Rela *irel;
+     boolean is_gd;
+{
+  bfd_byte *pos[5];
+  unsigned int insn;
+  Elf_Internal_Rela *gpdisp, *hint;
+  boolean dynamic, use_gottprel;
+
+  dynamic = alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info);
+
+  /* ??? For LD relaxation, we need a symbol referencing the beginning
+     of the TLS segment.  */
+  if (!is_gd)
+    return true;
+
+  /* If a TLS symbol is accessed using IE at least once, there is no point
+     to use dynamic model for it.  */
+  if (is_gd && info->h && (info->h->flags & ALPHA_ELF_LINK_HASH_TLS_IE))
+    ;
+
+  /* If the symbol is local, and we've already committed to DF_STATIC_TLS,
+     then we might as well relax to IE.  */
+  else if (info->link_info->shared && !dynamic
+          && (info->link_info->flags & DF_STATIC_TLS))
+    ;
+
+  /* Otherwise we must be building an executable to do anything.  */
+  else if (info->link_info->shared)
+    return true;
+
+  /* The TLSGD/TLSLDM relocation must be followed by a LITERAL and
+     the matching LITUSE_TLS relocations.  */
+  if (irel + 2 >= info->relend)
+    return true;
+  if (ELF64_R_TYPE (irel[1].r_info) != R_ALPHA_LITERAL
+      || ELF64_R_TYPE (irel[2].r_info) != R_ALPHA_LITUSE
+      || irel[2].r_addend != (is_gd ? LITUSE_ALPHA_TLSGD : LITUSE_ALPHA_TLSLDM))
+    return true;
+
+  /* There must be a GPDISP relocation positioned immediately after the
+     LITUSE relocation.  */
+  gpdisp = elf64_alpha_find_reloc_at_ofs (info->relocs, info->relend,
+                                         irel[2].r_offset + 4, R_ALPHA_GPDISP);
+  if (!gpdisp)
+    return true;
+
+  pos[0] = info->contents + irel[0].r_offset;
+  pos[1] = info->contents + irel[1].r_offset;
+  pos[2] = info->contents + irel[2].r_offset;
+  pos[3] = info->contents + gpdisp->r_offset;
+  pos[4] = pos[3] + gpdisp->r_addend;
+
+  /* Only positions 0 and 1 are allowed to be out of order.  */
+  if (pos[1] < pos[0])
+    {
+      bfd_byte *tmp = pos[0];
+      pos[0] = pos[1];
+      pos[1] = tmp;
+    }
+  if (pos[1] >= pos[2] || pos[2] >= pos[3] || pos[3] >= pos[4])
+    return true;
+
+  /* Reduce the use count on the LITERAL relocation.  Do this before we
+     smash the symndx when we adjust the relocations below.  */
+  {
+    struct alpha_elf_got_entry *lit_gotent;
+    struct alpha_elf_link_hash_entry *lit_h;
+    unsigned long indx;
+
+    BFD_ASSERT (ELF64_R_SYM (irel[1].r_info) >= info->symtab_hdr->sh_info);
+    indx = ELF64_R_SYM (irel[1].r_info) - info->symtab_hdr->sh_info;
+    lit_h = alpha_elf_sym_hashes (info->abfd)[indx];
+
+    while (lit_h->root.root.type == bfd_link_hash_indirect
+          || lit_h->root.root.type == bfd_link_hash_warning)
+      lit_h = (struct alpha_elf_link_hash_entry *) lit_h->root.root.u.i.link;
+
+    for (lit_gotent = lit_h->got_entries; lit_gotent ;
+        lit_gotent = lit_gotent->next)
+      if (lit_gotent->gotobj == info->gotobj
+         && lit_gotent->reloc_type == R_ALPHA_LITERAL
+         && lit_gotent->addend == irel[1].r_addend)
+       break;
+    BFD_ASSERT (lit_gotent);
+
+    if (--lit_gotent->use_count == 0)
+      {
+       int sz = alpha_got_entry_size (R_ALPHA_LITERAL);
+       alpha_elf_tdata (info->gotobj)->total_got_size -= sz;
+      }
+  }
+
+  /* Change
+
+       lda     $16,x($gp)              !tlsgd!1
+       ldq     $27,__tls_get_addr($gp) !literal!1
+       jsr     $26,($27)__tls_get_addr !lituse_tlsgd!1
+       ldah    $29,0($26)              !gpdisp!2
+       lda     $29,0($29)              !gpdisp!2
+     to
+       ldq     $16,x($gp)              !gottprel
+       unop
+       call_pal rduniq
+       addq    $16,$0,$0
+       unop
+     or the first pair to
+       lda     $16,x($gp)              !tprel
+       unop
+     or
+       ldah    $16,x($gp)              !tprelhi
+       lda     $16,x($16)              !tprello
+
+     as appropriate.  */
+
+  use_gottprel = false;
+  switch (!dynamic && !info->link_info->shared)
+    {
+    case 1:
+      {
+       bfd_vma tp_base;
+       bfd_signed_vma disp;
+
+       BFD_ASSERT (info->tls_segment != NULL);
+       tp_base = alpha_get_tprel_base (info->tls_segment);
+       disp = symval - tp_base;
+
+       if (disp >= -0x8000 && disp < 0x8000)
+         {
+           insn = (OP_LDA << 26) | (16 << 21) | (31 << 16);
+           bfd_put_32 (info->abfd, (bfd_vma) insn, pos[0]);
+           bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, pos[1]);
+
+           irel[0].r_offset = pos[0] - info->contents;
+           irel[0].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
+                                          R_ALPHA_TPREL16);
+           irel[1].r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
+           break;
+         }
+       else if (disp >= -(bfd_signed_vma) 0x80000000
+                && disp < (bfd_signed_vma) 0x7fff8000)
+         {
+           insn = (OP_LDAH << 26) | (16 << 21) | (31 << 16);
+           bfd_put_32 (info->abfd, (bfd_vma) insn, pos[0]);
+           insn = (OP_LDA << 26) | (16 << 21) | (16 << 16);
+           bfd_put_32 (info->abfd, (bfd_vma) insn, pos[1]);
+
+           irel[0].r_offset = pos[0] - info->contents;
+           irel[0].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
+                                          R_ALPHA_TPRELHI);
+           irel[1].r_offset = pos[1] - info->contents;
+           irel[1].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
+                                          R_ALPHA_TPRELLO);
+           break;
+         }
+      }
+      /* FALLTHRU */
+
+    default:
+      use_gottprel = true;
+
+      insn = (OP_LDQ << 26) | (16 << 21) | (29 << 16);
+      bfd_put_32 (info->abfd, (bfd_vma) insn, pos[0]);
+      bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, pos[1]);
+
+      irel[0].r_offset = pos[0] - info->contents;
+      irel[0].r_info = ELF64_R_INFO (ELF64_R_SYM (irel->r_info),
+                                    R_ALPHA_GOTTPREL);
+      irel[1].r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
+      break;
+    }
+
+  bfd_put_32 (info->abfd, (bfd_vma) INSN_RDUNIQ, pos[2]);
+
+  insn = INSN_ADDQ | (16 << 21) | (0 << 16) | (0 << 0);
+  bfd_put_32 (info->abfd, (bfd_vma) insn, pos[3]);
+
+  bfd_put_32 (info->abfd, (bfd_vma) INSN_UNOP, pos[4]);
+
+  irel[2].r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
+  gpdisp->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
+
+  hint = elf64_alpha_find_reloc_at_ofs (info->relocs, info->relend,
+                                       irel[2].r_offset, R_ALPHA_HINT);
+  if (hint)
+    hint->r_info = ELF64_R_INFO (0, R_ALPHA_NONE);
+
+  info->changed_contents = true;
+  info->changed_relocs = true;
+
+  /* Reduce the use count on the TLSGD/TLSLDM relocation.  */
+  if (--info->gotent->use_count == 0)
+    {
+      int sz = alpha_got_entry_size (info->gotent->reloc_type);
+      alpha_elf_tdata (info->gotobj)->total_got_size -= sz;
+      if (!info->h)
+       alpha_elf_tdata (info->gotobj)->local_got_size -= sz;
+    }
+
+  /* If we've switched to a GOTTPREL relocation, increment the reference
+     count on that got entry.  */
+  if (use_gottprel)
+    {
+      struct alpha_elf_got_entry *tprel_gotent;
+
+      for (tprel_gotent = *info->first_gotent; tprel_gotent ;
+          tprel_gotent = tprel_gotent->next)
+       if (tprel_gotent->gotobj == info->gotobj
+           && tprel_gotent->reloc_type == R_ALPHA_GOTTPREL
+           && tprel_gotent->addend == irel->r_addend)
+         break;
+      if (tprel_gotent)
+       tprel_gotent->use_count++;
+      else
+       {
+         if (info->gotent->use_count == 0)
+           tprel_gotent = info->gotent;
+         else
+           {
+             tprel_gotent = (struct alpha_elf_got_entry *)
+               bfd_alloc (info->abfd, sizeof (struct alpha_elf_got_entry));
+             if (!tprel_gotent)
+               return false;
+
+             tprel_gotent->next = *info->first_gotent;
+             *info->first_gotent = tprel_gotent;
+
+             tprel_gotent->gotobj = info->gotobj;
+             tprel_gotent->addend = irel->r_addend;
+             tprel_gotent->got_offset = -1;
+             tprel_gotent->reloc_done = 0;
+             tprel_gotent->reloc_xlated = 0;
+           }
+
+         tprel_gotent->use_count = 1;
+         tprel_gotent->reloc_type = R_ALPHA_GOTTPREL;
+       }
+    }
+
+  return true;
+}
+
+static struct elf_link_tls_segment *
+elf64_alpha_relax_find_tls_segment (info, seg)
+     struct alpha_relax_info *info;
+     struct elf_link_tls_segment *seg;
+{
+  bfd *output_bfd = info->sec->output_section->owner;
+  asection *o;
+  unsigned int align;
+  bfd_vma base, end;
+
+  for (o = output_bfd->sections; o ; o = o->next)
+    if ((o->flags & SEC_THREAD_LOCAL) != 0
+        && (o->flags & SEC_LOAD) != 0)
+      break;
+  if (!o)
+    return NULL;
+
+  base = o->vma;
+  align = 0;
+
+  do
+    {
+      bfd_vma size;
+
+      if (bfd_get_section_alignment (output_bfd, o) > align)
+       align = bfd_get_section_alignment (output_bfd, o);
+
+      size = o->_raw_size;
+      if (size == 0 && (o->flags & SEC_HAS_CONTENTS) == 0)
+       {
+         struct bfd_link_order *lo;
+         for (lo = o->link_order_head; lo ; lo = lo->next)
+           if (size < lo->offset + lo->size)
+             size = lo->offset + lo->size;
+       }
+      end = o->vma + size;
+      o = o->next;
+    }
+  while (o && (o->flags & SEC_THREAD_LOCAL));
+
+  seg->start = base;
+  seg->size = end - base;
+  seg->align = align;
+
+  return seg;
+}
+
+static boolean
+elf64_alpha_relax_section (abfd, sec, link_info, again)
+     bfd *abfd;
+     asection *sec;
+     struct bfd_link_info *link_info;
+     boolean *again;
+{
+  Elf_Internal_Shdr *symtab_hdr;
+  Elf_Internal_Shdr *shndx_hdr;
+  Elf_Internal_Rela *internal_relocs;
+  Elf_Internal_Rela *free_relocs = NULL;
+  Elf_Internal_Rela *irel, *irelend;
+  bfd_byte *free_contents = NULL;
+  Elf64_External_Sym *extsyms;
+  Elf64_External_Sym *free_extsyms = NULL;
+  Elf_External_Sym_Shndx *shndx_buf = NULL;
+  struct alpha_elf_got_entry **local_got_entries;
+  struct alpha_relax_info info;
+  struct elf_link_tls_segment tls_segment;
+
+  /* We are not currently changing any sizes, so only one pass.  */
+  *again = false;
+
+  if (link_info->relocateable
+      || (sec->flags & SEC_RELOC) == 0
+      || sec->reloc_count == 0)
+    return true;
+
+  /* If this is the first time we have been called for this section,
+     initialize the cooked size.  */
+  if (sec->_cooked_size == 0)
+    sec->_cooked_size = sec->_raw_size;
+
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  local_got_entries = alpha_elf_tdata(abfd)->local_got_entries;
 
   /* Load the relocations for this section.  */
   internal_relocs = (_bfd_elf64_link_read_relocs
@@ -1418,83 +2078,89 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
   info.abfd = abfd;
   info.sec = sec;
   info.link_info = link_info;
+  info.symtab_hdr = symtab_hdr;
   info.relocs = internal_relocs;
   info.relend = irelend = internal_relocs + sec->reloc_count;
 
-  /* Find the GP for this object.  */
+  /* Find the GP for this object.  Do not store the result back via
+     _bfd_set_gp_value, since this could change again before final.  */
   info.gotobj = alpha_elf_tdata (abfd)->gotobj;
   if (info.gotobj)
     {
       asection *sgot = alpha_elf_tdata (info.gotobj)->got;
-      info.gp = _bfd_get_gp_value (info.gotobj);
-      if (info.gp == 0)
-       {
-         info.gp = (sgot->output_section->vma
-                    + sgot->output_offset
-                    + 0x8000);
-         _bfd_set_gp_value (info.gotobj, info.gp);
-       }
+      info.gp = (sgot->output_section->vma
+                + sgot->output_offset
+                + 0x8000);
     }
 
-  for (irel = internal_relocs; irel < irelend; irel++)
+  /* Get the section contents.  */
+  if (elf_section_data (sec)->this_hdr.contents != NULL)
+    info.contents = elf_section_data (sec)->this_hdr.contents;
+  else
     {
-      bfd_vma symval;
-      Elf_Internal_Sym isym;
-      struct alpha_elf_got_entry *gotent;
-
-      if (ELF64_R_TYPE (irel->r_info) != (int) R_ALPHA_LITERAL)
-       continue;
-
-      /* Get the section contents.  */
+      info.contents = (bfd_byte *) bfd_malloc (sec->_raw_size);
       if (info.contents == NULL)
-       {
-         if (elf_section_data (sec)->this_hdr.contents != NULL)
-           info.contents = elf_section_data (sec)->this_hdr.contents;
-         else
-           {
-             info.contents = (bfd_byte *) bfd_malloc (sec->_raw_size);
-             if (info.contents == NULL)
-               goto error_return;
-             free_contents = info.contents;
+       goto error_return;
+      free_contents = info.contents;
 
-             if (! bfd_get_section_contents (abfd, sec, info.contents,
-                                             (file_ptr) 0, sec->_raw_size))
-               goto error_return;
-           }
-       }
+      if (! bfd_get_section_contents (abfd, sec, info.contents,
+                                     (file_ptr) 0, sec->_raw_size))
+       goto error_return;
+    }
 
-      /* Read this BFD's symbols if we haven't done so already.  */
+  /* Read this BFD's symbols.  */
+  if (symtab_hdr->contents != NULL)
+    extsyms = (Elf64_External_Sym *) symtab_hdr->contents;
+  else
+    {
+      bfd_size_type amt = symtab_hdr->sh_info * sizeof (Elf64_External_Sym);
+      extsyms = (Elf64_External_Sym *) bfd_malloc (amt);
       if (extsyms == NULL)
-       {
-         bfd_size_type amt;
+       goto error_return;
+      free_extsyms = extsyms;
+      if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0
+         || bfd_bread ((PTR) extsyms, amt, abfd) != amt)
+       goto error_return;
+    }
 
-         if (symtab_hdr->contents != NULL)
-           extsyms = (Elf64_External_Sym *) symtab_hdr->contents;
-         else
-           {
-             amt = symtab_hdr->sh_info;
-             amt *= sizeof (Elf64_External_Sym);
-             extsyms = (Elf64_External_Sym *) bfd_malloc (amt);
-             if (extsyms == NULL)
-               goto error_return;
-             free_extsyms = extsyms;
-             if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0
-                 || bfd_bread ((PTR) extsyms, amt, abfd) != amt)
-               goto error_return;
-           }
+  shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
+  if (shndx_hdr->sh_size != 0)
+    {
+      bfd_size_type amt;
+      amt = symtab_hdr->sh_info * sizeof (Elf_External_Sym_Shndx);
+      shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+      if (shndx_buf == NULL)
+       goto error_return;
+      if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0
+         || bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
+       goto error_return;
+    }
 
-         shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
-         if (shndx_hdr->sh_size != 0)
-           {
-             amt = symtab_hdr->sh_info;
-             amt *= sizeof (Elf_External_Sym_Shndx);
-             shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
-             if (shndx_buf == NULL)
-               goto error_return;
-             if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0
-                 || bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
-               goto error_return;
-           }
+  /* Compute the TLS segment information.  The version normally found in
+     elf_hash_table (link_info)->tls_segment isn't built until final_link.
+     ??? Probably should look into extracting this into a common function.  */
+  info.tls_segment = elf64_alpha_relax_find_tls_segment (&info, &tls_segment);
+
+  for (irel = internal_relocs; irel < irelend; irel++)
+    {
+      bfd_vma symval;
+      Elf_Internal_Sym isym;
+      struct alpha_elf_got_entry *gotent;
+      unsigned long r_type = ELF64_R_TYPE (irel->r_info);
+
+      /* Early exit for unhandled or unrelaxable relocations.  */
+      switch (r_type)
+       {
+       case R_ALPHA_LITERAL:
+       case R_ALPHA_GPRELHIGH:
+       case R_ALPHA_GPRELLOW:
+       case R_ALPHA_GOTDTPREL:
+       case R_ALPHA_GOTTPREL:
+       case R_ALPHA_TLSGD:
+       case R_ALPHA_TLSLDM:
+         break;
+       default:
+         continue;
        }
 
       /* Get the value of the symbol referred to by the reloc.  */
@@ -1508,7 +2174,7 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
          shndx = shndx_buf + (shndx_buf ? ELF64_R_SYM (irel->r_info) : 0);
          bfd_elf64_swap_symbol_in (abfd, esym, shndx, &isym);
          if (isym.st_shndx == SHN_UNDEF)
-           info.tsec = bfd_und_section_ptr;
+           continue;
          else if (isym.st_shndx == SHN_ABS)
            info.tsec = bfd_abs_section_ptr;
          else if (isym.st_shndx == SHN_COMMON)
@@ -1518,7 +2184,7 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
 
          info.h = NULL;
          info.other = isym.st_other;
-         gotent = local_got_entries[ELF64_R_SYM(irel->r_info)];
+         info.first_gotent = &local_got_entries[ELF64_R_SYM(irel->r_info)];
          symval = isym.st_value;
        }
       else
@@ -1534,62 +2200,93 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
                 || h->root.root.type == bfd_link_hash_warning)
            h = (struct alpha_elf_link_hash_entry *)h->root.root.u.i.link;
 
-         /* We can't do anthing with undefined or dynamic symbols.  */
-         if (h->root.root.type == bfd_link_hash_undefined
-             || h->root.root.type == bfd_link_hash_undefweak
-             || alpha_elf_dynamic_symbol_p (&h->root, link_info))
+         /* If the symbol is undefined, we can't do anything with it.  */
+         if (h->root.root.type == bfd_link_hash_undefweak
+             || h->root.root.type == bfd_link_hash_undefined)
+           continue;
+
+         /* If the symbol isn't defined in the current module, again
+            we can't do anything.  */
+         if (!(h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))
            continue;
 
          info.h = h;
          info.tsec = h->root.root.u.def.section;
          info.other = h->root.other;
-         gotent = h->got_entries;
+         info.first_gotent = &h->got_entries;
          symval = h->root.root.u.def.value;
        }
 
       /* Search for the got entry to be used by this relocation.  */
-      while (gotent->gotobj != info.gotobj || gotent->addend != irel->r_addend)
-       gotent = gotent->next;
+      for (gotent = *info.first_gotent; gotent ; gotent = gotent->next)
+       if (gotent->gotobj == info.gotobj
+           && gotent->reloc_type == r_type
+           && gotent->addend == irel->r_addend)
+         break;
       info.gotent = gotent;
 
       symval += info.tsec->output_section->vma + info.tsec->output_offset;
       symval += irel->r_addend;
 
-      BFD_ASSERT(info.gotent != NULL);
+      switch (r_type)
+       {
+       case R_ALPHA_LITERAL:
+         BFD_ASSERT(info.gotent != NULL);
 
-      /* If there exist LITUSE relocations immediately following, this
-        opens up all sorts of interesting optimizations, because we
-        now know every location that this address load is used.  */
+         /* If there exist LITUSE relocations immediately following, this
+            opens up all sorts of interesting optimizations, because we
+            now know every location that this address load is used.  */
+         if (irel+1 < irelend
+             && ELF64_R_TYPE (irel[1].r_info) == R_ALPHA_LITUSE)
+           {
+             if (!elf64_alpha_relax_with_lituse (&info, symval, irel))
+               goto error_return;
+           }
+         else
+           {
+             if (!elf64_alpha_relax_got_load (&info, symval, irel, r_type))
+               goto error_return;
+           }
+         break;
 
-      if (irel+1 < irelend && ELF64_R_TYPE (irel[1].r_info) == R_ALPHA_LITUSE)
-       {
-         irel = elf64_alpha_relax_with_lituse (&info, symval, irel, irelend);
-         if (irel == NULL)
+       case R_ALPHA_GPRELHIGH:
+       case R_ALPHA_GPRELLOW:
+         if (!elf64_alpha_relax_gprelhilo (&info, symval, irel,
+                                           r_type == R_ALPHA_GPRELHIGH))
            goto error_return;
-       }
-      else
-       {
-         if (!elf64_alpha_relax_without_lituse (&info, symval, irel))
+         break;
+
+       case R_ALPHA_GOTDTPREL:
+       case R_ALPHA_GOTTPREL:
+         BFD_ASSERT(info.gotent != NULL);
+         if (!elf64_alpha_relax_got_load (&info, symval, irel, r_type))
+           goto error_return;
+         break;
+
+       case R_ALPHA_TLSGD:
+       case R_ALPHA_TLSLDM:
+         BFD_ASSERT(info.gotent != NULL);
+         if (!elf64_alpha_relax_tls_get_addr (&info, symval, irel,
+                                              r_type == R_ALPHA_TLSGD))
            goto error_return;
+         break;
        }
     }
 
-  if (!elf64_alpha_size_got_sections (abfd, link_info))
+  if (!elf64_alpha_size_plt_section (link_info))
+    return false;
+  if (!elf64_alpha_size_got_sections (link_info))
+    return false;
+  if (!elf64_alpha_size_rela_got_section (link_info))
     return false;
 
   if (info.changed_relocs)
-    {
-      elf_section_data (sec)->relocs = internal_relocs;
-    }
+    elf_section_data (sec)->relocs = internal_relocs;
   else if (free_relocs != NULL)
-    {
-      free (free_relocs);
-    }
+    free (free_relocs);
 
   if (info.changed_contents)
-    {
-      elf_section_data (sec)->this_hdr.contents = info.contents;
-    }
+    elf_section_data (sec)->this_hdr.contents = info.contents;
   else if (free_contents != NULL)
     {
       if (! link_info->keep_memory)
@@ -1643,7 +2340,7 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
 #define PLT_ENTRY_WORD2                0
 #define PLT_ENTRY_WORD3                0
 
-#define MAX_GOT_ENTRIES                (64*1024 / 8)
+#define MAX_GOT_SIZE           (64*1024)
 
 #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so"
 \f
@@ -1656,7 +2353,7 @@ static boolean
 elf64_alpha_section_from_shdr (abfd, hdr, name)
      bfd *abfd;
      Elf64_Internal_Shdr *hdr;
-     char *name;
+     const char *name;
 {
   asection *newsect;
 
@@ -2120,18 +2817,21 @@ elf64_alpha_output_extsym (h, data)
   boolean strip;
   asection *sec, *output_section;
 
+  if (h->root.root.type == bfd_link_hash_warning)
+    h = (struct alpha_elf_link_hash_entry *) h->root.root.u.i.link;
+
   if (h->root.indx == -2)
     strip = false;
   else if (((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
-           || (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0)
-          && (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
-          && (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
+           || (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0)
+          && (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
+          && (h->root.elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
     strip = true;
   else if (einfo->info->strip == strip_all
-          || (einfo->info->strip == strip_some
-              && bfd_hash_lookup (einfo->info->keep_hash,
-                                  h->root.root.root.string,
-                                  false, false) == NULL))
+          || (einfo->info->strip == strip_some
+              && bfd_hash_lookup (einfo->info->keep_hash,
+                                  h->root.root.root.string,
+                                  false, false) == NULL))
     strip = true;
   else
     strip = false;
@@ -2150,44 +2850,44 @@ elf64_alpha_output_extsym (h, data)
       h->esym.asym.st = stGlobal;
 
       if (h->root.root.type != bfd_link_hash_defined
-         && h->root.root.type != bfd_link_hash_defweak)
-       h->esym.asym.sc = scAbs;
+         && h->root.root.type != bfd_link_hash_defweak)
+       h->esym.asym.sc = scAbs;
       else
-       {
-         const char *name;
-
-         sec = h->root.root.u.def.section;
-         output_section = sec->output_section;
-
-         /* When making a shared library and symbol h is the one from
-            the another shared library, OUTPUT_SECTION may be null.  */
-         if (output_section == NULL)
-           h->esym.asym.sc = scUndefined;
-         else
-           {
-             name = bfd_section_name (output_section->owner, output_section);
-
-             if (strcmp (name, ".text") == 0)
-               h->esym.asym.sc = scText;
-             else if (strcmp (name, ".data") == 0)
-               h->esym.asym.sc = scData;
-             else if (strcmp (name, ".sdata") == 0)
-               h->esym.asym.sc = scSData;
-             else if (strcmp (name, ".rodata") == 0
-                      || strcmp (name, ".rdata") == 0)
-               h->esym.asym.sc = scRData;
-             else if (strcmp (name, ".bss") == 0)
-               h->esym.asym.sc = scBss;
-             else if (strcmp (name, ".sbss") == 0)
-               h->esym.asym.sc = scSBss;
-             else if (strcmp (name, ".init") == 0)
-               h->esym.asym.sc = scInit;
-             else if (strcmp (name, ".fini") == 0)
-               h->esym.asym.sc = scFini;
-             else
-               h->esym.asym.sc = scAbs;
-           }
-       }
+       {
+         const char *name;
+
+         sec = h->root.root.u.def.section;
+         output_section = sec->output_section;
+
+         /* When making a shared library and symbol h is the one from
+            the another shared library, OUTPUT_SECTION may be null.  */
+         if (output_section == NULL)
+           h->esym.asym.sc = scUndefined;
+         else
+           {
+             name = bfd_section_name (output_section->owner, output_section);
+
+             if (strcmp (name, ".text") == 0)
+               h->esym.asym.sc = scText;
+             else if (strcmp (name, ".data") == 0)
+               h->esym.asym.sc = scData;
+             else if (strcmp (name, ".sdata") == 0)
+               h->esym.asym.sc = scSData;
+             else if (strcmp (name, ".rodata") == 0
+                      || strcmp (name, ".rdata") == 0)
+               h->esym.asym.sc = scRData;
+             else if (strcmp (name, ".bss") == 0)
+               h->esym.asym.sc = scBss;
+             else if (strcmp (name, ".sbss") == 0)
+               h->esym.asym.sc = scSBss;
+             else if (strcmp (name, ".init") == 0)
+               h->esym.asym.sc = scInit;
+             else if (strcmp (name, ".fini") == 0)
+               h->esym.asym.sc = scFini;
+             else
+               h->esym.asym.sc = scAbs;
+           }
+       }
 
       h->esym.asym.reserved = 0;
       h->esym.asym.index = indexNil;
@@ -2199,18 +2899,18 @@ elf64_alpha_output_extsym (h, data)
           || h->root.root.type == bfd_link_hash_defweak)
     {
       if (h->esym.asym.sc == scCommon)
-       h->esym.asym.sc = scBss;
+       h->esym.asym.sc = scBss;
       else if (h->esym.asym.sc == scSCommon)
-       h->esym.asym.sc = scSBss;
+       h->esym.asym.sc = scSBss;
 
       sec = h->root.root.u.def.section;
       output_section = sec->output_section;
       if (output_section != NULL)
-       h->esym.asym.value = (h->root.root.u.def.value
-                             + sec->output_offset
-                             + output_section->vma);
+       h->esym.asym.value = (h->root.root.u.def.value
+                             + sec->output_offset
+                             + output_section->vma);
       else
-       h->esym.asym.value = 0;
+       h->esym.asym.value = 0;
     }
   else if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
     {
@@ -2232,8 +2932,8 @@ elf64_alpha_output_extsym (h, data)
     }
 
   if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap,
-                                     h->root.root.root.string,
-                                     &h->esym))
+                                     h->root.root.root.string,
+                                     &h->esym))
     {
       einfo->failed = true;
       return false;
@@ -2241,35 +2941,103 @@ elf64_alpha_output_extsym (h, data)
 
   return true;
 }
-
-/* FIXME:  Create a runtime procedure table from the .mdebug section.
-
-static boolean
-mips_elf_create_procedure_table (handle, abfd, info, s, debug)
-     PTR handle;
-     bfd *abfd;
-     struct bfd_link_info *info;
-     asection *s;
-     struct ecoff_debug_info *debug;
-*/
 \f
-/* Handle dynamic relocations when doing an Alpha ELF link.  */
+/* Search for and possibly create a got entry.  */
 
-static boolean
-elf64_alpha_check_relocs (abfd, info, sec, relocs)
+static struct alpha_elf_got_entry *
+get_got_entry (abfd, h, r_type, r_symndx, r_addend)
      bfd *abfd;
-     struct bfd_link_info *info;
-     asection *sec;
-     const Elf_Internal_Rela *relocs;
+     struct alpha_elf_link_hash_entry *h;
+     unsigned long r_type, r_symndx;
+     bfd_vma r_addend;
 {
-  bfd *dynobj;
-  asection *sreloc;
+  struct alpha_elf_got_entry *gotent;
+  struct alpha_elf_got_entry **slot;
+
+  if (h)
+    slot = &h->got_entries;
+  else
+    {
+      /* This is a local .got entry -- record for merge.  */
+
+      struct alpha_elf_got_entry **local_got_entries;
+
+      local_got_entries = alpha_elf_tdata(abfd)->local_got_entries;
+      if (!local_got_entries)
+       {
+         bfd_size_type size;
+         Elf_Internal_Shdr *symtab_hdr;
+
+         symtab_hdr = &elf_tdata(abfd)->symtab_hdr;
+         size = symtab_hdr->sh_info;
+         size *= sizeof (struct alpha_elf_got_entry *);
+
+         local_got_entries
+           = (struct alpha_elf_got_entry **) bfd_alloc (abfd, size);
+         if (!local_got_entries)
+           return NULL;
+
+         memset (local_got_entries, 0, (size_t) size);
+         alpha_elf_tdata (abfd)->local_got_entries = local_got_entries;
+       }
+
+      slot = &local_got_entries[r_symndx];
+    }
+
+  for (gotent = *slot; gotent ; gotent = gotent->next)
+    if (gotent->gotobj == abfd
+       && gotent->reloc_type == r_type
+       && gotent->addend == r_addend)
+      break;
+
+  if (!gotent)
+    {
+      int entry_size;
+      bfd_size_type amt;
+
+      amt = sizeof (struct alpha_elf_got_entry);
+      gotent = (struct alpha_elf_got_entry *) bfd_alloc (abfd, amt);
+      if (!gotent)
+       return NULL;
+
+      gotent->gotobj = abfd;
+      gotent->addend = r_addend;
+      gotent->got_offset = -1;
+      gotent->use_count = 1;
+      gotent->reloc_type = r_type;
+      gotent->reloc_done = 0;
+      gotent->reloc_xlated = 0;
+
+      gotent->next = *slot;
+      *slot = gotent;
+
+      entry_size = alpha_got_entry_size (r_type);
+      alpha_elf_tdata (abfd)->total_got_size += entry_size;
+      if (!h)
+       alpha_elf_tdata(abfd)->local_got_size += entry_size;
+    }
+  else
+    gotent->use_count += 1;
+
+  return gotent;
+}
+
+/* Handle dynamic relocations when doing an Alpha ELF link.  */
+
+static boolean
+elf64_alpha_check_relocs (abfd, info, sec, relocs)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     asection *sec;
+     const Elf_Internal_Rela *relocs;
+{
+  bfd *dynobj;
+  asection *sreloc;
   const char *rel_sec_name;
   Elf_Internal_Shdr *symtab_hdr;
   struct alpha_elf_link_hash_entry **sym_hashes;
-  struct alpha_elf_got_entry **local_got_entries;
   const Elf_Internal_Rela *rel, *relend;
-  int got_created;
+  boolean got_created;
   bfd_size_type amt;
 
   if (info->relocateable)
@@ -2283,14 +3051,23 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
   rel_sec_name = NULL;
   symtab_hdr = &elf_tdata(abfd)->symtab_hdr;
   sym_hashes = alpha_elf_sym_hashes(abfd);
-  local_got_entries = alpha_elf_tdata(abfd)->local_got_entries;
-  got_created = 0;
+  got_created = false;
 
   relend = relocs + sec->reloc_count;
   for (rel = relocs; rel < relend; ++rel)
     {
+      enum {
+       NEED_GOT = 1,
+       NEED_GOT_ENTRY = 2,
+       NEED_DYNREL = 4
+      };
+
       unsigned long r_symndx, r_type;
       struct alpha_elf_link_hash_entry *h;
+      unsigned int gotent_flags;
+      boolean maybe_dynamic;
+      unsigned int need;
+      bfd_vma addend;
 
       r_symndx = ELF64_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -2305,125 +3082,40 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
 
          h->root.elf_link_hash_flags |= ELF_LINK_HASH_REF_REGULAR;
        }
+
+      /* We can only get preliminary data on whether a symbol is
+         locally or externally defined, as not all of the input files
+         have yet been processed.  Do something with what we know, as
+         this may help reduce memory usage and processing time later.  */
+      maybe_dynamic = false;
+      if (h && ((info->shared
+                && (!info->symbolic || info->allow_shlib_undefined))
+               || ! (h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)
+               || h->root.root.type == bfd_link_hash_defweak))
+        maybe_dynamic = true;
+
+      need = 0;
+      gotent_flags = 0;
       r_type = ELF64_R_TYPE (rel->r_info);
+      addend = rel->r_addend;
 
       switch (r_type)
        {
        case R_ALPHA_LITERAL:
-         {
-           struct alpha_elf_got_entry *gotent;
-           int flags = 0;
-
-           if (h)
-             {
-               /* Search for and possibly create a got entry.  */
-               for (gotent = h->got_entries; gotent ; gotent = gotent->next)
-                 if (gotent->gotobj == abfd &&
-                     gotent->addend == rel->r_addend)
-                   break;
-
-               if (!gotent)
-                 {
-                   amt = sizeof (struct alpha_elf_got_entry);
-                   gotent = ((struct alpha_elf_got_entry *)
-                             bfd_alloc (abfd, amt));
-                   if (!gotent)
-                     return false;
-
-                   gotent->gotobj = abfd;
-                   gotent->addend = rel->r_addend;
-                   gotent->got_offset = -1;
-                   gotent->flags = 0;
-                   gotent->use_count = 1;
-
-                   gotent->next = h->got_entries;
-                   h->got_entries = gotent;
-
-                   alpha_elf_tdata (abfd)->total_got_entries++;
-                 }
-               else
-                 gotent->use_count += 1;
-             }
-           else
-             {
-               /* This is a local .got entry -- record for merge.  */
-               if (!local_got_entries)
-                 {
-                   bfd_size_type size;
-                   size = symtab_hdr->sh_info;
-                   size *= sizeof (struct alpha_elf_got_entry *);
-
-                   local_got_entries = ((struct alpha_elf_got_entry **)
-                                        bfd_alloc (abfd, size));
-                   if (!local_got_entries)
-                     return false;
-
-                   memset (local_got_entries, 0, (size_t) size);
-                   alpha_elf_tdata (abfd)->local_got_entries =
-                     local_got_entries;
-                 }
-
-               for (gotent = local_got_entries[ELF64_R_SYM(rel->r_info)];
-                    gotent != NULL && gotent->addend != rel->r_addend;
-                    gotent = gotent->next)
-                 continue;
-               if (!gotent)
-                 {
-                   amt = sizeof (struct alpha_elf_got_entry);
-                   gotent = ((struct alpha_elf_got_entry *)
-                             bfd_alloc (abfd, amt));
-                   if (!gotent)
-                     return false;
-
-                   gotent->gotobj = abfd;
-                   gotent->addend = rel->r_addend;
-                   gotent->got_offset = -1;
-                   gotent->flags = 0;
-                   gotent->use_count = 1;
-
-                   gotent->next = local_got_entries[ELF64_R_SYM(rel->r_info)];
-                   local_got_entries[ELF64_R_SYM(rel->r_info)] = gotent;
-
-                   alpha_elf_tdata(abfd)->total_got_entries++;
-                   alpha_elf_tdata(abfd)->n_local_got_entries++;
-                 }
-               else
-                 gotent->use_count += 1;
-             }
-
-           /* Remember how this literal is used from its LITUSEs.
-              This will be important when it comes to decide if we can
-              create a .plt entry for a function symbol.  */
-           if (rel+1 < relend
-               && ELF64_R_TYPE (rel[1].r_info) == R_ALPHA_LITUSE)
-             {
-               do
-                 {
-                   ++rel;
-                   if (rel->r_addend >= 1 && rel->r_addend <= 3)
-                     flags |= 1 << rel->r_addend;
-                 }
-               while (rel+1 < relend &&
-                      ELF64_R_TYPE (rel[1].r_info) == R_ALPHA_LITUSE);
-             }
-           else
-             {
-               /* No LITUSEs -- presumably the address is not being
-                  loaded for nothing.  */
-               flags = ALPHA_ELF_LINK_HASH_LU_ADDR;
-             }
-
-           gotent->flags |= flags;
-           if (h)
-             {
-               /* Make a guess as to whether a .plt entry will be needed.  */
-               if ((h->flags |= flags) == ALPHA_ELF_LINK_HASH_LU_FUNC)
-                 h->root.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
-               else
-                 h->root.elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
-             }
-         }
-         /* FALLTHRU */
+         need = NEED_GOT | NEED_GOT_ENTRY;
+
+         /* Remember how this literal is used from its LITUSEs.
+            This will be important when it comes to decide if we can
+            create a .plt entry for a function symbol.  */
+         while (++rel < relend && ELF64_R_TYPE (rel->r_info) == R_ALPHA_LITUSE)
+           if (rel->r_addend >= 1 && rel->r_addend <= 5)
+             gotent_flags |= 1 << rel->r_addend;
+         --rel;
+
+         /* No LITUSEs -- presumably the address is used somehow.  */
+         if (gotent_flags == 0)
+           gotent_flags = ALPHA_ELF_LINK_HASH_LU_ADDR;
+         break;
 
        case R_ALPHA_GPDISP:
        case R_ALPHA_GPREL16:
@@ -2431,9 +3123,38 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
        case R_ALPHA_GPRELHIGH:
        case R_ALPHA_GPRELLOW:
        case R_ALPHA_BRSGP:
-         /* We don't actually use the .got here, but the sections must
-            be created before the linker maps input sections to output
-            sections.  */
+         need = NEED_GOT;
+         break;
+
+       case R_ALPHA_REFLONG:
+       case R_ALPHA_REFQUAD:
+         if (info->shared || maybe_dynamic)
+           need = NEED_DYNREL;
+         break;
+
+       case R_ALPHA_TLSGD:
+       case R_ALPHA_TLSLDM:
+       case R_ALPHA_GOTDTPREL:
+         need = NEED_GOT | NEED_GOT_ENTRY;
+         break;
+
+       case R_ALPHA_GOTTPREL:
+         need = NEED_GOT | NEED_GOT_ENTRY;
+         gotent_flags = ALPHA_ELF_LINK_HASH_TLS_IE;
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         break;
+
+       case R_ALPHA_TPREL64:
+         if (info->shared || maybe_dynamic)
+           need = NEED_DYNREL;
+         if (info->shared)
+           info->flags |= DF_STATIC_TLS;
+         break;
+       }
+
+      if (need & NEED_GOT)
+       {
          if (!got_created)
            {
              if (!elf64_alpha_create_got_section (abfd, info))
@@ -2447,17 +3168,36 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
 
              got_created = 1;
            }
-         break;
+       }
 
-       case R_ALPHA_SREL16:
-       case R_ALPHA_SREL32:
-       case R_ALPHA_SREL64:
-         if (h == NULL)
-           break;
-         /* FALLTHRU */
+      if (need & NEED_GOT_ENTRY)
+       {
+         struct alpha_elf_got_entry *gotent;
 
-       case R_ALPHA_REFLONG:
-       case R_ALPHA_REFQUAD:
+         gotent = get_got_entry (abfd, h, r_type, r_symndx, addend);
+         if (!gotent)
+           return false;
+
+         if (gotent_flags)
+           {
+             gotent->flags |= gotent_flags;
+             if (h)
+               {
+                 gotent_flags |= h->flags;
+                 h->flags = gotent_flags;
+
+                 /* Make a guess as to whether a .plt entry is needed.  */
+                 if ((gotent_flags & ALPHA_ELF_LINK_HASH_LU_FUNC)
+                     && !(gotent_flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC))
+                   h->root.elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+                 else
+                   h->root.elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+               }
+           }
+       }
+
+      if (need & NEED_DYNREL)
+       {
          if (rel_sec_name == NULL)
            {
              rel_sec_name = (bfd_elf_string_from_elf_section
@@ -2527,15 +3267,15 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
              else
                rent->count++;
            }
-         else if (info->shared && (sec->flags & SEC_ALLOC))
+         else if (info->shared)
            {
              /* If this is a shared library, and the section is to be
                 loaded into memory, we need a RELATIVE reloc.  */
              sreloc->_raw_size += sizeof (Elf64_External_Rela);
-             if (sec->flags & SEC_READONLY)
+             if ((sec->flags & (SEC_READONLY | SEC_ALLOC))
+                 == (SEC_READONLY | SEC_ALLOC))
                info->flags |= DF_TEXTREL;
            }
-         break;
        }
     }
 
@@ -2567,7 +3307,8 @@ elf64_alpha_adjust_dynamic_symbol (info, h)
       && ((h->type == STT_FUNC
           && !(ah->flags & ALPHA_ELF_LINK_HASH_LU_ADDR))
          || (h->type == STT_NOTYPE
-             && ah->flags == ALPHA_ELF_LINK_HASH_LU_FUNC))
+             && (ah->flags & ALPHA_ELF_LINK_HASH_LU_FUNC)
+             && !(ah->flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC)))
       /* Don't prevent otherwise valid programs from linking by attempting
         to create a new .got entry somewhere.  A Correct Solution would be
         to add a new .got section to a new object file and let it be merged
@@ -2664,8 +3405,13 @@ elf64_alpha_merge_ind_symbols (hi, dummy)
        {
          gin = gi->next;
          for (gs = gsh; gs ; gs = gs->next)
-           if (gi->gotobj == gs->gotobj && gi->addend == gs->addend)
-             goto got_found;
+           if (gi->gotobj == gs->gotobj
+               && gi->reloc_type == gs->reloc_type
+               && gi->addend == gs->addend)
+             {
+               gi->use_count += gs->use_count;
+               goto got_found;
+             }
          gi->next = hs->got_entries;
          hs->got_entries = gi;
        got_found:;
@@ -2707,15 +3453,15 @@ static boolean
 elf64_alpha_can_merge_gots (a, b)
      bfd *a, *b;
 {
-  int total = alpha_elf_tdata (a)->total_got_entries;
+  int total = alpha_elf_tdata (a)->total_got_size;
   bfd *bsub;
 
   /* Trivial quick fallout test.  */
-  if (total + alpha_elf_tdata (b)->total_got_entries <= MAX_GOT_ENTRIES)
+  if (total + alpha_elf_tdata (b)->total_got_size <= MAX_GOT_SIZE)
     return true;
 
   /* By their nature, local .got entries cannot be merged.  */
-  if ((total += alpha_elf_tdata (b)->n_local_got_entries) > MAX_GOT_ENTRIES)
+  if ((total += alpha_elf_tdata (b)->local_got_size) > MAX_GOT_SIZE)
     return false;
 
   /* Failing the common trivial comparison, we must effectively
@@ -2746,10 +3492,13 @@ elf64_alpha_can_merge_gots (a, b)
                continue;
 
              for (ae = h->got_entries; ae ; ae = ae->next)
-               if (ae->gotobj == a && ae->addend == be->addend)
+               if (ae->gotobj == a
+                   && ae->reloc_type == be->reloc_type
+                   && ae->addend == be->addend)
                  goto global_found;
 
-             if (++total > MAX_GOT_ENTRIES)
+             total += alpha_got_entry_size (be->reloc_type);
+             if (total > MAX_GOT_SIZE)
                return false;
            global_found:;
            }
@@ -2765,14 +3514,14 @@ static void
 elf64_alpha_merge_gots (a, b)
      bfd *a, *b;
 {
-  int total = alpha_elf_tdata (a)->total_got_entries;
+  int total = alpha_elf_tdata (a)->total_got_size;
   bfd *bsub;
 
   /* Remember local expansion.  */
   {
-    int e = alpha_elf_tdata (b)->n_local_got_entries;
+    int e = alpha_elf_tdata (b)->local_got_size;
     total += e;
-    alpha_elf_tdata (a)->n_local_got_entries += e;
+    alpha_elf_tdata (a)->local_got_size += e;
   }
 
   for (bsub = b; bsub ; bsub = alpha_elf_tdata (bsub)->in_got_link_next)
@@ -2822,7 +3571,9 @@ elf64_alpha_merge_gots (a, b)
                continue;
 
              for (ae = *start; ae ; ae = ae->next)
-               if (ae->gotobj == a && ae->addend == be->addend)
+               if (ae->gotobj == a
+                   && ae->reloc_type == be->reloc_type
+                   && ae->addend == be->addend)
                  {
                    ae->flags |= be->flags;
                    ae->use_count += be->use_count;
@@ -2830,7 +3581,7 @@ elf64_alpha_merge_gots (a, b)
                    goto global_found;
                  }
              be->gotobj = a;
-             total += 1;
+             total += alpha_got_entry_size (be->reloc_type);
 
            global_found:;
            }
@@ -2838,7 +3589,7 @@ elf64_alpha_merge_gots (a, b)
 
       alpha_elf_tdata (bsub)->gotobj = a;
     }
-  alpha_elf_tdata (a)->total_got_entries = total;
+  alpha_elf_tdata (a)->total_got_size = total;
 
   /* Merge the two in_got chains.  */
   {
@@ -2861,6 +3612,9 @@ elf64_alpha_calc_got_offsets_for_symbol (h, arg)
 {
   struct alpha_elf_got_entry *gotent;
 
+  if (h->root.root.type == bfd_link_hash_warning)
+    h = (struct alpha_elf_link_hash_entry *) h->root.root.u.i.link;
+
   for (gotent = h->got_entries; gotent; gotent = gotent->next)
     if (gotent->use_count > 0)
       {
@@ -2868,7 +3622,7 @@ elf64_alpha_calc_got_offsets_for_symbol (h, arg)
          = &alpha_elf_tdata (gotent->gotobj)->got->_raw_size;
 
        gotent->got_offset = *plge;
-       *plge += 8;
+       *plge += alpha_got_entry_size (gotent->reloc_type);
       }
 
   return true;
@@ -2910,7 +3664,7 @@ elf64_alpha_calc_got_offsets (info)
              if (gotent->use_count > 0)
                {
                  gotent->got_offset = got_offset;
-                 got_offset += 8;
+                 got_offset += alpha_got_entry_size (gotent->reloc_type);
                }
        }
 
@@ -2922,8 +3676,7 @@ elf64_alpha_calc_got_offsets (info)
 /* Constructs the gots.  */
 
 static boolean
-elf64_alpha_size_got_sections (output_bfd, info)
-     bfd *output_bfd ATTRIBUTE_UNUSED;
+elf64_alpha_size_got_sections (info)
      struct bfd_link_info *info;
 {
   bfd *i, *got_list, *cur_got_obj = NULL;
@@ -2944,13 +3697,13 @@ elf64_alpha_size_got_sections (output_bfd, info)
          /* We are assuming no merging has yet ocurred.  */
          BFD_ASSERT (this_got == i);
 
-          if (alpha_elf_tdata (this_got)->total_got_entries > MAX_GOT_ENTRIES)
+          if (alpha_elf_tdata (this_got)->total_got_size > MAX_GOT_SIZE)
            {
              /* Yikes! A single object file has too many entries.  */
              (*_bfd_error_handler)
                (_("%s: .got subsegment exceeds 64K (size %d)"),
                 bfd_archive_filename (i),
-                alpha_elf_tdata (this_got)->total_got_entries * 8);
+                alpha_elf_tdata (this_got)->total_got_size);
              return false;
            }
 
@@ -2997,9 +3750,80 @@ elf64_alpha_size_got_sections (output_bfd, info)
   return true;
 }
 
+/* Called from relax_section to rebuild the PLT in light of
+   potential changes in the function's status.  */
+
+static boolean
+elf64_alpha_size_plt_section (info)
+     struct bfd_link_info *info;
+{
+  asection *splt, *spltrel;
+  unsigned long entries;
+  bfd *dynobj;
+
+  dynobj = elf_hash_table(info)->dynobj;
+  splt = bfd_get_section_by_name(dynobj, ".plt");
+  if (splt == NULL)
+    return true;
+
+  splt->_raw_size = 0;
+
+  alpha_elf_link_hash_traverse (alpha_elf_hash_table (info),
+                               elf64_alpha_size_plt_section_1, splt);
+
+  splt->_cooked_size = splt->_raw_size;
+
+  /* Every plt entry requires a JMP_SLOT relocation.  */
+  spltrel = bfd_get_section_by_name (dynobj, ".rela.plt");
+  if (splt->_raw_size)
+    entries = (splt->_raw_size - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
+  else
+    entries = 0;
+  spltrel->_raw_size = entries * sizeof (Elf64_External_Rela);
+  spltrel->_cooked_size = spltrel->_raw_size;
+
+  return true;
+}
+
+static boolean
+elf64_alpha_size_plt_section_1 (h, data)
+     struct alpha_elf_link_hash_entry *h;
+     PTR data;
+{
+  asection *splt = (asection *) data;
+  struct alpha_elf_got_entry *gotent;
+
+  /* If we didn't need an entry before, we still don't.  */
+  if (!(h->root.elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT))
+    return true;
+
+  /* There must still be a LITERAL got entry for the function.  */
+  for (gotent = h->got_entries; gotent ; gotent = gotent->next)
+    if (gotent->reloc_type == R_ALPHA_LITERAL
+       && gotent->use_count > 0)
+      break;
+
+  /* If there is, reset the PLT offset.  If not, there's no longer
+     a need for the PLT entry.  */
+  if (gotent)
+    {
+      if (splt->_raw_size == 0)
+       splt->_raw_size = PLT_HEADER_SIZE;
+      h->root.plt.offset = splt->_raw_size;
+      splt->_raw_size += PLT_ENTRY_SIZE;
+    }
+  else
+    {
+      h->root.elf_link_hash_flags &= ~ELF_LINK_HASH_NEEDS_PLT;
+      h->root.plt.offset = -1;
+    }
+
+  return true;
+}
+
 static boolean
 elf64_alpha_always_size_sections (output_bfd, info)
-     bfd *output_bfd;
+     bfd *output_bfd ATTRIBUTE_UNUSED;
      struct bfd_link_info *info;
 {
   bfd *i;
@@ -3012,7 +3836,7 @@ elf64_alpha_always_size_sections (output_bfd, info)
                                elf64_alpha_merge_ind_symbols,
                                NULL);
 
-  if (!elf64_alpha_size_got_sections (output_bfd, info))
+  if (!elf64_alpha_size_got_sections (info))
     return false;
 
   /* Allocate space for all of the .got subsections.  */
@@ -3031,6 +3855,40 @@ elf64_alpha_always_size_sections (output_bfd, info)
   return true;
 }
 
+/* The number of dynamic relocations required by a static relocation.  */
+
+static int
+alpha_dynamic_entries_for_reloc (r_type, dynamic, shared)
+     int r_type, dynamic, shared;
+{
+  switch (r_type)
+    {
+    /* May appear in GOT entries.  */
+    case R_ALPHA_TLSGD:
+      return (dynamic ? 2 : shared ? 1 : 0);
+    case R_ALPHA_TLSLDM:
+      return shared;
+    case R_ALPHA_LITERAL:
+      return dynamic || shared;
+    case R_ALPHA_GOTDTPREL:
+    case R_ALPHA_GOTTPREL:
+      return dynamic;
+
+    /* May appear in data sections.  */
+    case R_ALPHA_REFLONG:
+    case R_ALPHA_REFQUAD:
+      return dynamic || shared;
+    case R_ALPHA_SREL64:
+    case R_ALPHA_TPREL64:
+      return dynamic;
+
+    /* Everything else is illegal.  We'll issue an error during
+       relocate_section.  */
+    default:
+      return 0;
+    }
+}
+
 /* Work out the sizes of the dynamic relocation entries.  */
 
 static boolean
@@ -3038,6 +3896,13 @@ elf64_alpha_calc_dynrel_sizes (h, info)
      struct alpha_elf_link_hash_entry *h;
      struct bfd_link_info *info;
 {
+  boolean dynamic;
+  struct alpha_elf_reloc_entry *relent;
+  unsigned long entries;
+
+  if (h->root.root.type == bfd_link_hash_warning)
+    h = (struct alpha_elf_link_hash_entry *) h->root.root.u.i.link;
+
   /* If the symbol was defined as a common symbol in a regular object
      file, and there was no definition in any dynamic object, then the
      linker will have allocated space for the symbol in a common
@@ -3053,51 +3918,125 @@ elf64_alpha_calc_dynrel_sizes (h, info)
       && (h->root.root.type == bfd_link_hash_defined
          || h->root.root.type == bfd_link_hash_defweak)
       && !(h->root.root.u.def.section->owner->flags & DYNAMIC))
-    {
-      h->root.elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
-    }
+    h->root.elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
 
   /* If the symbol is dynamic, we'll need all the relocations in their
      natural form.  If this is a shared object, and it has been forced
      local, we'll need the same number of RELATIVE relocations.  */
 
-  if (alpha_elf_dynamic_symbol_p (&h->root, info) || info->shared)
+  dynamic = alpha_elf_dynamic_symbol_p (&h->root, info);
+
+  for (relent = h->reloc_entries; relent; relent = relent->next)
     {
-      struct alpha_elf_reloc_entry *relent;
-      bfd *dynobj;
-      struct alpha_elf_got_entry *gotent;
-      bfd_size_type count;
-      asection *srel;
+      entries = alpha_dynamic_entries_for_reloc (relent->rtype, dynamic,
+                                                info->shared);
+      if (entries)
+       {
+         relent->srel->_raw_size +=
+           entries * sizeof (Elf64_External_Rela) * relent->count;
+         if (relent->reltext)
+           info->flags |= DT_TEXTREL;
+       }
+    }
 
-      for (relent = h->reloc_entries; relent; relent = relent->next)
-       if (relent->rtype == R_ALPHA_REFLONG
-           || relent->rtype == R_ALPHA_REFQUAD)
-         {
-           relent->srel->_raw_size +=
-             sizeof (Elf64_External_Rela) * relent->count;
-           if (relent->reltext)
-             info->flags |= DT_TEXTREL;
-         }
+  return true;
+}
 
-      dynobj = elf_hash_table(info)->dynobj;
-      count = 0;
+/* Set the sizes of the dynamic relocation sections.  */
 
-      for (gotent = h->got_entries; gotent ; gotent = gotent->next)
-       count++;
+static boolean
+elf64_alpha_size_rela_got_section (info)
+     struct bfd_link_info *info;
+{
+  unsigned long entries;
+  bfd *i, *dynobj;
+  asection *srel;
 
-      /* If we are using a .plt entry, subtract one, as the first
-        reference uses a .rela.plt entry instead.  */
-      if (h->root.plt.offset != MINUS_ONE)
-       count--;
+  /* Shared libraries often require RELATIVE relocs, and some relocs
+     require attention for the main application as well.  */
 
-      if (count > 0)
+  entries = 0;
+  for (i = alpha_elf_hash_table(info)->got_list;
+       i ; i = alpha_elf_tdata(i)->got_link_next)
+    {
+      bfd *j;
+
+      for (j = i; j ; j = alpha_elf_tdata(j)->in_got_link_next)
        {
-         srel = bfd_get_section_by_name (dynobj, ".rela.got");
-         BFD_ASSERT (srel != NULL);
-         srel->_raw_size += sizeof (Elf64_External_Rela) * count;
+         struct alpha_elf_got_entry **local_got_entries, *gotent;
+         int k, n;
+
+         local_got_entries = alpha_elf_tdata(j)->local_got_entries;
+         if (!local_got_entries)
+           continue;
+
+         for (k = 0, n = elf_tdata(j)->symtab_hdr.sh_info; k < n; ++k)
+           for (gotent = local_got_entries[k];
+                gotent ; gotent = gotent->next)
+             if (gotent->use_count > 0)
+               entries += (alpha_dynamic_entries_for_reloc
+                           (gotent->reloc_type, 0, info->shared));
        }
     }
 
+  dynobj = elf_hash_table(info)->dynobj;
+  srel = bfd_get_section_by_name (dynobj, ".rela.got");
+  if (!srel)
+    {
+      BFD_ASSERT (entries == 0);
+      return true;
+    }
+  srel->_raw_size = sizeof (Elf64_External_Rela) * entries;
+
+  /* Now do the non-local symbols.  */
+  alpha_elf_link_hash_traverse (alpha_elf_hash_table (info),
+                               elf64_alpha_size_rela_got_1, info);
+
+  srel->_cooked_size = srel->_raw_size;
+
+  return true;
+}
+
+/* Subroutine of elf64_alpha_size_rela_got_section for doing the
+   global symbols.  */
+
+static boolean
+elf64_alpha_size_rela_got_1 (h, info)
+     struct alpha_elf_link_hash_entry *h;
+     struct bfd_link_info *info;
+{
+  boolean dynamic;
+  struct alpha_elf_got_entry *gotent;
+  unsigned long entries;
+
+  if (h->root.root.type == bfd_link_hash_warning)
+    h = (struct alpha_elf_link_hash_entry *) h->root.root.u.i.link;
+
+  /* If the symbol is dynamic, we'll need all the relocations in their
+     natural form.  If this is a shared object, and it has been forced
+     local, we'll need the same number of RELATIVE relocations.  */
+
+  dynamic = alpha_elf_dynamic_symbol_p (&h->root, info);
+
+  entries = 0;
+  for (gotent = h->got_entries; gotent ; gotent = gotent->next)
+    if (gotent->use_count > 0)
+      entries += alpha_dynamic_entries_for_reloc (gotent->reloc_type,
+                                                 dynamic, info->shared);
+
+  /* If we are using a .plt entry, subtract one, as the first
+     reference uses a .rela.plt entry instead.  */
+  if (h->root.plt.offset != MINUS_ONE)
+    entries--;
+
+  if (entries > 0)
+    {
+      bfd *dynobj = elf_hash_table(info)->dynobj;
+      asection *srel = bfd_get_section_by_name (dynobj, ".rela.got");
+      BFD_ASSERT (srel != NULL);
+      srel->_raw_size += sizeof (Elf64_External_Rela) * entries;
+    }
+
   return true;
 }
 
@@ -3131,27 +4070,9 @@ elf64_alpha_size_dynamic_sections (output_bfd, info)
         collected information in check_relocs that we can now apply to
         size the dynamic relocation sections.  */
       alpha_elf_link_hash_traverse (alpha_elf_hash_table (info),
-                                   elf64_alpha_calc_dynrel_sizes,
-                                   info);
-
-      /* When building shared libraries, each local .got entry needs a
-        RELATIVE reloc.  */
-      if (info->shared)
-       {
-         bfd *i;
-         asection *srel;
-         bfd_size_type count;
+                                   elf64_alpha_calc_dynrel_sizes, info);
 
-         srel = bfd_get_section_by_name (dynobj, ".rela.got");
-         BFD_ASSERT (srel != NULL);
-
-         for (i = alpha_elf_hash_table(info)->got_list, count = 0;
-              i != NULL;
-              i = alpha_elf_tdata(i)->got_link_next)
-           count += alpha_elf_tdata(i)->n_local_got_entries;
-
-         srel->_raw_size += count * sizeof (Elf64_External_Rela);
-       }
+      elf64_alpha_size_rela_got_section (info);
     }
   /* else we're not dynamic and by definition we don't need such things.  */
 
@@ -3255,6 +4176,71 @@ elf64_alpha_size_dynamic_sections (output_bfd, info)
   return true;
 }
 
+/* Relocate an Alpha ELF section for a relocatable link.
+
+   We don't have to change anything unless the reloc is against a section
+   symbol, in which case we have to adjust according to where the section
+   symbol winds up in the output section.  */
+
+static boolean
+elf64_alpha_relocate_section_r (output_bfd, info, input_bfd, input_section,
+                               contents, relocs, local_syms, local_sections)
+     bfd *output_bfd ATTRIBUTE_UNUSED;
+     struct bfd_link_info *info ATTRIBUTE_UNUSED;
+     bfd *input_bfd;
+     asection *input_section;
+     bfd_byte *contents ATTRIBUTE_UNUSED;
+     Elf_Internal_Rela *relocs;
+     Elf_Internal_Sym *local_syms;
+     asection **local_sections;
+{
+  unsigned long symtab_hdr_sh_info;
+  Elf_Internal_Rela *rel;
+  Elf_Internal_Rela *relend;
+  boolean ret_val = true;
+
+  symtab_hdr_sh_info = elf_tdata (input_bfd)->symtab_hdr.sh_info;
+
+  relend = relocs + input_section->reloc_count;
+  for (rel = relocs; rel < relend; rel++)
+    {
+      unsigned long r_symndx;
+      Elf_Internal_Sym *sym;
+      asection *sec;
+      unsigned long r_type;
+
+      r_type = ELF64_R_TYPE(rel->r_info);
+      if (r_type >= R_ALPHA_max)
+       {
+         (*_bfd_error_handler)
+           (_("%s: unknown relocation type %d"),
+            bfd_archive_filename (input_bfd), (int)r_type);
+         bfd_set_error (bfd_error_bad_value);
+         ret_val = false;
+         continue;
+       }
+
+      r_symndx = ELF64_R_SYM(rel->r_info);
+
+      /* The symbol associated with GPDISP and LITUSE is
+        immaterial.  Only the addend is significant.  */
+      if (r_type == R_ALPHA_GPDISP || r_type == R_ALPHA_LITUSE)
+       continue;
+
+      if (r_symndx < symtab_hdr_sh_info)
+       {
+         sym = local_syms + r_symndx;
+         if (ELF_ST_TYPE(sym->st_info) == STT_SECTION)
+           {
+             sec = local_sections[r_symndx];
+             rel->r_addend += sec->output_offset + sym->st_value;
+           }
+       }
+    }
+
+  return ret_val;
+}
+
 /* Relocate an Alpha ELF section.  */
 
 static boolean
@@ -3272,22 +4258,39 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
-  asection *sec, *sgot, *srel, *srelgot;
+  struct elf_link_tls_segment *tls_segment;
+  asection *sgot, *srel, *srelgot;
   bfd *dynobj, *gotobj;
-  bfd_vma gp;
-  boolean ret_val = true;
+  bfd_vma gp, tp_base, dtp_base;
+  struct alpha_elf_got_entry **local_got_entries;
+  boolean ret_val;
+  const char *section_name;
+
+  /* Handle relocatable links with a smaller loop.  */
+  if (info->relocateable)
+    return elf64_alpha_relocate_section_r (output_bfd, info, input_bfd,
+                                          input_section, contents, relocs,
+                                          local_syms, local_sections);
+
+  /* This is a final link.  */
+
+  ret_val = true;
 
-  srelgot = srel = NULL;
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+
   dynobj = elf_hash_table (info)->dynobj;
   if (dynobj)
-    {
-      srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
-    }
+    srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
+  else
+    srelgot = NULL;
+
+  section_name = (bfd_elf_string_from_elf_section
+                 (input_bfd, elf_elfheader(input_bfd)->e_shstrndx,
+                  elf_section_data(input_section)->rel_hdr.sh_name));
+  BFD_ASSERT(section_name != NULL);
+  srel = bfd_get_section_by_name (dynobj, section_name);
 
   /* Find the gp value for this input bfd.  */
-  sgot = NULL;
-  gp = 0;
   gotobj = alpha_elf_tdata (input_bfd)->gotobj;
   if (gotobj)
     {
@@ -3301,66 +4304,97 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
          _bfd_set_gp_value (gotobj, gp);
        }
     }
+  else
+    {
+      sgot = NULL;
+      gp = 0;
+    }
+
+  local_got_entries = alpha_elf_tdata(input_bfd)->local_got_entries;
+
+  tls_segment = elf_hash_table (info)->tls_segment;
+  if (tls_segment)
+    {
+      dtp_base = alpha_get_dtprel_base (tls_segment);
+      tp_base = alpha_get_tprel_base (tls_segment);
+    }
+  else
+    dtp_base = tp_base = 0;
 
-  rel = relocs;
   relend = relocs + input_section->reloc_count;
-  for (; rel < relend; rel++)
+  for (rel = relocs; rel < relend; rel++)
     {
-      int r_type;
+      struct alpha_elf_link_hash_entry *h = NULL;
+      struct alpha_elf_got_entry *gotent;
+      bfd_reloc_status_type r;
       reloc_howto_type *howto;
       unsigned long r_symndx;
-      struct alpha_elf_link_hash_entry *h;
-      Elf_Internal_Sym *sym;
-      bfd_vma relocation;
+      Elf_Internal_Sym *sym = NULL;
+      asection *sec = NULL;
+      bfd_vma value;
       bfd_vma addend;
-      bfd_reloc_status_type r;
+      boolean dynamic_symbol_p;
+      boolean undef_weak_ref = false;
+      unsigned long r_type;
 
       r_type = ELF64_R_TYPE(rel->r_info);
-      if (r_type < 0 || r_type >= (int) R_ALPHA_max)
+      if (r_type >= R_ALPHA_max)
        {
+         (*_bfd_error_handler)
+           (_("%s: unknown relocation type %d"),
+            bfd_archive_filename (input_bfd), (int)r_type);
          bfd_set_error (bfd_error_bad_value);
-         return false;
+         ret_val = false;
+         continue;
        }
-      howto = elf64_alpha_howto_table + r_type;
 
+      howto = elf64_alpha_howto_table + r_type;
       r_symndx = ELF64_R_SYM(rel->r_info);
 
-      if (info->relocateable)
+      if (r_symndx < symtab_hdr->sh_info)
        {
-         /* This is a relocateable link.  We don't have to change
-            anything, unless the reloc is against a section symbol,
-            in which case we have to adjust according to where the
-            section symbol winds up in the output section.  */
-
-         /* The symbol associated with GPDISP and LITUSE is
-            immaterial.  Only the addend is significant.  */
-         if (r_type == R_ALPHA_GPDISP || r_type == R_ALPHA_LITUSE)
-           continue;
+         sym = local_syms + r_symndx;
+         sec = local_sections[r_symndx];
+         value = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
 
-         if (r_symndx < symtab_hdr->sh_info)
+         if (local_got_entries)
+           gotent = local_got_entries[r_symndx];
+         else
+           gotent = NULL;
+
+         /* Need to adjust local GOT entries' addends for SEC_MERGE
+            unless it has been done already.  */
+         if ((sec->flags & SEC_MERGE)
+             && ELF_ST_TYPE (sym->st_info) == STT_SECTION
+             && (elf_section_data (sec)->sec_info_type
+                 == ELF_INFO_TYPE_MERGE)
+             && gotent
+             && !gotent->reloc_xlated)
            {
-             sym = local_syms + r_symndx;
-             if (ELF_ST_TYPE(sym->st_info) == STT_SECTION)
+             struct alpha_elf_got_entry *ent;
+             asection *msec;
+
+             for (ent = gotent; ent; ent = ent->next)
                {
-                 sec = local_sections[r_symndx];
-                 rel->r_addend += sec->output_offset + sym->st_value;
+                 ent->reloc_xlated = 1;
+                 if (ent->use_count == 0)
+                   continue;
+                 msec = sec;
+                 ent->addend =
+                   _bfd_merged_section_offset (output_bfd, &msec,
+                                               elf_section_data (sec)->
+                                                 sec_info,
+                                               sym->st_value + ent->addend,
+                                               (bfd_vma) 0);
+                 ent->addend -= sym->st_value;
+                 ent->addend += msec->output_section->vma
+                                + msec->output_offset
+                                - sec->output_section->vma
+                                - sec->output_offset;
                }
            }
 
-         continue;
-       }
-
-      /* This is a final link.  */
-
-      h = NULL;
-      sym = NULL;
-      sec = NULL;
-
-      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);
+         dynamic_symbol_p = false;
        }
       else
        {
@@ -3370,27 +4404,32 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
                 || h->root.root.type == bfd_link_hash_warning)
            h = (struct alpha_elf_link_hash_entry *)h->root.root.u.i.link;
 
+         value = 0;
          if (h->root.root.type == bfd_link_hash_defined
              || h->root.root.type == bfd_link_hash_defweak)
            {
              sec = h->root.root.u.def.section;
 
-             if (sec->output_section == NULL)
-               relocation = 0;
-             else
-               {
-                 relocation = (h->root.root.u.def.value
-                               + sec->output_section->vma
-                               + sec->output_offset);
-               }
+             /* Detect the cases that sym_sec->output_section is
+                expected to be NULL -- all cases in which the symbol
+                is defined in another shared module.  This includes
+                PLT relocs for which we've created a PLT entry and
+                other relocs for which we're prepared to create
+                dynamic relocations.  */
+             /* ??? Just accept it NULL and continue.  */
+
+             if (sec->output_section != NULL)
+               value = (h->root.root.u.def.value
+                        + sec->output_section->vma
+                             + sec->output_offset);
            }
          else if (h->root.root.type == bfd_link_hash_undefweak)
-           relocation = 0;
+           undef_weak_ref = true;
          else if (info->shared
                   && (!info->symbolic || info->allow_shlib_undefined)
                   && !info->no_undefined
                   && ELF_ST_VISIBILITY (h->root.other) == STV_DEFAULT)
-           relocation = 0;
+           ;
          else
            {
              if (!((*info->callbacks->undefined_symbol)
@@ -3398,11 +4437,24 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
                     input_section, rel->r_offset,
                     (!info->shared || info->no_undefined
                      || ELF_ST_VISIBILITY (h->root.other)))))
-               ret_val = false;
-             relocation = 0;
+               return false;
+             ret_val = false;
+             continue;
            }
+
+          dynamic_symbol_p = alpha_elf_dynamic_symbol_p (&h->root, info);
+         gotent = h->got_entries;
        }
+
       addend = rel->r_addend;
+      value += addend;
+
+      /* Search for the proper got entry.  */
+      for (; gotent ; gotent = gotent->next)
+       if (gotent->gotobj == gotobj
+           && gotent->reloc_type == r_type
+           && gotent->addend == addend)
+         break;
 
       switch (r_type)
        {
@@ -3412,124 +4464,66 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
 
            BFD_ASSERT(gp != 0);
 
-           relocation = (input_section->output_section->vma
-                         + input_section->output_offset
-                         + rel->r_offset);
+           value = (input_section->output_section->vma
+                    + input_section->output_offset
+                    + rel->r_offset);
 
-           p_ldah = contents + rel->r_offset - input_section->vma;
+           p_ldah = contents + rel->r_offset;
            p_lda = p_ldah + rel->r_addend;
 
-           r = elf64_alpha_do_reloc_gpdisp (input_bfd, gp - relocation,
+           r = elf64_alpha_do_reloc_gpdisp (input_bfd, gp - value,
                                             p_ldah, p_lda);
          }
          break;
 
        case R_ALPHA_LITERAL:
-         {
-           struct alpha_elf_got_entry *gotent;
-           boolean dynamic_symbol;
-
-           BFD_ASSERT(sgot != NULL);
-           BFD_ASSERT(gp != 0);
-
-           if (h != NULL)
-             {
-               gotent = h->got_entries;
-               dynamic_symbol = alpha_elf_dynamic_symbol_p (&h->root, info);
-             }
-           else
-             {
-               gotent = (alpha_elf_tdata(input_bfd)->
-                         local_got_entries[r_symndx]);
-               dynamic_symbol = false;
-
-               /* Need to adjust local GOT entries' addends for SEC_MERGE
-                  unless it has been done already.  */
-               if ((sec->flags & SEC_MERGE)
-                   && ELF_ST_TYPE (sym->st_info) == STT_SECTION
-                   && (elf_section_data (sec)->sec_info_type
-                       == ELF_INFO_TYPE_MERGE)
-                   && (gotent->flags & ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED) == 0)
-                 {
-                   struct alpha_elf_got_entry *ent;
-                   asection *msec;
-
-                   for (ent = gotent; ent; ent = ent->next)
-                     {
-                       ent->flags |= ALPHA_ELF_GOT_ENTRY_RELOCS_XLATED;
-                       if (ent->use_count == 0)
-                         continue;
-                       msec = sec;
-                       ent->addend =
-                         _bfd_merged_section_offset (output_bfd, &msec,
-                                                     elf_section_data (sec)->
-                                                     sec_info,
-                                                     sym->st_value
-                                                     + ent->addend,
-                                                     (bfd_vma) 0);
-                       ent->addend -= sym->st_value;
-                       ent->addend += msec->output_section->vma
-                                      + msec->output_offset
-                                      - sec->output_section->vma
-                                      - sec->output_offset;
-                     }
-                 }
-             }
+         BFD_ASSERT(sgot != NULL);
+         BFD_ASSERT(gp != 0);
+         BFD_ASSERT(gotent != NULL);
+         BFD_ASSERT(gotent->use_count >= 1);
 
-           BFD_ASSERT(gotent != NULL);
+         if (!gotent->reloc_done)
+           {
+             gotent->reloc_done = 1;
 
-           while (gotent->gotobj != gotobj || gotent->addend != addend)
-             gotent = gotent->next;
+             bfd_put_64 (output_bfd, value,
+                         sgot->contents + gotent->got_offset);
 
-           BFD_ASSERT(gotent->use_count >= 1);
+             /* If the symbol has been forced local, output a
+                RELATIVE reloc, otherwise it will be handled in
+                finish_dynamic_symbol.  */
+             if (info->shared && !dynamic_symbol_p)
+               {
+                 Elf_Internal_Rela outrel;
 
-           /* Initialize the .got entry's value.  */
-           if (!(gotent->flags & ALPHA_ELF_GOT_ENTRY_RELOCS_DONE))
-             {
-               bfd_put_64 (output_bfd, relocation + addend,
-                           sgot->contents + gotent->got_offset);
+                 BFD_ASSERT(srelgot != NULL);
 
-               /* If the symbol has been forced local, output a
-                  RELATIVE reloc, otherwise it will be handled in
-                  finish_dynamic_symbol.  */
-               if (info->shared && !dynamic_symbol)
-                 {
-                   Elf_Internal_Rela outrel;
-
-                   BFD_ASSERT(srelgot != NULL);
-
-                   outrel.r_offset = (sgot->output_section->vma
-                                      + sgot->output_offset
-                                      + gotent->got_offset);
-                   outrel.r_info = ELF64_R_INFO(0, R_ALPHA_RELATIVE);
-                   outrel.r_addend = relocation + addend;
-
-                   bfd_elf64_swap_reloca_out (output_bfd, &outrel,
-                                              ((Elf64_External_Rela *)
-                                               srelgot->contents)
-                                              + srelgot->reloc_count++);
-                   BFD_ASSERT (sizeof (Elf64_External_Rela)
-                               * srelgot->reloc_count
-                               <= srelgot->_cooked_size);
-                 }
+                 outrel.r_offset = (sgot->output_section->vma
+                                    + sgot->output_offset
+                                    + gotent->got_offset);
+                 outrel.r_info = ELF64_R_INFO (0, R_ALPHA_RELATIVE);
+                 outrel.r_addend = value;
 
-               gotent->flags |= ALPHA_ELF_GOT_ENTRY_RELOCS_DONE;
-             }
+                 bfd_elf64_swap_reloca_out (output_bfd, &outrel,
+                                            ((Elf64_External_Rela *)
+                                             srelgot->contents)
+                                            + srelgot->reloc_count++);
+                 BFD_ASSERT (sizeof (Elf64_External_Rela)
+                             * srelgot->reloc_count
+                             <= srelgot->_cooked_size);
+               }
+           }
 
-           /* Figure the gprel relocation.  */
-           addend = 0;
-           relocation = (sgot->output_section->vma
-                         + sgot->output_offset
-                         + gotent->got_offset);
-           relocation -= gp;
-         }
-         /* overflow handled by _bfd_final_link_relocate */
+         value = (sgot->output_section->vma
+                  + sgot->output_offset
+                  + gotent->got_offset);
+         value -= gp;
          goto default_reloc;
 
        case R_ALPHA_GPREL16:
        case R_ALPHA_GPREL32:
        case R_ALPHA_GPRELLOW:
-         if (h && alpha_elf_dynamic_symbol_p (&h->root, info))
+         if (dynamic_symbol_p)
             {
               (*_bfd_error_handler)
                 (_("%s: gp-relative relocation against dynamic symbol %s"),
@@ -3537,11 +4531,11 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
               ret_val = false;
             }
          BFD_ASSERT(gp != 0);
-         relocation -= gp;
+         value -= gp;
          goto default_reloc;
 
        case R_ALPHA_GPRELHIGH:
-         if (h && alpha_elf_dynamic_symbol_p (&h->root, info))
+         if (dynamic_symbol_p)
             {
               (*_bfd_error_handler)
                 (_("%s: gp-relative relocation against dynamic symbol %s"),
@@ -3549,27 +4543,34 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
               ret_val = false;
             }
          BFD_ASSERT(gp != 0);
-         relocation -= gp;
-         relocation += addend;
-         addend = 0;
-         relocation = (((bfd_signed_vma) relocation >> 16)
-                       + ((relocation >> 15) & 1));
+         value -= gp;
+         value = ((bfd_signed_vma) value >> 16) + ((value >> 15) & 1);
          goto default_reloc;
 
        case R_ALPHA_HINT:
          /* A call to a dynamic symbol is definitely out of range of
             the 16-bit displacement.  Don't bother writing anything.  */
-         if (h && alpha_elf_dynamic_symbol_p (&h->root, info))
+         if (dynamic_symbol_p)
            {
              r = bfd_reloc_ok;
              break;
            }
-         /* FALLTHRU */
+         /* The regular PC-relative stuff measures from the start of
+            the instruction rather than the end.  */
+         value -= 4;
+         goto default_reloc;
 
        case R_ALPHA_BRADDR:
+         if (dynamic_symbol_p)
+            {
+              (*_bfd_error_handler)
+                (_("%s: pc-relative relocation against dynamic symbol %s"),
+                 bfd_archive_filename (input_bfd), h->root.root.root.string);
+              ret_val = false;
+            }
          /* The regular PC-relative stuff measures from the start of
             the instruction rather than the end.  */
-         addend -= 4;
+         value -= 4;
          goto default_reloc;
 
        case R_ALPHA_BRSGP:
@@ -3579,12 +4580,12 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
 
            /* The regular PC-relative stuff measures from the start of
               the instruction rather than the end.  */
-           addend -= 4;
+           value -= 4;
 
            /* The source and destination gp must be the same.  Note that
               the source will always have an assigned gp, since we forced
               one in check_relocs, but that the destination may not, as
-              it might not have had any relocations at all.  Also take 
+              it might not have had any relocations at all.  Also take
               care not to crash if H is an undefined symbol.  */
            if (h != NULL && sec != NULL
                && alpha_elf_tdata (sec->owner)->gotobj
@@ -3632,46 +4633,57 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
 
        case R_ALPHA_REFLONG:
        case R_ALPHA_REFQUAD:
+       case R_ALPHA_DTPREL64:
+       case R_ALPHA_TPREL64:
          {
            Elf_Internal_Rela outrel;
 
            /* Careful here to remember RELATIVE relocations for global
               variables for symbolic shared objects.  */
 
-           if (h && alpha_elf_dynamic_symbol_p (&h->root, info))
+           if (dynamic_symbol_p)
              {
                BFD_ASSERT(h->root.dynindx != -1);
-               outrel.r_info = ELF64_R_INFO(h->root.dynindx, r_type);
+               outrel.r_info = ELF64_R_INFO (h->root.dynindx, r_type);
                outrel.r_addend = addend;
-               addend = 0, relocation = 0;
+               addend = 0, value = 0;
+             }
+           else if (r_type == R_ALPHA_DTPREL64)
+             {
+               BFD_ASSERT(tls_segment != NULL);
+               value -= dtp_base;
+               goto default_reloc;
+             }
+           else if (r_type == R_ALPHA_TPREL64)
+             {
+               BFD_ASSERT(tls_segment != NULL);
+               value -= dtp_base;
+               goto default_reloc;
              }
            else if (info->shared
                     && r_symndx != 0
                     && (input_section->flags & SEC_ALLOC))
              {
-               outrel.r_info = ELF64_R_INFO(0, R_ALPHA_RELATIVE);
-               outrel.r_addend = relocation + addend;
+               if (r_type == R_ALPHA_REFLONG)
+                 {
+                   (*_bfd_error_handler)
+                     (_("%s: unhandled dynamic relocation against %s"),
+                      bfd_archive_filename (input_bfd),
+                      h->root.root.root.string);
+                   ret_val = false;
+                 }
+               outrel.r_info = ELF64_R_INFO (0, R_ALPHA_RELATIVE);
+               outrel.r_addend = value;
              }
            else
              goto default_reloc;
 
-           if (!srel)
-             {
-               const char *name;
-
-               name = (bfd_elf_string_from_elf_section
-                       (input_bfd, elf_elfheader(input_bfd)->e_shstrndx,
-                        elf_section_data(input_section)->rel_hdr.sh_name));
-               BFD_ASSERT(name != NULL);
-
-               srel = bfd_get_section_by_name (dynobj, name);
-               BFD_ASSERT(srel != NULL);
-             }
+           BFD_ASSERT(srel != NULL);
 
            outrel.r_offset =
              _bfd_elf_section_offset (output_bfd, info, input_section,
                                       rel->r_offset);
-           if (outrel.r_offset != (bfd_vma) -1)
+           if ((outrel.r_offset | 1) != (bfd_vma) -1)
              outrel.r_offset += (input_section->output_section->vma
                                  + input_section->output_offset);
            else
@@ -3686,11 +4698,153 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
          }
          goto default_reloc;
 
+       case R_ALPHA_SREL16:
+       case R_ALPHA_SREL32:
+       case R_ALPHA_SREL64:
+         if (dynamic_symbol_p)
+            {
+              (*_bfd_error_handler)
+                (_("%s: pc-relative relocation against dynamic symbol %s"),
+                 bfd_archive_filename (input_bfd), h->root.root.root.string);
+              ret_val = false;
+            }
+
+         /* ??? .eh_frame references to discarded sections will be smashed
+            to relocations against SHN_UNDEF.  The .eh_frame format allows
+            NULL to be encoded as 0 in any format, so this works here.  */
+         if (r_symndx == 0)
+           howto = (elf64_alpha_howto_table
+                    + (r_type - R_ALPHA_SREL32 + R_ALPHA_REFLONG));
+         goto default_reloc;
+
+       case R_ALPHA_TLSLDM:
+         /* Ignore the symbol for the relocation.  The result is always
+            the current module.  */
+         dynamic_symbol_p = 0;
+         /* FALLTHRU */
+
+       case R_ALPHA_TLSGD:
+         if (!gotent->reloc_done)
+           {
+             gotent->reloc_done = 1;
+
+             /* Note that the module index for the main program is 1.  */
+             bfd_put_64 (output_bfd, !info->shared && !dynamic_symbol_p,
+                         sgot->contents + gotent->got_offset);
+
+             /* If the symbol has been forced local, output a
+                DTPMOD64 reloc, otherwise it will be handled in
+                finish_dynamic_symbol.  */
+             if (info->shared && !dynamic_symbol_p)
+               {
+                 Elf_Internal_Rela outrel;
+
+                 BFD_ASSERT(srelgot != NULL);
+
+                 outrel.r_offset = (sgot->output_section->vma
+                                    + sgot->output_offset
+                                    + gotent->got_offset);
+                 /* ??? Proper dynindx here.  */
+                 outrel.r_info = ELF64_R_INFO (0, R_ALPHA_DTPMOD64);
+                 outrel.r_addend = 0;
+
+                 bfd_elf64_swap_reloca_out (output_bfd, &outrel,
+                                            ((Elf64_External_Rela *)
+                                             srelgot->contents)
+                                            + srelgot->reloc_count++);
+                 BFD_ASSERT (sizeof (Elf64_External_Rela)
+                             * srelgot->reloc_count
+                             <= srelgot->_cooked_size);
+               }
+
+             if (dynamic_symbol_p || r_type == R_ALPHA_TLSLDM)
+               value = 0;
+             else
+               {
+                 BFD_ASSERT(tls_segment != NULL);
+                 value -= dtp_base;
+               }
+             bfd_put_64 (output_bfd, value,
+                         sgot->contents + gotent->got_offset + 8);
+           }
+
+         value = (sgot->output_section->vma
+                  + sgot->output_offset
+                  + gotent->got_offset);
+         value -= gp;
+         goto default_reloc;
+
+       case R_ALPHA_DTPRELHI:
+       case R_ALPHA_DTPRELLO:
+       case R_ALPHA_DTPREL16:
+         if (dynamic_symbol_p)
+            {
+              (*_bfd_error_handler)
+                (_("%s: dtp-relative relocation against dynamic symbol %s"),
+                 bfd_archive_filename (input_bfd), h->root.root.root.string);
+              ret_val = false;
+            }
+         BFD_ASSERT(tls_segment != NULL);
+         value -= dtp_base;
+         if (r_type == R_ALPHA_DTPRELHI)
+           value = ((bfd_signed_vma) value >> 16) + ((value >> 15) & 1);
+         goto default_reloc;
+
+       case R_ALPHA_TPRELHI:
+       case R_ALPHA_TPRELLO:
+       case R_ALPHA_TPREL16:
+         if (info->shared)
+           {
+             (*_bfd_error_handler)
+               (_("%s: TLS local exec code cannot be linked into shared objects"),
+               bfd_archive_filename (input_bfd));
+              ret_val = false;
+           }
+         else if (dynamic_symbol_p)
+            {
+              (*_bfd_error_handler)
+                (_("%s: tp-relative relocation against dynamic symbol %s"),
+                 bfd_archive_filename (input_bfd), h->root.root.root.string);
+              ret_val = false;
+            }
+         BFD_ASSERT(tls_segment != NULL);
+         value -= tp_base;
+         if (r_type == R_ALPHA_TPRELHI)
+           value = ((bfd_signed_vma) value >> 16) + ((value >> 15) & 1);
+         goto default_reloc;
+
+       case R_ALPHA_GOTDTPREL:
+       case R_ALPHA_GOTTPREL:
+         BFD_ASSERT(sgot != NULL);
+         BFD_ASSERT(gp != 0);
+         BFD_ASSERT(gotent != NULL);
+         BFD_ASSERT(gotent->use_count >= 1);
+
+         if (!gotent->reloc_done)
+           {
+             gotent->reloc_done = 1;
+
+             if (dynamic_symbol_p)
+               value = 0;
+             else
+               {
+                 BFD_ASSERT(tls_segment != NULL);
+                 value -= (r_type == R_ALPHA_GOTDTPREL ? dtp_base : tp_base);
+               }
+             bfd_put_64 (output_bfd, value,
+                         sgot->contents + gotent->got_offset);
+           }
+
+         value = (sgot->output_section->vma
+                  + sgot->output_offset
+                  + gotent->got_offset);
+         value -= gp;
+         goto default_reloc;
+
        default:
        default_reloc:
          r = _bfd_final_link_relocate (howto, input_bfd, input_section,
-                                       contents, rel->r_offset, relocation,
-                                       addend);
+                                       contents, rel->r_offset, value, 0);
          break;
        }
 
@@ -3862,20 +5016,58 @@ elf64_alpha_finish_dynamic_symbol (output_bfd, info, h, sym)
       srel = bfd_get_section_by_name (dynobj, ".rela.got");
       BFD_ASSERT (srel != NULL);
 
-      outrel.r_info = ELF64_R_INFO (h->dynindx, R_ALPHA_GLOB_DAT);
       for (gotent = ((struct alpha_elf_link_hash_entry *) h)->got_entries;
           gotent != NULL;
           gotent = gotent->next)
        {
-         asection *sgot = alpha_elf_tdata (gotent->gotobj)->got;
+         asection *sgot;
+         int r_type;
+
+         if (gotent->use_count == 0)
+           continue;
+
+         sgot = alpha_elf_tdata (gotent->gotobj)->got;
          outrel.r_offset = (sgot->output_section->vma
                             + sgot->output_offset
                             + gotent->got_offset);
+
+         r_type = gotent->reloc_type;
+         switch (r_type)
+           {
+           case R_ALPHA_LITERAL:
+             r_type = R_ALPHA_GLOB_DAT;
+             break;
+           case R_ALPHA_TLSGD:
+             r_type = R_ALPHA_DTPMOD64;
+             break;
+           case R_ALPHA_GOTDTPREL:
+             r_type = R_ALPHA_DTPREL64;
+             break;
+           case R_ALPHA_GOTTPREL:
+             r_type = R_ALPHA_TPREL64;
+             break;
+           case R_ALPHA_TLSLDM:
+           default:
+             abort ();
+           }
+
+         outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
          outrel.r_addend = gotent->addend;
 
          bfd_elf64_swap_reloca_out (output_bfd, &outrel,
                                     ((Elf64_External_Rela *)srel->contents
                                      + srel->reloc_count++));
+
+         if (gotent->reloc_type == R_ALPHA_TLSGD)
+           {
+             outrel.r_offset += 8;
+             outrel.r_info = ELF64_R_INFO (h->dynindx, R_ALPHA_DTPREL64);
+
+             bfd_elf64_swap_reloca_out (output_bfd, &outrel,
+                                        ((Elf64_External_Rela *)srel->contents
+                                         + srel->reloc_count++));
+           }
+
          BFD_ASSERT (sizeof (Elf64_External_Rela) * srel->reloc_count
                      <= srel->_cooked_size);
        }
This page took 0.077683 seconds and 4 git commands to generate.