[PATCH] RISC-V: Support GNU indirect functions.

Fangrui Song i@maskray.me
Wed Jul 8 07:22:22 GMT 2020


I think I know ifunc and its implementation in LLD quite well
(https://reviews.llvm.org/D74022 ). However, I know little about GNU ld
and its terminology, so please bear with me.

On 2020-07-07, Nelson Chu wrote:
>For now, the following relocations are supported for IFUNC in linker,
>
>* R_RISCV_32 or R_RISCV_64
>Store the IFUNC symbol into the data section.  Consider the ifunc-10.s
>or others in the ld testsuite.
>
>* R_RISCV_HI20 and R_RISCV_LO12_I/S
>The LUI + ADDI/LW/SW patterns. The absolute access relocation. Originally,
>I thought we didn't need to care about them, until I noticed that the
>ld test suites "Build pr23169a" and "Build pr23169d" needed them. The
>test suites are in the ld/testsuite/ld-ifunc/ and need compiler
>support. I think the medlow model without -fPIC compiler option and
>-shared linker option will possibly generate them.
>
>* R_RISCV_CALL or R_RISCV_CALL_PLT
>I don't think the RISC-V compiler will generate R_RISCV_JAL directly
>to jump to an IFUNC.  Besides, I disable the relaxations for the
>relocation referenced to IFUNC, so just handling the R_RISCV_CALL and
>R_RISCV_CALL_PLT should be enough. We also need to allocate the PLT
>entry for the R_RISCV_CALL if it is referenced to IFUNC symbol.
>
>For now, GNU GCC and Binutils handle the R_RISCV_CALL and R_RISCV_CALL_PLT
>separately.  But they can (should) be handled identically, since RISC-V lld
>had made the same improvement.  And so had GNU AARCH64, they has only
>R_AARCH64_CALL26 for calls.  It will affect the current IFUNC implementation
>in this patch.  Therefore, I prefer to improve both of them in the future
>patches, if everyone is OK.
>
>* R_RISCV_PCREL_HI20 and R_RISCV_PCREL_LO12_I/S
>LA/LLA pattern with local fPIC ifunc symbol, or any non-PIC ifunc
>symbol. The PC-relative relocation.
>
>* R_RISCV_GOT_HI20 and R_RISCV_PCREL_LO12_I/S
>LA pattern with global PIC ifunc symbol. Also check if it is possible to
>generate the PLT entry for IFUNC GOT reference. This saves the PLT spaces,
>and also reduces the overload of glibc.
>
>Besides, consider the two commits,
>
>1. cebd6b8ac1c5a2a847a50e3efe932ff2d0867b3e
>IFUNC: Update IFUNC resolver check with DT_TEXTREL.
>
>2. 3084d7a27b8e4d13f0fdd0fac62ffadc9c2223b5
>ELF: Add _bfd_elf_add_dynamic_tags
>
>These two commits will warn users that they should recompile their code
>with -fPIC or -fPIE, otherwise the GNU indirect functions with DT_TEXTREL
>may result in a segfault at runtime.  R_RISCV_HI20 and R_RISCV_32/64 should
>be fine here, since we don't allow the former when generating the shared
>library or PIE executable.  And the later are used in the data section rather
>than the code for now.  But R_RISCV_CALL and R_RISCV_PCREL_HI20 may cause the
>warning.  Consider the ifunc-2 testsuite in the ld/testsuite/ld-riscv-elf/.
>For the ifunc-2, the dynamic relocation, which cause the TEXTREL, are all
>R_RISCV_NONE and do nothing in the glibc or other dynamic loaders.
>I believe this can be resolved by changing some checking rules in the
>riscv_elf_check_relocs.
>
>	bfd/
>	* elfnn-riscv.c: Include "objalloc.h" since we need objalloc_alloc.
>	(riscv_elf_link_hash_table): Add loc_hash_table and loc_hash_memory
>	used by local STT_GNU_IFUNC symbols.
>	(riscv_elf_got_plt_val): Removed.
>	(riscv_elf_local_htab_hash, riscv_elf_local_htab_eq): New functions.
>	Use to Compare local hash entries.
>	(riscv_elf_get_local_sym_hash): New function.  Find a hash entry for
>	local symbol, and create a new one if needed.
>	(riscv_elf_link_hash_table_free): New function.  Destroy an riscv
>	elf linker hash table.
>	(riscv_elf_link_hash_table_create): Create hash table for local IFUNC.
>	(riscv_elf_check_relocs): Create a fake global symbol to track the
>	local IFUNC symbol.  Add support to check and handle the relocation
>	reference to IFUNC symbol.
>	(allocate_dynrelocs): Let allocate_ifunc_dynrelocs and
>	allocate_local_ifunc_dynrelocs to handle the IFUNC symbols if they
>	are defined and referenced in a non-shared object.
>	(allocate_ifunc_dynrelocs): New function.  Allocate space in .plt,
>	.got and associated reloc sections for ifunc dynamic relocs.
>	(allocate_local_ifunc_dynrelocs): Likewise, but for local ifunc
>	dynamic relocs.
>	(riscv_elf_relocate_section): Add support to handle the relocation
>	referenced to IFUNC symbols.
>	(riscv_elf_size_dynamic_sections): Updated.
>	(riscv_elf_adjust_dynamic_symbol): Updated.
>	(riscv_elf_finish_dynamic_symbol): Finish up the IFUNC handling,
>	including fill the PLT and GOT entries for IFUNC symbols.
>	(riscv_elf_finish_local_dynamic_symbol): New function.  Called by
>	riscv_elf_finish_dynamic_symbol to handle the local IFUNC symbols.
>	(_bfd_riscv_relax_section): Don't do the relaxation for IFUNC.
>
>	* elfxx-riscv.c: Add R_RISCV_IRELATIVE.
>	* configure.ac: Link elf-ifunc.lo to use the generic ifunc support.
>	* configure: Regenerated.
>
>	include/
>	* elf/riscv.h: Add R_RISCV_IRELATIVE to 58.
>
>	ld/
>	* emulparams/elf32lriscv-defs.sh: Add IREL_IN_PLT.
>	* testsuite/ld-ifunc/ifunc.exp: Enable ifunc testcases for riscv.
>	* testsuite/ld-riscv-elf/ld-riscv-elf.exp: Add ifunc testcases.
>	These testcases are ported from testsuite/ld-ifunc.
>	* testsuite/ld-riscv-elf/ifunc-1-local.d: New testcase.
>	* testsuite/ld-riscv-elf/ifunc-1-local.map: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-1-local.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-1.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-1.map: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-1.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-2-local.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-2-local.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-2.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-2.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-3a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-3b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-3.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-4-local.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-4-local.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-4.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-4.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-5a-local.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-5b-local.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-5r-local.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-5-local.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-5a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-5b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-5.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-6a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-6b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-6.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-7a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-7b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-7.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-8.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-8a.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-8b.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-9.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-9.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-10.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-10.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-11a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-11b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-11.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-12.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-12.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-13.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-13a.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-13b.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-14a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-14b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-14c.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-14d.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-14e.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-14f.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-14a.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-14b.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-14c.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-15.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-15.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-16-now.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-16.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-16.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-17a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-17b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-17a.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-17b.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-18a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-18b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-18a.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-18b.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-19a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-19b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-19a.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-19b.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-20.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-20.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-21.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-21.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-22.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-22.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-23a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-23b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-23c.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-23.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-24a.d: Likewise
>	* testsuite/ld-riscv-elf/ifunc-24b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-24.s: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-25a.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-25b.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-25c.d: Likewise.
>	* testsuite/ld-riscv-elf/ifunc-25.s: Likewise.
>---
> bfd/configure                               |   4 +-
> bfd/configure.ac                            |   4 +-
> bfd/elfnn-riscv.c                           | 715 ++++++++++++++++++++++++++--
> bfd/elfxx-riscv.c                           |  15 +
> include/elf/riscv.h                         |   1 +
> ld/emulparams/elf32lriscv-defs.sh           |   1 +
> ld/testsuite/ld-ifunc/ifunc.exp             |   4 +-
> ld/testsuite/ld-riscv-elf/ifunc-1-local.d   |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-1-local.map |   3 +
> ld/testsuite/ld-riscv-elf/ifunc-1-local.s   |  13 +
> ld/testsuite/ld-riscv-elf/ifunc-1.d         |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-1.map       |   3 +
> ld/testsuite/ld-riscv-elf/ifunc-1.s         |  16 +
> ld/testsuite/ld-riscv-elf/ifunc-10.d        |   4 +
> ld/testsuite/ld-riscv-elf/ifunc-10.s        |  43 ++
> ld/testsuite/ld-riscv-elf/ifunc-11.s        |  44 ++
> ld/testsuite/ld-riscv-elf/ifunc-11a.d       |   5 +
> ld/testsuite/ld-riscv-elf/ifunc-11b.d       |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-12.d        |   5 +
> ld/testsuite/ld-riscv-elf/ifunc-12.s        |  42 ++
> ld/testsuite/ld-riscv-elf/ifunc-13.d        |   9 +
> ld/testsuite/ld-riscv-elf/ifunc-13a.s       |  12 +
> ld/testsuite/ld-riscv-elf/ifunc-13b.s       |   5 +
> ld/testsuite/ld-riscv-elf/ifunc-14a.d       |  10 +
> ld/testsuite/ld-riscv-elf/ifunc-14a.s       |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-14b.d       |  10 +
> ld/testsuite/ld-riscv-elf/ifunc-14b.s       |   5 +
> ld/testsuite/ld-riscv-elf/ifunc-14c.d       |  10 +
> ld/testsuite/ld-riscv-elf/ifunc-14c.s       |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-14d.d       |  10 +
> ld/testsuite/ld-riscv-elf/ifunc-14e.d       |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-14f.d       |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-15.d        |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-15.s        |  16 +
> ld/testsuite/ld-riscv-elf/ifunc-16-now.d    |   8 +
> ld/testsuite/ld-riscv-elf/ifunc-16.d        |   8 +
> ld/testsuite/ld-riscv-elf/ifunc-16.s        |  17 +
> ld/testsuite/ld-riscv-elf/ifunc-17a.d       |   8 +
> ld/testsuite/ld-riscv-elf/ifunc-17a.s       |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-17b.d       |   8 +
> ld/testsuite/ld-riscv-elf/ifunc-17b.s       |   6 +
> ld/testsuite/ld-riscv-elf/ifunc-18a.d       |  13 +
> ld/testsuite/ld-riscv-elf/ifunc-18a.s       |   5 +
> ld/testsuite/ld-riscv-elf/ifunc-18b.d       |  13 +
> ld/testsuite/ld-riscv-elf/ifunc-18b.s       |  15 +
> ld/testsuite/ld-riscv-elf/ifunc-19a.d       |  13 +
> ld/testsuite/ld-riscv-elf/ifunc-19a.s       |   5 +
> ld/testsuite/ld-riscv-elf/ifunc-19b.d       |  13 +
> ld/testsuite/ld-riscv-elf/ifunc-19b.s       |  15 +
> ld/testsuite/ld-riscv-elf/ifunc-2-local.d   |  12 +
> ld/testsuite/ld-riscv-elf/ifunc-2-local.s   |  16 +
> ld/testsuite/ld-riscv-elf/ifunc-2.d         |  13 +
> ld/testsuite/ld-riscv-elf/ifunc-2.s         |  19 +
> ld/testsuite/ld-riscv-elf/ifunc-20.d        |  12 +
> ld/testsuite/ld-riscv-elf/ifunc-20.s        |  16 +
> ld/testsuite/ld-riscv-elf/ifunc-21.d        |  20 +
> ld/testsuite/ld-riscv-elf/ifunc-21.s        |  31 ++
> ld/testsuite/ld-riscv-elf/ifunc-22.d        |  20 +
> ld/testsuite/ld-riscv-elf/ifunc-22.s        |  32 ++
> ld/testsuite/ld-riscv-elf/ifunc-23.s        |  12 +
> ld/testsuite/ld-riscv-elf/ifunc-23a.d       |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-23b.d       |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-23c.d       |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-24.s        |  13 +
> ld/testsuite/ld-riscv-elf/ifunc-24a.d       |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-24b.d       |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-25.s        |  14 +
> ld/testsuite/ld-riscv-elf/ifunc-25a.d       |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-25b.d       |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-25c.d       |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-3.s         |  16 +
> ld/testsuite/ld-riscv-elf/ifunc-3a.d        |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-3b.d        |   8 +
> ld/testsuite/ld-riscv-elf/ifunc-4-local.d   |   6 +
> ld/testsuite/ld-riscv-elf/ifunc-4-local.s   |  20 +
> ld/testsuite/ld-riscv-elf/ifunc-4.d         |   6 +
> ld/testsuite/ld-riscv-elf/ifunc-4.s         |  21 +
> ld/testsuite/ld-riscv-elf/ifunc-5-local.s   |  24 +
> ld/testsuite/ld-riscv-elf/ifunc-5.s         |  25 +
> ld/testsuite/ld-riscv-elf/ifunc-5a-local.d  |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-5a.d        |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-5b-local.d  |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-5b.d        |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-5r-local.d  |  10 +
> ld/testsuite/ld-riscv-elf/ifunc-6.s         |  26 +
> ld/testsuite/ld-riscv-elf/ifunc-6a.d        |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-6b.d        |  11 +
> ld/testsuite/ld-riscv-elf/ifunc-7.s         |  28 ++
> ld/testsuite/ld-riscv-elf/ifunc-7a.d        |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-7b.d        |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-8.d         |   9 +
> ld/testsuite/ld-riscv-elf/ifunc-8a.s        |  18 +
> ld/testsuite/ld-riscv-elf/ifunc-8b.s        |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-9.d         |   7 +
> ld/testsuite/ld-riscv-elf/ifunc-9.s         |  24 +
> ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp  | 201 ++++++++
> 96 files changed, 2017 insertions(+), 51 deletions(-)
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-1-local.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-1-local.map
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-1-local.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-1.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-1.map
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-1.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-10.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-10.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-11.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-11a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-11b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-12.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-12.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-13.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-13a.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-13b.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-14a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-14a.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-14b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-14b.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-14c.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-14c.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-14d.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-14e.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-14f.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-15.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-15.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-16-now.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-16.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-16.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-17a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-17a.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-17b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-17b.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-18a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-18a.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-18b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-18b.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-19a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-19a.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-19b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-19b.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-2-local.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-2-local.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-2.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-2.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-20.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-20.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-21.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-21.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-22.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-22.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-23.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-23a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-23b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-23c.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-24.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-24a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-24b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-25.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-25a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-25b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-25c.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-3.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-3a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-3b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-4-local.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-4-local.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-4.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-4.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-5-local.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-5.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-5a-local.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-5a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-5b-local.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-5b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-5r-local.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-6.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-6a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-6b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-7.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-7a.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-7b.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-8.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-8a.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-8b.s
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-9.d
> create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-9.s

The test naming is really terrible. It is difficult to understand the
purpose of individual files. Can they have more descriptive names?

ifunc-1.s and ifunc-3.s are identical except a visibility setting.
Can the tests be somehow combined? For example, 
echo '.hidden foo' > a.s and then concatenate two assembly files

>diff --git a/bfd/configure b/bfd/configure
>index 47fd457..aa647dc 100755
>--- a/bfd/configure
>+++ b/bfd/configure
>@@ -14889,8 +14889,8 @@ do
>     powerpc_pei_le_vec)		 tb="$tb pei-ppc.lo peigen.lo $coff" ;;
>     powerpc_xcoff_vec)		 tb="$tb coff-rs6000.lo $xcoff" ;;
>     pru_elf32_vec)		 tb="$tb elf32-pru.lo elf32.lo $elf" ;;
>-    riscv_elf32_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elf32.lo $elf" ;;
>-    riscv_elf64_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf32.lo $elf"; target_size=64 ;;
>+    riscv_elf32_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf" ;;
>+    riscv_elf64_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
>     rl78_elf32_vec)		 tb="$tb elf32-rl78.lo elf32.lo $elf" ;;
>     rs6000_xcoff64_vec)		 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
>     rs6000_xcoff64_aix_vec)	 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
>diff --git a/bfd/configure.ac b/bfd/configure.ac
>index 8e86f83..4989b25 100644
>--- a/bfd/configure.ac
>+++ b/bfd/configure.ac
>@@ -625,8 +625,8 @@ do
>     powerpc_pei_le_vec)		 tb="$tb pei-ppc.lo peigen.lo $coff" ;;
>     powerpc_xcoff_vec)		 tb="$tb coff-rs6000.lo $xcoff" ;;
>     pru_elf32_vec)		 tb="$tb elf32-pru.lo elf32.lo $elf" ;;
>-    riscv_elf32_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elf32.lo $elf" ;;
>-    riscv_elf64_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf32.lo $elf"; target_size=64 ;;
>+    riscv_elf32_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf" ;;
>+    riscv_elf64_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf-ifunc.lo elf32.lo $elf"; target_size=64 ;;
>     rl78_elf32_vec)		 tb="$tb elf32-rl78.lo elf32.lo $elf" ;;
>     rs6000_xcoff64_vec)		 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
>     rs6000_xcoff64_aix_vec)	 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
>diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
>index a5fa415..ca0dd11 100644
>--- a/bfd/elfnn-riscv.c
>+++ b/bfd/elfnn-riscv.c
>@@ -31,6 +31,7 @@
> #include "elfxx-riscv.h"
> #include "elf/riscv.h"
> #include "opcode/riscv.h"
>+#include "objalloc.h"
>
> /* Internal relocations used exclusively by the relaxation pass.  */
> #define R_RISCV_DELETE (R_RISCV_max + 1)
>@@ -118,6 +119,10 @@ struct riscv_elf_link_hash_table
>
>   /* The max alignment of output sections.  */
>   bfd_vma max_alignment;
>+
>+  /* Used by local STT_GNU_IFUNC symbols.  */
>+  htab_t loc_hash_table;
>+  void * loc_hash_memory;
> };
>
>
>@@ -155,17 +160,13 @@ riscv_elf_append_rela (bfd *abfd, asection *s, Elf_Internal_Rela *rel)
>
> #define GOT_ENTRY_SIZE RISCV_ELF_WORD_BYTES
>
>+/* Reserve two entries of GOTPLT for ld.so, one is used for PLT resolver,
>+   the other is used for link map.  Other targets also reserve one more
>+   entry used for runtime profile?  */
> #define GOTPLT_HEADER_SIZE (2 * GOT_ENTRY_SIZE)
>
> #define sec_addr(sec) ((sec)->output_section->vma + (sec)->output_offset)
>
>-static bfd_vma
>-riscv_elf_got_plt_val (bfd_vma plt_index, struct bfd_link_info *info)
>-{
>-  return sec_addr (riscv_elf_hash_table (info)->elf.sgotplt)
>-	 + GOTPLT_HEADER_SIZE + (plt_index * GOT_ENTRY_SIZE);
>-}
>-
> #if ARCH_SIZE == 32
> # define MATCH_LREG MATCH_LW
> #else
>@@ -267,6 +268,86 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
>   return entry;
> }
>
>+/* Compute a hash of a local hash entry.  We use elf_link_hash_entry
>+  for local symbol so that we can handle local STT_GNU_IFUNC symbols
>+  as global symbol.  We reuse indx and dynstr_index for local symbol
>+  hash since they aren't used by global symbols in this backend.  */
>+
>+static hashval_t
>+riscv_elf_local_htab_hash (const void *ptr)
>+{
>+  struct elf_link_hash_entry *h = (struct elf_link_hash_entry *) ptr;
>+  return ELF_LOCAL_SYMBOL_HASH (h->indx, h->dynstr_index);
>+}
>+
>+/* Compare local hash entries.  */
>+
>+static int
>+riscv_elf_local_htab_eq (const void *ptr1, const void *ptr2)
>+{
>+  struct elf_link_hash_entry *h1 = (struct elf_link_hash_entry *) ptr1;
>+  struct elf_link_hash_entry *h2 = (struct elf_link_hash_entry *) ptr2;
>+
>+  return h1->indx == h2->indx && h1->dynstr_index == h2->dynstr_index;
>+}
>+
>+/* Find and/or create a hash entry for local symbol.  */
>+
>+static struct elf_link_hash_entry *
>+riscv_elf_get_local_sym_hash (struct riscv_elf_link_hash_table *htab,
>+			      bfd *abfd, const Elf_Internal_Rela *rel,
>+			      bfd_boolean create)
>+{
>+  struct riscv_elf_link_hash_entry eh, *ret;
>+  asection *sec = abfd->sections;
>+  hashval_t h = ELF_LOCAL_SYMBOL_HASH (sec->id,
>+				       ELFNN_R_SYM (rel->r_info));
>+  void **slot;
>+
>+  eh.elf.indx = sec->id;
>+  eh.elf.dynstr_index = ELFNN_R_SYM (rel->r_info);
>+  slot = htab_find_slot_with_hash (htab->loc_hash_table, &eh, h,
>+				   create ? INSERT : NO_INSERT);
>+
>+  if (!slot)
>+    return NULL;
>+
>+  if (*slot)
>+    {
>+      ret = (struct riscv_elf_link_hash_entry *) *slot;
>+      return &ret->elf;
>+    }
>+
>+  ret = (struct riscv_elf_link_hash_entry *)
>+	objalloc_alloc ((struct objalloc *) htab->loc_hash_memory,
>+			sizeof (struct riscv_elf_link_hash_entry));
>+  if (ret)
>+    {
>+      memset (ret, 0, sizeof (*ret));
>+      ret->elf.indx = sec->id;
>+      ret->elf.dynstr_index = ELFNN_R_SYM (rel->r_info);
>+      ret->elf.dynindx = -1;
>+      *slot = ret;
>+    }
>+  return &ret->elf;
>+}
>+
>+/* Destroy a RISC-V elf linker hash table.  */
>+
>+static void
>+riscv_elf_link_hash_table_free (bfd *obfd)
>+{
>+  struct riscv_elf_link_hash_table *ret
>+    = (struct riscv_elf_link_hash_table *) obfd->link.hash;
>+
>+  if (ret->loc_hash_table)
>+    htab_delete (ret->loc_hash_table);
>+  if (ret->loc_hash_memory)
>+    objalloc_free ((struct objalloc *) ret->loc_hash_memory);
>+
>+  _bfd_elf_link_hash_table_free (obfd);
>+}
>+
> /* Create a RISC-V ELF linker hash table.  */
>
> static struct bfd_link_hash_table *
>@@ -288,6 +369,20 @@ riscv_elf_link_hash_table_create (bfd *abfd)
>     }
>
>   ret->max_alignment = (bfd_vma) -1;
>+
>+  /* Create hash table for local IFUNC.  */
>+  ret->loc_hash_table = htab_try_create (1024,
>+					 riscv_elf_local_htab_hash,
>+					 riscv_elf_local_htab_eq,
>+					 NULL);
>+  ret->loc_hash_memory = objalloc_create ();
>+  if (!ret->loc_hash_table || !ret->loc_hash_memory)
>+    {
>+      riscv_elf_link_hash_table_free (abfd);
>+      return NULL;
>+    }
>+  ret->elf.root.hash_table_free = riscv_elf_link_hash_table_free;
>+
>   return &ret->elf.root;
> }
>
>@@ -479,6 +574,9 @@ bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
> {
>   reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
>
>+  /* We propably can improve the information to tell users that they
>+     should be recompile the code with -fPIC or -fPIE, just like what
>+     x86 does.  */
>   (*_bfd_error_handler)
>     (_("%pB: relocation %s against `%s' can not be used when making a shared "
>        "object; recompile with -fPIC"),
>@@ -528,7 +626,32 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> 	}
>
>       if (r_symndx < symtab_hdr->sh_info)
>-	h = NULL;
>+	{
>+	  /* A local symbol.  */
>+	  Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
>+							  abfd, r_symndx);
>+	  if (isym == NULL)
>+	    return FALSE;
>+
>+	  /* Check relocation against local STT_GNU_IFUNC symbol.  */
>+	  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
>+	    {
>+	      h = riscv_elf_get_local_sym_hash (htab, abfd, rel, TRUE);
>+	      if (h == NULL)
>+		return FALSE;
>+
>+	      /* Fake STT_GNU_IFUNC global symbol.  */
>+	      h->root.root.string = bfd_elf_sym_name (abfd, symtab_hdr,
>+						      isym, NULL);
>+	      h->type = STT_GNU_IFUNC;
>+	      h->def_regular = 1;
>+	      h->ref_regular = 1;
>+	      h->forced_local = 1;
>+	      h->root.type = bfd_link_hash_defined;
>+	    }
>+	  else
>+	    h = NULL;
>+	}
>       else
> 	{
> 	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
>@@ -537,6 +660,32 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
> 	}
>
>+      if (h != NULL)
>+	{
>+	  switch (r_type)
>+	    {
>+	    case R_RISCV_32:
>+	    case R_RISCV_64:
>+	    case R_RISCV_CALL:
>+	    case R_RISCV_CALL_PLT:
>+	    case R_RISCV_HI20:
>+	    case R_RISCV_GOT_HI20:
>+	    case R_RISCV_PCREL_HI20:

The list has appears more than once with variance, I think a generic
relocation kind may help. 

>+	      /* Create the IFUNC sections, iplt and ipltgot, for static
>+		 executables.  */
>+	      if (h->type == STT_GNU_IFUNC
>+		  && !_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info))
>+		return FALSE;
>+	      break;
>+
>+	    default:
>+	      break;
>+	    }
>+
>+	  /* It is referenced by a non-shared object.  */
>+	  h->ref_regular = 1;
>+	}
>+
>       switch (r_type)
> 	{
> 	case R_RISCV_TLS_GD_HI20:
>@@ -573,17 +722,25 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> 	    }
> 	  break;
>
>-	case R_RISCV_CALL:
> 	case R_RISCV_JAL:
> 	case R_RISCV_BRANCH:
> 	case R_RISCV_RVC_BRANCH:
> 	case R_RISCV_RVC_JUMP:
>-	case R_RISCV_PCREL_HI20:
> 	  /* In shared libraries, these relocs are known to bind locally.  */
> 	  if (bfd_link_pic (info))
> 	    break;
> 	  goto static_reloc;
>
>+	case R_RISCV_CALL:
>+	case R_RISCV_PCREL_HI20:
>+	  /* In shared libraries, these relocs are known to bind locally,
>+	     except IFUNC symbol.  */
>+	  if (bfd_link_pic (info)
>+	      && (h == NULL
>+		  || h->type != STT_GNU_IFUNC))
>+	    break;
>+	  goto static_reloc;
>+
> 	case R_RISCV_TPREL_HI20:
> 	  if (!bfd_link_executable (info))
> 	    return bad_static_reloc (abfd, r_type, h);
>@@ -604,15 +761,23 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> 	  /* Fall through.  */
>
> 	static_reloc:
>-	  /* This reloc might not bind locally.  */
>-	  if (h != NULL)
>-	    h->non_got_ref = 1;
>
>-	  if (h != NULL && !bfd_link_pic (info))
>+	  if (h != NULL
>+	      && (bfd_link_executable (info)
>+		  || h->type == STT_GNU_IFUNC))
> 	    {
>-	      /* We may need a .plt entry if the function this reloc
>-		 refers to is in a shared lib.  */
>-	      h->plt.refcount += 1;
>+	      /* This reloc might not bind locally.  */
>+	      h->non_got_ref = 1;
>+	      h->pointer_equality_needed = 1;
>+
>+	      if (!h->def_regular
>+		  || (sec->flags & (SEC_CODE | SEC_READONLY)) != 0)
>+		{
>+		  /* We may need a .plt entry if the symbol is a function
>+		     defined in a shared lib or is a function referenced
>+		     from the code or read-only section.  */
>+		  h->plt.refcount += 1;
>+		}
> 	    }
>
> 	  /* If we are creating a shared library, and this is a reloc
>@@ -635,7 +800,10 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> 	     If on the other hand, we are creating an executable, we
> 	     may need to keep relocations for symbols satisfied by a
> 	     dynamic library if we manage to avoid copy relocs for the
>-	     symbol.  */
>+	     symbol.
>+
>+	     Generate dynamic pointer relocation against STT_GNU_IFUNC
>+	     symbol in the non-code section.  */
> 	  reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
>
> 	  if ((bfd_link_pic (info)
>@@ -649,7 +817,11 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
> 		  && (sec->flags & SEC_ALLOC) != 0
> 		  && h != NULL
> 		  && (h->root.type == bfd_link_hash_defweak
>-		      || !h->def_regular)))
>+		      || !h->def_regular))
>+	      || (!bfd_link_pic (info)
>+		  && h != NULL
>+		  && h->type == STT_GNU_IFUNC
>+		  && (sec->flags & SEC_CODE) == 0))
> 	    {
> 	      struct elf_dyn_relocs *p;
> 	      struct elf_dyn_relocs **head;
>@@ -786,9 +958,10 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
>   if (h->type == STT_FUNC || h->type == STT_GNU_IFUNC || h->needs_plt)
>     {
>       if (h->plt.refcount <= 0
>-	  || SYMBOL_CALLS_LOCAL (info, h)
>-	  || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
>-	      && h->root.type == bfd_link_hash_undefweak))
>+	  || (h->type != STT_GNU_IFUNC
>+	      && (SYMBOL_CALLS_LOCAL (info, h)
>+		  || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
>+		      && h->root.type == bfd_link_hash_undefweak))))
> 	{
> 	  /* This case can occur if we saw a R_RISCV_CALL_PLT reloc in an
> 	     input file, but the symbol was never referred to by a dynamic
>@@ -901,8 +1074,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
>   htab = riscv_elf_hash_table (info);
>   BFD_ASSERT (htab != NULL);
>
>-  if (htab->elf.dynamic_sections_created
>-      && h->plt.refcount > 0)
>+  /* Since STT_GNU_IFUNC symbols must go through PLT, we handle them
>+     in the allocate_ifunc_dynrelocs and allocate_local_ifunc_dynrelocs,
>+     if they are defined and referenced in a non-shared object.  */
>+  if (h->type == STT_GNU_IFUNC
>+      && h->def_regular)
>+    return TRUE;
>+  else if (htab->elf.dynamic_sections_created
>+	   && h->plt.refcount > 0)
>     {
>       /* Make sure this symbol is output as a dynamic symbol.
> 	 Undefined weak syms won't yet be marked as dynamic.  */
>@@ -1088,6 +1267,55 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
>   return TRUE;
> }
>
>+/* Allocate space in .plt, .got and associated reloc sections for
>+   ifunc dynamic relocs.  */
>+
>+static bfd_boolean
>+allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
>+			  void *inf)
>+{
>+  struct bfd_link_info *info;
>+
>+  if (h->root.type == bfd_link_hash_indirect)
>+    return TRUE;
>+
>+  if (h->root.type == bfd_link_hash_warning)
>+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
>+
>+  info = (struct bfd_link_info *) inf;
>+
>+  /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it
>+     here if it is defined and referenced in a non-shared object.  */
>+  if (h->type == STT_GNU_IFUNC
>+      && h->def_regular)
>+    return _bfd_elf_allocate_ifunc_dyn_relocs (info, h,
>+					       &h->dyn_relocs,
>+					       PLT_ENTRY_SIZE,
>+					       PLT_HEADER_SIZE,
>+					       GOT_ENTRY_SIZE,
>+					       TRUE);
>+  return TRUE;
>+}
>+
>+/* Allocate space in .plt, .got and associated reloc sections for
>+   local ifunc dynamic relocs.  */
>+
>+static bfd_boolean
>+allocate_local_ifunc_dynrelocs (void **slot, void *inf)
>+{
>+  struct elf_link_hash_entry *h
>+    = (struct elf_link_hash_entry *) *slot;
>+
>+  if (h->type != STT_GNU_IFUNC
>+      || !h->def_regular
>+      || !h->ref_regular
>+      || !h->forced_local
>+      || h->root.type != bfd_link_hash_defined)
>+    abort ();
>+
>+  return allocate_ifunc_dynrelocs (h, inf);
>+}
>+
> static bfd_boolean
> riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
> {
>@@ -1178,10 +1406,18 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
> 	}
>     }
>
>-  /* Allocate global sym .plt and .got entries, and space for global
>-     sym dynamic relocs.  */
>+  /* Allocate .plt and .got entries and space dynamic relocs for
>+     global symbols.  */
>   elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info);
>
>+  /* Allocate .plt and .got entries and space dynamic relocs for
>+     global IFUNC symbols.  */
>+  elf_link_hash_traverse (&htab->elf, allocate_ifunc_dynrelocs, info);
>+
>+  /* Allocate .plt and .got entries and space dynamic relocs for
>+     local IFUNC symbols.  */
>+  htab_traverse (htab->loc_hash_table, allocate_local_ifunc_dynrelocs, info);
>+
>   if (htab->elf.sgotplt)
>     {
>       struct elf_link_hash_entry *got;
>@@ -1213,6 +1449,8 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
>       if (s == htab->elf.splt
> 	  || s == htab->elf.sgot
> 	  || s == htab->elf.sgotplt
>+	  || s == htab->elf.iplt
>+	  || s == htab->elf.igotplt
> 	  || s == htab->elf.sdynbss
> 	  || s == htab->elf.sdynrelro
> 	  || s == htab->sdyntdata)
>@@ -1645,7 +1883,6 @@ riscv_elf_relocate_section (bfd *output_bfd,
>   Elf_Internal_Rela *relend;
>   riscv_pcrel_relocs pcrel_relocs;
>   bfd_boolean ret = FALSE;
>-  asection *sreloc = elf_section_data (input_section)->sreloc;
>   struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
>   Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd);
>   struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (input_bfd);
>@@ -1664,7 +1901,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
>       asection *sec;
>       bfd_vma relocation;
>       bfd_reloc_status_type r = bfd_reloc_ok;
>-      const char *name;
>+      const char *name = NULL;
>       bfd_vma off, ie_off;
>       bfd_boolean unresolved_reloc, is_ie = FALSE;
>       bfd_vma pc = sec_addr (input_section) + rel->r_offset;
>@@ -1689,6 +1926,19 @@ riscv_elf_relocate_section (bfd *output_bfd,
> 	  sym = local_syms + r_symndx;
> 	  sec = local_sections[r_symndx];
> 	  relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
>+
>+	  /* Relocate against local STT_GNU_IFUNC symbol.  */
>+	  if (!bfd_link_relocatable (info)
>+	      && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
>+	    {
>+	      h = riscv_elf_get_local_sym_hash (htab, input_bfd, rel, FALSE);
>+	      if (h == NULL)
>+		abort ();
>+
>+	      /* Set STT_GNU_IFUNC symbol value.  */
>+	      h->root.u.def.value = sym->st_value;
>+	      h->root.u.def.section = sec;
>+	    }
> 	}
>       else
> 	{
>@@ -1717,6 +1967,235 @@ riscv_elf_relocate_section (bfd *output_bfd,
>       if (bfd_link_relocatable (info))
> 	continue;
>
>+      /* 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;
>+
>+		    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;
>+
>+		r_type = ELFNN_R_TYPE (rel->r_info);
>+		howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
>+		if (howto == NULL)
>+		  r = bfd_reloc_notsupported;
>+		else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
>+						       relocation, 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:
>+		r_type = ELFNN_R_TYPE (rel->r_info);
>+		howto = riscv_elf_rtype_to_howto (input_bfd, r_type);
>+		if (howto == NULL)
>+		  r = bfd_reloc_notsupported;
>+		else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
>+						       relocation, 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:
>       if (h != NULL)
> 	name = h->root.root.string;
>       else
>@@ -2013,6 +2492,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
> 		      || h->root.type == bfd_link_hash_undefined)))
> 	    {
> 	      Elf_Internal_Rela outrel;
>+	      asection *sreloc;
> 	      bfd_boolean skip_static_relocation, skip_dynamic_relocation;
>
> 	      /* When generating a shared object, these relocations
>@@ -2042,6 +2522,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
> 		  outrel.r_addend = relocation + rel->r_addend;
> 		}
>
>+	      sreloc = elf_section_data (input_section)->sreloc;
> 	      riscv_elf_append_rela (output_bfd, sreloc, &outrel);
> 	      if (skip_static_relocation)
> 		continue;
>@@ -2217,6 +2698,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
> 	  r = bfd_reloc_notsupported;
> 	}
>
>+ do_relocation:
>       if (r == bfd_reloc_ok)
> 	r = perform_relocation (howto, rel, relocation, input_section,
> 				input_bfd, contents);
>@@ -2300,23 +2782,58 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
>     {
>       /* We've decided to create a PLT entry for this symbol.  */
>       bfd_byte *loc;
>-      bfd_vma i, header_address, plt_idx, got_address;
>+      bfd_vma i, header_address, plt_idx, got_offset, got_address;
>       uint32_t plt_entry[PLT_ENTRY_INSNS];
>       Elf_Internal_Rela rela;
>-
>-      BFD_ASSERT (h->dynindx != -1);
>+      asection *plt, *gotplt, *relplt;
>+
>+      /* When building a static executable, use .iplt, .igot.plt and
>+	 .rela.iplt sections for STT_GNU_IFUNC symbols.  */
>+      if (htab->elf.splt != NULL)
>+        {
>+          plt = htab->elf.splt;
>+          gotplt = htab->elf.sgotplt;
>+          relplt = htab->elf.srelplt;
>+        }
>+      else
>+        {
>+          plt = htab->elf.iplt;
>+          gotplt = htab->elf.igotplt;
>+          relplt = htab->elf.irelplt;
>+        }
>+
>+      /* This symbol has an entry in the procedure linkage table.  Set
>+         it up.  */
>+      if ((h->dynindx == -1
>+	   && !((h->forced_local || bfd_link_executable (info))
>+		&& h->def_regular
>+		&& h->type == STT_GNU_IFUNC))
>+	  || plt == NULL
>+	  || gotplt == NULL
>+	  || relplt == NULL)
>+	return FALSE;
>
>       /* Calculate the address of the PLT header.  */
>-      header_address = sec_addr (htab->elf.splt);
>+      header_address = sec_addr (plt);
>
>-      /* Calculate the index of the entry.  */
>-      plt_idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
>+      /* Calculate the index of the entry and the offset of .got.plt entry.
>+	 For static executables, we don't reserve anything.  */
>+      if (plt == htab->elf.splt)
>+	{
>+	  plt_idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
>+	  got_offset = GOTPLT_HEADER_SIZE + (plt_idx * GOT_ENTRY_SIZE);
>+	}
>+      else
>+	{
>+	  plt_idx = h->plt.offset / PLT_ENTRY_SIZE;
>+	  got_offset = plt_idx * GOT_ENTRY_SIZE;
>+	}
>
>       /* Calculate the address of the .got.plt entry.  */
>-      got_address = riscv_elf_got_plt_val (plt_idx, info);
>+      got_address = sec_addr (gotplt) + got_offset;
>
>       /* Find out where the .plt entry should go.  */
>-      loc = htab->elf.splt->contents + h->plt.offset;
>+      loc = plt->contents + h->plt.offset;
>
>       /* Fill in the PLT entry itself.  */
>       if (! riscv_make_plt_entry (output_bfd, got_address,
>@@ -2328,16 +2845,37 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
> 	bfd_put_32 (output_bfd, plt_entry[i], loc + 4*i);
>
>       /* Fill in the initial value of the .got.plt entry.  */
>-      loc = htab->elf.sgotplt->contents
>-	    + (got_address - sec_addr (htab->elf.sgotplt));
>-      bfd_put_NN (output_bfd, sec_addr (htab->elf.splt), loc);
>+      loc = gotplt->contents + (got_address - sec_addr (gotplt));
>+      bfd_put_NN (output_bfd, sec_addr (plt), loc);
>
>-      /* Fill in the entry in the .rela.plt section.  */
>       rela.r_offset = got_address;
>-      rela.r_addend = 0;
>-      rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_JUMP_SLOT);
>
>-      loc = htab->elf.srelplt->contents + plt_idx * sizeof (ElfNN_External_Rela);
>+      if (h->dynindx == -1
>+	  || ((bfd_link_executable (info)
>+	       || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
>+	      && h->def_regular
>+	      && h->type == STT_GNU_IFUNC))
>+	{
>+	  info->callbacks->minfo (_("Local IFUNC function `%s' in %pB\n"),
>+				  h->root.root.string,
>+				  h->root.u.def.section->owner);
>+
>+	  /* If an STT_GNU_IFUNC symbol is locally defined, generate
>+	     R_RISCV_IRELATIVE instead of R_RISCV_JUMP_SLOT.  */
>+	  asection *sec = h->root.u.def.section;
>+	  rela.r_info = ELFNN_R_INFO (0, R_RISCV_IRELATIVE);
>+	  rela.r_addend = h->root.u.def.value
>+			  + sec->output_section->vma
>+			  + sec->output_offset;
>+	}
>+      else
>+	{
>+	  /* Fill in the entry in the .rela.plt section.  */
>+	  rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_JUMP_SLOT);
>+	  rela.r_addend = 0;
>+	}
>+
>+      loc = relplt->contents + plt_idx * sizeof (ElfNN_External_Rela);
>       bed->s->swap_reloca_out (output_bfd, &rela, loc);
>
>       if (!h->def_regular)
>@@ -2370,13 +2908,73 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
>
>       rela.r_offset = sec_addr (sgot) + (h->got.offset &~ (bfd_vma) 1);
>
>+      /* Handle the IFUNC symbol in GOT entry.  */
>+      if (h->def_regular
>+	  && h->type == STT_GNU_IFUNC)
>+	{
>+	  if (h->plt.offset == (bfd_vma) -1)
>+	    {
>+	      /* STT_GNU_IFUNC is referenced without PLT.  */
>+	      if (htab->elf.splt == NULL)
>+		{
>+		  /* use .rel[a].iplt section to store .got relocations
>+		     in static executable.  */
>+		  srela = htab->elf.irelplt;
>+		}
>+	      if (SYMBOL_REFERENCES_LOCAL (info, h))
>+		{
>+		  info->callbacks->minfo (_("Local IFUNC function `%s' in %pB\n"),
>+					  h->root.root.string,
>+					  h->root.u.def.section->owner);
>+
>+		  rela.r_info = ELFNN_R_INFO (0, R_RISCV_IRELATIVE);
>+		  rela.r_addend = (h->root.u.def.value
>+				   + h->root.u.def.section->output_section->vma
>+				   + h->root.u.def.section->output_offset);
>+		}
>+	      else
>+		{
>+		  /* Generate R_RISCV_NN.  */
>+		  BFD_ASSERT((h->got.offset & 1) == 0);
>+		  BFD_ASSERT (h->dynindx != -1);
>+		  rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_NN);
>+		  rela.r_addend = 0;
>+		}
>+	    }
>+	  else if (bfd_link_pic (info))
>+	    {
>+	      /* Generate R_RISCV_NN.  */
>+	      BFD_ASSERT((h->got.offset & 1) == 0);
>+	      BFD_ASSERT (h->dynindx != -1);
>+	      rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_NN);
>+	      rela.r_addend = 0;
>+	    }
>+	  else
>+	    {
>+	      asection *plt;
>+
>+	      if (!h->pointer_equality_needed)
>+		abort ();
>+
>+	      /* For non-shared object, we can't use .got.plt, which
>+		 contains the real function address if we need pointer
>+		 equality.  We load the GOT entry with the PLT entry.  */
>+	      plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
>+	      bfd_put_NN (output_bfd, (plt->output_section->vma
>+				       + plt->output_offset
>+				       + h->plt.offset),
>+			  htab->elf.sgot->contents
>+			  + (h->got.offset & ~(bfd_vma) 1));
>+	      return TRUE;
>+	    }
>+	}
>       /* If this is a local symbol reference, we just want to emit a RELATIVE
> 	 reloc.  This can happen if it is a -Bsymbolic link, or a pie link, or
> 	 the symbol was forced to be local because of a version file.
> 	 The entry in the global offset table will already have been
> 	 initialized in the relocate_section function.  */
>-      if (bfd_link_pic (info)
>-	  && SYMBOL_REFERENCES_LOCAL (info, h))
>+      else if (bfd_link_pic (info)
>+	       && SYMBOL_REFERENCES_LOCAL (info, h))
> 	{
> 	  BFD_ASSERT((h->got.offset & 1) != 0);
> 	  asection *sec = h->root.u.def.section;
>@@ -2424,6 +3022,18 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
>   return TRUE;
> }
>
>+/* Finish up local dynamic symbol handling.  We set the contents of
>+   various dynamic sections here.  */
>+
>+static bfd_boolean
>+riscv_elf_finish_local_dynamic_symbol (void **slot, void *inf)
>+{
>+  struct elf_link_hash_entry *h = (struct elf_link_hash_entry *) *slot;
>+  struct bfd_link_info *info = (struct bfd_link_info *) inf;
>+
>+  return riscv_elf_finish_dynamic_symbol (info->output_bfd, info, h, NULL);
>+}
>+
> /* Finish up the dynamic sections.  */
>
> static bfd_boolean
>@@ -2550,6 +3160,11 @@ riscv_elf_finish_dynamic_sections (bfd *output_bfd,
>       elf_section_data (output_section)->this_hdr.sh_entsize = GOT_ENTRY_SIZE;
>     }
>
>+  /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols.  */
>+  htab_traverse (htab->loc_hash_table,
>+		 riscv_elf_finish_local_dynamic_symbol,
>+		 info);
>+
>   return TRUE;
> }
>
>@@ -4046,6 +4661,12 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
> 	  reserve_size = (isym->st_size - rel->r_addend) > isym->st_size
> 	    ? 0 : isym->st_size - rel->r_addend;
>
>+	  /* Relocate against local STT_GNU_IFUNC symbol.  we have created
>+	     a fake global symbol entry for this, so deal with the local IFUNC
>+	     as a global.  */
>+	  if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
>+	    continue;
>+
> 	  if (isym->st_shndx == SHN_UNDEF)
> 	    sym_sec = sec, symval = rel->r_offset;
> 	  else
>@@ -4076,6 +4697,10 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
> 		 || h->root.type == bfd_link_hash_warning)
> 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
>
>+	  /* Disable the relaxation for IFUNC.  */
>+	  if (h != NULL && h->type == STT_GNU_IFUNC)
>+	    continue;
>+
> 	  if (h->root.type == bfd_link_hash_undefweak
> 	      && (relax_func == _bfd_riscv_relax_lui
> 		  || relax_func == _bfd_riscv_relax_pc))
>diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
>index 1570f1d..ce300e4 100644
>--- a/bfd/elfxx-riscv.c
>+++ b/bfd/elfxx-riscv.c
>@@ -854,6 +854,21 @@ static reloc_howto_type howto_table[] =
> 	 0,				/* src_mask */
> 	 MINUS_ONE,			/* dst_mask */
> 	 FALSE),			/* pcrel_offset */
>+
>+  /* Relocation against a local ifunc symbol in a shared object.  */
>+  HOWTO (R_RISCV_IRELATIVE,		/* type */
>+	 0,				/* rightshift */
>+	 2,				/* size */
>+	 32,				/* bitsize */

On 64-bit this should be 64.

>+	 FALSE,				/* pc_relative */
>+	 0,				/* bitpos */
>+	 complain_overflow_dont,	/* complain_on_overflow */
>+	 bfd_elf_generic_reloc,		/* special_function */
>+	 "R_RISCV_IRELATIVE",		/* name */
>+	 FALSE,				/* partial_inplace */
>+	 0,				/* src_mask */
>+	 MINUS_ONE,			/* dst_mask */
>+	 FALSE),			/* pcrel_offset */
> };
>
> /* A mapping from BFD reloc types to RISC-V ELF reloc types.  */
>diff --git a/include/elf/riscv.h b/include/elf/riscv.h
>index 5062a49..98c7ac6 100644
>--- a/include/elf/riscv.h
>+++ b/include/elf/riscv.h
>@@ -88,6 +88,7 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type)
>   RELOC_NUMBER (R_RISCV_SET16, 55)
>   RELOC_NUMBER (R_RISCV_SET32, 56)
>   RELOC_NUMBER (R_RISCV_32_PCREL, 57)
>+  RELOC_NUMBER (R_RISCV_IRELATIVE, 58)
> END_RELOC_NUMBERS (R_RISCV_max)
>
> /* Processor specific flags for the ELF header e_flags field.  */
>diff --git a/ld/emulparams/elf32lriscv-defs.sh b/ld/emulparams/elf32lriscv-defs.sh
>index bc46491..b823ced 100644
>--- a/ld/emulparams/elf32lriscv-defs.sh
>+++ b/ld/emulparams/elf32lriscv-defs.sh
>@@ -26,6 +26,7 @@ case "$target" in
>     ;;
> esac
>
>+IREL_IN_PLT=
> TEXT_START_ADDR=0x10000
> MAXPAGESIZE="CONSTANT (MAXPAGESIZE)"
> COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)"
>diff --git a/ld/testsuite/ld-ifunc/ifunc.exp b/ld/testsuite/ld-ifunc/ifunc.exp
>index 08cc878..020b48e 100644
>--- a/ld/testsuite/ld-ifunc/ifunc.exp
>+++ b/ld/testsuite/ld-ifunc/ifunc.exp
>@@ -39,7 +39,6 @@ if { ![is_elf_format] || ![supports_gnu_osabi]
>      || [istarget nds32*-*-*]
>      || [istarget nios2-*-*]
>      || [istarget or1k-*-*]
>-     || [istarget riscv*-*-*]
>      || [istarget score*-*-*]
>      || [istarget sh*-*-*]
>      || [istarget tic6x-*-*]
>@@ -730,7 +729,8 @@ run_ld_link_exec_tests [list \
> if { [isnative]
>      && !([istarget "powerpc-*-*"]
>            || [istarget "aarch64*-*-*"]
>-           || [istarget "sparc*-*-*"]) } {
>+           || [istarget "sparc*-*-*"]
>+           || [istarget "riscv*-*-*"]) } {
> run_ld_link_exec_tests [list \
>     [list \
> 	"Run pr23169a" \
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-1-local.d b/ld/testsuite/ld-riscv-elf/ifunc-1-local.d
>new file mode 100644
>index 0000000..15faecc
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-1-local.d
>@@ -0,0 +1,11 @@
>+#target: [check_shared_lib_support]
>+#ld: -shared --hash-style=sysv -z norelro
>+#objdump: -dw
>+#map: ifunc-1-local.map
>+
>+#...
>+0+[0-9a-f]+ <__GI_foo>:
>+#...
>+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
>+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<\*ABS\*\+0x[0-9a-f]+@plt>
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-1-local.map b/ld/testsuite/ld-riscv-elf/ifunc-1-local.map
>new file mode 100644
>index 0000000..95313a5
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-1-local.map
>@@ -0,0 +1,3 @@
>+#...
>+Local IFUNC function `__GI_foo' in tmpdir/ifunc-1-local.o
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-1-local.s b/ld/testsuite/ld-riscv-elf/ifunc-1-local.s
>new file mode 100644
>index 0000000..cdfe37a
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-1-local.s
>@@ -0,0 +1,13 @@
>+	.type foo, %gnu_indirect_function
>+	.set __GI_foo, foo
>+	.text
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+.globl bar
>+	.type	bar, @function
>+bar:
>+	call	__GI_foo@plt
>+	ret
>+	.size	bar, .-bar
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-1.d b/ld/testsuite/ld-riscv-elf/ifunc-1.d
>new file mode 100644
>index 0000000..ee495f0
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-1.d
>@@ -0,0 +1,11 @@
>+#target: [check_shared_lib_support]
>+#ld: -shared --hash-style=sysv
>+#objdump: -dw
>+#map: ifunc-1.map
>+
>+#...
>+0+[0-9a-f]+ <foo>:
>+#...
>+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
>+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<\*ABS\*\+0x[0-9a-f]+@plt>
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-1.map b/ld/testsuite/ld-riscv-elf/ifunc-1.map
>new file mode 100644
>index 0000000..c5077d9
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-1.map
>@@ -0,0 +1,3 @@
>+#...
>+Local IFUNC function `__GI_foo' in tmpdir/ifunc-1.o
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-1.s b/ld/testsuite/ld-riscv-elf/ifunc-1.s
>new file mode 100644
>index 0000000..40922c2
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-1.s
>@@ -0,0 +1,16 @@
>+	.type foo, %gnu_indirect_function
>+	.global __GI_foo
>+	.hidden __GI_foo
>+	.set __GI_foo, foo
>+	.text
>+.globl foo
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+.globl bar
>+	.type	bar, @function
>+bar:
>+	call	__GI_foo@plt
>+	ret
>+	.size	bar, .-bar
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-10.d b/ld/testsuite/ld-riscv-elf/ifunc-10.d
>new file mode 100644
>index 0000000..7428057
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-10.d
>@@ -0,0 +1,4 @@
>+#ld: -e bar --gc-sections
>+#readelf: -r --wide
>+
>+There are no relocations in this file.
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-10.s b/ld/testsuite/ld-riscv-elf/ifunc-10.s
>new file mode 100644
>index 0000000..34ba4e5
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-10.s
>@@ -0,0 +1,43 @@
>+        .section .text.foo,"ax",@progbits
>+        .type foo, @function
>+foo:
>+        .global foo
>+.L1:
>+	auipc   x1, %got_pcrel_hi (ifunc)
>+.ifdef __64_bit__
>+	ld      x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+.L2:
>+	auipc   x1, %pcrel_hi (ifunc)
>+.ifdef __64_bit__
>+	ld      x1, %pcrel_lo (.L2) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+	call	ifunc@plt
>+	call	ifunc
>+.L3:
>+	auipc	x1, %pcrel_hi (xxx)
>+	addi	x1, x1, %pcrel_lo (.L3)
>+        ret
>+
>+        .section .text.bar,"ax",@progbits
>+        .type bar, @function
>+bar:
>+        .global bar
>+        ret
>+
>+        .section .text.ifunc,"ax",@progbits
>+        .type ifunc, @gnu_indirect_function
>+ifunc:
>+        ret
>+
>+        .section .data.foo,"aw",@progbits
>+xxx:
>+.ifdef __64_bit__
>+	.quad ifunc
>+.else
>+	.long ifunc
>+.endif
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-11.s b/ld/testsuite/ld-riscv-elf/ifunc-11.s
>new file mode 100644
>index 0000000..aeba5c2
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-11.s
>@@ -0,0 +1,44 @@
>+        .section .text.foo,"ax",@progbits
>+        .type foo, @function
>+foo:
>+        .global foo
>+.L1:
>+	auipc   x1, %got_pcrel_hi (ifunc)
>+.ifdef __64_bit__
>+	ld      x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+.L2:
>+	auipc   x1, %pcrel_hi (ifunc)
>+.ifdef __64_bit__
>+	ld      x1, %pcrel_lo (.L2) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+	call	ifunc@plt
>+	call	ifunc
>+.L3:
>+	auipc	x1, %pcrel_hi (xxx)
>+	addi	x1, x1, %pcrel_lo (.L3)
>+        ret
>+
>+        .section .text.bar,"ax",@progbits
>+        .type bar, @function
>+bar:
>+        .global bar
>+        ret
>+
>+        .section .text.ifunc,"ax",@progbits
>+        .type ifunc, @gnu_indirect_function
>+        .global ifunc
>+ifunc:
>+        ret
>+
>+        .section .data.foo,"aw",@progbits
>+xxx:
>+.ifdef __64_bit__
>+	.quad ifunc
>+.else
>+	.long ifunc
>+.endif
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-11a.d b/ld/testsuite/ld-riscv-elf/ifunc-11a.d
>new file mode 100644
>index 0000000..b72ea90
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-11a.d
>@@ -0,0 +1,5 @@
>+#source: ifunc-11.s
>+#ld: -e bar --gc-sections
>+#readelf: -r --wide
>+
>+There are no relocations in this file.
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-11b.d b/ld/testsuite/ld-riscv-elf/ifunc-11b.d
>new file mode 100644
>index 0000000..e41e61c
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-11b.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-11.s
>+#ld: -e bar
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-12.d b/ld/testsuite/ld-riscv-elf/ifunc-12.d
>new file mode 100644
>index 0000000..7d01e76
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-12.d
>@@ -0,0 +1,5 @@
>+#target: [check_shared_lib_support]
>+#ld: -shared -e bar --gc-sections
>+#readelf: -r --wide
>+
>+There are no relocations in this file.
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-12.s b/ld/testsuite/ld-riscv-elf/ifunc-12.s
>new file mode 100644
>index 0000000..e341155
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-12.s
>@@ -0,0 +1,42 @@
>+        .section .text.foo,"ax",@progbits
>+        .type foo, @function
>+foo:
>+.L1:
>+	auipc   x1, %got_pcrel_hi (ifunc)
>+.ifdef __64_bit__
>+	ld      x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+.L2:
>+	auipc   x1, %pcrel_hi (ifunc)
>+.ifdef __64_bit__
>+	ld      x1, %pcrel_lo (.L2) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+	call	ifunc@plt
>+	call	ifunc
>+.L3:
>+	auipc	x1, %pcrel_hi (xxx)
>+	addi	x1, x1, %pcrel_lo (.L3)
>+        ret
>+
>+        .section .text.bar,"ax",@progbits
>+        .type bar, @function
>+bar:
>+        .global bar
>+        ret
>+
>+        .section .text.ifunc,"ax",@progbits
>+        .type ifunc, @gnu_indirect_function
>+ifunc:
>+        ret
>+
>+        .section .data.foo,"aw",@progbits
>+xxx:
>+.ifdef __64_bit__
>+	.quad ifunc
>+.else
>+	.long ifunc
>+.endif
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-13.d b/ld/testsuite/ld-riscv-elf/ifunc-13.d
>new file mode 100644
>index 0000000..475bc7b
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-13.d
>@@ -0,0 +1,9 @@
>+#source: ifunc-13a.s
>+#source: ifunc-13b.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+Relocation section '.rela.ifunc' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+ifunc\(\)[ 	]+ifunc \+ 0
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-13a.s b/ld/testsuite/ld-riscv-elf/ifunc-13a.s
>new file mode 100644
>index 0000000..ae5ef75
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-13a.s
>@@ -0,0 +1,12 @@
>+	.text
>+        .type foo, @function
>+	.global foo
>+foo:
>+.L1:
>+	auipc	x1, %pcrel_hi (xxx)
>+	addi	x1, x1, %pcrel_lo (.L1)
>+        ret
>+
>+	.data
>+xxx:
>+	.quad ifunc
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-13b.s b/ld/testsuite/ld-riscv-elf/ifunc-13b.s
>new file mode 100644
>index 0000000..3560394
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-13b.s
>@@ -0,0 +1,5 @@
>+	.text
>+        .type ifunc, @gnu_indirect_function
>+	.globl ifunc
>+ifunc:
>+        ret
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-14a.d b/ld/testsuite/ld-riscv-elf/ifunc-14a.d
>new file mode 100644
>index 0000000..959b495
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-14a.d
>@@ -0,0 +1,10 @@
>+#source: ifunc-14a.s
>+#source: ifunc-14b.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -d
>+
>+#failif
>+#...
>+.*\(TEXTREL\).*
>+#...
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-14a.s b/ld/testsuite/ld-riscv-elf/ifunc-14a.s
>new file mode 100644
>index 0000000..876988c
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-14a.s
>@@ -0,0 +1,7 @@
>+	.text
>+	.globl bar
>+	.type	bar, @function
>+bar:
>+	call	foo@plt
>+	.size	bar, .-bar
>+	.hidden	foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-14b.d b/ld/testsuite/ld-riscv-elf/ifunc-14b.d
>new file mode 100644
>index 0000000..ac4db0b
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-14b.d
>@@ -0,0 +1,10 @@
>+#source: ifunc-14b.s
>+#source: ifunc-14a.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -d
>+
>+#failif
>+#...
>+.*\(TEXTREL\).*
>+#...
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-14b.s b/ld/testsuite/ld-riscv-elf/ifunc-14b.s
>new file mode 100644
>index 0000000..bac22eb
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-14b.s
>@@ -0,0 +1,5 @@
>+	.type foo, %gnu_indirect_function
>+	.globl foo
>+foo:
>+	ret
>+	.size	foo, .-foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-14c.d b/ld/testsuite/ld-riscv-elf/ifunc-14c.d
>new file mode 100644
>index 0000000..df86f62
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-14c.d
>@@ -0,0 +1,10 @@
>+#source: ifunc-14a.s
>+#source: ifunc-14b.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+#failif
>+#...
>+.* +R_RISCV_NONE +.*
>+#...
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-14c.s b/ld/testsuite/ld-riscv-elf/ifunc-14c.s
>new file mode 100644
>index 0000000..8cf89c3
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-14c.s
>@@ -0,0 +1,7 @@
>+	.text
>+	.globl xxx
>+	.type	xxx, @function
>+xxx:
>+	call	foo@plt
>+	.size	xxx, .-xxx
>+	.hidden	foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-14d.d b/ld/testsuite/ld-riscv-elf/ifunc-14d.d
>new file mode 100644
>index 0000000..b186471
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-14d.d
>@@ -0,0 +1,10 @@
>+#source: ifunc-14b.s
>+#source: ifunc-14a.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+#failif
>+#...
>+.* +R_RISCV_NONE +.*
>+#...
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-14e.d b/ld/testsuite/ld-riscv-elf/ifunc-14e.d
>new file mode 100644
>index 0000000..f365724
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-14e.d
>@@ -0,0 +1,11 @@
>+#source: ifunc-14a.s
>+#source: ifunc-14c.s
>+#source: ifunc-14b.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+#failif
>+#...
>+.* +R_RISCV_NONE +.*
>+#...
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-14f.d b/ld/testsuite/ld-riscv-elf/ifunc-14f.d
>new file mode 100644
>index 0000000..4d78ca7
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-14f.d
>@@ -0,0 +1,11 @@
>+#source: ifunc-14a.s
>+#source: ifunc-14b.s
>+#source: ifunc-14c.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+#failif
>+#...
>+.* +R_RISCV_NONE +.*
>+#...
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-15.d b/ld/testsuite/ld-riscv-elf/ifunc-15.d
>new file mode 100644
>index 0000000..41a5caa
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-15.d
>@@ -0,0 +1,7 @@
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+Relocation section '.rela.got' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+ifunc\(\)[ 	]+ifunc \+ 0
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-15.s b/ld/testsuite/ld-riscv-elf/ifunc-15.s
>new file mode 100644
>index 0000000..963c517
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-15.s
>@@ -0,0 +1,16 @@
>+	.text
>+        .type foo, @function
>+	.global foo
>+foo:
>+.L1:
>+	auipc	x1, %got_pcrel_hi (ifunc)
>+.ifdef __64_bit__
>+	ld	x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+        ret
>+        .type ifunc, @gnu_indirect_function
>+	.globl ifunc
>+ifunc:
>+        ret
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-16-now.d b/ld/testsuite/ld-riscv-elf/ifunc-16-now.d
>new file mode 100644
>index 0000000..3b2bb21
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-16-now.d
>@@ -0,0 +1,8 @@
>+#source: ifunc-16.s
>+#ld: -z now -shared
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_JUMP_SLOT[ 	]+0+[ 	]+ifunc \+ 0
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-16.d b/ld/testsuite/ld-riscv-elf/ifunc-16.d
>new file mode 100644
>index 0000000..787eb72
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-16.d
>@@ -0,0 +1,8 @@
>+#target: [check_shared_lib_support]
>+#ld: -shared
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_JUMP_SLOT[ 	]+0+[ 	]+ifunc \+ 0
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-16.s b/ld/testsuite/ld-riscv-elf/ifunc-16.s
>new file mode 100644
>index 0000000..bb7817a
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-16.s
>@@ -0,0 +1,17 @@
>+	.text
>+	.globl	fct
>+	.type	fct, @gnu_indirect_function
>+	.set	fct,resolve
>+	.hidden int_fct
>+	.globl	int_fct
>+	.set	int_fct,fct
>+	.p2align 4,,15
>+	.type	resolve, @function
>+resolve:
>+	call	ifunc@plt
>+	.size	resolve, .-resolve
>+	.globl	g
>+	.type	g, @function
>+g:
>+	call	int_fct@plt
>+	.size	g, .-g
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-17a.d b/ld/testsuite/ld-riscv-elf/ifunc-17a.d
>new file mode 100644
>index 0000000..30d9f64
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-17a.d
>@@ -0,0 +1,8 @@
>+#source: ifunc-17a.s
>+#source: ifunc-17b.s
>+#ld: -static
>+#readelf: -s --wide
>+
>+#...
>+ +[0-9]+: +[0-9a-f]+ +4 +OBJECT +GLOBAL +DEFAULT +[1-9] foo
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-17a.s b/ld/testsuite/ld-riscv-elf/ifunc-17a.s
>new file mode 100644
>index 0000000..e0bde49
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-17a.s
>@@ -0,0 +1,11 @@
>+	.globl main
>+	.globl start
>+	.globl _start
>+	.globl __start
>+	.text
>+main:
>+start:
>+_start:
>+__start:
>+	.byte 0
>+	.common	foo,4,4
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-17b.d b/ld/testsuite/ld-riscv-elf/ifunc-17b.d
>new file mode 100644
>index 0000000..fc58527
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-17b.d
>@@ -0,0 +1,8 @@
>+#source: ifunc-17b.s
>+#source: ifunc-17a.s
>+#ld: -static
>+#readelf: -s --wide
>+
>+#...
>+ +[0-9]+: +[0-9a-f]+ +4 +OBJECT +GLOBAL +DEFAULT +[1-9] foo
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-17b.s b/ld/testsuite/ld-riscv-elf/ifunc-17b.s
>new file mode 100644
>index 0000000..66abe04
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-17b.s
>@@ -0,0 +1,6 @@
>+	.weak	foo
>+	.type foo, %gnu_indirect_function
>+	.size	foo,1
>+	.text
>+foo:
>+	.byte	1
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-18a.d b/ld/testsuite/ld-riscv-elf/ifunc-18a.d
>new file mode 100644
>index 0000000..d15eb74
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-18a.d
>@@ -0,0 +1,13 @@
>+#source: ifunc-18a.s
>+#source: ifunc-18b.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+Relocation section '.rela.ifunc' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-18a.s b/ld/testsuite/ld-riscv-elf/ifunc-18a.s
>new file mode 100644
>index 0000000..f68c151
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-18a.s
>@@ -0,0 +1,5 @@
>+	.section .data.rel,"aw",@progbits
>+	.globl foo_ptr
>+	.type	foo_ptr, @object
>+foo_ptr:
>+	.dc.a foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-18b.d b/ld/testsuite/ld-riscv-elf/ifunc-18b.d
>new file mode 100644
>index 0000000..93513df
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-18b.d
>@@ -0,0 +1,13 @@
>+#source: ifunc-18b.s
>+#source: ifunc-18a.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+Relocation section '.rela.ifunc' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-18b.s b/ld/testsuite/ld-riscv-elf/ifunc-18b.s
>new file mode 100644
>index 0000000..b136348
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-18b.s
>@@ -0,0 +1,15 @@
>+	.text
>+	.type foo, %gnu_indirect_function
>+	.hidden foo
>+	.globl foo
>+foo:
>+	ret
>+	.size	foo, .-foo
>+	.globl bar
>+bar:
>+	call	foo1@plt
>+	ret
>+	.size	bar, .-bar
>+	.hidden foo1
>+	.globl foo1
>+	foo1 = foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-19a.d b/ld/testsuite/ld-riscv-elf/ifunc-19a.d
>new file mode 100644
>index 0000000..bc7bab5
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-19a.d
>@@ -0,0 +1,13 @@
>+#source: ifunc-19a.s
>+#source: ifunc-19b.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+Relocation section '.rela.ifunc' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-19a.s b/ld/testsuite/ld-riscv-elf/ifunc-19a.s
>new file mode 100644
>index 0000000..3a3d0cd
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-19a.s
>@@ -0,0 +1,5 @@
>+	.section .data.rel,"aw",@progbits
>+	.globl foo_ptrt
>+	.type	foo_ptr, @object
>+foo_ptr:
>+	.dc.a foo1
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-19b.d b/ld/testsuite/ld-riscv-elf/ifunc-19b.d
>new file mode 100644
>index 0000000..e018c62
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-19b.d
>@@ -0,0 +1,13 @@
>+#source: ifunc-19b.s
>+#source: ifunc-19a.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+Relocation section '.rela.ifunc' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-19b.s b/ld/testsuite/ld-riscv-elf/ifunc-19b.s
>new file mode 100644
>index 0000000..b136348
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-19b.s
>@@ -0,0 +1,15 @@
>+	.text
>+	.type foo, %gnu_indirect_function
>+	.hidden foo
>+	.globl foo
>+foo:
>+	ret
>+	.size	foo, .-foo
>+	.globl bar
>+bar:
>+	call	foo1@plt
>+	ret
>+	.size	bar, .-bar
>+	.hidden foo1
>+	.globl foo1
>+	foo1 = foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-2-local.d b/ld/testsuite/ld-riscv-elf/ifunc-2-local.d
>new file mode 100644
>index 0000000..da16a04
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-2-local.d
>@@ -0,0 +1,12 @@
>+#target: [check_shared_lib_support]
>+#ld: -shared --hash-style=sysv
>+#objdump: -dw
>+
>+#...
>+0+[0-9a-f]+ <__GI_foo>:
>+#...
>+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
>+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<\*ABS\*\+0x[0-9a-f]+@plt>
>+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
>+.*:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+.*<\*ABS\*\+0x[0-9a-f]+@plt>
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-2-local.s b/ld/testsuite/ld-riscv-elf/ifunc-2-local.s
>new file mode 100644
>index 0000000..f1df863
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-2-local.s
>@@ -0,0 +1,16 @@
>+	.type foo, %gnu_indirect_function
>+	.set __GI_foo, foo
>+	.text
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+.globl bar
>+	.type	bar, @function
>+bar:
>+	call	__GI_foo
>+.L1:
>+	auipc	x1, %pcrel_hi (__GI_foo)
>+	addi	x1, x1, %pcrel_lo (.L1)
>+	ret
>+	.size	bar, .-bar
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-2.d b/ld/testsuite/ld-riscv-elf/ifunc-2.d
>new file mode 100644
>index 0000000..49320bd
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-2.d
>@@ -0,0 +1,13 @@
>+#target: [check_shared_lib_support]
>+#ld: -shared --hash-style=sysv
>+#objdump: -dw
>+#warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIC
>+
>+#...
>+0+[0-9a-f]+ <foo>:
>+#...
>+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
>+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<\*ABS\*\+0x[0-9a-f]+@plt>
>+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
>+.*:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+.*<\*ABS\*\+0x[0-9a-f]+@plt>
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-2.s b/ld/testsuite/ld-riscv-elf/ifunc-2.s
>new file mode 100644
>index 0000000..3287880
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-2.s
>@@ -0,0 +1,19 @@
>+	.type foo, %gnu_indirect_function
>+	.global __GI_foo
>+	.hidden __GI_foo
>+	.set __GI_foo, foo
>+	.text
>+.globl foo
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+.globl bar
>+	.type	bar, @function
>+bar:
>+	call	__GI_foo
>+.L1:
>+	auipc	x1, %pcrel_hi (__GI_foo)
>+	addi	x1, x1, %pcrel_lo (.L1)
>+	ret
>+	.size	bar, .-bar
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-20.d b/ld/testsuite/ld-riscv-elf/ifunc-20.d
>new file mode 100644
>index 0000000..71461fb
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-20.d
>@@ -0,0 +1,12 @@
>+#source: ifunc-20.s
>+#target: [check_shared_lib_support]
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+Relocation section '.rela.ifunc' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+ifunc\(\)[ 	]+ifunc \+ 0
>+
>+Relocation section '.rela.plt' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_JUMP_SLOT[ 	]+ifunc\(\)[ 	]+ifunc \+ 0
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-20.s b/ld/testsuite/ld-riscv-elf/ifunc-20.s
>new file mode 100644
>index 0000000..0e85fed
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-20.s
>@@ -0,0 +1,16 @@
>+	.section .data.rel,"aw",@progbits
>+	.globl ifunc_ptrt
>+	.type	ifunc_ptr, @object
>+ifunc_ptr:
>+	.dc.a ifunc
>+	.text
>+	.type ifunc, @gnu_indirect_function
>+	.globl ifunc
>+ifunc:
>+	ret
>+	.size	ifunc, .-ifunc
>+	.type bar, @function
>+	.globl bar
>+bar:
>+	call	ifunc@plt
>+	.size	bar, .-bar
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-21.d b/ld/testsuite/ld-riscv-elf/ifunc-21.d
>new file mode 100644
>index 0000000..2f7509e
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-21.d
>@@ -0,0 +1,20 @@
>+#target: [check_shared_lib_support]
>+#ld: -shared --hash-style=sysv -z max-page-size=0x200000 -z noseparate-code
>+#objdump: -dw
>+
>+.*: +file format .*
>+
>+Disassembly of section .text:
>+
>+.* <__start>:
>+.*:[	 ]+[0-9a-f]+[	 ]+auipc[ 	]+ra,0x200
>+.*:[	 ]+[0-9a-f]+[	 ]+(lw|ld)[ 	]+.*<_GLOBAL_OFFSET_TABLE_\+.*>
>+.*:[	 ]+[0-9a-f]+[	 ]+auipc[ 	]+ra,0x200
>+.*:[	 ]+[0-9a-f]+[	 ]+(lw|ld)[ 	]+.*<_GLOBAL_OFFSET_TABLE_\+.*>
>+
>+.* <foo>:
>+.*:[ 	]+00008067[ 	]+ret
>+
>+.* <bar>:
>+.*:[ 	]+00008067[ 	]+ret
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-21.s b/ld/testsuite/ld-riscv-elf/ifunc-21.s
>new file mode 100644
>index 0000000..6b1b556
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-21.s
>@@ -0,0 +1,31 @@
>+	.text
>+	.type start,"function"
>+	.global start
>+start:
>+	.type _start,"function"
>+	.global _start
>+_start:
>+	.type __start,"function"
>+	.global __start
>+__start:
>+	.type __start,"function"
>+.L1:
>+	auipc	x1, %got_pcrel_hi (foo)
>+.ifdef __64_bit__
>+	ld	x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+.L2:
>+	auipc	x1, %got_pcrel_hi (bar)
>+.ifdef __64_bit__
>+	ld	x1, %pcrel_lo (.L2) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+	.type foo, %gnu_indirect_function
>+foo:
>+	ret
>+	.type bar, %function
>+bar:
>+	ret
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-22.d b/ld/testsuite/ld-riscv-elf/ifunc-22.d
>new file mode 100644
>index 0000000..58352a1
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-22.d
>@@ -0,0 +1,20 @@
>+#target: [check_shared_lib_support]
>+#ld: -shared --hash-style=sysv -z max-page-size=0x200000 -z noseparate-code
>+#objdump: -dw
>+
>+.*: +file format .*
>+
>+Disassembly of section .text:
>+
>+.* <__start>:
>+.*:[	 ]+[0-9a-f]+[	 ]+auipc[ 	]+ra,0x200
>+.*:[	 ]+[0-9a-f]+[	 ]+(lw|ld)[ 	]+.* <foo\+.*>
>+.*:[	 ]+[0-9a-f]+[	 ]+auipc[ 	]+ra,0x200
>+.*:[	 ]+[0-9a-f]+[	 ]+(lw|ld)[ 	]+.* <_GLOBAL_OFFSET_TABLE_\+.*>
>+
>+.* <foo>:
>+.*:[ 	]+00008067[ 	]+ret
>+
>+.* <bar>:
>+.*:[ 	]+00008067[ 	]+ret
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-22.s b/ld/testsuite/ld-riscv-elf/ifunc-22.s
>new file mode 100644
>index 0000000..9e97d05
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-22.s
>@@ -0,0 +1,32 @@
>+	.text
>+	.type start,"function"
>+	.global start
>+start:
>+	.type _start,"function"
>+	.global _start
>+_start:
>+	.type __start,"function"
>+	.global __start
>+__start:
>+	.type __start,"function"
>+.L1:
>+	auipc	x1, %got_pcrel_hi (foo)
>+.ifdef __64_bit__
>+	ld	x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+.L2:
>+	auipc	x1, %got_pcrel_hi (bar)
>+.ifdef __64_bit__
>+	ld	x1, %pcrel_lo (.L2) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>+	.type foo, %gnu_indirect_function
>+	.globl foo
>+foo:
>+	ret
>+	.type bar, %function
>+bar:
>+	ret
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-23.s b/ld/testsuite/ld-riscv-elf/ifunc-23.s
>new file mode 100644
>index 0000000..00b60c5
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-23.s
>@@ -0,0 +1,12 @@
>+	.type foo,%gnu_indirect_function
>+foo:
>+	ret
>+	.globl _start
>+_start:
>+	ret
>+	.globl __start
>+__start:
>+	.global _main
>+_main:
>+	.data
>+	.dc.a foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-23a.d b/ld/testsuite/ld-riscv-elf/ifunc-23a.d
>new file mode 100644
>index 0000000..855b67a
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-23a.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-23.s
>+#ld:
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-23b.d b/ld/testsuite/ld-riscv-elf/ifunc-23b.d
>new file mode 100644
>index 0000000..08009ac
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-23b.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-23.s
>+#ld: -pie
>+#readelf: -r --wide
>+
>+Relocation section '.rela.dyn' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-23c.d b/ld/testsuite/ld-riscv-elf/ifunc-23c.d
>new file mode 100644
>index 0000000..f32f0a3
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-23c.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-23.s
>+#ld: -shared
>+#readelf: -r --wide
>+
>+Relocation section '.rela.dyn' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-24.s b/ld/testsuite/ld-riscv-elf/ifunc-24.s
>new file mode 100644
>index 0000000..eb51312
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-24.s
>@@ -0,0 +1,13 @@
>+	.text
>+	.type foo,%gnu_indirect_function
>+foo:
>+	ret
>+	.globl _start
>+_start:
>+	call	foo@plt
>+	.globl __start
>+__start:
>+	.global _main
>+_main:
>+	.data
>+	.dc.a foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-24a.d b/ld/testsuite/ld-riscv-elf/ifunc-24a.d
>new file mode 100644
>index 0000000..eef0a9d
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-24a.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-24.s
>+#ld:
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-24b.d b/ld/testsuite/ld-riscv-elf/ifunc-24b.d
>new file mode 100644
>index 0000000..9069c70
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-24b.d
>@@ -0,0 +1,11 @@
>+#source: ifunc-24.s
>+#ld: -pie
>+#readelf: -r --wide
>+
>+Relocation section '.rela.dyn' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>+
>+Relocation section '.rela.plt' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-25.s b/ld/testsuite/ld-riscv-elf/ifunc-25.s
>new file mode 100644
>index 0000000..fafc920
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-25.s
>@@ -0,0 +1,14 @@
>+	.text
>+	.globl foo
>+	.type foo,%gnu_indirect_function
>+foo:
>+	ret
>+	.globl _start
>+_start:
>+	call	foo@plt
>+	.globl __start
>+__start:
>+	.global _main
>+_main:
>+	.data
>+	.dc.a foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-25a.d b/ld/testsuite/ld-riscv-elf/ifunc-25a.d
>new file mode 100644
>index 0000000..a3c8e96
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-25a.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-25.s
>+#ld:
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-25b.d b/ld/testsuite/ld-riscv-elf/ifunc-25b.d
>new file mode 100644
>index 0000000..c1940c6
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-25b.d
>@@ -0,0 +1,11 @@
>+#source: ifunc-25.s
>+#ld: -pie
>+#readelf: -r --wide
>+
>+Relocation section '.rela.dyn' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>+
>+Relocation section '.rela.plt' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-25c.d b/ld/testsuite/ld-riscv-elf/ifunc-25c.d
>new file mode 100644
>index 0000000..f4ca0a3
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-25c.d
>@@ -0,0 +1,11 @@
>+#source: ifunc-25.s
>+#ld: -shared
>+#readelf: -r --wide
>+
>+Relocation section '.rela.dyn' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+foo\(\)[ 	]+foo \+ 0
>+
>+Relocation section '.rela.plt' at offset 0x[0-9a-f]+ contains 1 entry:
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_JUMP_SLOT[ 	]+foo\(\)[ 	]+foo \+ 0
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-3.s b/ld/testsuite/ld-riscv-elf/ifunc-3.s
>new file mode 100644
>index 0000000..5b2ff25
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-3.s
>@@ -0,0 +1,16 @@
>+	.type foo, %gnu_indirect_function
>+	.global __GI_foo
>+	.protected __GI_foo
>+	.set __GI_foo, foo
>+	.text
>+.globl foo
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+.globl bar
>+	.type	bar, @function
>+bar:
>+	call	__GI_foo@plt
>+	ret
>+	.size	bar, .-bar
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-3a.d b/ld/testsuite/ld-riscv-elf/ifunc-3a.d
>new file mode 100644
>index 0000000..7465cf9
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-3a.d
>@@ -0,0 +1,11 @@
>+#source: ifunc-3.s
>+#target: [check_shared_lib_support]
>+#ld: -shared --hash-style=sysv
>+#objdump: -dw
>+
>+#...
>+0+[0-9a-f]+ <__GI_foo>:
>+#...
>+.*:[ 	]+[0-9a-f]+[ 	]+auipc[	 ]+.*
>+.*:[ 	]+[0-9a-f]+[ 	]+jalr[	 ]+.*<\*ABS\*\+0x[0-9a-f]+@plt>
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-3b.d b/ld/testsuite/ld-riscv-elf/ifunc-3b.d
>new file mode 100644
>index 0000000..1d27913
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-3b.d
>@@ -0,0 +1,8 @@
>+#source: ifunc-3.s
>+#target: [check_shared_lib_support]
>+#ld: -shared
>+#readelf: -r --wide
>+
>+#...
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-4-local.d b/ld/testsuite/ld-riscv-elf/ifunc-4-local.d
>new file mode 100644
>index 0000000..23772b9
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-4-local.d
>@@ -0,0 +1,6 @@
>+#ld:
>+#readelf: -r --wide
>+
>+#...
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-4-local.s b/ld/testsuite/ld-riscv-elf/ifunc-4-local.s
>new file mode 100644
>index 0000000..76ba885
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-4-local.s
>@@ -0,0 +1,20 @@
>+	.text
>+	.type foo, %gnu_indirect_function
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+	.type start,"function"
>+	.global start
>+start:
>+	.type _start,"function"
>+	.global _start
>+_start:
>+	.type __start,"function"
>+	.global __start
>+__start:
>+	.type __start,"function"
>+	.global _main
>+_main:
>+	call	foo
>+	#.dword	foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-4.d b/ld/testsuite/ld-riscv-elf/ifunc-4.d
>new file mode 100644
>index 0000000..23772b9
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-4.d
>@@ -0,0 +1,6 @@
>+#ld:
>+#readelf: -r --wide
>+
>+#...
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>+#pass
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-4.s b/ld/testsuite/ld-riscv-elf/ifunc-4.s
>new file mode 100644
>index 0000000..0ff6d1a
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-4.s
>@@ -0,0 +1,21 @@
>+	.text
>+	.type foo, %gnu_indirect_function
>+.global foo
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+	.type start,"function"
>+	.global start
>+start:
>+	.type _start,"function"
>+	.global _start
>+_start:
>+	.type __start,"function"
>+	.global __start
>+__start:
>+	.type __start,"function"
>+	.global _main
>+_main:
>+	call	foo
>+	#.dword	foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-5-local.s b/ld/testsuite/ld-riscv-elf/ifunc-5-local.s
>new file mode 100644
>index 0000000..c48ceaf
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-5-local.s
>@@ -0,0 +1,24 @@
>+	.text
>+	.type foo, %gnu_indirect_function
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+	.type start,"function"
>+	.global start
>+start:
>+	.type _start,"function"
>+	.global _start
>+_start:
>+	.type __start,"function"
>+	.global __start
>+__start:
>+	.type __start,"function"
>+	call	foo@plt
>+.L1:
>+	auipc	x1, %got_pcrel_hi (foo)
>+.ifdef __64_bit__
>+	ld	x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-5.s b/ld/testsuite/ld-riscv-elf/ifunc-5.s
>new file mode 100644
>index 0000000..7fd4ae7
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-5.s
>@@ -0,0 +1,25 @@
>+	.text
>+	.type foo, %gnu_indirect_function
>+.globl foo
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+	.type start,"function"
>+	.global start
>+start:
>+	.type _start,"function"
>+	.global _start
>+_start:
>+	.type __start,"function"
>+	.global __start
>+__start:
>+	.type __start,"function"
>+	call	foo@plt
>+.L1:
>+	auipc	x1, %got_pcrel_hi (foo)
>+.ifdef __64_bit__
>+	ld	x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-5a-local.d b/ld/testsuite/ld-riscv-elf/ifunc-5a-local.d
>new file mode 100644
>index 0000000..2216bb2
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-5a-local.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-5-local.s
>+#ld:
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-5a.d b/ld/testsuite/ld-riscv-elf/ifunc-5a.d
>new file mode 100644
>index 0000000..bdc1f50
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-5a.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-5.s
>+#ld:
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-5b-local.d b/ld/testsuite/ld-riscv-elf/ifunc-5b-local.d
>new file mode 100644
>index 0000000..aa3e00a
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-5b-local.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-5-local.s
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-5b.d b/ld/testsuite/ld-riscv-elf/ifunc-5b.d
>new file mode 100644
>index 0000000..d8fb901
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-5b.d
>@@ -0,0 +1,11 @@
>+#source: ifunc-5.s
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+Relocation section '.rela.got' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+foo\(\)[ 	]+foo \+ 0
>+#...
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_JUMP_SLOT[ 	]+foo\(\)[ 	]+foo \+ 0
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-5r-local.d b/ld/testsuite/ld-riscv-elf/ifunc-5r-local.d
>new file mode 100644
>index 0000000..223ac64
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-5r-local.d
>@@ -0,0 +1,10 @@
>+#source: ifunc-5-local.s
>+#as: --mno-relax
>+#ld: -r
>+#readelf: -r --wide
>+
>+Relocation section '.rela.text' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_CALL_PLT[ 	]+foo\(\)[ 	]+foo \+ 0
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_GOT_HI20[ 	]+foo\(\)[ 	]+foo \+ 0
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_PCREL_LO12_I[ 	]+[0-9a-f]+[ 	]+.L1 \+ 0
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-6.s b/ld/testsuite/ld-riscv-elf/ifunc-6.s
>new file mode 100644
>index 0000000..5bf3f66
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-6.s
>@@ -0,0 +1,26 @@
>+	.text
>+	.type foo, %gnu_indirect_function
>+.globl foo
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+	.protected foo
>+	.type start,"function"
>+	.global start
>+start:
>+	.type _start,"function"
>+	.global _start
>+_start:
>+	.type __start,"function"
>+	.global __start
>+__start:
>+	.type __start,"function"
>+	call	foo@plt
>+.L1:
>+	auipc	x1, %got_pcrel_hi (foo)
>+.ifdef __64_bits__
>+	ld	x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-6a.d b/ld/testsuite/ld-riscv-elf/ifunc-6a.d
>new file mode 100644
>index 0000000..24af75e
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-6a.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-6.s
>+#ld:
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-6b.d b/ld/testsuite/ld-riscv-elf/ifunc-6b.d
>new file mode 100644
>index 0000000..360901a
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-6b.d
>@@ -0,0 +1,11 @@
>+#source: ifunc-6.s
>+#ld: -shared -z nocombreloc
>+#readelf: -r --wide
>+
>+Relocation section '.rela.got' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+foo\(\)[ 	]+foo \+ 0
>+#...
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-7.s b/ld/testsuite/ld-riscv-elf/ifunc-7.s
>new file mode 100644
>index 0000000..2348f39
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-7.s
>@@ -0,0 +1,28 @@
>+	.text
>+	#nop
>+	#nop
>+	.type foo, %gnu_indirect_function
>+.globl foo
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+	.hidden foo
>+	.type start,"function"
>+	.global start
>+start:
>+	.type _start,"function"
>+	.global _start
>+_start:
>+	.type __start,"function"
>+	.global __start
>+__start:
>+	.type __start,"function"
>+	call	foo@plt
>+.L1:
>+	auipc	x1, %got_pcrel_hi (foo)
>+.ifdef __64_bit__
>+	ld	x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-7a.d b/ld/testsuite/ld-riscv-elf/ifunc-7a.d
>new file mode 100644
>index 0000000..69d43fc
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-7a.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-7.s
>+#ld:
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-7b.d b/ld/testsuite/ld-riscv-elf/ifunc-7b.d
>new file mode 100644
>index 0000000..c324105
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-7b.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-7.s
>+#ld: -shared
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-8.d b/ld/testsuite/ld-riscv-elf/ifunc-8.d
>new file mode 100644
>index 0000000..18c0ee2
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-8.d
>@@ -0,0 +1,9 @@
>+#source: ifunc-8a.s
>+#source: ifunc-8b.s
>+#as:
>+#ld:
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-8a.s b/ld/testsuite/ld-riscv-elf/ifunc-8a.s
>new file mode 100644
>index 0000000..9518b3a
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-8a.s
>@@ -0,0 +1,18 @@
>+	.text
>+	.type start,"function"
>+	.global start
>+start:
>+	.type _start,"function"
>+	.global _start
>+_start:
>+	.type __start,"function"
>+	.global __start
>+__start:
>+	.type __start,"function"
>+.L1:
>+	auipc	x1, %got_pcrel_hi (foo)
>+.ifdef __64_bit__
>+	ld	x1, %pcrel_lo (.L1) (x1)
>+.else
>+	lw	x1, %pcrel_lo (.L1) (x1)
>+.endif
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-8b.s b/ld/testsuite/ld-riscv-elf/ifunc-8b.s
>new file mode 100644
>index 0000000..1f108f8
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-8b.s
>@@ -0,0 +1,7 @@
>+	.text
>+	.type foo, %gnu_indirect_function
>+.globl foo
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-9.d b/ld/testsuite/ld-riscv-elf/ifunc-9.d
>new file mode 100644
>index 0000000..c140818
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-9.d
>@@ -0,0 +1,7 @@
>+#source: ifunc-9.s
>+#ld: --export-dynamic
>+#readelf: -r --wide
>+
>+Relocation section '.rela.plt' at .*
>+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
>+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
>diff --git a/ld/testsuite/ld-riscv-elf/ifunc-9.s b/ld/testsuite/ld-riscv-elf/ifunc-9.s
>new file mode 100644
>index 0000000..453bcfe
>--- /dev/null
>+++ b/ld/testsuite/ld-riscv-elf/ifunc-9.s
>@@ -0,0 +1,24 @@
>+	.text
>+	.type foo, %gnu_indirect_function
>+.globl foo
>+	.type	foo, @function
>+foo:
>+	ret
>+	.size	foo, .-foo
>+	.type start,"function"
>+	.global start
>+start:
>+	.type _start,"function"
>+	.global _start
>+_start:
>+	.type __start,"function"
>+	.global __start
>+__start:
>+	.type __start,"function"
>+.L1:
>+	auipc	x1, %pcrel_hi (data_p)
>+	addi	x1, x1, %pcrel_lo (.L1)
>+
>+        .data
>+data_p:
>+        .long  foo
>diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
>index 1a0c68f..049f573 100644
>--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
>+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
>@@ -87,4 +87,205 @@ if [istarget "riscv*-*-*"] {
> 	    {} "lib-nopic-01a.so" }
>     }
>     run_dump_test "lib-nopic-01b"
>+
>+    # IFUNC testcases.
>+    run_dump_test "ifunc-1"
>+    run_dump_test "ifunc-1-local"
>+    run_dump_test "ifunc-2"
>+    run_dump_test "ifunc-2-local"
>+    run_dump_test "ifunc-3a"
>+    run_dump_test "ifunc-3b"
>+    run_dump_test "ifunc-4"
>+    run_dump_test "ifunc-4-local"
>+    run_ld_link_tests {
>+	{ "ifunc-5a"
>+	  "" ""
>+	  "" {ifunc-5.s}
>+	  {{readelf {-r --wide} ifunc-5a.d}} "ifunc-5a"}
>+	{ "ifunc-5b"
>+	  "-shared -z nocombreloc" ""
>+	  "" {ifunc-5.s}
>+	  {{readelf {-r --wide} ifunc-5b.d}} "ifunc-5b"}
>+	{ "ifunc-5a-local"
>+	  "" ""
>+	  "" {ifunc-5-local.s}
>+	  {{readelf {-r --wide} ifunc-5a-local.d}} "ifunc-5a-local"}
>+	{ "ifunc-5b-local"
>+	  "-shared -z nocombreloc" ""
>+	  "" {ifunc-5-local.s}
>+	  {{readelf {-r --wide} ifunc-5b-local.d}} "ifunc-5b-local"}
>+	{ "ifunc-5r-local"
>+	  "-r" ""
>+	  "--mno-relax" {ifunc-5-local.s}
>+	  {{readelf {-r --wide} ifunc-5r-local.d}} "ifunc-5r-local"}
>+
>+	{ "ifunc-5a (rv64)"
>+	  "-melf64lriscv" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-5.s}
>+	  {{readelf {-r --wide} ifunc-5a.d}} "ifunc-5a-rv64"}
>+	{ "ifunc-5b (rv64)"
>+	  "-melf64lriscv -shared -z nocombreloc" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-5.s}
>+	  {{readelf {-r --wide} ifunc-5b.d}} "ifunc-5b-rv64"}
>+	{ "ifunc-5a-local (rv64)"
>+	  "-melf64lriscv" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-5-local.s}
>+	  {{readelf {-r --wide} ifunc-5a-local.d}} "ifunc-5a-local-rv64"}
>+	{ "ifunc-5b-local (rv64)"
>+	  "-melf64lriscv -shared -z nocombreloc" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-5-local.s}
>+	  {{readelf {-r --wide} ifunc-5b-local.d}} "ifunc-5b-local-rv64"}
>+	{ "ifunc-5r-local (rv64)"
>+	  "-melf64lriscv -r" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1 --mno-relax" {ifunc-5-local.s}
>+	  {{readelf {-r --wide} ifunc-5r-local.d}} "ifunc-5r-local-rv64"}
>+    }
>+    run_ld_link_tests {
>+	{ "ifunc-6a"
>+	  "" ""
>+	  "" {ifunc-6.s}
>+	  {{readelf {-r --wide} ifunc-6a.d}} "ifunc-6a"}
>+	{ "ifunc-6b"
>+	  "-shared -z nocombreloc" ""
>+	  "" {ifunc-6.s}
>+	  {{readelf {-r --wide} ifunc-6b.d}} "ifunc-6b"}
>+
>+	{ "ifunc-6a (rv64)"
>+	  "-melf64lriscv" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-6.s}
>+	  {{readelf {-r --wide} ifunc-6a.d}} "ifunc-6a-rv64"}
>+	{ "ifunc-6b (rv64)"
>+	  "-melf64lriscv -shared -z nocombreloc" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-6.s}
>+	  {{readelf {-r --wide} ifunc-6b.d}} "ifunc-6b-rv64"}
>+    }
>+    run_ld_link_tests {
>+	{ "ifunc-7a"
>+	  "" ""
>+	  "" {ifunc-7.s}
>+	  {{readelf {-r --wide} ifunc-7a.d}} "ifunc-7a"}
>+	{ "ifunc-7b"
>+	  "-shared" ""
>+	  "" {ifunc-7.s}
>+	  {{readelf {-r --wide} ifunc-7b.d}} "ifunc-7b"}
>+
>+	{ "ifunc-7a (rv64)"
>+	  "-melf64lriscv" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-7.s}
>+	  {{readelf {-r --wide} ifunc-7a.d}} "ifunc-7a-rv64"}
>+	{ "ifunc-7b (rv64)"
>+	  "-melf64lriscv -shared" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-7.s}
>+	  {{readelf {-r --wide} ifunc-7b.d}} "ifunc-7b-rv64"}
>+    }
>+    run_ld_link_tests {
>+	{ "ifunc-8"
>+	  "" ""
>+	  "" {ifunc-8a.s ifunc-8b.s}
>+	  {{readelf {-r --wide} ifunc-8.d}} "ifunc-8"}
>+
>+	{ "ifunc-8 (rv64)"
>+	  "" ""
>+	  "" {ifunc-8a.s ifunc-8b.s}
>+	  {{readelf {-r --wide} ifunc-8.d}} "ifunc-8-rv64"}
>+    }
>+    run_dump_test "ifunc-9"
>+    run_ld_link_tests {
>+	{ "ifunc-10"
>+	  "-e bar --gc-sections" ""
>+	  "" {ifunc-10.s}
>+	  {{readelf {-r --wide} ifunc-10.d}} "ifunc-10"}
>+
>+	{ "ifunc-10 (rv64)"
>+	  "-melf64lriscv -e bar --gc-sections" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-10.s}
>+	  {{readelf {-r --wide} ifunc-10.d}} "ifunc-10-rv64"}
>+    }
>+    run_ld_link_tests {
>+	{ "ifunc-11a"
>+	  "-e bar --gc-sections" ""
>+	  "" {ifunc-11.s}
>+	  {{readelf {-r --wide} ifunc-11a.d}} "ifunc-11a"}
>+	{ "ifunc-11b"
>+	  "-e bar" ""
>+	  "" {ifunc-11.s}
>+	  {{readelf {-r --wide} ifunc-11b.d}} "ifunc-11b"}
>+
>+	{ "ifunc-11a (rv64)"
>+	  "-melf64lriscv -e bar --gc-sections" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-11.s}
>+	  {{readelf {-r --wide} ifunc-11a.d}} "ifunc-11a-rv64"}
>+	{ "ifunc-11b (rv64)"
>+	  "-melf64lriscv -e bar" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-11.s}
>+	  {{readelf {-r --wide} ifunc-11b.d}} "ifunc-11b-rv64"}
>+    }
>+    run_ld_link_tests {
>+	{ "ifunc-12"
>+	  "-shared -e bar --gc-sections" ""
>+	  "" {ifunc-12.s}
>+	  {{readelf {-r --wide} ifunc-12.d}} "ifunc-12"}
>+
>+	{ "ifunc-12 (rv64)"
>+	  "-melf64lriscv -shared -e bar --gc-sections" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-12.s}
>+	  {{readelf {-r --wide} ifunc-12.d}} "ifunc-12-rv64"}
>+    }
>+    run_dump_test "ifunc-13"
>+    run_dump_test "ifunc-14a"
>+    run_dump_test "ifunc-14b"
>+    run_dump_test "ifunc-14c"
>+    run_dump_test "ifunc-14d"
>+    run_dump_test "ifunc-14e"
>+    run_dump_test "ifunc-14f"
>+    run_ld_link_tests {
>+	{ "ifunc-15"
>+	  "-shared -z nocombreloc" ""
>+	  "" {ifunc-15.s}
>+	  {{readelf {-r --wide} ifunc-15.d}} "ifunc-15"}
>+
>+	{ "ifunc-15 (rv64)"
>+	  "-melf64lriscv -shared -z nocombreloc" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-15.s}
>+	  {{readelf {-r --wide} ifunc-15.d}} "ifunc-15-rv64"}
>+    }
>+    run_dump_test "ifunc-16"
>+    run_dump_test "ifunc-16-now"
>+    run_dump_test "ifunc-17a"
>+    run_dump_test "ifunc-17b"
>+    run_dump_test "ifunc-18a"
>+    run_dump_test "ifunc-18b"
>+    run_dump_test "ifunc-19a"
>+    run_dump_test "ifunc-19b"
>+    run_dump_test "ifunc-20"
>+    run_ld_link_tests {
>+	{ "ifunc-21"
>+	  "-shared --hash-style=sysv -z max-page-size=0x200000 -z noseparate-code" ""
>+	  "" {ifunc-21.s}
>+	  {{objdump -dw ifunc-21.d}} "ifunc-21"}
>+
>+	{ "ifunc-21 (rv64)"
>+	  "-melf64lriscv -shared --hash-style=sysv -z max-page-size=0x200000 -z noseparate-code" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-21.s}
>+	  {{objdump -dw ifunc-21.d}} "ifunc-21-rv64"}
>+    }
>+    run_ld_link_tests {
>+	{ "ifunc-22"
>+	  "-shared --hash-style=sysv -z max-page-size=0x200000 -z noseparate-code" ""
>+	  "" {ifunc-22.s}
>+	  {{objdump -dw ifunc-22.d}} "ifunc-22"}
>+
>+	{ "ifunc-22 (rv64)"
>+	  "-melf64lriscv -shared --hash-style=sysv -z max-page-size=0x200000 -z noseparate-code" ""
>+	  "-march=rv64i -mabi=lp64 -defsym __64_bit__=1" {ifunc-22.s}
>+	  {{objdump -dw ifunc-22.d}} "ifunc-22-rv64"}
>+    }
>+    run_dump_test "ifunc-23a"
>+    run_dump_test "ifunc-23b"
>+    run_dump_test "ifunc-23c"
>+    run_dump_test "ifunc-24a"
>+    run_dump_test "ifunc-24b"
>+    run_dump_test "ifunc-25a"
>+    run_dump_test "ifunc-25b"
>+    run_dump_test "ifunc-25c"
> }
>-- 
>2.7.4
>

(Off-topic
I think -z separate-code being default on linux x86 since binutils 2.31
is not the best choice. GNU ld should have --rosegment like gold and LLD.
-z separate-code should be responsible for overlapping p_offset

https://reviews.llvm.org/D64903)


More information about the Binutils mailing list