diff options
Diffstat (limited to 'bfd/elf32-avr.c')
-rw-r--r-- | bfd/elf32-avr.c | 689 |
1 files changed, 318 insertions, 371 deletions
diff --git a/bfd/elf32-avr.c b/bfd/elf32-avr.c index 32268cc..ec42bb3 100644 --- a/bfd/elf32-avr.c +++ b/bfd/elf32-avr.c @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street - Fifth Floor, + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "bfd.h" @@ -26,37 +26,6 @@ #include "elf-bfd.h" #include "elf/avr.h" -static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup - PARAMS ((bfd *abfd, bfd_reloc_code_real_type code)); -static void avr_info_to_howto_rela - PARAMS ((bfd *, arelent *, Elf_Internal_Rela *)); -static asection *elf32_avr_gc_mark_hook - PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *, - struct elf_link_hash_entry *, Elf_Internal_Sym *)); -static bfd_boolean elf32_avr_gc_sweep_hook - PARAMS ((bfd *, struct bfd_link_info *, asection *, - const Elf_Internal_Rela *)); -static bfd_boolean elf32_avr_check_relocs - PARAMS ((bfd *, struct bfd_link_info *, asection *, - const Elf_Internal_Rela *)); -static bfd_reloc_status_type avr_final_link_relocate - PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_byte *, - Elf_Internal_Rela *, bfd_vma)); -static bfd_boolean elf32_avr_relocate_section - PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, - Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); -static void bfd_elf_avr_final_write_processing PARAMS ((bfd *, bfd_boolean)); -static bfd_boolean elf32_avr_object_p PARAMS ((bfd *)); - -/* Relaxing stuff */ -static bfd_boolean elf32_avr_relax_section - PARAMS((bfd *, asection *, struct bfd_link_info *, bfd_boolean *)); -static bfd_boolean elf32_avr_relax_delete_bytes - PARAMS((bfd *, asection *, bfd_vma, int)); -static bfd_byte *elf32_avr_get_relocated_section_contents - PARAMS((bfd *, struct bfd_link_info *, struct bfd_link_order *, - bfd_byte *, bfd_boolean, asymbol **)); - static reloc_howto_type elf_avr_howto_table[] = { HOWTO (R_AVR_NONE, /* type */ @@ -177,7 +146,7 @@ static reloc_howto_type elf_avr_howto_table[] = 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* A high 6 bit absolute relocation of 22 bit address. - For LDI command. As well second most significant 8 bit value of + For LDI command. As well second most significant 8 bit value of a 32 bit link-time constant. */ HOWTO (R_AVR_HH8_LDI, /* type */ 16, /* rightshift */ @@ -453,25 +422,24 @@ struct avr_reloc_map }; /* Meant to be filled one day with the wrap around address for the - specific device. I.e. should get the value 0x4000 for 16k devices, + specific device. I.e. should get the value 0x4000 for 16k devices, 0x8000 for 32k devices and so on. - + We initialize it here with a value of 0x1000000 resulting in - that we will never suggest a wrap-around jump during relaxation. - The logic of the source code later on assumes that in + that we will never suggest a wrap-around jump during relaxation. + The logic of the source code later on assumes that in avr_pc_wrap_around one single bit is set. */ - + unsigned int avr_pc_wrap_around = 0x10000000; /* Calculates the effective distance of a pc relative jump/call. */ static int avr_relative_distance_considering_wrap_around (unsigned int distance) -{ +{ unsigned int wrap_around_mask = avr_pc_wrap_around - 1; - int dist_with_wrap_around = distance & wrap_around_mask; - if (dist_with_wrap_around > ((int) (avr_pc_wrap_around >> 1)) ) + if (dist_with_wrap_around > ((int) (avr_pc_wrap_around >> 1))) dist_with_wrap_around -= avr_pc_wrap_around; return dist_with_wrap_around; @@ -479,9 +447,8 @@ avr_relative_distance_considering_wrap_around (unsigned int distance) static reloc_howto_type * -bfd_elf32_bfd_reloc_type_lookup (abfd, code) - bfd *abfd ATTRIBUTE_UNUSED; - bfd_reloc_code_real_type code; +bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, + bfd_reloc_code_real_type code) { unsigned int i; @@ -499,10 +466,9 @@ bfd_elf32_bfd_reloc_type_lookup (abfd, code) /* Set the howto pointer for an AVR ELF reloc. */ static void -avr_info_to_howto_rela (abfd, cache_ptr, dst) - bfd *abfd ATTRIBUTE_UNUSED; - arelent *cache_ptr; - Elf_Internal_Rela *dst; +avr_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED, + arelent *cache_ptr, + Elf_Internal_Rela *dst) { unsigned int r_type; @@ -512,12 +478,11 @@ avr_info_to_howto_rela (abfd, cache_ptr, dst) } static asection * -elf32_avr_gc_mark_hook (sec, info, rel, h, sym) - asection *sec; - struct bfd_link_info *info ATTRIBUTE_UNUSED; - Elf_Internal_Rela *rel; - struct elf_link_hash_entry *h; - Elf_Internal_Sym *sym; +elf32_avr_gc_mark_hook (asection *sec, + struct bfd_link_info *info ATTRIBUTE_UNUSED, + Elf_Internal_Rela *rel, + struct elf_link_hash_entry *h, + Elf_Internal_Sym *sym) { if (h != NULL) { @@ -545,11 +510,10 @@ elf32_avr_gc_mark_hook (sec, info, rel, h, sym) } static bfd_boolean -elf32_avr_gc_sweep_hook (abfd, info, sec, relocs) - bfd *abfd ATTRIBUTE_UNUSED; - struct bfd_link_info *info ATTRIBUTE_UNUSED; - asection *sec ATTRIBUTE_UNUSED; - const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED; +elf32_avr_gc_sweep_hook (bfd *abfd ATTRIBUTE_UNUSED, + struct bfd_link_info *info ATTRIBUTE_UNUSED, + asection *sec ATTRIBUTE_UNUSED, + const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED) { /* We don't use got and plt entries for avr. */ return TRUE; @@ -560,11 +524,10 @@ elf32_avr_gc_sweep_hook (abfd, info, sec, relocs) virtual table relocs for gc. */ static bfd_boolean -elf32_avr_check_relocs (abfd, info, sec, relocs) - bfd *abfd; - struct bfd_link_info *info; - asection *sec; - const Elf_Internal_Rela *relocs; +elf32_avr_check_relocs (bfd *abfd, + struct bfd_link_info *info, + asection *sec, + const Elf_Internal_Rela *relocs) { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes, **sym_hashes_end; @@ -576,7 +539,7 @@ elf32_avr_check_relocs (abfd, info, sec, relocs) symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); - sym_hashes_end = sym_hashes + symtab_hdr->sh_size/sizeof (Elf32_External_Sym); + sym_hashes_end = sym_hashes + symtab_hdr->sh_size / sizeof (Elf32_External_Sym); if (!elf_bad_symtab (abfd)) sym_hashes_end -= symtab_hdr->sh_info; @@ -605,14 +568,12 @@ elf32_avr_check_relocs (abfd, info, sec, relocs) routines, but a few relocs, we have to do them ourselves. */ static bfd_reloc_status_type -avr_final_link_relocate (howto, input_bfd, input_section, - contents, rel, relocation) - reloc_howto_type * howto; - bfd * input_bfd; - asection * input_section; - bfd_byte * contents; - Elf_Internal_Rela * rel; - bfd_vma relocation; +avr_final_link_relocate (reloc_howto_type * howto, + bfd * input_bfd, + asection * input_section, + bfd_byte * contents, + Elf_Internal_Rela * rel, + bfd_vma relocation) { bfd_reloc_status_type r = bfd_reloc_ok; bfd_vma x; @@ -688,8 +649,8 @@ avr_final_link_relocate (howto, input_bfd, input_section, case R_AVR_LDI: contents += rel->r_offset; srel = (bfd_signed_vma) relocation + rel->r_addend; - if ( ((srel > 0) && (srel & 0xffff) > 255) - || ((srel < 0) && ( (-srel) & 0xffff) > 128)) + if (((srel > 0) && (srel & 0xffff) > 255) + || ((srel < 0) && ((-srel) & 0xffff) > 128)) /* Remove offset for data/eeprom section. */ return bfd_reloc_overflow; @@ -705,7 +666,7 @@ avr_final_link_relocate (howto, input_bfd, input_section, /* Remove offset for data/eeprom section. */ return bfd_reloc_overflow; x = bfd_get_16 (input_bfd, contents); - x = (x & 0xd3f8) | ((srel & 7) | ((srel & (3 << 3)) << 7) + x = (x & 0xd3f8) | ((srel & 7) | ((srel & (3 << 3)) << 7) | ((srel & (1 << 5)) << 8)); bfd_put_16 (input_bfd, x, contents); break; @@ -717,7 +678,7 @@ avr_final_link_relocate (howto, input_bfd, input_section, /* Remove offset for data/eeprom section. */ return bfd_reloc_overflow; x = bfd_get_16 (input_bfd, contents); - x = (x & 0xff30) | (srel & 0xf) | ((srel & 0x30) << 2); + x = (x & 0xff30) | (srel & 0xf) | ((srel & 0x30) << 2); bfd_put_16 (input_bfd, x, contents); break; @@ -882,17 +843,16 @@ avr_final_link_relocate (howto, input_bfd, input_section, } /* Relocate an AVR ELF section. */ + static bfd_boolean -elf32_avr_relocate_section (output_bfd, info, input_bfd, input_section, - contents, relocs, local_syms, local_sections) - bfd *output_bfd ATTRIBUTE_UNUSED; - struct bfd_link_info *info; - bfd *input_bfd; - asection *input_section; - bfd_byte *contents; - Elf_Internal_Rela *relocs; - Elf_Internal_Sym *local_syms; - asection **local_sections; +elf32_avr_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED, + struct bfd_link_info *info, + bfd *input_bfd, + asection *input_section, + bfd_byte *contents, + Elf_Internal_Rela *relocs, + Elf_Internal_Sym *local_syms, + asection **local_sections) { Elf_Internal_Shdr * symtab_hdr; struct elf_link_hash_entry ** sym_hashes; @@ -1003,9 +963,8 @@ elf32_avr_relocate_section (output_bfd, info, input_bfd, input_section, number. */ static void -bfd_elf_avr_final_write_processing (abfd, linker) - bfd *abfd; - bfd_boolean linker ATTRIBUTE_UNUSED; +bfd_elf_avr_final_write_processing (bfd *abfd, + bfd_boolean linker ATTRIBUTE_UNUSED) { unsigned long val; @@ -1042,14 +1001,15 @@ bfd_elf_avr_final_write_processing (abfd, linker) /* Set the right machine number. */ static bfd_boolean -elf32_avr_object_p (abfd) - bfd *abfd; +elf32_avr_object_p (bfd *abfd) { unsigned int e_set = bfd_mach_avr2; + if (elf_elfheader (abfd)->e_machine == EM_AVR || elf_elfheader (abfd)->e_machine == EM_AVR_OLD) { int e_mach = elf_elfheader (abfd)->e_flags & EF_AVR_MACH; + switch (e_mach) { default: @@ -1082,12 +1042,177 @@ elf32_avr_object_p (abfd) /* Enable debugging printout at stdout with a value of 1. */ #define DEBUG_RELAX 0 +/* 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. */ + +static bfd_boolean +elf32_avr_relax_delete_bytes (bfd *abfd, + asection *sec, + bfd_vma addr, + int count) +{ + Elf_Internal_Shdr *symtab_hdr; + unsigned int sec_shndx; + bfd_byte *contents; + Elf_Internal_Rela *irel, *irelend; + Elf_Internal_Rela *irelalign; + Elf_Internal_Sym *isym; + Elf_Internal_Sym *isymbuf = NULL; + Elf_Internal_Sym *isymend; + bfd_vma toaddr; + struct elf_link_hash_entry **sym_hashes; + struct elf_link_hash_entry **end_hashes; + unsigned int symcount; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); + contents = elf_section_data (sec)->this_hdr.contents; + + /* The deletion must stop at the next ALIGN reloc for an aligment + power larger than the number of bytes we are deleting. */ + + irelalign = NULL; + toaddr = sec->size; + + irel = elf_section_data (sec)->relocs; + irelend = irel + sec->reloc_count; + + /* Actually delete the bytes. */ + if (toaddr - addr - count > 0) + memmove (contents + addr, contents + addr + count, + (size_t) (toaddr - addr - count)); + sec->size -= count; + + /* Adjust all the relocs. */ + for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++) + { + bfd_vma symval; + bfd_vma old_reloc_address; + bfd_vma shrinked_insn_address; + + old_reloc_address = (sec->output_section->vma + + sec->output_offset + irel->r_offset); + shrinked_insn_address = (sec->output_section->vma + + sec->output_offset + addr - count); + + /* Get the new reloc address. */ + if ((irel->r_offset > addr + && irel->r_offset < toaddr)) + { + if (DEBUG_RELAX) + printf ("Relocation at address 0x%x needs to be moved.\n" + "Old section offset: 0x%x, New section offset: 0x%x \n", + (unsigned int) old_reloc_address, + (unsigned int) irel->r_offset, + (unsigned int) ((irel->r_offset) - count)); + + irel->r_offset -= count; + } + + /* The reloc's own addresses are now ok. However, we need to readjust + the reloc's addend if two conditions are met: + 1.) the reloc is relative to a symbol in this section that + is located in front of the shrinked instruction + 2.) symbol plus addend end up behind the shrinked instruction. + + This should happen only for local symbols that are progmem related. */ + + /* Read this BFD's local symbols if we haven't done so already. */ + if (isymbuf == NULL && symtab_hdr->sh_info != 0) + { + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == NULL) + return FALSE; + } + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + /* A local symbol. */ + Elf_Internal_Sym *isym; + asection *sym_sec; + + isym = isymbuf + ELF32_R_SYM (irel->r_info); + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + symval = isym->st_value; + /* If the reloc is absolute, it will not have + a symbol or section associated with it. */ + if (sym_sec) + { + symval += sym_sec->output_section->vma + + sym_sec->output_offset; + + if (DEBUG_RELAX) + printf ("Checking if the relocation's " + "addend needs corrections.\n" + "Address of anchor symbol: 0x%x \n" + "Address of relocation target: 0x%x \n" + "Address of relaxed insn: 0x%x \n", + (unsigned int) symval, + (unsigned int) (symval + irel->r_addend), + (unsigned int) shrinked_insn_address); + + if (symval <= shrinked_insn_address + && (symval + irel->r_addend) > shrinked_insn_address) + { + irel->r_addend -= count; + + if (DEBUG_RELAX) + printf ("Anchor symbol and relocation target bracket " + "shrinked insn address.\n" + "Need for new addend : 0x%x\n", + (unsigned int) irel->r_addend); + } + } + /* else ... Reference symbol is absolute. No adjustment needed. */ + } + /* else ... Reference symbol is extern. No need for adjusting the addend. */ + } + + /* Adjust the local symbols defined in this section. */ + isym = (Elf_Internal_Sym *) symtab_hdr->contents; + isymend = isym + symtab_hdr->sh_info; + for (; isym < isymend; isym++) + { + if (isym->st_shndx == sec_shndx + && isym->st_value > addr + && isym->st_value < toaddr) + isym->st_value -= count; + } + + /* Now adjust the global symbols defined in this section. */ + symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) + - symtab_hdr->sh_info); + sym_hashes = elf_sym_hashes (abfd); + end_hashes = sym_hashes + symcount; + for (; sym_hashes < end_hashes; sym_hashes++) + { + struct elf_link_hash_entry *sym_hash = *sym_hashes; + if ((sym_hash->root.type == bfd_link_hash_defined + || sym_hash->root.type == bfd_link_hash_defweak) + && sym_hash->root.u.def.section == sec + && sym_hash->root.u.def.value > addr + && sym_hash->root.u.def.value < toaddr) + { + sym_hash->root.u.def.value -= count; + } + } + + return TRUE; +} + /* This function handles relaxing for the avr. Many important relaxing opportunities within functions are already realized by the compiler itself. Here we try to replace call (4 bytes) -> rcall (2 bytes) - and jump -> rjmp (safes also 2 bytes). - As well we now optimize seqences of + and jump -> rjmp (safes also 2 bytes). + As well we now optimize seqences of - call/rcall function - ret to yield @@ -1101,20 +1226,21 @@ elf32_avr_object_p (abfd) is not the target of a branch or jump within the same section, it is checked that there is no skip instruction before the jmp/rjmp and that there is no local or global label place at the address of the ret. - + We refrain from relaxing within sections ".vectors" and - ".jumptables" in order to maintain the position of the instructions. + ".jumptables" in order to maintain the position of the instructions. There, however, we substitute jmp/call by a sequence rjmp,nop/rcall,nop - if possible. (In future one could possibly use the space of the nop + if possible. (In future one could possibly use the space of the nop for the first instruction of the irq service function. The .jumptables sections is meant to be used for a future tablejump variant for the devices with 3-byte program counter where the table itself - contains 4-byte jump instructions whose relative offset must not + contains 4-byte jump instructions whose relative offset must not be changed. */ - + static bfd_boolean -elf32_avr_relax_section (bfd *abfd, asection *sec, +elf32_avr_relax_section (bfd *abfd, + asection *sec, struct bfd_link_info *link_info, bfd_boolean *again) { @@ -1137,20 +1263,17 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, || sec->reloc_count == 0 || (sec->flags & SEC_CODE) == 0) return TRUE; - + /* Check if the object file to relax uses internal symbols so that we could fix up the relocations. */ - if (!(elf_elfheader (abfd)->e_flags & EF_AVR_LINKRELAX_PREPARED)) return TRUE; - symtab_hdr = &elf_tdata (abfd)->symtab_hdr; /* Get a copy of the native relocations. */ internal_relocs = (_bfd_elf_link_read_relocs - (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL, - link_info->keep_memory)); + (abfd, sec, NULL, NULL, link_info->keep_memory)); if (internal_relocs == NULL) goto error_return; @@ -1165,11 +1288,11 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, { bfd_vma symval; - if (ELF32_R_TYPE (irel->r_info) != R_AVR_13_PCREL + if ( ELF32_R_TYPE (irel->r_info) != R_AVR_13_PCREL && ELF32_R_TYPE (irel->r_info) != R_AVR_7_PCREL && ELF32_R_TYPE (irel->r_info) != R_AVR_CALL) continue; - + /* Get the section contents if we haven't done so already. */ if (contents == NULL) { @@ -1179,7 +1302,7 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, else { /* Go get them off disk. */ - if (!bfd_malloc_and_get_section (abfd, sec, &contents)) + if (! bfd_malloc_and_get_section (abfd, sec, &contents)) goto error_return; } } @@ -1224,12 +1347,11 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, BFD_ASSERT (h != NULL); if (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak) - { - /* This appears to be a reference to an undefined - symbol. Just ignore it--it will be caught by the - regular reloc processing. */ - continue; - } + /* This appears to be a reference to an undefined + symbol. Just ignore it--it will be caught by the + regular reloc processing. */ + continue; + symval = (h->root.u.def.value + h->root.u.def.section->output_section->vma + h->root.u.def.section->output_offset); @@ -1261,12 +1383,12 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, /* If the distance is within -4094..+4098 inclusive, then we can relax this jump/call. +4098 because the call/jump target - will be closer after the relaxation. */ + will be closer after the relaxation. */ if ((int) gap >= -4094 && (int) gap <= 4098) distance_short_enough = 1; /* Here we handle the wrap-around case. E.g. for a 16k device - we could use a rjmp to jump from address 0x100 to 0x3d00! + we could use a rjmp to jump from address 0x100 to 0x3d00! In order to make this work properly, we need to fill the vaiable avr_pc_wrap_around with the appropriate value. I.e. 0x4000 for a 16k device. */ @@ -1277,23 +1399,22 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, again too large for the short jumps. Let's assume a typical code-size reduction due to relax for a 16k device of 600 bytes. So let's use twice the - typical value as safety margin. */ - + typical value as safety margin. */ int rgap; int safety_margin; int assumed_shrink = 600; if (avr_pc_wrap_around > 0x4000) assumed_shrink = 900; - + safety_margin = 2 * assumed_shrink; rgap = avr_relative_distance_considering_wrap_around (gap); - - if (rgap >= (-4092 + safety_margin) + + if (rgap >= (-4092 + safety_margin) && rgap <= (4094 - safety_margin)) - distance_short_enough = 1; - } + distance_short_enough = 1; + } if (distance_short_enough) { @@ -1330,7 +1451,7 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, bfd_put_8 (abfd, 0x00, contents + irel->r_offset); bfd_put_8 (abfd, 0xC0, contents + irel->r_offset + 1); } - else + else abort (); /* Fix the relocation's type. */ @@ -1360,6 +1481,7 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, } } } + default: { unsigned char code_msb; @@ -1372,8 +1494,8 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, /* Get the address of this instruction. */ dot = (sec->output_section->vma + sec->output_offset + irel->r_offset); - - /* Here we look for rcall/ret or call/ret sequences that could be + + /* Here we look for rcall/ret or call/ret sequences that could be safely replaced by rjmp/ret or jmp/ret */ if (0xd0 == (code_msb & 0xf0)) { @@ -1383,16 +1505,16 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, if (irel->r_offset + 3 < sec->size) { - next_insn_msb = + next_insn_msb = bfd_get_8 (abfd, contents + irel->r_offset + 3); - next_insn_lsb = + next_insn_lsb = bfd_get_8 (abfd, contents + irel->r_offset + 2); } - if ((0x95 == next_insn_msb) && (0x08 == next_insn_lsb)) + + if ((0x95 == next_insn_msb) && (0x08 == next_insn_lsb)) { /* The next insn is a ret. We now convert the rcall insn into a rjmp instruction. */ - code_msb &= 0xef; bfd_put_8 (abfd, code_msb, contents + irel->r_offset + 1); if (DEBUG_RELAX) @@ -1417,6 +1539,7 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, next_insn_lsb = bfd_get_8 (abfd, contents + irel->r_offset + 4); } + if ((0x95 == next_insn_msb) && (0x08 == next_insn_lsb)) { /* The next insn is a ret. We now convert the call insn @@ -1432,11 +1555,11 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, break; } } - else if ((0xc0 == (code_msb & 0xf0)) - || ((0x94 == (code_msb & 0xfe)) + else if ((0xc0 == (code_msb & 0xf0)) + || ((0x94 == (code_msb & 0xfe)) && (0x0c == (code_lsb & 0x0e)))) { - /* this insn is a rjmp or a jmp. */ + /* This insn is a rjmp or a jmp. */ unsigned char next_insn_msb = 0; unsigned char next_insn_lsb = 0; int insn_size; @@ -1448,11 +1571,11 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, if (irel->r_offset + insn_size + 1 < sec->size) { - next_insn_msb = - bfd_get_8 (abfd, contents + irel->r_offset + next_insn_msb = + bfd_get_8 (abfd, contents + irel->r_offset + insn_size + 1); - next_insn_lsb = - bfd_get_8 (abfd, contents + irel->r_offset + next_insn_lsb = + bfd_get_8 (abfd, contents + irel->r_offset + insn_size); } @@ -1468,12 +1591,10 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, address_of_ret = dot + insn_size; if (DEBUG_RELAX && (insn_size == 2)) - printf ("found rjmp / ret sequence at " - "address 0x%x\n", + printf ("found rjmp / ret sequence at address 0x%x\n", (int) dot); if (DEBUG_RELAX && (insn_size == 4)) - printf ("found jmp / ret sequence at " - "address 0x%x\n", + printf ("found jmp / ret sequence at address 0x%x\n", (int) dot); /* We have to make sure that there is a preceeding insn. */ @@ -1481,17 +1602,17 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, { unsigned char preceeding_msb; unsigned char preceeding_lsb; - preceeding_msb = + preceeding_msb = bfd_get_8 (abfd, contents + irel->r_offset - 1); - preceeding_lsb = + preceeding_lsb = bfd_get_8 (abfd, contents + irel->r_offset - 2); /* sbic. */ - if (0x99 == preceeding_msb) + if (0x99 == preceeding_msb) there_is_preceeding_non_skip_insn = 0; /* sbis. */ - if (0x9b == preceeding_msb) + if (0x9b == preceeding_msb) there_is_preceeding_non_skip_insn = 0; /* sbrc */ @@ -1499,15 +1620,15 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, && (0x00 == (preceeding_lsb & 0x08)))) there_is_preceeding_non_skip_insn = 0; - /* sbrs */ + /* sbrs */ if ((0xfe == (preceeding_msb & 0xfe) && (0x00 == (preceeding_lsb & 0x08)))) there_is_preceeding_non_skip_insn = 0; - + /* cpse */ if (0x10 == (preceeding_msb & 0xfc)) there_is_preceeding_non_skip_insn = 0; - + if (there_is_preceeding_non_skip_insn == 0) if (DEBUG_RELAX) printf ("preceeding skip insn prevents deletion of" @@ -1518,7 +1639,7 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, { /* There is no previous instruction. */ there_is_preceeding_non_skip_insn = 0; - } + } if (there_is_preceeding_non_skip_insn) { @@ -1528,13 +1649,13 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, in this section pointing to the ret. */ int deleting_ret_is_safe = 1; - unsigned int section_offset_of_ret_insn = + unsigned int section_offset_of_ret_insn = irel->r_offset + insn_size; Elf_Internal_Sym *isym, *isymend; unsigned int sec_shndx; - - sec_shndx = - _bfd_elf_section_from_bfd_section (abfd, sec); + + sec_shndx = + _bfd_elf_section_from_bfd_section (abfd, sec); /* Check for local symbols. */ isym = (Elf_Internal_Sym *) symtab_hdr->contents; @@ -1558,21 +1679,20 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, struct elf_link_hash_entry **sym_hashes; struct elf_link_hash_entry **end_hashes; - symcount = (symtab_hdr->sh_size + symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) - symtab_hdr->sh_info); sym_hashes = elf_sym_hashes (abfd); end_hashes = sym_hashes + symcount; for (; sym_hashes < end_hashes; sym_hashes++) { - struct elf_link_hash_entry *sym_hash = + struct elf_link_hash_entry *sym_hash = *sym_hashes; if ((sym_hash->root.type == bfd_link_hash_defined - || sym_hash->root.type == - bfd_link_hash_defweak) + || sym_hash->root.type == + bfd_link_hash_defweak) && sym_hash->root.u.def.section == sec - && sym_hash->root.u.def.value == - section_offset_of_ret_insn) + && sym_hash->root.u.def.value == section_offset_of_ret_insn) { deleting_ret_is_safe = 0; if (DEBUG_RELAX) @@ -1588,55 +1708,55 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, Elf_Internal_Rela *relend; Elf_Internal_Shdr *symtab_hdr; - symtab_hdr = &elf_tdata (abfd)->symtab_hdr; - relend = elf_section_data (sec)->relocs + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + relend = elf_section_data (sec)->relocs + sec->reloc_count; - for (irel = elf_section_data (sec)->relocs; + for (irel = elf_section_data (sec)->relocs; irel < relend; irel++) { bfd_vma reloc_target = 0; bfd_vma symval; Elf_Internal_Sym *isymbuf = NULL; - - /* Read this BFD's local symbols if we haven't + + /* Read this BFD's local symbols if we haven't done so already. */ if (isymbuf == NULL && symtab_hdr->sh_info != 0) { - isymbuf = (Elf_Internal_Sym *) + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; if (isymbuf == NULL) - isymbuf = bfd_elf_get_elf_syms ( - abfd, - symtab_hdr, - symtab_hdr->sh_info, 0, - NULL, NULL, NULL); + isymbuf = bfd_elf_get_elf_syms + (abfd, + symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); if (isymbuf == NULL) break; } - - /* Get the value of the symbol referred to + + /* Get the value of the symbol referred to by the reloc. */ - if (ELF32_R_SYM (irel->r_info) + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) { /* A local symbol. */ Elf_Internal_Sym *isym; asection *sym_sec; - isym = isymbuf + isym = isymbuf + ELF32_R_SYM (irel->r_info); - sym_sec = bfd_section_from_elf_index ( - abfd, isym->st_shndx); - symval = isym->st_value; - - /* If the reloc is absolute, it will not + sym_sec = bfd_section_from_elf_index + (abfd, isym->st_shndx); + symval = isym->st_value; + + /* If the reloc is absolute, it will not have a symbol or section associated with it. */ - + if (sym_sec) - { - symval += + { + symval += sym_sec->output_section->vma + sym_sec->output_offset; reloc_target = symval + irel->r_addend; @@ -1644,16 +1764,13 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, else { reloc_target = symval + irel->r_addend; - /* reference symbol is absolute. */ + /* Reference symbol is absolute. */ } } - else - { - /* reference symbol is extern. */ - } - + /* else ... reference symbol is extern. */ + if (address_of_ret == reloc_target) - { + { deleting_ret_is_safe = 0; if (DEBUG_RELAX) printf ("ret from " @@ -1671,23 +1788,22 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, printf ("unreachable ret instruction " "at address 0x%x deleted.\n", (int) dot + insn_size); - + /* Delete two bytes of data. */ if (!elf32_avr_relax_delete_bytes (abfd, sec, irel->r_offset + insn_size, 2)) goto error_return; - /* That will change things, so, we should relax - again. Note that this is not required, and it + /* That will change things, so, we should relax + again. Note that this is not required, and it may be slow. */ - *again = TRUE; break; } } - - } - } + + } + } break; } } @@ -1722,183 +1838,15 @@ elf32_avr_relax_section (bfd *abfd, asection *sec, && elf_section_data (sec)->relocs != internal_relocs) free (internal_relocs); - return FALSE; -} - -/* 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. */ -static bfd_boolean -elf32_avr_relax_delete_bytes (bfd *abfd, asection *sec, - bfd_vma addr, int count) -{ - Elf_Internal_Shdr *symtab_hdr; - unsigned int sec_shndx; - bfd_byte *contents; - Elf_Internal_Rela *irel, *irelend; - Elf_Internal_Rela *irelalign; - Elf_Internal_Sym *isym; - Elf_Internal_Sym *isymbuf = NULL; - Elf_Internal_Sym *isymend; - bfd_vma toaddr; - struct elf_link_hash_entry **sym_hashes; - struct elf_link_hash_entry **end_hashes; - unsigned int symcount; - - symtab_hdr = &elf_tdata (abfd)->symtab_hdr; - sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); - contents = elf_section_data (sec)->this_hdr.contents; - - /* The deletion must stop at the next ALIGN reloc for an aligment - power larger than the number of bytes we are deleting. */ - - irelalign = NULL; - toaddr = sec->size; - - irel = elf_section_data (sec)->relocs; - irelend = irel + sec->reloc_count; - - /* Actually delete the bytes. */ - if (toaddr - addr - count > 0) - memmove (contents + addr, contents + addr + count, - (size_t) (toaddr - addr - count)); - sec->size -= count; - - /* Adjust all the relocs. */ - for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++) - { - bfd_vma symval; - bfd_vma old_reloc_address; - bfd_vma shrinked_insn_address; - - old_reloc_address = (sec->output_section->vma - + sec->output_offset + irel->r_offset); - shrinked_insn_address = (sec->output_section->vma - + sec->output_offset + addr - count); - - /* Get the new reloc address. */ - if ((irel->r_offset > addr - && irel->r_offset < toaddr)) - { - if (DEBUG_RELAX) - printf ("Relocation at address 0x%x needs to be moved.\n" - "Old section offset: 0x%x, New section offset: 0x%x \n", - (unsigned int) old_reloc_address, - (unsigned int) irel->r_offset, - (unsigned int) ((irel->r_offset) - count)); - - irel->r_offset -= count; - } - - /* The reloc's own addresses are now ok. However, we need to readjust - the reloc's addend if two conditions are met: - 1.) the reloc is relative to a symbol in this section that - is located in front of the shrinked instruction - 2.) symbol plus addend end up behind the shrinked instruction. - - This should happen only for local symbols that are progmem related. */ - - /* Read this BFD's local symbols if we haven't done so already. */ - if (isymbuf == NULL && symtab_hdr->sh_info != 0) - { - isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; - if (isymbuf == NULL) - isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, - symtab_hdr->sh_info, 0, - NULL, NULL, NULL); - if (isymbuf == NULL) - return FALSE; - } - - /* Get the value of the symbol referred to by the reloc. */ - if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) - { - /* A local symbol. */ - Elf_Internal_Sym *isym; - asection *sym_sec; - - isym = isymbuf + ELF32_R_SYM (irel->r_info); - sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); - symval = isym->st_value; - /* If the reloc is absolute, it will not have - a symbol or section associated with it. */ - if (sym_sec) - { - symval += sym_sec->output_section->vma - + sym_sec->output_offset; - - if (DEBUG_RELAX) - printf ("Checking if the relocation's " - "addend needs corrections.\n" - "Address of anchor symbol: 0x%x \n" - "Address of relocation target: 0x%x \n" - "Address of relaxed insn: 0x%x \n", - (unsigned int) symval, - (unsigned int) (symval + irel->r_addend), - (unsigned int) shrinked_insn_address); - - if ( symval <= shrinked_insn_address - && (symval + irel->r_addend) > shrinked_insn_address) - { - irel->r_addend -= count; - - if (DEBUG_RELAX) - printf ("Anchor symbol and relocation target bracket " - "shrinked insn address.\n" - "Need for new addend : 0x%x\n", - (unsigned int) irel->r_addend); - } - } - else - { - /* Reference symbol is absolute. No adjustment needed. */ - } - } - else - { - /* Reference symbol is extern. No need for adjusting the addend. */ - } - } - - /* Adjust the local symbols defined in this section. */ - isym = (Elf_Internal_Sym *) symtab_hdr->contents; - isymend = isym + symtab_hdr->sh_info; - for (; isym < isymend; isym++) - { - if (isym->st_shndx == sec_shndx - && isym->st_value > addr - && isym->st_value < toaddr) - isym->st_value -= count; - } - - /* Now adjust the global symbols defined in this section. */ - symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) - - symtab_hdr->sh_info); - sym_hashes = elf_sym_hashes (abfd); - end_hashes = sym_hashes + symcount; - for (; sym_hashes < end_hashes; sym_hashes++) - { - struct elf_link_hash_entry *sym_hash = *sym_hashes; - if ((sym_hash->root.type == bfd_link_hash_defined - || sym_hash->root.type == bfd_link_hash_defweak) - && sym_hash->root.u.def.section == sec - && sym_hash->root.u.def.value > addr - && sym_hash->root.u.def.value < toaddr) - { - sym_hash->root.u.def.value -= count; - } - } - - return TRUE; + return FALSE; } /* This is a version of bfd_generic_get_relocated_section_contents - which uses elf32_avr_relocate_section. + which uses elf32_avr_relocate_section. - For avr it's essentially a cut and paste taken from the H8300 port. + For avr it's essentially a cut and paste taken from the H8300 port. The author of the relaxation support patch for avr had absolutely no - clue what is happening here but found out that this part of the code + clue what is happening here but found out that this part of the code seems to be important. */ static bfd_byte * @@ -1937,8 +1885,7 @@ elf32_avr_get_relocated_section_contents (bfd *output_bfd, bfd_size_type amt; internal_relocs = (_bfd_elf_link_read_relocs - (input_bfd, input_section, (PTR) NULL, - (Elf_Internal_Rela *) NULL, FALSE)); + (input_bfd, input_section, NULL, NULL, FALSE)); if (internal_relocs == NULL) goto error_return; @@ -1955,7 +1902,7 @@ elf32_avr_get_relocated_section_contents (bfd *output_bfd, amt = symtab_hdr->sh_info; amt *= sizeof (asection *); - sections = (asection **) bfd_malloc (amt); + sections = bfd_malloc (amt); if (sections == NULL && amt != 0) goto error_return; |