aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/ChangeLog10
-rw-r--r--bfd/elf32-avr.c99
-rw-r--r--ld/ChangeLog7
-rw-r--r--ld/testsuite/ld-avr/pr20789.d14
-rw-r--r--ld/testsuite/ld-avr/pr20789.s12
5 files changed, 105 insertions, 37 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index b8ae63404e0..fcc5b1c5e60 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 89c99fda7cc..46a2b271ffe 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 f0f6694138b..7add280f824 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 00000000000..fb1114bee69
--- /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 00000000000..cee46e7a692
--- /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