]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
LoongArch: Add linker relaxation for got_pcadd_hi20 and got_pcadd_lo12
authormengqinggang <mengqinggang@loongson.cn>
Mon, 22 Sep 2025 03:43:41 +0000 (11:43 +0800)
committermengqinggang <mengqinggang@loongson.cn>
Wed, 10 Dec 2025 08:06:48 +0000 (16:06 +0800)
.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
gas/config/tc-loongarch.c
include/opcode/loongarch.h

index 7a2f4274c5d20d928a8ef46179bdff105ddac158..f7dcb8453b20ec912042a1f6dafc8e0beb43c69a 100644 (file)
@@ -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;
@@ -5831,7 +5831,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,
@@ -5841,13 +5845,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,
@@ -5871,22 +5868,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;
 }
 
@@ -6232,6 +6253,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:
@@ -6261,7 +6283,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
@@ -6405,7 +6428,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,
index cf174e44ede2b1a9d42eff2ff8559c526ab207dc..e049427956c7b6caf7f37961b3ac177ccf78f3f1 100644 (file)
@@ -907,6 +907,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
@@ -1293,7 +1295,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);
index b76e295b6d496703b7dbe563e1dec1dd5ba41486..fb6f8be68de2a4cbe8d03d87740d3b88abdb8806 100644 (file)
@@ -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
@@ -85,6 +87,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)