+ else
+ {
+ fprintf (stderr, _("FAILED to find previous HI16 reloc\n"));
+ return bfd_reloc_overflow;
+ }
+ }
+
+ /* Do not complain if value has top bit set, as this has been anticipated. */
+ insn = result & 0xffff;
+ break;
+ }
+
+ case R_V850_8:
+ addend += (char) bfd_get_8 (abfd, address);
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0x7f || saddend < -0x80)
+ return bfd_reloc_overflow;
+
+ bfd_put_8 (abfd, addend, address);
+ return bfd_reloc_ok;
+
+ case R_V850_CALLT_16_16_OFFSET:
+ addend += bfd_get_16 (abfd, address);
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0xffff || saddend < 0)
+ return bfd_reloc_overflow;
+
+ insn = addend;
+ break;
+
+ case R_V850_16:
+
+ /* drop through */
+ case R_V850_SDA_16_16_OFFSET:
+ case R_V850_ZDA_16_16_OFFSET:
+ case R_V850_TDA_16_16_OFFSET:
+ addend += bfd_get_16 (abfd, address);
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0x7fff || saddend < -0x8000)
+ return bfd_reloc_overflow;
+
+ insn = addend;
+ break;
+
+ case R_V850_SDA_15_16_OFFSET:
+ case R_V850_ZDA_15_16_OFFSET:
+ insn = bfd_get_16 (abfd, address);
+ addend += (insn & 0xfffe);
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0x7ffe || saddend < -0x8000)
+ return bfd_reloc_overflow;
+
+ if (addend & 1)
+ return bfd_reloc_dangerous;
+
+ insn = (addend & ~1) | (insn & 1);
+ break;
+
+ case R_V850_TDA_6_8_OFFSET:
+ insn = bfd_get_16 (abfd, address);
+ addend += ((insn & 0x7e) << 1);
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0xfc || saddend < 0)
+ return bfd_reloc_overflow;
+
+ if (addend & 3)
+ return bfd_reloc_dangerous;
+
+ insn &= 0xff81;
+ insn |= (addend >> 1);
+ break;
+
+ case R_V850_TDA_7_8_OFFSET:
+ insn = bfd_get_16 (abfd, address);
+ addend += ((insn & 0x7f) << 1);
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0xfe || saddend < 0)
+ return bfd_reloc_overflow;
+
+ if (addend & 1)
+ return bfd_reloc_dangerous;
+
+ insn &= 0xff80;
+ insn |= (addend >> 1);
+ break;
+
+ case R_V850_TDA_7_7_OFFSET:
+ insn = bfd_get_16 (abfd, address);
+ addend += insn & 0x7f;
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0x7f || saddend < 0)
+ return bfd_reloc_overflow;
+
+ insn &= 0xff80;
+ insn |= addend;
+ break;
+
+ case R_V850_TDA_4_5_OFFSET:
+ insn = bfd_get_16 (abfd, address);
+ addend += ((insn & 0xf) << 1);
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0x1e || saddend < 0)
+ return bfd_reloc_overflow;
+
+ if (addend & 1)
+ return bfd_reloc_dangerous;
+
+ insn &= 0xfff0;
+ insn |= (addend >> 1);
+ break;
+
+ case R_V850_TDA_4_4_OFFSET:
+ insn = bfd_get_16 (abfd, address);
+ addend += insn & 0xf;
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0xf || saddend < 0)
+ return bfd_reloc_overflow;
+
+ insn &= 0xfff0;
+ insn |= addend;
+ break;
+
+ case R_V850_ZDA_16_16_SPLIT_OFFSET:
+ case R_V850_SDA_16_16_SPLIT_OFFSET:
+ insn = bfd_get_32 (abfd, address);
+ addend += ((insn & 0xfffe0000) >> 16) + ((insn & 0x20) >> 5);
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0x7fff || saddend < -0x8000)
+ return bfd_reloc_overflow;
+
+ insn &= 0x0001ffdf;
+ insn |= (addend & 1) << 5;
+ insn |= (addend & ~1) << 16;
+
+ bfd_put_32 (abfd, insn, address);
+ return bfd_reloc_ok;
+
+ case R_V850_CALLT_6_7_OFFSET:
+ insn = bfd_get_16 (abfd, address);
+ addend += ((insn & 0x3f) << 1);
+
+ saddend = (bfd_signed_vma) addend;
+
+ if (saddend > 0x7e || saddend < 0)
+ return bfd_reloc_overflow;
+
+ if (addend & 1)
+ return bfd_reloc_dangerous;
+
+ insn &= 0xff80;
+ insn |= (addend >> 1);
+ break;
+
+ case R_V850_GNU_VTINHERIT:
+ case R_V850_GNU_VTENTRY:
+ return bfd_reloc_ok;
+
+ }
+
+ bfd_put_16 (abfd, insn, address);
+ return bfd_reloc_ok;
+}
+\f
+/* Insert the addend into the instruction. */
+
+static bfd_reloc_status_type
+v850_elf_reloc (abfd, reloc, symbol, data, isection, obfd, err)
+ bfd * abfd ATTRIBUTE_UNUSED;
+ arelent * reloc;
+ asymbol * symbol;
+ PTR data ATTRIBUTE_UNUSED;
+ asection * isection;
+ bfd * obfd;
+ char ** err ATTRIBUTE_UNUSED;
+{
+ long relocation;
+
+ /* If there is an output BFD,
+ and the symbol is not a section name (which is only defined at final link time),
+ and either we are not putting the addend into the instruction
+ or the addend is zero, so there is nothing to add into the instruction
+ then just fixup the address and return. */
+ if (obfd != (bfd *) NULL
+ && (symbol->flags & BSF_SECTION_SYM) == 0
+ && (! reloc->howto->partial_inplace
+ || reloc->addend == 0))
+ {
+ reloc->address += isection->output_offset;
+ return bfd_reloc_ok;
+ }
+#if 0
+ else if (obfd != NULL)
+ return bfd_reloc_continue;
+#endif
+
+ /* Catch relocs involving undefined symbols. */
+ if (bfd_is_und_section (symbol->section)
+ && (symbol->flags & BSF_WEAK) == 0
+ && obfd == NULL)
+ return bfd_reloc_undefined;
+
+ /* We handle final linking of some relocs ourselves. */
+
+ /* Is the address of the relocation really within the section? */
+ if (reloc->address > isection->_cooked_size)
+ return bfd_reloc_outofrange;
+
+ /* Work out which section the relocation is targetted at and the
+ initial relocation command value. */
+
+ /* Get symbol value. (Common symbols are special.) */
+ if (bfd_is_com_section (symbol->section))
+ relocation = 0;
+ else
+ relocation = symbol->value;
+
+ /* Convert input-section-relative symbol value to absolute + addend. */
+ relocation += symbol->section->output_section->vma;
+ relocation += symbol->section->output_offset;
+ relocation += reloc->addend;
+
+#if 0 /* Since this reloc is going to be processed later on, we should
+ not make it pc-relative here. To test this, try assembling and
+ linking this program:
+
+ .text
+ .globl _start
+ nop
+ _start:
+ jr foo
+
+ .section ".foo","ax"
+ nop
+ foo:
+ nop */
+ if (reloc->howto->pc_relative == true)
+ {
+ /* Here the variable relocation holds the final address of the
+ symbol we are relocating against, plus any addend. */
+ relocation -= isection->output_section->vma + isection->output_offset;
+
+ /* Deal with pcrel_offset. */
+ relocation -= reloc->address;
+ }
+#endif
+ reloc->addend = relocation;
+ return bfd_reloc_ok;
+}
+\f
+static boolean
+v850_elf_is_local_label_name (abfd, name)
+ bfd * abfd ATTRIBUTE_UNUSED;
+ const char * name;
+{
+ return ( (name[0] == '.' && (name[1] == 'L' || name[1] == '.'))
+ || (name[0] == '_' && name[1] == '.' && name[2] == 'L' && name[3] == '_'));
+}
+\f
+/* Perform a relocation as part of a final link. */
+
+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)
+ reloc_howto_type * howto;
+ bfd * input_bfd;
+ bfd * output_bfd ATTRIBUTE_UNUSED;
+ asection * input_section;
+ bfd_byte * contents;
+ bfd_vma offset;
+ bfd_vma value;
+ bfd_vma addend;
+ struct bfd_link_info * info;
+ asection * sym_sec;
+ int is_local ATTRIBUTE_UNUSED;
+{
+ unsigned long r_type = howto->type;
+ bfd_byte * hit_data = contents + offset;
+
+ /* Adjust the value according to the relocation. */
+ switch (r_type)
+ {
+ case R_V850_9_PCREL:
+ value -= (input_section->output_section->vma
+ + input_section->output_offset);
+ value -= offset;
+ break;
+
+ case R_V850_22_PCREL:
+ value -= (input_section->output_section->vma
+ + input_section->output_offset
+ + offset);
+
+ /* If the sign extension will corrupt the value then we have overflowed. */
+ if (((value & 0xff000000) != 0x0) && ((value & 0xff000000) != 0xff000000))
+ return bfd_reloc_overflow;
+
+ /* Only the bottom 24 bits of the PC are valid */
+ value = SEXT24 (value);
+ break;
+
+ case R_V850_HI16_S:
+ case R_V850_HI16:
+ case R_V850_LO16:
+ case R_V850_16:
+ case R_V850_32:
+ case R_V850_8:
+ break;
+
+ case R_V850_ZDA_15_16_OFFSET:
+ case R_V850_ZDA_16_16_OFFSET:
+ case R_V850_ZDA_16_16_SPLIT_OFFSET:
+ if (sym_sec == NULL)
+ return bfd_reloc_undefined;
+
+ value -= sym_sec->output_section->vma;
+ break;
+
+ case R_V850_SDA_15_16_OFFSET:
+ case R_V850_SDA_16_16_OFFSET:
+ case R_V850_SDA_16_16_SPLIT_OFFSET:
+ {
+ unsigned long gp;
+ struct bfd_link_hash_entry * h;
+
+ if (sym_sec == NULL)
+ return bfd_reloc_undefined;
+
+ /* Get the value of __gp. */
+ h = bfd_link_hash_lookup (info->hash, "__gp", false, false, true);
+ if (h == (struct bfd_link_hash_entry *) NULL
+ || h->type != bfd_link_hash_defined)
+ return bfd_reloc_other;
+
+ gp = (h->u.def.value
+ + h->u.def.section->output_section->vma
+ + h->u.def.section->output_offset);
+
+ value -= sym_sec->output_section->vma;
+ value -= (gp - sym_sec->output_section->vma);
+ }
+ break;
+
+ case R_V850_TDA_4_4_OFFSET:
+ case R_V850_TDA_4_5_OFFSET:
+ case R_V850_TDA_16_16_OFFSET:
+ case R_V850_TDA_7_7_OFFSET:
+ case R_V850_TDA_7_8_OFFSET:
+ case R_V850_TDA_6_8_OFFSET:
+ {
+ unsigned long ep;
+ struct bfd_link_hash_entry * h;
+
+ /* Get the value of __ep. */
+ h = bfd_link_hash_lookup (info->hash, "__ep", false, false, true);
+ if (h == (struct bfd_link_hash_entry *) NULL
+ || h->type != bfd_link_hash_defined)
+ /* Actually this indicates that __ep could not be found. */
+ return bfd_reloc_continue;
+
+ ep = (h->u.def.value
+ + h->u.def.section->output_section->vma
+ + h->u.def.section->output_offset);
+
+ value -= ep;
+ }
+ break;
+
+ case R_V850_CALLT_6_7_OFFSET:
+ {
+ unsigned long ctbp;
+ struct bfd_link_hash_entry * h;
+
+ /* Get the value of __ctbp. */
+ h = bfd_link_hash_lookup (info->hash, "__ctbp", false, false, true);
+ if (h == (struct bfd_link_hash_entry *) NULL
+ || h->type != bfd_link_hash_defined)
+ /* Actually this indicates that __ctbp could not be found. */
+ return bfd_reloc_dangerous + 1;
+
+ ctbp = (h->u.def.value
+ + h->u.def.section->output_section->vma
+ + h->u.def.section->output_offset);
+ value -= ctbp;
+ }
+ break;
+
+ case R_V850_CALLT_16_16_OFFSET:
+ {
+ unsigned long ctbp;
+ struct bfd_link_hash_entry * h;
+
+ if (sym_sec == NULL)
+ return bfd_reloc_undefined;
+
+ /* Get the value of __ctbp. */
+ h = bfd_link_hash_lookup (info->hash, "__ctbp", false, false, true);
+ if (h == (struct bfd_link_hash_entry *) NULL
+ || h->type != bfd_link_hash_defined)
+ return (bfd_reloc_dangerous + 1);
+
+ ctbp = (h->u.def.value
+ + h->u.def.section->output_section->vma
+ + h->u.def.section->output_offset);
+
+ value -= sym_sec->output_section->vma;
+ value -= (ctbp - sym_sec->output_section->vma);
+ }
+ break;
+
+ case R_V850_NONE:
+ case R_V850_GNU_VTINHERIT:
+ case R_V850_GNU_VTENTRY:
+ return bfd_reloc_ok;
+
+ default:
+ return bfd_reloc_notsupported;
+ }
+
+ /* Perform the relocation. */
+ return v850_elf_perform_relocation (input_bfd, r_type, value + addend, hit_data);
+}
+\f
+/* Relocate an V850 ELF section. */
+
+static boolean
+v850_elf_relocate_section (output_bfd, info, input_bfd, input_section,
+ contents, relocs, local_syms, local_sections)
+ bfd * output_bfd;
+ 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;
+ Elf_Internal_Rela * rel;
+ Elf_Internal_Rela * relend;
+
+ symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+ sym_hashes = elf_sym_hashes (input_bfd);
+
+ if (sym_hashes == NULL)
+ {
+ info->callbacks->warning
+ (info, "no hash table available", NULL, input_bfd, input_section, 0);
+
+ return false;
+ }
+
+ /* Reset the list of remembered HI16S relocs to empty. */
+ free_hi16s = previous_hi16s;
+ previous_hi16s = NULL;
+ hi16s_counter = 0;
+
+ rel = relocs;
+ relend = relocs + input_section->reloc_count;
+ for (; rel < relend; rel++)
+ {
+ int r_type;
+ reloc_howto_type * howto;
+ unsigned long r_symndx;
+ Elf_Internal_Sym * sym;
+ asection * sec;
+ struct elf_link_hash_entry * h;
+ bfd_vma relocation;
+ bfd_reloc_status_type r;
+
+ r_symndx = ELF32_R_SYM (rel->r_info);
+ r_type = ELF32_R_TYPE (rel->r_info);
+
+ if (r_type == R_V850_GNU_VTENTRY
+ || r_type == R_V850_GNU_VTINHERIT)
+ continue;
+
+ howto = v850_elf_howto_table + r_type;
+
+ if (info->relocateable)
+ {
+ /* This is a relocateable link. We don't have to change
+ anything, unless the reloc is against a section symbol,
+ in which case we have to adjust according to where the
+ section symbol winds up in the output section. */
+ if (r_symndx < symtab_hdr->sh_info)
+ {
+ sym = local_syms + r_symndx;
+ if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
+ {
+ sec = local_sections[r_symndx];
+ rel->r_addend += sec->output_offset + sym->st_value;
+ }
+ }
+
+ continue;
+ }
+
+ /* This is a final link. */
+ h = NULL;
+ sym = NULL;
+ sec = NULL;
+ if (r_symndx < symtab_hdr->sh_info)
+ {
+ sym = local_syms + r_symndx;
+ sec = local_sections[r_symndx];
+ relocation = (sec->output_section->vma
+ + sec->output_offset
+ + sym->st_value);
+#if 0
+ {
+ char * name;
+
+ name = bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, sym->st_name);
+ name = (name == NULL) ? "<none>" : name;
+ fprintf (stderr, "local: sec: %s, sym: %s (%d), value: %x + %x + %x addend %x\n",
+ sec->name, name, sym->st_name,
+ sec->output_section->vma, sec->output_offset, sym->st_value, rel->r_addend);
+ }
+#endif
+ }
+ else
+ {
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ if (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ {
+ sec = h->root.u.def.section;
+ relocation = (h->root.u.def.value
+ + sec->output_section->vma
+ + sec->output_offset);
+#if 0
+ fprintf (stderr, "defined: sec: %s, name: %s, value: %x + %x + %x gives: %x\n",
+ sec->name, h->root.root.string, h->root.u.def.value, sec->output_section->vma, sec->output_offset, relocation);
+#endif
+ }
+ else if (h->root.type == bfd_link_hash_undefweak)
+ {
+#if 0
+ fprintf (stderr, "undefined: sec: %s, name: %s\n",
+ sec->name, h->root.root.string);
+#endif
+ relocation = 0;
+ }
+ else
+ {
+ if (! ((*info->callbacks->undefined_symbol)
+ (info, h->root.root.string, input_bfd,
+ input_section, rel->r_offset, true)))
+ return false;
+#if 0
+ fprintf (stderr, "unknown: name: %s\n", h->root.root.string);
+#endif
+ relocation = 0;
+ }
+ }
+
+ /* FIXME: We should use the addend, but the COFF relocations don't. */
+ r = v850_elf_final_link_relocate (howto, input_bfd, output_bfd,
+ input_section,
+ contents, rel->r_offset,
+ relocation, rel->r_addend,
+ info, sec, h == NULL);
+
+ if (r != bfd_reloc_ok)
+ {
+ const char * name;
+ const char * msg = (const char *)0;
+
+ if (h != NULL)
+ name = h->root.root.string;
+ else
+ {
+ name = (bfd_elf_string_from_elf_section
+ (input_bfd, symtab_hdr->sh_link, sym->st_name));
+ if (name == NULL || *name == '\0')
+ name = bfd_section_name (input_bfd, sec);
+ }
+
+ switch (r)
+ {
+ case bfd_reloc_overflow:
+ if (! ((*info->callbacks->reloc_overflow)
+ (info, name, howto->name, (bfd_vma) 0,
+ input_bfd, input_section, rel->r_offset)))
+ return false;
+ break;
+
+ case bfd_reloc_undefined:
+ if (! ((*info->callbacks->undefined_symbol)
+ (info, name, input_bfd, input_section,
+ rel->r_offset, true)))
+ return false;
+ break;
+
+ case bfd_reloc_outofrange:
+ msg = _("internal error: out of range error");
+ goto common_error;
+
+ case bfd_reloc_notsupported:
+ msg = _("internal error: unsupported relocation error");
+ goto common_error;
+
+ case bfd_reloc_dangerous:
+ msg = _("internal error: dangerous relocation");
+ goto common_error;
+
+ case bfd_reloc_other:
+ msg = _("could not locate special linker symbol __gp");
+ goto common_error;
+
+ case bfd_reloc_continue:
+ msg = _("could not locate special linker symbol __ep");
+ goto common_error;
+
+ case (bfd_reloc_dangerous + 1):
+ msg = _("could not locate special linker symbol __ctbp");
+ goto common_error;
+
+ default:
+ msg = _("internal error: unknown error");
+ /* fall through */
+
+ common_error:
+ if (!((*info->callbacks->warning)
+ (info, msg, name, input_bfd, input_section,
+ rel->r_offset)))
+ return false;