[v2] LoongArch: Do not relax pcalau12i+ld.d when there is overflow

Message ID 20241116092202.51171-1-cailulu@loongson.cn
State New
Headers
Series [v2] LoongArch: Do not relax pcalau12i+ld.d when there is overflow |

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. 16, 2024, 9:22 a.m. UTC
  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.

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.

---
Changes from v1:
  * Update test case.

v1: https://sourceware.org/pipermail/binutils/2024-November/137597.html
---
 bfd/elfnn-loongarch.c                         | 32 ++++++++--
 .../ld-loongarch-elf/check_got_relax.d        | 60 +++++++++++++++++++
 .../ld-loongarch-elf/check_got_relax.s        | 43 +++++++++++++
 .../ld-loongarch-elf/check_relax_got.ld       | 25 ++++++++
 .../ld-loongarch-elf/ld-loongarch-elf.exp     |  1 +
 5 files changed, 156 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/check_relax_got.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..da00fec416b
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/check_got_relax.d
@@ -0,0 +1,60 @@ 
+#ld: -T check_relax_got.ld
+#objdump: -D
+
+.*:     file format .*
+
+
+Disassembly of section .text:
+
+0+10000 <_start>:
+   10000:	1a03fe04 	pcalau12i   	\$a0, 8176
+   10004:	28c06084 	ld.d        	\$a0, \$a0, 24
+   10008:	1a03fe04 	pcalau12i   	\$a0, 8176
+   1000c:	28c08084 	ld.d        	\$a0, \$a0, 32
+   10010:	1a03fe04 	pcalau12i   	\$a0, 8176
+   10014:	28c04084 	ld.d        	\$a0, \$a0, 16
+
+0+10018 <sym_default_1>:
+   10018:	00000123 	.word		0x00000123
+
+0+1001c <sym_hidden_1>:
+   1001c:	00000123 	.word		0x00000123
+
+0+10020 <sym_protected_1>:
+   10020:	00000123 	.word		0x00000123
+
+Disassembly of section .got:
+
+0+2000000 <_GLOBAL_OFFSET_TABLE_>:
+	...
+ 2000008:	00010018 	.word		0x00010018
+ 200000c:	00000000 	.word		0x00000000
+ 2000010:	82000020 	.word		0x82000020
+ 2000014:	00000000 	.word		0x00000000
+ 2000018:	82000018 	.word		0x82000018
+ 200001c:	00000000 	.word		0x00000000
+ 2000020:	8200001c 	.word		0x8200001c
+ 2000024:	00000000 	.word		0x00000000
+ 2000028:	0001001c 	.word		0x0001001c
+ 200002c:	00000000 	.word		0x00000000
+ 2000030:	00010020 	asrtle.d    	\$ra, \$zero
+ 2000034:	00000000 	.word		0x00000000
+
+Disassembly of section .other:
+
+0+82000000 <underflow>:
+    82000000:	1b000004 	pcalau12i   	\$a0, -524288
+    82000004:	28c02084 	ld.d        	\$a0, \$a0, 8
+    82000008:	1b000004 	pcalau12i   	\$a0, -524288
+    8200000c:	28c0a084 	ld.d        	\$a0, \$a0, 40
+    82000010:	1b000004 	pcalau12i   	\$a0, -524288
+    82000014:	28c0c084 	ld.d        	\$a0, \$a0, 48
+
+0+82000018 <sym_default_2>:
+    82000018:	00000456 	.word		0x00000456
+
+0+8200001c <sym_hidden_2>:
+    8200001c:	00000456 	.word		0x00000456
+
+0+82000020 <sym_protected_2>:
+    82000020:	00000456 	.word		0x00000456
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..6bdf8174f58
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/check_got_relax.s
@@ -0,0 +1,43 @@ 
+	.text
+	.global _start
+_start:
+
+	# dot not relax when overflow
+	la.got	  $a0,sym_default_2
+	la.got	  $a0,sym_hidden_2
+	la.got	  $a0,sym_protected_2
+
+sym_default_1:
+	.word	  0x123
+
+	.global	  sym_hidden_1
+	.hidden	  sym_hidden_1
+sym_hidden_1:
+	.word	  0x123
+
+	.global	    sym_protected_1
+	.protected  sym_protected_1
+sym_protected_1:
+	.word	  0x123
+
+
+	.section  .other,"ax",@progbits
+underflow:
+	# dot not relax when underflow
+	la.got	  $a0,sym_default_1
+	la.got	  $a0,sym_hidden_1
+	la.got	  $a0,sym_protected_1
+
+	.global	  sym_default_2
+sym_default_2:
+	.word	  0x456
+
+	.global	  sym_hidden_2
+	.hidden	  sym_hidden_2
+sym_hidden_2:
+	.word	  0x456
+
+	.global	    sym_protected_2
+	.protected  sym_protected_2
+sym_protected_2:
+	.word	  0x456
diff --git a/ld/testsuite/ld-loongarch-elf/check_relax_got.ld b/ld/testsuite/ld-loongarch-elf/check_relax_got.ld
new file mode 100644
index 00000000000..287f28ecf94
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/check_relax_got.ld
@@ -0,0 +1,25 @@ 
+OUTPUT_ARCH(loongarch)
+ENTRY(_start)
+SECTIONS
+{
+  PROVIDE (__executable_start = 0x8000);
+  . = 0x10000;
+  .text :
+  {
+    *(.text)
+  } =0
+  . = 0x2000000;
+  .got :
+  {
+    *(.got.plt) *(.got)
+  }
+  . = 0x82000000;
+  .other :
+  {
+    *(.other)
+  }
+  /DISCARD/ :
+  {
+    *(*)
+  }
+}
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-*-*"] {