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

Nelson Chu nelson.chu@sifive.com
Wed Jul 8 04:43:11 GMT 2020


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

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:
+	      /* 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 */
+	 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



More information about the Binutils mailing list