[v2,11/12] LoongArch: Add linker relaxation for got_pcadd_hi20 and got_pcadd_lo12

Message ID 20251202111745.1558349-12-mengqinggang@loongson.cn
State New
Headers
Series Add support for LoongArch32 |

Commit Message

mengqinggang Dec. 2, 2025, 11:17 a.m. UTC
  .L1:
pcaddu12i $t0, %got_pcadd_hi20(a)     -> pcaddu12i $t0, %pcadd_hi20(a)
ld.w/d $t0, $t0, %got_pcadd_lo12(.L1) -> addi.w/d $t0, $t0, %pcadd_lo12(.L1)
---
 bfd/elfnn-loongarch.c      | 63 ++++++++++++++++++++++++++------------
 gas/config/tc-loongarch.c  |  7 ++++-
 include/opcode/loongarch.h |  3 ++
 3 files changed, 53 insertions(+), 20 deletions(-)
  

Patch

diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index 955f0c18ebc..f44154da1e6 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -4502,7 +4502,7 @@  loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	      || (h && ELF_ST_VISIBILITY (h->other) != STV_DEFAULT))
 	     && is_undefweak);
 	  if (resolve_pcrel_undef_weak)
-	    pc = 0;
+	    relocation = pc; /* Use pc to avoid duplicate pcrel_hi record.  */
 
 	  if (h && h->plt.offset != MINUS_ONE)
 	    relocation = sec_addr (plt) + h->plt.offset;
@@ -5828,7 +5828,11 @@  loongarch_relax_call36 (bfd *abfd, asection *sec, asection *sym_sec,
   return true;
 }
 
-/* Relax pcalau12i,ld.d => pcalau12i,addi.d.  */
+/* pcalau12i $t0, %got_pcala_hi20(a)   -> pcalau12i $t0, %pcala_hi20(a)
+   ld.w/d $t0, $t0, %got_pcala_lo12(a) -> addi.w/d $t0, $t0, %pcala_lo12(a)
+
+   pcaddu12i $t0, %got_pcadd_hi20(a)   -> pcaddu12i $t0, %pcadd_hi20(a)
+   ld.w/d $t0, $t0, %got_pcadd_lo12(a) -> addi.w/d $t0, $t0, %pcadd_lo12(a)  */
 static bool
 loongarch_relax_pcala_ld (bfd *abfd, asection *sec,
 			  asection *sym_sec,
@@ -5838,13 +5842,6 @@  loongarch_relax_pcala_ld (bfd *abfd, asection *sec,
 			  bool *again ATTRIBUTE_UNUSED,
 			  bfd_vma max_alignment)
 {
-  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
-  Elf_Internal_Rela *rel_lo = rel_hi + 2;
-  uint32_t pca = bfd_get (32, abfd, contents + rel_hi->r_offset);
-  uint32_t ld = bfd_get (32, abfd, contents + rel_lo->r_offset);
-  uint32_t rd = LARCH_GET_RD (pca);
-  uint32_t addi_d = LARCH_OP_ADDI_D;
-
   /* This section's output_offset need to subtract the bytes of instructions
      relaxed by the previous sections, so it needs to be updated beforehand.
      size_input_section already took care of updating it after relaxation,
@@ -5868,22 +5865,46 @@  loongarch_relax_pcala_ld (bfd *abfd, asection *sec,
   else if (symval < pc)
     pc += (max_alignment > 4 ? max_alignment : 0);
 
-  if ((ELFNN_R_TYPE (rel_lo->r_info) != R_LARCH_GOT_PC_LO12)
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+  Elf_Internal_Rela *rel_lo = rel_hi + 2;
+  uint32_t pca = bfd_get (32, abfd, contents + rel_hi->r_offset);
+  uint32_t ld = bfd_get (32, abfd, contents + rel_lo->r_offset);
+  uint32_t rd = LARCH_GET_RD (pca);
+
+  if ((ELFNN_R_TYPE (rel_lo->r_info) != R_LARCH_GOT_PC_LO12
+	&& ELFNN_R_TYPE (rel_lo->r_info) != R_LARCH_GOT_PCADD_LO12)
       || (LARCH_GET_RD (ld) != rd)
       || (LARCH_GET_RJ (ld) != rd)
-      || !LARCH_INSN_LD_D (ld)
+      || (!LARCH_INSN_LD_D (ld) && !LARCH_INSN_LD_W (ld))
       /* Within +-2G addressing range.  */
       || (bfd_signed_vma)(symval - pc) < (bfd_signed_vma)(int32_t)0x80000000
       || (bfd_signed_vma)(symval - pc) > (bfd_signed_vma)(int32_t)0x7fffffff)
     return false;
 
-  addi_d = addi_d | (rd << 5) | rd;
-  bfd_put (32, abfd, addi_d, contents + rel_lo->r_offset);
+  uint32_t addi;
+  if (LARCH_INSN_LD_D (ld))
+    addi = LARCH_OP_ADDI_D;
+  else
+    addi = LARCH_OP_ADDI_W;
+
+  addi = addi | (rd << 5) | rd;
+  bfd_put (32, abfd, addi, contents + rel_lo->r_offset);
+
+  if (ELFNN_R_TYPE (rel_hi->r_info) == R_LARCH_GOT_PC_HI20)
+    {
+      rel_hi->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel_hi->r_info),
+				     R_LARCH_PCALA_HI20);
+      rel_lo->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel_lo->r_info),
+				     R_LARCH_PCALA_LO12);
+    }
+  else
+    {
+      rel_hi->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel_hi->r_info),
+				     R_LARCH_PCADD_HI20);
+      rel_lo->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel_lo->r_info),
+				     R_LARCH_PCADD_LO12);
+    }
 
-  rel_hi->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel_hi->r_info),
-				 R_LARCH_PCALA_HI20);
-  rel_lo->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel_lo->r_info),
-				 R_LARCH_PCALA_LO12);
   return true;
 }
 
@@ -6229,6 +6250,7 @@  loongarch_elf_relax_section (bfd *abfd, asection *sec,
 	      relax_func = loongarch_relax_pcala_addi;
 	      break;
 	    case R_LARCH_GOT_PC_HI20:
+	    case R_LARCH_GOT_PCADD_HI20:
 	      relax_func = loongarch_relax_pcala_ld;
 	      break;
 	    case R_LARCH_CALL36:
@@ -6258,7 +6280,8 @@  loongarch_elf_relax_section (bfd *abfd, asection *sec,
 	      || r_type == R_LARCH_TLS_GD_PC_HI20
 	      || r_type == R_LARCH_TLS_DESC_PC_HI20
 	      || r_type == R_LARCH_PCALA_HI20
-	      || r_type == R_LARCH_GOT_PC_HI20)
+	      || r_type == R_LARCH_GOT_PC_HI20
+	      || r_type == R_LARCH_GOT_PCADD_HI20)
 	    {
 	      if ((i + 2) == sec->reloc_count - 1
 		  || ELFNN_R_TYPE ((rel + 1)->r_info) != R_LARCH_RELAX
@@ -6402,7 +6425,9 @@  loongarch_elf_relax_section (bfd *abfd, asection *sec,
 
       symval += sec_addr (sym_sec);
 
-      if (r_type == R_LARCH_GOT_PC_HI20 && !local_got)
+      if ((r_type == R_LARCH_GOT_PC_HI20
+	    || r_type == R_LARCH_GOT_PCADD_HI20)
+	  && !local_got)
 	continue;
 
       if (relax_func (abfd, sec, sym_sec, rel, symval,
diff --git a/gas/config/tc-loongarch.c b/gas/config/tc-loongarch.c
index f09220bad5c..307e10c7e1b 100644
--- a/gas/config/tc-loongarch.c
+++ b/gas/config/tc-loongarch.c
@@ -905,6 +905,8 @@  loongarch_args_parser_can_match_arg_helper (char esc_ch1, char esc_ch2,
 			|| BFD_RELOC_LARCH_TLS_DESC_CALL == reloc_type
 			|| BFD_RELOC_LARCH_TLS_IE_PC_HI20 == reloc_type
 			|| BFD_RELOC_LARCH_TLS_IE_PC_LO12 == reloc_type
+			|| BFD_RELOC_LARCH_GOT_PCADD_HI20 == reloc_type
+			|| BFD_RELOC_LARCH_GOT_PCADD_LO12 == reloc_type
 			|| BFD_RELOC_LARCH_TLS_DESC_PCADD_HI20 == reloc_type
 			|| BFD_RELOC_LARCH_TLS_DESC_PCADD_LO12 == reloc_type
 			|| BFD_RELOC_LARCH_TLS_IE_PCADD_HI20 == reloc_type
@@ -1289,7 +1291,10 @@  append_fixp_and_insn (struct loongarch_cl_insn *ip)
 	  || BFD_RELOC_LARCH_TLS_LE_HI20 == reloc_info[0].type
 	  || BFD_RELOC_LARCH_TLS_LE_LO12 == reloc_info[0].type
 	  || BFD_RELOC_LARCH_TLS_LE64_LO20 == reloc_info[0].type
-	  || BFD_RELOC_LARCH_TLS_LE64_HI12 == reloc_info[0].type))
+	  || BFD_RELOC_LARCH_TLS_LE64_HI12 == reloc_info[0].type
+	  || BFD_RELOC_LARCH_GOT_PCADD_HI20 == reloc_info[0].type
+	  || BFD_RELOC_LARCH_TLS_IE_PCADD_HI20 == reloc_info[0].type
+	  || BFD_RELOC_LARCH_TLS_DESC_PCADD_HI20 == reloc_info[0].type))
     {
       frag_wane (frag_now);
       frag_new (0);
diff --git a/include/opcode/loongarch.h b/include/opcode/loongarch.h
index 367dc2392a5..861bcd3dae0 100644
--- a/include/opcode/loongarch.h
+++ b/include/opcode/loongarch.h
@@ -36,6 +36,7 @@  extern "C"
 
   #define LARCH_MK_ADDI_D 0xffc00000
   #define LARCH_OP_ADDI_D 0x02c00000
+  #define LARCH_MK_ADDI_W LARCH_MK_ADDI_D
   #define LARCH_OP_ADDI_W 0x02800000
   #define LARCH_MK_PCADDI 0xfe000000
   #define LARCH_OP_PCADDI 0x18000000
@@ -50,6 +51,7 @@  extern "C"
   #define LARCH_OP_LU12I_W 0x14000000
   #define LARCH_MK_LD_D 0xffc00000
   #define LARCH_OP_LD_D 0x28c00000
+  #define LARCH_MK_LD_W LARCH_MK_LD_D
   #define LARCH_OP_LD_W 0x28800000
   #define LARCH_MK_JIRL 0xfc000000
   #define LARCH_OP_JIRL 0x4c000000
@@ -83,6 +85,7 @@  extern "C"
   #define LARCH_INSN_ORI(insn) LARCH_INSN_OPS((insn), ORI)
   #define LARCH_INSN_LU12I_W(insn) LARCH_INSN_OPS((insn), LU12I_W)
   #define LARCH_INSN_LD_D(insn) LARCH_INSN_OPS((insn), LD_D)
+  #define LARCH_INSN_LD_W(insn) LARCH_INSN_OPS((insn), LD_W)
   #define LARCH_INSN_JIRL(insn) LARCH_INSN_OPS((insn), JIRL)
   #define LARCH_INSN_BCEQZ(insn) LARCH_INSN_OPS((insn), BCEQZ)
   #define LARCH_INSN_BCNEZ(insn) LARCH_INSN_OPS((insn), BCNEZ)