[v1] LoongArch: Do not relax pcalau12i+ld.d when overflow occurs after relaxation

Message ID 20241112025701.2678086-1-cailulu@loongson.cn
State New
Headers
Series [v1] LoongArch: Do not relax pcalau12i+ld.d when overflow occurs after relaxation |

Checks

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

Commit Message

Lulu Cai Nov. 12, 2024, 2:57 a.m. UTC
  Currently, there is no overflow check for the relaxation of
pcalau12i+ld.d => pcalau12i+addi.d. For instruction sequences that can
be relaxed, they are directly relaxed to pcalau12i+addi.d. However, when
the relative distance between the symbol and the pc exceeds the 32-bit
range, the symbol value cannot be obtained correctly.

This modification adds an overflow check for the relaxation of
pcalau12i+ld.d. If it is found that the relaxation will overflow, it
will not be relaxed.
---
 bfd/elfnn-loongarch.c                         | 32 ++++++++++++++++---
 .../ld-loongarch-elf/check_got_relax.d        | 30 +++++++++++++++++
 .../ld-loongarch-elf/check_got_relax.s        | 16 ++++++++++
 .../ld-loongarch-elf/ld-loongarch-elf.exp     |  1 +
 ld/testsuite/ld-loongarch-elf/relax.ld        | 21 ++++++++++++
 5 files changed, 95 insertions(+), 5 deletions(-)
 create mode 100644 ld/testsuite/ld-loongarch-elf/check_got_relax.d
 create mode 100644 ld/testsuite/ld-loongarch-elf/check_got_relax.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/relax.ld
  

Patch

diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index 608d179c168..8189a23a3a9 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -5099,12 +5099,12 @@  loongarch_relax_call36 (bfd *abfd, asection *sec, asection *sym_sec,
 /* Relax pcalau12i,ld.d => pcalau12i,addi.d.  */
 static bool
 loongarch_relax_pcala_ld (bfd *abfd, asection *sec,
-			  asection *sym_sec ATTRIBUTE_UNUSED,
+			  asection *sym_sec,
 			  Elf_Internal_Rela *rel_hi,
-			  bfd_vma symval ATTRIBUTE_UNUSED,
-			  struct bfd_link_info *info ATTRIBUTE_UNUSED,
+			  bfd_vma symval,
+			  struct bfd_link_info *info,
 			  bool *again ATTRIBUTE_UNUSED,
-			  bfd_vma max_alignment ATTRIBUTE_UNUSED)
+			  bfd_vma max_alignment)
 {
   bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
   Elf_Internal_Rela *rel_lo = rel_hi + 2;
@@ -5113,10 +5113,32 @@  loongarch_relax_pcala_ld (bfd *abfd, asection *sec,
   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,
+     so we additionally update once here.  */
+  sec->output_offset = sec->output_section->size;
+  bfd_vma pc = sec_addr (sec) + rel_hi->r_offset;
+
+  /* If pc and symbol not in the same segment, add/sub segment alignment.  */
+  if (!loongarch_two_sections_in_same_segment (info->output_bfd,
+					       sec->output_section,
+					       sym_sec->output_section))
+    max_alignment = info->maxpagesize > max_alignment ? info->maxpagesize
+							: max_alignment;
+
+  if (symval > pc)
+    pc -= (max_alignment > 4 ? max_alignment : 0);
+  else if (symval < pc)
+    pc += (max_alignment > 4 ? max_alignment : 0);
+
   if ((ELFNN_R_TYPE (rel_lo->r_info) != R_LARCH_GOT_PC_LO12)
       || (LARCH_GET_RD (ld) != rd)
       || (LARCH_GET_RJ (ld) != rd)
-      || !LARCH_INSN_LD_D (ld))
+      || !LARCH_INSN_LD_D (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;
diff --git a/ld/testsuite/ld-loongarch-elf/check_got_relax.d b/ld/testsuite/ld-loongarch-elf/check_got_relax.d
new file mode 100644
index 00000000000..3fb3b6d4d01
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/check_got_relax.d
@@ -0,0 +1,30 @@ 
+#ld: -T relax.ld
+#objdump: -D -j.text -j.got -j.other
+
+.*:     file format .*
+
+
+Disassembly of section \.text:
+
+0+10000 <_start>:
+   10000:	1a000204 	pcalau12i   	\$a0, 16
+   10004:	28c02084 	ld.d        	\$a0, \$a0, 8
+   10008:	1a000204 	pcalau12i   	\$a0, 16
+   1000c:	28c04084 	ld.d        	\$a0, \$a0, 16
+
+Disassembly of section \.got:
+
+0+20000 <_GLOBAL_OFFSET_TABLE_>:
+	...
+   20008:	0000000f 	.word		0x0000000f
+   2000c:	00000001 	.word		0x00000001
+   20010:	00000013 	.word		0x00000013
+   20014:	00000001 	.word		0x00000001
+
+Disassembly of section \.other:
+
+0+10000000f <sym_hidden>:
+   10000000f:	03400000 	nop
+
+0+100000013 <sym_protected>:
+   100000013:	03400000 	nop
diff --git a/ld/testsuite/ld-loongarch-elf/check_got_relax.s b/ld/testsuite/ld-loongarch-elf/check_got_relax.s
new file mode 100644
index 00000000000..bc13dca8461
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/check_got_relax.s
@@ -0,0 +1,16 @@ 
+	.text
+	.global _start
+_start:
+	la.got $a0,sym_hidden
+	la.got $a0,sym_protected
+
+	.section .other
+	.global sym_hidden
+	.hidden sym_hidden
+sym_hidden:
+	nop
+
+	.global sym_protected
+	.protected  sym_protected
+sym_protected:
+	nop
diff --git a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
index 3313f72eee4..e1b038cb579 100644
--- a/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
+++ b/ld/testsuite/ld-loongarch-elf/ld-loongarch-elf.exp
@@ -47,6 +47,7 @@  if [istarget "loongarch64-*-*"] {
     run_dump_test "tls-le-relax"
     run_dump_test "relax-medium-call"
     run_dump_test "relax-medium-call-1"
+    run_dump_test "check_got_relax"
 }
 
 if [istarget "loongarch32-*-*"] {
diff --git a/ld/testsuite/ld-loongarch-elf/relax.ld b/ld/testsuite/ld-loongarch-elf/relax.ld
new file mode 100644
index 00000000000..810a8b669a8
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/relax.ld
@@ -0,0 +1,21 @@ 
+OUTPUT_ARCH(loongarch)
+ENTRY(_start)
+SECTIONS
+{
+  PROVIDE (__executable_start = 0x8000); . = 0x10000;
+  .text :
+  {
+    *(.text)
+  } =0
+  . = 0x20000;
+  .got :
+  {
+    *(.got)
+    *(.got.plt)
+  }
+  . = 0x10000000f;
+  .other :
+  {
+    *(.other)
+  }
+}