summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/ChangeLog36
-rwxr-xr-xbfd/configure4
-rw-r--r--bfd/configure.ac4
-rw-r--r--bfd/elfnn-riscv.c725
-rw-r--r--bfd/elfxx-riscv.c15
-rw-r--r--include/ChangeLog4
-rw-r--r--include/elf/riscv.h1
-rw-r--r--ld/ChangeLog57
-rw-r--r--ld/emulparams/elf32lriscv-defs.sh1
-rw-r--r--ld/testsuite/ld-ifunc/ifunc.exp4
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-nonplt-exe.rd4
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-nonplt-pic.rd7
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-nonplt-pie.rd7
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-nonplt.d11
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-nonplt.s39
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-plt-01-exe.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-plt-01-pic.rd7
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-plt-01-pie.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-plt-01.d19
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-plt-01.s31
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-plt-02-exe.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-plt-02-pic.rd11
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-plt-02-pie.rd7
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-plt-02.d21
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-plt-02.s46
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-exe.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pic.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01-pie.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.d13
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-call-01.s17
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-exe.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pic.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02-pie.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.d15
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-call-02.s18
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-data-exe.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pic.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-data-pie.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-data.d9
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-data.s31
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-got-exe.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pic.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-got-pie.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-got.d9
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-got.s23
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-exe.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pic.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel-pie.rd3
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.d15
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-reloc-pcrel.s26
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-nonplt.s23
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-pcrel.s14
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-caller-plt.s26
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-exe.d14
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pic.d13
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-nonplt-pie.d14
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pic.d5
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-pcrel-pie.d5
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-exe.d14
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pic.d17
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-plt-pie.d18
-rw-r--r--ld/testsuite/ld-riscv-elf/ifunc-seperate-resolver.s11
-rw-r--r--ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp111
63 files changed, 1523 insertions, 53 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index a7e0caea688..b0cd8b8ec51 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,39 @@
+2020-10-16 Nelson Chu <nelson.chu@sifive.com>
+
+ * 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.
+
2020-10-16 Alan Modra <amodra@gmail.com>
* elf32-arc.c (replace_func): Correct return type.
diff --git a/bfd/configure b/bfd/configure
index 636f338569b..5d84aed069d 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 cecb0fbb761..5ec4d4f0b48 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 c08827873cd..a26cd3f2a6f 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 e5adea57b12..003df59841e 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/ChangeLog b/include/ChangeLog
index 8fdf30f4a90..5c1145aa5d5 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,7 @@
+2020-10-16 Nelson Chu <nelson.chu@sifive.com>
+
+ * elf/riscv.h: Add R_RISCV_IRELATIVE to 58.
+
2020-10-09 H.J. Lu <hongjiu.lu@intel.com>
PR gas/26703
diff --git a/include/elf/riscv.h b/include/elf/riscv.h
index 5062a4998c7..98c7ac63a9d 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/ChangeLog b/ld/ChangeLog
index 205f6acb87c..3a9d3810984 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,60 @@
+2020-10-16 Nelson Chu <nelson.chu@sifive.com>
+
+ * 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.
+
2020-10-09 H.J. Lu <hongjiu.lu@intel.com>
PR gas/26703
diff --git a/ld/emulparams/elf32lriscv-defs.sh b/ld/emulparams/elf32lriscv-defs.sh
index bc464918e5c..b823cedacab 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 63ab18d41ac..9ed4bd7841a 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 00000000000..0de47a4009f
--- /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 00000000000..e2e7ad923a5
--- /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 00000000000..f9fbd877c58
--- /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 00000000000..e3517d3c654
--- /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 00000000000..ce6ca691fa7
--- /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 00000000000..97461e43ccc
--- /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 00000000000..6f5218b9678
--- /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 00000000000..97461e43ccc
--- /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 00000000000..bed9fe67955
--- /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 00000000000..65c65cdca57
--- /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 00000000000..97461e43ccc
--- /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 00000000000..3299aa48f24
--- /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 00000000000..28a3c992f4b
--- /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 00000000000..b8638b963fd
--- /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 00000000000..c3022be0e08
--- /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 00000000000..97461e43ccc
--- /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 00000000000..7bfaa2d266b
--- /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 00000000000..97461e43ccc
--- /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 00000000000..d4457c9be3c
--- /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 00000000000..89e63260fe2
--- /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 00000000000..97461e43ccc
--- /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 00000000000..7bfaa2d266b
--- /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 00000000000..97461e43ccc
--- /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 00000000000..40c0309ee26
--- /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 00000000000..e493c47341a
--- /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 00000000000..97461e43ccc
--- /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 00000000000..9be346bd20e
--- /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 00000000000..e14b02ba75d
--- /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 00000000000..1956cc3e101
--- /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 00000000000..b49bda1279d
--- /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 00000000000..97461e43ccc
--- /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 00000000000..41cbc0712c9
--- /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 00000000000..cef1a77c437
--- /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 00000000000..3277e8f10bd
--- /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 00000000000..eca16d52cfd
--- /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 00000000000..97461e43ccc
--- /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 00000000000..7bfaa2d266b
--- /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 00000000000..97461e43ccc
--- /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 00000000000..bc947e3cf29
--- /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 00000000000..7ea454cec58
--- /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 00000000000..23c7254ad5b
--- /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 00000000000..2d29bcd121b
--- /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 00000000000..8aa64034706
--- /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 00000000000..540a21bc0a3
--- /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 00000000000..3ed1812cbf5
--- /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 00000000000..c9c9eabaeb0
--- /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 00000000000..1c11a2d15b2
--- /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 00000000000..0d0e3cc1c82
--- /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 00000000000..a5385641868
--- /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 00000000000..9efa244f044
--- /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 00000000000..8349e61ed48
--- /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 00000000000..a2228473422
--- /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 2c008d4c35c..b82e0921afd 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"
}