LoongArch: Disallow TLS transition when a section contains TLS_IE64 or TLS_DESC64 reloc

Message ID 20240125134238.174841-1-xry111@xry111.site
State New
Headers
Series LoongArch: Disallow TLS transition when a section contains TLS_IE64 or TLS_DESC64 reloc |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Testing passed

Commit Message

Xi Ruoyao Jan. 25, 2024, 1:36 p.m. UTC
  We cannot do TLS transition for them without a psABI revision marking
some "key" instructions with reloc.  Disallow the transition when we see
such a reloc in a section to prevent an invalid "partial" transition.

Signed-off-by: Xi Ruoyao <xry111@xry111.site>
---

To Nick:

Sorry for another late update, but we need this for 2.42 or anything
compiled with -mcmodel=extreme and using errno will just blow up :(.

 bfd/elfnn-loongarch.c                         | 53 +++++++++++++++++--
 .../ld-loongarch-elf/ld-loongarch-elf.exp     | 14 +++++
 .../ld-loongarch-elf/tls-ie64-notrans.d       |  3 ++
 .../ld-loongarch-elf/tls-ie64-notrans.s       |  7 +++
 4 files changed, 73 insertions(+), 4 deletions(-)
 create mode 100644 ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.d
 create mode 100644 ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.s
  

Comments

Nick Clifton Jan. 25, 2024, 1:49 p.m. UTC | #1
Hi Xi,

> We cannot do TLS transition for them without a psABI revision marking
> some "key" instructions with reloc.  Disallow the transition when we see
> such a reloc in a section to prevent an invalid "partial" transition.

> To Nick:
> 
> Sorry for another late update, but we need this for 2.42 or anything
> compiled with -mcmodel=extreme and using errno will just blow up :(.

Ok, well that is a good enough reason to allow the patch into the 2.42 branch - please apply.

Cheers
   Nick
  
Xi Ruoyao Jan. 25, 2024, 1:52 p.m. UTC | #2
On Thu, 2024-01-25 at 13:49 +0000, Nick Clifton wrote:
> Hi Xi,
> 
> > We cannot do TLS transition for them without a psABI revision
> > marking
> > some "key" instructions with reloc.  Disallow the transition when we
> > see
> > such a reloc in a section to prevent an invalid "partial"
> > transition.
> 
> > To Nick:
> > 
> > Sorry for another late update, but we need this for 2.42 or anything
> > compiled with -mcmodel=extreme and using errno will just blow up :(.
> 
> Ok, well that is a good enough reason to allow the patch into the 2.42
> branch - please apply.

It will still need an approval from Qinggang.
  
mengqinggang Jan. 26, 2024, 1:43 a.m. UTC | #3
Hi,

For TLS transition, we want to change to that the instructions that can be
converted must have a R_LARCH_RELAX relocation.

After 32ee2b4b71c78d3dc0c0b1f87f25fe5df8786b71 commit, the gas not
emit R_LARCH_RELAX for two register macros.


在 2024/1/25 下午9:36, Xi Ruoyao 写道:
> We cannot do TLS transition for them without a psABI revision marking
> some "key" instructions with reloc.  Disallow the transition when we see
> such a reloc in a section to prevent an invalid "partial" transition.
>
> Signed-off-by: Xi Ruoyao <xry111@xry111.site>
> ---
>
> To Nick:
>
> Sorry for another late update, but we need this for 2.42 or anything
> compiled with -mcmodel=extreme and using errno will just blow up :(.
>
>   bfd/elfnn-loongarch.c                         | 53 +++++++++++++++++--
>   .../ld-loongarch-elf/ld-loongarch-elf.exp     | 14 +++++
>   .../ld-loongarch-elf/tls-ie64-notrans.d       |  3 ++
>   .../ld-loongarch-elf/tls-ie64-notrans.s       |  7 +++
>   4 files changed, 73 insertions(+), 4 deletions(-)
>   create mode 100644 ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.d
>   create mode 100644 ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.s
>
> diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
> index b62bb424644..b9709401f56 100644
> --- a/bfd/elfnn-loongarch.c
> +++ b/bfd/elfnn-loongarch.c
> @@ -717,6 +717,40 @@ loongarch_tls_transition (struct bfd_link_info *info, unsigned int r_type,
>     return loongarch_tls_transition_without_check (info, r_type, h);
>   }
>   
> +/* For TLS IE in extreme code model:
> +       pcalau12i t0, %ie_pc_hi20(x)
> +       li.d      t1, %ie_pc_lo12(x)
> +       lu32i.d   t1, %ie64_pc_lo20(x)
> +       lu52i.d   t1, t1, %ie64_pc_hi12(x)
> +       ldx.d     t0, t0, t1
> +   We've no idea how to remove the ldx.d instruction or turn it into a
> +   nop because there is no reloc on it.  But we have to stop turning this
> +   into LE or we'll end up
> +       lu12i.w   t0, %le_hi20(x)
> +       ori       t1, t1, %le_lo12(x)
> +       lu32i.d   t1, %ie64_pc_lo20(x)
> +       lu52i.d   t1, t1, %ie64_pc_hi12(x)
> +       ldx.d     t0, t0, t1
> +   which is completely wrong.  For TLS DESC it's similar: we cannot remove
> +   the add.d instruction but we have to stop the transition.  */
> +static bool
> +loongarch_elf_allow_tls_transition_p (const Elf_Internal_Rela *r_begin,
> +				      const Elf_Internal_Rela *r_end)
> +{
> +  for (; r_begin != r_end; r_begin++)
> +    switch (ELFNN_R_TYPE (r_begin->r_info))
> +      {
> +      case R_LARCH_TLS_IE64_PC_HI12:
> +      case R_LARCH_TLS_IE64_PC_LO20:
> +      case R_LARCH_TLS_DESC64_PC_HI12:
> +      case R_LARCH_TLS_DESC64_PC_LO20:
> +	return false;
> +      }
> +
> +  return true;
> +}
> +
> +
>   /* Look through the relocs for a section during the first phase, and
>      allocate space in the global offset table or procedure linkage
>      table.  */
> @@ -730,6 +764,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>     struct elf_link_hash_entry **sym_hashes;
>     const Elf_Internal_Rela *rel;
>     asection *sreloc = NULL;
> +  bool allow_tls_transition;
>   
>     if (bfd_link_relocatable (info))
>       return true;
> @@ -741,6 +776,9 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>     if (htab->elf.dynobj == NULL)
>       htab->elf.dynobj = abfd;
>   
> +  allow_tls_transition = loongarch_elf_allow_tls_transition_p (
> +    relocs, relocs + sec->reloc_count);
> +
>     for (rel = relocs; rel < relocs + sec->reloc_count; rel++)
>       {
>         unsigned int r_type;
> @@ -818,7 +856,8 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>         int need_dynreloc = 0;
>         int only_need_pcrel = 0;
>   
> -      r_type = loongarch_tls_transition (info, r_type, h, abfd, r_symndx);
> +      if (allow_tls_transition)
> +	r_type = loongarch_tls_transition (info, r_type, h, abfd, r_symndx);
>         switch (r_type)
>   	{
>   	case R_LARCH_GOT_PC_HI20:
> @@ -2632,7 +2671,6 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
>   				asection **local_sections)
>   {
>     Elf_Internal_Rela *rel;
> -  Elf_Internal_Rela *relend;
>     bool fatal = false;
>     asection *sreloc = elf_section_data (input_section)->sreloc;
>     struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info);
> @@ -2643,8 +2681,10 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
>     bool is_dyn = elf_hash_table (info)->dynamic_sections_created;
>     asection *plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
>     asection *got = htab->elf.sgot;
> +  Elf_Internal_Rela *relend = relocs + input_section->reloc_count;
> +  bool allow_tls_transition =
> +    loongarch_elf_allow_tls_transition_p (relocs, relend);
>   
> -  relend = relocs + input_section->reloc_count;
>     for (rel = relocs; rel < relend; rel++)
>       {
>         unsigned int r_type = ELFNN_R_TYPE (rel->r_info);
> @@ -2789,7 +2829,12 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
>   
>         BFD_ASSERT (!resolved_local || defined_local);
>   
> -      relaxed_r_type = loongarch_tls_transition (info, r_type, h, input_bfd, r_symndx);
> +      relaxed_r_type =
> +	(allow_tls_transition ? loongarch_tls_transition (info, r_type,
> +							  h, input_bfd,
> +							  r_symndx)
> +			      : r_type);
> +
>         if (relaxed_r_type != r_type)
>         {
>   	howto = loongarch_elf_rtype_to_howto (input_bfd, relaxed_r_type);
> diff --git a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
> index 2ff06d62236..9e35940f5d6 100644
> --- a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
> +++ b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
> @@ -88,6 +88,20 @@ if [istarget "loongarch64-*-*"] {
>   	      "medium-call" \
>   	  ] \
>         ]
> +
> +  run_ld_link_tests \
> +      [list \
> +	  [list \
> +	      "disable TLS IE transition with TLS_IE64_PC reloc" \
> +	      "-e 0x0" "" \
> +	      "" \
> +	      {tls-ie64-notrans.s} \
> +	      [list \
> +		  [list objdump -D tls-ie64-notrans.d] \
> +	      ] \
> +	      "tls-ie64-notrans" \
> +	  ] \
> +      ]
>   }
>   
>   if [istarget "loongarch64-*-*"] {
> diff --git a/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.d b/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.d
> new file mode 100644
> index 00000000000..47d507a0538
> --- /dev/null
> +++ b/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.d
> @@ -0,0 +1,3 @@
> +#...
> +.*pcalau12i.*
> +#pass
> diff --git a/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.s b/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.s
> new file mode 100644
> index 00000000000..cd8c65ff9d3
> --- /dev/null
> +++ b/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.s
> @@ -0,0 +1,7 @@
> +# it had segfaulted the linker due to invalid IE->LE transition
> +.globl _start
> +_start:
> +  la.tls.ie $a0, $a1, foo
> +
> +.section .tdata
> +foo: .word 114514
  
Xi Ruoyao Jan. 26, 2024, 8:12 a.m. UTC | #4
On Fri, 2024-01-26 at 09:43 +0800, mengqinggang wrote:
> Hi,
> 
> For TLS transition, we want to change to that the instructions that can be
> converted must have a R_LARCH_RELAX relocation.
> 
> After 32ee2b4b71c78d3dc0c0b1f87f25fe5df8786b71 commit, the gas not
> emit R_LARCH_RELAX for two register macros.

But with master branch at e14f7abaf81d5d0aa7898570024286de8c188817:

$ cat t.s
.globl _start
_start:
  la.tls.ie $a0, $a1, foo

.section .tdata
foo: .word 114514
$ gas/as-new t.s -o t.o
$ ld/ld-new t.o
[1]    7202 segmentation fault (core dumped)  ld/ld-new t.o
  
Xi Ruoyao Jan. 26, 2024, 8:19 a.m. UTC | #5
On Fri, 2024-01-26 at 16:12 +0800, Xi Ruoyao wrote:
> On Fri, 2024-01-26 at 09:43 +0800, mengqinggang wrote:
> > Hi,
> > 
> > For TLS transition, we want to change to that the instructions that can be
> > converted must have a R_LARCH_RELAX relocation.

So we need something like

diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index b62bb424644..63aa7e254b5 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -818,7 +818,10 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       int need_dynreloc = 0;
       int only_need_pcrel = 0;
 
-      r_type = loongarch_tls_transition (info, r_type, h, abfd, r_symndx);
+      if (rel + 1 != relocs + sec->reloc_count
+	  && ELFNN_R_TYPE (rel[1].r_info) == R_LARCH_RELAX)
+	r_type = loongarch_tls_transition (info, r_type, h, abfd, r_symndx);
+
       switch (r_type)
 	{
 	case R_LARCH_GOT_PC_HI20:
@@ -2789,7 +2792,12 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
       BFD_ASSERT (!resolved_local || defined_local);
 
-      relaxed_r_type = loongarch_tls_transition (info, r_type, h, input_bfd, r_symndx);
+      relaxed_r_type = r_type;
+      if (rel + 1 != relend
+	  && ELFNN_R_TYPE (rel[1].r_info) == R_LARCH_RELAX)
+	relaxed_r_type = loongarch_tls_transition (info, r_type, h,
+						   input_bfd, r_symndx);
+
       if (relaxed_r_type != r_type)
       {
 	howto = loongarch_elf_rtype_to_howto (input_bfd, relaxed_r_type);

and this is missing in the master branch?

> > After 32ee2b4b71c78d3dc0c0b1f87f25fe5df8786b71 commit, the gas not
> > emit R_LARCH_RELAX for two register macros.
> 
> But with master branch at e14f7abaf81d5d0aa7898570024286de8c188817:
> 
> $ cat t.s
> .globl _start
> _start:
>   la.tls.ie $a0, $a1, foo
> 
> .section .tdata
> foo: .word 114514
> $ gas/as-new t.s -o t.o
> $ ld/ld-new t.o
> [1]    7202 segmentation fault (core dumped)  ld/ld-new t.o
> 
>
  
mengqinggang Jan. 26, 2024, 8:40 a.m. UTC | #6
We will send a patch to fix this and some other questions today.


在 2024/1/26 下午4:19, Xi Ruoyao 写道:
> On Fri, 2024-01-26 at 16:12 +0800, Xi Ruoyao wrote:
>> On Fri, 2024-01-26 at 09:43 +0800, mengqinggang wrote:
>>> Hi,
>>>
>>> For TLS transition, we want to change to that the instructions that can be
>>> converted must have a R_LARCH_RELAX relocation.
> So we need something like
>
> diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
> index b62bb424644..63aa7e254b5 100644
> --- a/bfd/elfnn-loongarch.c
> +++ b/bfd/elfnn-loongarch.c
> @@ -818,7 +818,10 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>         int need_dynreloc = 0;
>         int only_need_pcrel = 0;
>   
> -      r_type = loongarch_tls_transition (info, r_type, h, abfd, r_symndx);
> +      if (rel + 1 != relocs + sec->reloc_count
> +	  && ELFNN_R_TYPE (rel[1].r_info) == R_LARCH_RELAX)
> +	r_type = loongarch_tls_transition (info, r_type, h, abfd, r_symndx);
> +
>         switch (r_type)
>   	{
>   	case R_LARCH_GOT_PC_HI20:
> @@ -2789,7 +2792,12 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
>   
>         BFD_ASSERT (!resolved_local || defined_local);
>   
> -      relaxed_r_type = loongarch_tls_transition (info, r_type, h, input_bfd, r_symndx);
> +      relaxed_r_type = r_type;
> +      if (rel + 1 != relend
> +	  && ELFNN_R_TYPE (rel[1].r_info) == R_LARCH_RELAX)
> +	relaxed_r_type = loongarch_tls_transition (info, r_type, h,
> +						   input_bfd, r_symndx);
> +
>         if (relaxed_r_type != r_type)
>         {
>   	howto = loongarch_elf_rtype_to_howto (input_bfd, relaxed_r_type);
>
> and this is missing in the master branch?
>
>>> After 32ee2b4b71c78d3dc0c0b1f87f25fe5df8786b71 commit, the gas not
>>> emit R_LARCH_RELAX for two register macros.
>> But with master branch at e14f7abaf81d5d0aa7898570024286de8c188817:
>>
>> $ cat t.s
>> .globl _start
>> _start:
>>    la.tls.ie $a0, $a1, foo
>>
>> .section .tdata
>> foo: .word 114514
>> $ gas/as-new t.s -o t.o
>> $ ld/ld-new t.o
>> [1]    7202 segmentation fault (core dumped)  ld/ld-new t.o
>>
>>
  
Xi Ruoyao Jan. 26, 2024, 9:45 a.m. UTC | #7
On Fri, 2024-01-26 at 16:40 +0800, mengqinggang wrote:
> We will send a patch to fix this and some other questions today.

Just a note: with e14f7abaf81d5d0aa7898570024286de8c188817 gas is not
emitting R_LARCH_RELAX for 2-operand la.tls.ie:

$ cat t.s
.globl _start
_start:
  la.tls.ie $a0, foo

.section .tdata
foo: .word 114514
$ gas/as-new t.s -o t.o
$ binutils/objdump -dr t.o

t.o:     file format elf64-loongarch


Disassembly of section .text:

0000000000000000 <_start>:
   0:	1a000004 	pcalau12i   	$a0, 0
			0: R_LARCH_TLS_IE_PC_HI20	foo
   4:	28c00084 	ld.d        	$a0, $a0, 0
			4: R_LARCH_TLS_IE_PC_LO12	foo

I guess this needs to be fixed too.
  

Patch

diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index b62bb424644..b9709401f56 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -717,6 +717,40 @@  loongarch_tls_transition (struct bfd_link_info *info, unsigned int r_type,
   return loongarch_tls_transition_without_check (info, r_type, h);
 }
 
+/* For TLS IE in extreme code model:
+       pcalau12i t0, %ie_pc_hi20(x)
+       li.d      t1, %ie_pc_lo12(x)
+       lu32i.d   t1, %ie64_pc_lo20(x)
+       lu52i.d   t1, t1, %ie64_pc_hi12(x)
+       ldx.d     t0, t0, t1
+   We've no idea how to remove the ldx.d instruction or turn it into a
+   nop because there is no reloc on it.  But we have to stop turning this
+   into LE or we'll end up
+       lu12i.w   t0, %le_hi20(x)
+       ori       t1, t1, %le_lo12(x)
+       lu32i.d   t1, %ie64_pc_lo20(x)
+       lu52i.d   t1, t1, %ie64_pc_hi12(x)
+       ldx.d     t0, t0, t1
+   which is completely wrong.  For TLS DESC it's similar: we cannot remove
+   the add.d instruction but we have to stop the transition.  */
+static bool
+loongarch_elf_allow_tls_transition_p (const Elf_Internal_Rela *r_begin,
+				      const Elf_Internal_Rela *r_end)
+{
+  for (; r_begin != r_end; r_begin++)
+    switch (ELFNN_R_TYPE (r_begin->r_info))
+      {
+      case R_LARCH_TLS_IE64_PC_HI12:
+      case R_LARCH_TLS_IE64_PC_LO20:
+      case R_LARCH_TLS_DESC64_PC_HI12:
+      case R_LARCH_TLS_DESC64_PC_LO20:
+	return false;
+      }
+
+  return true;
+}
+
+
 /* Look through the relocs for a section during the first phase, and
    allocate space in the global offset table or procedure linkage
    table.  */
@@ -730,6 +764,7 @@  loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   struct elf_link_hash_entry **sym_hashes;
   const Elf_Internal_Rela *rel;
   asection *sreloc = NULL;
+  bool allow_tls_transition;
 
   if (bfd_link_relocatable (info))
     return true;
@@ -741,6 +776,9 @@  loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   if (htab->elf.dynobj == NULL)
     htab->elf.dynobj = abfd;
 
+  allow_tls_transition = loongarch_elf_allow_tls_transition_p (
+    relocs, relocs + sec->reloc_count);
+
   for (rel = relocs; rel < relocs + sec->reloc_count; rel++)
     {
       unsigned int r_type;
@@ -818,7 +856,8 @@  loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       int need_dynreloc = 0;
       int only_need_pcrel = 0;
 
-      r_type = loongarch_tls_transition (info, r_type, h, abfd, r_symndx);
+      if (allow_tls_transition)
+	r_type = loongarch_tls_transition (info, r_type, h, abfd, r_symndx);
       switch (r_type)
 	{
 	case R_LARCH_GOT_PC_HI20:
@@ -2632,7 +2671,6 @@  loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 				asection **local_sections)
 {
   Elf_Internal_Rela *rel;
-  Elf_Internal_Rela *relend;
   bool fatal = false;
   asection *sreloc = elf_section_data (input_section)->sreloc;
   struct loongarch_elf_link_hash_table *htab = loongarch_elf_hash_table (info);
@@ -2643,8 +2681,10 @@  loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
   bool is_dyn = elf_hash_table (info)->dynamic_sections_created;
   asection *plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
   asection *got = htab->elf.sgot;
+  Elf_Internal_Rela *relend = relocs + input_section->reloc_count;
+  bool allow_tls_transition =
+    loongarch_elf_allow_tls_transition_p (relocs, relend);
 
-  relend = relocs + input_section->reloc_count;
   for (rel = relocs; rel < relend; rel++)
     {
       unsigned int r_type = ELFNN_R_TYPE (rel->r_info);
@@ -2789,7 +2829,12 @@  loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
       BFD_ASSERT (!resolved_local || defined_local);
 
-      relaxed_r_type = loongarch_tls_transition (info, r_type, h, input_bfd, r_symndx);
+      relaxed_r_type =
+	(allow_tls_transition ? loongarch_tls_transition (info, r_type,
+							  h, input_bfd,
+							  r_symndx)
+			      : r_type);
+
       if (relaxed_r_type != r_type)
       {
 	howto = loongarch_elf_rtype_to_howto (input_bfd, relaxed_r_type);
diff --git a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
index 2ff06d62236..9e35940f5d6 100644
--- a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
+++ b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
@@ -88,6 +88,20 @@  if [istarget "loongarch64-*-*"] {
 	      "medium-call" \
 	  ] \
       ]
+
+  run_ld_link_tests \
+      [list \
+	  [list \
+	      "disable TLS IE transition with TLS_IE64_PC reloc" \
+	      "-e 0x0" "" \
+	      "" \
+	      {tls-ie64-notrans.s} \
+	      [list \
+		  [list objdump -D tls-ie64-notrans.d] \
+	      ] \
+	      "tls-ie64-notrans" \
+	  ] \
+      ]
 }
 
 if [istarget "loongarch64-*-*"] {
diff --git a/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.d b/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.d
new file mode 100644
index 00000000000..47d507a0538
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.d
@@ -0,0 +1,3 @@ 
+#...
+.*pcalau12i.*
+#pass
diff --git a/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.s b/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.s
new file mode 100644
index 00000000000..cd8c65ff9d3
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/tls-ie64-notrans.s
@@ -0,0 +1,7 @@ 
+# it had segfaulted the linker due to invalid IE->LE transition
+.globl _start
+_start:
+  la.tls.ie $a0, $a1, foo
+
+.section .tdata
+foo: .word 114514