public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v3 0/2] RISC-V: Support GNU indirect functions
@ 2020-10-07  3:48 Nelson Chu
  2020-10-07  3:48 ` [PATCH v3 1/2] " Nelson Chu
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Nelson Chu @ 2020-10-07  3:48 UTC (permalink / raw)
  To: binutils, jimw, palmer, andrew, i; +Cc: vincent.chen, kito.cheng, nelson.chu

Hi binutils,

I have arranged the ifunc patches recently, and also pass elf/linux
toolchain regressions.  Compared to the v2 patches, the second patch
- RISC-V: Fix that IRELATIVE relocs may be inserted to the wrong place,
fix the ifunc overwrite issue when generating the static executable.
Otherwise, the v3 patches just fix some minor conflicts, and almost the
same the the v2 patches.

As for the different ifunc behaviors between ld and lld, I have tested
the x86 ifunc behaviors both for ld and lld, and they are also different.
Our ld ifunc behavior is the same as the x86 ld, and the riscv lld is
the same as the x86 lld.  Consider the following tests,

* x86 test file
nelson@LAPTOP-QFSGI1F2:~/test/ifunc/x86$ cat x86.s
        .text
        .type   foo_resolver, @function
foo_resolver:
        ret
        .size   foo_resolver, .-foo_resolver
        .globl  foo
        .type   foo, %gnu_indirect_function
        .set    foo, foo_resolver
        .globl  main
        .type   main, @function
main:
        # GOT ifunc
        movq    foo@GOTPCREL(%rip), %rax
        # PCREL ifunc
        movq    foo(%rip), %rax
        # Data xxx
        movl xxx(%rip), %eax
        # PLT ifunc
        call    foo@PLT
        ret
        .size   main, .-main

        # Data ifunc
        .data
xxx:
        .quad foo

* x86 GNU ld ifunc behavior
nelson@LAPTOP-QFSGI1F2:~/test/ifunc/x86$ build-binutils-x86/build-install/bin/as x86.s -o x86.o
nelson@LAPTOP-QFSGI1F2:~/test/ifunc/x86$ build-binutils-x86/build-install/bin/readelf -Wr x86.o

Relocation section '.rela.text' at offset 0x1c0 contains 4 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000000004  000000070000002a R_X86_64_REX_GOTPCRELX foo()            foo - 4
000000000000000b  0000000700000002 R_X86_64_PC32          foo()            foo - 4
0000000000000011  0000000200000002 R_X86_64_PC32          0000000000000000 .data - 4
0000000000000016  0000000700000004 R_X86_64_PLT32         foo()            foo - 4

Relocation section '.rela.data' at offset 0x220 contains 1 entry:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000000000  0000000700000001 R_X86_64_64            foo()            foo + 0

nelson@LAPTOP-QFSGI1F2:~/test/ifunc/x86$ build-binutils-x86/build-install/bin/ld -pie x86.o -o x86.pie.ld
nelson@LAPTOP-QFSGI1F2:~/test/ifunc/x86$ build-binutils-x86/build-install/bin/readelf -Wr x86.pie.ld

Relocation section '.rela.dyn' at offset 0x300 contains 1 entry:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000003020  0000000000000025 R_X86_64_IRELATIVE      1020

Relocation section '.rela.plt' at offset 0x318 contains 1 entry:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000003018  0000000000000025 R_X86_64_IRELATIVE      1020

nelson@LAPTOP-QFSGI1F2:~/test/ifunc/x86$ build-binutils-x86/build-install/bin/objdump -d x86.pie.ld

x86.pie:     file format elf64-x86-64


Disassembly of section .plt:

0000000000001000 <.plt>:
    1000:       ff 35 02 20 00 00       push   0x2002(%rip)        #3008 <_GLOBAL_OFFSET_TABLE_+0x8>
    1006:       ff 25 04 20 00 00       jmp    *0x2004(%rip)       #3010 <_GLOBAL_OFFSET_TABLE_+0x10>
    100c:       0f 1f 40 00             nopl   0x0(%rax)
    1010:       ff 25 02 20 00 00       jmp    *0x2002(%rip)       #3018 <_GLOBAL_OFFSET_TABLE_+0x18>
    1016:       68 00 00 00 00          push   $0x0
    101b:       e9 e0 ff ff ff          jmp    1000 <.plt>

Disassembly of section .text:

0000000000001020 <foo_resolver>:
    1020:       c3                      ret

0000000000001021 <main>:
    1021:       48 8b 05 f0 1f 00 00    mov    0x1ff0(%rip),%rax   # 3018 <_GLOBAL_OFFSET_TABLE_+0x18>
    1028:       48 8b 05 e1 ff ff ff    mov    -0x1f(%rip),%rax    # 1010 <.plt+0x10>
    102f:       8b 05 eb 1f 00 00       mov    0x1feb(%rip),%eax   # 3020 <xxx>
    1035:       e8 d6 ff ff ff          call   1010 <.plt+0x10>
    103a:       c3                      ret

1. The first mov is GOT reference, 0x3018 is a got entry, and ld have
inserted a dynamic R_X86_64_IRELATIVE for it.
2. The second mov is PCREL reloc, it refers to the .plt entry 0x1010.
3. The fourth instruction call is the PLT reference (0x1010).  The PLT
and GOT share the same .got entry (0x3018).
4. The third mov refers to the data xxx (0x3020).  GNU ld inserts a
dynamic R_X86_64_IRELATIVE for the data section.

* x86 lld ifunc behavior (v12.0.0)
nelson@LAPTOP-QFSGI1F2:~/test/ifunc/x86$ build-binutils-x86/build-install/bin/readelf -Wr x86.pie.lld

Relocation section '.rela.dyn' at offset 0x248 contains 3 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
00000000000023d0  0000000000000008 R_X86_64_RELATIVE      12b0
00000000000033d8  0000000000000008 R_X86_64_RELATIVE      12b0
00000000000033f8  0000000000000025 R_X86_64_IRELATIVE     1290

nelson@LAPTOP-QFSGI1F2:~/test/ifunc/x86$ build-binutils-x86/build-install/bin/objdump -d x86.pie.lld

x86.pie.lld:     file format elf64-x86-64


Disassembly of section .text:

0000000000001290 <foo_resolver>:
    1290:       c3                      ret

0000000000001291 <main>:
    1291:       48 8b 05 38 11 00 00    mov    0x1138(%rip),%rax  # 23d0 <_DYNAMIC+0x110>
    1298:       48 8b 05 11 00 00 00    mov    0x11(%rip),%rax    # 12b0 <foo>
    129f:       8b 05 33 21 00 00       mov    0x2133(%rip),%eax  # 33d8 <xxx>
    12a5:       e8 06 00 00 00          call   12b0 <foo>
    12aa:       c3                      ret

Disassembly of section .iplt:

00000000000012b0 <foo>:
    12b0:       ff 25 42 21 00 00       jmp    *0x2142(%rip)      #33f8 <_GLOBAL_OFFSET_TABLE_+0x18>
    12b6:       68 00 00 00 00          push   $0x0
    12bb:       e9 40 ed ff ff          jmp    0 <foo_resolver-0x1290>

1. The first mov is GOT reference, 0x23d0 is a got entry.  lld insert
a dynamic R_X86_64_RELATIVE for it rather than the R_X86_64_IRELATIVE,
so we will get the .plt ifunc entry in the GOT, rather than the
resolved ifunc foo address.
2. The second mov is PCREL reloc, it refers to the .plt entry 0x12b0.
3. The fourth instruction call is the PLT reference (0x12b0).  The PLT
will refer to the .got.plt entry 0x33f8, and ld should insert a
dynamic R_X86_64_IRELATIVE for it.
4. The third mov refers to the data xxx (0x33d8).  lld inserts a
dynamic R_X86_64_RELATIVE for the data section, it refers to the
corresponding .plt entry rather than the resolved ifunc foo address.


Please feel free to correct me if I'm wrong or miss anything.

Thanks
Nelson


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v3 1/2] RISC-V: Support GNU indirect functions.
  2020-10-07  3:48 [PATCH v3 0/2] RISC-V: Support GNU indirect functions Nelson Chu
@ 2020-10-07  3:48 ` Nelson Chu
  2021-01-06  6:46   ` Fangrui Song
  2020-10-07  3:48 ` [PATCH v3 2/2] RISC-V: Fix that IRELATIVE relocs may be inserted to the wrong place Nelson Chu
  2020-10-14  0:58 ` [PATCH v3 0/2] RISC-V: Support GNU indirect functions Jim Wilson
  2 siblings, 1 reply; 8+ messages in thread
From: Nelson Chu @ 2020-10-07  3:48 UTC (permalink / raw)
  To: binutils, jimw, palmer, andrew, i; +Cc: vincent.chen, kito.cheng, nelson.chu

Generally, glibc dynamic linker should have two ways to deal with ifunc
- one is to handle the IRELATIVE relocations for the non-preemtive ifunc
symbols, the other is to handle the R_RISCV_32/64 and R_RISCV_JUMP_SLOT
relocations with the STT_IFUNC preemtive symbols.  No matter which method
is used, both of them should get the resolved ifunc symbols at runtime.
Therefore, linker needs to generate the correct dynamic relocations for
ifunc to make sure the the dynamic linker works well.  For now, there are
thirteen relocations are supported for ifunc in GNU ld,

* R_RISCV_CALL and R_RISCV_CALL_PLT:
The RISC-V compiler won't generate R_RISCV_JAL directly to jump to an
ifunc.  Besides, we disable the relaxations for the relocation referenced
to ifunc, so just handling the R_RISCV_CALL and R_RISCV_CALL_PLT should be
enough.  Linker should generate a .plt entry and a .got.plt entry for it,
and also needs to insert a dynamic IRELATIVE in the .got.plt enrty, or
insert a R_RISCV_JUMP_SLOT when generating shared library.

* 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.  The current linker will deal with them in
the same way as R_RISCV_CALL_PLT.

* R_RISCV_GOT_HI20 and R_RISCV_PCREL_LO12_I/S:
LA pattern with global PIC ifunc symbol.  Linker should insert a dynamic
IRELATIVE in the .got entry, or insert a R_RISCV_32/64 when generating
shared library.

* R_RISCV_32 and R_RISCV_64:
Store the ifunc symbol into the data section.  Linker should insert a
dynamic IRELATIVE in the data section, or insert a R_RISCV_32/64 when
generating shared library.

* R_RISCV_HI20 and R_RISCV_LO12_I/S:
The LUI + ADDI/LW/SW patterns.  The absolute access relocation.  The
medlow model without the -fPIC compiler option should generate them.
The ld ifunc testsuites "Build pr23169a" and "Build pr23169d" need the
relocations, they are in the ld/testsuite/ld-ifunc/, and need compiler
support.

However, we also made some optimizations with reference to x86,

* If GOT and PLT relocations refer to the same ifunc symbol when generating
pie, then they can actually share a .got entry without creating two entries
to store the same value and relocation.

* If GOT, PLT and DATA relocations refer to the same ifunc symbol when
generating position dependency executable, then linker will fill the address
of .plt entry into the corresponding .got entry and data section, without
insert any dynamic relocations for the GOT and DATA relocations.

For the ifunc testcases, there are three types of them,

1. ifunc-reloc-*: Only check the single type of relocation refers to
ifunc symbol.
* ifunc-reloc-call: R_RISCV_CALL and R_RISCV_CALL_PLT.
* ifunc-reloc-data: R_RISCV_32 and R_RISCV_64.
* ifunc-reloc-got: R_RISCV_GOT_HI20 and R_RISCV_PCREL_LO_I/S.
* ifunc-reloc-pcrel: R_RISCV_PCREL_HI20 and R_RISCV_PCREL_LO_I/S.

2. ifunc-[nonplt|plt]-*: If we don't have PLT relocs, then don't need to
create the PLT and it's .plt entries.
* ifunc-nonplt: Combine R_RISCV_GOT_HI20 and R_RISCV_32/64.
* ifunc-plt: Combine all ifunc relocations.

3. ifunc-seperate-*: If we link the ifunc caller and resolver into the
same module (link the objects), then the results are the same as the
ifunc-reloc-* and ifunc-[noplt|plt]-* testcases.  Consider the cases that
the ifunc callers and resolver are in the different modules, that is, we
compile the ifunc resolver to the shared library first, and then link it
with the ifunc callers.  The output of ifunc callers should be the same as
the normal STT_FUNC cases, and the shared ifunc resolver should define the
symbols as STT_IFUNC.

The R_RISCV_PCREL_HI20 reloc is special.  It should be linked and resolved
locally, so if the ifunc resolver is defined in other modules (other shared
libraries), then the R_RISCV_PCREL_HI20 is unresolvable, and linker should
issue an unresolvable reloc error.

	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
	for 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 relocations
	reference to ifunc symbols.
	(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 tests for RISC-V.
	* testsuite/ld-riscv-elf/ld-riscv-elf.exp (run_dump_test_ifunc):
	New dump test for ifunc.  There are two arguments, 'target` and
	`output`.  The `target` is rv32 or rv64, and the `output` is used
	to choose which output you want to test (exe, pie or .so).
	* testsuite/ld-riscv-elf/ifunc-reloc-call-01.s: New testcase.
	* testsuite/ld-riscv-elf/ifunc-reloc-call-01.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-call-01-pic.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-call-01-pie.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-call-02.s: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-call-02.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-call-02-pic.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-call-02-pie.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-data.s: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-data.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-got.s: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-got.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-nonplt.s: Likewise.
	* testsuite/ld-riscv-elf/ifunc-nonplt.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-nonplt-exe.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-01.s: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-01.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-02.s: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-02.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-seperate-resolver.s: Likewise.
	* testsuite/ld-riscv-elf/ifunc-seperate-caller.s: Likewise.
	* testsuite/ld-riscv-elf/ifunc-seperate-exe.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-seperate-pic.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-seperate-pie.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s: Likewise.
	* testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d: Likewise.
---
 bfd/configure                                      |   4 +-
 bfd/configure.ac                                   |   4 +-
 bfd/elfnn-riscv.c                                  | 725 +++++++++++++++++++--
 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-nonplt-exe.rd      |   4 +
 ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd      |   7 +
 ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd      |   7 +
 ld/testsuite/ld-riscv-elf/ifunc-nonplt.d           |  11 +
 ld/testsuite/ld-riscv-elf/ifunc-nonplt.s           |  39 ++
 ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd      |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd      |   7 +
 ld/testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd      |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-plt-01.d           |  19 +
 ld/testsuite/ld-riscv-elf/ifunc-plt-01.s           |  31 +
 ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd      |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd      |  11 +
 ld/testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd      |   7 +
 ld/testsuite/ld-riscv-elf/ifunc-plt-02.d           |  21 +
 ld/testsuite/ld-riscv-elf/ifunc-plt-02.s           |  46 ++
 .../ld-riscv-elf/ifunc-reloc-call-01-exe.rd        |   3 +
 .../ld-riscv-elf/ifunc-reloc-call-01-pic.rd        |   3 +
 .../ld-riscv-elf/ifunc-reloc-call-01-pie.rd        |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.d    |  13 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s    |  17 +
 .../ld-riscv-elf/ifunc-reloc-call-02-exe.rd        |   3 +
 .../ld-riscv-elf/ifunc-reloc-call-02-pic.rd        |   3 +
 .../ld-riscv-elf/ifunc-reloc-call-02-pie.rd        |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.d    |  15 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s    |  18 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd  |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd  |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd  |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-data.d       |   9 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s       |  31 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd   |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd   |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd   |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-got.d        |   9 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s        |  23 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd |   3 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d      |  15 +
 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s      |  26 +
 .../ld-riscv-elf/ifunc-seperate-caller-nonplt.s    |  23 +
 .../ld-riscv-elf/ifunc-seperate-caller-pcrel.s     |  14 +
 .../ld-riscv-elf/ifunc-seperate-caller-plt.s       |  26 +
 .../ld-riscv-elf/ifunc-seperate-nonplt-exe.d       |  14 +
 .../ld-riscv-elf/ifunc-seperate-nonplt-pic.d       |  13 +
 .../ld-riscv-elf/ifunc-seperate-nonplt-pie.d       |  14 +
 .../ld-riscv-elf/ifunc-seperate-pcrel-pic.d        |   5 +
 .../ld-riscv-elf/ifunc-seperate-pcrel-pie.d        |   5 +
 ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d |  14 +
 ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d |  17 +
 ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d |  18 +
 .../ld-riscv-elf/ifunc-seperate-resolver.s         |  11 +
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp         | 111 ++++
 60 files changed, 1426 insertions(+), 53 deletions(-)
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-nonplt-exe.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-nonplt.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-nonplt.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-01.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-01.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-02.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-02.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-data.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-got.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-nonplt.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-plt.s
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-exe.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pie.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-resolver.s

diff --git a/bfd/configure b/bfd/configure
index 636f338..5d84aed 100755
--- a/bfd/configure
+++ b/bfd/configure
@@ -14917,8 +14917,8 @@ do
     powerpc_elf64_fbsd_le_vec)	 tb="$tb elf64-ppc.lo elf64-gen.lo elf64.lo $elf" target_size=64 ;;
     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 cecb0fb..5ec4d4f 100644
--- a/bfd/configure.ac
+++ b/bfd/configure.ac
@@ -623,8 +623,8 @@ do
     powerpc_elf64_fbsd_le_vec)	 tb="$tb elf64-ppc.lo elf64-gen.lo elf64.lo $elf" target_size=64 ;;
     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 c088278..a26cd3f 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)
@@ -115,6 +116,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;
 };
 
 
@@ -153,17 +158,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
@@ -265,6 +266,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 *
@@ -286,6 +367,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;
 }
 
@@ -477,6 +572,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"),
@@ -526,7 +624,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->elf.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];
@@ -535,6 +658,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:
@@ -574,12 +723,26 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  h->plt.refcount += 1;
 	  break;
 
+	case R_RISCV_PCREL_HI20:
+	  if (h != NULL
+	      && h->type == STT_GNU_IFUNC)
+	    {
+	      h->non_got_ref = 1;
+	      h->pointer_equality_needed = 1;
+
+	      /* We don't use the PCREL_HI20 in the data section,
+		 so we always need the plt when it refers to
+		 ifunc symbol.  */
+	      h->plt.refcount += 1;
+	    }
+	  /* Fall through.  */
+
 	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.  */
+	  /* In shared libraries and pie, these relocs are known
+	     to bind locally.  */
 	  if (bfd_link_pic (info))
 	    break;
 	  goto static_reloc;
@@ -604,15 +767,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_pic (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,21 +806,28 @@ 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 (R_RISCV_32/R_RISCV_64).  */
 	  reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
 
 	  if ((bfd_link_pic (info)
 	       && (sec->flags & SEC_ALLOC) != 0
-	       && ((r != NULL && ! r->pc_relative)
+	       && ((r != NULL && !r->pc_relative)
 		   || (h != NULL
-		       && (! info->symbolic
+		       && (!info->symbolic
 			   || h->root.type == bfd_link_hash_defweak
 			   || !h->def_regular))))
 	      || (!bfd_link_pic (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 +964,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 +1080,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 +1273,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 +1412,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 +1455,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 +1889,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 +1907,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 +1932,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 +1973,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;
+
+		    riscv_elf_append_rela (output_bfd, sreloc, &outrel);
+
+		    /* If this reloc is against an external symbol, we
+		       do not want to fiddle with the addend.  Otherwise,
+		       we need to include the symbol value so that it
+		       becomes an addend for the dynamic reloc.  For an
+		       internal symbol, we have updated addend.  */
+		    continue;
+		  }
+		goto do_relocation;
+
+	      case R_RISCV_GOT_HI20:
+		base_got = htab->elf.sgot;
+		off = h->got.offset;
+
+		if (base_got == NULL)
+		  abort ();
+
+		if (off == (bfd_vma) -1)
+		  {
+		    bfd_vma plt_idx;
+
+		    /* We can't use h->got.offset here to save state, or
+		       even just remember the offset, as finish_dynamic_symbol
+		       would use that as offset into .got.  */
+
+		    if (htab->elf.splt != NULL)
+		      {
+			plt_idx = (h->plt.offset - PLT_HEADER_SIZE)
+				  / PLT_ENTRY_SIZE;
+			off = GOTPLT_HEADER_SIZE + (plt_idx * GOT_ENTRY_SIZE);
+			base_got = htab->elf.sgotplt;
+		      }
+		    else
+		      {
+			plt_idx = h->plt.offset / PLT_ENTRY_SIZE;
+			off = plt_idx * GOT_ENTRY_SIZE;
+			base_got = htab->elf.igotplt;
+		      }
+
+		    if (h->dynindx == -1
+			|| h->forced_local
+			|| info->symbolic)
+		      {
+			/* This references the local definition.  We must
+			   initialize this entry in the global offset table.
+			   Since the offset must always be a multiple of 8,
+			   we use the least significant bit to record
+			   whether we have initialized it already.
+
+			   When doing a dynamic link, we create a .rela.got
+			   relocation entry to initialize the value.  This
+			   is done in the finish_dynamic_symbol routine.   */
+			if ((off & 1) != 0)
+			  off &= ~1;
+			else
+			  {
+			    bfd_put_NN (output_bfd, relocation,
+					base_got->contents + off);
+			    /* Note that this is harmless for the case,
+			       as -1 | 1 still is -1.  */
+			    h->got.offset |= 1;
+			  }
+		      }
+		  }
+
+		relocation = base_got->output_section->vma
+			     + base_got->output_offset + off;
+
+		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 +2498,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 +2528,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;
@@ -2216,6 +2703,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);
@@ -2299,23 +2787,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,
@@ -2327,16 +2850,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)
@@ -2369,13 +2913,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;
@@ -2423,6 +3027,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
@@ -2549,6 +3165,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;
 }
 
@@ -4052,6 +4673,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
@@ -4082,6 +4709,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 e5adea5..003df59 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -854,6 +854,21 @@ static reloc_howto_type howto_table[] =
 	 0,				/* src_mask */
 	 0xffffffff,			/* 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 */
+	 0xffffffff,			/* 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 63ab18d..9ed4bd7 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-*-*]
@@ -736,7 +735,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-nonplt-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-exe.rd
new file mode 100644
index 0000000..0de47a4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-exe.rd
@@ -0,0 +1,4 @@
+Relocation section '.rela.plt' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd
new file mode 100644
index 0000000..e2e7ad9
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd
@@ -0,0 +1,7 @@
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+foo\(\)[ 	]+foo \+ 0
+#...
+Relocation section '.rela.ifunc' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+foo\(\)[ 	]+foo \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd
new file mode 100644
index 0000000..f9fbd87
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd
@@ -0,0 +1,7 @@
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
+#...
+Relocation section '.rela.ifunc' 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-nonplt.d b/ld/testsuite/ld-riscv-elf/ifunc-nonplt.d
new file mode 100644
index 0000000..e3517d3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt.d
@@ -0,0 +1,11 @@
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <foo_resolver>:
+#...
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+(lw|ld)[ 	]+.*<(_GLOBAL_OFFSET_TABLE_.*|.*)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+(lw|ld)[ 	]+.*<(__DATA_BEGIN__|.*)>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-nonplt.s b/ld/testsuite/ld-riscv-elf/ifunc-nonplt.s
new file mode 100644
index 0000000..ce6ca69
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt.s
@@ -0,0 +1,39 @@
+	.text
+
+	.type	foo_resolver, @function
+foo_resolver:
+	ret
+	.size	foo_resolver, .-foo_resolver
+
+	.globl	foo
+	.type	foo, %gnu_indirect_function
+	.set	foo, foo_resolver
+
+	.globl	bar
+	.type	bar, @function
+bar:
+.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	x2, %pcrel_hi (foo_addr)
+.ifdef __64_bit__
+	ld	x2, %pcrel_lo (.L2) (x2)
+.else
+	lw	x2, %pcrel_lo (.L2) (x2)
+.endif
+	ret
+	.size	bar, .-bar
+
+	.data
+foo_addr:
+.ifdef __64_bit__
+	.quad	foo
+.else
+	.long	foo
+.endif
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd
@@ -0,0 +1,3 @@
+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-plt-01-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd
new file mode 100644
index 0000000..6f5218b
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd
@@ -0,0 +1,7 @@
+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-plt-01-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd
@@ -0,0 +1,3 @@
+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-plt-01.d b/ld/testsuite/ld-riscv-elf/ifunc-plt-01.d
new file mode 100644
index 0000000..bed9fe6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-01.d
@@ -0,0 +1,19 @@
+#...
+Disassembly of section .plt:
+#...
+0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <foo_resolver>:
+#...
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+(lw|ld)[ 	]+.*<(_GLOBAL_OFFSET_TABLE_.*|__DATA_BEGIN__.*|.*)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-01.s b/ld/testsuite/ld-riscv-elf/ifunc-plt-01.s
new file mode 100644
index 0000000..65c65cd
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-01.s
@@ -0,0 +1,31 @@
+	.text
+
+	.type	foo_resolver, @function
+foo_resolver:
+	ret
+	.size	foo_resolver, .-foo_resolver
+
+	.globl	foo
+	.type	foo, %gnu_indirect_function
+	.set	foo, foo_resolver
+
+	.globl	bar
+	.type	bar, @function
+bar:
+.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	x2, %pcrel_hi (foo)
+	addi	x2, x2, %pcrel_lo (.L2)
+
+	call	foo
+	call	foo@plt
+
+	ret
+	.size	bar, .-bar
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd
@@ -0,0 +1,3 @@
+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-plt-02-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd
new file mode 100644
index 0000000..3299aa4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd
@@ -0,0 +1,11 @@
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+foo\(\)[ 	]+foo \+ 0
+#...
+Relocation section '.rela.ifunc' 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-plt-02-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd
new file mode 100644
index 0000000..28a3c99
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd
@@ -0,0 +1,7 @@
+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-plt-02.d b/ld/testsuite/ld-riscv-elf/ifunc-plt-02.d
new file mode 100644
index 0000000..b8638b9
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02.d
@@ -0,0 +1,21 @@
+#...
+Disassembly of section .plt:
+#...
+0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <foo_resolver>:
+#...
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+(lw|ld)[ 	]+.*<(_GLOBAL_OFFSET_TABLE_.*|.*)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+(lw|ld)[ 	]+.*<(__DATA_BEGIN__.*|.*)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-02.s b/ld/testsuite/ld-riscv-elf/ifunc-plt-02.s
new file mode 100644
index 0000000..c3022be
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02.s
@@ -0,0 +1,46 @@
+	.text
+
+	.type	foo_resolver, @function
+foo_resolver:
+	ret
+	.size	foo_resolver, .-foo_resolver
+
+	.globl	foo
+	.type	foo, %gnu_indirect_function
+	.set	foo, foo_resolver
+
+	.globl	bar
+	.type	bar, @function
+bar:
+.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	x2, %pcrel_hi (foo_addr)
+.ifdef __64_bit__
+	ld	x2, %pcrel_lo (.L2) (x2)
+.else
+	lw	x2, %pcrel_lo (.L2) (x2)
+.endif
+
+.L3:
+	auipc	x3, %pcrel_hi (foo)
+	addi	x3, x3, %pcrel_lo (.L3)
+
+	call	foo
+	call	foo@plt
+	ret
+	.size	bar, .-bar
+
+	.data
+foo_addr:
+.ifdef __64_bit__
+	.quad	foo
+.else
+	.long	foo
+.endif
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd
@@ -0,0 +1,3 @@
+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-reloc-call-01-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pic.rd
new file mode 100644
index 0000000..7bfaa2d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pic.rd
@@ -0,0 +1,3 @@
+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-reloc-call-01-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pie.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pie.rd
@@ -0,0 +1,3 @@
+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-reloc-call-01.d b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.d
new file mode 100644
index 0000000..d4457c9
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.d
@@ -0,0 +1,13 @@
+#...
+Disassembly of section .plt:
+#...
+0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <foo_resolver>:
+#...
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s
new file mode 100644
index 0000000..89e6326
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s
@@ -0,0 +1,17 @@
+	.text
+
+	.type	foo_resolver, @function
+foo_resolver:
+	ret
+	.size	foo_resolver, .-foo_resolver
+
+	.globl	foo
+	.type	foo, %gnu_indirect_function
+	.set	foo, foo_resolver
+
+	.globl	bar
+	.type	bar, @function
+bar:
+	call	foo
+	ret
+	.size	bar, .-bar
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd
@@ -0,0 +1,3 @@
+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-reloc-call-02-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pic.rd
new file mode 100644
index 0000000..7bfaa2d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pic.rd
@@ -0,0 +1,3 @@
+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-reloc-call-02-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pie.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pie.rd
@@ -0,0 +1,3 @@
+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-reloc-call-02.d b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.d
new file mode 100644
index 0000000..40c0309
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.d
@@ -0,0 +1,15 @@
+#...
+Disassembly of section .plt:
+#...
+0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <foo_resolver>:
+#...
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s
new file mode 100644
index 0000000..e493c47
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s
@@ -0,0 +1,18 @@
+	.text
+
+	.type	foo_resolver, @function
+foo_resolver:
+	ret
+	.size	foo_resolver, .-foo_resolver
+
+	.globl	foo
+	.type	foo, %gnu_indirect_function
+	.set	foo, foo_resolver
+
+	.globl	bar
+	.type	bar, @function
+bar:
+	call	foo@plt
+	call	foo
+	ret
+	.size	bar, .-bar
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd
@@ -0,0 +1,3 @@
+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-reloc-data-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd
new file mode 100644
index 0000000..9be346b
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd
@@ -0,0 +1,3 @@
+Relocation section '.rela.ifunc' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+foo\(\)[ 	]+foo \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd
new file mode 100644
index 0000000..e14b02b
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd
@@ -0,0 +1,3 @@
+Relocation section '.rela.ifunc' 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-reloc-data.d b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.d
new file mode 100644
index 0000000..1956cc3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.d
@@ -0,0 +1,9 @@
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <foo_resolver>:
+#...
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+(lw|ld)[ 	]+.*<(__DATA_BEGIN__.*|.*)>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s
new file mode 100644
index 0000000..b49bda1
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s
@@ -0,0 +1,31 @@
+	.text
+
+	.type	foo_resolver, @function
+foo_resolver:
+	ret
+	.size	foo_resolver, .-foo_resolver
+
+	.globl	foo
+	.type	foo, %gnu_indirect_function
+	.set	foo, foo_resolver
+
+	.globl	bar
+	.type	bar, @function
+bar:
+.L1:
+	auipc	x1, %pcrel_hi (foo_addr)
+.ifdef __64_bit__
+	ld	x1, %pcrel_lo (.L1) (x1)
+.else
+	lw	x1, %pcrel_lo (.L1) (x1)
+.endif
+	ret
+	.size	bar, .-bar
+
+	.data
+foo_addr:
+.ifdef __64_bit__
+	.quad	foo
+.else
+	.long	foo
+.endif
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd
@@ -0,0 +1,3 @@
+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-reloc-got-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd
new file mode 100644
index 0000000..41cbc07
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd
@@ -0,0 +1,3 @@
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+foo\(\)[ 	]+foo \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd
new file mode 100644
index 0000000..cef1a77
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd
@@ -0,0 +1,3 @@
+Relocation section '.rela.got' 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-reloc-got.d b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got.d
new file mode 100644
index 0000000..3277e8f
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got.d
@@ -0,0 +1,9 @@
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <foo_resolver>:
+#...
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+(lw|ld)[ 	]+.*<(_GLOBAL_OFFSET_TABLE_.*|.*)>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s
new file mode 100644
index 0000000..eca16d5
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s
@@ -0,0 +1,23 @@
+	.text
+
+	.type	foo_resolver, @function
+foo_resolver:
+	ret
+	.size	foo_resolver, .-foo_resolver
+
+	.globl	foo
+	.type	foo, %gnu_indirect_function
+	.set	foo, foo_resolver
+
+	.globl	bar
+	.type	bar, @function
+bar:
+.L1:
+	auipc	x1, %got_pcrel_hi (foo)
+.ifdef __64_bit__
+	ld	x1, %pcrel_lo (.L1) (x1)
+.else
+	lw	x1, %pcrel_lo (.L1) (x1)
+.endif
+	ret
+	.size	bar, .-bar
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd
@@ -0,0 +1,3 @@
+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-reloc-pcrel-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd
new file mode 100644
index 0000000..7bfaa2d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd
@@ -0,0 +1,3 @@
+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-reloc-pcrel-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd
new file mode 100644
index 0000000..97461e4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd
@@ -0,0 +1,3 @@
+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-reloc-pcrel.d b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d
new file mode 100644
index 0000000..bc947e3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d
@@ -0,0 +1,15 @@
+#...
+Disassembly of section .plt:
+#...
+0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <foo_resolver>:
+#...
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+addi[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+(lw|ld)[ 	]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s
new file mode 100644
index 0000000..7ea454c
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s
@@ -0,0 +1,26 @@
+	.text
+
+	.type	foo_resolver, @function
+foo_resolver:
+	ret
+	.size	foo_resolver, .-foo_resolver
+
+	.globl	foo
+	.type	foo, %gnu_indirect_function
+	.set	foo, foo_resolver
+
+	.globl	bar
+	.type	bar, @function
+bar:
+.L1:
+	auipc	x1, %pcrel_hi (foo)
+	addi	x1, x1, %pcrel_lo (.L1)
+.L2:
+	auipc	x2, %pcrel_hi (foo)
+.ifdef __64_bit__
+	ld	x2, %pcrel_lo (.L2) (x2)
+.else
+	lw	x2, %pcrel_lo (.L2) (x2)
+.endif
+	ret
+	.size	bar, .-bar
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-nonplt.s b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-nonplt.s
new file mode 100644
index 0000000..23c7254
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-nonplt.s
@@ -0,0 +1,23 @@
+	.text
+
+	# Call the IFUNC `foo` which is defined in the other modules.
+	.globl	foo
+	.type	foo, %function
+
+	.globl	main
+	.type	main, @function
+main:
+.L1:
+	auipc	x1, %got_pcrel_hi (foo)
+	addi	x1, x1, %pcrel_lo (.L1)
+
+.L2:
+	auipc	x2, %pcrel_hi (foo_addr)
+	addi	x2, x2, %pcrel_lo (.L2)
+
+	ret
+	.size	main, .-main
+
+	.data
+foo_addr:
+	.long	foo
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s
new file mode 100644
index 0000000..2d29bcd
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s
@@ -0,0 +1,14 @@
+	.text
+
+	# Call the IFUNC `foo` which is defined in the other modules.
+	.globl	foo
+	.type	foo, %function
+
+	.globl	main
+	.type	main, @function
+main:
+.L1:
+	auipc	x1, %pcrel_hi (foo)
+	addi	x1, x1, %pcrel_lo (.L1)
+	ret
+	.size	main, .-main
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-plt.s b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-plt.s
new file mode 100644
index 0000000..8aa6403
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-plt.s
@@ -0,0 +1,26 @@
+	.text
+
+	# Call the IFUNC `foo` which is defined in the other modules.
+	.globl	foo
+	.type	foo, %function
+
+	.globl	main
+	.type	main, @function
+main:
+.L1:
+	auipc	x1, %got_pcrel_hi (foo)
+	addi	x1, x1, %pcrel_lo (.L1)
+
+.L2:
+	auipc	x2, %pcrel_hi (foo_addr)
+	addi	x2, x2, %pcrel_lo (.L2)
+
+	call	foo
+	call	foo@plt
+
+	ret
+	.size	main, .-main
+
+	.data
+foo_addr:
+	.long	foo
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-exe.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-exe.d
new file mode 100644
index 0000000..540a21b
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-exe.d
@@ -0,0 +1,14 @@
+#name: Link shared ifunc resolver with non-PLT caller (exe)
+#source: ifunc-seperate-caller-nonplt.s
+#as:
+#ld: -z nocombreloc tmpdir/ifunc-seperate-resolver.so
+#warning: .*
+#readelf: -rW
+
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+[0-9a-f]+[ 	]+foo \+ 0
+#...
+Relocation section '.rela.plt' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_JUMP_SLOT[ 	]+[0-9a-f]+[ 	]+foo \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pic.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pic.d
new file mode 100644
index 0000000..3ed1812
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pic.d
@@ -0,0 +1,13 @@
+#name: Link shared ifunc resolver with non-PLT caller (pic)
+#source: ifunc-seperate-caller-nonplt.s
+#as:
+#ld: -z nocombreloc -shared tmpdir/ifunc-seperate-resolver.so
+#readelf: -rW
+
+Relocation section '.rela.data' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+[0-9a-f]+[ 	]+foo \+ 0
+#...
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+[0-9a-f]+[ 	]+foo \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pie.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pie.d
new file mode 100644
index 0000000..c9c9eab
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pie.d
@@ -0,0 +1,14 @@
+#name: Link shared ifunc resolver with non-PLT caller (pie)
+#source: ifunc-seperate-caller-nonplt.s
+#as:
+#ld: -z nocombreloc -pie tmpdir/ifunc-seperate-resolver.so
+#warning: .*
+#readelf: -rW
+
+Relocation section '.rela.data' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+[0-9a-f]+[ 	]+foo \+ 0
+#...
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+[0-9a-f]+[ 	]+foo \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d
new file mode 100644
index 0000000..1c11a2d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d
@@ -0,0 +1,5 @@
+#name: Link shared IFUNC resolver with PCREL caller (pic)
+#source: ifunc-seperate-caller-pcrel.s
+#as:
+#ld: -z nocombreloc -shared tmpdir/ifunc-seperate-resolver.so
+#error: .*unresolvable R_RISCV_PCREL_HI20 relocation.*
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d
new file mode 100644
index 0000000..0d0e3cc
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d
@@ -0,0 +1,5 @@
+#name: Link shared IFUNC resolver with PCREL caller (pie)
+#source: ifunc-seperate-caller-pcrel.s
+#as:
+#ld: -z nocombreloc -pie tmpdir/ifunc-seperate-resolver.so
+#error: .*unresolvable R_RISCV_PCREL_HI20 relocation.*
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d
new file mode 100644
index 0000000..a538564
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d
@@ -0,0 +1,14 @@
+#name: Link shared ifunc resolver with PLT caller (exe)
+#source: ifunc-seperate-caller-plt.s
+#as:
+#ld: -z nocombreloc tmpdir/ifunc-seperate-resolver.so
+#warning: .*
+#readelf: -rW
+
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+[0-9a-f]+[ 	]+foo \+ 0
+#...
+Relocation section '.rela.plt' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_JUMP_SLOT[ 	]+[0-9a-f]+[ 	]+foo \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d
new file mode 100644
index 0000000..9efa244
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d
@@ -0,0 +1,17 @@
+#name: Link shared ifunc resolver with PLT caller (pic)
+#source: ifunc-seperate-caller-plt.s
+#as:
+#ld: -z nocombreloc -shared tmpdir/ifunc-seperate-resolver.so
+#readelf: -rW
+
+Relocation section '.rela.data' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+[0-9a-f]+[ 	]+foo \+ 0
+#...
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+[0-9a-f]+[ 	]+foo \+ 0
+#...
+Relocation section '.rela.plt' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_JUMP_SLOT[ 	]+[0-9a-f]+[ 	]+foo \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d
new file mode 100644
index 0000000..8349e61
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d
@@ -0,0 +1,18 @@
+#name: Link shared ifunc resolver with PLT caller (pie)
+#source: ifunc-seperate-caller-plt.s
+#as:
+#ld: -z nocombreloc -pie tmpdir/ifunc-seperate-resolver.so
+#warning: .*
+#readelf: -rW
+
+Relocation section '.rela.data' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+[0-9a-f]+[ 	]+foo \+ 0
+#...
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+[0-9a-f]+[ 	]+foo \+ 0
+#...
+Relocation section '.rela.plt' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_JUMP_SLOT[ 	]+[0-9a-f]+[ 	]+foo \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-resolver.s b/ld/testsuite/ld-riscv-elf/ifunc-seperate-resolver.s
new file mode 100644
index 0000000..a222847
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-resolver.s
@@ -0,0 +1,11 @@
+	.text
+
+	.type	foo_resolver, @function
+foo_resolver:
+	ret
+	.size	foo_resolver, .-foo_resolver
+
+	# The ifunc `foo` is called by the ifunc-caller.
+	.globl	foo
+	.type	foo, %gnu_indirect_function
+	.set	foo, foo_resolver
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index 2c008d4..b82e092 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -19,6 +19,47 @@
 # MA 02110-1301, USA.
 #
 
+# target: rv32 or rv64.
+# output: Which output you want?  (exe, pie, .so)
+proc run_dump_test_ifunc { name target output} {
+    set asflags ""
+    set ldflags "-z nocombreloc"
+
+    switch -- $output {
+	exe {
+	    set ext "exe"
+	}
+	pie {
+	    set ext "pie"
+	    set ldflags "$ldflags -pie"
+	}
+	pic {
+	    set ext "so"
+	    set ldflags "$ldflags -shared"
+	}
+    }
+
+    switch -- $target {
+	rv32 {
+	    set asflags "$asflags -march=rv32i -mabi=ilp32"
+	    set ldflags "$ldflags -melf32lriscv"
+	}
+	rv64 {
+	    set asflags "$asflags -march=rv64i -mabi=lp64 -defsym __64_bit__=1"
+	    set ldflags "$ldflags -melf64lriscv"
+	}
+    }
+
+    run_ld_link_tests [list \
+	[list "$name ($target-$output)" \
+	      "$ldflags" "" \
+	      "$asflags" \
+	      [list "$name.s"] \
+	      [concat [list "readelf -rW $name-$output.rd"] \
+		      [list "objdump -dw $name.d"]] \
+	      "$name-$target.$ext"]]
+}
+
 if [istarget "riscv*-*-*"] {
     run_dump_test "call-relax"
     run_dump_test "c-lui"
@@ -88,4 +129,74 @@ if [istarget "riscv*-*-*"] {
 	    {} "lib-nopic-01a.so" }
     }
     run_dump_test "lib-nopic-01b"
+
+    # IFUNC testcases.
+    # Check IFUNC by single type relocs.
+    run_dump_test_ifunc "ifunc-reloc-call-01" rv32 exe
+    run_dump_test_ifunc "ifunc-reloc-call-01" rv32 pie
+    run_dump_test_ifunc "ifunc-reloc-call-01" rv32 pic
+    run_dump_test_ifunc "ifunc-reloc-call-02" rv32 exe
+    run_dump_test_ifunc "ifunc-reloc-call-02" rv32 pie
+    run_dump_test_ifunc "ifunc-reloc-call-02" rv32 pic
+    run_dump_test_ifunc "ifunc-reloc-pcrel" rv32 exe
+    run_dump_test_ifunc "ifunc-reloc-pcrel" rv32 pie
+    run_dump_test_ifunc "ifunc-reloc-pcrel" rv32 pic
+    run_dump_test_ifunc "ifunc-reloc-data" rv32 exe
+    run_dump_test_ifunc "ifunc-reloc-data" rv32 pie
+    run_dump_test_ifunc "ifunc-reloc-data" rv32 pic
+    run_dump_test_ifunc "ifunc-reloc-got" rv32 exe
+    run_dump_test_ifunc "ifunc-reloc-got" rv32 pie
+    run_dump_test_ifunc "ifunc-reloc-got" rv32 pic
+    run_dump_test_ifunc "ifunc-reloc-pcrel" rv64 exe
+    run_dump_test_ifunc "ifunc-reloc-pcrel" rv64 pie
+    run_dump_test_ifunc "ifunc-reloc-pcrel" rv64 pic
+    run_dump_test_ifunc "ifunc-reloc-data" rv64 exe
+    run_dump_test_ifunc "ifunc-reloc-data" rv64 pie
+    run_dump_test_ifunc "ifunc-reloc-data" rv64 pic
+    run_dump_test_ifunc "ifunc-reloc-got" rv64 exe
+    run_dump_test_ifunc "ifunc-reloc-got" rv64 pie
+    run_dump_test_ifunc "ifunc-reloc-got" rv64 pic
+    # Check the IFUNC PLT and non-PLT relocs.
+    run_dump_test_ifunc "ifunc-nonplt" rv32 exe
+    run_dump_test_ifunc "ifunc-nonplt" rv32 pie
+    run_dump_test_ifunc "ifunc-nonplt" rv32 pic
+    run_dump_test_ifunc "ifunc-plt-01" rv32 exe
+    run_dump_test_ifunc "ifunc-plt-01" rv32 pie
+    run_dump_test_ifunc "ifunc-plt-01" rv32 pic
+    run_dump_test_ifunc "ifunc-plt-02" rv32 exe
+    run_dump_test_ifunc "ifunc-plt-02" rv32 pie
+    run_dump_test_ifunc "ifunc-plt-02" rv32 pic
+    run_dump_test_ifunc "ifunc-nonplt" rv64 exe
+    run_dump_test_ifunc "ifunc-nonplt" rv64 pie
+    run_dump_test_ifunc "ifunc-nonplt" rv64 pic
+    run_dump_test_ifunc "ifunc-plt-01" rv64 exe
+    run_dump_test_ifunc "ifunc-plt-01" rv64 pie
+    run_dump_test_ifunc "ifunc-plt-01" rv64 pic
+    run_dump_test_ifunc "ifunc-plt-02" rv64 exe
+    run_dump_test_ifunc "ifunc-plt-02" rv64 pie
+    run_dump_test_ifunc "ifunc-plt-02" rv64 pic
+
+    # Setup shared libraries.
+    run_ld_link_tests {
+       { "Build shared library for IFUNC non-PLT caller"
+	 "-shared" "" "" {ifunc-seperate-caller-nonplt.s}
+	 {} "ifunc-seperate-caller.so" }
+       { "Build shared library for IFUNC PLT caller"
+	 "-shared" "" "" {ifunc-seperate-caller-plt.s}
+	 {} "ifunc-seperate-caller.so" }
+       { "Build shared library for IFUNC resolver"
+	 "-shared" "" "" {ifunc-seperate-resolver.s}
+	 {} "ifunc-seperate-resolver.so" }
+    }
+    # The IFUNC resolver and caller are in the seperate modules.
+    # If IFUNC resolver and caller are linked to the same module,
+    # then the result are the same as the run_dump_test_ifunc.
+    run_dump_test "ifunc-seperate-nonplt-exe"
+    run_dump_test "ifunc-seperate-nonplt-pie"
+    run_dump_test "ifunc-seperate-nonplt-pic"
+    run_dump_test "ifunc-seperate-plt-exe"
+    run_dump_test "ifunc-seperate-plt-pie"
+    run_dump_test "ifunc-seperate-plt-pic"
+    run_dump_test "ifunc-seperate-pcrel-pie"
+    run_dump_test "ifunc-seperate-pcrel-pic"
 }
-- 
2.7.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v3 2/2] RISC-V: Fix that IRELATIVE relocs may be inserted to the wrong place.
  2020-10-07  3:48 [PATCH v3 0/2] RISC-V: Support GNU indirect functions Nelson Chu
  2020-10-07  3:48 ` [PATCH v3 1/2] " Nelson Chu
@ 2020-10-07  3:48 ` Nelson Chu
  2020-10-14  0:57   ` Jim Wilson
  2020-10-14  0:58 ` [PATCH v3 0/2] RISC-V: Support GNU indirect functions Jim Wilson
  2 siblings, 1 reply; 8+ messages in thread
From: Nelson Chu @ 2020-10-07  3:48 UTC (permalink / raw)
  To: binutils, jimw, palmer, andrew, i; +Cc: vincent.chen, kito.cheng, nelson.chu

For the ifunc symbol, which is referenced by GOT rather than PLT relocs,
we should add the dynamic reloc (usually IRELATIVE) into the .rel.iplt
when generating the static executable.  But if we use riscv_elf_append_rela
to add the dynamic relocs into .rela.iplt, this may cause the overwrite
problem.

The reason is that we don't handle the `reloc_index` of .rela.iplt, but
the riscv_elf_append_rela adds the relocs to the place that are calculated
from the reloc_index (in seqential).  Therefore, we may overwrite the
dynamic relocs when the `reloc_index` of .rela.iplt isn't handled correctly.

One solution is that we can add these dynamic relocs (GOT ifunc) from
the last of .rela.iplt section.  But I'm not sure if it is the best way.

	bfd/
	* elfnn-riscv.c (riscv_elf_link_hash_table): Add last_iplt_index.
	(riscv_elf_size_dynamic_sections): Initialize the last_iplt_index.
	(riscv_elf_relocate_section): Use riscv_elf_append_rela.
	(riscv_elf_finish_dynamic_symbol): If the use_elf_append_rela is
	false, then we should add the dynamic relocs from the last of
	the .rela.iplt, and don't use the riscv_elf_append_rela to add.

	ld/
	* testsuite/ld-riscv-elf/ifunc-plt-got-overwrite.s: New testcase.
	* testsuite/ld-riscv-elf/ifunc-plt-got-overwrite.d: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-exe.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-pic.rd: Likewise.
	* testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-pie.rd: Likewise.
	* testsuite/ld-riscv-elf/ld-riscv-elf.exp: Updated.
---
 bfd/elfnn-riscv.c                                  | 46 ++++++++++++++++++----
 .../ld-riscv-elf/ifunc-plt-got-overwrite-exe.rd    |  4 ++
 .../ld-riscv-elf/ifunc-plt-got-overwrite-pic.rd    |  8 ++++
 .../ld-riscv-elf/ifunc-plt-got-overwrite-pie.rd    |  7 ++++
 .../ld-riscv-elf/ifunc-plt-got-overwrite.d         | 19 +++++++++
 .../ld-riscv-elf/ifunc-plt-got-overwrite.s         | 38 ++++++++++++++++++
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp         |  7 ++++
 7 files changed, 122 insertions(+), 7 deletions(-)
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-exe.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-pic.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-pie.rd
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite.d
 create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite.s

diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index a26cd3f..c854957 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -120,6 +120,9 @@ struct riscv_elf_link_hash_table
   /* Used by local STT_GNU_IFUNC symbols.  */
   htab_t loc_hash_table;
   void * loc_hash_memory;
+
+  /* The index of the last unused .rel.iplt solt.  */
+  bfd_vma last_iplt_index;
 };
 
 
@@ -1424,6 +1427,11 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
      local ifunc symbols.  */
   htab_traverse (htab->loc_hash_table, allocate_local_ifunc_dynrelocs, info);
 
+  /* Used to resolve the dynamic relocs overwite problems when
+     generating static executable.  */
+  if (htab->elf.irelplt)
+    htab->last_iplt_index = htab->elf.irelplt->reloc_count - 1;
+
   if (htab->elf.sgotplt)
     {
       struct elf_link_hash_entry *got;
@@ -2904,6 +2912,7 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
       asection *sgot;
       asection *srela;
       Elf_Internal_Rela rela;
+      bfd_boolean use_elf_append_rela = TRUE;
 
       /* This symbol has an entry in the GOT.  Set it up.  */
 
@@ -2920,12 +2929,18 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
 	  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
+		  /* Use .rela.iplt section to store .got relocations
 		     in static executable.  */
 		  srela = htab->elf.irelplt;
+
+		  /* Do not use riscv_elf_append_rela to add dynamic
+		     relocs.  */
+		  use_elf_append_rela = FALSE;
 		}
+
 	      if (SYMBOL_REFERENCES_LOCAL (info, h))
 		{
 		  info->callbacks->minfo (_("Local IFUNC function `%s' in %pB\n"),
@@ -2973,14 +2988,14 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
 	      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.  */
       else if (bfd_link_pic (info)
 	       && SYMBOL_REFERENCES_LOCAL (info, h))
 	{
+	  /* 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.  */
 	  BFD_ASSERT((h->got.offset & 1) != 0);
 	  asection *sec = h->root.u.def.section;
 	  rela.r_info = ELFNN_R_INFO (0, R_RISCV_RELATIVE);
@@ -2998,7 +3013,24 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
 
       bfd_put_NN (output_bfd, 0,
 		  sgot->contents + (h->got.offset & ~(bfd_vma) 1));
-      riscv_elf_append_rela (output_bfd, srela, &rela);
+
+      if (use_elf_append_rela)
+	riscv_elf_append_rela (output_bfd, srela, &rela);
+      else
+	{
+	  /* Use riscv_elf_append_rela to add the dynamic relocs into
+	     .rela.iplt may cause the overwrite problems.  Since we insert
+	     the relocs for PLT didn't handle the reloc_index of .rela.iplt,
+	     but the riscv_elf_append_rela adds the relocs to the place
+	     that are calculated from the reloc_index (in seqential).
+
+	     One solution is that add these dynamic relocs (GOT IFUNC)
+	     from the last of .rela.iplt section.  */
+	  bfd_vma iplt_idx = htab->last_iplt_index--;
+	  bfd_byte *loc = srela->contents
+			  + iplt_idx * sizeof (ElfNN_External_Rela);
+	  bed->s->swap_reloca_out (output_bfd, &rela, loc);
+	}
     }
 
   if (h->needs_copy)
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-exe.rd
new file mode 100644
index 0000000..0de47a4
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-exe.rd
@@ -0,0 +1,4 @@
+Relocation section '.rela.plt' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_IRELATIVE[ 	]+[0-9a-f]*
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-pic.rd
new file mode 100644
index 0000000..f65d789
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-pic.rd
@@ -0,0 +1,8 @@
+Relocation section '.rela.got' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+foo2\(\)[ 	]+foo2 \+ 0
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_(32|64)[ 	]+foo1\(\)[ 	]+foo1 \+ 0
+#...
+Relocation section '.rela.plt' at .*
+[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+R_RISCV_JUMP_SLOT[ 	]+foo1\(\)[ 	]+foo1 \+ 0
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-pie.rd
new file mode 100644
index 0000000..32e66f0
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite-pie.rd
@@ -0,0 +1,7 @@
+Relocation section '.rela.got' 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-plt-got-overwrite.d b/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite.d
new file mode 100644
index 0000000..333dea3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite.d
@@ -0,0 +1,19 @@
+#...
+Disassembly of section .plt:
+#...
+0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <foo_resolver>:
+#...
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+(lw|ld)[ 	]+.*<(_GLOBAL_OFFSET_TABLE_.*|.*)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<(.*plt.*)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+.*<(.*plt.*)>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+.*
+.*:[ 	]+[0-9a-f]+[ 	]+(lw|ld)[ 	]+.*<(_GLOBAL_OFFSET_TABLE_.*|.*)>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite.s b/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite.s
new file mode 100644
index 0000000..6c2f8e8
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-got-overwrite.s
@@ -0,0 +1,38 @@
+	.text
+
+	.type	foo_resolver, @function
+foo_resolver:
+	ret
+	.size	foo_resolver, .-foo_resolver
+
+	.globl	foo1
+	.type	foo1, %gnu_indirect_function
+	.set	foo1, foo_resolver
+
+	.globl	foo2
+	.type	foo2, %gnu_indirect_function
+	.set	foo2, foo_resolver
+
+	.globl	bar
+	.type	bar, @function
+bar:
+.L1:
+	auipc	x1, %got_pcrel_hi (foo1)
+.ifdef __64_bit__
+	ld	x1, %pcrel_lo (.L1) (x1)
+.else
+	lw	x1, %pcrel_lo (.L1) (x1)
+.endif
+
+	call	foo1
+	call	foo1@plt
+
+.L2:
+	auipc	x2, %got_pcrel_hi (foo2)
+.ifdef __64_bit__
+	ld	x2, %pcrel_lo (.L2) (x2)
+.else
+	lw	x2, %pcrel_lo (.L2) (x2)
+.endif
+	ret
+	.size	bar, .-bar
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index b82e092..9834041 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -175,6 +175,13 @@ if [istarget "riscv*-*-*"] {
     run_dump_test_ifunc "ifunc-plt-02" rv64 exe
     run_dump_test_ifunc "ifunc-plt-02" rv64 pie
     run_dump_test_ifunc "ifunc-plt-02" rv64 pic
+    # Check the .rela.iplt overwrite issue.
+    run_dump_test_ifunc "ifunc-plt-got-overwrite" rv32 exe
+    run_dump_test_ifunc "ifunc-plt-got-overwrite" rv32 pie
+    run_dump_test_ifunc "ifunc-plt-got-overwrite" rv32 pic
+    run_dump_test_ifunc "ifunc-plt-got-overwrite" rv64 exe
+    run_dump_test_ifunc "ifunc-plt-got-overwrite" rv64 pie
+    run_dump_test_ifunc "ifunc-plt-got-overwrite" rv64 pic
 
     # Setup shared libraries.
     run_ld_link_tests {
-- 
2.7.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 2/2] RISC-V: Fix that IRELATIVE relocs may be inserted to the wrong place.
  2020-10-07  3:48 ` [PATCH v3 2/2] RISC-V: Fix that IRELATIVE relocs may be inserted to the wrong place Nelson Chu
@ 2020-10-14  0:57   ` Jim Wilson
  2020-10-16  2:15     ` Nelson Chu
  0 siblings, 1 reply; 8+ messages in thread
From: Jim Wilson @ 2020-10-14  0:57 UTC (permalink / raw)
  To: Nelson Chu
  Cc: Binutils, Palmer Dabbelt, Andrew Waterman, Fangrui Song,
	Vincent Chen, Kito Cheng

On Tue, Oct 6, 2020 at 8:48 PM Nelson Chu <nelson.chu@sifive.com> wrote:
> +  /* The index of the last unused .rel.iplt solt.  */
> +  bfd_vma last_iplt_index;

I noticed a comment typo, "solt" -> "slot".

Jim

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 0/2] RISC-V: Support GNU indirect functions
  2020-10-07  3:48 [PATCH v3 0/2] RISC-V: Support GNU indirect functions Nelson Chu
  2020-10-07  3:48 ` [PATCH v3 1/2] " Nelson Chu
  2020-10-07  3:48 ` [PATCH v3 2/2] RISC-V: Fix that IRELATIVE relocs may be inserted to the wrong place Nelson Chu
@ 2020-10-14  0:58 ` Jim Wilson
  2020-10-16  2:17   ` Nelson Chu
  2 siblings, 1 reply; 8+ messages in thread
From: Jim Wilson @ 2020-10-14  0:58 UTC (permalink / raw)
  To: Nelson Chu
  Cc: Binutils, Palmer Dabbelt, Andrew Waterman, Fangrui Song,
	Vincent Chen, Kito Cheng

On Tue, Oct 6, 2020 at 8:48 PM Nelson Chu <nelson.chu@sifive.com> wrote:
> I have arranged the ifunc patches recently, and also pass elf/linux
> toolchain regressions.  Compared to the v2 patches, the second patch
> - RISC-V: Fix that IRELATIVE relocs may be inserted to the wrong place,
> fix the ifunc overwrite issue when generating the static executable.
> Otherwise, the v3 patches just fix some minor conflicts, and almost the
> same the the v2 patches.

These patches are OK.

Jim

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 2/2] RISC-V: Fix that IRELATIVE relocs may be inserted to the wrong place.
  2020-10-14  0:57   ` Jim Wilson
@ 2020-10-16  2:15     ` Nelson Chu
  0 siblings, 0 replies; 8+ messages in thread
From: Nelson Chu @ 2020-10-16  2:15 UTC (permalink / raw)
  To: Jim Wilson
  Cc: Binutils, Palmer Dabbelt, Andrew Waterman, Fangrui Song,
	Vincent Chen, Kito Cheng

fixed and committed.  Thanks.

Nelson

On Wed, Oct 14, 2020 at 8:57 AM Jim Wilson <jimw@sifive.com> wrote:
>
> On Tue, Oct 6, 2020 at 8:48 PM Nelson Chu <nelson.chu@sifive.com> wrote:
> > +  /* The index of the last unused .rel.iplt solt.  */
> > +  bfd_vma last_iplt_index;
>
> I noticed a comment typo, "solt" -> "slot".
>
> Jim

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 0/2] RISC-V: Support GNU indirect functions
  2020-10-14  0:58 ` [PATCH v3 0/2] RISC-V: Support GNU indirect functions Jim Wilson
@ 2020-10-16  2:17   ` Nelson Chu
  0 siblings, 0 replies; 8+ messages in thread
From: Nelson Chu @ 2020-10-16  2:17 UTC (permalink / raw)
  To: Jim Wilson
  Cc: Binutils, Palmer Dabbelt, Andrew Waterman, Fangrui Song,
	Vincent Chen, Kito Cheng

Committed, thanks.

Nelson.

On Wed, Oct 14, 2020 at 8:58 AM Jim Wilson <jimw@sifive.com> wrote:
>
> On Tue, Oct 6, 2020 at 8:48 PM Nelson Chu <nelson.chu@sifive.com> wrote:
> > I have arranged the ifunc patches recently, and also pass elf/linux
> > toolchain regressions.  Compared to the v2 patches, the second patch
> > - RISC-V: Fix that IRELATIVE relocs may be inserted to the wrong place,
> > fix the ifunc overwrite issue when generating the static executable.
> > Otherwise, the v3 patches just fix some minor conflicts, and almost the
> > same the the v2 patches.
>
> These patches are OK.
>
> Jim

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v3 1/2] RISC-V: Support GNU indirect functions.
  2020-10-07  3:48 ` [PATCH v3 1/2] " Nelson Chu
@ 2021-01-06  6:46   ` Fangrui Song
  0 siblings, 0 replies; 8+ messages in thread
From: Fangrui Song @ 2021-01-06  6:46 UTC (permalink / raw)
  To: Nelson Chu; +Cc: binutils, jimw, palmer, andrew, vincent.chen, kito.cheng

On Tue, Oct 6, 2020 at 8:48 PM Nelson Chu <nelson.chu@sifive.com> wrote:
>
> Generally, glibc dynamic linker should have two ways to deal with ifunc
> - one is to handle the IRELATIVE relocations for the non-preemtive ifunc
> symbols, the other is to handle the R_RISCV_32/64 and R_RISCV_JUMP_SLOT
> relocations with the STT_IFUNC preemtive symbols.  No matter which method
> is used, both of them should get the resolved ifunc symbols at runtime.
> Therefore, linker needs to generate the correct dynamic relocations for
> ifunc to make sure the the dynamic linker works well.  For now, there are
> thirteen relocations are supported for ifunc in GNU ld,
>
> * R_RISCV_CALL and R_RISCV_CALL_PLT:
> The RISC-V compiler won't generate R_RISCV_JAL directly to jump to an
> ifunc.  Besides, we disable the relaxations for the relocation referenced
> to ifunc, so just handling the R_RISCV_CALL and R_RISCV_CALL_PLT should be
> enough.  Linker should generate a .plt entry and a .got.plt entry for it,
> and also needs to insert a dynamic IRELATIVE in the .got.plt enrty, or
> insert a R_RISCV_JUMP_SLOT when generating shared library.
>
> * 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.  The current linker will deal with them in
> the same way as R_RISCV_CALL_PLT.
>
> * R_RISCV_GOT_HI20 and R_RISCV_PCREL_LO12_I/S:
> LA pattern with global PIC ifunc symbol.  Linker should insert a dynamic
> IRELATIVE in the .got entry, or insert a R_RISCV_32/64 when generating
> shared library.
>
> * R_RISCV_32 and R_RISCV_64:
> Store the ifunc symbol into the data section.  Linker should insert a
> dynamic IRELATIVE in the data section, or insert a R_RISCV_32/64 when
> generating shared library.
>
> * R_RISCV_HI20 and R_RISCV_LO12_I/S:
> The LUI + ADDI/LW/SW patterns.  The absolute access relocation.  The
> medlow model without the -fPIC compiler option should generate them.
> The ld ifunc testsuites "Build pr23169a" and "Build pr23169d" need the
> relocations, they are in the ld/testsuite/ld-ifunc/, and need compiler
> support.
>
> However, we also made some optimizations with reference to x86,
>
> * If GOT and PLT relocations refer to the same ifunc symbol when generating
> pie, then they can actually share a .got entry without creating two entries
> to store the same value and relocation.
>
> * If GOT, PLT and DATA relocations refer to the same ifunc symbol when
> generating position dependency executable, then linker will fill the address
> of .plt entry into the corresponding .got entry and data section, without
> insert any dynamic relocations for the GOT and DATA relocations.
>
> For the ifunc testcases, there are three types of them,
>
> 1. ifunc-reloc-*: Only check the single type of relocation refers to
> ifunc symbol.
> * ifunc-reloc-call: R_RISCV_CALL and R_RISCV_CALL_PLT.
> * ifunc-reloc-data: R_RISCV_32 and R_RISCV_64.
> * ifunc-reloc-got: R_RISCV_GOT_HI20 and R_RISCV_PCREL_LO_I/S.
> * ifunc-reloc-pcrel: R_RISCV_PCREL_HI20 and R_RISCV_PCREL_LO_I/S.
>
> 2. ifunc-[nonplt|plt]-*: If we don't have PLT relocs, then don't need to
> create the PLT and it's .plt entries.
> * ifunc-nonplt: Combine R_RISCV_GOT_HI20 and R_RISCV_32/64.
> * ifunc-plt: Combine all ifunc relocations.
>
> 3. ifunc-seperate-*: If we link the ifunc caller and resolver into the
> same module (link the objects), then the results are the same as the
> ifunc-reloc-* and ifunc-[noplt|plt]-* testcases.  Consider the cases that
> the ifunc callers and resolver are in the different modules, that is, we
> compile the ifunc resolver to the shared library first, and then link it
> with the ifunc callers.  The output of ifunc callers should be the same as
> the normal STT_FUNC cases, and the shared ifunc resolver should define the
> symbols as STT_IFUNC.
>
> The R_RISCV_PCREL_HI20 reloc is special.  It should be linked and resolved
> locally, so if the ifunc resolver is defined in other modules (other shared
> libraries), then the R_RISCV_PCREL_HI20 is unresolvable, and linker should
> issue an unresolvable reloc error.
>
>         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
>         for 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 relocations
>         reference to ifunc symbols.
>         (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 tests for RISC-V.
>         * testsuite/ld-riscv-elf/ld-riscv-elf.exp (run_dump_test_ifunc):
>         New dump test for ifunc.  There are two arguments, 'target` and
>         `output`.  The `target` is rv32 or rv64, and the `output` is used
>         to choose which output you want to test (exe, pie or .so).
>         * testsuite/ld-riscv-elf/ifunc-reloc-call-01.s: New testcase.
>         * testsuite/ld-riscv-elf/ifunc-reloc-call-01.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-call-01-pic.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-call-01-pie.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-call-02.s: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-call-02.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-call-02-pic.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-call-02-pie.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-data.s: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-data.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-got.s: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-got.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-nonplt.s: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-nonplt.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-nonplt-exe.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-plt-01.s: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-plt-01.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-plt-02.s: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-plt-02.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-seperate-resolver.s: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-seperate-caller.s: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-seperate-exe.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-seperate-pic.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-seperate-pie.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d: Likewise.
>         * testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d: Likewise.
> ---
>  bfd/configure                                      |   4 +-
>  bfd/configure.ac                                   |   4 +-
>  bfd/elfnn-riscv.c                                  | 725 +++++++++++++++++++--
>  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-nonplt-exe.rd      |   4 +
>  ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd      |   7 +
>  ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd      |   7 +
>  ld/testsuite/ld-riscv-elf/ifunc-nonplt.d           |  11 +
>  ld/testsuite/ld-riscv-elf/ifunc-nonplt.s           |  39 ++
>  ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd      |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd      |   7 +
>  ld/testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd      |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-plt-01.d           |  19 +
>  ld/testsuite/ld-riscv-elf/ifunc-plt-01.s           |  31 +
>  ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd      |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd      |  11 +
>  ld/testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd      |   7 +
>  ld/testsuite/ld-riscv-elf/ifunc-plt-02.d           |  21 +
>  ld/testsuite/ld-riscv-elf/ifunc-plt-02.s           |  46 ++
>  .../ld-riscv-elf/ifunc-reloc-call-01-exe.rd        |   3 +
>  .../ld-riscv-elf/ifunc-reloc-call-01-pic.rd        |   3 +
>  .../ld-riscv-elf/ifunc-reloc-call-01-pie.rd        |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.d    |  13 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s    |  17 +
>  .../ld-riscv-elf/ifunc-reloc-call-02-exe.rd        |   3 +
>  .../ld-riscv-elf/ifunc-reloc-call-02-pic.rd        |   3 +
>  .../ld-riscv-elf/ifunc-reloc-call-02-pie.rd        |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.d    |  15 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s    |  18 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd  |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd  |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd  |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-data.d       |   9 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s       |  31 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd   |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd   |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd   |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-got.d        |   9 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s        |  23 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd |   3 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d      |  15 +
>  ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s      |  26 +
>  .../ld-riscv-elf/ifunc-seperate-caller-nonplt.s    |  23 +
>  .../ld-riscv-elf/ifunc-seperate-caller-pcrel.s     |  14 +
>  .../ld-riscv-elf/ifunc-seperate-caller-plt.s       |  26 +
>  .../ld-riscv-elf/ifunc-seperate-nonplt-exe.d       |  14 +
>  .../ld-riscv-elf/ifunc-seperate-nonplt-pic.d       |  13 +
>  .../ld-riscv-elf/ifunc-seperate-nonplt-pie.d       |  14 +
>  .../ld-riscv-elf/ifunc-seperate-pcrel-pic.d        |   5 +
>  .../ld-riscv-elf/ifunc-seperate-pcrel-pie.d        |   5 +
>  ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d |  14 +
>  ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d |  17 +
>  ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d |  18 +
>  .../ld-riscv-elf/ifunc-seperate-resolver.s         |  11 +
>  ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp         | 111 ++++
>  60 files changed, 1426 insertions(+), 53 deletions(-)
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-nonplt-exe.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-nonplt.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-nonplt.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-01.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-01.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-02.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-plt-02.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pic.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pie.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pic.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pie.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-data.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-got.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-nonplt.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-plt.s
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-exe.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pic.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pie.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d
>  create mode 100644 ld/testsuite/ld-riscv-elf/ifunc-seperate-resolver.s
>
> diff --git a/bfd/configure b/bfd/configure
> index 636f338..5d84aed 100755
> --- a/bfd/configure
> +++ b/bfd/configure
> @@ -14917,8 +14917,8 @@ do
>      powerpc_elf64_fbsd_le_vec)  tb="$tb elf64-ppc.lo elf64-gen.lo elf64.lo $elf" target_size=64 ;;
>      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 cecb0fb..5ec4d4f 100644
> --- a/bfd/configure.ac
> +++ b/bfd/configure.ac
> @@ -623,8 +623,8 @@ do
>      powerpc_elf64_fbsd_le_vec)  tb="$tb elf64-ppc.lo elf64-gen.lo elf64.lo $elf" target_size=64 ;;
>      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 c088278..a26cd3f 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)
> @@ -115,6 +116,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;
>  };
>
>
> @@ -153,17 +158,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
> @@ -265,6 +266,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 *
> @@ -286,6 +367,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;
>  }
>
> @@ -477,6 +572,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"),
> @@ -526,7 +624,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->elf.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];
> @@ -535,6 +658,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:
> @@ -574,12 +723,26 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>           h->plt.refcount += 1;
>           break;
>
> +       case R_RISCV_PCREL_HI20:
> +         if (h != NULL
> +             && h->type == STT_GNU_IFUNC)
> +           {
> +             h->non_got_ref = 1;
> +             h->pointer_equality_needed = 1;
> +
> +             /* We don't use the PCREL_HI20 in the data section,
> +                so we always need the plt when it refers to
> +                ifunc symbol.  */
> +             h->plt.refcount += 1;
> +           }
> +         /* Fall through.  */
> +
>         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.  */
> +         /* In shared libraries and pie, these relocs are known
> +            to bind locally.  */
>           if (bfd_link_pic (info))
>             break;
>           goto static_reloc;
> @@ -604,15 +767,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_pic (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,21 +806,28 @@ 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 (R_RISCV_32/R_RISCV_64).  */
>           reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type);
>
>           if ((bfd_link_pic (info)
>                && (sec->flags & SEC_ALLOC) != 0
> -              && ((r != NULL && ! r->pc_relative)
> +              && ((r != NULL && !r->pc_relative)
>                    || (h != NULL
> -                      && (! info->symbolic
> +                      && (!info->symbolic
>                            || h->root.type == bfd_link_hash_defweak
>                            || !h->def_regular))))
>               || (!bfd_link_pic (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 +964,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 +1080,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 +1273,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 +1412,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 +1455,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 +1889,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 +1907,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 +1932,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 +1973,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;
> +
> +                   riscv_elf_append_rela (output_bfd, sreloc, &outrel);
> +
> +                   /* If this reloc is against an external symbol, we
> +                      do not want to fiddle with the addend.  Otherwise,
> +                      we need to include the symbol value so that it
> +                      becomes an addend for the dynamic reloc.  For an
> +                      internal symbol, we have updated addend.  */
> +                   continue;
> +                 }
> +               goto do_relocation;
> +
> +             case R_RISCV_GOT_HI20:
> +               base_got = htab->elf.sgot;
> +               off = h->got.offset;
> +
> +               if (base_got == NULL)
> +                 abort ();
> +
> +               if (off == (bfd_vma) -1)
> +                 {
> +                   bfd_vma plt_idx;
> +
> +                   /* We can't use h->got.offset here to save state, or
> +                      even just remember the offset, as finish_dynamic_symbol
> +                      would use that as offset into .got.  */
> +
> +                   if (htab->elf.splt != NULL)
> +                     {
> +                       plt_idx = (h->plt.offset - PLT_HEADER_SIZE)
> +                                 / PLT_ENTRY_SIZE;
> +                       off = GOTPLT_HEADER_SIZE + (plt_idx * GOT_ENTRY_SIZE);
> +                       base_got = htab->elf.sgotplt;
> +                     }
> +                   else
> +                     {
> +                       plt_idx = h->plt.offset / PLT_ENTRY_SIZE;
> +                       off = plt_idx * GOT_ENTRY_SIZE;
> +                       base_got = htab->elf.igotplt;
> +                     }
> +
> +                   if (h->dynindx == -1
> +                       || h->forced_local
> +                       || info->symbolic)
> +                     {
> +                       /* This references the local definition.  We must
> +                          initialize this entry in the global offset table.
> +                          Since the offset must always be a multiple of 8,
> +                          we use the least significant bit to record
> +                          whether we have initialized it already.
> +
> +                          When doing a dynamic link, we create a .rela.got
> +                          relocation entry to initialize the value.  This
> +                          is done in the finish_dynamic_symbol routine.   */
> +                       if ((off & 1) != 0)
> +                         off &= ~1;
> +                       else
> +                         {
> +                           bfd_put_NN (output_bfd, relocation,
> +                                       base_got->contents + off);
> +                           /* Note that this is harmless for the case,
> +                              as -1 | 1 still is -1.  */
> +                           h->got.offset |= 1;
> +                         }
> +                     }
> +                 }
> +
> +               relocation = base_got->output_section->vma
> +                            + base_got->output_offset + off;
> +
> +               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 +2498,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 +2528,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;
> @@ -2216,6 +2703,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);
> @@ -2299,23 +2787,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,
> @@ -2327,16 +2850,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.  */

A local definition includes -Bsymbolic. Filed
https://sourceware.org/bugzilla/show_bug.cgi?id=27153 (very minor
issue).
The symbol representation needs a preemptible bit to avoid issues in
various backends.

> +         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)
> @@ -2369,13 +2913,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;
> @@ -2423,6 +3027,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
> @@ -2549,6 +3165,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;
>  }
>
> @@ -4052,6 +4673,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
> @@ -4082,6 +4709,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 e5adea5..003df59 100644
> --- a/bfd/elfxx-riscv.c
> +++ b/bfd/elfxx-riscv.c
> @@ -854,6 +854,21 @@ static reloc_howto_type howto_table[] =
>          0,                             /* src_mask */
>          0xffffffff,                    /* 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 */
> +        0xffffffff,                    /* 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 63ab18d..9ed4bd7 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-*-*]
> @@ -736,7 +735,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-nonplt-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-exe.rd
> new file mode 100644
> index 0000000..0de47a4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-exe.rd
> @@ -0,0 +1,4 @@
> +Relocation section '.rela.plt' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_IRELATIVE[    ]+[0-9a-f]*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_IRELATIVE[    ]+[0-9a-f]*
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd
> new file mode 100644
> index 0000000..e2e7ad9
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd
> @@ -0,0 +1,7 @@
> +Relocation section '.rela.got' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+foo\(\)[      ]+foo \+ 0
> +#...
> +Relocation section '.rela.ifunc' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+foo\(\)[      ]+foo \+ 0
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd
> new file mode 100644
> index 0000000..f9fbd87
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd
> @@ -0,0 +1,7 @@
> +Relocation section '.rela.got' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_IRELATIVE[    ]+[0-9a-f]*
> +#...
> +Relocation section '.rela.ifunc' 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-nonplt.d b/ld/testsuite/ld-riscv-elf/ifunc-nonplt.d
> new file mode 100644
> index 0000000..e3517d3
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt.d
> @@ -0,0 +1,11 @@
> +#...
> +Disassembly of section .text:
> +#...
> +0+[0-9a-f]+ <foo_resolver>:
> +#...
> +0+[0-9a-f]+ <bar>:
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+(lw|ld)[      ]+.*<(_GLOBAL_OFFSET_TABLE_.*|.*)>
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+(lw|ld)[      ]+.*<(__DATA_BEGIN__|.*)>
> +#...
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-nonplt.s b/ld/testsuite/ld-riscv-elf/ifunc-nonplt.s
> new file mode 100644
> index 0000000..ce6ca69
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-nonplt.s
> @@ -0,0 +1,39 @@
> +       .text
> +
> +       .type   foo_resolver, @function
> +foo_resolver:
> +       ret
> +       .size   foo_resolver, .-foo_resolver
> +
> +       .globl  foo
> +       .type   foo, %gnu_indirect_function
> +       .set    foo, foo_resolver
> +
> +       .globl  bar
> +       .type   bar, @function
> +bar:
> +.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   x2, %pcrel_hi (foo_addr)
> +.ifdef __64_bit__
> +       ld      x2, %pcrel_lo (.L2) (x2)
> +.else
> +       lw      x2, %pcrel_lo (.L2) (x2)
> +.endif
> +       ret
> +       .size   bar, .-bar
> +
> +       .data
> +foo_addr:
> +.ifdef __64_bit__
> +       .quad   foo
> +.else
> +       .long   foo
> +.endif
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd
> @@ -0,0 +1,3 @@
> +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-plt-01-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd
> new file mode 100644
> index 0000000..6f5218b
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd
> @@ -0,0 +1,7 @@
> +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-plt-01-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd
> @@ -0,0 +1,3 @@
> +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-plt-01.d b/ld/testsuite/ld-riscv-elf/ifunc-plt-01.d
> new file mode 100644
> index 0000000..bed9fe6
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-01.d
> @@ -0,0 +1,19 @@
> +#...
> +Disassembly of section .plt:
> +#...
> +0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
> +#...
> +Disassembly of section .text:
> +#...
> +0+[0-9a-f]+ <foo_resolver>:
> +#...
> +0+[0-9a-f]+ <bar>:
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+(lw|ld)[      ]+.*<(_GLOBAL_OFFSET_TABLE_.*|__DATA_BEGIN__.*|.*)>
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+addi[         ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+jalr[         ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+jalr[         ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +#...
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-01.s b/ld/testsuite/ld-riscv-elf/ifunc-plt-01.s
> new file mode 100644
> index 0000000..65c65cd
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-01.s
> @@ -0,0 +1,31 @@
> +       .text
> +
> +       .type   foo_resolver, @function
> +foo_resolver:
> +       ret
> +       .size   foo_resolver, .-foo_resolver
> +
> +       .globl  foo
> +       .type   foo, %gnu_indirect_function
> +       .set    foo, foo_resolver
> +
> +       .globl  bar
> +       .type   bar, @function
> +bar:
> +.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   x2, %pcrel_hi (foo)
> +       addi    x2, x2, %pcrel_lo (.L2)
> +
> +       call    foo
> +       call    foo@plt
> +
> +       ret
> +       .size   bar, .-bar
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd
> @@ -0,0 +1,3 @@
> +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-plt-02-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd
> new file mode 100644
> index 0000000..3299aa4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd
> @@ -0,0 +1,11 @@
> +Relocation section '.rela.got' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+foo\(\)[      ]+foo \+ 0
> +#...
> +Relocation section '.rela.ifunc' 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-plt-02-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd
> new file mode 100644
> index 0000000..28a3c99
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd
> @@ -0,0 +1,7 @@
> +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-plt-02.d b/ld/testsuite/ld-riscv-elf/ifunc-plt-02.d
> new file mode 100644
> index 0000000..b8638b9
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02.d
> @@ -0,0 +1,21 @@
> +#...
> +Disassembly of section .plt:
> +#...
> +0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
> +#...
> +Disassembly of section .text:
> +#...
> +0+[0-9a-f]+ <foo_resolver>:
> +#...
> +0+[0-9a-f]+ <bar>:
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+(lw|ld)[      ]+.*<(_GLOBAL_OFFSET_TABLE_.*|.*)>
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+(lw|ld)[      ]+.*<(__DATA_BEGIN__.*|.*)>
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+addi[         ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+jalr[         ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+jalr[         ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +#...
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-plt-02.s b/ld/testsuite/ld-riscv-elf/ifunc-plt-02.s
> new file mode 100644
> index 0000000..c3022be
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-plt-02.s
> @@ -0,0 +1,46 @@
> +       .text
> +
> +       .type   foo_resolver, @function
> +foo_resolver:
> +       ret
> +       .size   foo_resolver, .-foo_resolver
> +
> +       .globl  foo
> +       .type   foo, %gnu_indirect_function
> +       .set    foo, foo_resolver
> +
> +       .globl  bar
> +       .type   bar, @function
> +bar:
> +.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   x2, %pcrel_hi (foo_addr)
> +.ifdef __64_bit__
> +       ld      x2, %pcrel_lo (.L2) (x2)
> +.else
> +       lw      x2, %pcrel_lo (.L2) (x2)
> +.endif
> +
> +.L3:
> +       auipc   x3, %pcrel_hi (foo)
> +       addi    x3, x3, %pcrel_lo (.L3)
> +
> +       call    foo
> +       call    foo@plt
> +       ret
> +       .size   bar, .-bar
> +
> +       .data
> +foo_addr:
> +.ifdef __64_bit__
> +       .quad   foo
> +.else
> +       .long   foo
> +.endif
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd
> @@ -0,0 +1,3 @@
> +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-reloc-call-01-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pic.rd
> new file mode 100644
> index 0000000..7bfaa2d
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pic.rd
> @@ -0,0 +1,3 @@
> +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-reloc-call-01-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pie.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pie.rd
> @@ -0,0 +1,3 @@
> +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-reloc-call-01.d b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.d
> new file mode 100644
> index 0000000..d4457c9
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.d
> @@ -0,0 +1,13 @@
> +#...
> +Disassembly of section .plt:
> +#...
> +0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
> +#...
> +Disassembly of section .text:
> +#...
> +0+[0-9a-f]+ <foo_resolver>:
> +#...
> +0+[0-9a-f]+ <bar>:
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+jalr[         ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +#...
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s
> new file mode 100644
> index 0000000..89e6326
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s
> @@ -0,0 +1,17 @@
> +       .text
> +
> +       .type   foo_resolver, @function
> +foo_resolver:
> +       ret
> +       .size   foo_resolver, .-foo_resolver
> +
> +       .globl  foo
> +       .type   foo, %gnu_indirect_function
> +       .set    foo, foo_resolver
> +
> +       .globl  bar
> +       .type   bar, @function
> +bar:
> +       call    foo
> +       ret
> +       .size   bar, .-bar
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd
> @@ -0,0 +1,3 @@
> +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-reloc-call-02-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pic.rd
> new file mode 100644
> index 0000000..7bfaa2d
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pic.rd
> @@ -0,0 +1,3 @@
> +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-reloc-call-02-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pie.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pie.rd
> @@ -0,0 +1,3 @@
> +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-reloc-call-02.d b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.d
> new file mode 100644
> index 0000000..40c0309
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.d
> @@ -0,0 +1,15 @@
> +#...
> +Disassembly of section .plt:
> +#...
> +0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
> +#...
> +Disassembly of section .text:
> +#...
> +0+[0-9a-f]+ <foo_resolver>:
> +#...
> +0+[0-9a-f]+ <bar>:
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+jalr[         ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+jalr[         ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +#...
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s
> new file mode 100644
> index 0000000..e493c47
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s
> @@ -0,0 +1,18 @@
> +       .text
> +
> +       .type   foo_resolver, @function
> +foo_resolver:
> +       ret
> +       .size   foo_resolver, .-foo_resolver
> +
> +       .globl  foo
> +       .type   foo, %gnu_indirect_function
> +       .set    foo, foo_resolver
> +
> +       .globl  bar
> +       .type   bar, @function
> +bar:
> +       call    foo@plt
> +       call    foo
> +       ret
> +       .size   bar, .-bar
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd
> @@ -0,0 +1,3 @@
> +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-reloc-data-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd
> new file mode 100644
> index 0000000..9be346b
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd
> @@ -0,0 +1,3 @@
> +Relocation section '.rela.ifunc' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+foo\(\)[      ]+foo \+ 0
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd
> new file mode 100644
> index 0000000..e14b02b
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd
> @@ -0,0 +1,3 @@
> +Relocation section '.rela.ifunc' 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-reloc-data.d b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.d
> new file mode 100644
> index 0000000..1956cc3
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.d
> @@ -0,0 +1,9 @@
> +#...
> +Disassembly of section .text:
> +#...
> +0+[0-9a-f]+ <foo_resolver>:
> +#...
> +0+[0-9a-f]+ <bar>:
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+(lw|ld)[      ]+.*<(__DATA_BEGIN__.*|.*)>
> +#...
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s
> new file mode 100644
> index 0000000..b49bda1
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s
> @@ -0,0 +1,31 @@
> +       .text
> +
> +       .type   foo_resolver, @function
> +foo_resolver:
> +       ret
> +       .size   foo_resolver, .-foo_resolver
> +
> +       .globl  foo
> +       .type   foo, %gnu_indirect_function
> +       .set    foo, foo_resolver
> +
> +       .globl  bar
> +       .type   bar, @function
> +bar:
> +.L1:
> +       auipc   x1, %pcrel_hi (foo_addr)
> +.ifdef __64_bit__
> +       ld      x1, %pcrel_lo (.L1) (x1)
> +.else
> +       lw      x1, %pcrel_lo (.L1) (x1)
> +.endif
> +       ret
> +       .size   bar, .-bar
> +
> +       .data
> +foo_addr:
> +.ifdef __64_bit__
> +       .quad   foo
> +.else
> +       .long   foo
> +.endif
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd
> @@ -0,0 +1,3 @@
> +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-reloc-got-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd
> new file mode 100644
> index 0000000..41cbc07
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd
> @@ -0,0 +1,3 @@
> +Relocation section '.rela.got' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+foo\(\)[      ]+foo \+ 0
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd
> new file mode 100644
> index 0000000..cef1a77
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd
> @@ -0,0 +1,3 @@
> +Relocation section '.rela.got' 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-reloc-got.d b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got.d
> new file mode 100644
> index 0000000..3277e8f
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got.d
> @@ -0,0 +1,9 @@
> +#...
> +Disassembly of section .text:
> +#...
> +0+[0-9a-f]+ <foo_resolver>:
> +#...
> +0+[0-9a-f]+ <bar>:
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+(lw|ld)[      ]+.*<(_GLOBAL_OFFSET_TABLE_.*|.*)>
> +#...
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s
> new file mode 100644
> index 0000000..eca16d5
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s
> @@ -0,0 +1,23 @@
> +       .text
> +
> +       .type   foo_resolver, @function
> +foo_resolver:
> +       ret
> +       .size   foo_resolver, .-foo_resolver
> +
> +       .globl  foo
> +       .type   foo, %gnu_indirect_function
> +       .set    foo, foo_resolver
> +
> +       .globl  bar
> +       .type   bar, @function
> +bar:
> +.L1:
> +       auipc   x1, %got_pcrel_hi (foo)
> +.ifdef __64_bit__
> +       ld      x1, %pcrel_lo (.L1) (x1)
> +.else
> +       lw      x1, %pcrel_lo (.L1) (x1)
> +.endif
> +       ret
> +       .size   bar, .-bar
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd
> @@ -0,0 +1,3 @@
> +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-reloc-pcrel-pic.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd
> new file mode 100644
> index 0000000..7bfaa2d
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd
> @@ -0,0 +1,3 @@
> +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-reloc-pcrel-pie.rd b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd
> new file mode 100644
> index 0000000..97461e4
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd
> @@ -0,0 +1,3 @@
> +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-reloc-pcrel.d b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d
> new file mode 100644
> index 0000000..bc947e3
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d
> @@ -0,0 +1,15 @@
> +#...
> +Disassembly of section .plt:
> +#...
> +0+[0-9a-f]+ <(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>:
> +#...
> +Disassembly of section .text:
> +#...
> +0+[0-9a-f]+ <foo_resolver>:
> +#...
> +0+[0-9a-f]+ <bar>:
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+addi[         ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +.*:[   ]+[0-9a-f]+[    ]+auipc[        ]+.*
> +.*:[   ]+[0-9a-f]+[    ]+(lw|ld)[      ]+.*<(\*ABS\*\+0x[0-9a-f]+@plt|foo@plt|.plt)>
> +#...
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s
> new file mode 100644
> index 0000000..7ea454c
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s
> @@ -0,0 +1,26 @@
> +       .text
> +
> +       .type   foo_resolver, @function
> +foo_resolver:
> +       ret
> +       .size   foo_resolver, .-foo_resolver
> +
> +       .globl  foo
> +       .type   foo, %gnu_indirect_function
> +       .set    foo, foo_resolver
> +
> +       .globl  bar
> +       .type   bar, @function
> +bar:
> +.L1:
> +       auipc   x1, %pcrel_hi (foo)
> +       addi    x1, x1, %pcrel_lo (.L1)
> +.L2:
> +       auipc   x2, %pcrel_hi (foo)
> +.ifdef __64_bit__
> +       ld      x2, %pcrel_lo (.L2) (x2)
> +.else
> +       lw      x2, %pcrel_lo (.L2) (x2)
> +.endif
> +       ret
> +       .size   bar, .-bar
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-nonplt.s b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-nonplt.s
> new file mode 100644
> index 0000000..23c7254
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-nonplt.s
> @@ -0,0 +1,23 @@
> +       .text
> +
> +       # Call the IFUNC `foo` which is defined in the other modules.
> +       .globl  foo
> +       .type   foo, %function
> +
> +       .globl  main
> +       .type   main, @function
> +main:
> +.L1:
> +       auipc   x1, %got_pcrel_hi (foo)
> +       addi    x1, x1, %pcrel_lo (.L1)
> +
> +.L2:
> +       auipc   x2, %pcrel_hi (foo_addr)
> +       addi    x2, x2, %pcrel_lo (.L2)
> +
> +       ret
> +       .size   main, .-main
> +
> +       .data
> +foo_addr:
> +       .long   foo
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s
> new file mode 100644
> index 0000000..2d29bcd
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s
> @@ -0,0 +1,14 @@
> +       .text
> +
> +       # Call the IFUNC `foo` which is defined in the other modules.
> +       .globl  foo
> +       .type   foo, %function
> +
> +       .globl  main
> +       .type   main, @function
> +main:
> +.L1:
> +       auipc   x1, %pcrel_hi (foo)
> +       addi    x1, x1, %pcrel_lo (.L1)
> +       ret
> +       .size   main, .-main
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-plt.s b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-plt.s
> new file mode 100644
> index 0000000..8aa6403
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-plt.s
> @@ -0,0 +1,26 @@
> +       .text
> +
> +       # Call the IFUNC `foo` which is defined in the other modules.
> +       .globl  foo
> +       .type   foo, %function
> +
> +       .globl  main
> +       .type   main, @function
> +main:
> +.L1:
> +       auipc   x1, %got_pcrel_hi (foo)
> +       addi    x1, x1, %pcrel_lo (.L1)
> +
> +.L2:
> +       auipc   x2, %pcrel_hi (foo_addr)
> +       addi    x2, x2, %pcrel_lo (.L2)
> +
> +       call    foo
> +       call    foo@plt
> +
> +       ret
> +       .size   main, .-main
> +
> +       .data
> +foo_addr:
> +       .long   foo
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-exe.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-exe.d
> new file mode 100644
> index 0000000..540a21b
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-exe.d
> @@ -0,0 +1,14 @@
> +#name: Link shared ifunc resolver with non-PLT caller (exe)
> +#source: ifunc-seperate-caller-nonplt.s
> +#as:
> +#ld: -z nocombreloc tmpdir/ifunc-seperate-resolver.so
> +#warning: .*
> +#readelf: -rW
> +
> +Relocation section '.rela.got' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+[0-9a-f]+[    ]+foo \+ 0
> +#...
> +Relocation section '.rela.plt' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_JUMP_SLOT[    ]+[0-9a-f]+[    ]+foo \+ 0
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pic.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pic.d
> new file mode 100644
> index 0000000..3ed1812
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pic.d
> @@ -0,0 +1,13 @@
> +#name: Link shared ifunc resolver with non-PLT caller (pic)
> +#source: ifunc-seperate-caller-nonplt.s
> +#as:
> +#ld: -z nocombreloc -shared tmpdir/ifunc-seperate-resolver.so
> +#readelf: -rW
> +
> +Relocation section '.rela.data' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+[0-9a-f]+[    ]+foo \+ 0
> +#...
> +Relocation section '.rela.got' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+[0-9a-f]+[    ]+foo \+ 0
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pie.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pie.d
> new file mode 100644
> index 0000000..c9c9eab
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pie.d
> @@ -0,0 +1,14 @@
> +#name: Link shared ifunc resolver with non-PLT caller (pie)
> +#source: ifunc-seperate-caller-nonplt.s
> +#as:
> +#ld: -z nocombreloc -pie tmpdir/ifunc-seperate-resolver.so
> +#warning: .*
> +#readelf: -rW
> +
> +Relocation section '.rela.data' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+[0-9a-f]+[    ]+foo \+ 0
> +#...
> +Relocation section '.rela.got' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+[0-9a-f]+[    ]+foo \+ 0
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d
> new file mode 100644
> index 0000000..1c11a2d
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d
> @@ -0,0 +1,5 @@
> +#name: Link shared IFUNC resolver with PCREL caller (pic)
> +#source: ifunc-seperate-caller-pcrel.s
> +#as:
> +#ld: -z nocombreloc -shared tmpdir/ifunc-seperate-resolver.so
> +#error: .*unresolvable R_RISCV_PCREL_HI20 relocation.*
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d
> new file mode 100644
> index 0000000..0d0e3cc
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d
> @@ -0,0 +1,5 @@
> +#name: Link shared IFUNC resolver with PCREL caller (pie)
> +#source: ifunc-seperate-caller-pcrel.s
> +#as:
> +#ld: -z nocombreloc -pie tmpdir/ifunc-seperate-resolver.so
> +#error: .*unresolvable R_RISCV_PCREL_HI20 relocation.*
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d
> new file mode 100644
> index 0000000..a538564
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d
> @@ -0,0 +1,14 @@
> +#name: Link shared ifunc resolver with PLT caller (exe)
> +#source: ifunc-seperate-caller-plt.s
> +#as:
> +#ld: -z nocombreloc tmpdir/ifunc-seperate-resolver.so
> +#warning: .*
> +#readelf: -rW
> +
> +Relocation section '.rela.got' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+[0-9a-f]+[    ]+foo \+ 0
> +#...
> +Relocation section '.rela.plt' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_JUMP_SLOT[    ]+[0-9a-f]+[    ]+foo \+ 0
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d
> new file mode 100644
> index 0000000..9efa244
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d
> @@ -0,0 +1,17 @@
> +#name: Link shared ifunc resolver with PLT caller (pic)
> +#source: ifunc-seperate-caller-plt.s
> +#as:
> +#ld: -z nocombreloc -shared tmpdir/ifunc-seperate-resolver.so
> +#readelf: -rW
> +
> +Relocation section '.rela.data' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+[0-9a-f]+[    ]+foo \+ 0
> +#...
> +Relocation section '.rela.got' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+[0-9a-f]+[    ]+foo \+ 0
> +#...
> +Relocation section '.rela.plt' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_JUMP_SLOT[    ]+[0-9a-f]+[    ]+foo \+ 0
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d
> new file mode 100644
> index 0000000..8349e61
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d
> @@ -0,0 +1,18 @@
> +#name: Link shared ifunc resolver with PLT caller (pie)
> +#source: ifunc-seperate-caller-plt.s
> +#as:
> +#ld: -z nocombreloc -pie tmpdir/ifunc-seperate-resolver.so
> +#warning: .*
> +#readelf: -rW
> +
> +Relocation section '.rela.data' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+[0-9a-f]+[    ]+foo \+ 0
> +#...
> +Relocation section '.rela.got' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_(32|64)[      ]+[0-9a-f]+[    ]+foo \+ 0
> +#...
> +Relocation section '.rela.plt' at .*
> +[ ]+Offset[ ]+Info[ ]+Type[ ]+.*
> +[0-9a-f]+[     ]+[0-9a-f]+[    ]+R_RISCV_JUMP_SLOT[    ]+[0-9a-f]+[    ]+foo \+ 0
> diff --git a/ld/testsuite/ld-riscv-elf/ifunc-seperate-resolver.s b/ld/testsuite/ld-riscv-elf/ifunc-seperate-resolver.s
> new file mode 100644
> index 0000000..a222847
> --- /dev/null
> +++ b/ld/testsuite/ld-riscv-elf/ifunc-seperate-resolver.s
> @@ -0,0 +1,11 @@
> +       .text
> +
> +       .type   foo_resolver, @function
> +foo_resolver:
> +       ret
> +       .size   foo_resolver, .-foo_resolver
> +
> +       # The ifunc `foo` is called by the ifunc-caller.
> +       .globl  foo
> +       .type   foo, %gnu_indirect_function
> +       .set    foo, foo_resolver
> diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
> index 2c008d4..b82e092 100644
> --- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
> +++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
> @@ -19,6 +19,47 @@
>  # MA 02110-1301, USA.
>  #
>
> +# target: rv32 or rv64.
> +# output: Which output you want?  (exe, pie, .so)
> +proc run_dump_test_ifunc { name target output} {
> +    set asflags ""
> +    set ldflags "-z nocombreloc"
> +
> +    switch -- $output {
> +       exe {
> +           set ext "exe"
> +       }
> +       pie {
> +           set ext "pie"
> +           set ldflags "$ldflags -pie"
> +       }
> +       pic {
> +           set ext "so"
> +           set ldflags "$ldflags -shared"
> +       }
> +    }
> +
> +    switch -- $target {
> +       rv32 {
> +           set asflags "$asflags -march=rv32i -mabi=ilp32"
> +           set ldflags "$ldflags -melf32lriscv"
> +       }
> +       rv64 {
> +           set asflags "$asflags -march=rv64i -mabi=lp64 -defsym __64_bit__=1"
> +           set ldflags "$ldflags -melf64lriscv"
> +       }
> +    }
> +
> +    run_ld_link_tests [list \
> +       [list "$name ($target-$output)" \
> +             "$ldflags" "" \
> +             "$asflags" \
> +             [list "$name.s"] \
> +             [concat [list "readelf -rW $name-$output.rd"] \
> +                     [list "objdump -dw $name.d"]] \
> +             "$name-$target.$ext"]]
> +}
> +
>  if [istarget "riscv*-*-*"] {
>      run_dump_test "call-relax"
>      run_dump_test "c-lui"
> @@ -88,4 +129,74 @@ if [istarget "riscv*-*-*"] {
>             {} "lib-nopic-01a.so" }
>      }
>      run_dump_test "lib-nopic-01b"
> +
> +    # IFUNC testcases.
> +    # Check IFUNC by single type relocs.
> +    run_dump_test_ifunc "ifunc-reloc-call-01" rv32 exe
> +    run_dump_test_ifunc "ifunc-reloc-call-01" rv32 pie
> +    run_dump_test_ifunc "ifunc-reloc-call-01" rv32 pic
> +    run_dump_test_ifunc "ifunc-reloc-call-02" rv32 exe
> +    run_dump_test_ifunc "ifunc-reloc-call-02" rv32 pie
> +    run_dump_test_ifunc "ifunc-reloc-call-02" rv32 pic
> +    run_dump_test_ifunc "ifunc-reloc-pcrel" rv32 exe
> +    run_dump_test_ifunc "ifunc-reloc-pcrel" rv32 pie
> +    run_dump_test_ifunc "ifunc-reloc-pcrel" rv32 pic
> +    run_dump_test_ifunc "ifunc-reloc-data" rv32 exe
> +    run_dump_test_ifunc "ifunc-reloc-data" rv32 pie
> +    run_dump_test_ifunc "ifunc-reloc-data" rv32 pic
> +    run_dump_test_ifunc "ifunc-reloc-got" rv32 exe
> +    run_dump_test_ifunc "ifunc-reloc-got" rv32 pie
> +    run_dump_test_ifunc "ifunc-reloc-got" rv32 pic
> +    run_dump_test_ifunc "ifunc-reloc-pcrel" rv64 exe
> +    run_dump_test_ifunc "ifunc-reloc-pcrel" rv64 pie
> +    run_dump_test_ifunc "ifunc-reloc-pcrel" rv64 pic
> +    run_dump_test_ifunc "ifunc-reloc-data" rv64 exe
> +    run_dump_test_ifunc "ifunc-reloc-data" rv64 pie
> +    run_dump_test_ifunc "ifunc-reloc-data" rv64 pic
> +    run_dump_test_ifunc "ifunc-reloc-got" rv64 exe
> +    run_dump_test_ifunc "ifunc-reloc-got" rv64 pie
> +    run_dump_test_ifunc "ifunc-reloc-got" rv64 pic
> +    # Check the IFUNC PLT and non-PLT relocs.
> +    run_dump_test_ifunc "ifunc-nonplt" rv32 exe
> +    run_dump_test_ifunc "ifunc-nonplt" rv32 pie
> +    run_dump_test_ifunc "ifunc-nonplt" rv32 pic
> +    run_dump_test_ifunc "ifunc-plt-01" rv32 exe
> +    run_dump_test_ifunc "ifunc-plt-01" rv32 pie
> +    run_dump_test_ifunc "ifunc-plt-01" rv32 pic
> +    run_dump_test_ifunc "ifunc-plt-02" rv32 exe
> +    run_dump_test_ifunc "ifunc-plt-02" rv32 pie
> +    run_dump_test_ifunc "ifunc-plt-02" rv32 pic
> +    run_dump_test_ifunc "ifunc-nonplt" rv64 exe
> +    run_dump_test_ifunc "ifunc-nonplt" rv64 pie
> +    run_dump_test_ifunc "ifunc-nonplt" rv64 pic
> +    run_dump_test_ifunc "ifunc-plt-01" rv64 exe
> +    run_dump_test_ifunc "ifunc-plt-01" rv64 pie
> +    run_dump_test_ifunc "ifunc-plt-01" rv64 pic
> +    run_dump_test_ifunc "ifunc-plt-02" rv64 exe
> +    run_dump_test_ifunc "ifunc-plt-02" rv64 pie
> +    run_dump_test_ifunc "ifunc-plt-02" rv64 pic
> +
> +    # Setup shared libraries.
> +    run_ld_link_tests {
> +       { "Build shared library for IFUNC non-PLT caller"
> +        "-shared" "" "" {ifunc-seperate-caller-nonplt.s}
> +        {} "ifunc-seperate-caller.so" }
> +       { "Build shared library for IFUNC PLT caller"
> +        "-shared" "" "" {ifunc-seperate-caller-plt.s}
> +        {} "ifunc-seperate-caller.so" }
> +       { "Build shared library for IFUNC resolver"
> +        "-shared" "" "" {ifunc-seperate-resolver.s}
> +        {} "ifunc-seperate-resolver.so" }
> +    }
> +    # The IFUNC resolver and caller are in the seperate modules.
> +    # If IFUNC resolver and caller are linked to the same module,
> +    # then the result are the same as the run_dump_test_ifunc.
> +    run_dump_test "ifunc-seperate-nonplt-exe"
> +    run_dump_test "ifunc-seperate-nonplt-pie"
> +    run_dump_test "ifunc-seperate-nonplt-pic"
> +    run_dump_test "ifunc-seperate-plt-exe"
> +    run_dump_test "ifunc-seperate-plt-pie"
> +    run_dump_test "ifunc-seperate-plt-pic"
> +    run_dump_test "ifunc-seperate-pcrel-pie"
> +    run_dump_test "ifunc-seperate-pcrel-pic"
>  }
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2021-01-06  6:46 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-07  3:48 [PATCH v3 0/2] RISC-V: Support GNU indirect functions Nelson Chu
2020-10-07  3:48 ` [PATCH v3 1/2] " Nelson Chu
2021-01-06  6:46   ` Fangrui Song
2020-10-07  3:48 ` [PATCH v3 2/2] RISC-V: Fix that IRELATIVE relocs may be inserted to the wrong place Nelson Chu
2020-10-14  0:57   ` Jim Wilson
2020-10-16  2:15     ` Nelson Chu
2020-10-14  0:58 ` [PATCH v3 0/2] RISC-V: Support GNU indirect functions Jim Wilson
2020-10-16  2:17   ` Nelson Chu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).