/* 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
- byte just behind the original unshrinked instruction. */
+ byte just behind the original unshrinked instruction. If delete_shrinks_insn
+ is FALSE, we are deleting redundant padding bytes from relax_info prop
+ record handling. In that case, addr is section-relative offset of start
+ of padding, and count is the number of padding bytes to delete. */
static bfd_boolean
elf32_avr_relax_delete_bytes (bfd *abfd,
asection *sec,
bfd_vma addr,
- int count)
+ int count,
+ bfd_boolean delete_shrinks_insn)
{
Elf_Internal_Shdr *symtab_hdr;
unsigned int sec_shndx;
Elf_Internal_Rela *irel, *irelend;
Elf_Internal_Sym *isym;
Elf_Internal_Sym *isymbuf = NULL;
- bfd_vma toaddr;
+ bfd_vma toaddr, reloc_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);
}
}
+ /* 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;
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. */
/* Get the new reloc address. */
if ((irel->r_offset > addr
- && irel->r_offset < toaddr))
+ && irel->r_offset < reloc_toaddr))
{
if (debug_relax)
printf ("Relocation at address 0x%x needs to be moved.\n"
continue;
shrinked_insn_address = (sec->output_section->vma
- + sec->output_offset + addr - count);
+ + sec->output_offset + addr);
+ if (delete_shrinks_insn)
+ shrinked_insn_address -= count;
irel = elf_section_data (isec)->relocs;
/* PR 12161: Read in the relocs for this section if necessary. */
a symbol or section associated with it. */
if (sym_sec == sec)
{
+ /* If there is an alignment boundary, we only need to
+ adjust addends that end up below the boundary. */
+ 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;
(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)
+ && (symval + irel->r_addend) > shrinked_insn_address
+ && addend_within_shrink_boundary)
{
if (elf32_avr_is_diff_reloc (irel))
{
{
/* Delete two bytes of data. */
if (!elf32_avr_relax_delete_bytes (abfd, sec,
- irel->r_offset + 2, 2))
+ irel->r_offset + 2, 2,
+ TRUE))
goto error_return;
/* That will change things, so, we should relax again.
}
}
}
+ /* Fall through. */
default:
{
/* Delete two bytes of data. */
if (!elf32_avr_relax_delete_bytes (abfd, sec,
- irel->r_offset + insn_size, 2))
+ irel->r_offset + insn_size, 2,
+ TRUE))
goto error_return;
/* That will change things, so, we should relax
record->offset -= count;
elf32_avr_relax_delete_bytes (abfd, sec,
addr - count,
- count);
+ count, FALSE);
*again = TRUE;
}
}
if (hsh == NULL)
{
- (*_bfd_error_handler) (_("%B: cannot create stub entry %s"),
- NULL, stub_name);
+ _bfd_error_handler (_("%B: cannot create stub entry %s"),
+ NULL, stub_name);
return NULL;
}