aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-avr.c
diff options
context:
space:
mode:
authorSenthil Kumar Selvaraj <senthil_kumar.selvaraj@atmel.com>2017-04-24 15:17:14 +0530
committerSenthil Kumar Selvaraj <senthil_kumar.selvaraj@atmel.com>2017-05-04 10:34:29 +0530
commitb660e9eb7a45cfe99e719c5d16af35913a2fdc96 (patch)
tree3b4b2285e27d3ab355596ce85d0bf138f2c078dc /bfd/elf32-avr.c
parent8293e73636484b403150ae94a93619779bcbae96 (diff)
downloadfsf-binutils-gdb-b660e9eb7a45cfe99e719c5d16af35913a2fdc96.zip
fsf-binutils-gdb-b660e9eb7a45cfe99e719c5d16af35913a2fdc96.tar.gz
fsf-binutils-gdb-b660e9eb7a45cfe99e719c5d16af35913a2fdc96.tar.bz2
Fix PR21404 - assertion fail when calculating symbol size
Fix a host of problems related to adjustment of symbol values and sizes when relaxing for avr. 1. Adjust symbol size first before adjusting symbol value. Otherwise, a symbol whose value just got adjusted to the relaxed address also ends up getting resized. See pr21404-1.s. 2. Reduce symbol sizes only if their span is below an alignment boundary. Otherwise, the size gets decremented once when the actual instruction is relaxed and padding bytes are added, and again when the padding bytes are deleted (if padding ends up being unnecessary). pr21404-2.s addresses that, and this bug is really the root cause of PR21404. 3. Adjust all symbol values before an alignment boundary. Previous code did not adjust symbol values if they fell in the would-be padded area, resulting in incorrect symbol values in some cases (see pr21404-3.s). 4. Increase symbol sizes if alignment directives require so. As pr21404-4.s shows .global nonzero_sym L1: jmp L1 nonzero_sym: nop nop .p2align 2 .size nonzero_sym, .-nonzero_sym The two nops satisfy the 4 byte alignment at assembly time and therefore the size of nonzero_sym is 4. Relaxation shortens the 4 byte jmp to a 2 byte rjmp, and to satisfy 4 byte alignment the code places 2 extra padding bytes after the nops, increasing nonzero_sym's size by 2. This wasn't handled before. If the assembly code does not have any align directives, then the boundary is the section size, and symbol values and sizes == boundary should also get adjusted. To handle that case, add a did_pad variable and use that to determine whether it should use < boundary or <= boundary. Also get rid of reloc_toaddr, which is now redundant. toaddr is now not adjusted to handle the above case - the newly added did_pad variable does the job. pr21404-{5,6,7,8} are the same testcases written for local symbols, as the code handles them slightly differently.
Diffstat (limited to 'bfd/elf32-avr.c')
-rw-r--r--bfd/elf32-avr.c91
1 files changed, 60 insertions, 31 deletions
diff --git a/bfd/elf32-avr.c b/bfd/elf32-avr.c
index 0f6c188..f140aa7 100644
--- a/bfd/elf32-avr.c
+++ b/bfd/elf32-avr.c
@@ -1854,6 +1854,40 @@ elf32_avr_adjust_reloc_if_spans_insn (bfd *abfd,
}
}
+static bfd_boolean
+avr_should_move_sym (symvalue symval,
+ bfd_vma start,
+ bfd_vma end,
+ bfd_boolean did_pad)
+{
+ bfd_boolean sym_within_boundary =
+ did_pad ? symval < end : symval <= end;
+ return (symval > start && sym_within_boundary);
+}
+
+static bfd_boolean
+avr_should_reduce_sym_size (symvalue symval,
+ symvalue symend,
+ bfd_vma start,
+ bfd_vma end,
+ bfd_boolean did_pad)
+{
+ bfd_boolean sym_end_within_boundary =
+ did_pad ? symend < end : symend <= end;
+ return (symval <= start && symend > start && sym_end_within_boundary);
+}
+
+static bfd_boolean
+avr_should_increase_sym_size (symvalue symval,
+ symvalue symend,
+ bfd_vma start,
+ bfd_vma end,
+ bfd_boolean did_pad)
+{
+ return avr_should_move_sym (symval, start, end, did_pad)
+ && symend >= end && did_pad;
+}
+
/* 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
@@ -1875,13 +1909,14 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
Elf_Internal_Rela *irel, *irelend;
Elf_Internal_Sym *isym;
Elf_Internal_Sym *isymbuf = NULL;
- bfd_vma toaddr, reloc_toaddr;
+ bfd_vma toaddr;
struct elf_link_hash_entry **sym_hashes;
struct elf_link_hash_entry **end_hashes;
unsigned int symcount;
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);
@@ -1912,17 +1947,6 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
}
}
- /* We need to look at all relocs with offsets less than toaddr. prop
- records handling adjusts toaddr downwards to avoid moving syms at the
- address of the property record, but all relocs with offsets between addr
- and the current value of toaddr need to have their offsets adjusted.
- Assume addr = 0, toaddr = 4 and count = 2. After prop records handling,
- toaddr becomes 2, but relocs with offsets 2 and 3 still need to be
- adjusted (to 0 and 1 respectively), as the first 2 bytes are now gone.
- So record the current value of toaddr here, and use it when adjusting
- reloc offsets. */
- reloc_toaddr = toaddr;
-
irel = elf_section_data (sec)->relocs;
irelend = irel + sec->reloc_count;
@@ -1962,10 +1986,7 @@ 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);
-
- /* Adjust the TOADDR to avoid moving symbols located at the address
- of the property record, which has not moved. */
- toaddr -= count;
+ did_pad = TRUE;
}
if (!did_shrink)
@@ -1981,7 +2002,7 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
/* Get the new reloc address. */
if ((irel->r_offset > addr
- && irel->r_offset < reloc_toaddr))
+ && irel->r_offset < toaddr))
{
if (debug_relax)
printf ("Relocation at address 0x%x needs to be moved.\n"
@@ -2059,7 +2080,7 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
{
/* If there is an alignment boundary, we only need to
adjust addends that end up below the boundary. */
- bfd_vma shrink_boundary = (reloc_toaddr
+ bfd_vma shrink_boundary = (toaddr
+ sec->output_section->vma
+ sec->output_offset);
@@ -2102,12 +2123,10 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
{
if (isym->st_shndx == sec_shndx)
{
- if (isym->st_value > addr
- && isym->st_value <= toaddr)
- isym->st_value -= count;
-
- if (isym->st_value <= addr
- && isym->st_value + isym->st_size > addr)
+ symvalue symval = isym->st_value;
+ symvalue symend = symval + isym->st_size;
+ if (avr_should_reduce_sym_size (symval, symend,
+ addr, toaddr, did_pad))
{
/* If this assert fires then we have a symbol that ends
part way through an instruction. Does that make
@@ -2115,6 +2134,12 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
BFD_ASSERT (isym->st_value + isym->st_size >= addr + count);
isym->st_size -= count;
}
+ else if (avr_should_increase_sym_size (symval, symend,
+ addr, toaddr, did_pad))
+ isym->st_size += count;
+
+ if (avr_should_move_sym (symval, addr, toaddr, did_pad))
+ isym->st_value -= count;
}
}
}
@@ -2131,20 +2156,24 @@ elf32_avr_relax_delete_bytes (bfd *abfd,
|| sym_hash->root.type == bfd_link_hash_defweak)
&& sym_hash->root.u.def.section == sec)
{
- if (sym_hash->root.u.def.value > addr
- && sym_hash->root.u.def.value <= toaddr)
- sym_hash->root.u.def.value -= count;
+ symvalue symval = sym_hash->root.u.def.value;
+ symvalue symend = symval + sym_hash->size;
- if (sym_hash->root.u.def.value <= addr
- && (sym_hash->root.u.def.value + sym_hash->size > addr))
+ if (avr_should_reduce_sym_size (symval, symend,
+ addr, toaddr, did_pad))
{
/* If this assert fires then we have a symbol that ends
part way through an instruction. Does that make
sense? */
- BFD_ASSERT (sym_hash->root.u.def.value + sym_hash->size
- >= addr + count);
+ BFD_ASSERT (symend >= addr + count);
sym_hash->size -= count;
}
+ else if (avr_should_increase_sym_size (symval, symend,
+ addr, toaddr, did_pad))
+ sym_hash->size += count;
+
+ if (avr_should_move_sym (symval, addr, toaddr, did_pad))
+ sym_hash->root.u.def.value -= count;
}
}