+ 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;
+
+ if (! bfd_get_section_contents (abfd, sec, contents,
+ (file_ptr) 0, sec->_raw_size))
+ goto error_return;
+ }
+ }
+
+ /* Read this BFD's local symbols if we haven't done so already. */
+ if (isymbuf == NULL && symtab_hdr->sh_info != 0)
+ {
+ isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (isymbuf == NULL)
+ isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+ symtab_hdr->sh_info, 0,
+ NULL, NULL, NULL);
+ if (isymbuf == NULL)
+ 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)
+ {
+ Elf_Internal_Sym * isym;
+
+ /* A local symbol. */
+ isym = isymbuf + ELF32_R_SYM (irelcall->r_info);
+
+ 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)
+ {
+ Elf_Internal_Sym * isym;
+ asection * sym_sec;
+
+ /* A local symbol. */
+ isym = isymbuf + ELF32_R_SYM (hi_irelfn->r_info);
+
+ 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 (hi_irelfn->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;
+ elf_section_data (sec)->this_hdr.contents = contents;
+ symtab_hdr->contents = (bfd_byte *) isymbuf;
+
+ /* 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)
+ {
+ Elf_Internal_Sym * isym;
+ asection * sym_sec;
+
+ /* A local symbol. */
+ isym = isymbuf + ELF32_R_SYM (hi_irelfn->r_info);
+
+ 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;
+ elf_section_data (sec)->this_hdr.contents = contents;
+ symtab_hdr->contents = (bfd_byte *) isymbuf;
+
+ 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);