Fix the sorting algorithm for reloc entries
authorTomoaki Kawada <kawada@kmckk.co.jp>
Thu, 16 Jun 2022 09:54:30 +0000 (09:54 +0000)
committerAlan Modra <amodra@gmail.com>
Sat, 18 Jun 2022 10:41:23 +0000 (20:11 +0930)
The optimized insertion sort algorithm in `elf_link_adjust_relocs`
incorrectly assembled "runs" from unsorted entries and inserted them to an
already-sorted prefix, breaking the loop invariants of insertion sort.
This commit updates the run assembly loop to break upon encountering a
non-monotonic change in the sort key.

PR 29259
bfd/
* elflink.c (elf_link_adjust_relocs): Ensure run being inserted
is sorted.
ld/
* testsuite/ld-elf/pr29259.d,
* testsuite/ld-elf/pr29259.s,
* testsuite/ld-elf/pr29259.t: New test.

bfd/elflink.c
ld/testsuite/ld-elf/pr29259.d [new file with mode: 0644]
ld/testsuite/ld-elf/pr29259.s [new file with mode: 0644]
ld/testsuite/ld-elf/pr29259.t [new file with mode: 0644]

index e4f6df430428e4be609bbe2928c0263e42f50430..dcafac328009ef93576f289f9630fe8ad72f6a65 100644 (file)
@@ -9548,12 +9548,20 @@ elf_link_adjust_relocs (bfd *abfd,
              size_t sortlen = p - loc;
              bfd_vma r_off2 = (*ext_r_off) (loc);
              size_t runlen = elt_size;
+             bfd_vma r_off_runend = r_off;
+             bfd_vma r_off_runend_next;
              size_t buf_size = 96 * 1024;
              while (p + runlen < end
                     && (sortlen <= buf_size
                         || runlen + elt_size <= buf_size)
-                    && r_off2 > (*ext_r_off) (p + runlen))
-               runlen += elt_size;
+                    /* run must not break the ordering of base..loc+1 */
+                    && r_off2 > (r_off_runend_next = (*ext_r_off) (p + runlen))
+                    /* run must be already sorted */
+                    && r_off_runend_next >= r_off_runend)
+               {
+                 runlen += elt_size;
+                 r_off_runend = r_off_runend_next;
+               }
              if (buf == NULL)
                {
                  buf = bfd_malloc (buf_size);
diff --git a/ld/testsuite/ld-elf/pr29259.d b/ld/testsuite/ld-elf/pr29259.d
new file mode 100644 (file)
index 0000000..c7eaa7a
--- /dev/null
@@ -0,0 +1,13 @@
+#ld: -r -T pr29259.t
+#readelf: -Wr
+
+Relocation section .*
+ +Offset +Info +Type .*
+0+00 .*
+#...
+0+20 .*
+#...
+0+40 .*
+#...
+0+60 .*
+#pass
diff --git a/ld/testsuite/ld-elf/pr29259.s b/ld/testsuite/ld-elf/pr29259.s
new file mode 100644 (file)
index 0000000..65fc73f
--- /dev/null
@@ -0,0 +1,14 @@
+ .data
+.L1:
+ .section ".data.1", "aw", %progbits
+ .p2align 5
+ .dc.a .L1
+ .section ".data.2", "aw", %progbits
+ .p2align 5
+ .dc.a .L1
+ .section ".data.3", "aw", %progbits
+ .p2align 5
+ .dc.a .L1
+ .section ".data.4", "aw", %progbits
+ .p2align 5
+ .dc.a .L1
diff --git a/ld/testsuite/ld-elf/pr29259.t b/ld/testsuite/ld-elf/pr29259.t
new file mode 100644 (file)
index 0000000..ed80f87
--- /dev/null
@@ -0,0 +1,4 @@
+SECTIONS
+{
+  .data : { *(.data.1) *(.data.4) *(.data.3) *(.data.2) *(.data) }
+}