From 86aba9dbfa7eb9bf07dd611c85e3cff31ebaa740 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Thu, 29 Aug 2002 06:49:35 +0000 Subject: [PATCH] Add linker relaxation to v850 toolchain --- bfd/ChangeLog | 18 + bfd/archures.c | 1 - bfd/bfd-in2.h | 10 +- bfd/cpu-v850.c | 30 +- bfd/elf32-v850.c | 1085 +++++++++++++++++++++++++++++++++++++++-- bfd/libbfd.h | 4 +- bfd/reloc.c | 14 +- gas/ChangeLog | 21 + gas/config/tc-v850.c | 118 ++++- gas/config/tc-v850.h | 5 + gas/doc/c-v850.texi | 23 +- include/elf/ChangeLog | 5 + include/elf/v850.h | 47 +- 13 files changed, 1274 insertions(+), 107 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 43a03634e4..b2869495ea 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,21 @@ +2002-08-28 Catherine Moore + + * elf32-v850.c (v850_elf_reloc_map): Add new relocs. + (v850-elf-reloc): Don't resolve pc relative relocs. + (v850_elf_ignore_reloc): New routine. + (v850_elf_final_link_relocate): Handle new relocs. + (v850_elf_relax_delete_bytes ): New routine. + (v850_elf_relax_section): New routine. + (bfd_elf32_bfd_relax_section): Define. + (HOWTO): New entries for new relocs. + * reloc.c (BFD_RELOC_V850_LONGCALL): New reloc. + (BFD_RELOC_V850_LONGJUMP): New reloc. + (BFD_RELOC_V850_ALIGN): New reloc. + * archures.c: Remove redundant v850ea architecture. + * cpu-v850.c: Remove redundant v850ea support. + * libbfd.h: Regenerate. + * bfd-in2.h: Regenerated. + 2002-08-28 Svein E. Seldal * config.bfd: Add tic4x-*-*coff* and c4x-*-*coff* target. diff --git a/bfd/archures.c b/bfd/archures.c index f3ca094bcf..3f0ba7bb65 100644 --- a/bfd/archures.c +++ b/bfd/archures.c @@ -236,7 +236,6 @@ DESCRIPTION . bfd_arch_v850, {* NEC V850 *} .#define bfd_mach_v850 0 .#define bfd_mach_v850e 'E' -.#define bfd_mach_v850ea 'A' . bfd_arch_arc, {* ARC Cores *} .#define bfd_mach_arc_5 0 .#define bfd_mach_arc_6 1 diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 34fb98ed30..0ab4245202 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -274,7 +274,7 @@ bfd_format; memory. If this is set, iostream points to a bfd_in_memory struct. */ #define BFD_IN_MEMORY 0x800 -/* The sections in this BFD specify a memory page */ +/* The sections in this BFD specify a memory page. */ #define HAS_LOAD_PAGE 0x1000 /* Symbols and relocation. */ @@ -2668,6 +2668,14 @@ bits placed non-contigously in the instruction. */ /* This is a 16 bit offset from the call table base pointer. */ BFD_RELOC_V850_CALLT_16_16_OFFSET, +/* Used for relaxing indirect function calls. */ + BFD_RELOC_V850_LONGCALL, + +/* Used for relaxing indirect jumps. */ + BFD_RELOC_V850_LONGJUMP, + +/* Used to maintain alignment whilst relaxing. */ + BFD_RELOC_V850_ALIGN, /* This is a 32bit pcrel reloc for the mn10300, offset by two bytes in the instruction. */ diff --git a/bfd/cpu-v850.c b/bfd/cpu-v850.c index 8836362026..ce61caa756 100644 --- a/bfd/cpu-v850.c +++ b/bfd/cpu-v850.c @@ -1,21 +1,21 @@ /* BFD support for the NEC V850 processor - Copyright 1996, 1997, 1998, 2000, 2001 Free Software Foundation, Inc. + Copyright 1996, 1997, 1998, 2000, 2001, 2002 Free Software Foundation, Inc. -This file is part of BFD, the Binary File Descriptor library. + This file is part of BFD, the Binary File Descriptor library. -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "bfd.h" #include "sysdep.h" @@ -67,7 +67,6 @@ scan (info, string) switch (number) { case bfd_mach_v850e: arch = bfd_arch_v850; break; - case bfd_mach_v850ea: arch = bfd_arch_v850; break; default: return false; } @@ -89,8 +88,7 @@ scan (info, string) static const bfd_arch_info_type arch_info_struct[] = { - N (bfd_mach_v850e, "v850e", false, & arch_info_struct[1]), - N (bfd_mach_v850ea, "v850ea", false, NULL) + N (bfd_mach_v850e, "v850e", false, NULL) }; #undef NEXT diff --git a/bfd/elf32-v850.c b/bfd/elf32-v850.c index 07890c6a7c..193a0bf493 100644 --- a/bfd/elf32-v850.c +++ b/bfd/elf32-v850.c @@ -2,24 +2,24 @@ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. -This file is part of BFD, the Binary File Descriptor library. + This file is part of BFD, the Binary File Descriptor library. -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* XXX FIXME: This code is littered with 32bit int, 16bit short, 8bit char - dependencies. As is the gas & simulator code or the v850. */ + dependencies. As is the gas & simulator code for the v850. */ #include "bfd.h" #include "sysdep.h" @@ -87,6 +87,12 @@ static asection * v850_elf_gc_mark_hook PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *, struct elf_link_hash_entry *, Elf_Internal_Sym *)); +static bfd_reloc_status_type v850_elf_ignore_reloc + PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **)); +static boolean v850_elf_relax_delete_bytes + PARAMS ((bfd *, asection *, bfd_vma, bfd_vma, int)); +static boolean v850_elf_relax_section + PARAMS ((bfd *, asection *, struct bfd_link_info *, boolean *)); /* Note: It is REQUIRED that the 'type' value of each entry in this array match the index of the entry in the array. */ @@ -467,6 +473,51 @@ static reloc_howto_type v850_elf_howto_table[] = 0, /* dst_mask */ false), /* pcrel_offset */ + /* Indicates a .longcall pseudo-op. The compiler will generate a .longcall + pseudo-op when it finds a function call which can be relaxed. */ + HOWTO (R_V850_LONGCALL, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + v850_elf_ignore_reloc, /* special_function */ + "R_V850_LONGCALL", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + true), /* pcrel_offset */ + + /* Indicates a .longjump pseudo-op. The compiler will generate a + .longjump pseudo-op when it finds a branch which can be relaxed. */ + HOWTO (R_V850_LONGJUMP, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + v850_elf_ignore_reloc, /* special_function */ + "R_V850_LONGJUMP", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + true), /* pcrel_offset */ + + HOWTO (R_V850_ALIGN, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 0, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_unsigned, /* complain_on_overflow */ + v850_elf_ignore_reloc, /* special_function */ + "R_V850_ALIGN", /* name */ + false, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + true), /* pcrel_offset */ }; /* Map BFD reloc types to V850 ELF reloc types. */ @@ -481,31 +532,34 @@ struct v850_elf_reloc_map static const struct v850_elf_reloc_map v850_elf_reloc_map[] = { - { BFD_RELOC_NONE, R_V850_NONE }, - { BFD_RELOC_V850_9_PCREL, R_V850_9_PCREL }, - { BFD_RELOC_V850_22_PCREL, R_V850_22_PCREL }, - { BFD_RELOC_HI16_S, R_V850_HI16_S }, - { BFD_RELOC_HI16, R_V850_HI16 }, - { BFD_RELOC_LO16, R_V850_LO16 }, - { BFD_RELOC_32, R_V850_32 }, - { BFD_RELOC_16, R_V850_16 }, - { BFD_RELOC_8, R_V850_8 }, - { BFD_RELOC_V850_SDA_16_16_OFFSET, R_V850_SDA_16_16_OFFSET }, - { BFD_RELOC_V850_SDA_15_16_OFFSET, R_V850_SDA_15_16_OFFSET }, - { BFD_RELOC_V850_ZDA_16_16_OFFSET, R_V850_ZDA_16_16_OFFSET }, - { BFD_RELOC_V850_ZDA_15_16_OFFSET, R_V850_ZDA_15_16_OFFSET }, - { BFD_RELOC_V850_TDA_6_8_OFFSET, R_V850_TDA_6_8_OFFSET }, - { BFD_RELOC_V850_TDA_7_8_OFFSET, R_V850_TDA_7_8_OFFSET }, - { BFD_RELOC_V850_TDA_7_7_OFFSET, R_V850_TDA_7_7_OFFSET }, - { BFD_RELOC_V850_TDA_16_16_OFFSET, R_V850_TDA_16_16_OFFSET }, + { BFD_RELOC_NONE, R_V850_NONE }, + { BFD_RELOC_V850_9_PCREL, R_V850_9_PCREL }, + { BFD_RELOC_V850_22_PCREL, R_V850_22_PCREL }, + { BFD_RELOC_HI16_S, R_V850_HI16_S }, + { BFD_RELOC_HI16, R_V850_HI16 }, + { BFD_RELOC_LO16, R_V850_LO16 }, + { BFD_RELOC_32, R_V850_32 }, + { BFD_RELOC_16, R_V850_16 }, + { BFD_RELOC_8, R_V850_8 }, + { BFD_RELOC_V850_SDA_16_16_OFFSET, R_V850_SDA_16_16_OFFSET }, + { BFD_RELOC_V850_SDA_15_16_OFFSET, R_V850_SDA_15_16_OFFSET }, + { BFD_RELOC_V850_ZDA_16_16_OFFSET, R_V850_ZDA_16_16_OFFSET }, + { BFD_RELOC_V850_ZDA_15_16_OFFSET, R_V850_ZDA_15_16_OFFSET }, + { BFD_RELOC_V850_TDA_6_8_OFFSET, R_V850_TDA_6_8_OFFSET }, + { BFD_RELOC_V850_TDA_7_8_OFFSET, R_V850_TDA_7_8_OFFSET }, + { BFD_RELOC_V850_TDA_7_7_OFFSET, R_V850_TDA_7_7_OFFSET }, + { BFD_RELOC_V850_TDA_16_16_OFFSET, R_V850_TDA_16_16_OFFSET }, { BFD_RELOC_V850_TDA_4_5_OFFSET, R_V850_TDA_4_5_OFFSET }, { BFD_RELOC_V850_TDA_4_4_OFFSET, R_V850_TDA_4_4_OFFSET }, { BFD_RELOC_V850_SDA_16_16_SPLIT_OFFSET, R_V850_SDA_16_16_SPLIT_OFFSET }, { BFD_RELOC_V850_ZDA_16_16_SPLIT_OFFSET, R_V850_ZDA_16_16_SPLIT_OFFSET }, { BFD_RELOC_V850_CALLT_6_7_OFFSET, R_V850_CALLT_6_7_OFFSET }, { BFD_RELOC_V850_CALLT_16_16_OFFSET, R_V850_CALLT_16_16_OFFSET }, - { BFD_RELOC_VTABLE_INHERIT, R_V850_GNU_VTINHERIT }, - { BFD_RELOC_VTABLE_ENTRY, R_V850_GNU_VTENTRY }, + { BFD_RELOC_VTABLE_INHERIT, R_V850_GNU_VTINHERIT }, + { BFD_RELOC_VTABLE_ENTRY, R_V850_GNU_VTENTRY }, + { BFD_RELOC_V850_LONGCALL, R_V850_LONGCALL }, + { BFD_RELOC_V850_LONGJUMP, R_V850_LONGJUMP }, + { BFD_RELOC_V850_ALIGN, R_V850_ALIGN }, }; @@ -1297,6 +1351,9 @@ v850_elf_reloc (abfd, reloc, symbol, data, isection, obfd, err) /* Work out which section the relocation is targetted at and the initial relocation command value. */ + if (reloc->howto->pc_relative == true) + return bfd_reloc_ok; + /* Get symbol value. (Common symbols are special.) */ if (bfd_is_com_section (symbol->section)) relocation = 0; @@ -1335,6 +1392,26 @@ v850_elf_reloc (abfd, reloc, symbol, data, isection, obfd, err) reloc->addend = relocation; return bfd_reloc_ok; } + +/* This function is used for relocs which are only used + for relaxing, which the linker should otherwise ignore. */ + +static bfd_reloc_status_type +v850_elf_ignore_reloc (abfd, reloc_entry, symbol, data, input_section, + output_bfd, error_message) + bfd * abfd ATTRIBUTE_UNUSED; + arelent * reloc_entry; + asymbol * symbol ATTRIBUTE_UNUSED; + PTR data ATTRIBUTE_UNUSED; + asection * input_section; + bfd * output_bfd; + char ** error_message ATTRIBUTE_UNUSED; +{ + if (output_bfd != NULL) + reloc_entry->address += input_section->output_offset; + + return bfd_reloc_ok; +} static boolean v850_elf_is_local_label_name (abfd, name) @@ -1349,8 +1426,8 @@ v850_elf_is_local_label_name (abfd, name) static bfd_reloc_status_type v850_elf_final_link_relocate (howto, input_bfd, output_bfd, - input_section, contents, offset, value, - addend, info, sym_sec, is_local) + input_section, contents, offset, value, + addend, info, sym_sec, is_local) reloc_howto_type * howto; bfd * input_bfd; bfd * output_bfd ATTRIBUTE_UNUSED; @@ -1500,6 +1577,9 @@ v850_elf_final_link_relocate (howto, input_bfd, output_bfd, case R_V850_NONE: case R_V850_GNU_VTINHERIT: case R_V850_GNU_VTENTRY: + case R_V850_LONGCALL: + case R_V850_LONGJUMP: + case R_V850_ALIGN: return bfd_reloc_ok; default: @@ -1770,7 +1850,6 @@ v850_elf_object_p (abfd) default: case E_V850_ARCH: (void) bfd_default_set_arch_mach (abfd, bfd_arch_v850, 0); break; case E_V850E_ARCH: (void) bfd_default_set_arch_mach (abfd, bfd_arch_v850, bfd_mach_v850e); break; - case E_V850EA_ARCH: (void) bfd_default_set_arch_mach (abfd, bfd_arch_v850, bfd_mach_v850ea); break; } return true; } @@ -1789,7 +1868,6 @@ v850_elf_final_write_processing (abfd, linker) default: case 0: val = E_V850_ARCH; break; case bfd_mach_v850e: val = E_V850E_ARCH; break; - case bfd_mach_v850ea: val = E_V850EA_ARCH; break; } elf_elfheader (abfd)->e_flags &=~ EF_V850_ARCH; @@ -1882,7 +1960,6 @@ v850_elf_print_private_bfd_data (abfd, ptr) default: case E_V850_ARCH: fprintf (file, _("v850 architecture")); break; case E_V850E_ARCH: fprintf (file, _("v850e architecture")); break; - case E_V850EA_ARCH: fprintf (file, _("v850ea architecture")); break; } fputc ('\n', file); @@ -2166,6 +2243,939 @@ v850_elf_fake_sections (abfd, hdr, sec) return true; } + +/* Delete some bytes from a section while relaxing. */ + +static boolean +v850_elf_relax_delete_bytes (abfd, sec, addr, toaddr, count) + bfd * abfd; + asection * sec; + bfd_vma addr; + bfd_vma toaddr; + int count; +{ + Elf_Internal_Shdr * symtab_hdr; + Elf32_External_Sym * extsyms; + Elf32_External_Sym * esym; + Elf32_External_Sym * esymend; + int index; + unsigned int sec_shndx; + bfd_byte * contents; + Elf_Internal_Rela * irel; + Elf_Internal_Rela * irelend; + struct elf_link_hash_entry * sym_hash; + Elf_Internal_Shdr * shndx_hdr; + Elf_External_Sym_Shndx * shndx; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + extsyms = (Elf32_External_Sym *) symtab_hdr->contents; + + 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 alignment + power larger than the number of bytes we are deleting. */ + + /* Actually delete the bytes. */ +#if (DEBUG_RELAX & 2) + fprintf (stderr, "relax_delete: contents: sec: %s %p .. %p %x\n", + sec->name, addr, toaddr, count ); +#endif + memmove (contents + addr, contents + addr + count, + toaddr - addr - count); + memset (contents + toaddr-count, 0, count); + + /* Adjust all the relocs. */ + irel = elf_section_data (sec)->relocs; + irelend = irel + sec->reloc_count; + shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr; + shndx = (Elf_External_Sym_Shndx *) shndx_hdr->contents; + + for (; irel < irelend; irel++) + { + bfd_vma raddr, paddr, symval; + Elf_Internal_Sym isym; + + /* Get the new reloc address. */ + raddr = irel->r_offset; + if ((raddr >= (addr + count) && raddr < toaddr)) + irel->r_offset -= count; + + if (raddr >= addr && raddr < addr + count) + { + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + (int) R_V850_NONE); + continue; + } + + if (ELF32_R_TYPE (irel->r_info) == (int) R_V850_ALIGN) + continue; + + bfd_elf32_swap_symbol_in (abfd, + extsyms + ELF32_R_SYM (irel->r_info), + shndx ? shndx + ELF32_R_SYM (irel->r_info) : NULL, + & isym); + + if (isym.st_shndx != sec_shndx) + continue; + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + symval = isym.st_value; +#if (DEBUG_RELAX & 2) + { + char * name = bfd_elf_string_from_elf_section + (abfd, symtab_hdr->sh_link, isym.st_name); + fprintf (stderr, + "relax_delete: local: sec: %s, sym: %s (%d), value: %x + %x + %x addend %x\n", + sec->name, name, isym.st_name, + sec->output_section->vma, sec->output_offset, + isym.st_value, irel->r_addend); + } +#endif + } + else + { + unsigned long indx; + struct elf_link_hash_entry * h; + + /* An external symbol. */ + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + + h = elf_sym_hashes (abfd) [indx]; + BFD_ASSERT (h != NULL); + + symval = h->root.u.def.value; +#if (DEBUG_RELAX & 2) + fprintf (stderr, + "relax_delete: defined: sec: %s, name: %s, value: %x + %x + %x addend %x\n", + sec->name, h->root.root.string, h->root.u.def.value, + sec->output_section->vma, sec->output_offset, irel->r_addend); +#endif + } + + paddr = symval + irel->r_addend; + + if ( (symval >= addr + count && symval < toaddr) + && (paddr < addr + count || paddr >= toaddr)) + irel->r_addend += count; + else if ( (symval < addr + count || symval >= toaddr) + && (paddr >= addr + count && paddr < toaddr)) + irel->r_addend -= count; + } + + /* Adjust the local symbols defined in this section. */ + esym = extsyms; + esymend = esym + symtab_hdr->sh_info; + + for (; esym < esymend; esym++, shndx = (shndx ? shndx + 1 : NULL)) + { + Elf_Internal_Sym isym; + + bfd_elf32_swap_symbol_in (abfd, esym, shndx, & isym); + + if (isym.st_shndx == sec_shndx + && isym.st_value >= addr + count + && isym.st_value < toaddr) + { + isym.st_value -= count; + + if (isym.st_value + isym.st_size >= toaddr) + isym.st_size += count; + + bfd_elf32_swap_symbol_out (abfd, & isym, shndx, esym); + } + else if (isym.st_shndx == sec_shndx + && isym.st_value < addr + count) + { + if (isym.st_value+isym.st_size >= addr + count + && isym.st_value+isym.st_size < toaddr) + isym.st_size -= count; + + if (isym.st_value >= addr + && isym.st_value < addr + count) + isym.st_value = addr; + + bfd_elf32_swap_symbol_out (abfd, & isym, shndx, esym); + } + } + + /* Now adjust the global symbols defined in this section. */ + esym = extsyms + symtab_hdr->sh_info; + esymend = extsyms + (symtab_hdr->sh_size / sizeof (Elf32_External_Sym)); + + for (index = 0; esym < esymend; esym ++, index ++) + { + Elf_Internal_Sym isym; + + bfd_elf32_swap_symbol_in (abfd, esym, shndx, & isym); + sym_hash = elf_sym_hashes (abfd) [index]; + + if (isym.st_shndx == sec_shndx + && ((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 + count + && (sym_hash)->root.u.def.value < toaddr) + { + if ((sym_hash)->root.u.def.value + isym.st_size >= toaddr) + { + isym.st_size += count; + bfd_elf32_swap_symbol_out (abfd, & isym, shndx, esym); + } + + (sym_hash)->root.u.def.value -= count; + } + else if (isym.st_shndx == sec_shndx + && ((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 + count) + { + if ((sym_hash)->root.u.def.value+isym.st_size >= addr + count + && (sym_hash)->root.u.def.value+isym.st_size < toaddr) + isym.st_size -= count; + + if ((sym_hash)->root.u.def.value >= addr + && (sym_hash)->root.u.def.value < addr + count) + (sym_hash)->root.u.def.value = addr; + + bfd_elf32_swap_symbol_out (abfd, & isym, shndx, esym); + } + + if (shndx) + ++ shndx; + } + + return true; +} + +#define NOP_OPCODE (0x0000) +#define MOVHI 0x0640 /* 4byte */ +#define MOVHI_MASK 0x07e0 +#define MOVHI_R1(insn) ((insn) & 0x1f) /* 4byte */ +#define MOVHI_R2(insn) ((insn) >> 11) +#define MOVEA 0x0620 /* 2byte */ +#define MOVEA_MASK 0x07e0 +#define MOVEA_R1(insn) ((insn) & 0x1f) +#define MOVEA_R2(insn) ((insn) >> 11) +#define JARL_4 0x00040780 /* 4byte */ +#define JARL_4_MASK 0xFFFF07FF +#define JARL_R2(insn) (int)(((insn) & (~JARL_4_MASK)) >> 11) +#define ADD_I 0x0240 /* 2byte */ +#define ADD_I_MASK 0x07e0 +#define ADD_I5(insn) ((((insn) & 0x001f) << 11) >> 11) /* 2byte */ +#define ADD_R2(insn) ((insn) >> 11) +#define JMP_R 0x0060 /* 2byte */ +#define JMP_R_MASK 0xFFE0 +#define JMP_R1(insn) ((insn) & 0x1f) + +static boolean +v850_elf_relax_section (abfd, sec, link_info, again) + bfd * abfd; + asection * sec; + struct bfd_link_info * link_info; + boolean * again; +{ + Elf_Internal_Shdr * symtab_hdr; + Elf_Internal_Rela * internal_relocs; + Elf_Internal_Rela * free_relocs = NULL; + Elf_Internal_Rela * irel; + Elf_Internal_Rela * irelend; + Elf_Internal_Rela * irelalign = NULL; + bfd_byte * contents = NULL; + bfd_byte * free_contents = NULL; + Elf32_External_Sym * extsyms = NULL; + Elf32_External_Sym * free_extsyms = NULL; + bfd_vma addr = 0; + bfd_vma toaddr; + int align_pad_size = 0; + Elf_Internal_Shdr * shndx_hdr = NULL; + Elf_External_Sym_Shndx * shndx_buf = NULL; + + * again = false; + + if (link_info->relocateable + || (sec->flags & SEC_RELOC) == 0 + || sec->reloc_count == 0) + return true; + + /* If this is the first time we have been called + for this section, initialize the cooked size. */ + if (sec->_cooked_size == 0) + sec->_cooked_size = sec->_raw_size; + + symtab_hdr = & elf_tdata (abfd)->symtab_hdr; + + internal_relocs = (_bfd_elf32_link_read_relocs + (abfd, sec, (PTR) NULL, (Elf_Internal_Rela *) NULL, + link_info->keep_memory)); + if (internal_relocs == NULL) + goto error_return; + if (! link_info->keep_memory) + free_relocs = internal_relocs; + + irelend = internal_relocs + sec->reloc_count; + + while (addr < sec->_cooked_size) + { + toaddr = sec->_cooked_size; + + for (irel = internal_relocs; irel < irelend; irel ++) + if (ELF32_R_TYPE (irel->r_info) == (int) R_V850_ALIGN + && irel->r_offset > addr + && irel->r_offset < toaddr) + toaddr = irel->r_offset; + +#ifdef DEBUG_RELAX + fprintf (stderr, "relax region 0x%x to 0x%x align pad %d\n", + addr, toaddr, align_pad_size); +#endif + if (irelalign) + { + bfd_vma alignto; + bfd_vma alignmoveto; + + alignmoveto = BFD_ALIGN (addr - align_pad_size, 1 << irelalign->r_addend); + alignto = BFD_ALIGN (addr, 1 << irelalign->r_addend); + + if (alignmoveto < alignto) + { + unsigned int i; + + align_pad_size = alignto - alignmoveto; +#ifdef DEBUG_RELAX + fprintf (stderr, "relax move region 0x%x to 0x%x delete size 0x%x\n", + alignmoveto, toaddr, align_pad_size); +#endif + if (!v850_elf_relax_delete_bytes (abfd, sec, alignmoveto, + toaddr, align_pad_size)) + goto error_return; + + for (i = BFD_ALIGN (toaddr - align_pad_size, 1); + (i + 1) < toaddr; i += 2) + bfd_put_16 (abfd, NOP_OPCODE, contents + i); + + addr = alignmoveto; + } + else + align_pad_size = 0; + } + + for (irel = internal_relocs; irel < irelend; irel++) + { + bfd_vma laddr; + bfd_vma addend; + bfd_vma symval; + int insn[5]; + int no_match = -1; + Elf_Internal_Rela * hi_irelfn; + Elf_Internal_Rela * lo_irelfn; + Elf_Internal_Rela * irelcall; + bfd_signed_vma foff; + + if (! (irel->r_offset >= addr && irel->r_offset < toaddr + && (ELF32_R_TYPE (irel->r_info) == (int) R_V850_LONGCALL + || ELF32_R_TYPE (irel->r_info) == (int) R_V850_LONGJUMP))) + continue; + +#ifdef DEBUG_RELAX + fprintf (stderr, "relax check r_info 0x%x r_offset 0x%x r_addend 0x%x\n", + irel->r_info, + irel->r_offset, + irel->r_addend ); +#endif + + /* Get the section contents. */ + if (contents == NULL) + { + if (elf_section_data (sec)->this_hdr.contents != NULL) + contents = elf_section_data (sec)->this_hdr.contents; + else + { + contents = (bfd_byte *) bfd_malloc (sec->_raw_size); + if (contents == NULL) + goto error_return; + + free_contents = contents; + + if (! bfd_get_section_contents (abfd, sec, contents, + (file_ptr) 0, sec->_raw_size)) + goto error_return; + } + } + + /* Read this BFD's symbols if we haven't done so already. */ + if (extsyms == NULL) + { + /* Get cached copy if it exists. */ + if (symtab_hdr->contents != NULL) + extsyms = (Elf32_External_Sym *) symtab_hdr->contents; + else + { + /* Go get them off disk. */ + bfd_size_type amt; + + amt = symtab_hdr->sh_info; + amt *= sizeof (Elf32_External_Sym); + extsyms = (Elf32_External_Sym *) bfd_malloc (amt); + if (extsyms == NULL) + goto error_return; + free_extsyms = extsyms; + if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0 + || bfd_bread ((PTR) extsyms, amt, abfd) != amt) + goto error_return; + } + + if (shndx_hdr->sh_size != 0) + { + bfd_size_type amt; + + amt = symtab_hdr->sh_info; + amt *= sizeof (Elf_External_Sym_Shndx); + shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt); + if (shndx_buf == NULL) + goto error_return; + if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0 + || bfd_bread ((PTR) shndx_buf, amt, abfd) != amt) + goto error_return; + } + } + + laddr = irel->r_offset; + + if (ELF32_R_TYPE (irel->r_info) == (int) R_V850_LONGCALL) + { + /* Check code for -mlong-calls output. */ + if (laddr + 16 <= (bfd_vma) sec->_raw_size) + { + insn[0] = bfd_get_16 (abfd, contents + laddr); + insn[1] = bfd_get_16 (abfd, contents + laddr + 4); + insn[2] = bfd_get_32 (abfd, contents + laddr + 8); + insn[3] = bfd_get_16 (abfd, contents + laddr + 12); + insn[4] = bfd_get_16 (abfd, contents + laddr + 14); + + if ((insn[0] & MOVHI_MASK) != MOVHI + || MOVHI_R1 (insn[0]) != 0) + no_match = 0; + + if (no_match < 0 + && ((insn[1] & MOVEA_MASK) != MOVEA + || MOVHI_R2 (insn[0]) != MOVEA_R1 (insn[1]))) + no_match = 1; + + if (no_match < 0 + && (insn[2] & JARL_4_MASK) != JARL_4) + no_match = 2; + + if (no_match < 0 + && ((insn[3] & ADD_I_MASK) != ADD_I + || ADD_I5 (insn[3]) != 4 + || JARL_R2 (insn[2]) != ADD_R2 (insn[3]))) + no_match = 3; + + if (no_match < 0 + && ((insn[4] & JMP_R_MASK) != JMP_R + || MOVEA_R2 (insn[1]) != JMP_R1 (insn[4]))) + no_match = 4; + } + else + { + ((*_bfd_error_handler) + ("%s: 0x%lx: warning: R_V850_LONGCALL points to unrecognized insns", + bfd_get_filename (abfd), (unsigned long) irel->r_offset)); + + continue; + } + + if (no_match >= 0) + { + ((*_bfd_error_handler) + ("%s: 0x%lx: warning: R_V850_LONGCALL points to unrecognized insn 0x%x", + bfd_get_filename (abfd), (unsigned long) irel->r_offset+no_match, insn[no_match])); + + continue; + } + + /* Get the reloc for the address from which the register is + being loaded. This reloc will tell us which function is + actually being called. */ + for (hi_irelfn = internal_relocs; hi_irelfn < irelend; hi_irelfn ++) + if (hi_irelfn->r_offset == laddr + 2 + && ELF32_R_TYPE (hi_irelfn->r_info) + == (int) R_V850_HI16_S) + break; + + for (lo_irelfn = internal_relocs; lo_irelfn < irelend; lo_irelfn ++) + if (lo_irelfn->r_offset == laddr + 6 + && ELF32_R_TYPE (lo_irelfn->r_info) + == (int) R_V850_LO16) + break; + + for (irelcall = internal_relocs; irelcall < irelend; irelcall ++) + if (irelcall->r_offset == laddr + 8 + && ELF32_R_TYPE (irelcall->r_info) + == (int) R_V850_22_PCREL) + break; + + if ( hi_irelfn == irelend + || lo_irelfn == irelend + || irelcall == irelend) + { + ((*_bfd_error_handler) + ("%s: 0x%lx: warning: R_V850_LONGCALL points to unrecognized reloc", + bfd_get_filename (abfd), (unsigned long) irel->r_offset )); + + continue; + } + + if (ELF32_R_SYM (irelcall->r_info) < symtab_hdr->sh_info) + { + unsigned int r_index; + Elf_Internal_Sym isym; + asection * sym_sec; + Elf32_External_Sym * esym; + Elf_External_Sym_Shndx * shndx; + + /* A local symbol. */ + r_index = ELF32_R_SYM (irelcall->r_info); + esym = extsyms + r_index; + shndx = shndx_buf + (shndx_buf ? r_index : 0); + bfd_elf32_swap_symbol_in (abfd, esym, shndx, & isym); + + if (isym.st_shndx == SHN_UNDEF) + sym_sec = bfd_und_section_ptr; + else if (isym.st_shndx == SHN_ABS) + sym_sec = bfd_abs_section_ptr; + else if (isym.st_shndx == SHN_COMMON) + sym_sec = bfd_com_section_ptr; + else + sym_sec = bfd_section_from_elf_index (abfd, isym.st_shndx); + symval = isym.st_value; + } + else + { + unsigned long indx; + struct elf_link_hash_entry * h; + + /* An external symbol. */ + indx = ELF32_R_SYM (irelcall->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + 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; + + symval = h->root.u.def.value; + } + + if (symval + irelcall->r_addend != irelcall->r_offset + 4) + { + ((*_bfd_error_handler) + ("%s: 0x%lx: warning: R_V850_LONGCALL points to unrecognized reloc 0x%lx", + bfd_get_filename (abfd), (unsigned long) irel->r_offset, irelcall->r_offset )); + + continue; + } + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (hi_irelfn->r_info) < symtab_hdr->sh_info) + { + unsigned int r_index; + Elf_Internal_Sym isym; + asection * sym_sec; + Elf32_External_Sym * esym; + Elf_External_Sym_Shndx * shndx; + + /* A local symbol. */ + r_index = ELF32_R_SYM (irel->r_info); + esym = extsyms + r_index; + shndx = shndx_buf + (shndx_buf ? r_index : 0); + bfd_elf32_swap_symbol_in (abfd, esym, shndx, & isym); + + if (isym.st_shndx == SHN_UNDEF) + sym_sec = bfd_und_section_ptr; + else if (isym.st_shndx == SHN_ABS) + sym_sec = bfd_abs_section_ptr; + else if (isym.st_shndx == SHN_COMMON) + sym_sec = bfd_com_section_ptr; + else + sym_sec = bfd_section_from_elf_index (abfd, isym.st_shndx); + symval = (isym.st_value + + sym_sec->output_section->vma + + sym_sec->output_offset); + } + else + { + unsigned long indx; + struct elf_link_hash_entry * h; + + /* An external symbol. */ + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + 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; + + symval = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + + addend = irel->r_addend; + + foff = (symval + addend + - (irel->r_offset + + sec->output_section->vma + + sec->output_offset + + 4)); +#ifdef DEBUG_RELAX + fprintf (stderr, "relax longcall r_offset 0x%x ptr 0x%x symbol 0x%x addend 0x%x distance 0x%x\n", + irel->r_offset, + (irel->r_offset + + sec->output_section->vma + + sec->output_offset), + symval, addend, foff); +#endif + + if (foff < -0x100000 || foff >= 0x100000) + /* After all that work, we can't shorten this function call. */ + continue; + + /* For simplicity of coding, we are going to modify the section + contents, the section relocs, and the BFD symbol table. We + must tell the rest of the code not to free up this + information. It would be possible to instead create a table + of changes which have to be made, as is done in coff-mips.c; + that would be more work, but would require less memory when + the linker is run. */ + elf_section_data (sec)->relocs = internal_relocs; + free_relocs = NULL; + + elf_section_data (sec)->this_hdr.contents = contents; + free_contents = NULL; + + symtab_hdr->contents = (bfd_byte *) extsyms; + free_extsyms = NULL; + + /* Replace the long call with a jarl. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_V850_22_PCREL); + + addend = 0; + + if (ELF32_R_SYM (hi_irelfn->r_info) < symtab_hdr->sh_info) + /* If this needs to be changed because of future relaxing, + it will be handled here like other internal IND12W + relocs. */ + bfd_put_32 (abfd, + 0x00000780 | (JARL_R2 (insn[2])<<11) | ((addend << 16) & 0xffff) | ((addend >> 16) & 0xf), + contents + irel->r_offset); + else + /* We can't fully resolve this yet, because the external + symbol value may be changed by future relaxing. + We let the final link phase handle it. */ + bfd_put_32 (abfd, 0x00000780 | (JARL_R2 (insn[2])<<11), + contents + irel->r_offset); + + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_V850_NONE); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_V850_NONE); + irelcall->r_info = + ELF32_R_INFO (ELF32_R_SYM (irelcall->r_info), R_V850_NONE); + + if (! v850_elf_relax_delete_bytes (abfd, sec, + irel->r_offset + 4, toaddr, 12)) + goto error_return; + + align_pad_size += 12; + } + else if (ELF32_R_TYPE (irel->r_info) == (int) R_V850_LONGJUMP) + { + /* Check code for -mlong-jumps output. */ + if (laddr + 10 <= (bfd_vma) sec->_raw_size) + { + insn[0] = bfd_get_16 (abfd, contents + laddr); + insn[1] = bfd_get_16 (abfd, contents + laddr + 4); + insn[2] = bfd_get_16 (abfd, contents + laddr + 8); + + if ((insn[0] & MOVHI_MASK) != MOVHI + || MOVHI_R1 (insn[0]) != 0) + no_match = 0; + + if (no_match < 0 + && ((insn[1] & MOVEA_MASK) != MOVEA + || MOVHI_R2 (insn[0]) != MOVEA_R1 (insn[1]))) + no_match = 1; + + if (no_match < 0 + && ((insn[2] & JMP_R_MASK) != JMP_R + || MOVEA_R2 (insn[1]) != JMP_R1 (insn[2]))) + no_match = 4; + } + else + { + ((*_bfd_error_handler) + ("%s: 0x%lx: warning: R_V850_LONGJUMP points to unrecognized insns", + bfd_get_filename (abfd), (unsigned long) irel->r_offset)); + + continue; + } + + if (no_match >= 0) + { + ((*_bfd_error_handler) + ("%s: 0x%lx: warning: R_V850_LONGJUMP points to unrecognized insn 0x%x", + bfd_get_filename (abfd), (unsigned long) irel->r_offset+no_match, insn[no_match])); + + continue; + } + + /* Get the reloc for the address from which the register is + being loaded. This reloc will tell us which function is + actually being called. */ + for (hi_irelfn = internal_relocs; hi_irelfn < irelend; hi_irelfn ++) + if (hi_irelfn->r_offset == laddr + 2 + && ELF32_R_TYPE (hi_irelfn->r_info) == (int) R_V850_HI16_S) + break; + + for (lo_irelfn = internal_relocs; lo_irelfn < irelend; lo_irelfn ++) + if (lo_irelfn->r_offset == laddr + 6 + && ELF32_R_TYPE (lo_irelfn->r_info) == (int) R_V850_LO16) + break; + + if ( hi_irelfn == irelend + || lo_irelfn == irelend) + { + ((*_bfd_error_handler) + ("%s: 0x%lx: warning: R_V850_LONGJUMP points to unrecognized reloc", + bfd_get_filename (abfd), (unsigned long) irel->r_offset )); + + continue; + } + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (hi_irelfn->r_info) < symtab_hdr->sh_info) + { + unsigned int r_index; + Elf_Internal_Sym isym; + asection * sym_sec; + Elf32_External_Sym * esym; + Elf_External_Sym_Shndx * shndx; + + /* A local symbol. */ + r_index = ELF32_R_SYM (irel->r_info); + esym = extsyms + r_index; + shndx = shndx_buf + (shndx_buf ? r_index : 0); + bfd_elf32_swap_symbol_in (abfd, esym, shndx, & isym); + + if (isym.st_shndx == SHN_UNDEF) + sym_sec = bfd_und_section_ptr; + else if (isym.st_shndx == SHN_ABS) + sym_sec = bfd_abs_section_ptr; + else if (isym.st_shndx == SHN_COMMON) + sym_sec = bfd_com_section_ptr; + else + sym_sec = bfd_section_from_elf_index (abfd, isym.st_shndx); + symval = (isym.st_value + + sym_sec->output_section->vma + + sym_sec->output_offset); +#ifdef DEBUG_RELAX + { + char * name = bfd_elf_string_from_elf_section + (abfd, symtab_hdr->sh_link, isym.st_name); + + fprintf (stderr, "relax long jump local: sec: %s, sym: %s (%d), value: %x + %x + %x addend %x\n", + sym_sec->name, name, isym.st_name, + sym_sec->output_section->vma, sym_sec->output_offset, + isym.st_value, irel->r_addend); + } +#endif + } + else + { + unsigned long indx; + struct elf_link_hash_entry * h; + + /* An external symbol. */ + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + 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; + + symval = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); +#ifdef DEBUG_RELAX + fprintf (stderr, + "relax longjump defined: sec: %s, name: %s, value: %x + %x + %x addend %x\n", + sec->name, h->root.root.string, h->root.u.def.value, + sec->output_section->vma, sec->output_offset, irel->r_addend); +#endif + } + + addend = irel->r_addend; + + foff = (symval + addend + - (irel->r_offset + + sec->output_section->vma + + sec->output_offset + + 4)); +#ifdef DEBUG_RELAX + fprintf (stderr, "relax longjump r_offset 0x%x ptr 0x%x symbol 0x%x addend 0x%x distance 0x%x\n", + irel->r_offset, + (irel->r_offset + + sec->output_section->vma + + sec->output_offset), + symval, addend, foff); +#endif + if (foff < -0x100000 || foff >= 0x100000) + /* After all that work, we can't shorten this function call. */ + continue; + + /* For simplicity of coding, we are going to modify the section + contents, the section relocs, and the BFD symbol table. We + must tell the rest of the code not to free up this + information. It would be possible to instead create a table + of changes which have to be made, as is done in coff-mips.c; + that would be more work, but would require less memory when + the linker is run. */ + elf_section_data (sec)->relocs = internal_relocs; + free_relocs = NULL; + + elf_section_data (sec)->this_hdr.contents = contents; + free_contents = NULL; + + symtab_hdr->contents = (bfd_byte *) extsyms; + free_extsyms = NULL; + + if (foff < -0x100 || foff >= 0x100) + { + /* Replace the long jump with a jr. */ + + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_V850_22_PCREL); + + irel->r_addend = addend; + addend = 0; + + if (ELF32_R_SYM (hi_irelfn->r_info) < symtab_hdr->sh_info) + /* If this needs to be changed because of future relaxing, + it will be handled here like other internal IND12W + relocs. */ + bfd_put_32 (abfd, + 0x00000780 | ((addend << 15) & 0xffff0000) | ((addend >> 17) & 0xf), + contents + irel->r_offset); + else + /* We can't fully resolve this yet, because the external + symbol value may be changed by future relaxing. + We let the final link phase handle it. */ + bfd_put_32 (abfd, 0x00000780, contents + irel->r_offset); + + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_V850_NONE); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_V850_NONE); + if (!v850_elf_relax_delete_bytes (abfd, sec, + irel->r_offset + 4, toaddr, 6)) + goto error_return; + + align_pad_size += 6; + } + else + { + /* Replace the long jump with a br. */ + + irel->r_info = + ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_V850_9_PCREL); + + irel->r_addend = addend; + addend = 0; + + if (ELF32_R_SYM (hi_irelfn->r_info) < symtab_hdr->sh_info) + /* If this needs to be changed because of future relaxing, + it will be handled here like other internal IND12W + relocs. */ + bfd_put_16 (abfd, + 0x0585 | ((addend << 10) & 0xf800) | ((addend << 3) & 0x0070), + contents + irel->r_offset); + else + /* We can't fully resolve this yet, because the external + symbol value may be changed by future relaxing. + We let the final link phase handle it. */ + bfd_put_16 (abfd, 0x0585, contents + irel->r_offset); + + hi_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (hi_irelfn->r_info), R_V850_NONE); + lo_irelfn->r_info = + ELF32_R_INFO (ELF32_R_SYM (lo_irelfn->r_info), R_V850_NONE); + if (!v850_elf_relax_delete_bytes (abfd, sec, + irel->r_offset + 2, toaddr, 8)) + goto error_return; + + align_pad_size += 8; + } + } + } + + irelalign = NULL; + for (irel = internal_relocs; irel < irelend; irel++) + { + if (ELF32_R_TYPE (irel->r_info) == (int) R_V850_ALIGN + && irel->r_offset == toaddr) + { + irel->r_offset -= align_pad_size; + + if (irelalign == NULL || irelalign->r_addend > irel->r_addend) + irelalign = irel; + } + } + + addr = toaddr; + } + + if (!irelalign) + { +#ifdef DEBUG_RELAX + fprintf (stderr, "relax pad %d shorten %d -> %d\n", + align_pad_size, + sec->_cooked_size, + sec->_cooked_size - align_pad_size); +#endif + sec->_cooked_size -= align_pad_size; + } + + return true; + + error_return: + if (free_relocs != NULL) + free (free_relocs); + + if (free_contents != NULL) + free (free_contents); + + if (free_extsyms != NULL) + free (free_extsyms); + + return false; +} #define TARGET_LITTLE_SYM bfd_elf32_v850_vec #define TARGET_LITTLE_NAME "elf32-v850" @@ -2198,6 +3208,7 @@ v850_elf_fake_sections (abfd, hdr, sec) #define bfd_elf32_bfd_merge_private_bfd_data v850_elf_merge_private_bfd_data #define bfd_elf32_bfd_set_private_flags v850_elf_set_private_flags #define bfd_elf32_bfd_print_private_bfd_data v850_elf_print_private_bfd_data +#define bfd_elf32_bfd_relax_section v850_elf_relax_section #define elf_symbol_leading_char '_' diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 88beae8a9f..e855db196a 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -1041,7 +1041,9 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_V850_ZDA_16_16_SPLIT_OFFSET", "BFD_RELOC_V850_CALLT_6_7_OFFSET", "BFD_RELOC_V850_CALLT_16_16_OFFSET", - + "BFD_RELOC_V850_LONGCALL", + "BFD_RELOC_V850_LONGJUMP", + "BFD_RELOC_V850_ALIGN", "BFD_RELOC_MN10300_32_PCREL", "BFD_RELOC_MN10300_16_PCREL", "BFD_RELOC_TIC30_LDP", diff --git a/bfd/reloc.c b/bfd/reloc.c index deba952832..e60c26e051 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -2788,8 +2788,18 @@ ENUM BFD_RELOC_V850_CALLT_16_16_OFFSET ENUMDOC This is a 16 bit offset from the call table base pointer. -COMMENT - +ENUM + BFD_RELOC_V850_LONGCALL +ENUMDOC + Used for relaxing indirect function calls. +ENUM + BFD_RELOC_V850_LONGJUMP +ENUMDOC + Used for relaxing indirect jumps. +ENUM + BFD_RELOC_V850_ALIGN +ENUMDOC + Used to maintain alignment whilst relaxing. ENUM BFD_RELOC_MN10300_32_PCREL ENUMDOC diff --git a/gas/ChangeLog b/gas/ChangeLog index e8bf9fea53..a083a87cd3 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,24 @@ +2000-08-28 Catherine Moore + + * tc-v850.c (v850_relax): Declare. + (v850_longcode): New routine. + (v850_handle_align): New routine. + (md_pseudo_table): Add longcall and longjump. + (md_parse_option): Check for relax option. + (tc_gen_reloc): Handle BFD_RELOC_V850_LONGCALL, + BFD_RELOC_V850_LONGJUMP, and BFD_RELOC_V850_ALIGN. + (md_apply_fix3): Likewise. + (v850_force_relocation): Likewise. + (v850_comm): Change the current section. + (md_assemble): Ensure that the correct value is put in the + fixup. + (v850_sdata, v850_tdata, v850_zdata, v850_sbss, v850_tbss, + v850_zbss, v850_rosdata, v850_rozdata): Fix section book keeping. + Remove redundant v850ea support. + * tc-v850.h (HANDLE_ALIGN): Define. + (v850_handle_align): Declare. + * doc/c-v850.c: Document -mrelax, .longcall and .longjump. + 2002-08-28 Svein E. Seldal * configure.in: Add tic4x-coff* and c4x-coff*-coff-coff targets. diff --git a/gas/config/tc-v850.c b/gas/config/tc-v850.c index 2d159cd519..aa789b3dc2 100644 --- a/gas/config/tc-v850.c +++ b/gas/config/tc-v850.c @@ -78,6 +78,8 @@ const relax_typeS md_relax_table[] = { {0x1fffff, -0x200000, 4, 0}, }; +static int v850_relax = 0; + /* Fixups. */ #define MAX_INSN_FIXUPS (5) struct v850_fixup { @@ -394,7 +396,13 @@ v850_comm (area) } else { + segT old_sec; + int old_subsec; + allocate_common: + old_sec = now_seg; + old_subsec = now_subseg; + S_SET_VALUE (symbolP, (valueT) size); S_SET_ALIGN (symbolP, temp); S_SET_EXTERNAL (symbolP); @@ -411,6 +419,9 @@ v850_comm (area) default: abort (); } + + obj_elf_section_change_hook (); + subseg_set (old_sec, old_subsec); } } else @@ -468,10 +479,45 @@ set_machine (number) { case 0: processor_mask = PROCESSOR_V850; break; case bfd_mach_v850e: processor_mask = PROCESSOR_V850E; break; - case bfd_mach_v850ea: processor_mask = PROCESSOR_V850EA; break; } } +static void v850_longcode PARAMS ((int)); + +static void +v850_longcode (type) + int type; +{ + expressionS ex; + + if (! v850_relax) + { + if (type == 1) + as_warn (".longcall pseudo-op seen when not relaxing"); + else + as_warn (".longjump pseudo-op seen when not relaxing"); + } + + expression (&ex); + + if (ex.X_op != O_symbol || ex.X_add_number != 0) + { + as_bad ("bad .longcall format"); + ignore_rest_of_line (); + + return; + } + + if (type == 1) + fix_new_exp (frag_now, frag_now_fix (), 4, & ex, 1, + BFD_RELOC_V850_LONGCALL); + else + fix_new_exp (frag_now, frag_now_fix (), 4, & ex, 1, + BFD_RELOC_V850_LONGJUMP); + + demand_empty_rest_of_line (); +} + /* The target specific pseudo-ops which we support. */ const pseudo_typeS md_pseudo_table[] = { { "sdata", v850_seg, SDATA_SECTION }, @@ -492,9 +538,10 @@ const pseudo_typeS md_pseudo_table[] = { { "call_table_data", v850_seg, CALL_TABLE_DATA_SECTION }, { "call_table_text", v850_seg, CALL_TABLE_TEXT_SECTION }, { "v850e", set_machine, bfd_mach_v850e }, - { "v850ea", set_machine, bfd_mach_v850ea }, { "file", (void (*) PARAMS ((int))) dwarf2_directive_file, 0 }, { "loc", dwarf2_directive_loc, 0 }, + { "longcall", v850_longcode, 1 }, + { "longjump", v850_longcode, 2 }, { NULL, NULL, 0 } }; @@ -1103,8 +1150,9 @@ md_show_usage (stream) fprintf (stream, _(" -mwarn-unsigned-overflow Warn if unsigned immediate values overflow\n")); fprintf (stream, _(" -mv850 The code is targeted at the v850\n")); fprintf (stream, _(" -mv850e The code is targeted at the v850e\n")); - fprintf (stream, _(" -mv850ea The code is targeted at the v850ea\n")); fprintf (stream, _(" -mv850any The code is generic, despite any processor specific instructions\n")); + fprintf (stream, _(" -mrelax Enable relaxation\n")); + } int @@ -1138,19 +1186,16 @@ md_parse_option (c, arg) machine = bfd_mach_v850e; processor_mask = PROCESSOR_V850E; } - else if (strcmp (arg, "v850ea") == 0) - { - machine = bfd_mach_v850ea; - processor_mask = PROCESSOR_V850EA; - } else if (strcmp (arg, "v850any") == 0) { /* Tell the world that this is for any v850 chip. */ machine = 0; /* But support instructions for the extended versions. */ - processor_mask = PROCESSOR_V850EA; + processor_mask = PROCESSOR_V850E; } + else if (strcmp (arg, "relax") == 0) + v850_relax = 1; else { /* xgettext:c-format */ @@ -1275,17 +1320,9 @@ void md_begin () { char *prev_name = ""; - register const struct v850_opcode *op; + const struct v850_opcode *op; - if (strncmp (TARGET_CPU, "v850ea", 6) == 0) - { - if (machine == -1) - machine = bfd_mach_v850ea; - - if (processor_mask == -1) - processor_mask = PROCESSOR_V850EA; - } - else if (strncmp (TARGET_CPU, "v850e", 5) == 0) + if (strncmp (TARGET_CPU, "v850e", 5) == 0) { if (machine == -1) machine = bfd_mach_v850e; @@ -1750,8 +1787,7 @@ md_assemble (str) extra_data_after_insn = true; extra_data_len = 4; - extra_data = ex.X_add_number; - ex.X_add_number = 0; + extra_data = 0; break; default: @@ -2215,12 +2251,32 @@ tc_gen_reloc (seg, fixp) if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY || fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT) reloc->addend = fixp->fx_offset; + else if ( fixp->fx_r_type == BFD_RELOC_V850_LONGCALL + || fixp->fx_r_type == BFD_RELOC_V850_LONGJUMP + || fixp->fx_r_type == BFD_RELOC_V850_ALIGN) + reloc->addend = fixp->fx_offset; else reloc->addend = fixp->fx_addnumber; return reloc; } +void +v850_handle_align (frag) + fragS * frag; +{ + if (v850_relax + && frag->fr_type == rs_align + && frag->fr_address + frag->fr_fix > 0 + && frag->fr_offset > 1 + && now_seg != bss_section + && now_seg != v850_seg_table[SBSS_SECTION].s + && now_seg != v850_seg_table[TBSS_SECTION].s + && now_seg != v850_seg_table[ZBSS_SECTION].s) + fix_new (frag, frag->fr_fix, 2, & abs_symbol, frag->fr_offset, 0, + BFD_RELOC_V850_ALIGN); +} + /* Return current size of variable part of frag. */ int @@ -2261,6 +2317,8 @@ md_apply_fix3 (fixP, valueP, seg) char *where; if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixP->fx_r_type == BFD_RELOC_V850_LONGCALL + || fixP->fx_r_type == BFD_RELOC_V850_LONGJUMP || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) { fixP->fx_done = 0; @@ -2268,10 +2326,11 @@ md_apply_fix3 (fixP, valueP, seg) } if (fixP->fx_addsy == (symbolS *) NULL) + fixP->fx_addnumber = value, fixP->fx_done = 1; else if (fixP->fx_pcrel) - ; + fixP->fx_addnumber = fixP->fx_offset; else { @@ -2287,6 +2346,7 @@ md_apply_fix3 (fixP, valueP, seg) _("expression too complex")); } } + fixP->fx_addnumber = value; } if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED) @@ -2345,8 +2405,6 @@ md_apply_fix3 (fixP, valueP, seg) else if (fixP->fx_size == 4) bfd_putl32 (value, (unsigned char *) where); } - - fixP->fx_addnumber = value; } /* Parse a cons expression. We have to handle hi(), lo(), etc @@ -2430,5 +2488,17 @@ v850_force_relocation (fixP) || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) return 1; + if ( fixP->fx_r_type == BFD_RELOC_V850_LONGCALL + || fixP->fx_r_type == BFD_RELOC_V850_LONGJUMP) + return 1; + + if (v850_relax + && (fixP->fx_pcrel + || fixP->fx_r_type == BFD_RELOC_V850_ALIGN + || fixP->fx_r_type == BFD_RELOC_V850_22_PCREL + || fixP->fx_r_type == BFD_RELOC_V850_9_PCREL + || fixP->fx_r_type >= BFD_RELOC_UNUSED)) + return 1; + return 0; } diff --git a/gas/config/tc-v850.h b/gas/config/tc-v850.h index 8257428eab..f3827e7c9c 100644 --- a/gas/config/tc-v850.h +++ b/gas/config/tc-v850.h @@ -70,6 +70,11 @@ extern void cons_fix_new_v850 PARAMS ((fragS *, int, int, expressionS *)); #define TC_GENERIC_RELAX_TABLE md_relax_table extern const struct relax_type md_relax_table[]; +/* When relaxing, we need to generate + relocations for alignment directives. */ +#define HANDLE_ALIGN(frag) v850_handle_align (frag) +extern void v850_handle_align PARAMS ((fragS *)); + /* This section must be in the small data area (pointed to by GP). */ #define SHF_V850_GPREL 0x10000000 /* This section must be in the tiny data area (pointed to by EP). */ diff --git a/gas/doc/c-v850.texi b/gas/doc/c-v850.texi index 4b36461ee5..f67ad5c743 100644 --- a/gas/doc/c-v850.texi +++ b/gas/doc/c-v850.texi @@ -62,6 +62,14 @@ routines used by the code produced by GCC for all versions of the v850 architecture, together with support routines only used by the V850E architecture. +@cindex @code{-mrelax} command line option, V850 +@item -mrelax +Enables relaxation. This allows the .longcall and .longjump pseudo +ops to be used in the assembler source code. These ops label sections +of code which are either a long function call or a long branch. The +assembler will then flag these sections of code and the linker will +attempt to relax them. + @end table @@ -354,10 +362,23 @@ example: will put the call the function whoes address is held in the call table at the location labeled 'table_func1'. +@cindex @code{longcall} pseudo-op, V850 +@item .longcall @code{name} +Indicates that the following sequence of instructions is a long call +to function @code{name}. The linker will attempt to shorten this call +sequence if @code{name} is within a 22bit offset of the call. Only +valid if the @code{-mrelax} command line switch has been enabled. + +@cindex @code{longjump} pseudo-op, V850 +@item .longjump @code{name} +Indicates that the following sequence of instructions is a long jump +to label @code{name}. The linker will attempt to shorten this code +sequence if @code{name} is within a 22bit offset of the jump. Only +valid if the @code{-mrelax} command line switch has been enabled. + @end table For information on the V850 instruction set, see @cite{V850 Family 32-/16-Bit single-Chip Microcontroller Architecture Manual} from NEC. Ltd. - diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog index a7dfa25965..0ffa6ab9d3 100644 --- a/include/elf/ChangeLog +++ b/include/elf/ChangeLog @@ -1,3 +1,8 @@ +2002-08-28 Catherine Moore + + * elf/v850.h (R_V850_LONGCALL, R_V850_ALIGN, + R_V850_LONGJUMP): New relocations. + 2002-08-15 Alan Modra * i370.h: Define relocs using reloc-macros.h. diff --git a/include/elf/v850.h b/include/elf/v850.h index 62b9541e8e..a15c9ee086 100644 --- a/include/elf/v850.h +++ b/include/elf/v850.h @@ -1,22 +1,22 @@ /* V850 ELF support for BFD. - Copyright 1997, 1998, 2000 Free Software Foundation, Inc. + Copyright 1997, 1998, 2000, 2002 Free Software Foundation, Inc. Created by Michael Meissner, Cygnus Support -This file is part of BFD, the Binary File Descriptor library. + This file is part of BFD, the Binary File Descriptor library. -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This file holds definitions specific to the MIPS ELF ABI. Note that most of this is not actually implemented by BFD. */ @@ -35,18 +35,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* v850e code. */ #define E_V850E_ARCH 0x10000000 -/* v850ea code. */ -#define E_V850EA_ARCH 0x20000000 +/* Flags for the st_other field. */ +#define V850_OTHER_SDA 0x01 /* Symbol had SDA relocations. */ +#define V850_OTHER_ZDA 0x02 /* Symbol had ZDA relocations. */ +#define V850_OTHER_TDA 0x04 /* Symbol had TDA relocations. */ +#define V850_OTHER_TDA_BYTE 0x08 /* Symbol had TDA byte relocations. */ +#define V850_OTHER_ERROR 0x80 /* Symbol had an error reported. */ -/* Flags for the st_other field */ -#define V850_OTHER_SDA 0x01 /* symbol had SDA relocations */ -#define V850_OTHER_ZDA 0x02 /* symbol had ZDA relocations */ -#define V850_OTHER_TDA 0x04 /* symbol had TDA relocations */ -#define V850_OTHER_TDA_BYTE 0x08 /* symbol had TDA byte relocations */ -#define V850_OTHER_ERROR 0x80 /* symbol had an error reported */ - -/* V850 relocations */ +/* V850 relocations. */ #include "elf/reloc-macros.h" START_RELOC_NUMBERS (v850_reloc_type) @@ -75,6 +72,9 @@ START_RELOC_NUMBERS (v850_reloc_type) RELOC_NUMBER( R_V850_CALLT_16_16_OFFSET, 22) /* For callt */ RELOC_NUMBER (R_V850_GNU_VTINHERIT, 23) RELOC_NUMBER (R_V850_GNU_VTENTRY, 24) + RELOC_NUMBER (R_V850_LONGCALL, 25) + RELOC_NUMBER (R_V850_LONGJUMP, 26) + RELOC_NUMBER (R_V850_ALIGN, 27) END_RELOC_NUMBERS (R_V850_max) @@ -103,5 +103,4 @@ END_RELOC_NUMBERS (R_V850_max) /* Section contains the .scommon data. */ #define SHT_V850_ZCOMMON 0x70000002 - #endif /* _ELF_V850_H */