[SPARC] Fix PR ld/22263

Message ID 2115454.pZlpFT49hm@polaris
State New
Headers show
Series
  • [SPARC] Fix PR ld/22263
Related show

Commit Message

Eric Botcazou Jan. 29, 2018, 11:57 p.m.
Hi,

this is -fpie -pie generating dynamic relocations in the text section, simply 
because no TLS transitions are applied in PIE mode.  As diagnosed by H.J., the 
meat of the patch is to turn calls to bfd_link_pic (info) in TLS-related code 
into !bfd_link_executable (info) and there are quite a lot of them...
It also contains a few obvious cleanups and formatting fixes and was tested 
with a bootstrap/regtest of GCC mainline on SPARC64/Linux.  OK for mainline?


2018-01-29  Eric Botcazou  <ebotcazou@adacore.com>

bfd/
	* elfxx-sparc.c (sparc_elf_tls_transition): Turn call to bfd_link_pic
	into call to !bfd_link_executable and tidy up.
	(_bfd_sparc_elf_check_relocs): Fix formatting and tidy up.
	<R_SPARC_TLS_LE_HIX22>: Turn call to bfd_link_pic into call to
	!bfd_link_executable.
	<R_SPARC_TLS_IE_HI22>: Likewise.
	<GOT relocations>: Remove useless code, tidy up and merge related blocks.
	<R_SPARC_TLS_GD_CALL>: Turn call to bfd_link_pic into call to
	!bfd_link_executable.
	<R_SPARC_WPLT30>: Tidy up.
	(_bfd_sparc_elf_gc_mark_hook): Turn call to bfd_link_pic into call to
	!bfd_link_executable.
	(allocate_dynrelocs): Likewise.
	(_bfd_sparc_elf_relocate_section): Fix formatting and tidy up.
	<R_SPARC_TLS_GD_HI22>: Merge into...
	<R_SPARC_TLS_GD_LO10>: ...this.  Adjust 4th argument in call to
	sparc_elf_tls_transition and remove redundant code.
	<R_SPARC_TLS_LDM_HI22>: Turn call to bfd_link_pic into call to
	!bfd_link_executable.
	<R_SPARC_TLS_LDO_HIX22>: Likewise.
	<R_SPARC_TLS_LE_HIX22>: Likewise.  Tidy up.
	<R_SPARC_TLS_LDM_CALL>: Likewise.
	<R_SPARC_TLS_GD_CALL>: Likewise.  Tidy up.
	<R_SPARC_TLS_GD_ADD>: Likewise.
	<R_SPARC_TLS_LDM_ADD>: Likewise.
	<R_SPARC_TLS_LDO_ADD>: Likewise.
	<R_SPARC_TLS_IE_LD>: Likewise.
ld/
	* testsuite/ld-elf/tls.exp (AFLAGS_PIC): Define on SPARC.
	(pr22263-1): Pass AFLAGS_PIC to the assembler.
	* testsuite/ld-sparc/tlspie32.s: Add test for other 3 transitions.
	* testsuite/ld-sparc/tlspie32.dd: Adjust to above.
	* testsuite/ld-sparc/tlspie64.s: Add test for other 3 transitions.
	* testsuite/ld-sparc/tlspie64.dd: Adjust to above.

-- 
Eric Botcazou

Patch

diff --git a/bfd/elfxx-sparc.c b/bfd/elfxx-sparc.c
index 9f9fedadfc..afd62d4a50 100644
--- a/bfd/elfxx-sparc.c
+++ b/bfd/elfxx-sparc.c
@@ -1331,33 +1331,25 @@  sparc_elf_tls_transition (struct bfd_link_info *info, bfd *abfd,
   if (! ABI_64_P (abfd)
       && r_type == R_SPARC_TLS_GD_HI22
       && ! _bfd_sparc_elf_tdata (abfd)->has_tlsgd)
-    r_type = R_SPARC_REV32;
+    return R_SPARC_REV32;
 
-  if (bfd_link_pic (info))
+  if (!bfd_link_executable (info))
     return r_type;
 
   switch (r_type)
     {
     case R_SPARC_TLS_GD_HI22:
-      if (is_local)
-	return R_SPARC_TLS_LE_HIX22;
-      return R_SPARC_TLS_IE_HI22;
+      return is_local ? R_SPARC_TLS_LE_HIX22 : R_SPARC_TLS_IE_HI22;
     case R_SPARC_TLS_GD_LO10:
-      if (is_local)
-	return R_SPARC_TLS_LE_LOX10;
-      return R_SPARC_TLS_IE_LO10;
-    case R_SPARC_TLS_IE_HI22:
-      if (is_local)
-	return R_SPARC_TLS_LE_HIX22;
-      return r_type;
-    case R_SPARC_TLS_IE_LO10:
-      if (is_local)
-	return R_SPARC_TLS_LE_LOX10;
-      return r_type;
+      return is_local ? R_SPARC_TLS_LE_LOX10 : R_SPARC_TLS_IE_LO10;
     case R_SPARC_TLS_LDM_HI22:
       return R_SPARC_TLS_LE_HIX22;
     case R_SPARC_TLS_LDM_LO10:
       return R_SPARC_TLS_LE_LOX10;
+    case R_SPARC_TLS_IE_HI22:
+      return is_local ? R_SPARC_TLS_LE_HIX22 : r_type;
+    case R_SPARC_TLS_IE_LO10:
+      return is_local ? R_SPARC_TLS_LE_LOX10 : r_type;
     }
 
   return r_type;
@@ -1425,16 +1417,14 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       if (r_symndx < symtab_hdr->sh_info)
 	{
 	  /* A local symbol.  */
-	  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
-					abfd, r_symndx);
+	  isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx);
 	  if (isym == NULL)
 	    return FALSE;
 
 	  /* Check relocation against local STT_GNU_IFUNC symbol.  */
 	  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
 	    {
-	      h = elf_sparc_get_local_sym_hash (htab, abfd, rel,
-						TRUE);
+	      h = elf_sparc_get_local_sym_hash (htab, abfd, rel, TRUE);
 	      if (h == NULL)
 		return FALSE;
 
@@ -1456,13 +1446,10 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 	}
 
-      if (h && h->type == STT_GNU_IFUNC)
+      if (h && h->type == STT_GNU_IFUNC && h->def_regular)
 	{
-	  if (h->def_regular)
-	    {
-	      h->ref_regular = 1;
-	      h->plt.refcount += 1;
-	    }
+	  h->ref_regular = 1;
+	  h->plt.refcount += 1;
 	}
 
       /* Compatibility with old R_SPARC_REV32 reloc conflicting
@@ -1505,13 +1492,13 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	case R_SPARC_TLS_LE_HIX22:
 	case R_SPARC_TLS_LE_LOX10:
-	  if (bfd_link_pic (info))
+	  if (!bfd_link_executable (info))
 	    goto r_sparc_plt32;
 	  break;
 
 	case R_SPARC_TLS_IE_HI22:
 	case R_SPARC_TLS_IE_LO10:
-	  if (bfd_link_pic (info))
+	  if (!bfd_link_executable (info))
 	    info->flags |= DF_STATIC_TLS;
 	  /* Fall through */
 
@@ -1530,14 +1517,6 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	    switch (r_type)
 	      {
-	      default:
-	      case R_SPARC_GOT10:
-	      case R_SPARC_GOT13:
-	      case R_SPARC_GOT22:
-	      case R_SPARC_GOTDATA_OP_HIX22:
-	      case R_SPARC_GOTDATA_OP_LOX10:
-		tls_type = GOT_NORMAL;
-		break;
 	      case R_SPARC_TLS_GD_HI22:
 	      case R_SPARC_TLS_GD_LO10:
 		tls_type = GOT_TLS_GD;
@@ -1546,6 +1525,9 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	      case R_SPARC_TLS_IE_LO10:
 		tls_type = GOT_TLS_IE;
 		break;
+	      default:
+		tls_type = GOT_NORMAL;
+		break;
 	      }
 
 	    if (h != NULL)
@@ -1573,26 +1555,23 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		    _bfd_sparc_elf_local_got_tls_type (abfd)
 		      = (char *) (local_got_refcounts + symtab_hdr->sh_info);
 		  }
-		switch (r_type)
-		  {
-		  case R_SPARC_GOTDATA_OP_HIX22:
-		  case R_SPARC_GOTDATA_OP_LOX10:
-		    break;
 
-		  default:
-		    local_got_refcounts[r_symndx] += 1;
-		    break;
-		  }
+		if (r_type != R_SPARC_GOTDATA_OP_HIX22
+		    && r_type != R_SPARC_GOTDATA_OP_LOX10)
+		  local_got_refcounts[r_symndx] += 1;
+
 		old_tls_type = _bfd_sparc_elf_local_got_tls_type (abfd) [r_symndx];
 	      }
 
-	    /* If a TLS symbol is accessed using IE at least once,
-	       there is no point to use dynamic model for it.  */
-	    if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN
-		&& (old_tls_type != GOT_TLS_GD
-		    || tls_type != GOT_TLS_IE))
+	    /* If a TLS symbol is accessed using IE at least once, there is no point
+	       in using the dynamic model for it.  */
+	    if (old_tls_type != tls_type)
 	      {
-		if (old_tls_type == GOT_TLS_IE && tls_type == GOT_TLS_GD)
+		if (old_tls_type == GOT_UNKNOWN)
+		  ;
+		else if (old_tls_type == GOT_TLS_GD && tls_type == GOT_TLS_IE)
+		  ;
+		else if (old_tls_type == GOT_TLS_IE && tls_type == GOT_TLS_GD)
 		  tls_type = old_tls_type;
 		else
 		  {
@@ -1602,10 +1581,7 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		       abfd, h ? h->root.root.string : "<local>");
 		    return FALSE;
 		  }
-	      }
 
-	    if (old_tls_type != tls_type)
-	      {
 		if (h != NULL)
 		  _bfd_sparc_elf_hash_entry (h)->tls_type = tls_type;
 		else
@@ -1613,11 +1589,9 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	      }
 	  }
 
-	  if (htab->elf.sgot == NULL)
-	    {
-	      if (!_bfd_elf_create_got_section (htab->elf.dynobj, info))
-		return FALSE;
-	    }
+	  if (!htab->elf.sgot
+	      && !_bfd_elf_create_got_section (htab->elf.dynobj, info))
+	    return FALSE;
 
 	  if (eh != NULL)
 	    eh->has_got_reloc = 1;
@@ -1625,28 +1599,25 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	case R_SPARC_TLS_GD_CALL:
 	case R_SPARC_TLS_LDM_CALL:
-	  if (bfd_link_pic (info))
-	    {
-	      /* These are basically R_SPARC_TLS_WPLT30 relocs against
-		 __tls_get_addr.  */
-	      h = (struct elf_link_hash_entry *)
-		  bfd_link_hash_lookup (info->hash, "__tls_get_addr", TRUE,
-					FALSE, TRUE);
-	    }
-	  else
+	  if (bfd_link_executable (info))
 	    break;
+
+	  /* Essentially R_SPARC_WPLT30 relocs against __tls_get_addr.  */
+	  h = (struct elf_link_hash_entry *)
+	       bfd_link_hash_lookup (info->hash, "__tls_get_addr", TRUE,
+				     FALSE, TRUE);
 	  /* Fall through */
 
-	case R_SPARC_PLT32:
 	case R_SPARC_WPLT30:
+	case R_SPARC_PLT32:
+	case R_SPARC_PLT64:
 	case R_SPARC_HIPLT22:
 	case R_SPARC_LOPLT10:
 	case R_SPARC_PCPLT32:
 	case R_SPARC_PCPLT22:
 	case R_SPARC_PCPLT10:
-	case R_SPARC_PLT64:
-	  /* This symbol requires a procedure linkage table entry.  We
-	     actually build the entry in adjust_dynamic_symbol,
+	  /* This symbol requires a procedure linkage table entry.
+	     We actually build the entry in adjust_dynamic_symbol,
 	     because this might be a case of linking PIC code without
 	     linking in any dynamic objects, in which case we don't
 	     need to generate a procedure linkage table after all.  */
@@ -1659,7 +1630,7 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 		     reloc for a local symbol if you assemble a call from
 		     one section to another when using -K pic.  We treat
 		     it as WDISP30.  */
-		  if (ELF32_R_TYPE (rel->r_info) == R_SPARC_PLT32)
+		  if (r_type == R_SPARC_PLT32)
 		    goto r_sparc_plt32;
 		  break;
 		}
@@ -1675,14 +1646,9 @@  _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	  h->needs_plt = 1;
 
-	  {
-	    int this_r_type;
+	  if (r_type == R_SPARC_PLT32 || r_type == R_SPARC_PLT64)
+	    goto r_sparc_plt32;
 
-	    this_r_type = SPARC_ELF_R_TYPE (rel->r_info);
-	    if (this_r_type == R_SPARC_PLT32
-		|| this_r_type == R_SPARC_PLT64)
-	      goto r_sparc_plt32;
-	  }
 	  h->plt.refcount += 1;
 
 	  eh = (struct _bfd_sparc_elf_link_hash_entry *) h;
@@ -1886,9 +1852,7 @@  _bfd_sparc_elf_gc_mark_hook (asection *sec,
 	return NULL;
       }
 
-  /* FIXME: The test here, in check_relocs and in relocate_section
-     dealing with TLS optimization, ought to be !bfd_link_executable (info).  */
-  if (bfd_link_pic (info))
+  if (!bfd_link_executable (info))
     {
       switch (SPARC_ELF_R_TYPE (rel->r_info))
 	{
@@ -2227,7 +2191,7 @@  allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
   /* If R_SPARC_TLS_IE_{HI22,LO10} symbol is now local to the binary,
      make it a R_SPARC_TLS_LE_{HI22,LO10} requiring no TLS entry.  */
   if (h->got.refcount > 0
-      && !bfd_link_pic (info)
+      && bfd_link_executable (info)
       && h->dynindx == -1
       && _bfd_sparc_elf_hash_entry(h)->tls_type == GOT_TLS_IE)
     h->got.offset = (bfd_vma) -1;
@@ -2963,8 +2927,8 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 	  bfd_set_error (bfd_error_bad_value);
 	  return FALSE;
 	}
-      howto = _bfd_sparc_elf_howto_table + r_type;
 
+      howto = _bfd_sparc_elf_howto_table + r_type;
       r_symndx = SPARC_ELF_R_SYMNDX (htab, rel->r_info);
       h = NULL;
       sym = NULL;
@@ -3131,8 +3095,7 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 	}
 
       eh = (struct _bfd_sparc_elf_link_hash_entry *) h;
-      resolved_to_zero = (eh != NULL
-			  && UNDEFINED_WEAK_RESOLVED_TO_ZERO (info, eh));
+      resolved_to_zero = eh && UNDEFINED_WEAK_RESOLVED_TO_ZERO (info, eh);
 
       switch (r_type)
 	{
@@ -3378,8 +3341,7 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 	case R_SPARC_H34:
 	case R_SPARC_UA64:
 	r_sparc_plt32:
-	  if ((input_section->flags & SEC_ALLOC) == 0
-	      || is_vxworks_tls)
+	  if ((input_section->flags & SEC_ALLOC) == 0 || is_vxworks_tls)
 	    break;
 
 	  /* Copy dynamic function pointer relocations.  Don't generate
@@ -3551,40 +3513,19 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 	  break;
 
 	case R_SPARC_TLS_GD_HI22:
-	  if (! ABI_64_P (input_bfd)
-	      && ! _bfd_sparc_elf_tdata (input_bfd)->has_tlsgd)
-	    {
-	      /* R_SPARC_REV32 used the same reloc number as
-		 R_SPARC_TLS_GD_HI22.  */
-	      r_type = R_SPARC_REV32;
-	      break;
-	    }
-	  /* Fall through */
-
 	case R_SPARC_TLS_GD_LO10:
 	case R_SPARC_TLS_IE_HI22:
 	case R_SPARC_TLS_IE_LO10:
-	  r_type = sparc_elf_tls_transition (info, input_bfd, r_type, h == NULL);
-	  tls_type = GOT_UNKNOWN;
-	  if (h == NULL && local_got_offsets)
+	  r_type = sparc_elf_tls_transition (info, input_bfd, r_type,
+					     h == NULL || h->dynindx == -1);
+	  if (r_type == R_SPARC_REV32)
+	    break;
+	  if (h != NULL)
+	    tls_type = _bfd_sparc_elf_hash_entry (h)->tls_type;
+	  else if (local_got_offsets)
 	    tls_type = _bfd_sparc_elf_local_got_tls_type (input_bfd) [r_symndx];
-	  else if (h != NULL)
-	    {
-	      tls_type = _bfd_sparc_elf_hash_entry(h)->tls_type;
-	      if (!bfd_link_pic (info)
-		  && h->dynindx == -1
-		  && tls_type == GOT_TLS_IE)
-		switch (SPARC_ELF_R_TYPE (rel->r_info))
-		  {
-		  case R_SPARC_TLS_GD_HI22:
-		  case R_SPARC_TLS_IE_HI22:
-		    r_type = R_SPARC_TLS_LE_HIX22;
-		    break;
-		  default:
-		    r_type = R_SPARC_TLS_LE_LOX10;
-		    break;
-		  }
-	    }
+	  else
+	    tls_type = GOT_UNKNOWN;
 	  if (tls_type == GOT_TLS_IE)
 	    switch (r_type)
 	      {
@@ -3695,7 +3636,8 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 
 	case R_SPARC_TLS_LDM_HI22:
 	case R_SPARC_TLS_LDM_LO10:
-	  if (! bfd_link_pic (info))
+	  /* LD -> LE */
+	  if (bfd_link_executable (info))
 	    {
 	      bfd_put_32 (output_bfd, SPARC_NOP, contents + rel->r_offset);
 	      continue;
@@ -3706,43 +3648,42 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 
 	case R_SPARC_TLS_LDO_HIX22:
 	case R_SPARC_TLS_LDO_LOX10:
-	  if (bfd_link_pic (info))
+	  /* LD -> LE */
+	  if (bfd_link_executable (info))
+	    {
+	      if (r_type == R_SPARC_TLS_LDO_HIX22)
+		r_type = R_SPARC_TLS_LE_HIX22;
+	      else
+		r_type = R_SPARC_TLS_LE_LOX10;
+	    }
+	  else
 	    {
 	      relocation -= dtpoff_base (info);
 	      break;
 	    }
-
-	  r_type = (r_type == R_SPARC_TLS_LDO_HIX22
-		    ? R_SPARC_TLS_LE_HIX22 : R_SPARC_TLS_LE_LOX10);
 	  /* Fall through.  */
 
 	case R_SPARC_TLS_LE_HIX22:
 	case R_SPARC_TLS_LE_LOX10:
-	  if (bfd_link_pic (info))
+	  if (!bfd_link_executable (info))
 	    {
 	      Elf_Internal_Rela outrel;
-	      bfd_boolean skip;
-
-	      BFD_ASSERT (sreloc != NULL);
-	      skip = FALSE;
-	      outrel.r_offset =
-		_bfd_elf_section_offset (output_bfd, info, input_section,
-					 rel->r_offset);
-	      if (outrel.r_offset == (bfd_vma) -1)
-		skip = TRUE;
-	      else if (outrel.r_offset == (bfd_vma) -2)
-		skip = TRUE;
-	      outrel.r_offset += (input_section->output_section->vma
-				  + input_section->output_offset);
-	      if (skip)
+	      bfd_vma offset
+		= _bfd_elf_section_offset (output_bfd, info, input_section,
+					   rel->r_offset);
+	      if (offset == (bfd_vma) -1 || offset == (bfd_vma) -2)
 		memset (&outrel, 0, sizeof outrel);
 	      else
 		{
+		  outrel.r_offset = offset
+				    + input_section->output_section->vma
+				    + input_section->output_offset;
 		  outrel.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, r_type);
-		  outrel.r_addend = relocation - dtpoff_base (info)
-				    + rel->r_addend;
+		  outrel.r_addend
+		    = relocation - dtpoff_base (info) + rel->r_addend;
 		}
 
+	      BFD_ASSERT (sreloc != NULL);
 	      sparc_elf_append_rela (output_bfd, sreloc, &outrel);
 	      continue;
 	    }
@@ -3750,7 +3691,8 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 	  break;
 
 	case R_SPARC_TLS_LDM_CALL:
-	  if (! bfd_link_pic (info))
+	  /* LD -> LE */
+	  if (bfd_link_executable (info))
 	    {
 	      /* mov %g0, %o0 */
 	      bfd_put_32 (output_bfd, 0x90100000, contents + rel->r_offset);
@@ -3759,20 +3701,22 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 	  /* Fall through */
 
 	case R_SPARC_TLS_GD_CALL:
-	  tls_type = GOT_UNKNOWN;
-	  if (h == NULL && local_got_offsets)
+	  if (h != NULL)
+	    tls_type = _bfd_sparc_elf_hash_entry (h)->tls_type;
+	  else if (local_got_offsets)
 	    tls_type = _bfd_sparc_elf_local_got_tls_type (input_bfd) [r_symndx];
-	  else if (h != NULL)
-	    tls_type = _bfd_sparc_elf_hash_entry(h)->tls_type;
-	  if (! bfd_link_pic (info)
+	  else
+	    tls_type = GOT_UNKNOWN;
+	  /* GD -> IE or LE */
+	  if (bfd_link_executable (info)
 	      || (r_type == R_SPARC_TLS_GD_CALL && tls_type == GOT_TLS_IE))
 	    {
 	      Elf_Internal_Rela *rel2;
 	      bfd_vma insn;
 
-	      if (!bfd_link_pic (info) && (h == NULL || h->dynindx == -1))
+	      /* GD -> LE */
+	      if (bfd_link_executable (info) && (h == NULL || h->dynindx == -1))
 		{
-		  /* GD -> LE */
 		  bfd_put_32 (output_bfd, SPARC_NOP, contents + rel->r_offset);
 		  continue;
 		}
@@ -3835,12 +3779,14 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 	  goto r_sparc_wplt30;
 
 	case R_SPARC_TLS_GD_ADD:
-	  tls_type = GOT_UNKNOWN;
-	  if (h == NULL && local_got_offsets)
+	  if (h != NULL)
+	    tls_type = _bfd_sparc_elf_hash_entry (h)->tls_type;
+	  else if (local_got_offsets)
 	    tls_type = _bfd_sparc_elf_local_got_tls_type (input_bfd) [r_symndx];
-	  else if (h != NULL)
-	    tls_type = _bfd_sparc_elf_hash_entry(h)->tls_type;
-	  if (! bfd_link_pic (info) || tls_type == GOT_TLS_IE)
+	  else
+	    tls_type = GOT_UNKNOWN;
+	  /* GD -> IE or LE */
+	  if (bfd_link_executable (info) || tls_type == GOT_TLS_IE)
 	    {
 	      /* add %reg1, %reg2, %reg3, %tgd_add(foo)
 		 changed into IE:
@@ -3848,21 +3794,23 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 		 or LE:
 		 add %g7, %reg2, %reg3.  */
 	      bfd_vma insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
-	      if ((h != NULL && h->dynindx != -1) || bfd_link_pic (info))
-		relocation = insn | (ABI_64_P (output_bfd) ? 0xc0580000 : 0xc0000000);
-	      else
+	      if (bfd_link_executable (info) && (h == NULL || h->dynindx == -1))
 		relocation = (insn & ~0x7c000) | 0x1c000;
+	      else
+		relocation = insn | (ABI_64_P (output_bfd) ? 0xc0580000 : 0xc0000000);
 	      bfd_put_32 (output_bfd, relocation, contents + rel->r_offset);
 	    }
 	  continue;
 
 	case R_SPARC_TLS_LDM_ADD:
-	  if (! bfd_link_pic (info))
+	  /* LD -> LE */
+	  if (bfd_link_executable (info))
 	    bfd_put_32 (output_bfd, SPARC_NOP, contents + rel->r_offset);
 	  continue;
 
 	case R_SPARC_TLS_LDO_ADD:
-	  if (! bfd_link_pic (info))
+	  /* LD -> LE */
+	  if (bfd_link_executable (info))
 	    {
 	      /* Change rs1 into %g7.  */
 	      bfd_vma insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
@@ -3873,7 +3821,8 @@  _bfd_sparc_elf_relocate_section (bfd *output_bfd,
 
 	case R_SPARC_TLS_IE_LD:
 	case R_SPARC_TLS_IE_LDX:
-	  if (! bfd_link_pic (info) && (h == NULL || h->dynindx == -1))
+	  /* IE -> LE */
+	  if (bfd_link_executable (info) && (h == NULL || h->dynindx == -1))
 	    {
 	      bfd_vma insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
 	      int rs2 = insn & 0x1f;
diff --git a/ld/testsuite/ld-elf/tls.exp b/ld/testsuite/ld-elf/tls.exp
index f43b0b46a1..de5a86b5e5 100644
--- a/ld/testsuite/ld-elf/tls.exp
+++ b/ld/testsuite/ld-elf/tls.exp
@@ -37,12 +37,18 @@  if { [which $CC] == 0 } {
     return
 }
 
+# This target requires extra GAS options when building PIC/PIE code.
+set AFLAGS_PIC ""
+if [istarget "sparc*-*-*"] {
+    append AFLAGS_PIC " -K PIC"
+}
+
 run_ld_link_tests [list \
     [list \
 	"Build pr22263-1" \
 	"-pie -e _start -z text" \
 	"" \
-	"" \
+	"$AFLAGS_PIC" \
 	{ pr22263-1a.c pr22263-1b.c } \
 	{{readelf -r pr22263-1.rd}} \
 	"pr22263-1" \
diff --git a/ld/testsuite/ld-sparc/tlspie32.dd b/ld/testsuite/ld-sparc/tlspie32.dd
index c31bcf5eef..a31e11a074 100644
--- a/ld/testsuite/ld-sparc/tlspie32.dd
+++ b/ld/testsuite/ld-sparc/tlspie32.dd
@@ -10,9 +10,56 @@ 
 
 Disassembly of section .text:
 
-0[0-9a-f]+ <get_gdp>:
+0[0-9a-f]+ <foo-0x8>:
+ +[0-9a-f]+:	81 c3 e0 08 	retl 
+ +[0-9a-f]+:	ae 03 c0 17 	add  %o7, %l7, %l7
+
+0[0-9a-f]+ <foo>:
+ +[0-9a-f]+:	9d e3 bf 98 	save  %sp, -104, %sp
+ +[0-9a-f]+:	2f 00 00 40 	sethi  %hi\(0x10000\), %l7
+ +[0-9a-f]+:	7f ff ff fc 	call  [0-9a-f]+ <.*>
+ +[0-9a-f]+:	ae 05 e1 60 	add  %l7, 0x160, %l7	! 10160 <.*>
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	03 00 00 00 	sethi  %hi\(0\), %g1
+ +[0-9a-f]+:	82 00 60 04 	add  %g1, 4, %g1	! 4 <.*>
+ +[0-9a-f]+:	d0 05 c0 01 	ld  \[ %l7 \+ %g1 ], %o0
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	90 01 c0 08 	add  %g7, %o0, %o0
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
  +[0-9a-f]+:	03 00 00 00 	sethi  %hi\(0\), %g1
- +[0-9a-f]+:	82 00 60 04 	add  %g1, 4, %g1	! 4 <tls_gd\+0x4>
- +[0-9a-f]+:	90 05 c0 01 	add  %l7, %g1, %o0
- +[0-9a-f]+:	40 00 40 43 	call  [0-9a-f]+ <__tls_get_addr@plt>
+ +[0-9a-f]+:	82 18 7f f4 	xor  %g1, -12, %g1
+ +[0-9a-f]+:	90 01 c0 01 	add  %g7, %g1, %o0
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	90 10 00 00 	mov  %g0, %o0
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	03 00 00 00 	sethi  %hi\(0\), %g1
+ +[0-9a-f]+:	82 18 7f f8 	xor  %g1, -8, %g1
+ +[0-9a-f]+:	82 01 c0 01 	add  %g7, %g1, %g1
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	03 00 00 00 	sethi  %hi\(0\), %g1
+ +[0-9a-f]+:	82 18 7f fc 	xor  %g1, -4, %g1
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	82 01 c0 01 	add  %g7, %g1, %g1
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
  +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	81 c7 e0 08 	ret 
+ +[0-9a-f]+:	81 e8 00 00 	restore 
diff --git a/ld/testsuite/ld-sparc/tlspie32.s b/ld/testsuite/ld-sparc/tlspie32.s
index 4d38d307cb..a63aca6d52 100644
--- a/ld/testsuite/ld-sparc/tlspie32.s
+++ b/ld/testsuite/ld-sparc/tlspie32.s
@@ -1,20 +1,70 @@ 
-	.section	".text"
-	.global tls_gd
 	.section	.tbss,"awT",@nobits
+	.global tls_gd
 	.align 4
 	.type	tls_gd, #object
 	.size	tls_gd, 4
 tls_gd:
 	.skip	4
+	.global tls_ld
+	.align 4
+	.type	tls_ld, #object
+	.size	tls_ld, 4
+tls_ld:
+	.skip	4
+	.global tls_ie
+	.align 4
+	.type	tls_ie, #object
+	.size	tls_ie, 4
+tls_ie:
+	.skip	4
 	.section	".text"
+.LLGETPC0:
+	retl
+	  add     %o7, %l7, %l7
 	.align 4
-	.global get_gdp
-	.type	get_gdp, #function
+	.global foo
+	.type	foo, #function
 	.proc	0104
-get_gdp:
+foo:
+	save	%sp, -104, %sp
+	sethi	%hi(_GLOBAL_OFFSET_TABLE_-4), %l7
+	call	.LLGETPC0
+	add	%l7, %lo(_GLOBAL_OFFSET_TABLE_+4), %l7
+	nop;nop;nop;nop
+
+	/* GD -> IE with global variable not defined in executable */
+	sethi	%tgd_hi22(sG1), %g1
+	add	%g1, %tgd_lo10(sG1), %g1
+	add	%l7, %g1, %o0, %tgd_add(sG1)
+	call	__tls_get_addr, %tgd_call(sG1)
+	 nop
+	nop;nop;nop;nop
+
+	/* GD -> LE with global variable defined in executable */
 	sethi	%tgd_hi22(tls_gd), %g1
 	add	%g1, %tgd_lo10(tls_gd), %g1
 	add	%l7, %g1, %o0, %tgd_add(tls_gd)
 	call	__tls_get_addr, %tgd_call(tls_gd)
 	 nop
-	.size	get_gdp, .-get_gdp
+	nop;nop;nop;nop
+
+	/* LD -> LE with global variable defined in executable */
+	sethi	%tldm_hi22(tls_ld), %g1
+	add	%g1, %tldm_lo10(tls_ld), %g1
+	add	%l7, %g1, %o0, %tldm_add(tls_ld)
+	call	__tls_get_addr, %tldm_call(tls_ld)
+	 nop
+	sethi	%tldo_hix22(tls_ld), %g1
+        xor	%g1, %tldo_lox10(tls_ld), %g1
+        add	%o0, %g1, %g1, %tldo_add(tls_ld)
+	nop;nop;nop;nop
+
+	/* IE -> LE with global variable defined in executable */
+	sethi	%tie_hi22(tls_ie), %g1
+	add	%g1, %tie_lo10(tls_ie), %g1
+	ld	[%l7 + %g1], %g1, %tie_ld(tls_ie)
+	add	%g7, %g1, %g1, %tie_add(tls_ie)
+	nop;nop;nop;nop
+
+	ret
+	restore
diff --git a/ld/testsuite/ld-sparc/tlspie64.dd b/ld/testsuite/ld-sparc/tlspie64.dd
index bc4532e8e3..6c8aea9288 100644
--- a/ld/testsuite/ld-sparc/tlspie64.dd
+++ b/ld/testsuite/ld-sparc/tlspie64.dd
@@ -10,9 +10,56 @@ 
 
 Disassembly of section .text:
 
-0[0-9a-f]+ <get_gdp>:
+0[0-9a-f]+ <foo-0x8>:
+ +[0-9a-f]+:	81 c3 e0 08 	retl 
+ +[0-9a-f]+:	ae 03 c0 17 	add  %o7, %l7, %l7
+
+0[0-9a-f]+ <foo>:
+ +[0-9a-f]+:	9d e3 bf 60 	save  %sp, -160, %sp
+ +[0-9a-f]+:	2f 00 04 00 	sethi  %hi\(0x100000\), %l7
+ +[0-9a-f]+:	7f ff ff fc 	call  [0-9a-f]+ <.*>
+ +[0-9a-f]+:	ae 05 e2 08 	add  %l7, 0x208, %l7	! 100208 <.*>
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	03 00 00 00 	sethi  %hi\(0\), %g1
+ +[0-9a-f]+:	82 00 60 08 	add  %g1, 8, %g1	! 8 <.*>
+ +[0-9a-f]+:	d0 5d c0 01 	ldx  \[ %l7 \+ %g1 ], %o0
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	90 01 c0 08 	add  %g7, %o0, %o0
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
  +[0-9a-f]+:	03 00 00 00 	sethi  %hi\(0\), %g1
- +[0-9a-f]+:	82 00 60 08 	add  %g1, 8, %g1	! 8 <tls_gd\+0x8>
- +[0-9a-f]+:	90 05 c0 01 	add  %l7, %g1, %o0
- +[0-9a-f]+:	40 04 00 bb 	call  [0-9a-f]+ <__tls_get_addr@plt>
+ +[0-9a-f]+:	82 18 7f f4 	xor  %g1, -12, %g1
+ +[0-9a-f]+:	90 01 c0 01 	add  %g7, %g1, %o0
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	90 10 00 00 	mov  %g0, %o0
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	03 00 00 00 	sethi  %hi\(0\), %g1
+ +[0-9a-f]+:	82 18 7f f8 	xor  %g1, -8, %g1
+ +[0-9a-f]+:	82 01 c0 01 	add  %g7, %g1, %g1
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	03 00 00 00 	sethi  %hi\(0\), %g1
+ +[0-9a-f]+:	82 18 7f fc 	xor  %g1, -4, %g1
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	82 01 c0 01 	add  %g7, %g1, %g1
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	01 00 00 00 	nop 
  +[0-9a-f]+:	01 00 00 00 	nop 
+ +[0-9a-f]+:	81 c7 e0 08 	ret 
+ +[0-9a-f]+:	81 e8 00 00 	restore 
diff --git a/ld/testsuite/ld-sparc/tlspie64.s b/ld/testsuite/ld-sparc/tlspie64.s
index 4d38d307cb..7d47da7acb 100644
--- a/ld/testsuite/ld-sparc/tlspie64.s
+++ b/ld/testsuite/ld-sparc/tlspie64.s
@@ -1,20 +1,70 @@ 
-	.section	".text"
-	.global tls_gd
 	.section	.tbss,"awT",@nobits
+	.global tls_gd
 	.align 4
 	.type	tls_gd, #object
 	.size	tls_gd, 4
 tls_gd:
 	.skip	4
+	.global tls_ld
+	.align 4
+	.type	tls_ld, #object
+	.size	tls_ld, 4
+tls_ld:
+	.skip	4
+	.global tls_ie
+	.align 4
+	.type	tls_ie, #object
+	.size	tls_ie, 4
+tls_ie:
+	.skip	4
 	.section	".text"
+.LLGETPC0:
+	retl
+	  add     %o7, %l7, %l7
 	.align 4
-	.global get_gdp
-	.type	get_gdp, #function
+	.global foo
+	.type	foo, #function
 	.proc	0104
-get_gdp:
+foo:
+	save	%sp, -160, %sp
+	sethi	%hi(_GLOBAL_OFFSET_TABLE_-4), %l7
+	call	.LLGETPC0
+	add	%l7, %lo(_GLOBAL_OFFSET_TABLE_+4), %l7
+	nop;nop;nop;nop
+
+	/* GD -> IE with global variable not defined in executable */
+	sethi	%tgd_hi22(sG1), %g1
+	add	%g1, %tgd_lo10(sG1), %g1
+	add	%l7, %g1, %o0, %tgd_add(sG1)
+	call	__tls_get_addr, %tgd_call(sG1)
+	 nop
+	nop;nop;nop;nop
+
+	/* GD -> LE with global variable defined in executable */
 	sethi	%tgd_hi22(tls_gd), %g1
 	add	%g1, %tgd_lo10(tls_gd), %g1
 	add	%l7, %g1, %o0, %tgd_add(tls_gd)
 	call	__tls_get_addr, %tgd_call(tls_gd)
 	 nop
-	.size	get_gdp, .-get_gdp
+	nop;nop;nop;nop
+
+	/* LD -> LE with global variable defined in executable */
+	sethi	%tldm_hi22(tls_ld), %g1
+	add	%g1, %tldm_lo10(tls_ld), %g1
+	add	%l7, %g1, %o0, %tldm_add(tls_ld)
+	call	__tls_get_addr, %tldm_call(tls_ld)
+	 nop
+	sethi	%tldo_hix22(tls_ld), %g1
+        xor	%g1, %tldo_lox10(tls_ld), %g1
+        add	%o0, %g1, %g1, %tldo_add(tls_ld)
+	nop;nop;nop;nop
+
+	/* IE -> LE with global variable defined in executable */
+	sethi	%tie_hi22(tls_ie), %g1
+	add	%g1, %tie_lo10(tls_ie), %g1
+	ldx	[%l7 + %g1], %g1, %tie_ldx(tls_ie)
+	add	%g7, %g1, %g1, %tie_add(tls_ie)
+	nop;nop;nop;nop
+
+	ret
+	restore