diff options
Diffstat (limited to 'bfd/elf32-avr.c')
-rw-r--r-- | bfd/elf32-avr.c | 91 |
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; } } |