diff options
Diffstat (limited to 'bfd/elf32-ip2k.c')
-rw-r--r-- | bfd/elf32-ip2k.c | 1228 |
1 files changed, 575 insertions, 653 deletions
diff --git a/bfd/elf32-ip2k.c b/bfd/elf32-ip2k.c index 4645fa2..135ef13 100644 --- a/bfd/elf32-ip2k.c +++ b/bfd/elf32-ip2k.c @@ -16,7 +16,8 @@ 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, Boston, MA 02110-1301, USA. */ + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ #include "bfd.h" #include "sysdep.h" @@ -39,59 +40,12 @@ struct ip2k_opcode unsigned short opcode; unsigned short mask; }; - -/* Prototypes. */ -static reloc_howto_type *ip2k_reloc_type_lookup - PARAMS ((bfd *, bfd_reloc_code_real_type)); -static int ip2k_is_opcode - PARAMS ((bfd_byte *, const struct ip2k_opcode *)); -static bfd_vma symbol_value - PARAMS ((bfd *, Elf_Internal_Shdr *, Elf_Internal_Sym *, - Elf_Internal_Rela *)); -static void ip2k_get_mem - PARAMS ((bfd *, bfd_byte *, int, bfd_byte *)); -static bfd_vma ip2k_nominal_page_bits - PARAMS ((bfd *, asection *, bfd_vma, bfd_byte *)); -static bfd_boolean ip2k_test_page_insn - PARAMS ((bfd *, asection *, Elf_Internal_Rela *, struct misc *)); -static bfd_boolean ip2k_delete_page_insn - PARAMS ((bfd *, asection *, Elf_Internal_Rela *, bfd_boolean *, struct misc *)); -static int ip2k_is_switch_table_128 - PARAMS ((bfd *, asection *, bfd_vma, bfd_byte *)); -static bfd_boolean ip2k_relax_switch_table_128 - PARAMS ((bfd *, asection *, Elf_Internal_Rela *, bfd_boolean *, struct misc *)); -static int ip2k_is_switch_table_256 - PARAMS ((bfd *, asection *, bfd_vma, bfd_byte *)); -static bfd_boolean ip2k_relax_switch_table_256 - PARAMS ((bfd *, asection *, Elf_Internal_Rela *, bfd_boolean *, struct misc *)); -static bfd_boolean ip2k_elf_relax_section - PARAMS ((bfd *, asection *, struct bfd_link_info *, bfd_boolean *)); -static bfd_boolean ip2k_elf_relax_section_page - PARAMS ((bfd *, asection *, bfd_boolean *, struct misc *, unsigned long, unsigned long)); -static void adjust_all_relocations - PARAMS ((bfd *, asection *, bfd_vma, bfd_vma, int, int)); -static bfd_boolean ip2k_elf_relax_delete_bytes - PARAMS ((bfd *, asection *, bfd_vma, int)); -static void ip2k_info_to_howto_rela - PARAMS ((bfd *, arelent *, Elf_Internal_Rela *)); -static bfd_reloc_status_type ip2k_final_link_relocate - PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_byte *, - Elf_Internal_Rela *, bfd_vma)); -static bfd_boolean ip2k_elf_relocate_section - PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *, - Elf_Internal_Rela *, Elf_Internal_Sym *, asection **)); -static asection *ip2k_elf_gc_mark_hook - PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *, - struct elf_link_hash_entry *, Elf_Internal_Sym *)); -static bfd_boolean ip2k_elf_gc_sweep_hook - PARAMS ((bfd *, struct bfd_link_info *, asection *, - const Elf_Internal_Rela *)); static bfd_boolean ip2k_relaxed = FALSE; static const struct ip2k_opcode ip2k_page_opcode[] = { - {0x0010, 0xFFF8}, /* page */ + {0x0010, 0xFFF8}, /* Page. */ {0x0000, 0x0000}, }; @@ -100,7 +54,7 @@ static const struct ip2k_opcode ip2k_page_opcode[] = static const struct ip2k_opcode ip2k_jmp_opcode[] = { - {0xE000, 0xE000}, /* jmp */ + {0xE000, 0xE000}, /* Jmp. */ {0x0000, 0x0000}, }; @@ -109,7 +63,7 @@ static const struct ip2k_opcode ip2k_jmp_opcode[] = static const struct ip2k_opcode ip2k_snc_opcode[] = { - {0xA00B, 0xFFFF}, /* snc */ + {0xA00B, 0xFFFF}, /* Snc. */ {0x0000, 0x0000}, }; @@ -118,7 +72,7 @@ static const struct ip2k_opcode ip2k_snc_opcode[] = static const struct ip2k_opcode ip2k_inc_1sp_opcode[] = { - {0x2B81, 0xFFFF}, /* inc 1(SP) */ + {0x2B81, 0xFFFF}, /* Inc 1(SP). */ {0x0000, 0x0000}, }; @@ -127,7 +81,7 @@ static const struct ip2k_opcode ip2k_inc_1sp_opcode[] = static const struct ip2k_opcode ip2k_add_2sp_w_opcode[] = { - {0x1F82, 0xFFFF}, /* add 2(SP),w */ + {0x1F82, 0xFFFF}, /* Add 2(SP),w. */ {0x0000, 0x0000}, }; @@ -136,8 +90,8 @@ static const struct ip2k_opcode ip2k_add_2sp_w_opcode[] = static const struct ip2k_opcode ip2k_add_w_wreg_opcode[] = { - {0x1C0A, 0xFFFF}, /* add w,wreg */ - {0x1E0A, 0xFFFF}, /* add wreg,w */ + {0x1C0A, 0xFFFF}, /* Add w,wreg. */ + {0x1E0A, 0xFFFF}, /* Add wreg,w. */ {0x0000, 0x0000}, }; @@ -146,7 +100,7 @@ static const struct ip2k_opcode ip2k_add_w_wreg_opcode[] = static const struct ip2k_opcode ip2k_add_pcl_w_opcode[] = { - {0x1E09, 0xFFFF}, /* add pcl,w */ + {0x1E09, 0xFFFF}, /* Add pcl,w. */ {0x0000, 0x0000}, }; @@ -220,10 +174,10 @@ static reloc_howto_type ip2k_elf_howto_table [] = /* Map BFD reloc types to IP2K ELF reloc types. */ + static reloc_howto_type * -ip2k_reloc_type_lookup (abfd, code) - bfd * abfd ATTRIBUTE_UNUSED; - bfd_reloc_code_real_type code; +ip2k_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, + bfd_reloc_code_real_type code) { /* Note that the ip2k_elf_howto_table is indxed by the R_ constants. Thus, the order that the howto records appear in the @@ -270,20 +224,17 @@ ip2k_reloc_type_lookup (abfd, code) } static void -ip2k_get_mem (abfd, addr, length, ptr) - bfd *abfd ATTRIBUTE_UNUSED; - bfd_byte *addr; - int length; - bfd_byte *ptr; +ip2k_get_mem (bfd *abfd ATTRIBUTE_UNUSED, + bfd_byte *addr, + int length, + bfd_byte *ptr) { while (length --) * ptr ++ = bfd_get_8 (abfd, addr ++); } static bfd_boolean -ip2k_is_opcode (code, opcodes) - bfd_byte *code; - const struct ip2k_opcode *opcodes; +ip2k_is_opcode (bfd_byte *code, const struct ip2k_opcode *opcodes) { unsigned short insn = (code[0] << 8) | code[1]; @@ -306,11 +257,10 @@ ip2k_is_opcode (code, opcodes) /* Return the value of the symbol associated with the relocation IREL. */ static bfd_vma -symbol_value (abfd, symtab_hdr, isymbuf, irel) - bfd *abfd; - Elf_Internal_Shdr *symtab_hdr; - Elf_Internal_Sym *isymbuf; - Elf_Internal_Rela *irel; +symbol_value (bfd *abfd, + Elf_Internal_Shdr *symtab_hdr, + Elf_Internal_Sym *isymbuf, + Elf_Internal_Rela *irel) { if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) { @@ -346,15 +296,177 @@ symbol_value (abfd, symtab_hdr, isymbuf, irel) } } +/* Determine if the instruction sequence matches that for + the prologue of a switch dispatch table with fewer than + 128 entries. + + sc + page $nnn0 + jmp $nnn0 + add w,wreg + add pcl,w + addr=> + page $nnn1 + jmp $nnn1 + page $nnn2 + jmp $nnn2 + ... + page $nnnN + jmp $nnnN + + After relaxation. + sc + page $nnn0 + jmp $nnn0 + add pcl,w + addr=> + jmp $nnn1 + jmp $nnn2 + ... + jmp $nnnN */ + +static int +ip2k_is_switch_table_128 (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + bfd_vma addr, + bfd_byte *contents) +{ + bfd_byte code[4]; + int index = 0; + + /* Check current page-jmp. */ + if (addr + 4 > sec->size) + return -1; + + ip2k_get_mem (abfd, contents + addr, 4, code); + + if ((! IS_PAGE_OPCODE (code + 0)) + || (! IS_JMP_OPCODE (code + 2))) + return -1; + + /* Search back. */ + while (1) + { + if (addr < 4) + return -1; + + /* Check previous 2 instructions. */ + ip2k_get_mem (abfd, contents + addr - 4, 4, code); + if ((IS_ADD_W_WREG_OPCODE (code + 0)) + && (IS_ADD_PCL_W_OPCODE (code + 2))) + return index; + + if ((! IS_PAGE_OPCODE (code + 0)) + || (! IS_JMP_OPCODE (code + 2))) + return -1; + + index++; + addr -= 4; + } +} + +/* Determine if the instruction sequence matches that for + the prologue switch dispatch table with fewer than + 256 entries but more than 127. + + Before relaxation. + push %lo8insn(label) ; Push address of table + push %hi8insn(label) + add w,wreg ; index*2 => offset + snc ; CARRY SET? + inc 1(sp) ; Propagate MSB into table address + add 2(sp),w ; Add low bits of offset to table address + snc ; and handle any carry-out + inc 1(sp) + addr=> + page __indjmp ; Do an indirect jump to that location + jmp __indjmp + label: ; case dispatch table starts here + page $nnn1 + jmp $nnn1 + page $nnn2 + jmp $nnn2 + ... + page $nnnN + jmp $nnnN + + After relaxation. + push %lo8insn(label) ; Push address of table + push %hi8insn(label) + add 2(sp),w ; Add low bits of offset to table address + snc ; and handle any carry-out + inc 1(sp) + addr=> + page __indjmp ; Do an indirect jump to that location + jmp __indjmp + label: ; case dispatch table starts here + jmp $nnn1 + jmp $nnn2 + ... + jmp $nnnN */ + +static int +ip2k_is_switch_table_256 (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + bfd_vma addr, + bfd_byte *contents) +{ + bfd_byte code[16]; + int index = 0; + + /* Check current page-jmp. */ + if (addr + 4 > sec->size) + return -1; + + ip2k_get_mem (abfd, contents + addr, 4, code); + if ((! IS_PAGE_OPCODE (code + 0)) + || (! IS_JMP_OPCODE (code + 2))) + return -1; + + /* Search back. */ + while (1) + { + if (addr < 16) + return -1; + + /* Check previous 8 instructions. */ + ip2k_get_mem (abfd, contents + addr - 16, 16, code); + if ((IS_ADD_W_WREG_OPCODE (code + 0)) + && (IS_SNC_OPCODE (code + 2)) + && (IS_INC_1SP_OPCODE (code + 4)) + && (IS_ADD_2SP_W_OPCODE (code + 6)) + && (IS_SNC_OPCODE (code + 8)) + && (IS_INC_1SP_OPCODE (code + 10)) + && (IS_PAGE_OPCODE (code + 12)) + && (IS_JMP_OPCODE (code + 14))) + return index; + + if ((IS_ADD_W_WREG_OPCODE (code + 2)) + && (IS_SNC_OPCODE (code + 4)) + && (IS_INC_1SP_OPCODE (code + 6)) + && (IS_ADD_2SP_W_OPCODE (code + 8)) + && (IS_SNC_OPCODE (code + 10)) + && (IS_INC_1SP_OPCODE (code + 12)) + && (IS_JMP_OPCODE (code + 14))) + return index; + + if ((! IS_PAGE_OPCODE (code + 0)) + || (! IS_JMP_OPCODE (code + 2))) + return -1; + + index++; + addr -= 4; + } +} + /* Returns the expected page state for the given instruction not including the effect of page instructions. */ static bfd_vma -ip2k_nominal_page_bits (abfd, sec, addr, contents) - bfd *abfd ATTRIBUTE_UNUSED; - asection *sec; - bfd_vma addr; - bfd_byte *contents; +ip2k_nominal_page_bits (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + bfd_vma addr, + bfd_byte *contents) { bfd_vma page = PAGENO (BASEADDR (sec) + addr); @@ -405,11 +517,10 @@ ip2k_nominal_page_bits (abfd, sec, addr, contents) } static bfd_boolean -ip2k_test_page_insn (abfd, sec, irel, misc) - bfd *abfd ATTRIBUTE_UNUSED; - asection *sec; - Elf_Internal_Rela *irel; - struct misc *misc; +ip2k_test_page_insn (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + Elf_Internal_Rela *irel, + struct misc *misc) { bfd_vma symval; @@ -429,13 +540,269 @@ ip2k_test_page_insn (abfd, sec, irel, misc) return TRUE; } +/* Parts of a Stabs entry. */ + +#define STRDXOFF 0 +#define TYPEOFF 4 +#define OTHEROFF 5 +#define DESCOFF 6 +#define VALOFF 8 +#define STABSIZE 12 + +/* Adjust all the relocations entries after adding or inserting instructions. */ + +static void +adjust_all_relocations (bfd *abfd, + asection *sec, + bfd_vma addr, + bfd_vma endaddr, + int count, + int noadj) +{ + Elf_Internal_Shdr *symtab_hdr; + Elf_Internal_Sym *isymbuf, *isym, *isymend; + unsigned int shndx; + bfd_byte *contents; + Elf_Internal_Rela *irel, *irelend, *irelbase; + struct elf_link_hash_entry **sym_hashes; + struct elf_link_hash_entry **end_hashes; + unsigned int symcount; + asection *stab; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + + shndx = _bfd_elf_section_from_bfd_section (abfd, sec); + + contents = elf_section_data (sec)->this_hdr.contents; + + irelbase = elf_section_data (sec)->relocs; + irelend = irelbase + sec->reloc_count; + + for (irel = irelbase; irel < irelend; irel++) + { + if (ELF32_R_TYPE (irel->r_info) != R_IP2K_NONE) + { + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + asection *sym_sec; + + /* A local symbol. */ + isym = isymbuf + ELF32_R_SYM (irel->r_info); + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + + if (isym->st_shndx == shndx) + { + bfd_vma baseaddr = BASEADDR (sec); + bfd_vma symval = BASEADDR (sym_sec) + isym->st_value + + irel->r_addend; + + if ((baseaddr + addr + noadj) <= symval + && symval < (baseaddr + endaddr)) + irel->r_addend += count; + } + } + } + + /* Do this only for PC space relocations. */ + if (addr <= irel->r_offset && irel->r_offset < endaddr) + irel->r_offset += count; + } + + /* Now fix the stab relocations. */ + stab = bfd_get_section_by_name (abfd, ".stab"); + if (stab) + { + bfd_byte *stabcontents, *stabend, *stabp; + bfd_size_type stab_size = stab->rawsize ? stab->rawsize : stab->size; + + irelbase = elf_section_data (stab)->relocs; + irelend = irelbase + stab->reloc_count; + + /* Pull out the contents of the stab section. */ + if (elf_section_data (stab)->this_hdr.contents != NULL) + stabcontents = elf_section_data (stab)->this_hdr.contents; + else + { + if (!bfd_malloc_and_get_section (abfd, stab, &stabcontents)) + { + if (stabcontents != NULL) + free (stabcontents); + return; + } + + /* We need to remember this. */ + elf_section_data (stab)->this_hdr.contents = stabcontents; + } + + stabend = stabcontents + stab_size; + + for (irel = irelbase; irel < irelend; irel++) + { + if (ELF32_R_TYPE (irel->r_info) != R_IP2K_NONE) + { + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + asection *sym_sec; + + /* A local symbol. */ + isym = isymbuf + ELF32_R_SYM (irel->r_info); + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + + if (sym_sec == sec) + { + const char *name; + unsigned long strx; + unsigned char type, other; + unsigned short desc; + bfd_vma value; + bfd_vma baseaddr = BASEADDR (sec); + bfd_vma symval = BASEADDR (sym_sec) + isym->st_value + + irel->r_addend; + + if ((baseaddr + addr) <= symval + && symval <= (baseaddr + endaddr)) + irel->r_addend += count; + + /* Go hunt up a function and fix its line info if needed. */ + stabp = stabcontents + irel->r_offset - 8; + + /* Go pullout the stab entry. */ + strx = bfd_h_get_32 (abfd, stabp + STRDXOFF); + type = bfd_h_get_8 (abfd, stabp + TYPEOFF); + other = bfd_h_get_8 (abfd, stabp + OTHEROFF); + desc = bfd_h_get_16 (abfd, stabp + DESCOFF); + value = bfd_h_get_32 (abfd, stabp + VALOFF); + + name = bfd_get_stab_name (type); + + if (strcmp (name, "FUN") == 0) + { + int function_adjusted = 0; + + if (symval > (baseaddr + addr)) + /* Not in this function. */ + continue; + + /* Hey we got a function hit. */ + stabp += STABSIZE; + for (;stabp < stabend; stabp += STABSIZE) + { + /* Go pullout the stab entry. */ + strx = bfd_h_get_32 (abfd, stabp + STRDXOFF); + type = bfd_h_get_8 (abfd, stabp + TYPEOFF); + other = bfd_h_get_8 (abfd, stabp + OTHEROFF); + desc = bfd_h_get_16 (abfd, stabp + DESCOFF); + value = bfd_h_get_32 (abfd, stabp + VALOFF); + + name = bfd_get_stab_name (type); + + if (strcmp (name, "FUN") == 0) + { + /* Hit another function entry. */ + if (function_adjusted) + { + /* Adjust the value. */ + value += count; + + /* We need to put it back. */ + bfd_h_put_32 (abfd, value,stabp + VALOFF); + } + + /* And then bale out. */ + break; + } + + if (strcmp (name, "SLINE") == 0) + { + /* Got a line entry. */ + if ((baseaddr + addr) <= (symval + value)) + { + /* Adjust the line entry. */ + value += count; + + /* We need to put it back. */ + bfd_h_put_32 (abfd, value,stabp + VALOFF); + function_adjusted = 1; + } + } + } + } + } + } + } + } + } + + /* When adding an instruction back it is sometimes necessary to move any + global or local symbol that was referencing the first instruction of + the moved block to refer to the first instruction of the inserted block. + + For example adding a PAGE instruction before a CALL or JMP requires + that any label on the CALL or JMP is moved to the PAGE insn. */ + addr += noadj; + + /* Adjust the local symbols defined in this section. */ + isymend = isymbuf + symtab_hdr->sh_info; + for (isym = isymbuf; isym < isymend; isym++) + { + if (isym->st_shndx == shndx + && addr <= isym->st_value + && isym->st_value < endaddr) + 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) + { + if (addr <= sym_hash->root.u.def.value + && sym_hash->root.u.def.value < endaddr) + sym_hash->root.u.def.value += count; + } + } + + return; +} + +/* Delete some bytes from a section while relaxing. */ + +static bfd_boolean +ip2k_elf_relax_delete_bytes (bfd *abfd, + asection *sec, + bfd_vma addr, + int count) +{ + bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; + bfd_vma endaddr = sec->size; + + /* Actually delete the bytes. */ + memmove (contents + addr, contents + addr + count, + endaddr - addr - count); + + sec->size -= count; + + adjust_all_relocations (abfd, sec, addr + count, endaddr, -count, 0); + return TRUE; +} + static bfd_boolean -ip2k_delete_page_insn (abfd, sec, irel, again, misc) - bfd *abfd ATTRIBUTE_UNUSED; - asection *sec; - Elf_Internal_Rela *irel; - bfd_boolean *again; - struct misc *misc; +ip2k_delete_page_insn (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + Elf_Internal_Rela *irel, + bfd_boolean *again, + struct misc *misc) { /* Note that we've changed the relocs, section contents, etc. */ elf_section_data (sec)->relocs = misc->irelbase; @@ -455,83 +822,12 @@ ip2k_delete_page_insn (abfd, sec, irel, again, misc) return TRUE; } -/* Determine if the instruction sequence matches that for - the prologue of a switch dispatch table with fewer than - 128 entries. - - sc - page $nnn0 - jmp $nnn0 - add w,wreg - add pcl,w - addr=> - page $nnn1 - jmp $nnn1 - page $nnn2 - jmp $nnn2 - ... - page $nnnN - jmp $nnnN - - After relaxation. - sc - page $nnn0 - jmp $nnn0 - add pcl,w - addr=> - jmp $nnn1 - jmp $nnn2 - ... - jmp $nnnN */ - -static int -ip2k_is_switch_table_128 (abfd, sec, addr, contents) - bfd *abfd ATTRIBUTE_UNUSED; - asection *sec; - bfd_vma addr; - bfd_byte *contents; -{ - bfd_byte code[4]; - int index = 0; - - /* Check current page-jmp. */ - if (addr + 4 > sec->size) - return -1; - - ip2k_get_mem (abfd, contents + addr, 4, code); - - if ((! IS_PAGE_OPCODE (code + 0)) - || (! IS_JMP_OPCODE (code + 2))) - return -1; - - /* Search back. */ - while (1) - { - if (addr < 4) - return -1; - - /* Check previous 2 instructions. */ - ip2k_get_mem (abfd, contents + addr - 4, 4, code); - if ((IS_ADD_W_WREG_OPCODE (code + 0)) - && (IS_ADD_PCL_W_OPCODE (code + 2))) - return index; - - if ((! IS_PAGE_OPCODE (code + 0)) - || (! IS_JMP_OPCODE (code + 2))) - return -1; - - index++; - addr -= 4; - } -} - static bfd_boolean -ip2k_relax_switch_table_128 (abfd, sec, irel, again, misc) - bfd *abfd ATTRIBUTE_UNUSED; - asection *sec; - Elf_Internal_Rela *irel; - bfd_boolean *again; - struct misc *misc; +ip2k_relax_switch_table_128 (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + Elf_Internal_Rela *irel, + bfd_boolean *again, + struct misc *misc) { Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count; Elf_Internal_Rela *ireltest = irel; @@ -597,108 +893,12 @@ ip2k_relax_switch_table_128 (abfd, sec, irel, again, misc) return TRUE; } -/* Determine if the instruction sequence matches that for - the prologue switch dispatch table with fewer than - 256 entries but more than 127. - - Before relaxation. - push %lo8insn(label) ; Push address of table - push %hi8insn(label) - add w,wreg ; index*2 => offset - snc ; CARRY SET? - inc 1(sp) ; Propagate MSB into table address - add 2(sp),w ; Add low bits of offset to table address - snc ; and handle any carry-out - inc 1(sp) - addr=> - page __indjmp ; Do an indirect jump to that location - jmp __indjmp - label: ; case dispatch table starts here - page $nnn1 - jmp $nnn1 - page $nnn2 - jmp $nnn2 - ... - page $nnnN - jmp $nnnN - - After relaxation. - push %lo8insn(label) ; Push address of table - push %hi8insn(label) - add 2(sp),w ; Add low bits of offset to table address - snc ; and handle any carry-out - inc 1(sp) - addr=> - page __indjmp ; Do an indirect jump to that location - jmp __indjmp - label: ; case dispatch table starts here - jmp $nnn1 - jmp $nnn2 - ... - jmp $nnnN */ - -static int -ip2k_is_switch_table_256 (abfd, sec, addr, contents) - bfd *abfd ATTRIBUTE_UNUSED; - asection *sec; - bfd_vma addr; - bfd_byte *contents; -{ - bfd_byte code[16]; - int index = 0; - - /* Check current page-jmp. */ - if (addr + 4 > sec->size) - return -1; - - ip2k_get_mem (abfd, contents + addr, 4, code); - if ((! IS_PAGE_OPCODE (code + 0)) - || (! IS_JMP_OPCODE (code + 2))) - return -1; - - /* Search back. */ - while (1) - { - if (addr < 16) - return -1; - - /* Check previous 8 instructions. */ - ip2k_get_mem (abfd, contents + addr - 16, 16, code); - if ((IS_ADD_W_WREG_OPCODE (code + 0)) - && (IS_SNC_OPCODE (code + 2)) - && (IS_INC_1SP_OPCODE (code + 4)) - && (IS_ADD_2SP_W_OPCODE (code + 6)) - && (IS_SNC_OPCODE (code + 8)) - && (IS_INC_1SP_OPCODE (code + 10)) - && (IS_PAGE_OPCODE (code + 12)) - && (IS_JMP_OPCODE (code + 14))) - return index; - - if ((IS_ADD_W_WREG_OPCODE (code + 2)) - && (IS_SNC_OPCODE (code + 4)) - && (IS_INC_1SP_OPCODE (code + 6)) - && (IS_ADD_2SP_W_OPCODE (code + 8)) - && (IS_SNC_OPCODE (code + 10)) - && (IS_INC_1SP_OPCODE (code + 12)) - && (IS_JMP_OPCODE (code + 14))) - return index; - - if ((! IS_PAGE_OPCODE (code + 0)) - || (! IS_JMP_OPCODE (code + 2))) - return -1; - - index++; - addr -= 4; - } -} - static bfd_boolean -ip2k_relax_switch_table_256 (abfd, sec, irel, again, misc) - bfd *abfd ATTRIBUTE_UNUSED; - asection *sec; - Elf_Internal_Rela *irel; - bfd_boolean *again; - struct misc *misc; +ip2k_relax_switch_table_256 (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + Elf_Internal_Rela *irel, + bfd_boolean *again, + struct misc *misc) { Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count; Elf_Internal_Rela *ireltest = irel; @@ -777,6 +977,75 @@ ip2k_relax_switch_table_256 (abfd, sec, irel, again, misc) return TRUE; } +/* This function handles relaxation of a section in a specific page. */ + +static bfd_boolean +ip2k_elf_relax_section_page (bfd *abfd, + asection *sec, + bfd_boolean *again, + struct misc *misc, + unsigned long page_start, + unsigned long page_end) +{ + Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count; + Elf_Internal_Rela *irel; + int switch_table_128; + int switch_table_256; + + /* Walk thru the section looking for relaxation opportunities. */ + for (irel = misc->irelbase; irel < irelend; irel++) + { + if (ELF32_R_TYPE (irel->r_info) != (int) R_IP2K_PAGE3) + /* Ignore non page instructions. */ + continue; + + if (BASEADDR (sec) + irel->r_offset < page_start) + /* Ignore page instructions on earlier page - they have + already been processed. Remember that there is code flow + that crosses a page boundary. */ + continue; + + if (BASEADDR (sec) + irel->r_offset > page_end) + /* Flow beyond end of page => nothing more to do for this page. */ + return TRUE; + + /* Detect switch tables. */ + switch_table_128 = ip2k_is_switch_table_128 (abfd, sec, irel->r_offset, misc->contents); + switch_table_256 = ip2k_is_switch_table_256 (abfd, sec, irel->r_offset, misc->contents); + + if ((switch_table_128 > 0) || (switch_table_256 > 0)) + /* If the index is greater than 0 then it has already been processed. */ + continue; + + if (switch_table_128 == 0) + { + if (!ip2k_relax_switch_table_128 (abfd, sec, irel, again, misc)) + return FALSE; + + continue; + } + + if (switch_table_256 == 0) + { + if (!ip2k_relax_switch_table_256 (abfd, sec, irel, again, misc)) + return FALSE; + + continue; + } + + /* Simple relax. */ + if (ip2k_test_page_insn (abfd, sec, irel, misc)) + { + if (!ip2k_delete_page_insn (abfd, sec, irel, again, misc)) + return FALSE; + + continue; + } + } + + return TRUE; +} + /* This function handles relaxing for the ip2k. Principle: Start with the first page and remove page instructions that @@ -788,11 +1057,10 @@ ip2k_relax_switch_table_256 (abfd, sec, irel, again, misc) only policy to be used - pages can be removed but are never reinserted. */ static bfd_boolean -ip2k_elf_relax_section (abfd, sec, link_info, again) - bfd *abfd; - asection *sec; - struct bfd_link_info *link_info; - bfd_boolean *again; +ip2k_elf_relax_section (bfd *abfd, + asection *sec, + struct bfd_link_info *link_info, + bfd_boolean *again) { Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Rela *internal_relocs; @@ -834,8 +1102,7 @@ ip2k_elf_relax_section (abfd, sec, link_info, again) symtab_hdr = &elf_tdata (abfd)->symtab_hdr; - internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, - (Elf_Internal_Rela *)NULL, + internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, link_info->keep_memory); if (internal_relocs == NULL) goto error_return; @@ -848,8 +1115,7 @@ ip2k_elf_relax_section (abfd, sec, link_info, again) /* So stab does exits. */ Elf_Internal_Rela * irelbase; - irelbase = _bfd_elf_link_read_relocs (abfd, stab, NULL, - (Elf_Internal_Rela *)NULL, + irelbase = _bfd_elf_link_read_relocs (abfd, stab, NULL, NULL, link_info->keep_memory); } @@ -973,371 +1239,29 @@ ip2k_elf_relax_section (abfd, sec, link_info, again) return FALSE; } -/* This function handles relaxation of a section in a specific page. */ - -static bfd_boolean -ip2k_elf_relax_section_page (abfd, sec, again, misc, page_start, page_end) - bfd *abfd; - asection *sec; - bfd_boolean *again; - struct misc *misc; - unsigned long page_start; - unsigned long page_end; -{ - Elf_Internal_Rela *irelend = misc->irelbase + sec->reloc_count; - Elf_Internal_Rela *irel; - int switch_table_128; - int switch_table_256; - - /* Walk thru the section looking for relaxation opportunities. */ - for (irel = misc->irelbase; irel < irelend; irel++) - { - if (ELF32_R_TYPE (irel->r_info) != (int) R_IP2K_PAGE3) - /* Ignore non page instructions. */ - continue; - - if (BASEADDR (sec) + irel->r_offset < page_start) - /* Ignore page instructions on earlier page - they have - already been processed. Remember that there is code flow - that crosses a page boundary. */ - continue; - - if (BASEADDR (sec) + irel->r_offset > page_end) - /* Flow beyond end of page => nothing more to do for this page. */ - return TRUE; - - /* Detect switch tables. */ - switch_table_128 = ip2k_is_switch_table_128 (abfd, sec, irel->r_offset, misc->contents); - switch_table_256 = ip2k_is_switch_table_256 (abfd, sec, irel->r_offset, misc->contents); - - if ((switch_table_128 > 0) || (switch_table_256 > 0)) - /* If the index is greater than 0 then it has already been processed. */ - continue; - - if (switch_table_128 == 0) - { - if (!ip2k_relax_switch_table_128 (abfd, sec, irel, again, misc)) - return FALSE; - - continue; - } - - if (switch_table_256 == 0) - { - if (!ip2k_relax_switch_table_256 (abfd, sec, irel, again, misc)) - return FALSE; - - continue; - } - - /* Simple relax. */ - if (ip2k_test_page_insn (abfd, sec, irel, misc)) - { - if (!ip2k_delete_page_insn (abfd, sec, irel, again, misc)) - return FALSE; - - continue; - } - } - - return TRUE; -} - -/* Parts of a Stabs entry. */ - -#define STRDXOFF (0) -#define TYPEOFF (4) -#define OTHEROFF (5) -#define DESCOFF (6) -#define VALOFF (8) -#define STABSIZE (12) - -/* Adjust all the relocations entries after adding or inserting instructions. */ - -static void -adjust_all_relocations (abfd, sec, addr, endaddr, count, noadj) - bfd *abfd; - asection *sec; - bfd_vma addr; - bfd_vma endaddr; - int count; - int noadj; -{ - Elf_Internal_Shdr *symtab_hdr; - Elf_Internal_Sym *isymbuf, *isym, *isymend; - unsigned int shndx; - bfd_byte *contents; - Elf_Internal_Rela *irel, *irelend, *irelbase; - struct elf_link_hash_entry **sym_hashes; - struct elf_link_hash_entry **end_hashes; - unsigned int symcount; - asection *stab; - - symtab_hdr = &elf_tdata (abfd)->symtab_hdr; - isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; - - shndx = _bfd_elf_section_from_bfd_section (abfd, sec); - - contents = elf_section_data (sec)->this_hdr.contents; - - irelbase = elf_section_data (sec)->relocs; - irelend = irelbase + sec->reloc_count; - - for (irel = irelbase; irel < irelend; irel++) - { - if (ELF32_R_TYPE (irel->r_info) != R_IP2K_NONE) - { - /* Get the value of the symbol referred to by the reloc. */ - if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) - { - asection *sym_sec; - - /* A local symbol. */ - isym = isymbuf + ELF32_R_SYM (irel->r_info); - sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); - - if (isym->st_shndx == shndx) - { - bfd_vma baseaddr = BASEADDR (sec); - bfd_vma symval = BASEADDR (sym_sec) + isym->st_value - + irel->r_addend; - - if ((baseaddr + addr + noadj) <= symval - && symval < (baseaddr + endaddr)) - irel->r_addend += count; - } - } - } - - /* Do this only for PC space relocations. */ - if (addr <= irel->r_offset && irel->r_offset < endaddr) - irel->r_offset += count; - } - - /* Now fix the stab relocations. */ - stab = bfd_get_section_by_name (abfd, ".stab"); - if (stab) - { - bfd_byte *stabcontents, *stabend, *stabp; - bfd_size_type stab_size = stab->rawsize ? stab->rawsize : stab->size; - - irelbase = elf_section_data (stab)->relocs; - irelend = irelbase + stab->reloc_count; - - /* Pull out the contents of the stab section. */ - if (elf_section_data (stab)->this_hdr.contents != NULL) - stabcontents = elf_section_data (stab)->this_hdr.contents; - else - { - if (!bfd_malloc_and_get_section (abfd, stab, &stabcontents)) - { - if (stabcontents != NULL) - free (stabcontents); - return; - } - - /* We need to remember this. */ - elf_section_data (stab)->this_hdr.contents = stabcontents; - } - - stabend = stabcontents + stab_size; - - for (irel = irelbase; irel < irelend; irel++) - { - if (ELF32_R_TYPE (irel->r_info) != R_IP2K_NONE) - { - /* Get the value of the symbol referred to by the reloc. */ - if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) - { - asection *sym_sec; - - /* A local symbol. */ - isym = isymbuf + ELF32_R_SYM (irel->r_info); - sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); - - if (sym_sec == sec) - { - const char *name; - unsigned long strx; - unsigned char type, other; - unsigned short desc; - bfd_vma value; - bfd_vma baseaddr = BASEADDR (sec); - bfd_vma symval = BASEADDR (sym_sec) + isym->st_value - + irel->r_addend; - - if ((baseaddr + addr) <= symval - && symval <= (baseaddr + endaddr)) - irel->r_addend += count; - - /* Go hunt up a function and fix its line info if needed. */ - stabp = stabcontents + irel->r_offset - 8; - - /* Go pullout the stab entry. */ - strx = bfd_h_get_32 (abfd, stabp + STRDXOFF); - type = bfd_h_get_8 (abfd, stabp + TYPEOFF); - other = bfd_h_get_8 (abfd, stabp + OTHEROFF); - desc = bfd_h_get_16 (abfd, stabp + DESCOFF); - value = bfd_h_get_32 (abfd, stabp + VALOFF); - - name = bfd_get_stab_name (type); - - if (strcmp (name, "FUN") == 0) - { - int function_adjusted = 0; - - if (symval > (baseaddr + addr)) - /* Not in this function. */ - continue; - - /* Hey we got a function hit. */ - stabp += STABSIZE; - for (;stabp < stabend; stabp += STABSIZE) - { - /* Go pullout the stab entry. */ - strx = bfd_h_get_32 (abfd, stabp + STRDXOFF); - type = bfd_h_get_8 (abfd, stabp + TYPEOFF); - other = bfd_h_get_8 (abfd, stabp + OTHEROFF); - desc = bfd_h_get_16 (abfd, stabp + DESCOFF); - value = bfd_h_get_32 (abfd, stabp + VALOFF); - - name = bfd_get_stab_name (type); - - if (strcmp (name, "FUN") == 0) - { - /* Hit another function entry. */ - if (function_adjusted) - { - /* Adjust the value. */ - value += count; - - /* We need to put it back. */ - bfd_h_put_32 (abfd, value,stabp + VALOFF); - } - - /* And then bale out. */ - break; - } - - if (strcmp (name, "SLINE") == 0) - { - /* Got a line entry. */ - if ((baseaddr + addr) <= (symval + value)) - { - /* Adjust the line entry. */ - value += count; - - /* We need to put it back. */ - bfd_h_put_32 (abfd, value,stabp + VALOFF); - function_adjusted = 1; - } - } - } - } - } - } - } - } - } - - /* When adding an instruction back it is sometimes necessary to move any - global or local symbol that was referencing the first instruction of - the moved block to refer to the first instruction of the inserted block. - - For example adding a PAGE instruction before a CALL or JMP requires - that any label on the CALL or JMP is moved to the PAGE insn. */ - addr += noadj; - - /* Adjust the local symbols defined in this section. */ - isymend = isymbuf + symtab_hdr->sh_info; - for (isym = isymbuf; isym < isymend; isym++) - { - if (isym->st_shndx == shndx - && addr <= isym->st_value - && isym->st_value < endaddr) - 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) - { - if (addr <= sym_hash->root.u.def.value - && sym_hash->root.u.def.value < endaddr) - sym_hash->root.u.def.value += count; - } - } - - return; -} - -/* Delete some bytes from a section while relaxing. */ - -static bfd_boolean -ip2k_elf_relax_delete_bytes (abfd, sec, addr, count) - bfd *abfd; - asection *sec; - bfd_vma addr; - int count; -{ - bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; - bfd_vma endaddr = sec->size; - - /* Actually delete the bytes. */ - memmove (contents + addr, contents + addr + count, - endaddr - addr - count); - - sec->size -= count; - - adjust_all_relocations (abfd, sec, addr + count, endaddr, -count, 0); - return TRUE; -} - -/* -------------------------------------------------------------------- */ - -/* XXX: The following code is the result of a cut&paste. This unfortunate - practice is very widespread in the various target back-end files. */ - /* Set the howto pointer for a IP2K ELF reloc. */ static void -ip2k_info_to_howto_rela (abfd, cache_ptr, dst) - bfd * abfd ATTRIBUTE_UNUSED; - arelent * cache_ptr; - Elf_Internal_Rela * dst; +ip2k_info_to_howto_rela (bfd * abfd ATTRIBUTE_UNUSED, + arelent * cache_ptr, + Elf_Internal_Rela * dst) { unsigned int r_type; r_type = ELF32_R_TYPE (dst->r_info); - switch (r_type) - { - default: - cache_ptr->howto = & ip2k_elf_howto_table [r_type]; - break; - } + cache_ptr->howto = & ip2k_elf_howto_table [r_type]; } /* Perform a single relocation. By default we use the standard BFD routines. */ static bfd_reloc_status_type -ip2k_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; +ip2k_final_link_relocate (reloc_howto_type * howto, + bfd * input_bfd, + asection * input_section, + bfd_byte * contents, + Elf_Internal_Rela * rel, + bfd_vma relocation) { static bfd_vma page_addr = 0; @@ -1383,11 +1307,13 @@ ip2k_final_link_relocate (howto, input_bfd, input_section, contents, rel, /* Preceding page instruction. Verify that the page instruction is really needed. One reason for the relaxation to miss a page is if the section is not marked as executable. */ - if (!ip2k_is_switch_table_128 (input_bfd, input_section, rel->r_offset - 2, contents) && - !ip2k_is_switch_table_256 (input_bfd, input_section, rel->r_offset - 2, contents) && - (PAGENO (relocation + rel->r_addend) == - ip2k_nominal_page_bits (input_bfd, input_section, - rel->r_offset - 2, contents))) + if (!ip2k_is_switch_table_128 (input_bfd, input_section, + rel->r_offset - 2, contents) + && !ip2k_is_switch_table_256 (input_bfd, input_section, + rel->r_offset - 2, contents) + && (PAGENO (relocation + rel->r_addend) == + ip2k_nominal_page_bits (input_bfd, input_section, + rel->r_offset - 2, contents))) _bfd_error_handler (_("ip2k linker: redundant page instruction at 0x%08lx (dest = 0x%08lx)."), page_addr, relocation + rel->r_addend); @@ -1459,16 +1385,14 @@ ip2k_final_link_relocate (howto, input_bfd, input_section, contents, rel, accordingly. */ static bfd_boolean -ip2k_elf_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; +ip2k_elf_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; @@ -1531,7 +1455,7 @@ ip2k_elf_relocate_section (output_bfd, info, input_bfd, input_section, if (r != bfd_reloc_ok) { - const char * msg = (const char *) NULL; + const char * msg = NULL; switch (r) { @@ -1579,12 +1503,11 @@ ip2k_elf_relocate_section (output_bfd, info, input_bfd, input_section, } static asection * -ip2k_elf_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; +ip2k_elf_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) { @@ -1617,11 +1540,10 @@ ip2k_elf_gc_mark_hook (sec, info, rel, h, sym) } static bfd_boolean -ip2k_elf_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; +ip2k_elf_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 ip2k. */ return TRUE; |