This is the mail archive of the binutils-cvs@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Fix PR20789 - relaxation with negative valued diff relocs


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=4cb771f214ed6a2102e37bce255c6be5d0642f3a

commit 4cb771f214ed6a2102e37bce255c6be5d0642f3a
Author: Senthil Kumar Selvaraj <senthil_kumar.selvaraj@atmel.com>
Date:   Wed Nov 16 16:11:46 2016 +0530

    Fix PR20789 - relaxation with negative valued diff relocs
    
    Fix issues with diff relocs that have a negative value
    i.e. sym2 - sym1 where sym2 is lesser than sym1.
    
    The assembler generates a diff reloc with symbol as start of section
    and addend as sym2 offset, and encodes assembly time difference at
    the reloc offset.
    
    The existing relaxation logic adjusts addends if the relaxed insn lies
    between symbol and addend. That doesn't work for diff relocs where
    sym2 is less than sym1 *and* the relaxed insn happens to be between
    sym2 and sym1.
    
    Fix the problems by
    
    1. Using signed handling of the difference value (bfd_signed_vma instead
    of bfd_vma, bfd_{get,set}_signed_xxx instead of bfd_{get,set}_xxx).
    
    2. Not assuming sym2 is bigger than sym1. It instead computes the actual
    addresses and sets the lower and higher addresses as start and end
    addresses respectively and then sees if insn is between start and end.
    
    3. Creating a new function elf32_avr_adjust_reloc_if_spans_insn to
    centralize reloc adjustment, and ensuring diff relocs get adjusted
    correctly even if their sym + addend doesn't overlap a relaxed insn.
    
    It also removes a redundant variable did_pad. It is never set if
    did_shrink is TRUE, and the code does a early return if did_shrink is
    FALSE.
    
    bfd/ChangeLog
    
    2016-11-15  Senthil Kumar Selvaraj  <senthil_kumar.selvaraj@atmel.com>
    
           PR ld/20789
           * bfd/elf32-avr.c (elf32_avr_adjust_diff_reloc_value): Do signed
           manipulation of diff value, and don't assume sym2 is less than sym1.
           (elf32_avr_adjust_reloc_if_spans_insn): New function.
           (elf32_avr_relax_delete_bytes): Use elf32_avr_adjust_diff_reloc_value,
           and remove redundant did_pad.
    
    ld/ChangeLog
    
    2016-11-15  Senthil Kumar Selvaraj  <senthil_kumar.selvaraj@atmel.com>
    
           PR ld/20789
           * ld/testsuite/ld-avr/pr20789.d: New test.
           * ld/testsuite/ld-avr/pr20789.s: New test.

Diff:
---
 bfd/ChangeLog                 | 10 +++++
 bfd/elf32-avr.c               | 99 +++++++++++++++++++++++++++----------------
 ld/ChangeLog                  |  7 +++
 ld/testsuite/ld-avr/pr20789.d | 14 ++++++
 ld/testsuite/ld-avr/pr20789.s | 12 ++++++
 5 files changed, 105 insertions(+), 37 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index b8ae634..fcc5b1c 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,13 @@
+2016-11-15  Senthil Kumar Selvaraj  <senthil_kumar.selvaraj@atmel.com>
+
+	PR ld/20789
+	* bfd/elf32-avr.c (elf32_avr_adjust_diff_reloc_value): Do signed
+	manipulation of diff value, and don't assume sym2 is less than sym1.
+	(elf32_avr_adjust_reloc_if_spans_insn): New function.
+	(elf32_avr_relax_delete_bytes): Use elf32_avr_adjust_diff_reloc_value,
+	and remove redundant did_pad.
+
+
 2016-11-14  H.J. Lu  <hongjiu.lu@intel.com>
 
 	PR ld/20800
diff --git a/bfd/elf32-avr.c b/bfd/elf32-avr.c
index 89c99fd..46a2b27 100644
--- a/bfd/elf32-avr.c
+++ b/bfd/elf32-avr.c
@@ -1742,22 +1742,22 @@ elf32_avr_adjust_diff_reloc_value (bfd *abfd,
   reloc_contents = isec_contents + irel->r_offset;
 
   /* Read value written in object file. */
- bfd_vma x = 0;
+  bfd_signed_vma x = 0;
   switch (ELF32_R_TYPE (irel->r_info))
   {
   case R_AVR_DIFF8:
     {
-      x = *reloc_contents;
+      x = bfd_get_signed_8 (abfd, reloc_contents);
       break;
     }
   case R_AVR_DIFF16:
     {
-      x = bfd_get_16 (abfd, reloc_contents);
+      x = bfd_get_signed_16 (abfd, reloc_contents);
       break;
     }
   case R_AVR_DIFF32:
     {
-      x = bfd_get_32 (abfd, reloc_contents);
+      x = bfd_get_signed_32 (abfd, reloc_contents);
       break;
     }
   default:
@@ -1771,30 +1771,41 @@ elf32_avr_adjust_diff_reloc_value (bfd *abfd,
      symval (<start_of_section>) + reloc addend. Compute the start and end
      addresses and check if the shrinked insn falls between sym1 and sym2. */
 
-  bfd_vma end_address = symval + irel->r_addend;
-  bfd_vma start_address = end_address - x;
+  bfd_vma sym2_address = symval + irel->r_addend;
+  bfd_vma sym1_address = sym2_address - x;
+
+  /* Don't assume sym2 is bigger than sym1 - the difference
+     could be negative. Compute start and end addresses, and
+     use those to see if they span shrinked_insn_address. */
+
+  bfd_vma start_address = sym1_address < sym2_address
+    ? sym1_address : sym2_address;
+  bfd_vma end_address = sym1_address > sym2_address
+    ? sym1_address : sym2_address;
 
-  /* Reduce the diff value by count bytes and write it back into section
-    contents. */
 
   if (shrinked_insn_address >= start_address
       && shrinked_insn_address <= end_address)
   {
+    /* Reduce the diff value by count bytes and write it back into section
+       contents. */
+    bfd_signed_vma new_diff = x < 0 ? x + count : x - count;
+
     switch (ELF32_R_TYPE (irel->r_info))
     {
     case R_AVR_DIFF8:
       {
-        *reloc_contents = (x - count);
+        bfd_put_signed_8 (abfd, new_diff, reloc_contents);
         break;
       }
     case R_AVR_DIFF16:
       {
-        bfd_put_16 (abfd, (x - count) & 0xFFFF, reloc_contents);
+        bfd_put_signed_16 (abfd, new_diff & 0xFFFF, reloc_contents);
         break;
       }
     case R_AVR_DIFF32:
       {
-        bfd_put_32 (abfd, (x - count) & 0xFFFFFFFF, reloc_contents);
+        bfd_put_signed_32 (abfd, new_diff & 0xFFFFFFFF, reloc_contents);
         break;
       }
     default:
@@ -1806,6 +1817,43 @@ elf32_avr_adjust_diff_reloc_value (bfd *abfd,
   }
 }
 
+static void
+elf32_avr_adjust_reloc_if_spans_insn (bfd *abfd,
+                                      asection *isec,
+                                      Elf_Internal_Rela *irel,  bfd_vma symval,
+                                      bfd_vma shrinked_insn_address,
+                                      bfd_vma shrink_boundary,
+                                      int count)
+{
+
+  if (elf32_avr_is_diff_reloc (irel))
+    {
+      elf32_avr_adjust_diff_reloc_value (abfd, isec, irel,
+                                         symval,
+                                         shrinked_insn_address,
+                                         count);
+    }
+  else
+    {
+      bfd_vma reloc_value = symval + irel->r_addend;
+      bfd_boolean addend_within_shrink_boundary =
+        (reloc_value <= shrink_boundary);
+
+      bfd_boolean reloc_spans_insn =
+        (symval <= shrinked_insn_address
+         && reloc_value > shrinked_insn_address
+         && addend_within_shrink_boundary);
+
+      if (! reloc_spans_insn)
+        return;
+
+      irel->r_addend -= count;
+
+      if (debug_relax)
+        printf ("Relocation's addend needed to be fixed \n");
+    }
+}
+
 /* Delete some bytes from a section while changing the size of an instruction.
    The parameter "addr" denotes the section-relative offset pointing just
    behind the shrinked instruction. "addr+count" point at the first
@@ -1834,7 +1882,6 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
   struct avr_relax_info *relax_info;
   struct avr_property_record *prop_record = NULL;
   bfd_boolean did_shrink = FALSE;
-  bfd_boolean did_pad = FALSE;
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
@@ -1915,7 +1962,6 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
          to remember we didn't delete anything i.e. don't set did_shrink,
          so that we don't corrupt reloc offsets or symbol values.*/
       memset (contents + toaddr - count, fill, count);
-      did_pad = TRUE;
 
       /* Adjust the TOADDR to avoid moving symbols located at the address
          of the property record, which has not moved.  */
@@ -2016,7 +2062,6 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
                    bfd_vma shrink_boundary = (reloc_toaddr
                                               + sec->output_section->vma
                                               + sec->output_offset);
-                   bfd_boolean addend_within_shrink_boundary = FALSE;
 
                    symval += sym_sec->output_section->vma
                              + sym_sec->output_offset;
@@ -2031,31 +2076,11 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
                              (unsigned int) (symval + irel->r_addend),
                              (unsigned int) shrinked_insn_address);
 
-                   /* If we padded bytes, then the boundary didn't change,
-                      so there's no need to adjust addends pointing at the boundary.
-                      If we didn't pad, then we actually shrank the boundary, so
-                      addends pointing at the boundary need to be adjusted too. */
-                    addend_within_shrink_boundary = did_pad
-                      ? ((symval + irel->r_addend) < shrink_boundary)
-                      : ((symval + irel->r_addend) <= shrink_boundary);
-
-                   if (symval <= shrinked_insn_address
-                       && (symval + irel->r_addend) > shrinked_insn_address
-                       && addend_within_shrink_boundary)
-                     {
-                       if (elf32_avr_is_diff_reloc (irel))
-                         {
-                           elf32_avr_adjust_diff_reloc_value (abfd, isec, irel,
+                   elf32_avr_adjust_reloc_if_spans_insn (abfd, isec, irel,
                                                          symval,
                                                          shrinked_insn_address,
-                                                        count);
-                         }
-
-                       irel->r_addend -= count;
-
-                       if (debug_relax)
-                         printf ("Relocation's addend needed to be fixed \n");
-                     }
+                                                         shrink_boundary,
+                                                         count);
                  }
 	       /* else...Reference symbol is absolute.  No adjustment needed.  */
 	     }
diff --git a/ld/ChangeLog b/ld/ChangeLog
index f0f6694..7add280 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,10 @@
+2016-11-15  Senthil Kumar Selvaraj  <senthil_kumar.selvaraj@atmel.com>
+
+	PR ld/20789
+	* ld/testsuite/ld-avr/pr20789.d: New test.
+	* ld/testsuite/ld-avr/pr20789.s: New test.
+
+
 2016-11-14  H.J. Lu  <hongjiu.lu@intel.com>
 
 	PR ld/20800
diff --git a/ld/testsuite/ld-avr/pr20789.d b/ld/testsuite/ld-avr/pr20789.d
new file mode 100644
index 0000000..fb1114b
--- /dev/null
+++ b/ld/testsuite/ld-avr/pr20789.d
@@ -0,0 +1,14 @@
+#name: AVR Account for relaxation in negative label differences
+#as: -mmcu=avrxmega2 -mlink-relax
+#ld:  -mavrxmega2 --relax
+#source: pr20789.s
+#objdump: -s
+#target: avr-*-*
+
+.*:     file format elf32-avr
+
+Contents of section .text:
+ 0000 ffcf                                 .*
+Contents of section .data:
+ 802000 feff                               .*
+
diff --git a/ld/testsuite/ld-avr/pr20789.s b/ld/testsuite/ld-avr/pr20789.s
new file mode 100644
index 0000000..cee46e7
--- /dev/null
+++ b/ld/testsuite/ld-avr/pr20789.s
@@ -0,0 +1,12 @@
+    .file "pr20789.s"
+.section	.text,"ax",@progbits
+main:
+L1:
+    jmp  L1
+L2:
+.global	x
+	.section .data
+	.type	x, @object
+	.size	x, 2
+x:
+	.word	L1 - L2


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]