* elf-bfd.h (struct elf_reloc_cookie): Remove locsym_shndx,
[deliverable/binutils-gdb.git] / bfd / elf64-alpha.c
index 7bd9fd5084f936c101a28b05beca8675072d8985..36b0cb69d4c0cf1d0bf7825d19201b2881a43e5e 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,13 +106,21 @@ 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 *));
@@ -126,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 **));
@@ -153,7 +164,7 @@ struct alpha_elf_link_hash_entry
   /* Cumulative flags for all the .got entries.  */
   int flags;
 
-  /* Contexts (LITUSE) in which a literal was referenced.  */
+  /* 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
@@ -161,6 +172,7 @@ struct alpha_elf_link_hash_entry
 #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
@@ -1165,6 +1177,15 @@ elf64_alpha_info_to_howto (abfd, cache_ptr, dst)
 /* 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.
 
@@ -1187,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));
@@ -1236,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;
@@ -1258,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)
     {
@@ -1283,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)
            {
@@ -1340,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
@@ -1358,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;
@@ -1409,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);
@@ -1444,10 +1486,10 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
     {
       if (--info->gotent->use_count == 0)
        {
-         int sz = alpha_got_entry_size (info->gotent->reloc_type);
-         alpha_elf_tdata (info->gotent->gotobj)->total_got_size -= sz;
+         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->gotent->gotobj)->local_got_size -= sz;
+           alpha_elf_tdata (info->gotobj)->local_got_size -= sz;
        }
 
       /* If the literal instruction is no longer needed (it may have been
@@ -1465,7 +1507,7 @@ elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
        }
     }
 
-  return irel + count;
+  return true;
 }
 
 static bfd_vma
@@ -1541,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;
@@ -1554,39 +1597,73 @@ 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.  */
   if (--info->gotent->use_count == 0)
     {
-      int sz = alpha_got_entry_size (info->gotent->reloc_type);
-      alpha_elf_tdata (info->gotent->gotobj)->total_got_size -= sz;
+      int sz = alpha_got_entry_size (r_type);
+      alpha_elf_tdata (info->gotobj)->total_got_size -= sz;
       if (!info->h)
-       alpha_elf_tdata (info->gotent->gotobj)->local_got_size -= sz;
+       alpha_elf_tdata (info->gotobj)->local_got_size -= sz;
     }
 
   /* ??? Search forward through this basic block looking for insns
@@ -1605,6 +1682,353 @@ elf64_alpha_relax_without_lituse (info, symval, irel)
   return true;
 }
 
+static boolean
+elf64_alpha_relax_gprelhilo (info, symval, irel, hi)
+     struct alpha_relax_info *info;
+     bfd_vma symval;
+     Elf_Internal_Rela *irel;
+     boolean hi;
+{
+  unsigned int insn;
+  bfd_signed_vma disp;
+  bfd_byte *pos = info->contents + irel->r_offset;
+
+  /* ??? This assumes that the compiler doesn't render
+
+       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 (hi)
+    {
+      /* Nop out the high instruction.  */
+
+      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;
@@ -1613,16 +2037,12 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
      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 = NULL;
-  Elf64_External_Sym *free_extsyms = NULL;
-  Elf_External_Sym_Shndx *shndx_buf = NULL;
+  Elf_Internal_Sym *isymbuf = 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;
@@ -1645,116 +2065,99 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
                     (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL,
                      link_info->keep_memory));
   if (internal_relocs == NULL)
-    goto error_return;
-  if (! link_info->keep_memory)
-    free_relocs = internal_relocs;
+    return false;
 
   memset(&info, 0, sizeof (info));
   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);
+    }
+
+  /* Get the section contents.  */
+  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;
+
+      if (! bfd_get_section_contents (abfd, sec, info.contents,
+                                     (file_ptr) 0, sec->_raw_size))
+       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);
 
-      if (ELF64_R_TYPE (irel->r_info) != (int) R_ALPHA_LITERAL)
-       continue;
-
-      /* Get the section contents.  */
-      if (info.contents == NULL)
+      /* Early exit for unhandled or unrelaxable relocations.  */
+      switch (r_type)
        {
-         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;
-
-             if (! bfd_get_section_contents (abfd, sec, info.contents,
-                                             (file_ptr) 0, sec->_raw_size))
-               goto error_return;
-           }
+       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;
        }
 
-      /* Read this BFD's symbols if we haven't done so already.  */
-      if (extsyms == NULL)
+      /* Get the value of the symbol referred to by the reloc.  */
+      if (ELF64_R_SYM (irel->r_info) < symtab_hdr->sh_info)
        {
-         bfd_size_type amt;
-
-         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;
-           }
+         /* A local symbol.  */
+         Elf_Internal_Sym *isym;
 
-         shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
-         if (shndx_hdr->sh_size != 0)
+         /* Read this BFD's local symbols.  */
+         if (isymbuf == NULL)
            {
-             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)
+             isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+             if (isymbuf == NULL)
+               isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+                                               symtab_hdr->sh_info, 0,
+                                               NULL, NULL, NULL);
+             if (isymbuf == NULL)
                goto error_return;
            }
-       }
 
-      /* Get the value of the symbol referred to by the reloc.  */
-      if (ELF64_R_SYM (irel->r_info) < symtab_hdr->sh_info)
-       {
-         /* A local symbol.  */
-         Elf64_External_Sym *esym;
-         Elf_External_Sym_Shndx *shndx;
-
-         esym = extsyms + ELF64_R_SYM (irel->r_info);
-         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;
-         else if (isym.st_shndx == SHN_ABS)
+         isym = isymbuf + ELF64_R_SYM (irel->r_info);
+         if (isym->st_shndx == SHN_UNDEF)
+           continue;
+         else if (isym->st_shndx == SHN_ABS)
            info.tsec = bfd_abs_section_ptr;
-         else if (isym.st_shndx == SHN_COMMON)
+         else if (isym->st_shndx == SHN_COMMON)
            info.tsec = bfd_com_section_ptr;
          else
-           info.tsec = bfd_section_from_elf_index (abfd, isym.st_shndx);
+           info.tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
 
          info.h = NULL;
-         info.other = isym.st_other;
-         gotent = local_got_entries[ELF64_R_SYM(irel->r_info)];
-         symval = isym.st_value;
+         info.other = isym->st_other;
+         info.first_gotent = &local_got_entries[ELF64_R_SYM(irel->r_info)];
+         symval = isym->st_value;
        }
       else
        {
@@ -1769,66 +2172,103 @@ 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)
+  if (isymbuf != NULL
+      && symtab_hdr->contents != (unsigned char *) isymbuf)
     {
-      elf_section_data (sec)->relocs = internal_relocs;
-    }
-  else if (free_relocs != NULL)
-    {
-      free (free_relocs);
+      if (!link_info->keep_memory)
+       free (isymbuf);
+      else
+       {
+         /* Cache the symbols for elf_link_input_bfd.  */
+         symtab_hdr->contents = (unsigned char *) isymbuf;
+       }
     }
 
-  if (info.changed_contents)
-    {
-      elf_section_data (sec)->this_hdr.contents = info.contents;
-    }
-  else if (free_contents != NULL)
+  if (info.contents != NULL
+      && elf_section_data (sec)->this_hdr.contents != info.contents)
     {
-      if (! link_info->keep_memory)
-       free (free_contents);
+      if (!info.changed_contents && !link_info->keep_memory)
+       free (info.contents);
       else
        {
          /* Cache the section contents for elf_link_input_bfd.  */
@@ -1836,18 +2276,12 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
        }
     }
 
-  if (shndx_buf != NULL)
-    free (shndx_buf);
-
-  if (free_extsyms != NULL)
+  if (elf_section_data (sec)->relocs != internal_relocs)
     {
-      if (! link_info->keep_memory)
-       free (free_extsyms);
+      if (!info.changed_relocs)
+       free (internal_relocs);
       else
-       {
-         /* Cache the symbols for elf_link_input_bfd.  */
-         symtab_hdr->contents = (unsigned char *) extsyms;
-       }
+       elf_section_data (sec)->relocs = internal_relocs;
     }
 
   *again = info.changed_contents || info.changed_relocs;
@@ -1855,14 +2289,15 @@ elf64_alpha_relax_section (abfd, sec, link_info, again)
   return true;
 
  error_return:
-  if (free_relocs != NULL)
-    free (free_relocs);
-  if (free_contents != NULL)
-    free (free_contents);
-  if (shndx_buf != NULL)
-    free (shndx_buf);
-  if (free_extsyms != NULL)
-    free (free_extsyms);
+  if (isymbuf != NULL
+      && symtab_hdr->contents != (unsigned char *) isymbuf)
+    free (isymbuf);
+  if (info.contents != NULL
+      && elf_section_data (sec)->this_hdr.contents != info.contents)
+    free (info.contents);
+  if (internal_relocs != NULL
+      && elf_section_data (sec)->relocs != internal_relocs)
+    free (internal_relocs);
   return false;
 }
 \f
@@ -1891,7 +2326,7 @@ static boolean
 elf64_alpha_section_from_shdr (abfd, hdr, name)
      bfd *abfd;
      Elf64_Internal_Shdr *hdr;
-     char *name;
+     const char *name;
 {
   asection *newsect;
 
@@ -2143,9 +2578,8 @@ elf64_alpha_read_ecoff_info (abfd, section, debug)
   if (ext_hdr == NULL && swap->external_hdr_size != 0)
     goto error_return;
 
-  if (bfd_get_section_contents (abfd, section, ext_hdr, (file_ptr) 0,
-                               swap->external_hdr_size)
-      == false)
+  if (! bfd_get_section_contents (abfd, section, ext_hdr, (file_ptr) 0,
+                                 swap->external_hdr_size))
     goto error_return;
 
   symhdr = &debug->symbolic_header;
@@ -2479,17 +2913,6 @@ 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
 /* Search for and possibly create a got entry.  */
 
@@ -2522,11 +2945,10 @@ get_got_entry (abfd, h, r_type, r_symndx, r_addend)
          size *= sizeof (struct alpha_elf_got_entry *);
 
          local_got_entries
-           = (struct alpha_elf_got_entry **) bfd_alloc (abfd, size);
+           = (struct alpha_elf_got_entry **) bfd_zalloc (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;
        }
 
@@ -2689,6 +3111,7 @@ elf64_alpha_check_relocs (abfd, info, sec, relocs)
 
        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;
@@ -3224,8 +3647,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;
@@ -3299,9 +3721,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;
@@ -3314,7 +3807,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.  */
@@ -3376,8 +3869,7 @@ elf64_alpha_calc_dynrel_sizes (h, info)
 {
   boolean dynamic;
   struct alpha_elf_reloc_entry *relent;
-  struct alpha_elf_got_entry *gotent;
-  int entries;
+  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;
@@ -3397,9 +3889,7 @@ 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
@@ -3420,10 +3910,90 @@ elf64_alpha_calc_dynrel_sizes (h, info)
        }
     }
 
+  return true;
+}
+
+/* Set the sizes of the dynamic relocation sections.  */
+
+static boolean
+elf64_alpha_size_rela_got_section (info)
+     struct bfd_link_info *info;
+{
+  unsigned long entries;
+  bfd *i, *dynobj;
+  asection *srel;
+
+  /* Shared libraries often require RELATIVE relocs, and some relocs
+     require attention for the main application as well.  */
+
+  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)
+       {
+         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)
-    entries += alpha_dynamic_entries_for_reloc (gotent->reloc_type,
-                                               dynamic, info->shared);
+    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.  */
@@ -3457,9 +4027,6 @@ elf64_alpha_size_dynamic_sections (output_bfd, info)
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
-      int entries;
-      bfd *i;
-
       /* Set the contents of the .interp section to the interpreter.  */
       if (!info->shared)
        {
@@ -3474,42 +4041,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);
-
-      /* Shared libraries often require RELATIVE relocs, and some relocs
-        require attention for the main application as well.  */
-        
-      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)
-           {
-             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));
-           }
-       }
+                                   elf64_alpha_calc_dynrel_sizes, info);
 
-      if (entries > 0)
-       {
-         s = bfd_get_section_by_name (dynobj, ".rela.got");
-         BFD_ASSERT (s != NULL);
-         s->_raw_size += sizeof (Elf64_External_Rela) * entries;
-       }
+      elf64_alpha_size_rela_got_section (info);
     }
   /* else we're not dynamic and by definition we don't need such things.  */
 
@@ -3613,6 +4147,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
@@ -3627,71 +4226,86 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
      Elf_Internal_Sym *local_syms;
      asection **local_sections;
 {
-  Elf_Internal_Shdr *symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
-  struct elf_link_tls_segment *tls_segment = NULL;
-  asection *sgot = NULL, *srel = NULL, *srelgot = NULL;
-  bfd *dynobj = NULL, *gotobj = NULL;
-  bfd_vma gp = 0, tp_base = 0, dtp_base = 0;
-  boolean ret_val = true;
+  struct elf_link_tls_segment *tls_segment;
+  asection *sgot, *srel, *srelgot;
+  bfd *dynobj, *gotobj;
+  bfd_vma gp, tp_base, dtp_base;
+  struct alpha_elf_got_entry **local_got_entries;
+  boolean ret_val;
+  const char *section_name;
 
-  if (!info->relocateable)
-    {
-      const char *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;
 
-      dynobj = elf_hash_table (info)->dynobj;
-      if (dynobj)
-        srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
+  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
 
-      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);
+  dynobj = elf_hash_table (info)->dynobj;
+  if (dynobj)
+    srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
+  else
+    srelgot = NULL;
 
-      /* Find the gp value for this input bfd.  */
-      gotobj = alpha_elf_tdata (input_bfd)->gotobj;
-      if (gotobj)
+  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.  */
+  gotobj = alpha_elf_tdata (input_bfd)->gotobj;
+  if (gotobj)
+    {
+      sgot = alpha_elf_tdata (gotobj)->got;
+      gp = _bfd_get_gp_value (gotobj);
+      if (gp == 0)
        {
-         sgot = alpha_elf_tdata (gotobj)->got;
-         gp = _bfd_get_gp_value (gotobj);
-         if (gp == 0)
-           {
-             gp = (sgot->output_section->vma
-                   + sgot->output_offset
-                   + 0x8000);
-             _bfd_set_gp_value (gotobj, gp);
-           }
-        }
+         gp = (sgot->output_section->vma
+               + sgot->output_offset
+               + 0x8000);
+         _bfd_set_gp_value (gotobj, gp);
+       }
+    }
+  else
+    {
+      sgot = NULL;
+      gp = 0;
+    }
 
-      tls_segment = elf_hash_table (info)->tls_segment;
-      if (tls_segment)
-        {
-          /* This is PT_TLS segment p_vaddr.  */
-          dtp_base = tls_segment->start;
+  local_got_entries = alpha_elf_tdata(input_bfd)->local_got_entries;
 
-          /* Main program TLS (whose template starts at PT_TLS p_vaddr)
-            is assigned offset round(16, PT_TLS p_align).  */
-          tp_base = dtp_base - align_power (16, tls_segment->align);
-        }
+  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++)
     {
-      struct alpha_elf_link_hash_entry *h;
+      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;
-      Elf_Internal_Sym *sym;
-      asection *sec;
+      Elf_Internal_Sym *sym = NULL;
+      asection *sec = NULL;
       bfd_vma value;
       bfd_vma addend;
       boolean dynamic_symbol_p;
-      boolean undef_weak_ref;
+      boolean undef_weak_ref = false;
       unsigned long r_type;
 
       r_type = ELF64_R_TYPE(rel->r_info);
@@ -3708,53 +4322,25 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
       howto = elf64_alpha_howto_table + r_type;
       r_symndx = ELF64_R_SYM(rel->r_info);
 
-      if (info->relocateable)
-       {
-         /* 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;
-
-         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;
-               }
-           }
-
-         continue;
-       }
-
-      /* This is a final link.  */
-
-      h = NULL;
-      sym = NULL;
-      sec = NULL;
-      undef_weak_ref = false;
-
       if (r_symndx < symtab_hdr->sh_info)
        {
          sym = local_syms + r_symndx;
          sec = local_sections[r_symndx];
          value = _bfd_elf_rela_local_sym (output_bfd, sym, sec, rel);
 
-         gotent = alpha_elf_tdata(input_bfd)->local_got_entries[r_symndx];
+         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->reloc_xlated)
+             && ELF_ST_TYPE (sym->st_info) == STT_SECTION
+             && (elf_section_data (sec)->sec_info_type
+                 == ELF_INFO_TYPE_MERGE)
+             && gotent
+             && !gotent->reloc_xlated)
            {
              struct alpha_elf_got_entry *ent;
              asection *msec;
@@ -3970,7 +4556,7 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
            /* 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
@@ -4171,12 +4757,21 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
             }
          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 (dynamic_symbol_p)
+         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"),
@@ -4185,6 +4780,8 @@ elf64_alpha_relocate_section (output_bfd, info, input_bfd, input_section,
             }
          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:
@@ -4394,9 +4991,13 @@ elf64_alpha_finish_dynamic_symbol (output_bfd, info, h, sym)
           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);
@@ -4898,6 +5499,7 @@ const struct elf_size_info alpha_elf_size_info =
   bfd_elf64_write_out_phdrs,
   bfd_elf64_write_shdrs_and_ehdr,
   bfd_elf64_write_relocs,
+  bfd_elf64_swap_symbol_in,
   bfd_elf64_swap_symbol_out,
   bfd_elf64_slurp_reloc_table,
   bfd_elf64_slurp_symbol_table,
This page took 0.04209 seconds and 4 git commands to generate.