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

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

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

Nelson Chu-2
Hi binutils,

This patch is used to support GNU ifunc for RISC-V in binutils.
And we also have the ifunc supports in the gcc and glibc.  For glibc,
Vincent Chen have finished the related works, and he is waiting for
the binutils' patch accepted and upstreamed.  For gcc, the following
patch should be enough to enable the ifunc supports.  Jim also reminded
me that we need to check the bintuils ifunc feature in gcc, in case someone
actually uses the ifunc attribute, but they don't have good enough configure
checks.  The gcc check will be sent by another patch to gcc mail list.
However, We have tested and passed the toolchain regressions, including gcc
and binutils testsuites, and glibc testcases for ifunc support.


From c7ce689c15cc8b4609abcad476ea330eaff4308f Mon Sep 17 00:00:00 2001
From: Nelson Chu <[hidden email]>
Date: Mon, 18 May 2020 19:01:29 -0700
Subject: [PATCH] RISC-V: Enable ifunc attribute by default for RISC-V.

---
 gcc/config.gcc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gcc/config.gcc b/gcc/config.gcc
index ddd3b8f..a07c5b46 100644
--- a/gcc/config.gcc
+++ b/gcc/config.gcc
@@ -3281,7 +3281,7 @@ case ${target} in
         ;;
 *-*-linux*)
        case ${target} in
-       aarch64*-* | arm*-* | i[34567]86-* | powerpc*-* | s390*-* | sparc*-* | x86_64-*)
+       aarch64*-* | arm*-* | i[34567]86-* | powerpc*-* | riscv*-*-* | s390*-* | sparc*-* | x86_64-*)
                default_gnu_indirect_function=yes
                ;;
        esac


For now, we handle the ifunc for the following relocations.
Please see the details in the first patch comments.

R_RISCV_32/64, R_RISCV_HI20, R_RISCV_LO12_I/S, R_RISCV_CALL,
R_RISCV_CALL_PLT, R_RISCV_PCREL_HI20, R_RISCV_GOT_HI20, and
R_RISCV_PCREL_LO12_I/S.

There should be two potential issues currently,

1. For R_RISCV_CALL and R_RISCV_CALL_PLT, GNU GCC and Binutils handle
them separately.  But in fact they can (should) be handled identically.
RISC-V lld had made the same improvement, and so had GNU AARCH64, they
has only R_AARCH64_CALL26 for calls rather than two seperate relocations.
Jim Wilson had suggested me that I should improve this, and also need to
change the ifunc checking rules in the riscv_elf_check_relocs.  I prefer
to improve the R_RISCV_CALL/R_RISCV_CALL_PLT, and also update the ifunc
checking in the riscv_elf_check_relocs by the future patches, if everyone
is OK.

2. Recent DT_TEXTREL changes will issue the warning for this patch.
Consider the commit cebd6b8ac1c5a2a847a50e3efe932ff2d0867b3e
(IFUNC: Update IFUNC resolver check with DT_TEXTREL).  At the beginning,
I think we don't need the ifunc resolver checks, since we don't allow
the R_RISCV_HI20 when generating the shared library or PIE executable.
And the R_RISCV_32/64 are used in the data section rather than the code
for now.  There should be no situations that make the IFUNC resolvers with
DT_TEXTREL happen currently, so I disable the ifunc resolver.

But consider the recent commit 3084d7a27b8e4d13f0fdd0fac62ffadc9c2223b5
(ELF: Add _bfd_elf_add_dynamic_tags).  The commit enables the ifunc resolver
checks for all targets, and helps to find that the R_RISCV_CALL and
R_RISCV_PCREL_HI20 may have chances to generate DT_TEXTREL for ifunc resolver.
This bring me back to see what happen here.  Consider the ifunc-2 testsuite in
the ld/testsuite/ld-riscv-elf/.  I notice that the dynamic relocation, which
cause the TEXTREL, are all R_RISCV_NONE and do nothing in the glibc and other
dynamic loaders.  I believe this shouldn't affect correctness, and can be
resolved by changing some checking rules in the riscv_elf_check_relocs by
the future patches.

Any suggestion and feedback are welcome.

Thanks
Nelson

Reply | Threaded
Open this post in threaded view
|

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

Nelson Chu-2
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

Reply | Threaded
Open this post in threaded view
|

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

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

On 2020-07-07, Nelson Chu wrote:

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

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

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

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

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

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

On 64-bit this should be 64.

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

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

https://reviews.llvm.org/D64903)
Reply | Threaded
Open this post in threaded view
|

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

Nelson Chu-2
Hi MaskRay,


On Wed, Jul 8, 2020 at 3:22 PM Fangrui Song <[hidden email]> wrote:
>
> I think I know ifunc and its implementation in LLD quite well
> (https://reviews.llvm.org/D74022 ). However, I know little about GNU ld
> and its terminology, so please bear with me.

I would like to thank you for your suggestions and the great ideas.
So please feel free to join the discussion.


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

Agreed... I just keep the original naming when porting them from
ld/testsuite/ld-ifunc/.  I do plan to improve these, the descriptive
names should help, or we probably can add some comments to describe
them in the ld-riscv-elf.exp.  I will add some comments first in the
ld-riscv-elf.exp, and then will try to give them the more descriptive
names.  However, thanks for the suggestion.


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

This may be one of the ways to combine the assembly files.  Another
way is used in the ifunc-10.s, it should also work.  We can define the
symbols by "-defsym xxx=1" to assembler, and then use ".ifdef xxx" to
decide which code patterns are prefered to be generated.  I remember
not only ifunc-1.s and ifunc-3.s can be combined, so we probably can
reduce the entire number of the ifunc testcases.


> >+      if (h != NULL)
> >+      {
> >+        switch (r_type)
> >+          {
> >+          case R_RISCV_32:
> >+          case R_RISCV_64:
> >+          case R_RISCV_CALL:
> >+          case R_RISCV_CALL_PLT:
> >+          case R_RISCV_HI20:
> >+          case R_RISCV_GOT_HI20:
> >+          case R_RISCV_PCREL_HI20:
>
> The list has appears more than once with variance, I think a generic
> relocation kind may help.

I assume you mean something like "_bfd_elf_x86_valid_reloc_p" or some
macros defined in the bfd/elfxx-x86.h.  If so, yeah that's a good
idea.  I think generic relocation kinds are helpful.


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

Thanks for the information.  I will figure this out.


Thank you very much, and other suggestions are also welcome.
Nelson
Reply | Threaded
Open this post in threaded view
|

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

Jim Wilson-2
In reply to this post by Nelson Chu-2
On Tue, Jul 7, 2020 at 9:43 PM Nelson Chu <[hidden email]> wrote:
> This patch is used to support GNU ifunc for RISC-V in binutils.

The binutils patch is OK with me, provided that it is compatible with
the clang implementation.  I will leave that for Nelson and Fangrui to
work out.

Jim
Reply | Threaded
Open this post in threaded view
|

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

Fangrui Song-2
In reply to this post by Nelson Chu-2
On 2020-07-08, Nelson Chu wrote:

>Hi MaskRay,
>
>
>On Wed, Jul 8, 2020 at 3:22 PM Fangrui Song <[hidden email]> wrote:
>>
>> I think I know ifunc and its implementation in LLD quite well
>> (https://reviews.llvm.org/D74022 ). However, I know little about GNU ld
>> and its terminology, so please bear with me.
>
>I would like to thank you for your suggestions and the great ideas.
>So please feel free to join the discussion.
>
>
>> The test naming is really terrible. It is difficult to understand the
>> purpose of individual files. Can they have more descriptive names?
>
>Agreed... I just keep the original naming when porting them from
>ld/testsuite/ld-ifunc/.  I do plan to improve these, the descriptive
>names should help, or we probably can add some comments to describe
>them in the ld-riscv-elf.exp.  I will add some comments first in the
>ld-riscv-elf.exp, and then will try to give them the more descriptive
>names.  However, thanks for the suggestion.
>
>
>> ifunc-1.s and ifunc-3.s are identical except a visibility setting.
>> Can the tests be somehow combined? For example,
>> echo '.hidden foo' > a.s and then concatenate two assembly files
>
>This may be one of the ways to combine the assembly files.  Another
>way is used in the ifunc-10.s, it should also work.  We can define the
>symbols by "-defsym xxx=1" to assembler, and then use ".ifdef xxx" to
>decide which code patterns are prefered to be generated.  I remember
>not only ifunc-1.s and ifunc-3.s can be combined, so we probably can
>reduce the entire number of the ifunc testcases.
>
>
>> >+      if (h != NULL)
>> >+      {
>> >+        switch (r_type)
>> >+          {
>> >+          case R_RISCV_32:
>> >+          case R_RISCV_64:
>> >+          case R_RISCV_CALL:
>> >+          case R_RISCV_CALL_PLT:
>> >+          case R_RISCV_HI20:
>> >+          case R_RISCV_GOT_HI20:
>> >+          case R_RISCV_PCREL_HI20:
>>
>> The list has appears more than once with variance, I think a generic
>> relocation kind may help.
>
>I assume you mean something like "_bfd_elf_x86_valid_reloc_p" or some
>macros defined in the bfd/elfxx-x86.h.  If so, yeah that's a good
>idea.  I think generic relocation kinds are helpful.
>
>
>> (Off-topic
>> I think -z separate-code being default on linux x86 since binutils 2.31
>> is not the best choice. GNU ld should have --rosegment like gold and LLD.
>> -z separate-code should be responsible for overlapping p_offset
>>
>> https://reviews.llvm.org/D64903)
>
>Thanks for the information.  I will figure this out.
>
>
>Thank you very much, and other suggestions are also welcome.
>Nelson

Hi Nelson,

For this test
https://github.com/llvm/llvm-project/blob/master/lld/test/ELF/riscv-ifunc-nonpreemptible.s

.text
.globl func
.type func, @gnu_indirect_function
func:
   ret

.globl _start
_start:
.L:
   auipc a0, %pcrel_hi(func)
   addi a0, a0, %pcrel_lo(.L)

Do you know why it reports this (spurious) diagnostic?

% ld.lld -pie a.64.o -o a.64  # silent
% ld-new -pie a.64.o -o a.64
....: warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIE

There is also a spurious R_RISCV_NONE dynamic relocation, which should be fixed.

% readelf -Wr a.64

Relocation section '.rela.dyn' at offset 0x210 contains 1 entry:
     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000000000  0000000000000000 R_RISCV_NONE                              0

Relocation section '.rela.plt' at offset 0x228 contains 1 entry:
     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000002010  000000000000003a R_RISCV_IRELATIVE                         270


This test checks a non-GOT-non-PLT relocation to a STT_GNU_IFUNC.
Such relocations can be used to take the address of the indirect function.
To make sure pointer equality holds (i.e. taking the address from another module will get the same address),
LLD will create a PLT (called "canonical PLT" among a few linker people),
change func's type to STT_FUNC, and redirect all references to the PLT.

      4: 0000000000001270     0 FUNC    GLOBAL DEFAULT    7 func

This patch does not change func's type. If you link with ld-new -E -pie a.64.o -o a.64,
func will be exported. Taking the address of func from another module will break pointer equality.

     20: 00000000000003f0     0 IFUNC   GLOBAL DEFAULT    9 func



Regarding tests, it'd be nice to check whether this example works
https://reviews.llvm.org/rG45acc35ac21323bafaf5d4367df10ebc4eed35f4

   // gcc -fno-pie -no-pie a.c
   // gcc -fPIE -pie a.c
   #include <stdio.h>
   static void impl(void) { puts("meow"); }
   void thefunc(void) __attribute__((ifunc("resolver")));
   void *resolver(void) { return &impl; }
   int main(void) {
     thefunc();
     void (*theptr)(void) = &thefunc;
     theptr();
   }
Reply | Threaded
Open this post in threaded view
|

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

Nelson Chu-2
Hi MaskRay,

Thank you very much for reporting this.

On Wed, Jul 15, 2020 at 12:44 PM Fangrui Song <[hidden email]> wrote:

> For this test
> https://github.com/llvm/llvm-project/blob/master/lld/test/ELF/riscv-ifunc-nonpreemptible.s
>
> .text
> .globl func
> .type func, @gnu_indirect_function
> func:
>    ret
>
> .globl _start
> _start:
> .L:
>    auipc a0, %pcrel_hi(func)
>    addi a0, a0, %pcrel_lo(.L)
>
> Do you know why it reports this (spurious) diagnostic?
>
> % ld.lld -pie a.64.o -o a.64  # silent
> % ld-new -pie a.64.o -o a.64
> ....: warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIE
>
> There is also a spurious R_RISCV_NONE dynamic relocation, which should be fixed.
>
> % readelf -Wr a.64
>
> Relocation section '.rela.dyn' at offset 0x210 contains 1 entry:
>      Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
> 0000000000000000  0000000000000000 R_RISCV_NONE                              0
>
> Relocation section '.rela.plt' at offset 0x228 contains 1 entry:
>      Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
> 0000000000002010  000000000000003a R_RISCV_IRELATIVE                         270

I need to see the riscv_elf_check_relocs again and think of the
details.  I believe all R_RISCV_NONE and redundant dynamic relocation
problems can be resolved by rewriting the riscv_elf_check_relocs,
according to the TLS issues reported by you before.

> This test checks a non-GOT-non-PLT relocation to a STT_GNU_IFUNC.
> Such relocations can be used to take the address of the indirect function.
> To make sure pointer equality holds (i.e. taking the address from another module will get the same address),
> LLD will create a PLT (called "canonical PLT" among a few linker people),
> change func's type to STT_FUNC, and redirect all references to the PLT.

Oh... This should be fixed.  Looks like the
_bfd_x86_elf_link_fixup_ifunc_symbol that I didn't notice before...

I will come back to deal with these problems, including what you have
suggested in the previous mail, ASAP.  Maybe a few days later.


Thank you very much
Nelson
Reply | Threaded
Open this post in threaded view
|

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

Nelson Chu-2
In reply to this post by Fangrui Song-2
Hi MaskRay,

On Wed, Jul 15, 2020 at 12:44 PM Fangrui Song <[hidden email]> wrote:

> .text
> .globl func
> .type func, @gnu_indirect_function
> func:
>    ret
>
> .globl _start
> _start:
> .L:
>    auipc a0, %pcrel_hi(func)
>    addi a0, a0, %pcrel_lo(.L)
>
> % ld.lld -pie a.64.o -o a.64  # silent
> % ld-new -pie a.64.o -o a.64
> ....: warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIE

The warning and redundant R_RISCV_NOP can be fixed by the following
change, but haven't run all tests.

diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index ca0dd11..73f44d8 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -808,9 +808,10 @@ riscv_elf_check_relocs (bfd *abfd, struct
bfd_link_info *info,

          if ((bfd_link_pic (info)
               && (sec->flags & SEC_ALLOC) != 0
               && ((r != NULL && ! r->pc_relative)
                   || (h != NULL
-                      && (! info->symbolic
+                      && (! (bfd_link_pie (info)
+                             || info->symbolic)
                           || h->root.type == bfd_link_hash_defweak
                           || !h->def_regular))))
              || (!bfd_link_pic (info)


> This test checks a non-GOT-non-PLT relocation to a STT_GNU_IFUNC.
> Such relocations can be used to take the address of the indirect function.
> To make sure pointer equality holds (i.e. taking the address from another module will get the same address),
> LLD will create a PLT (called "canonical PLT" among a few linker people),
> change func's type to STT_FUNC, and redirect all references to the PLT.
>
>       4: 0000000000001270     0 FUNC    GLOBAL DEFAULT    7 func
>
> This patch does not change func's type. If you link with ld-new -E -pie a.64.o -o a.64,
> func will be exported. Taking the address of func from another module will break pointer equality.
>
>      20: 00000000000003f0     0 IFUNC   GLOBAL DEFAULT    9 func

Consider Vincent Chen's ifunc support in GLIBC,
https://sourceware.org/pipermail/libc-alpha/2020-July/116059.html

I'm not sure why we need to change the symbol type from STT_IFUNC to
STT_FUNC, and then update the symbol value to the corresponding plt
entry.  If other modules want to call `func`, it will use
R_RISCV_JUMP_SLOT with the undef STT_FUNC `func`.  Glibc will know the
`func` is actually an STT_IFUNC according to the executable (probably
need --export-dynamic) or shared library which defines it, and then
handle the case just like what R_RISCV_IRELATIVE does.  If we want to
call the `ifunc`, and the `ifunc` is defined in the same
executable/shared library, then linker will generate the
R_RISCV_IRELATIVE for the call, it should be worked with or without
changing the STT_IFUNC to STT_FUNC.  So are there any cases that would
fail since we don't change the STT_IFUNC to STT_FUNC, and redirect it
to the plt entry?

I would be happy to do the changes, even if the GNU toolchain (gcc +
binutils + glibc) seems OK for now.  But this wouldn't be the first
time I was wrong, so please feel free to correct me.


> Regarding tests, it'd be nice to check whether this example works
> https://reviews.llvm.org/rG45acc35ac21323bafaf5d4367df10ebc4eed35f4
>
>    // gcc -fno-pie -no-pie a.c
>    // gcc -fPIE -pie a.c
>    #include <stdio.h>
>    static void impl(void) { puts("meow"); }
>    void thefunc(void) __attribute__((ifunc("resolver")));
>    void *resolver(void) { return &impl; }
>    int main(void) {
>      thefunc();
>      void (*theptr)(void) = &thefunc;
>      theptr();
>    }

I assume both `thefunc` and `theptr` should print "meow".  If the
result is expected, then our patches (Vincent Chen's glibc patch and
my binutils patch) should work.


Thanks
Nelson
Reply | Threaded
Open this post in threaded view
|

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

Fangrui Song-2
On 2020-07-21, Nelson Chu wrote:

>Hi MaskRay,
>
>On Wed, Jul 15, 2020 at 12:44 PM Fangrui Song <[hidden email]> wrote:
>> .text
>> .globl func
>> .type func, @gnu_indirect_function
>> func:
>>    ret
>>
>> .globl _start
>> _start:
>> .L:
>>    auipc a0, %pcrel_hi(func)
>>    addi a0, a0, %pcrel_lo(.L)
>>
>> % ld.lld -pie a.64.o -o a.64  # silent
>> % ld-new -pie a.64.o -o a.64
>> ....: warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIE
>
>The warning and redundant R_RISCV_NOP can be fixed by the following
>change, but haven't run all tests.
>
>diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
>index ca0dd11..73f44d8 100644
>--- a/bfd/elfnn-riscv.c
>+++ b/bfd/elfnn-riscv.c
>@@ -808,9 +808,10 @@ riscv_elf_check_relocs (bfd *abfd, struct
>bfd_link_info *info,
>
>          if ((bfd_link_pic (info)
>               && (sec->flags & SEC_ALLOC) != 0
>               && ((r != NULL && ! r->pc_relative)
>                   || (h != NULL
>-                      && (! info->symbolic
>+                      && (! (bfd_link_pie (info)
>+                             || info->symbolic)
>                           || h->root.type == bfd_link_hash_defweak
>                           || !h->def_regular))))
>              || (!bfd_link_pic (info)
>
>
>> This test checks a non-GOT-non-PLT relocation to a STT_GNU_IFUNC.
>> Such relocations can be used to take the address of the indirect function.
>> To make sure pointer equality holds (i.e. taking the address from another module will get the same address),
>> LLD will create a PLT (called "canonical PLT" among a few linker people),
>> change func's type to STT_FUNC, and redirect all references to the PLT.
>>
>>       4: 0000000000001270     0 FUNC    GLOBAL DEFAULT    7 func
>>
>> This patch does not change func's type. If you link with ld-new -E -pie a.64.o -o a.64,
>> func will be exported. Taking the address of func from another module will break pointer equality.
>>
>>      20: 00000000000003f0     0 IFUNC   GLOBAL DEFAULT    9 func
>
>Consider Vincent Chen's ifunc support in GLIBC,
>https://sourceware.org/pipermail/libc-alpha/2020-July/116059.html
>
>I'm not sure why we need to change the symbol type from STT_IFUNC to
>STT_FUNC, and then update the symbol value to the corresponding plt
>entry.  If other modules want to call `func`, it will use
>R_RISCV_JUMP_SLOT with the undef STT_FUNC `func`.  Glibc will know the
>`func` is actually an STT_IFUNC according to the executable (probably
>need --export-dynamic) or shared library which defines it, and then
>handle the case just like what R_RISCV_IRELATIVE does.  If we want to
>call the `ifunc`, and the `ifunc` is defined in the same
>executable/shared library, then linker will generate the
>R_RISCV_IRELATIVE for the call, it should be worked with or without
>changing the STT_IFUNC to STT_FUNC.  So are there any cases that would
>fail since we don't change the STT_IFUNC to STT_FUNC, and redirect it
>to the plt entry?

STT_FUNC is required for references within the module. If an ifunc is
referended by a local PC-relative relocation, the intention may be to
take the address. The address should be equal to other potentially
address taking relocation types. I don't think there is a way keeping
STT_IFUNC if no dynamic relocation is desired.

Compilers can produce a PC-relative relocation if it does not know the
target function is an ifunc (marked via inline asm, instead of
__attribute__((ifunc(...)))).

>I would be happy to do the changes, even if the GNU toolchain (gcc +
>binutils + glibc) seems OK for now.  But this wouldn't be the first
>time I was wrong, so please feel free to correct me.
>
>
>> Regarding tests, it'd be nice to check whether this example works
>> https://reviews.llvm.org/rG45acc35ac21323bafaf5d4367df10ebc4eed35f4
>>
>>    // gcc -fno-pie -no-pie a.c
>>    // gcc -fPIE -pie a.c
>>    #include <stdio.h>
>>    static void impl(void) { puts("meow"); }
>>    void thefunc(void) __attribute__((ifunc("resolver")));
>>    void *resolver(void) { return &impl; }
>>    int main(void) {
>>      thefunc();
>>      void (*theptr)(void) = &thefunc;
>>      theptr();
>>    }
>
>I assume both `thefunc` and `theptr` should print "meow".  If the
>result is expected, then our patches (Vincent Chen's glibc patch and
>my binutils patch) should work.

Great!