+ /* We don't want to warn on calls to undefined weak symbols,
+ as calls to them must be protected by non-NULL tests
+ anyway, and unprotected calls would invoke undefined
+ behavior. */
+ if (h->root.type == bfd_link_hash_undefweak)
+ check_segment[0] = check_segment[1] = -1;
+
+ if (h->forced_local)
+ goto final_link_relocate;
+
+ if (h->plt.offset == (bfd_vma) -1)
+ {
+ /* We didn't make a PLT entry for this symbol. This
+ happens when statically linking PIC code, or when
+ using -Bsymbolic. */
+ goto final_link_relocate;
+ }
+
+ BFD_ASSERT (splt != NULL);
+ check_segment[1] = plt_segment;
+ relocation = (splt->output_section->vma
+ + splt->output_offset
+ + h->plt.offset);
+
+#ifdef INCLUDE_SHMEDIA
+ relocation++;
+#endif
+
+ addend = rel->r_addend;
+
+ goto final_link_relocate;
+
+ /* Relocation is to the canonical function descriptor for this
+ symbol, possibly via the GOT. Initialize the GOT
+ entry and function descriptor if necessary. */
+ case R_SH_GOTFUNCDESC:
+ case R_SH_GOTFUNCDESC20:
+ case R_SH_FUNCDESC:
+ {
+ int dynindx = -1;
+ asection *reloc_section;
+ bfd_vma reloc_offset;
+ int reloc_type = R_SH_FUNCDESC;
+
+ BFD_ASSERT (htab);
+
+ check_segment[0] = check_segment[1] = -1;
+
+ /* FIXME: See what FRV does for global symbols in the
+ executable, with --export-dynamic. Do they need ld.so
+ to allocate official descriptors? See what this code
+ does. */
+
+ relocation = 0;
+ addend = 0;
+
+ if (r_type == R_SH_FUNCDESC)
+ {
+ reloc_section = input_section;
+ reloc_offset = rel->r_offset;
+ }
+ else
+ {
+ reloc_section = sgot;
+
+ if (h != NULL)
+ reloc_offset = h->got.offset;
+ else
+ {
+ BFD_ASSERT (local_got_offsets != NULL);
+ reloc_offset = local_got_offsets[r_symndx];
+ }
+ BFD_ASSERT (reloc_offset != MINUS_ONE);
+
+ if (reloc_offset & 1)
+ {
+ reloc_offset &= ~1;
+ goto funcdesc_done_got;
+ }
+ }
+
+ if (h && h->root.type == bfd_link_hash_undefweak
+ && (SYMBOL_CALLS_LOCAL (info, h)
+ || !htab->root.dynamic_sections_created))
+ /* Undefined weak symbol which will not be dynamically
+ resolved later; leave it at zero. */
+ goto funcdesc_leave_zero;
+ else if (SYMBOL_CALLS_LOCAL (info, h)
+ && ! SYMBOL_FUNCDESC_LOCAL (info, h))
+ {
+ /* If the symbol needs a non-local function descriptor
+ but binds locally (i.e., its visibility is
+ protected), emit a dynamic relocation decayed to
+ section+offset. This is an optimization; the dynamic
+ linker would resolve our function descriptor request
+ to our copy of the function anyway. */
+ dynindx = elf_section_data (h->root.u.def.section
+ ->output_section)->dynindx;
+ relocation += h->root.u.def.section->output_offset
+ + h->root.u.def.value;
+ }
+ else if (! SYMBOL_FUNCDESC_LOCAL (info, h))
+ {
+ /* If the symbol is dynamic and there will be dynamic
+ symbol resolution because we are or are linked with a
+ shared library, emit a FUNCDESC relocation such that
+ the dynamic linker will allocate the function
+ descriptor. */
+ BFD_ASSERT (h->dynindx != -1);
+ dynindx = h->dynindx;
+ }
+ else
+ {
+ bfd_vma offset;
+
+ /* Otherwise, we know we have a private function
+ descriptor, so reference it directly. */
+ reloc_type = R_SH_DIR32;
+ dynindx = elf_section_data (htab->sfuncdesc
+ ->output_section)->dynindx;
+
+ if (h)
+ {
+ offset = sh_elf_hash_entry (h)->funcdesc.offset;
+ BFD_ASSERT (offset != MINUS_ONE);
+ if ((offset & 1) == 0)
+ {
+ if (!sh_elf_initialize_funcdesc (output_bfd, info, h,
+ offset, NULL, 0))
+ return FALSE;
+ sh_elf_hash_entry (h)->funcdesc.offset |= 1;
+ }
+ }
+ else
+ {
+ union gotref *local_funcdesc;
+
+ local_funcdesc = sh_elf_local_funcdesc (input_bfd);
+ offset = local_funcdesc[r_symndx].offset;
+ BFD_ASSERT (offset != MINUS_ONE);
+ if ((offset & 1) == 0)
+ {
+ if (!sh_elf_initialize_funcdesc (output_bfd, info, NULL,
+ offset, sec,
+ sym->st_value))
+ return FALSE;
+ local_funcdesc[r_symndx].offset |= 1;
+ }
+ }
+
+ relocation = htab->sfuncdesc->output_offset + (offset & ~1);
+ }
+
+ if (!info->shared && SYMBOL_FUNCDESC_LOCAL (info, h))
+ {
+ bfd_vma offset;
+
+ if (sh_elf_osec_readonly_p (output_bfd,
+ reloc_section->output_section))
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): cannot emit fixup to `%s' in read-only section"),
+ input_bfd,
+ input_section,
+ (long) rel->r_offset,
+ symname);
+ return FALSE;
+ }
+
+ offset = _bfd_elf_section_offset (output_bfd, info,
+ reloc_section, reloc_offset);
+
+ if (offset != (bfd_vma)-1)
+ sh_elf_add_rofixup (output_bfd, htab->srofixup,
+ offset
+ + reloc_section->output_section->vma
+ + reloc_section->output_offset);
+ }
+ else if ((reloc_section->output_section->flags
+ & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
+ {
+ bfd_vma offset;
+
+ if (sh_elf_osec_readonly_p (output_bfd,
+ reloc_section->output_section))
+ {
+ info->callbacks->warning
+ (info,
+ _("cannot emit dynamic relocations in read-only section"),
+ symname, input_bfd, reloc_section, reloc_offset);
+ return FALSE;
+ }
+
+ if (srelgot == NULL)
+ {
+ srelgot = bfd_get_linker_section (dynobj, ".rela.got");
+ BFD_ASSERT (srelgot != NULL);
+ }
+
+ offset = _bfd_elf_section_offset (output_bfd, info,
+ reloc_section, reloc_offset);
+
+ if (offset != (bfd_vma)-1)
+ sh_elf_add_dyn_reloc (output_bfd, srelgot,
+ offset
+ + reloc_section->output_section->vma
+ + reloc_section->output_offset,
+ reloc_type, dynindx, relocation);
+
+ if (r_type == R_SH_FUNCDESC)
+ {
+ r = bfd_reloc_ok;
+ break;
+ }
+ else
+ {
+ relocation = 0;
+ goto funcdesc_leave_zero;
+ }
+ }
+
+ if (SYMBOL_FUNCDESC_LOCAL (info, h))
+ relocation += htab->sfuncdesc->output_section->vma;
+ funcdesc_leave_zero:
+ if (r_type != R_SH_FUNCDESC)
+ {
+ bfd_put_32 (output_bfd, relocation,
+ reloc_section->contents + reloc_offset);
+ if (h != NULL)
+ h->got.offset |= 1;
+ else
+ local_got_offsets[r_symndx] |= 1;
+
+ funcdesc_done_got:
+
+ relocation = sh_elf_got_offset (htab) + reloc_offset;
+#ifdef GOT_BIAS
+ relocation -= GOT_BIAS;
+#endif
+ }
+ if (r_type == R_SH_GOTFUNCDESC20)
+ {
+ r = install_movi20_field (output_bfd, relocation + addend,
+ input_bfd, input_section, contents,
+ rel->r_offset);
+ break;
+ }
+ else
+ goto final_link_relocate;
+ }
+ break;
+
+ case R_SH_GOTOFFFUNCDESC:
+ case R_SH_GOTOFFFUNCDESC20:
+ /* FIXME: See R_SH_FUNCDESC comment about global symbols in the
+ executable and --export-dynamic. If such symbols get
+ ld.so-allocated descriptors we can not use R_SH_GOTOFFFUNCDESC
+ for them. */
+ BFD_ASSERT (htab);
+
+ check_segment[0] = check_segment[1] = -1;
+ relocation = 0;
+ addend = rel->r_addend;
+
+ if (h && (h->root.type == bfd_link_hash_undefweak
+ || !SYMBOL_FUNCDESC_LOCAL (info, h)))
+ {
+ _bfd_error_handler
+ (_("%B(%A+0x%lx): %s relocation against external symbol \"%s\""),
+ input_bfd, input_section, (long) rel->r_offset, howto->name,
+ h->root.root.string);
+ return FALSE;
+ }
+ else
+ {
+ bfd_vma offset;
+
+ /* Otherwise, we know we have a private function
+ descriptor, so reference it directly. */
+ if (h)
+ {
+ offset = sh_elf_hash_entry (h)->funcdesc.offset;
+ BFD_ASSERT (offset != MINUS_ONE);
+ if ((offset & 1) == 0)
+ {
+ if (!sh_elf_initialize_funcdesc (output_bfd, info, h,
+ offset, NULL, 0))
+ return FALSE;
+ sh_elf_hash_entry (h)->funcdesc.offset |= 1;
+ }
+ }
+ else
+ {
+ union gotref *local_funcdesc;
+
+ local_funcdesc = sh_elf_local_funcdesc (input_bfd);
+ offset = local_funcdesc[r_symndx].offset;
+ BFD_ASSERT (offset != MINUS_ONE);
+ if ((offset & 1) == 0)
+ {
+ if (!sh_elf_initialize_funcdesc (output_bfd, info, NULL,
+ offset, sec,
+ sym->st_value))
+ return FALSE;
+ local_funcdesc[r_symndx].offset |= 1;
+ }
+ }