[PATCH v1 2/2] LoongArch: Add bfd ld support for LA32 relocations

Lulu Cai cailulu@loongson.cn
Sun Apr 27 09:22:24 GMT 2025


* Add ld.bfd support for 6 new relocations for LA32.
* TLS type transition and relaxation are not supported,
  so when a TLS symbol is referenced by multiple types,
  the required GOT slots are cumulative.
---
 bfd/elfnn-loongarch.c                         | 215 +++++++++++++++++-
 bfd/elfxx-loongarch.c                         |  10 +
 .../ld-loongarch-elf/bad_pcadd_hi20_global.d  |   6 +
 .../ld-loongarch-elf/bad_pcadd_hi20_global.s  |  12 +
 .../ld-loongarch-elf/bad_pcadd_lo12.d         |   4 +
 .../ld-loongarch-elf/bad_pcadd_lo12.s         |   4 +
 .../ld-loongarch-elf/call32_undefweak.d       |   6 +
 .../ld-loongarch-elf/call32_undefweak.s       |  15 ++
 ld/testsuite/ld-loongarch-elf/la32r_relocs.s  |  44 ++++
 .../ld-loongarch-elf/la32r_relocs_nopie.d     |  36 +++
 .../ld-loongarch-elf/la32r_relocs_pie.d       |  36 +++
 ld/testsuite/ld-loongarch-elf/la32r_tls.s     |  34 +++
 ld/testsuite/ld-loongarch-elf/la32r_tls_bin.d |  17 ++
 .../ld-loongarch-elf/la32r_tls_shared.d       |  18 ++
 ld/testsuite/ld-loongarch-elf/la32r_tlslib.s  |   5 +
 .../ld-loongarch-elf/ld-loongarch-elf.exp     |  36 +++
 16 files changed, 492 insertions(+), 6 deletions(-)
 create mode 100644 ld/testsuite/ld-loongarch-elf/bad_pcadd_hi20_global.d
 create mode 100644 ld/testsuite/ld-loongarch-elf/bad_pcadd_hi20_global.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/bad_pcadd_lo12.d
 create mode 100644 ld/testsuite/ld-loongarch-elf/bad_pcadd_lo12.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/call32_undefweak.d
 create mode 100644 ld/testsuite/ld-loongarch-elf/call32_undefweak.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/la32r_relocs.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/la32r_relocs_nopie.d
 create mode 100644 ld/testsuite/ld-loongarch-elf/la32r_relocs_pie.d
 create mode 100644 ld/testsuite/ld-loongarch-elf/la32r_tls.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/la32r_tls_bin.d
 create mode 100644 ld/testsuite/ld-loongarch-elf/la32r_tls_shared.d
 create mode 100644 ld/testsuite/ld-loongarch-elf/la32r_tlslib.s

diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index faad512f339..4055be33de1 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -219,7 +219,8 @@ loongarch_elf_new_section_hook (bfd *abfd, asection *sec)
 
 #define IS_CALL_RELOC(R_TYPE)	  \
   ((R_TYPE) == R_LARCH_B26	  \
-   ||(R_TYPE) == R_LARCH_CALL36)
+   ||(R_TYPE) == R_LARCH_CALL36   \
+   ||(R_TYPE) == R_LARCH_CALL32)
 
 /* If TLS GD/IE need dynamic relocations, INDX will be the dynamic indx,
    and set NEED_RELOC to true used in allocate_dynrelocs and
@@ -876,7 +877,8 @@ bad_static_reloc (struct bfd_link_info *info,
   bool bad_extern_access =
     (bfd_link_pde (info)
      || r_type == R_LARCH_PCREL20_S2
-     || r_type == R_LARCH_PCALA_HI20);
+     || r_type == R_LARCH_PCALA_HI20
+     || r_type == R_LARCH_PCADD_HI20);
 
   if (h)
     name = h->root.root.string;
@@ -1044,6 +1046,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_LARCH_GOT_PC_HI20:
 	case R_LARCH_GOT_HI20:
 	case R_LARCH_SOP_PUSH_GPREL:
+	case R_LARCH_PCADD_GOT_HI20:
 	  /* For la.global.  */
 	  if (h)
 	    h->pointer_equality_needed = 1;
@@ -1069,6 +1072,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_LARCH_TLS_IE_PC_HI20:
 	case R_LARCH_TLS_IE_HI20:
 	case R_LARCH_SOP_PUSH_TLS_GOT:
+	case R_LARCH_PCADD_TLS_IE_HI20:
 	  if (bfd_link_pic (info))
 	    /* May fail for lazy-bind.  */
 	    info->flags |= DF_STATIC_TLS;
@@ -1095,6 +1099,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	case R_LARCH_TLS_DESC_PC_HI20:
 	case R_LARCH_TLS_DESC_HI20:
+	case R_LARCH_PCADD_TLS_DESC_HI20:
 	  if (!loongarch_elf_record_tls_and_got_reference (abfd, info, h,
 							   r_symndx,
 							   GOT_TLS_GDESC,
@@ -1124,6 +1129,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	   In static PIE undefined weak symbols may be allowed
 	   by rewriting pcaddi to addi.w if addend is in [-2048, 2048).  */
 	case R_LARCH_PCREL20_S2:
+	case R_LARCH_PCADD_HI20:
 	  if (bfd_link_pic (info)
 	      && (sec->flags & SEC_ALLOC) != 0
 	      && (sec->flags & SEC_READONLY) != 0
@@ -1170,6 +1176,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_LARCH_B21:
 	case R_LARCH_B26:
 	case R_LARCH_CALL36:
+	case R_LARCH_CALL32:
 	  if (h != NULL)
 	    {
 	      h->needs_plt = 1;
@@ -2993,6 +3000,12 @@ perform_relocation (const Elf_Internal_Rela *rel, asection *input_section,
     case R_LARCH_TLS_LD_PCREL20_S2:
     case R_LARCH_TLS_GD_PCREL20_S2:
     case R_LARCH_TLS_DESC_PCREL20_S2:
+    case R_LARCH_PCADD_HI20:
+    case R_LARCH_PCADD_LO12_I:
+    case R_LARCH_PCADD_GOT_HI20:
+    case R_LARCH_PCADD_TLS_IE_HI20:
+    case R_LARCH_PCADD_TLS_DESC_HI20:
+    case R_LARCH_CALL32:
       r = loongarch_check_offset (rel, input_section);
       if (r != bfd_reloc_ok)
 	break;
@@ -3249,6 +3262,153 @@ tlsoff (struct bfd_link_info *info, bfd_vma addr)
   return addr - elf_hash_table (info)->tls_sec->vma;
 }
 
+typedef struct
+{
+  /* PC value.  */
+  bfd_vma address;
+  /* Relocation value with addend.  */
+  bfd_vma value;
+} loongarch_pcrel_hi_reloc;
+
+typedef struct loongarch_pcrel_lo_reloc
+{
+  /* PC value of pcaddu12i.  */
+  bfd_vma address;
+  /* Internal relocation.  */
+  Elf_Internal_Rela *reloc;
+  /* loongarch_elf_relocate_section can only handle an input section at a time,
+     so we can only resolved pcadd_hi20 and pcadd_lo12 in the same section. If
+     these pcrel relocs are not in the same section we should report dangerous
+     relocation errors.  */
+  asection *input_section;
+  struct bfd_link_info *info;
+  reloc_howto_type *howto;
+  bfd_byte *contents;
+  /* The next loongarch_pcrel_lo_reloc.  */
+  struct loongarch_pcrel_lo_reloc *next;
+} loongarch_pcrel_lo_reloc;
+
+typedef struct
+{
+  /* Hash table for loongarch_pcrel_hi_reloc.  */
+  htab_t hi_relocs;
+  /* Linked list for loongarch_pcrel_lo_reloc.  */
+  loongarch_pcrel_lo_reloc *lo_relocs;
+} loongarch_pcrel_relocs;
+
+/* Hash function of the pcrel_hi_reloc hash table.  */
+static hashval_t
+loongarch_pcrel_reloc_hash (const void *entry)
+{
+  const loongarch_pcrel_hi_reloc *e = entry;
+  return (hashval_t)(e->address >> 2);
+}
+
+/* Comparison function of the pcrel_hi_reloc hash table.  */
+static int
+loongarch_pcrel_reloc_eq (const void *entry1, const void *entry2)
+{
+  const loongarch_pcrel_hi_reloc *e1 = entry1, *e2 = entry2;
+  return e1->address == e2->address;
+}
+
+static bool
+loongarch_init_pcrel_relocs (loongarch_pcrel_relocs *p)
+{
+  p->lo_relocs = NULL;
+  p->hi_relocs = htab_create (1024, loongarch_pcrel_reloc_hash,
+			      loongarch_pcrel_reloc_eq, free);
+  return p->hi_relocs != NULL;
+}
+
+static void
+loongarch_free_pcrel_reloc (loongarch_pcrel_relocs *p)
+{
+  loongarch_pcrel_lo_reloc *cur = p->lo_relocs;
+
+  while (cur != NULL)
+    {
+      loongarch_pcrel_lo_reloc *next = cur->next;
+      free (cur);
+      cur = next;
+    }
+  htab_delete (p->hi_relocs);
+}
+
+static bool
+loongarch_record_pcrel_hi_reloc (loongarch_pcrel_relocs *p,
+				 bfd_vma addr,
+				 bfd_vma *value)
+{
+  bfd_vma offset = *value - addr;
+  bfd_vma off_lo = offset & (bfd_vma)0xfff;
+  /* If lo12 immediate > 0x7ff, because sign-extend caused by addi.w/ld.w,
+     hi20 immediate need to add 0x1.
+     See RELOCATE_CALC_PC32_HI20(relocation, pc)  */
+  if (off_lo > 0x7ff)
+    offset += 0x1000;
+
+  *value = offset;
+
+  loongarch_pcrel_hi_reloc entry = {addr, offset};
+  loongarch_pcrel_hi_reloc **slot =
+    (loongarch_pcrel_hi_reloc **)htab_find_slot (p->hi_relocs, &entry, INSERT);
+
+  BFD_ASSERT (*slot == NULL);
+  *slot = (loongarch_pcrel_hi_reloc *) bfd_malloc (sizeof (loongarch_pcrel_hi_reloc));
+  if (*slot == NULL)
+    return false;
+  **slot = entry;
+  return true;
+}
+
+static bool
+loongarch_record_pcrel_lo_reloc (loongarch_pcrel_relocs *p,
+				 bfd_vma addr,
+				 Elf_Internal_Rela *reloc,
+				 asection *input_section,
+				 struct bfd_link_info *info,
+				 reloc_howto_type *howto,
+				 bfd_byte *contents)
+{
+  loongarch_pcrel_lo_reloc *entry;
+  entry = (loongarch_pcrel_lo_reloc *) bfd_malloc (sizeof (loongarch_pcrel_lo_reloc));
+  if (entry == NULL)
+    return false;
+  *entry = (loongarch_pcrel_lo_reloc) {addr, reloc, input_section, info,
+				       howto, contents, p->lo_relocs};
+  p->lo_relocs = entry;
+  return true;
+}
+
+static bool
+loongarch_resolve_pcrel_lo_relocs (loongarch_pcrel_relocs *p)
+{
+  loongarch_pcrel_lo_reloc *r;
+  for (r = p->lo_relocs; r != NULL; r = r->next)
+    {
+      bfd *input_bfd = r->input_section->owner;
+
+      loongarch_pcrel_hi_reloc search = {r->address, 0};
+      loongarch_pcrel_hi_reloc *entry = htab_find (p->hi_relocs, &search);
+
+      char *string = NULL;
+      if (entry == NULL)
+	string = _("%pcadd_lo12 missing marching %pcadd_*_hi20");
+
+      if (string != NULL)
+	{
+	  (*r->info->callbacks->reloc_dangerous)
+	   (r->info, string, input_bfd, r->input_section, r->reloc->r_offset);
+	  return true;
+	}
+
+      perform_relocation (r->reloc, r->input_section, r->howto, entry->value,
+			  input_bfd, r->contents);
+    }
+  return true;
+}
+
 static int
 loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 				bfd *input_bfd, asection *input_section,
@@ -3258,6 +3418,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 {
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
+  loongarch_pcrel_relocs pcrel_relocs;
   bool fatal = false;
   asection *sreloc = elf_section_data (input_section)->sreloc;
   struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info);
@@ -3269,6 +3430,9 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
   asection *plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
   asection *got = htab->elf.sgot;
 
+  if (!loongarch_init_pcrel_relocs (&pcrel_relocs))
+    return false;
+
   relend = relocs + input_section->reloc_count;
   for (rel = relocs; rel < relend; rel++)
     {
@@ -4049,6 +4213,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	case R_LARCH_B21:
 	case R_LARCH_B26:
 	case R_LARCH_CALL36:
+	case R_LARCH_CALL32:
 	  unresolved_reloc = false;
 	  bool via_plt =
 	    plt != NULL && h != NULL && h->plt.offset != (bfd_vma) - 1;
@@ -4061,8 +4226,10 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	      if (!via_plt && IS_CALL_RELOC (r_type))
 		{
 		  /* call36 fn1 => pcaddu18i $ra,0+jirl $ra,$zero,0
-		     tail36 $t0,fn1 => pcaddi18i $t0,0+jirl $zero,$zero,0  */
-		  if (R_LARCH_CALL36 == r_type)
+		     tail36 $t0,fn1 => pcaddi18i $t0,0+jirl $zero,$zero,0
+		     call32 fn1 => pcaddu12i $ra,0+jirl $ra,$zero,0  */
+		  if (R_LARCH_CALL36 == r_type
+		      || R_LARCH_CALL32 == r_type)
 		    {
 		      uint32_t jirl = bfd_get (32, input_bfd,
 					  contents + rel->r_offset + 4);
@@ -4245,6 +4412,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
 	case R_LARCH_GOT_PC_HI20:
 	case R_LARCH_GOT_HI20:
+	case R_LARCH_PCADD_GOT_HI20:
 	  /* Calc got offset.  */
 	    {
 	      unresolved_reloc = false;
@@ -4336,6 +4504,13 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	  if (r_type == R_LARCH_GOT_PC_HI20)
 	    RELOCATE_CALC_PC32_HI20 (relocation, pc);
 
+	  if (r_type == R_LARCH_PCADD_GOT_HI20)
+	    {
+	      if (!loongarch_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+						    &relocation))
+		r = bfd_reloc_overflow;
+	    }
+
 	  break;
 
 	case R_LARCH_GOT_PC_LO12:
@@ -4408,16 +4583,20 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	case R_LARCH_TLS_LD_PCREL20_S2:
 	case R_LARCH_TLS_GD_PCREL20_S2:
 	case R_LARCH_TLS_DESC_PCREL20_S2:
+	case R_LARCH_PCADD_TLS_IE_HI20:
+	case R_LARCH_PCADD_TLS_DESC_HI20:
 	  BFD_ASSERT (rel->r_addend == 0);
 	  unresolved_reloc = false;
 
 	  if (r_type == R_LARCH_TLS_IE_PC_HI20
-	      || r_type == R_LARCH_TLS_IE_HI20)
+	      || r_type == R_LARCH_TLS_IE_HI20
+	      || r_type == R_LARCH_PCADD_TLS_IE_HI20)
 	    is_ie = true;
 
 	  if (r_type == R_LARCH_TLS_DESC_PC_HI20
 	      || r_type == R_LARCH_TLS_DESC_HI20
-	      || r_type == R_LARCH_TLS_DESC_PCREL20_S2)
+	      || r_type == R_LARCH_TLS_DESC_PCREL20_S2
+	      || r_type == R_LARCH_PCADD_TLS_DESC_HI20)
 	    is_desc = true;
 
 	  bfd_vma got_off = 0;
@@ -4549,6 +4728,11 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	      || r_type == R_LARCH_TLS_GD_PCREL20_S2
 	      || r_type == R_LARCH_TLS_DESC_PCREL20_S2)
 	    relocation -= pc;
+	  else if (r_type == R_LARCH_PCADD_TLS_IE_HI20
+		   || r_type == R_LARCH_PCADD_TLS_DESC_HI20)
+	    if (!loongarch_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+						  &relocation))
+	      r = bfd_reloc_overflow;
 	  /* else {} ABS relocations.  */
 	  break;
 
@@ -4620,6 +4804,21 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	  unresolved_reloc = false;
 	  break;
 
+	case R_LARCH_PCADD_HI20:
+	  relocation += rel->r_addend;
+	  if (!loongarch_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+						&relocation))
+	    r = bfd_reloc_overflow;
+	  break;
+
+	case R_LARCH_PCADD_LO12_I:
+	  if (loongarch_record_pcrel_lo_reloc (&pcrel_relocs, relocation, rel,
+					       input_section, info, howto,
+					       contents))
+	    continue;
+	  r = bfd_reloc_overflow;
+	  break;
+
 	default:
 	  break;
 	}
@@ -4711,6 +4910,10 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
       fatal = true;
     }
 
+  bool ret = loongarch_resolve_pcrel_lo_relocs (&pcrel_relocs);
+  fatal = !ret;
+  loongarch_free_pcrel_reloc (&pcrel_relocs);
+
   return !fatal;
 }
 
diff --git a/bfd/elfxx-loongarch.c b/bfd/elfxx-loongarch.c
index 0eead492e76..4efb480032f 100644
--- a/bfd/elfxx-loongarch.c
+++ b/bfd/elfxx-loongarch.c
@@ -2167,6 +2167,16 @@ reloc_bits_sanity (bfd *abfd, reloc_howto_type *howto, bfd_vma *fix_val,
 	 so the high part need to add 0x8000.  */
       val = (((val + 0x8000) >> 16) << 5) | (((val & 0xffff) << 10) << 32);
       break;
+    case R_LARCH_CALL32:
+      /* call32 = pcaddu12i+jirl, the jirl immediate field has 16 bits,
+	 and the sign extension at run time does not affect the result.
+	 But at link time we need to sign-extend the lower 12 bits of
+	 the offset to write the 16-bit immediate field of jirl.
+	 Since there is "val = val >> howto->rightshift" in front, only
+	 the lower 10 bits need to be sign-extended.  */
+      bfd_vma off_lo = val & 0x3ff;
+      val = ((val >> 10) << 5) | (((off_lo & 0xffff) << 10) << 32);
+      break;
     default:
       val <<= howto->bitpos;
       break;
diff --git a/ld/testsuite/ld-loongarch-elf/bad_pcadd_hi20_global.d b/ld/testsuite/ld-loongarch-elf/bad_pcadd_hi20_global.d
new file mode 100644
index 00000000000..196817ed84b
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/bad_pcadd_hi20_global.d
@@ -0,0 +1,6 @@
+#name: la32r PC-relative relocation making shared
+#source: bad_pcadd_hi20_global.s
+#skip: loongarch64-*-*
+#target: [check_shared_lib_support]
+#ld: -shared --defsym global_a=0x10 --defsym global_b=0x20
+#error: .*: relocation R_LARCH_PCADD_HI20 against `global_b` can not be used when making a shared object; recompile with -fPIC and check the symbol visibility
diff --git a/ld/testsuite/ld-loongarch-elf/bad_pcadd_hi20_global.s b/ld/testsuite/ld-loongarch-elf/bad_pcadd_hi20_global.s
new file mode 100644
index 00000000000..3993bcd407e
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/bad_pcadd_hi20_global.s
@@ -0,0 +1,12 @@
+    .hidden global_a
+    .text
+    .align 2
+main:
+    # Symbols defined .hidden are bound local and
+    # the linker should differenciate them.
+.L1:
+    pcaddu12i $t0, %pcadd_hi20(global_a)
+    addi.w    $t0, $t0, %pcadd_lo12(.L1)
+.L2:
+    pcaddu12i $t0, %pcadd_hi20(global_b)
+    addi.w    $t0, $t0, %pcadd_lo12(.L2)
diff --git a/ld/testsuite/ld-loongarch-elf/bad_pcadd_lo12.d b/ld/testsuite/ld-loongarch-elf/bad_pcadd_lo12.d
new file mode 100644
index 00000000000..a479491013b
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/bad_pcadd_lo12.d
@@ -0,0 +1,4 @@
+#name: %pcadd_lo12 match
+#source: bad_pcadd_lo12.s
+#ld: -e0
+#error: .*dangerous relocation: %pcadd_lo12 missing marching %pcadd_\*_hi20
diff --git a/ld/testsuite/ld-loongarch-elf/bad_pcadd_lo12.s b/ld/testsuite/ld-loongarch-elf/bad_pcadd_lo12.s
new file mode 100644
index 00000000000..faa2b631aa7
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/bad_pcadd_lo12.s
@@ -0,0 +1,4 @@
+    .text
+    .align 2
+.L1:
+    addi.w    $t0, $t0, %pcadd_lo12(.L1)
diff --git a/ld/testsuite/ld-loongarch-elf/call32_undefweak.d b/ld/testsuite/ld-loongarch-elf/call32_undefweak.d
new file mode 100644
index 00000000000..7d37107221f
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/call32_undefweak.d
@@ -0,0 +1,6 @@
+#...
+[0-9a-f]+ <main>:
+   [0-9a-f]+:	........ 	pcaddu12i   	$a0, .*
+   [0-9a-f]+:	........ 	jirl        	$ra, $a0, .*
+   [0-9a-f]+:	1c000004 	pcaddu12i   	$a0, 0
+   [0-9a-f]+:	4c000001 	jirl        	$ra, $zero, 0
diff --git a/ld/testsuite/ld-loongarch-elf/call32_undefweak.s b/ld/testsuite/ld-loongarch-elf/call32_undefweak.s
new file mode 100644
index 00000000000..067b024abb7
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/call32_undefweak.s
@@ -0,0 +1,15 @@
+	.text
+	.globl	main
+	.type	main, @function
+main:
+	pcaddu12i $a0,%call32(fn1)
+	jirl	  $ra,$a0,0
+	# Calls to undefined weak symbols that do not
+	# require a plt entry are converted to calls to 0.
+	pcaddu12i $a0,%call32(fn2)
+	jirl	  $ra,$a0,0
+
+	.weak fn1
+
+	.weak	fn2
+	.hidden	fn2
diff --git a/ld/testsuite/ld-loongarch-elf/la32r_relocs.s b/ld/testsuite/ld-loongarch-elf/la32r_relocs.s
new file mode 100644
index 00000000000..7ad975cade9
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/la32r_relocs.s
@@ -0,0 +1,44 @@
+	.text
+	.globl	f1
+	.type	f1, @function
+
+f1:
+	ret
+
+	.global _start
+_start:
+# call32
+	pcaddu12i   $t0,%call32(f1)
+	jirl	    $ra,$t0,0
+
+# pcrel
+L1:
+	pcaddu12i   $t0,%pcadd_hi20(s)
+	addi.w	    $a0,$t0,%pcadd_lo12(L1)
+
+# got
+L2:
+	pcaddu12i   $t0,%pcadd_got_hi20(s)
+	ld.w	    $a0,$t0,%pcadd_lo12(L2)
+
+# tls ie
+L3:
+	pcaddu12i   $a0,%pcadd_ie_hi20(t)
+	ld.w	    $a0,$a0,%pcadd_lo12(L3)
+
+# tls desc
+L4:
+        pcaddu12i   $a0,%pcadd_desc_hi20(t)
+        addi.w      $a0,$a0,%pcadd_lo12(L4)
+        ld.w        $ra,$a0,%desc_ld(L4)
+        jirl        $ra,$ra,%desc_call(L4)
+
+	.global	s
+s:
+	nop
+
+	.section  .tata,"awT",@progbits
+	.align	  2
+	.globl	  t
+t:
+	.word 0x123
diff --git a/ld/testsuite/ld-loongarch-elf/la32r_relocs_nopie.d b/ld/testsuite/ld-loongarch-elf/la32r_relocs_nopie.d
new file mode 100644
index 00000000000..807f9e498ee
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/la32r_relocs_nopie.d
@@ -0,0 +1,36 @@
+#source: la32r_relocs.s
+#ld: -e0 --hash-style=both
+#objdump: -d
+
+.*:     file format .*
+
+
+Disassembly of section .text:
+
+[0-9a-f]+ <f1>:
+   [0-9a-f]+:	4c000020 	ret
+
+[0-9a-f]+ <_start>:
+   [0-9a-f]+:	1dffffec 	pcaddu12i   	\$t0, -1
+   [0-9a-f]+:	4c0ffd81 	jirl        	\$ra, \$t0, 4092
+
+[0-9a-f]+ <L1>:
+   [0-9a-f]+:	1c00000c 	pcaddu12i   	\$t0, 0
+   [0-9a-f]+:	0280a184 	addi.w      	\$a0, \$t0, 40
+
+[0-9a-f]+ <L2>:
+   [0-9a-f]+:	1c00020c 	pcaddu12i   	\$t0, 16
+   [0-9a-f]+:	2880e184 	ld.w        	\$a0, \$t0, 56
+
+[0-9a-f]+ <L3>:
+   [0-9a-f]+:	1c000204 	pcaddu12i   	\$a0, 16
+   [0-9a-f]+:	2880b084 	ld.w        	\$a0, \$a0, 44
+
+[0-9a-f]+ <L4>:
+   [0-9a-f]+:	1c000204 	pcaddu12i   	\$a0, 16
+   [0-9a-f]+:	02807084 	addi.w      	\$a0, \$a0, 28
+   [0-9a-f]+:	28800081 	ld.w        	\$ra, \$a0, 0
+   [0-9a-f]+:	4c000021 	jirl        	\$ra, \$ra, 0
+
+[0-9a-f]+ <s>:
+   [0-9a-f]+:	03400000 	nop
diff --git a/ld/testsuite/ld-loongarch-elf/la32r_relocs_pie.d b/ld/testsuite/ld-loongarch-elf/la32r_relocs_pie.d
new file mode 100644
index 00000000000..c7ae214b9f7
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/la32r_relocs_pie.d
@@ -0,0 +1,36 @@
+#source: la32r_relocs.s
+#ld: -e0 -pie --hash-style=both
+#objdump: -d
+
+.*:     file format .*
+
+
+Disassembly of section .text:
+
+[0-9a-f]+ <f1>:
+ [0-9a-f]+:	4c000020 	ret
+
+[0-9a-f]+ <_start>:
+ [0-9a-f]+:	1dffffec 	pcaddu12i   	\$t0, -1
+ [0-9a-f]+:	4c0ffd81 	jirl        	\$ra, \$t0, 4092
+
+[0-9a-f]+ <L1>:
+ [0-9a-f]+:	1c00000c 	pcaddu12i   	\$t0, 0
+ [0-9a-f]+:	0280a184 	addi.w      	\$a0, \$t0, 40
+
+[0-9a-f]+ <L2>:
+ [0-9a-f]+:	1c00020c 	pcaddu12i   	\$t0, 16
+ [0-9a-f]+:	28832184 	ld.w        	\$a0, \$t0, 200
+
+[0-9a-f]+ <L3>:
+ [0-9a-f]+:	1c000204 	pcaddu12i   	\$a0, 16
+ [0-9a-f]+:	2882f084 	ld.w        	\$a0, \$a0, 188
+
+[0-9a-f]+ <L4>:
+ [0-9a-f]+:	1c000204 	pcaddu12i   	\$a0, 16
+ [0-9a-f]+:	0282b084 	addi.w      	\$a0, \$a0, 172
+ [0-9a-f]+:	28800081 	ld.w        	\$ra, \$a0, 0
+ [0-9a-f]+:	4c000021 	jirl        	\$ra, \$ra, 0
+
+[0-9a-f]+ <s>:
+ [0-9a-f]+:	03400000 	nop
diff --git a/ld/testsuite/ld-loongarch-elf/la32r_tls.s b/ld/testsuite/ld-loongarch-elf/la32r_tls.s
new file mode 100644
index 00000000000..360f59dd180
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/la32r_tls.s
@@ -0,0 +1,34 @@
+# DESC and IE require a total of 3 GOT slots to access the same symbol.
+# DESC occupies the first two and IE occupies the last one.
+	.section  .tata,"awT",@progbits
+	.align	  2
+	.globl	  gl1
+gl1:
+	.word 0x123
+lo1:
+	.word 0x456
+
+      .text
+	.globl _start
+_start:
+# tls ie
+.L1:
+	pcaddu12i   $a0,%pcadd_ie_hi20(gl1)
+	ld.w	    $a0,$a0,%pcadd_lo12(.L1)
+	add.w	    $a0,$a0,$tp
+.L2:
+	pcaddu12i   $a0,%pcadd_ie_hi20(lo1)
+	ld.w	    $a0,$a0,%pcadd_lo12(.L2)
+	add.w	    $a0,$a0,$tp
+
+# tls desc
+.L3:
+        pcaddu12i   $a0,%pcadd_desc_hi20(gl1)
+        addi.w      $a0,$a0,%pcadd_lo12(.L3)
+        ld.w        $ra,$a0,%desc_ld(.L3)
+        jirl        $ra,$ra,%desc_call(.L3)
+.L4:
+        pcaddu12i   $a0,%pcadd_desc_hi20(lo1)
+        addi.w      $a0,$a0,%pcadd_lo12(.L4)
+        ld.w        $ra,$a0,%desc_ld(.L4)
+        jirl        $ra,$ra,%desc_call(.L4)
diff --git a/ld/testsuite/ld-loongarch-elf/la32r_tls_bin.d b/ld/testsuite/ld-loongarch-elf/la32r_tls_bin.d
new file mode 100644
index 00000000000..4adadd2ce0e
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/la32r_tls_bin.d
@@ -0,0 +1,17 @@
+#name: la32r tls relocs binary
+#source: la32r_tls.s
+#ld: -no-pie tmpdir/la32r-tlslib.so
+#objdump: -dRj.got
+#skip: loongarch64-*-*
+
+.*:     file format .*
+
+
+Disassembly of section .got:
+
+[0-9a-f]+ <_GLOBAL_OFFSET_TABLE_>:
+   [0-9a-f]+:	.*     ................
+			[0-9a-f]+: R_LARCH_TLS_DESC32	\*ABS\*\+0x4
+	...
+			[0-9a-f]+: R_LARCH_TLS_DESC32	gl1
+			[0-9a-f]+: R_LARCH_TLS_TPREL32	gl1
diff --git a/ld/testsuite/ld-loongarch-elf/la32r_tls_shared.d b/ld/testsuite/ld-loongarch-elf/la32r_tls_shared.d
new file mode 100644
index 00000000000..a694cf6d3f3
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/la32r_tls_shared.d
@@ -0,0 +1,18 @@
+#name: la32r tls relocs shared
+#source: la32r_tls.s
+#ld: -shared tmpdir/la32r-tlslib.so
+#objdump: -dRj.got
+#skip: loongarch64-*-*
+
+.*:     file format .*
+
+
+Disassembly of section .got:
+
+[0-9a-f]+ <.got>:
+   [0-9a-f]+:	[0-9a-f]+ 	.*		0x[0-9a-f]+
+	\.\.\.
+			[0-9a-f]+: R_LARCH_TLS_DESC32	\*ABS\*\+0x4
+			[0-9a-f]+: R_LARCH_TLS_TPREL32	\*ABS\*\+0x4
+			[0-9a-f]+: R_LARCH_TLS_DESC32	gl1
+			[0-9a-f]+: R_LARCH_TLS_TPREL32	gl1
diff --git a/ld/testsuite/ld-loongarch-elf/la32r_tlslib.s b/ld/testsuite/ld-loongarch-elf/la32r_tlslib.s
new file mode 100644
index 00000000000..3d0fa96b9c1
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/la32r_tlslib.s
@@ -0,0 +1,5 @@
+	.section  .tata,"awT",@progbits
+	.align	  2
+	.globl	  gl1
+gl1:
+	.word 0x123
diff --git a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
index 0295be81562..c55dbdb9ea2 100644
--- a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
+++ b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
@@ -57,6 +57,31 @@ if [istarget "loongarch32-*-*"] {
     run_dump_test "disas-jirl-32"
 }
 
+if [istarget "loongarch32-*-*"] {
+  run_dump_test "la32r_relocs_nopie"
+  run_dump_test "bad_pcadd_lo12"
+  if [check_pie_support] {
+    run_dump_test "la32r_relocs_pie"
+  }
+  if [check_shared_lib_support] {
+    run_ld_link_tests \
+	[list \
+	    [list \
+		"Build shared lib for la32r TLS runtime" \
+		"-shared" "" "" \
+		{la32r_tlslib.s} \
+		{} \
+		"la32r_tlslib.so" \
+	    ] \
+	]
+
+    run_dump_test "bad_pcadd_hi20_global"
+    run_dump_test "la32r_tls_shared"
+    run_dump_test "la32r_tls_bin"
+
+  }
+}
+
 if [istarget "loongarch64-*-*"] {
   if [check_shared_lib_support] {
     run_ld_link_tests \
@@ -237,3 +262,14 @@ if [istarget "loongarch64-*-*"] {
 
 }
 
+if [istarget "loongarch32-*-*"] {
+  run_cc_link_tests [list \
+      [list \
+	  "call32 undefweak symbol" \
+	  "-no-pie" "" \
+	  {call_undefweak.s} \
+	  {{objdump {-d} call_undefweak.d}} \
+	  "call32_undefweak" \
+      ] \
+  ]
+}
-- 
2.45.2



More information about the Binutils mailing list