From e43c6e4abc81d9bbf66fb210d86682d9f647031d Mon Sep 17 00:00:00 2001
From: Lulu Cai <cailulu@loongson.cn>
Date: Thu, 21 Mar 2024 16:33:21 +0800
Subject: [PATCH 077/123] LoongArch: Fix the issue of excessive relocation
 generated by GD and IE

Currently, whether GD and IE generate dynamic relocation is
determined by SYMBOL_REFERENCES_LOCAL and bfd_link_executable.
This results in dynamic relocations still being generated in some
situations where dynamic relocations are not necessary (such as
the undefined weak symbol in static links).

We use RLARCH_TLS_GD_IE_NEED_DYN_RELOC macros to determine whether
GD/IE needs dynamic relocation. If GD/IE requires dynamic relocation,
set need_reloc to true and indx to be a dynamic index.

At the same time, some test cases were modified to use regular
expression matching instead of complete disassembly matching.
---
 bfd/elfnn-loongarch.c                       | 179 ++++++++++----------
 ld/testsuite/ld-loongarch-elf/desc-ie.d     |   4 +-
 ld/testsuite/ld-loongarch-elf/tlsdesc-dso.d |  94 +++++-----
 3 files changed, 142 insertions(+), 135 deletions(-)

diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index f6975957..7f98dce1 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -159,6 +159,27 @@ struct loongarch_elf_link_hash_table
    || (R_TYPE) == R_LARCH_TLS_LE64_LO20	  \
    || (R_TYPE) == R_LARCH_TLS_LE64_HI12)
 
+/* If TLS GD/IE need dynamic relocations, INDX will be the dynamic indx,
+   and set NEED_RELOC to true used in allocate_dynrelocs and
+   loongarch_elf_relocate_section for TLS GD/IE.  */
+#define LARCH_TLS_GD_IE_NEED_DYN_RELOC(INFO, DYN, H, INDX, NEED_RELOC) \
+  do \
+    { \
+      if ((H) != NULL \
+	  && (H)->dynindx != -1 \
+	  && WILL_CALL_FINISH_DYNAMIC_SYMBOL ((DYN), \
+				    bfd_link_pic (INFO), (H))) \
+      (INDX) = (H)->dynindx; \
+      if (((H) == NULL \
+	    || ELF_ST_VISIBILITY ((H)->other) == STV_DEFAULT \
+	    || (H)->root.type != bfd_link_hash_undefweak) \
+	    && (!bfd_link_executable (INFO) \
+	      || (INDX) != 0)) \
+      (NEED_RELOC) = true; \
+    } \
+    while (0)
+
+
 /* Generate a PLT header.  */
 
 static bool
@@ -1276,40 +1297,24 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       h->got.offset = s->size;
       if (tls_type & (GOT_TLS_GD | GOT_TLS_IE | GOT_TLS_GDESC))
 	{
+	  int indx = 0;
+	  bool need_reloc = false;
+	  LARCH_TLS_GD_IE_NEED_DYN_RELOC (info, dyn, h, indx,
+					need_reloc);
 	  /* TLS_GD needs two dynamic relocs and two GOT slots.  */
 	  if (tls_type & GOT_TLS_GD)
 	    {
 	      s->size += 2 * GOT_ENTRY_SIZE;
-	      if (bfd_link_executable (info))
-		{
-		  /* Link exe and not defined local.  */
-		  if (!SYMBOL_REFERENCES_LOCAL (info, h))
-		    htab->elf.srelgot->size += 2 * sizeof (ElfNN_External_Rela);
-		}
-	      else
-		{
-		  if (SYMBOL_REFERENCES_LOCAL (info, h))
-		    htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
-		  else
-		    htab->elf.srelgot->size += 2 * sizeof (ElfNN_External_Rela);
-		}
+	      if (need_reloc)
+		htab->elf.srelgot->size += 2 * sizeof (ElfNN_External_Rela);
 	    }
 
 	  /* TLS_IE needs one dynamic reloc and one GOT slot.  */
 	  if (tls_type & GOT_TLS_IE)
 	    {
 	      s->size += GOT_ENTRY_SIZE;
-
-	      if (bfd_link_executable (info))
-		{
-		  /* Link exe and not defined local.  */
-		  if (!SYMBOL_REFERENCES_LOCAL (info, h))
-		    htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
-		}
-	      else
-		{
-		  htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
-		}
+	      if (need_reloc)
+		htab->elf.srelgot->size += 2 * sizeof (ElfNN_External_Rela);
 	    }
 
 	  /* TLS_DESC needs one dynamic reloc and two GOT slot.  */
@@ -2550,13 +2555,18 @@ loongarch_reloc_is_fatal (struct bfd_link_info *info,
   })
 
 
+/* Compute the tp/dtp offset of a tls symbol.
+   It is dtp offset in dynamic tls model (gd/ld) and tp
+   offset in static tls model (ie/le). Both offsets are
+   calculated the same way on LoongArch, so the same
+   function is used.  */
 static bfd_vma
-tls_dtpoff_base (struct bfd_link_info *info)
+tlsoff (struct bfd_link_info *info, bfd_vma addr)
 {
   /* If tls_sec is NULL, we should have signalled an error already.  */
   if (elf_hash_table (info)->tls_sec == NULL)
     return 0;
-  return elf_hash_table (info)->tls_sec->vma;
+  return addr - elf_hash_table (info)->tls_sec->vma;
 }
 
 
@@ -2890,7 +2900,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 			  is_undefweak, name, "TLS section not be created");
 		}
 	      else
-		relocation -= elf_hash_table (info)->tls_sec->vma;
+		relocation = tlsoff (info, relocation);
 	    }
 	  else
 	    {
@@ -3416,7 +3426,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
 	case R_LARCH_TLS_LE_HI20_R:
 	  relocation += rel->r_addend;
-	  relocation -= elf_hash_table (info)->tls_sec->vma;
+	  relocation = tlsoff (info, relocation);
 	  RELOCATE_TLS_TP32_HI20 (relocation);
 	  break;
 
@@ -3599,7 +3609,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	  BFD_ASSERT (resolved_local && elf_hash_table (info)->tls_sec);
 
 	  relocation += rel->r_addend;
-	  relocation -= elf_hash_table (info)->tls_sec->vma;
+	  relocation = tlsoff (info, relocation);
 	  break;
 
 	/* TLS IE LD/GD process separately is troublesome.
@@ -3654,71 +3664,72 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	  /* If a tls variable is accessed in multiple ways, GD uses
 	     the first two slots of GOT, desc follows with two slots,
 	     and IE uses one slot at the end.  */
-	  desc_off = 0;
-	  if (GOT_TLS_GD_BOTH_P (tls_type))
-	    desc_off = 2 * GOT_ENTRY_SIZE;
-
-	  ie_off = 0;
-	  if (GOT_TLS_GD_BOTH_P (tls_type) && (tls_type & GOT_TLS_IE))
-	    ie_off = 4 * GOT_ENTRY_SIZE;
-	  else if (GOT_TLS_GD_ANY_P (tls_type) && (tls_type & GOT_TLS_IE))
-	    ie_off = 2 * GOT_ENTRY_SIZE;
+	  off = 0;
+	  if (tls_type & GOT_TLS_GD)
+	    off += 2 * GOT_ENTRY_SIZE;
+	  desc_off = off;
+	  if (tls_type & GOT_TLS_GDESC)
+	    off += 2 * GOT_ENTRY_SIZE;
+	  ie_off = off;
 
 	  if ((got_off & 1) == 0)
 	    {
 	      Elf_Internal_Rela rela;
 	      asection *relgot = htab->elf.srelgot;
-	      bfd_vma tls_block_off = 0;
 
-	      if (SYMBOL_REFERENCES_LOCAL (info, h))
-		{
-		  BFD_ASSERT (elf_hash_table (info)->tls_sec);
-		  tls_block_off = relocation
-		      - elf_hash_table (info)->tls_sec->vma;
-		}
+	      int indx = 0;
+	      bool need_reloc = false;
+	      LARCH_TLS_GD_IE_NEED_DYN_RELOC (info, is_dyn, h, indx,
+					      need_reloc);
 
 	      if (tls_type & GOT_TLS_GD)
 		{
-		  rela.r_offset = sec_addr (got) + got_off;
-		  rela.r_addend = 0;
-		  if (SYMBOL_REFERENCES_LOCAL (info, h))
+		  if (need_reloc)
 		    {
-		      /* Local sym, used in exec, set module id 1.  */
-		      if (bfd_link_executable (info))
-			bfd_put_NN (output_bfd, 1, got->contents + got_off);
+		  /* Dynamic resolved Module ID.  */
+		      rela.r_offset = sec_addr (got) + got_off;
+		      rela.r_addend = 0;
+		      rela.r_info = ELFNN_R_INFO (indx,R_LARCH_TLS_DTPMODNN);
+		      bfd_put_NN (output_bfd, 0, got->contents + got_off);
+		      loongarch_elf_append_rela (output_bfd, relgot, &rela);
+
+		      if (indx == 0)
+			{
+			  /* Local symbol, tp offset has been known.  */
+			  BFD_ASSERT (! unresolved_reloc);
+			  bfd_put_NN (output_bfd,
+			      tlsoff (info, relocation),
+			      (got->contents + got_off + GOT_ENTRY_SIZE));
+			}
 		      else
 			{
-			  rela.r_info = ELFNN_R_INFO (0, R_LARCH_TLS_DTPMODNN);
+			  /* Dynamic resolved block offset.  */
+			  bfd_put_NN (output_bfd, 0,
+			      got->contents + got_off + GOT_ENTRY_SIZE);
+			  rela.r_info = ELFNN_R_INFO (indx,
+						R_LARCH_TLS_DTPRELNN);
+			  rela.r_offset += GOT_ENTRY_SIZE;
 			  loongarch_elf_append_rela (output_bfd, relgot, &rela);
 			}
-
-		      bfd_put_NN (output_bfd, tls_block_off,
-				  got->contents + got_off + GOT_ENTRY_SIZE);
 		    }
-		  /* Dynamic resolved.  */
 		  else
 		    {
-		      /* Dynamic relocate module id.  */
-		      rela.r_info = ELFNN_R_INFO (h->dynindx,
-						  R_LARCH_TLS_DTPMODNN);
-		      loongarch_elf_append_rela (output_bfd, relgot, &rela);
-
-		      /* Dynamic relocate offset of block.  */
-		      rela.r_offset += GOT_ENTRY_SIZE;
-		      rela.r_info = ELFNN_R_INFO (h->dynindx,
-						  R_LARCH_TLS_DTPRELNN);
-		      loongarch_elf_append_rela (output_bfd, relgot, &rela);
+		      /* In a static link or an executable link with the symbol
+			 binding locally.  Mark it as belonging to module 1.  */
+		      bfd_put_NN (output_bfd, 1, got->contents + got_off);
+		      bfd_put_NN (output_bfd, tlsoff (info, relocation),
+			  got->contents + got_off + GOT_ENTRY_SIZE);
 		    }
 		}
 	      if (tls_type & GOT_TLS_GDESC)
 		{
 		  /* Unless it is a static link, DESC always emits a
 		     dynamic relocation.  */
-		  int indx = h && h->dynindx != -1 ? h->dynindx : 0;
+		  indx = h && h->dynindx != -1 ? h->dynindx : 0;
 		  rela.r_offset = sec_addr (got) + got_off + desc_off;
 		  rela.r_addend = 0;
 		  if (indx == 0)
-		    rela.r_addend = relocation - tls_dtpoff_base (info);
+		    rela.r_addend = tlsoff (info, relocation);
 
 		  rela.r_info = ELFNN_R_INFO (indx, R_LARCH_TLS_DESCNN);
 		  loongarch_elf_append_rela (output_bfd, relgot, &rela);
@@ -3727,28 +3738,24 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 		}
 	      if (tls_type & GOT_TLS_IE)
 		{
-		  rela.r_offset = sec_addr (got) + got_off + ie_off;
-		  if (SYMBOL_REFERENCES_LOCAL (info, h))
+		  if (need_reloc)
 		    {
-		      /* Local sym, used in exec, set module id 1.  */
-		      if (!bfd_link_executable (info))
-			{
-			  rela.r_info = ELFNN_R_INFO (0, R_LARCH_TLS_TPRELNN);
-			  rela.r_addend = tls_block_off;
-			  loongarch_elf_append_rela (output_bfd, relgot, &rela);
-			}
+		      bfd_put_NN (output_bfd, 0,
+			  got->contents + got_off + ie_off);
+		      rela.r_offset = sec_addr (got) + got_off + ie_off;
+		      rela.r_addend = 0;
 
-		      bfd_put_NN (output_bfd, tls_block_off,
-				  got->contents + got_off + ie_off);
+		      if (indx == 0)
+			rela.r_addend = tlsoff (info, relocation);
+		      rela.r_info = ELFNN_R_INFO (indx, R_LARCH_TLS_TPRELNN);
+		      loongarch_elf_append_rela (output_bfd, relgot, &rela);
 		    }
-		  /* Dynamic resolved.  */
 		  else
 		    {
-		      /* Dynamic relocate offset of block.  */
-		      rela.r_info = ELFNN_R_INFO (h->dynindx,
-						  R_LARCH_TLS_TPRELNN);
-		      rela.r_addend = 0;
-		      loongarch_elf_append_rela (output_bfd, relgot, &rela);
+		      /* In a static link or an executable link with the symbol
+			 bindinglocally, compute offset directly.  */
+		      bfd_put_NN (output_bfd, tlsoff (info, relocation),
+			  got->contents + got_off + ie_off);
 		    }
 		}
 	    }
@@ -3787,7 +3794,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
 	    tls_type = _bfd_loongarch_elf_tls_type (input_bfd, h, r_symndx);
 	    /* Use both TLS_GD and TLS_DESC.  */
-	    if ((tls_type & GOT_TLS_GD) && (tls_type & GOT_TLS_GDESC))
+	    if (GOT_TLS_GD_BOTH_P (tls_type))
 	      relocation += 2 * GOT_ENTRY_SIZE;
 
 	    if (r_type == R_LARCH_TLS_DESC64_PC_LO20)
diff --git a/ld/testsuite/ld-loongarch-elf/desc-ie.d b/ld/testsuite/ld-loongarch-elf/desc-ie.d
index e1f49e2d..c833b233 100644
--- a/ld/testsuite/ld-loongarch-elf/desc-ie.d
+++ b/ld/testsuite/ld-loongarch-elf/desc-ie.d
@@ -9,6 +9,6 @@ Disassembly of section .text:
 
 [0-9a-f]+ <fn1>:
  +[0-9a-f]+:	1a000084 	pcalau12i   	\$a0, .*
- +[0-9a-f]+:	28cca084 	ld.d        	\$a0, \$a0, .*
+ +[0-9a-f]+:	28cd0084 	ld.d        	\$a0, \$a0, .*
  +[0-9a-f]+:	1a000084 	pcalau12i   	\$a0, .*
- +[0-9a-f]+:	28cca084 	ld.d        	\$a0, \$a0, .*
+ +[0-9a-f]+:	28cd0084 	ld.d        	\$a0, \$a0, .*
diff --git a/ld/testsuite/ld-loongarch-elf/tlsdesc-dso.d b/ld/testsuite/ld-loongarch-elf/tlsdesc-dso.d
index 84ea97e0..8f66302f 100644
--- a/ld/testsuite/ld-loongarch-elf/tlsdesc-dso.d
+++ b/ld/testsuite/ld-loongarch-elf/tlsdesc-dso.d
@@ -8,53 +8,53 @@
 
 Disassembly of section .text:
 
-0+448 <fun_gl1>:
- 448:	18021584 	pcaddi      	\$a0, 4268
- 44c:	1a000084 	pcalau12i   	\$a0, 4
- 450:	28dc2084 	ld.d        	\$a0, \$a0, 1800
- 454:	18021364 	pcaddi      	\$a0, 4251
- 458:	180213c4 	pcaddi      	\$a0, 4254
- 45c:	28c00081 	ld.d        	\$ra, \$a0, 0
- 460:	4c000021 	jirl        	\$ra, \$ra, 0
- 464:	1a000084 	pcalau12i   	\$a0, 4
- 468:	28dae084 	ld.d        	\$a0, \$a0, 1720
- 46c:	1a000084 	pcalau12i   	\$a0, 4
- 470:	28dae084 	ld.d        	\$a0, \$a0, 1720
- 474:	18021364 	pcaddi      	\$a0, 4251
- 478:	18021344 	pcaddi      	\$a0, 4250
- 47c:	28c00081 	ld.d        	\$ra, \$a0, 0
- 480:	4c000021 	jirl        	\$ra, \$ra, 0
- 484:	1a000084 	pcalau12i   	\$a0, 4
- 488:	28dbc084 	ld.d        	\$a0, \$a0, 1776
+[0-9a-f]+ <fun_gl1>:
+ +[0-9a-f]+:	18021584 	pcaddi      	\$a0, 4268
+ +[0-9a-f]+:	1a000084 	pcalau12i   	\$a0, 4
+ +[0-9a-f]+:	28dd4084 	ld.d        	\$a0, \$a0, 1872
+ +[0-9a-f]+:	18021364 	pcaddi      	\$a0, 4251
+ +[0-9a-f]+:	180213c4 	pcaddi      	\$a0, 4254
+ +[0-9a-f]+:	28c00081 	ld.d        	\$ra, \$a0, 0
+ +[0-9a-f]+:	4c000021 	jirl        	\$ra, \$ra, 0
+ +[0-9a-f]+:	1a000084 	pcalau12i   	\$a0, 4
+ +[0-9a-f]+:	28dc0084 	ld.d        	\$a0, \$a0, 1792
+ +[0-9a-f]+:	1a000084 	pcalau12i   	\$a0, 4
+ +[0-9a-f]+:	28dc0084 	ld.d        	\$a0, \$a0, 1792
+ +[0-9a-f]+:	18021364 	pcaddi      	\$a0, 4251
+ +[0-9a-f]+:	180213c4 	pcaddi      	\$a0, 4254
+ +[0-9a-f]+:	28c00081 	ld.d        	\$ra, \$a0, 0
+ +[0-9a-f]+:	4c000021 	jirl        	\$ra, \$ra, 0
+ +[0-9a-f]+:	1a000084 	pcalau12i   	\$a0, 4
+ +[0-9a-f]+:	28dce084 	ld.d        	\$a0, \$a0, 1848
 
-0+48c <fun_lo>:
- 48c:	1a000084 	pcalau12i   	\$a0, 4
- 490:	28d98084 	ld.d        	\$a0, \$a0, 1632
- 494:	18020de4 	pcaddi      	\$a0, 4207
- 498:	18020f04 	pcaddi      	\$a0, 4216
- 49c:	28c00081 	ld.d        	\$ra, \$a0, 0
- 4a0:	4c000021 	jirl        	\$ra, \$ra, 0
- 4a4:	18020e24 	pcaddi      	\$a0, 4209
- 4a8:	1a000084 	pcalau12i   	\$a0, 4
- 4ac:	28da2084 	ld.d        	\$a0, \$a0, 1672
- 4b0:	1a000084 	pcalau12i   	\$a0, 4
- 4b4:	28da2084 	ld.d        	\$a0, \$a0, 1672
- 4b8:	18020ec4 	pcaddi      	\$a0, 4214
- 4bc:	28c00081 	ld.d        	\$ra, \$a0, 0
- 4c0:	4c000021 	jirl        	\$ra, \$ra, 0
- 4c4:	18020e64 	pcaddi      	\$a0, 4211
- 4c8:	1a000084 	pcalau12i   	\$a0, 4
- 4cc:	28da8084 	ld.d        	\$a0, \$a0, 1696
+[0-9a-f]+ <fun_lo>:
+ +[0-9a-f]+:	1a000084 	pcalau12i   	\$a0, 4
+ +[0-9a-f]+:	28daa084 	ld.d        	\$a0, \$a0, 1704
+ +[0-9a-f]+:	18020de4 	pcaddi      	\$a0, 4207
+ +[0-9a-f]+:	18020f04 	pcaddi      	\$a0, 4216
+ +[0-9a-f]+:	28c00081 	ld.d        	\$ra, \$a0, 0
+ +[0-9a-f]+:	4c000021 	jirl        	\$ra, \$ra, 0
+ +[0-9a-f]+:	18020e24 	pcaddi      	\$a0, 4209
+ +[0-9a-f]+:	1a000084 	pcalau12i   	\$a0, 4
+ +[0-9a-f]+:	28db4084 	ld.d        	\$a0, \$a0, 1744
+ +[0-9a-f]+:	1a000084 	pcalau12i   	\$a0, 4
+ +[0-9a-f]+:	28db4084 	ld.d        	\$a0, \$a0, 1744
+ +[0-9a-f]+:	18020f44 	pcaddi      	\$a0, 4218
+ +[0-9a-f]+:	28c00081 	ld.d        	\$ra, \$a0, 0
+ +[0-9a-f]+:	4c000021 	jirl        	\$ra, \$ra, 0
+ +[0-9a-f]+:	18020e64 	pcaddi      	\$a0, 4211
+ +[0-9a-f]+:	1a000084 	pcalau12i   	\$a0, 4
+ +[0-9a-f]+:	28dba084 	ld.d        	\$a0, \$a0, 1768
 
-0+4d0 <fun_external>:
- 4d0:	18020ec4 	pcaddi      	\$a0, 4214
- 4d4:	28c00081 	ld.d        	\$ra, \$a0, 0
- 4d8:	4c000021 	jirl        	\$ra, \$ra, 0
+[0-9a-f]+ <fun_external>:
+ +[0-9a-f]+:	18020ec4 	pcaddi      	\$a0, 4214
+ +[0-9a-f]+:	28c00081 	ld.d        	\$ra, \$a0, 0
+ +[0-9a-f]+:	4c000021 	jirl        	\$ra, \$ra, 0
 
-0+4dc <fun_hidden>:
- 4dc:	18021224 	pcaddi      	\$a0, 4241
- 4e0:	28c00081 	ld.d        	\$ra, \$a0, 0
- 4e4:	4c000021 	jirl        	\$ra, \$ra, 0
- 4e8:	18021144 	pcaddi      	\$a0, 4234
- 4ec:	28c00081 	ld.d        	\$ra, \$a0, 0
- 4f0:	4c000021 	jirl        	\$ra, \$ra, 0
+[0-9a-f]+ <fun_hidden>:
+ +[0-9a-f]+:	18021224 	pcaddi      	\$a0, 4241
+ +[0-9a-f]+:	28c00081 	ld.d        	\$ra, \$a0, 0
+ +[0-9a-f]+:	4c000021 	jirl        	\$ra, \$ra, 0
+ +[0-9a-f]+:	18021144 	pcaddi      	\$a0, 4234
+ +[0-9a-f]+:	28c00081 	ld.d        	\$ra, \$a0, 0
+ +[0-9a-f]+:	4c000021 	jirl        	\$ra, \$ra, 0
-- 
2.33.0

