+ /* Since STT_GNU_IFUNC symbol must go through PLT, we handle
+ it here if it is defined in a non-shared object. */
+ if (h != NULL
+ && h->type == STT_GNU_IFUNC
+ && h->def_regular)
+ {
+ asection *plt;
+ bfd_vma plt_index;
+ const char *name;
+
+ if ((input_section->flags & SEC_ALLOC) == 0
+ || h->plt.offset == (bfd_vma) -1)
+ abort ();
+
+ /* STT_GNU_IFUNC symbol must go through PLT. */
+ plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
+ relocation = (plt->output_section->vma
+ + plt->output_offset + h->plt.offset);
+
+ switch (r_type)
+ {
+ default:
+ if (h->root.root.string)
+ name = h->root.root.string;
+ else
+ name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
+ NULL);
+ (*_bfd_error_handler)
+ (_("%B: relocation %s against STT_GNU_IFUNC "
+ "symbol `%s' isn't handled by %s"), input_bfd,
+ x86_64_elf_howto_table[r_type].name,
+ name, __FUNCTION__);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+
+ case R_X86_64_32S:
+ if (info->shared)
+ abort ();
+ goto do_relocation;
+
+ case R_X86_64_64:
+ if (rel->r_addend != 0)
+ {
+ if (h->root.root.string)
+ name = h->root.root.string;
+ else
+ name = bfd_elf_sym_name (input_bfd, symtab_hdr,
+ sym, NULL);
+ (*_bfd_error_handler)
+ (_("%B: relocation %s against STT_GNU_IFUNC "
+ "symbol `%s' has non-zero addend: %d"),
+ input_bfd, x86_64_elf_howto_table[r_type].name,
+ name, rel->r_addend);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ /* Generate dynamic relcoation only when there is a
+ non-GOF reference in a shared object. */
+ if (info->shared && h->non_got_ref)
+ {
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc;
+ asection *sreloc;
+
+ /* Need a dynamic relocation to get the real function
+ address. */
+ outrel.r_offset = _bfd_elf_section_offset (output_bfd,
+ info,
+ input_section,
+ rel->r_offset);
+ if (outrel.r_offset == (bfd_vma) -1
+ || outrel.r_offset == (bfd_vma) -2)
+ abort ();
+
+ outrel.r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+
+ if (h->dynindx == -1
+ || h->forced_local
+ || info->executable)
+ {
+ /* This symbol is resolved locally. */
+ outrel.r_info = ELF64_R_INFO (0, R_X86_64_IRELATIVE);
+ outrel.r_addend = (h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset);
+ }
+ else
+ {
+ outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
+ outrel.r_addend = 0;
+ }
+
+ sreloc = htab->elf.irelifunc;
+ loc = sreloc->contents;
+ loc += (sreloc->reloc_count++
+ * sizeof (Elf64_External_Rela));
+ bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+
+ /* If this reloc is against an external symbol, we
+ do not want to fiddle with the addend. Otherwise,
+ we need to include the symbol value so that it
+ becomes an addend for the dynamic reloc. For an
+ internal symbol, we have updated addend. */
+ continue;
+ }
+
+ case R_X86_64_32:
+ case R_X86_64_PC32:
+ case R_X86_64_PC64:
+ case R_X86_64_PLT32:
+ goto do_relocation;
+
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCREL64:
+ base_got = htab->elf.sgot;
+ off = h->got.offset;
+
+ if (base_got == NULL)
+ abort ();
+
+ if (off == (bfd_vma) -1)
+ {
+ /* We can't use h->got.offset here to save state, or
+ even just remember the offset, as finish_dynamic_symbol
+ would use that as offset into .got. */
+
+ if (htab->elf.splt != NULL)
+ {
+ plt_index = h->plt.offset / PLT_ENTRY_SIZE - 1;
+ off = (plt_index + 3) * GOT_ENTRY_SIZE;
+ base_got = htab->elf.sgotplt;
+ }
+ else
+ {
+ plt_index = h->plt.offset / PLT_ENTRY_SIZE;
+ off = plt_index * GOT_ENTRY_SIZE;
+ base_got = htab->elf.igotplt;
+ }
+
+ if (h->dynindx == -1
+ || h->forced_local
+ || info->symbolic)
+ {
+ /* This references the local defitionion. We must
+ initialize this entry in the global offset table.
+ Since the offset must always be a multiple of 8,
+ we use the least significant bit to record
+ whether we have initialized it already.
+
+ When doing a dynamic link, we create a .rela.got
+ relocation entry to initialize the value. This
+ is done in the finish_dynamic_symbol routine. */
+ if ((off & 1) != 0)
+ off &= ~1;
+ else
+ {
+ bfd_put_64 (output_bfd, relocation,
+ base_got->contents + off);
+ /* Note that this is harmless for the GOTPLT64
+ case, as -1 | 1 still is -1. */
+ h->got.offset |= 1;
+ }
+ }
+ }
+
+ relocation = (base_got->output_section->vma
+ + base_got->output_offset + off);
+
+ if (r_type != R_X86_64_GOTPCREL
+ && r_type != R_X86_64_GOTPCREL64)
+ {
+ asection *gotplt;
+ if (htab->elf.splt != NULL)
+ gotplt = htab->elf.sgotplt;
+ else
+ gotplt = htab->elf.igotplt;
+ relocation -= (gotplt->output_section->vma
+ - gotplt->output_offset);
+ }
+
+ goto do_relocation;
+ }
+ }
+