This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

alpha tls relocation relaxation


	include/elf/
        * alpha.h (LITUSE_ALPHA_ADDR, LITUSE_ALPHA_BASE, LITUSE_ALPHA_BYTOFF,
        LITUSE_ALPHA_JSR, LITUSE_ALPHA_TLSGD, LITUSE_ALPHA_TLSLDM): New.

	gas/
        * config/tc-alpha.c: Move LITUSE constants to "elf/alpha.h".
        Rename them LITUSE_ALPHA_*.

	bfd/
        * elf64-alpha.c (alpha_get_dtprel_base, alpha_get_tprel_base): New.
        (elf64_alpha_relocate_section): Use them.  Reject LE TLS relocs
        in shared libraries.  Fix DTPRELHI and TPRELHI value.
        (INSN_ADDQ, INSN_RDUNIQ): New.
        (struct alpha_relax_info): Add symtab_hdr, tls_segment, first_gotent.
        (elf64_alpha_relax_with_lituse): Return boolean.  Remove irelend
        argument.  Reject dynamic symbols.  Use LITUSE symbolic constants.
        (elf64_alpha_relax_got_load): Rename from relax_without_lituse.
        Handle GOTDTPREL and GOTTPREL relocations.
        (elf64_alpha_relax_gprelhilo): New.
        (elf64_alpha_relax_tls_get_addr): New.
        (elf64_alpha_relax_find_tls_segment): New.
        (elf64_alpha_relax_section): Handle TLS relocations.
        (ALPHA_ELF_LINK_HASH_TLS_IE): New.
        (elf64_alpha_check_relocs): Set it.

Index: include/elf/alpha.h
===================================================================
RCS file: /cvs/src/src/include/elf/alpha.h,v
retrieving revision 1.7
diff -c -p -d -r1.7 alpha.h
*** include/elf/alpha.h	30 May 2002 22:01:38 -0000	1.7
--- include/elf/alpha.h	2 Jun 2002 02:16:02 -0000
*************** START_RELOC_NUMBERS (elf_alpha_reloc_typ
*** 116,119 ****
--- 116,126 ----
  
  END_RELOC_NUMBERS (R_ALPHA_max)
  
+ #define LITUSE_ALPHA_ADDR	0
+ #define LITUSE_ALPHA_BASE	1
+ #define LITUSE_ALPHA_BYTOFF	2
+ #define LITUSE_ALPHA_JSR	3
+ #define LITUSE_ALPHA_TLSGD	4
+ #define LITUSE_ALPHA_TLSLDM	5
+ 
  #endif /* _ELF_ALPHA_H */
Index: gas/config/tc-alpha.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-alpha.c,v
retrieving revision 1.42
diff -c -p -d -r1.42 tc-alpha.c
*** gas/config/tc-alpha.c	30 May 2002 22:01:32 -0000	1.42
--- gas/config/tc-alpha.c	2 Jun 2002 02:16:02 -0000
*************** struct alpha_macro {
*** 137,149 ****
  #define DUMMY_RELOC_LITUSE_TLSGD	(BFD_RELOC_UNUSED + 5)
  #define DUMMY_RELOC_LITUSE_TLSLDM	(BFD_RELOC_UNUSED + 6)
  
- #define LITUSE_ADDR	0
- #define LITUSE_BASE	1
- #define LITUSE_BYTOFF	2
- #define LITUSE_JSR	3
- #define LITUSE_TLSGD	4
- #define LITUSE_TLSLDM	5
- 
  #define USER_RELOC_P(R) ((R) >= O_literal && (R) <= O_tprel)
  
  /* Macros for extracting the type and number of encoded register tokens */
--- 137,142 ----
*************** alpha_adjust_symtab_relocs (abfd, sec, p
*** 1758,1771 ****
  	    as_bad_where (fixp->fx_file, fixp->fx_line,
  			  _("No !literal!%ld was found"),
  			  fixp->tc_fix_data.info->sequence);
! 	  if (fixp->fx_offset == LITUSE_TLSGD)
  	    {
  	      if (! fixp->tc_fix_data.info->saw_tlsgd)
  		as_bad_where (fixp->fx_file, fixp->fx_line,
  			      _("No !tlsgd!%ld was found"),
  			      fixp->tc_fix_data.info->sequence);
  	    }
! 	  else if (fixp->fx_offset == LITUSE_TLSLDM)
  	    {
  	      if (! fixp->tc_fix_data.info->saw_tlsldm)
  		as_bad_where (fixp->fx_file, fixp->fx_line,
--- 1751,1764 ----
  	    as_bad_where (fixp->fx_file, fixp->fx_line,
  			  _("No !literal!%ld was found"),
  			  fixp->tc_fix_data.info->sequence);
! 	  if (fixp->fx_offset == LITUSE_ALPHA_TLSGD)
  	    {
  	      if (! fixp->tc_fix_data.info->saw_tlsgd)
  		as_bad_where (fixp->fx_file, fixp->fx_line,
  			      _("No !tlsgd!%ld was found"),
  			      fixp->tc_fix_data.info->sequence);
  	    }
! 	  else if (fixp->fx_offset == LITUSE_ALPHA_TLSLDM)
  	    {
  	      if (! fixp->tc_fix_data.info->saw_tlsldm)
  		as_bad_where (fixp->fx_file, fixp->fx_line,
*************** emit_insn (insn)
*** 2686,2707 ****
  	  break;
  
  	case DUMMY_RELOC_LITUSE_ADDR:
! 	  fixP->fx_offset = LITUSE_ADDR;
  	  goto do_lituse;
  	case DUMMY_RELOC_LITUSE_BASE:
! 	  fixP->fx_offset = LITUSE_BASE;
  	  goto do_lituse;
  	case DUMMY_RELOC_LITUSE_BYTOFF:
! 	  fixP->fx_offset = LITUSE_BYTOFF;
  	  goto do_lituse;
  	case DUMMY_RELOC_LITUSE_JSR:
! 	  fixP->fx_offset = LITUSE_JSR;
  	  goto do_lituse;
  	case DUMMY_RELOC_LITUSE_TLSGD:
! 	  fixP->fx_offset = LITUSE_TLSGD;
  	  goto do_lituse;
  	case DUMMY_RELOC_LITUSE_TLSLDM:
! 	  fixP->fx_offset = LITUSE_TLSLDM;
  	  goto do_lituse;
  	do_lituse:
  	  fixP->fx_addsy = section_symbol (now_seg);
--- 2679,2700 ----
  	  break;
  
  	case DUMMY_RELOC_LITUSE_ADDR:
! 	  fixP->fx_offset = LITUSE_ALPHA_ADDR;
  	  goto do_lituse;
  	case DUMMY_RELOC_LITUSE_BASE:
! 	  fixP->fx_offset = LITUSE_ALPHA_BASE;
  	  goto do_lituse;
  	case DUMMY_RELOC_LITUSE_BYTOFF:
! 	  fixP->fx_offset = LITUSE_ALPHA_BYTOFF;
  	  goto do_lituse;
  	case DUMMY_RELOC_LITUSE_JSR:
! 	  fixP->fx_offset = LITUSE_ALPHA_JSR;
  	  goto do_lituse;
  	case DUMMY_RELOC_LITUSE_TLSGD:
! 	  fixP->fx_offset = LITUSE_ALPHA_TLSGD;
  	  goto do_lituse;
  	case DUMMY_RELOC_LITUSE_TLSLDM:
! 	  fixP->fx_offset = LITUSE_ALPHA_TLSLDM;
  	  goto do_lituse;
  	do_lituse:
  	  fixP->fx_addsy = section_symbol (now_seg);
Index: bfd/elf64-alpha.c
===================================================================
RCS file: /cvs/src/src/bfd/elf64-alpha.c,v
retrieving revision 1.66
diff -c -p -d -r1.66 elf64-alpha.c
*** bfd/elf64-alpha.c	1 Jun 2002 21:56:41 -0000	1.66
--- bfd/elf64-alpha.c	2 Jun 2002 02:16:02 -0000
*************** struct alpha_elf_link_hash_entry
*** 153,159 ****
    /* 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
--- 153,159 ----
    /* Cumulative flags for all the .got entries.  */
    int flags;
  
!   /* 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
*************** struct alpha_elf_link_hash_entry
*** 161,166 ****
--- 161,167 ----
  #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
*************** elf64_alpha_info_to_howto (abfd, cache_p
*** 1165,1170 ****
--- 1166,1180 ----
  /* 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))
  
  /* These functions do relaxation for Alpha ELF.
  
*************** elf64_alpha_info_to_howto (abfd, cache_p
*** 1187,1221 ****
  #define OP_BR		0x30
  #define OP_BSR		0x34
  #define INSN_UNOP	0x2ffe0000
  
  struct alpha_relax_info
  {
    bfd *abfd;
    asection *sec;
    bfd_byte *contents;
    Elf_Internal_Rela *relocs, *relend;
    struct bfd_link_info *link_info;
!   boolean changed_contents;
!   boolean changed_relocs;
    bfd_vma gp;
    bfd *gotobj;
    asection *tsec;
    struct alpha_elf_link_hash_entry *h;
    struct alpha_elf_got_entry *gotent;
    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
    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_section
    PARAMS((bfd *abfd, asection *sec, struct bfd_link_info *link_info,
  	  boolean *again));
--- 1197,1241 ----
  #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;
!   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 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));
*************** elf64_alpha_find_reloc_at_ofs (rel, rele
*** 1236,1248 ****
    return NULL;
  }
  
! static Elf_Internal_Rela *
! elf64_alpha_relax_with_lituse (info, symval, irel, irelend)
       struct alpha_relax_info *info;
       bfd_vma symval;
!      Elf_Internal_Rela *irel, *irelend;
  {
!   Elf_Internal_Rela *urel;
    int flags, count, i;
    bfd_signed_vma disp;
    boolean fits16;
--- 1256,1268 ----
    return NULL;
  }
  
! static boolean
! elf64_alpha_relax_with_lituse (info, symval, irel)
       struct alpha_relax_info *info;
       bfd_vma symval;
!      Elf_Internal_Rela *irel;
  {
!   Elf_Internal_Rela *urel, *irelend = info->relend;
    int flags, count, i;
    bfd_signed_vma disp;
    boolean fits16;
*************** elf64_alpha_relax_with_lituse (info, sym
*** 1258,1266 ****
         ("%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;
      }
  
    /* Summarize how this particular LITERAL is used.  */
    for (urel = irel+1, flags = count = 0; urel < irelend; ++urel, ++count)
      {
--- 1278,1290 ----
         ("%s: %s+0x%lx: warning: LITERAL relocation against unexpected insn",
  	bfd_archive_filename (info->abfd), info->sec->name,
  	(unsigned long) irel->r_offset));
!       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)
      {
*************** elf64_alpha_relax_with_lituse (info, sym
*** 1283,1307 ****
  
        switch (urel->r_addend)
  	{
! 	default: /* 0 = ADDRESS FORMAT */
  	  /* 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 */
  	  /* 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.  */
  
  	  xdisp = disp + insn_disp;
! 	  fits16 = (xdisp >= - (bfd_signed_vma) 0x00008000 && xdisp < 0x00008000);
! 	  fits32 = (xdisp >= - (bfd_signed_vma) 0x80000000 && xdisp < 0x7fff8000);
  
  	  if (fits16)
  	    {
--- 1307,1333 ----
  
        switch (urel->r_addend)
  	{
! 	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 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 & 0x8000)
! 	    insn_disp |= ~0xffff;  /* Negative: sign-extend.  */
  
  	  xdisp = disp + insn_disp;
! 	  fits16 = (xdisp >= - (bfd_signed_vma) 0x8000 && xdisp < 0x8000);
! 	  fits32 = (xdisp >= - (bfd_signed_vma) 0x80000000
! 		    && xdisp < 0x7fff8000);
  
  	  if (fits16)
  	    {
*************** elf64_alpha_relax_with_lituse (info, sym
*** 1340,1346 ****
  	    all_optimized = false;
  	  break;
  
! 	case 2: /* BYTE OFFSET FORMAT */
  	  /* We can always optimize byte instructions.  */
  
  	  /* FIXME: sanity check the insn for byte op.  Check that the
--- 1366,1372 ----
  	    all_optimized = false;
  	  break;
  
! 	case LITUSE_ALPHA_BYTOFF:
  	  /* We can always optimize byte instructions.  */
  
  	  /* FIXME: sanity check the insn for byte op.  Check that the
*************** elf64_alpha_relax_with_lituse (info, sym
*** 1358,1364 ****
  	  info->changed_contents = true;
  	  break;
  
! 	case 3: /* CALL FORMAT */
  	  {
  	    /* If not zero, place to jump without needing pv.  */
  	    bfd_vma optdest = elf64_alpha_relax_opt_call (info, symval);
--- 1384,1392 ----
  	  info->changed_contents = true;
  	  break;
  
! 	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);
*************** elf64_alpha_relax_with_lituse (info, sym
*** 1409,1415 ****
  	      {
  		Elf_Internal_Rela *gpdisp
  		  = (elf64_alpha_find_reloc_at_ofs
! 		     (irel, irelend, urel->r_offset + 4, R_ALPHA_GPDISP));
  		if (gpdisp)
  		  {
  		    bfd_byte *p_ldah = info->contents + gpdisp->r_offset; 
--- 1437,1444 ----
  	      {
  		Elf_Internal_Rela *gpdisp
  		  = (elf64_alpha_find_reloc_at_ofs
! 		     (info->relocs, irelend, urel->r_offset + 4,
! 		      R_ALPHA_GPDISP));
  		if (gpdisp)
  		  {
  		    bfd_byte *p_ldah = info->contents + gpdisp->r_offset; 
*************** elf64_alpha_relax_with_lituse (info, sym
*** 1444,1453 ****
      {
        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;
  	  if (!info->h)
! 	    alpha_elf_tdata (info->gotent->gotobj)->local_got_size -= sz;
  	}
  
        /* If the literal instruction is no longer needed (it may have been
--- 1473,1482 ----
      {
        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
*************** elf64_alpha_relax_with_lituse (info, sym
*** 1465,1471 ****
  	}
      }
  
!   return irel + count;
  }
  
  static bfd_vma
--- 1494,1500 ----
  	}
      }
  
!   return true;
  }
  
  static bfd_vma
*************** elf64_alpha_relax_opt_call (info, symval
*** 1541,1550 ****
  }
  
  static boolean
! elf64_alpha_relax_without_lituse (info, symval, irel)
       struct alpha_relax_info *info;
       bfd_vma symval;
       Elf_Internal_Rela *irel;
  {
    unsigned int insn;
    bfd_signed_vma disp;
--- 1570,1580 ----
  }
  
  static boolean
! 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;
*************** elf64_alpha_relax_without_lituse (info, 
*** 1554,1592 ****
  
    if (insn >> 26 != OP_LDQ)
      {
        ((*_bfd_error_handler)
!        ("%s: %s+0x%lx: warning: LITERAL relocation against unexpected insn",
  	bfd_archive_filename (info->abfd), info->sec->name,
! 	(unsigned long) irel->r_offset));
        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.  */
  
!   disp = symval - info->gp;
!   if (disp < -0x8000 || disp >= 0x8000)
      return true;
  
!   /* On the LITERAL instruction itself, consider exchanging
!      `ldq R,X(gp)' for `lda R,Y(gp)'.  */
  
!   insn = (OP_LDA << 26) | (insn & 0x03ff0000);
    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);
    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;
        if (!info->h)
! 	alpha_elf_tdata (info->gotent->gotobj)->local_got_size -= sz;
      }
  
    /* ??? Search forward through this basic block looking for insns
--- 1584,1656 ----
  
    if (insn >> 26 != OP_LDQ)
      {
+       reloc_howto_type *howto = elf64_alpha_howto_table + r_type;
        ((*_bfd_error_handler)
!        ("%s: %s+0x%lx: warning: %s relocation against unexpected insn",
  	bfd_archive_filename (info->abfd), info->sec->name,
! 	(unsigned long) irel->r_offset, howto->name));
        return true;
      }
  
!   /* Can't relax dynamic symbols.  */
!   if (alpha_elf_dynamic_symbol_p (&info->h->root, info->link_info))
!     return true;
  
!   /* Can't use local-exec relocations in shared libraries.  */
!   if (r_type == R_ALPHA_GOTTPREL && info->link_info->shared)
      return true;
  
!   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;
! 
!   /* 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;
+   
+   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 (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
*************** elf64_alpha_relax_without_lituse (info, 
*** 1606,1611 ****
--- 1670,2023 ----
  }
  
  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 *first_tls_sec = NULL, *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)
+       {
+         first_tls_sec = o;
+         break;
+       }
+   if (!first_tls_sec)
+     return NULL;
+ 
+   base = first_tls_sec->vma;
+   align = 0;
+ 
+   for (o = first_tls_sec; o && (o->flags & SEC_THREAD_LOCAL); o = o->next)
+     {
+       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;
+     }
+ 
+   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;
*************** elf64_alpha_relax_section (abfd, sec, li
*** 1618,1628 ****
    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;
  
    /* We are not currently changing any sizes, so only one pass.  */
    *again = false;
--- 2030,2041 ----
    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;
*************** elf64_alpha_relax_section (abfd, sec, li
*** 1653,1658 ****
--- 2066,2072 ----
    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;
  
*************** elf64_alpha_relax_section (abfd, sec, li
*** 1667,1731 ****
  		 + 0x8000);
      }
  
!   for (irel = internal_relocs; irel < irelend; irel++)
      {
!       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.  */
        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;
  
! 	      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.  */
        if (extsyms == NULL)
! 	{
! 	  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;
! 	    }
  
! 	  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;
! 	    }
  	}
  
        /* Get the value of the symbol referred to by the reloc.  */
--- 2081,2154 ----
  		 + 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;
!       free_contents = info.contents;
  
!       if (! bfd_get_section_contents (abfd, sec, info.contents,
! 				      (file_ptr) 0, sec->_raw_size))
! 	goto error_return;
!     }
  
!   /* 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)
! 	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;
!     }
  
!   /* 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.  */
*************** elf64_alpha_relax_section (abfd, sec, li
*** 1749,1755 ****
  
  	  info.h = NULL;
  	  info.other = isym.st_other;
! 	  gotent = local_got_entries[ELF64_R_SYM(irel->r_info)];
  	  symval = isym.st_value;
  	}
        else
--- 2172,2178 ----
  
  	  info.h = NULL;
  	  info.other = isym.st_other;
! 	  info.first_gotent = &local_got_entries[ELF64_R_SYM(irel->r_info)];
  	  symval = isym.st_value;
  	}
        else
*************** elf64_alpha_relax_section (abfd, sec, li
*** 1765,1807 ****
  		 || 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))
- 	    continue;
- 
  	  info.h = h;
  	  info.tsec = h->root.root.u.def.section;
  	  info.other = h->root.other;
! 	  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;
        info.gotent = gotent;
  
        symval += info.tsec->output_section->vma + info.tsec->output_offset;
        symval += irel->r_addend;
  
!       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 (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)
  	    goto error_return;
! 	}
!       else
! 	{
! 	  if (!elf64_alpha_relax_without_lituse (&info, symval, irel))
  	    goto error_return;
  	}
      }
  
--- 2188,2253 ----
  		 || h->root.root.type == bfd_link_hash_warning)
  	    h = (struct alpha_elf_link_hash_entry *)h->root.root.u.i.link;
  
  	  info.h = h;
  	  info.tsec = h->root.root.u.def.section;
  	  info.other = h->root.other;
! 	  info.first_gotent = &h->got_entries;
  	  symval = h->root.root.u.def.value;
  	}
  
        /* Search for the got entry to be used by this relocation.  */
!       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;
  
!       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 (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;
  
! 	case R_ALPHA_GPRELHIGH:
! 	case R_ALPHA_GPRELLOW:
! 	  if (!elf64_alpha_relax_gprelhilo (&info, symval, irel,
! 					    r_type == R_ALPHA_GPRELHIGH))
  	    goto error_return;
! 	  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;
  	}
      }
  
*************** elf64_alpha_relax_section (abfd, sec, li
*** 1809,1826 ****
      return false;
  
    if (info.changed_relocs)
!     {
!       elf_section_data (sec)->relocs = internal_relocs;
!     }
    else if (free_relocs != NULL)
!     {
!       free (free_relocs);
!     }
  
    if (info.changed_contents)
!     {
!       elf_section_data (sec)->this_hdr.contents = info.contents;
!     }
    else if (free_contents != NULL)
      {
        if (! link_info->keep_memory)
--- 2255,2266 ----
      return false;
  
    if (info.changed_relocs)
!     elf_section_data (sec)->relocs = internal_relocs;
    else if (free_relocs != NULL)
!     free (free_relocs);
  
    if (info.changed_contents)
!     elf_section_data (sec)->this_hdr.contents = info.contents;
    else if (free_contents != NULL)
      {
        if (! link_info->keep_memory)
*************** elf64_alpha_check_relocs (abfd, info, se
*** 2674,2679 ****
--- 3114,3120 ----
  
  	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;
*************** elf64_alpha_relocate_section (output_bfd
*** 3653,3664 ****
        tls_segment = elf_hash_table (info)->tls_segment;
        if (tls_segment)
          {
!           /* This is PT_TLS segment p_vaddr.  */
!           dtp_base = tls_segment->start;
! 
!           /* 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);
          }
      }
  
--- 4094,4101 ----
        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);
          }
      }
  
*************** elf64_alpha_relocate_section (output_bfd
*** 4156,4167 ****
              }
  	  BFD_ASSERT(tls_segment != NULL);
  	  value -= dtp_base;
  	  goto default_reloc;
  
  	case R_ALPHA_TPRELHI:
  	case R_ALPHA_TPRELLO:
  	case R_ALPHA_TPREL16:
! 	  if (dynamic_symbol_p)
              {
                (*_bfd_error_handler)
                  (_("%s: tp-relative relocation against dynamic symbol %s"),
--- 4593,4613 ----
              }
  	  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"),
*************** elf64_alpha_relocate_section (output_bfd
*** 4170,4175 ****
--- 4616,4623 ----
              }
  	  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:


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]