V2 [PATCH] IFUNC: Update IFUNC resolver check with DT_TEXTREL

Message ID CAMe9rOoXd+gVJGZxYbOvWAiWp4RXNHoxxpF1goDEvENGoUWYaw@mail.gmail.com
State New
Headers show
Series
  • V2 [PATCH] IFUNC: Update IFUNC resolver check with DT_TEXTREL
Related show

Commit Message

David Faust via Binutils June 9, 2020, 12:10 p.m.
On Mon, Jun 8, 2020 at 5:47 PM Alan Modra <amodra@gmail.com> wrote:
>

> On Mon, Jun 08, 2020 at 08:49:06AM -0700, H.J. Lu wrote:

> > @@ -264,17 +260,13 @@ _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,

> >        bfd_size_type count = 0;

> >        do

> >       {

> > -       if (!readonly_dynrelocs_against_ifunc)

> > -         {

> > -           asection *s = p->sec->output_section;

> > -           if (s != NULL && (s->flags & SEC_READONLY) != 0)

> > -             readonly_dynrelocs_against_ifunc = TRUE;

> > -         }

> >         count += p->count;

> >         p = p->next;

> >       }

> >        while (p != NULL);

> >

> > +      htab->ifunc_resolvers = count != 0;

> > +

> >        /* Dynamic relocations are stored in

> >        1. .rel[a].ifunc section in PIC object.

> >        2. .rel[a].got section in dynamic executable.

>

> For ppc64 I set flags when recording the dynamic relocation rather

> than when allocating space.  That allows you to distinguish three

> cases:

> 1) The dynamic ifunc relocation is in an executable and will always be

>    to an ifunc resolver in the executable.

> 2) The dynamic ifunc relocation is in a shared library which provides

>    an ifunc resolver, but that may be overridden at runtime to use a

>    resolver in another binary.

> 3) The dynamic ifunc relocation is not to a locally defined ifunc

>    resolver.

>

> Case (3) won't cause a segfault trying to run resolver code that is

> non-exec on older glibc.

>


Is there an external ifunc resolver for case (3)? Isn't it just a normal
external function which returns a function pointer?  Isn't the ifunc
resolver always local by definition?

Here is the updated patch with testcases renamed.  I changed ppc64
to use the same ifunc_resolvers field.

Any objections for master branch?

Thanks.

-- 
H.J.

Comments

David Faust via Binutils June 9, 2020, 1:43 p.m. | #1
On Tue, Jun 09, 2020 at 05:10:12AM -0700, H.J. Lu wrote:
> On Mon, Jun 8, 2020 at 5:47 PM Alan Modra <amodra@gmail.com> wrote:

> >

> > On Mon, Jun 08, 2020 at 08:49:06AM -0700, H.J. Lu wrote:

> > > @@ -264,17 +260,13 @@ _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,

> > >        bfd_size_type count = 0;

> > >        do

> > >       {

> > > -       if (!readonly_dynrelocs_against_ifunc)

> > > -         {

> > > -           asection *s = p->sec->output_section;

> > > -           if (s != NULL && (s->flags & SEC_READONLY) != 0)

> > > -             readonly_dynrelocs_against_ifunc = TRUE;

> > > -         }

> > >         count += p->count;

> > >         p = p->next;

> > >       }

> > >        while (p != NULL);

> > >

> > > +      htab->ifunc_resolvers = count != 0;

> > > +

> > >        /* Dynamic relocations are stored in

> > >        1. .rel[a].ifunc section in PIC object.

> > >        2. .rel[a].got section in dynamic executable.

> >

> > For ppc64 I set flags when recording the dynamic relocation rather

> > than when allocating space.  That allows you to distinguish three

> > cases:

> > 1) The dynamic ifunc relocation is in an executable and will always be

> >    to an ifunc resolver in the executable.

> > 2) The dynamic ifunc relocation is in a shared library which provides

> >    an ifunc resolver, but that may be overridden at runtime to use a

> >    resolver in another binary.

> > 3) The dynamic ifunc relocation is not to a locally defined ifunc

> >    resolver.

> >

> > Case (3) won't cause a segfault trying to run resolver code that is

> > non-exec on older glibc.

> >

> 

> Is there an external ifunc resolver for case (3)?


Yes.

> Isn't it just a normal

> external function which returns a function pointer?


No, it has type STT_GNU_IFUNC.

>  Isn't the ifunc

> resolver always local by definition?


I know we used to have external ifunc resolver cases in the glibc
testsuite..  External ifunc resolvers are a really bad idea though,
since the ifunc resolver itself might need dynamic relocations to be
applied before it can run.  So you need to know the order in which
shared libraries are relocated by ld.so.

> 

> Here is the updated patch with testcases renamed.  I changed ppc64

> to use the same ifunc_resolvers field.

> 

> Any objections for master branch?


No, the patch is OK.

-- 
Alan Modra
Australia Development Lab, IBM

Patch

From 3969e46406feccdfe06aa1d6a085f0653a67cfa4 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 6 Jun 2020 07:26:03 -0700
Subject: [PATCH] IFUNC: Update IFUNC resolver check with DT_TEXTREL

Add ifunc_resolvers to elf_link_hash_table and use it for both x86 and
ppc64.  Before glibc commit b5c45e837, DT_TEXTREL is incompatible with
IFUNC resolvers.  Set ifunc_resolvers if there are IFUNC resolvers and
issue a warning for IFUNC resolvers with DT_TEXTREL.

bfd/

	PR ld/18801
	* elf-bfd.h (elf_link_hash_table): Add ifunc_resolvers.
	(_bfd_elf_allocate_ifunc_dyn_relocs): Remove the
	bfd_boolean * argument.  Set ifunc_resolvers if there are IFUNC
	resolvers.
	* elf-ifunc.c (_bfd_elf_allocate_ifunc_dyn_relocs): Updated.
	Set ifunc_resolvers if there are FUNC resolvers.
	* elf64-ppc.c (ppc_link_hash_table): Remove local_ifunc_resolver.
	(build_global_entry_stubs_and_plt): Replace local_ifunc_resolver
	with elf.ifunc_resolvers.
	(write_plt_relocs_for_local_syms): Likewise.
	(ppc64_elf_relocate_section): Likewise.
	(ppc64_elf_finish_dynamic_sections): Likewise.
	* elfnn-aarch64.c (elfNN_aarch64_allocate_ifunc_dynrelocs):
	Updated.
	* elfxx-x86.c (elf_x86_allocate_dynrelocs): Likewise.
	(_bfd_x86_elf_size_dynamic_sections): Check elf.ifunc_resolvers
	instead of readonly_dynrelocs_against_ifunc.
	* elfxx-x86.h (elf_x86_link_hash_table): Remove
	readonly_dynrelocs_against_ifunc.

ld/

	PR ld/18801
	* testsuite/ld-i386/i386.exp: Run ifunc-textrel-1a,
	ifunc-textrel-1b, ifunc-textrel-2a and ifunc-textrel-2b.
	* testsuite/ld-x86-64/x86-64.exp: Likewise.
	* testsuite/ld-i386/ifunc-textrel-1a.d: Likewise.
	* testsuite/ld-i386/ifunc-textrel-1b.d: Likewise.
	* testsuite/ld-i386/ifunc-textrel-2a.d: Likewise.
	* testsuite/ld-i386/ifunc-textrel-2b.d: Likewise.
	* testsuite/ld-x86-64/ifunc-textrel-1.s: Likewise.
	* testsuite/ld-x86-64/ifunc-textrel-1a.d: Likewise.
	* testsuite/ld-x86-64/ifunc-textrel-1b.d: Likewise.
	* testsuite/ld-x86-64/ifunc-textrel-2.s: Likewise.
	* testsuite/ld-x86-64/ifunc-textrel-2a.d: Likewise.
	* testsuite/ld-x86-64/ifunc-textrel-2b.d: Likewise.
	* testsuite/ld-i386/pr18801a.d: Expect warning for IFUNC
	resolvers.
	* testsuite/ld-i386/pr18801b.d: Likewise.
	* estsuite/ld-x86-64/pr18801a.d: Likewise.
	* estsuite/ld-x86-64/pr18801b.d: Likewise.
---
 bfd/elf-bfd.h                             |  7 ++++--
 bfd/elf-ifunc.c                           | 15 ++----------
 bfd/elf64-ppc.c                           | 16 +++++--------
 bfd/elfnn-aarch64.c                       |  1 -
 bfd/elfxx-x86.c                           | 15 ++++--------
 bfd/elfxx-x86.h                           |  4 ----
 ld/testsuite/ld-i386/i386.exp             |  4 ++++
 ld/testsuite/ld-i386/ifunc-textrel-1a.d   |  4 ++++
 ld/testsuite/ld-i386/ifunc-textrel-1b.d   |  4 ++++
 ld/testsuite/ld-i386/ifunc-textrel-2a.d   |  8 +++++++
 ld/testsuite/ld-i386/ifunc-textrel-2b.d   |  8 +++++++
 ld/testsuite/ld-i386/pr18801a.d           |  2 +-
 ld/testsuite/ld-i386/pr18801b.d           |  2 +-
 ld/testsuite/ld-x86-64/ifunc-textrel-1.s  | 28 +++++++++++++++++++++++
 ld/testsuite/ld-x86-64/ifunc-textrel-1a.d |  4 ++++
 ld/testsuite/ld-x86-64/ifunc-textrel-1b.d |  4 ++++
 ld/testsuite/ld-x86-64/ifunc-textrel-2.s  | 28 +++++++++++++++++++++++
 ld/testsuite/ld-x86-64/ifunc-textrel-2a.d |  8 +++++++
 ld/testsuite/ld-x86-64/ifunc-textrel-2b.d |  8 +++++++
 ld/testsuite/ld-x86-64/pr18801a.d         |  2 +-
 ld/testsuite/ld-x86-64/pr18801b.d         |  2 +-
 ld/testsuite/ld-x86-64/x86-64.exp         |  4 ++++
 22 files changed, 134 insertions(+), 44 deletions(-)
 create mode 100644 ld/testsuite/ld-i386/ifunc-textrel-1a.d
 create mode 100644 ld/testsuite/ld-i386/ifunc-textrel-1b.d
 create mode 100644 ld/testsuite/ld-i386/ifunc-textrel-2a.d
 create mode 100644 ld/testsuite/ld-i386/ifunc-textrel-2b.d
 create mode 100644 ld/testsuite/ld-x86-64/ifunc-textrel-1.s
 create mode 100644 ld/testsuite/ld-x86-64/ifunc-textrel-1a.d
 create mode 100644 ld/testsuite/ld-x86-64/ifunc-textrel-1b.d
 create mode 100644 ld/testsuite/ld-x86-64/ifunc-textrel-2.s
 create mode 100644 ld/testsuite/ld-x86-64/ifunc-textrel-2a.d
 create mode 100644 ld/testsuite/ld-x86-64/ifunc-textrel-2b.d

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 0e31ed1c3e..242750fa58 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -570,6 +570,9 @@  struct elf_link_hash_table
      section symbols.  */
   bfd_boolean is_relocatable_executable;
 
+  /* TRUE if there are IFUNC resolvers.  */
+  bfd_boolean ifunc_resolvers;
+
   /* The BFD used to hold special sections created by the linker.
      This will be the first BFD found which requires these sections to
      be created.  */
@@ -2875,8 +2878,8 @@  extern bfd_boolean _bfd_elf_create_ifunc_sections
   (bfd *, struct bfd_link_info *);
 extern bfd_boolean _bfd_elf_allocate_ifunc_dyn_relocs
   (struct bfd_link_info *, struct elf_link_hash_entry *,
-   struct elf_dyn_relocs **, bfd_boolean *, unsigned int,
-   unsigned int, unsigned int, bfd_boolean);
+   struct elf_dyn_relocs **, unsigned int, unsigned int,
+   unsigned int, bfd_boolean);
 
 extern void elf_append_rela (bfd *, asection *, Elf_Internal_Rela *);
 extern void elf_append_rel (bfd *, asection *, Elf_Internal_Rela *);
diff --git a/bfd/elf-ifunc.c b/bfd/elf-ifunc.c
index b044164216..904950ccf4 100644
--- a/bfd/elf-ifunc.c
+++ b/bfd/elf-ifunc.c
@@ -107,7 +107,6 @@  bfd_boolean
 _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
 				    struct elf_link_hash_entry *h,
 				    struct elf_dyn_relocs **head,
-				    bfd_boolean *readonly_dynrelocs_against_ifunc_p,
 				    unsigned int plt_entry_size,
 				    unsigned int plt_header_size,
 				    unsigned int got_entry_size,
@@ -118,7 +117,6 @@  _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
   unsigned int sizeof_reloc;
   const struct elf_backend_data *bed;
   struct elf_link_hash_table *htab;
-  bfd_boolean readonly_dynrelocs_against_ifunc;
   /* If AVOID_PLT is TRUE, don't use PLT if possible.  */
   bfd_boolean use_plt = !avoid_plt || h->plt.refcount > 0;
   bfd_boolean need_dynreloc = !use_plt || bfd_link_pic (info);
@@ -255,8 +253,6 @@  _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
   if (!need_dynreloc || !h->non_got_ref)
     *head = NULL;
 
-  readonly_dynrelocs_against_ifunc = FALSE;
-
   /* Finally, allocate space.  */
   p = *head;
   if (p != NULL)
@@ -264,17 +260,13 @@  _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
       bfd_size_type count = 0;
       do
 	{
-	  if (!readonly_dynrelocs_against_ifunc)
-	    {
-	      asection *s = p->sec->output_section;
-	      if (s != NULL && (s->flags & SEC_READONLY) != 0)
-		readonly_dynrelocs_against_ifunc = TRUE;
-	    }
 	  count += p->count;
 	  p = p->next;
 	}
       while (p != NULL);
 
+      htab->ifunc_resolvers = count != 0;
+
       /* Dynamic relocations are stored in
 	 1. .rel[a].ifunc section in PIC object.
 	 2. .rel[a].got section in dynamic executable.
@@ -290,9 +282,6 @@  _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
 	}
     }
 
-  if (readonly_dynrelocs_against_ifunc_p)
-    *readonly_dynrelocs_against_ifunc_p = readonly_dynrelocs_against_ifunc;
-
   /* For STT_GNU_IFUNC symbol, .got.plt has the real function address
      and .got has the PLT entry adddress.  We will load the GOT entry
      with the PLT entry in finish_dynamic_symbol if it is used.  For
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 9868f6a755..8d710848ba 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -3239,10 +3239,6 @@  struct ppc_link_hash_table
   /* Whether func_desc_adjust needs to be run over symbols.  */
   unsigned int need_func_desc_adj:1;
 
-  /* Whether there exist local gnu indirect function resolvers,
-     referenced by dynamic relocations.  */
-  unsigned int local_ifunc_resolver:1;
-
   /* Whether plt calls for ELFv2 localentry:0 funcs have been optimized.  */
   unsigned int has_plt_localentry0:1;
 
@@ -13880,7 +13876,7 @@  build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
 	      {
 		plt = htab->elf.iplt;
 		relplt = htab->elf.irelplt;
-		htab->local_ifunc_resolver = 1;
+		htab->elf.ifunc_resolvers = TRUE;
 		if (htab->opd_abi)
 		  rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
 		else
@@ -13934,7 +13930,7 @@  build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
 		   + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab))
 		      / PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela)));
 	    if (h->type == STT_GNU_IFUNC && is_static_defined (h))
-	      htab->local_ifunc_resolver = 1;
+	      htab->elf.ifunc_resolvers = TRUE;
 	    bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc);
 	  }
       }
@@ -14076,7 +14072,7 @@  write_plt_relocs_for_local_syms (struct bfd_link_info *info)
 
 	      if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
 		{
-		  htab->local_ifunc_resolver = 1;
+		  htab->elf.ifunc_resolvers = TRUE;
 		  plt = htab->elf.iplt;
 		  relplt = htab->elf.irelplt;
 		}
@@ -16103,7 +16099,7 @@  ppc64_elf_relocate_section (bfd *output_bfd,
 		  {
 		    relgot = htab->elf.irelplt;
 		    if (indx == 0 || is_static_defined (&h->elf))
-		      htab->local_ifunc_resolver = 1;
+		      htab->elf.ifunc_resolvers = TRUE;
 		  }
 		else if (indx != 0
 			 || (bfd_link_pic (info)
@@ -16633,7 +16629,7 @@  ppc64_elf_relocate_section (bfd *output_bfd,
 		{
 		  sreloc = htab->elf.irelplt;
 		  if (indx == 0 || is_static_defined (&h->elf))
-		    htab->local_ifunc_resolver = 1;
+		    htab->elf.ifunc_resolvers = TRUE;
 		}
 	      if (sreloc == NULL)
 		abort ();
@@ -17397,7 +17393,7 @@  ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
 	      break;
 
 	    case DT_TEXTREL:
-	      if (htab->local_ifunc_resolver)
+	      if (htab->elf.ifunc_resolvers)
 		info->callbacks->einfo
 		  (_("%P: warning: text relocations and GNU indirect "
 		     "functions may result in a segfault at runtime\n"));
diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index 6857c4cc8b..eff27f6ae3 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -8818,7 +8818,6 @@  elfNN_aarch64_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
       && h->def_regular)
     return _bfd_elf_allocate_ifunc_dyn_relocs (info, h,
 					       &h->dyn_relocs,
-					       NULL,
 					       htab->plt_entry_size,
 					       htab->plt_header_size,
 					       GOT_ENTRY_SIZE,
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index f02024096e..29b020442f 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -132,7 +132,6 @@  elf_x86_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       && h->def_regular)
     {
       if (_bfd_elf_allocate_ifunc_dyn_relocs (info, h, &h->dyn_relocs,
-					      &htab->readonly_dynrelocs_against_ifunc,
 					      plt_entry_size,
 					      (htab->plt.has_plt0
 					       * plt_entry_size),
@@ -1416,15 +1415,11 @@  _bfd_x86_elf_size_dynamic_sections (bfd *output_bfd,
 
 	  if ((info->flags & DF_TEXTREL) != 0)
 	    {
-	      if (htab->readonly_dynrelocs_against_ifunc)
-		{
-		  info->callbacks->einfo
-		    (_("%P%X: read-only segment has dynamic IFUNC relocations;"
-		       " recompile with %s\n"),
-		     bfd_link_dll (info) ? "-fPIC" : "-fPIE");
-		  bfd_set_error (bfd_error_bad_value);
-		  return FALSE;
-		}
+	      if (htab->elf.ifunc_resolvers)
+		info->callbacks->einfo
+		  (_("%P: warning: GNU indirect functions with DT_TEXTREL "
+		     "may result in a segfault at runtime; recompile with %s\n"),
+		   bfd_link_dll (info) ? "-fPIC" : "-fPIE");
 
 	      if (!add_dynamic_entry (DT_TEXTREL, 0))
 		return FALSE;
diff --git a/bfd/elfxx-x86.h b/bfd/elfxx-x86.h
index dc7e6beb76..915cd4d5d0 100644
--- a/bfd/elfxx-x86.h
+++ b/bfd/elfxx-x86.h
@@ -483,10 +483,6 @@  struct elf_x86_link_hash_table
   /* The index of the next R_X86_64_IRELATIVE entry in .rela.plt.  */
   bfd_vma next_irelative_index;
 
-  /* TRUE if there are dynamic relocs against IFUNC symbols that apply
-     to read-only sections.  */
-  bfd_boolean readonly_dynrelocs_against_ifunc;
-
   /* The (unloaded but important) .rel.plt.unloaded section on VxWorks.
      This is used for i386 only.  */
   asection *srelplt2;
diff --git a/ld/testsuite/ld-i386/i386.exp b/ld/testsuite/ld-i386/i386.exp
index e1bbcddcd3..891ebce772 100644
--- a/ld/testsuite/ld-i386/i386.exp
+++ b/ld/testsuite/ld-i386/i386.exp
@@ -519,6 +519,10 @@  run_dump_test "pr17935-1"
 run_dump_test "pr17935-2"
 run_dump_test "pr18801a"
 run_dump_test "pr18801b"
+run_dump_test "ifunc-textrel-1a"
+run_dump_test "ifunc-textrel-1b"
+run_dump_test "ifunc-textrel-2a"
+run_dump_test "ifunc-textrel-2b"
 run_dump_test "pr18815"
 run_dump_test "pr19939a"
 run_dump_test "pr19939b"
diff --git a/ld/testsuite/ld-i386/ifunc-textrel-1a.d b/ld/testsuite/ld-i386/ifunc-textrel-1a.d
new file mode 100644
index 0000000000..15f545db03
--- /dev/null
+++ b/ld/testsuite/ld-i386/ifunc-textrel-1a.d
@@ -0,0 +1,4 @@ 
+#source: ../ld-x86-64/ifunc-textrel-1.s
+#as: --32
+#ld: -m elf_i386 -pie
+#warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIE
diff --git a/ld/testsuite/ld-i386/ifunc-textrel-1b.d b/ld/testsuite/ld-i386/ifunc-textrel-1b.d
new file mode 100644
index 0000000000..6e4a67c48f
--- /dev/null
+++ b/ld/testsuite/ld-i386/ifunc-textrel-1b.d
@@ -0,0 +1,4 @@ 
+#source: ../ld-x86-64/ifunc-textrel-1.s
+#as: --32
+#ld: -m elf_i386 -shared
+#warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIC
diff --git a/ld/testsuite/ld-i386/ifunc-textrel-2a.d b/ld/testsuite/ld-i386/ifunc-textrel-2a.d
new file mode 100644
index 0000000000..7195912caf
--- /dev/null
+++ b/ld/testsuite/ld-i386/ifunc-textrel-2a.d
@@ -0,0 +1,8 @@ 
+#source: ../ld-x86-64/ifunc-textrel-2.s
+#as: --32
+#ld: -m elf_i386 -pie -z notext
+#readelf: -r --wide
+
+#failif
+[0-9a-f]+ +[0-9a-f]+ +R_386_IRELATIVE +
+#..
diff --git a/ld/testsuite/ld-i386/ifunc-textrel-2b.d b/ld/testsuite/ld-i386/ifunc-textrel-2b.d
new file mode 100644
index 0000000000..7d51e6809d
--- /dev/null
+++ b/ld/testsuite/ld-i386/ifunc-textrel-2b.d
@@ -0,0 +1,8 @@ 
+#source: ../ld-x86-64/ifunc-textrel-2.s
+#as: --32
+#ld: -m elf_i386 -shared -z notext
+#readelf: -r --wide
+
+#failif
+[0-9a-f]+ +[0-9a-f]+ +R_386_IRELATIVE +
+#..
diff --git a/ld/testsuite/ld-i386/pr18801a.d b/ld/testsuite/ld-i386/pr18801a.d
index f8dc3f170b..73cb5d17be 100644
--- a/ld/testsuite/ld-i386/pr18801a.d
+++ b/ld/testsuite/ld-i386/pr18801a.d
@@ -1,4 +1,4 @@ 
 #source: pr18801.s
 #as: --32
 #ld: -m elf_i386 -pie
-#error: read-only segment has dynamic IFUNC relocations; recompile with -fPIE
+#warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIE
diff --git a/ld/testsuite/ld-i386/pr18801b.d b/ld/testsuite/ld-i386/pr18801b.d
index f1d5c8daee..0bf7fb729c 100644
--- a/ld/testsuite/ld-i386/pr18801b.d
+++ b/ld/testsuite/ld-i386/pr18801b.d
@@ -1,4 +1,4 @@ 
 #source: pr18801.s
 #as: --32
 #ld: -m elf_i386 -shared
-#error: read-only segment has dynamic IFUNC relocations; recompile with -fPIC
+#warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIC
diff --git a/ld/testsuite/ld-x86-64/ifunc-textrel-1.s b/ld/testsuite/ld-x86-64/ifunc-textrel-1.s
new file mode 100644
index 0000000000..373e15f968
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/ifunc-textrel-1.s
@@ -0,0 +1,28 @@ 
+	.text
+	.type   selector, %function
+foo:
+	movl	$0, %eax
+	ret
+selector:
+.ifdef __x86_64__
+	leaq	foo(%rip), %rax
+.else
+	leal	foo@GOTOFF(%eax), %eax
+.endif
+	ret
+	.type   selector, %gnu_indirect_function
+	.globl	_start
+_start:
+.ifdef __x86_64__
+	movabs	ptr, %rax
+	call	*%rax
+.else
+	mov	ptr, %eax
+	call	*%eax
+.endif
+	ret
+	.data
+	.type	ptr, @object
+ptr:
+	.dc.a	selector
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/ifunc-textrel-1a.d b/ld/testsuite/ld-x86-64/ifunc-textrel-1a.d
new file mode 100644
index 0000000000..64a1e7021f
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/ifunc-textrel-1a.d
@@ -0,0 +1,4 @@ 
+#source: ifunc-textrel-1.s
+#as: --64 -defsym __x86_64__=1
+#ld: -m elf_x86_64 -pie
+#warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIE
diff --git a/ld/testsuite/ld-x86-64/ifunc-textrel-1b.d b/ld/testsuite/ld-x86-64/ifunc-textrel-1b.d
new file mode 100644
index 0000000000..aeb31fdb3d
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/ifunc-textrel-1b.d
@@ -0,0 +1,4 @@ 
+#source: ifunc-textrel-1.s
+#as: --64 -defsym __x86_64__=1
+#ld: -m elf_x86_64 -shared
+#warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIC
diff --git a/ld/testsuite/ld-x86-64/ifunc-textrel-2.s b/ld/testsuite/ld-x86-64/ifunc-textrel-2.s
new file mode 100644
index 0000000000..5c8f2714b5
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/ifunc-textrel-2.s
@@ -0,0 +1,28 @@ 
+	.text
+	.type   selector, %function
+foo:
+	movl	$0, %eax
+	ret
+selector:
+.ifdef __x86_64__
+	leaq	foo(%rip), %rax
+.else
+	leal	foo@GOTOFF(%eax), %eax
+.endif
+	ret
+	.type   selector, %gnu_indirect_function
+	.globl	_start
+_start:
+.ifdef __x86_64__
+	movabs	ptr, %rax
+	call	*%rax
+.else
+	mov	ptr, %eax
+	call	*%eax
+.endif
+	ret
+	.data
+	.type	ptr, @object
+ptr:
+	.dc.a	foo
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/ifunc-textrel-2a.d b/ld/testsuite/ld-x86-64/ifunc-textrel-2a.d
new file mode 100644
index 0000000000..7bef52e25f
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/ifunc-textrel-2a.d
@@ -0,0 +1,8 @@ 
+#source: ifunc-textrel-2.s
+#as: --64 -defsym __x86_64__=1
+#ld: -m elf_x86_64 -pie -z notext
+#readelf: -r --wide
+
+#failif
+[0-9a-f]+ +[0-9a-f]+ +R_X86_64_IRELATIVE +[0-9a-f]+
+#..
diff --git a/ld/testsuite/ld-x86-64/ifunc-textrel-2b.d b/ld/testsuite/ld-x86-64/ifunc-textrel-2b.d
new file mode 100644
index 0000000000..629ecc8ac0
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/ifunc-textrel-2b.d
@@ -0,0 +1,8 @@ 
+#source: ifunc-textrel-2.s
+#as: --64 -defsym __x86_64__=1
+#ld: -m elf_x86_64 -shared -z notext
+#readelf: -r --wide
+
+#failif
+[0-9a-f]+ +[0-9a-f]+ +R_X86_64_IRELATIVE +[0-9a-f]+
+#..
diff --git a/ld/testsuite/ld-x86-64/pr18801a.d b/ld/testsuite/ld-x86-64/pr18801a.d
index b527f04250..2b4159d304 100644
--- a/ld/testsuite/ld-x86-64/pr18801a.d
+++ b/ld/testsuite/ld-x86-64/pr18801a.d
@@ -1,4 +1,4 @@ 
 #source: pr18801.s
 #as: --64
 #ld: -melf_x86_64 -pie
-#error: read-only segment has dynamic IFUNC relocations; recompile with -fPIE
+#warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIE
diff --git a/ld/testsuite/ld-x86-64/pr18801b.d b/ld/testsuite/ld-x86-64/pr18801b.d
index 7cdb2cd17d..34dab1aa6c 100644
--- a/ld/testsuite/ld-x86-64/pr18801b.d
+++ b/ld/testsuite/ld-x86-64/pr18801b.d
@@ -1,4 +1,4 @@ 
 #source: pr18801.s
 #as: --64
 #ld: -melf_x86_64 -shared
-#error: read-only segment has dynamic IFUNC relocations; recompile with -fPIC
+#warning: GNU indirect functions with DT_TEXTREL may result in a segfault at runtime; recompile with -fPIC
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index fd8fd34a77..69548f27b5 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -581,6 +581,10 @@  run_dump_test "pr18160"
 run_dump_test "pr18176"
 run_dump_test "pr18801a"
 run_dump_test "pr18801b"
+run_dump_test "ifunc-textrel-1a"
+run_dump_test "ifunc-textrel-1b"
+run_dump_test "ifunc-textrel-2a"
+run_dump_test "ifunc-textrel-2b"
 run_dump_test "pr18815"
 run_dump_test "pr19013"
 run_dump_test "pr19013-x32"
-- 
2.26.2