+ /* 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, *base_got;
+
+ if ((input_section->flags & SEC_ALLOC) == 0)
+ {
+ /* If this is a SHT_NOTE section without SHF_ALLOC, treat
+ STT_GNU_IFUNC symbol as STT_FUNC. */
+ if (elf_section_type (input_section) == SHT_NOTE)
+ goto skip_ifunc;
+
+ /* Dynamic relocs are not propagated for SEC_DEBUGGING
+ sections because such sections are not SEC_ALLOC and
+ thus ld.so will not process them. */
+ if ((input_section->flags & SEC_DEBUGGING) != 0)
+ continue;
+
+ abort ();
+ }
+ else if (h->plt.offset == (bfd_vma) -1
+ /* The following relocation may not need the .plt entries
+ when all references to a STT_GNU_IFUNC symbols are done
+ via GOT or static function pointers. */
+ && r_type != R_RISCV_32
+ && r_type != R_RISCV_64
+ && r_type != R_RISCV_HI20
+ && r_type != R_RISCV_GOT_HI20
+ && r_type != R_RISCV_LO12_I
+ && r_type != R_RISCV_LO12_S)
+ goto bad_ifunc_reloc;
+
+ /* 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)
+ {
+ case R_RISCV_32:
+ case R_RISCV_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
+ /* xgettext:c-format */
+ (_("%pB: relocation %s against STT_GNU_IFUNC "
+ "symbol `%s' has non-zero addend: %" PRId64),
+ input_bfd, howto->name, name, (int64_t) rel->r_addend);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ /* Generate dynamic relocation only when there is a non-GOT
+ reference in a shared object or there is no PLT. */
+ if ((bfd_link_pic (info) && h->non_got_ref)
+ || h->plt.offset == (bfd_vma) -1)
+ {
+ Elf_Internal_Rela outrel;
+ 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
+ || bfd_link_executable (info))
+ {
+ info->callbacks->minfo
+ (_("Local IFUNC function `%s' in %pB\n"),
+ h->root.root.string,
+ h->root.u.def.section->owner);
+
+ /* This symbol is resolved locally. */
+ outrel.r_info = ELFNN_R_INFO (0, R_RISCV_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 = ELFNN_R_INFO (h->dynindx, r_type);
+ outrel.r_addend = 0;
+ }
+
+ /* Dynamic relocations are stored in
+ 1. .rela.ifunc section in PIC object.
+ 2. .rela.got section in dynamic executable.
+ 3. .rela.iplt section in static executable. */
+ if (bfd_link_pic (info))
+ sreloc = htab->elf.irelifunc;
+ else if (htab->elf.splt != NULL)
+ sreloc = htab->elf.srelgot;
+ else
+ sreloc = htab->elf.irelplt;
+
+ riscv_elf_append_rela (output_bfd, sreloc, &outrel);
+
+ /* 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;
+ }
+ goto do_relocation;
+
+ case R_RISCV_GOT_HI20:
+ base_got = htab->elf.sgot;
+ off = h->got.offset;
+
+ if (base_got == NULL)
+ abort ();
+
+ if (off == (bfd_vma) -1)
+ {
+ bfd_vma plt_idx;
+
+ /* 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_idx = (h->plt.offset - PLT_HEADER_SIZE)
+ / PLT_ENTRY_SIZE;
+ off = GOTPLT_HEADER_SIZE + (plt_idx * GOT_ENTRY_SIZE);
+ base_got = htab->elf.sgotplt;
+ }
+ else
+ {
+ plt_idx = h->plt.offset / PLT_ENTRY_SIZE;
+ off = plt_idx * GOT_ENTRY_SIZE;
+ base_got = htab->elf.igotplt;
+ }
+
+ if (h->dynindx == -1
+ || h->forced_local
+ || info->symbolic)
+ {
+ /* This references the local definition. 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_NN (output_bfd, relocation,
+ base_got->contents + off);
+ /* Note that this is harmless for the case,
+ as -1 | 1 still is -1. */
+ h->got.offset |= 1;
+ }
+ }
+ }
+
+ relocation = base_got->output_section->vma
+ + base_got->output_offset + off;
+
+ if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+ relocation, r_type,
+ false))
+ r = bfd_reloc_overflow;
+ goto do_relocation;
+
+ case R_RISCV_CALL:
+ case R_RISCV_CALL_PLT:
+ case R_RISCV_HI20:
+ case R_RISCV_LO12_I:
+ case R_RISCV_LO12_S:
+ goto do_relocation;
+
+ case R_RISCV_PCREL_HI20:
+ if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+ relocation, r_type,
+ false))
+ r = bfd_reloc_overflow;
+ goto do_relocation;
+
+ default:
+ bad_ifunc_reloc:
+ if (h->root.root.string)
+ name = h->root.root.string;
+ else
+ /* The entry of local ifunc is fake in global hash table,
+ we should find the name by the original local symbol. */
+ name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym, NULL);
+
+ _bfd_error_handler
+ /* xgettext:c-format */
+ (_("%pB: relocation %s against STT_GNU_IFUNC "
+ "symbol `%s' isn't supported"), input_bfd,
+ howto->name, name);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+ }
+
+ skip_ifunc: