diff --git a/elf/Makefile b/elf/Makefile
index f4d22c15991..0f888887be7 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1255,6 +1255,10 @@ ifeq (yes,$(build-shared))
 tests += \
   tst-ifunc-fault-bindnow \
   tst-ifunc-fault-lazy \
+  tst-ifunc-plt \
+  tst-ifunc-plt-bindnow \
+  tst-ifunc-plt-dlopen \
+  tst-ifunc-plt-dlopen-bindnow \
   # tests
 # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
 tests-internal += \
@@ -1317,6 +1321,8 @@ modules-names += \
   ifuncmod1 \
   ifuncmod3 \
   ifuncmod6 \
+  tst-ifunc-plt-dep \
+  tst-ifunc-plt-lib \
   # modules-names
 ifeq (no,$(with-lld))
 modules-names += ifuncmod5
@@ -1796,6 +1802,7 @@ unload4mod1.so-no-z-defs = yes
 ifuncmod1.so-no-z-defs = yes
 ifuncmod5.so-no-z-defs = yes
 ifuncmod6.so-no-z-defs = yes
+tst-ifunc-plt-lib.so-no-z-defs = yes
 tst-auditmod9a.so-no-z-defs = yes
 tst-auditmod9b.so-no-z-defs = yes
 tst-nodelete-uniquemod.so-no-z-defs = yes
@@ -2429,6 +2436,19 @@ $(objpfx)tst-ifunc-fault-bindnow.out: $(objpfx)tst-ifunc-fault-bindnow \
    $(objpfx)ld.so
 	$(tst-ifunc-fault-script)
 
+LDFLAGS-tst-ifunc-plt-lib.so = -Wl,-z,lazy
+
+tst-ifunc-plt-bindnow-ENV = LD_BIND_NOW=1
+tst-ifunc-plt-dlopen-bindnow-ENV = LD_BIND_NOW=1
+
+$(objpfx)tst-ifunc-plt-lib.so: $(objpfx)tst-ifunc-plt-dep.so
+$(objpfx)tst-ifunc-plt: $(objpfx)tst-ifunc-plt-lib.so
+$(objpfx)tst-ifunc-plt-bindnow: $(objpfx)tst-ifunc-plt-lib.so
+$(objpfx)tst-ifunc-plt-dlopen.out: \
+  $(objpfx)tst-ifunc-plt-lib.so $(objpfx)tst-ifunc-plt-dep.so
+$(objpfx)tst-ifunc-plt-dlopen-bindnow.out: \
+  $(objpfx)tst-ifunc-plt-lib.so $(objpfx)tst-ifunc-plt-dep.so
+
 $(objpfx)tst-unique1.out: $(objpfx)tst-unique1mod1.so \
 			  $(objpfx)tst-unique1mod2.so
 
diff --git a/elf/do-rel.h b/elf/do-rel.h
index d00ffab7e96..c7a8be368f0 100644
--- a/elf/do-rel.h
+++ b/elf/do-rel.h
@@ -23,6 +23,9 @@
 
 #ifdef DO_RELA
 # define elf_dynamic_do_Rel		elf_dynamic_do_Rela
+# define elf_dynamic_do_Rel_irelative	elf_dynamic_do_Rela_irelative
+# define elf_dynamic_is_Rel_irelative	elf_dynamic_is_Rela_irelative
+# define elf_dynamic_Rel_audit_symbind	elf_dynamic_Rela_audit_symbind
 # define Rel				Rela
 # define elf_machine_rel		elf_machine_rela
 # define elf_machine_rel_relative	elf_machine_rela_relative
@@ -34,16 +37,53 @@
 			    (void *) (l_addr + relative->r_offset))
 #endif
 
+static __always_inline bool
+elf_dynamic_is_Rel_irelative (const ElfW(Rel) *reloc, const ElfW(Sym) *sym)
+{
+#ifdef ELF_MACHINE_IRELATIVE
+  const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
+  return ((sym != NULL
+	   && ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC
+	   && sym->st_shndx != SHN_UNDEF)
+	  || r_type == ELF_MACHINE_IRELATIVE);
+#else
+  return false;
+#endif
+}
+
+static __always_inline void
+elf_dynamic_Rel_audit_symbind (struct link_map *map,
+			       struct r_scope_elem *scope[],
+			       const ElfW(Rel) *reloc, const ElfW(Sym) *sym,
+			       const struct r_found_version *rversion,
+			       void *r_addr_arg)
+{
+#if defined SHARED
+  if (ELFW(R_TYPE) (reloc->r_info) == ELF_MACHINE_JMP_SLOT
+      && GLRO(dl_naudit) > 0)
+    {
+      struct link_map *sym_map
+	= RESOLVE_MAP (map, scope, &sym, rversion, ELF_MACHINE_JMP_SLOT);
+      if (sym != NULL)
+	_dl_audit_symbind (map, NULL, reloc, sym, r_addr_arg, sym_map, false);
+    }
+#endif
+}
+
 /* Perform the relocations in MAP on the running program image as specified
    by RELTAG, SZTAG.  If LAZY is nonzero, this is the first pass on PLT
    relocations; they should be set up to call _dl_runtime_resolve, rather
-   than fully resolved now.  */
+   than fully resolved now.
+
+   IRELATIVE entries are always skipped (non-bootstrap); they are handled
+   separately by elf_dynamic_do_Rel_irelative after all other relocations
+   for both .rel.dyn and .rel.plt have been processed.  */
 
 static inline void __attribute__ ((always_inline))
 elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 		    ElfW(Addr) reladdr, ElfW(Addr) relsize,
 		    __typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative,
-		    int lazy, int skip_ifunc)
+		    int lazy)
 {
   const ElfW(Rel) *relative = (const void *) reladdr;
   const ElfW(Rel) *r = relative + nrelative;
@@ -65,14 +105,9 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
       void *const r_addr_arg = (void *) (l_addr + r->r_offset);
       const struct r_found_version *rversion = &map->l_versions[ndx];
 
-      elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, skip_ifunc);
+      elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, 0);
     }
 #else /* !RTLD_BOOTSTRAP */
-# if defined ELF_MACHINE_IRELATIVE
-  const ElfW(Rel) *r2 = NULL;
-  const ElfW(Rel) *end2 = NULL;
-# endif
-
 #if !defined DO_RELA || !defined ELF_MACHINE_PLT_REL
   /* We never bind lazily during ld.so bootstrap.  Unfortunately gcc is
      not clever enough to see through all the function calls to realize
@@ -81,23 +116,12 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
     {
       /* Doing lazy PLT relocations; they need very little info.  */
       for (; r < end; ++r)
-# ifdef ELF_MACHINE_IRELATIVE
-	if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
-	  {
-	    if (r2 == NULL)
-	      r2 = r;
-	    end2 = r;
-	  }
-	else
-# endif
-	  elf_machine_lazy_rel (map, scope, l_addr, r, skip_ifunc);
-
-# ifdef ELF_MACHINE_IRELATIVE
-      if (r2 != NULL)
-	for (; r2 <= end2; ++r2)
-	  if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
-	    elf_machine_lazy_rel (map, scope, l_addr, r2, skip_ifunc);
-# endif
+	{
+	  const ElfW (Sym) *sym = &symtab[ELFW (R_SYM) (r->r_info)];
+	  if (elf_dynamic_is_Rel_irelative (r, sym))
+	    continue;
+	  elf_machine_lazy_rel (map, scope, l_addr, r, 0);
+	}
     }
   else
 #endif
@@ -125,46 +149,13 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 	      const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
 	      void *const r_addr_arg = (void *) (l_addr + r->r_offset);
 	      const struct r_found_version *rversion = &map->l_versions[ndx];
-#if defined ELF_MACHINE_IRELATIVE
-	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
-		{
-		  if (r2 == NULL)
-		    r2 = r;
-		  end2 = r;
-		  continue;
-		}
-#endif
 
-	      elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg,
-			       skip_ifunc);
-#if defined SHARED
-	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
-		{
-		  struct link_map *sym_map
-		    = RESOLVE_MAP (map, scope, &sym, rversion,
-				   ELF_MACHINE_JMP_SLOT);
-		  if (sym != NULL)
-		    _dl_audit_symbind (map, NULL, r, sym, r_addr_arg, sym_map,
-				       false);
-		}
-#endif
+	      if (elf_dynamic_is_Rel_irelative (r, sym))
+		continue;
+	      elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, 0);
+	      elf_dynamic_Rel_audit_symbind (map, scope, r, sym, rversion,
+					     r_addr_arg);
 	    }
-
-#if defined ELF_MACHINE_IRELATIVE
-	  if (r2 != NULL)
-	    for (; r2 <= end2; ++r2)
-	      if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
-		{
-		  ElfW(Half) ndx
-		    = version[ELFW(R_SYM) (r2->r_info)] & 0x7fff;
-		  elf_machine_rel (map, scope, r2,
-				   &symtab[ELFW(R_SYM) (r2->r_info)],
-				   &map->l_versions[ndx],
-				   (void *) (l_addr + r2->r_offset),
-				   skip_ifunc);
-		}
-#endif
 	}
       else
 	{
@@ -172,46 +163,97 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 	    {
 	      const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
 	      void *const r_addr_arg = (void *) (l_addr + r->r_offset);
-# ifdef ELF_MACHINE_IRELATIVE
-	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
-		{
-		  if (r2 == NULL)
-		    r2 = r;
-		  end2 = r;
-		  continue;
-		}
-# endif
-	      elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg,
-			       skip_ifunc);
-# if defined SHARED
-	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
-		{
-		  struct link_map *sym_map
-		    = RESOLVE_MAP (map, scope, &sym,
-				   (struct r_found_version *) NULL,
-				   ELF_MACHINE_JMP_SLOT);
-		  if (sym != NULL)
-		    _dl_audit_symbind (map, NULL, r, sym,r_addr_arg, sym_map,
-				       false);
-		}
-# endif
-	    }
 
-# ifdef ELF_MACHINE_IRELATIVE
-	  if (r2 != NULL)
-	    for (; r2 <= end2; ++r2)
-	      if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
-		elf_machine_rel (map, scope, r2, &symtab[ELFW(R_SYM) (r2->r_info)],
-				 NULL, (void *) (l_addr + r2->r_offset),
-				 skip_ifunc);
-# endif
+	      if (elf_dynamic_is_Rel_irelative (r, sym))
+		continue;
+	      elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg, 0);
+	      elf_dynamic_Rel_audit_symbind (map, scope, r, sym, NULL,
+					     r_addr_arg);
+	    }
 	}
     }
 #endif /* !RTLD_BOOTSTRAP */
 }
 
+/* Process only IRELATIVE entries (and other relocations targeting a defined
+   STT_GNU_IFUNC symbol) in the relocation range [reladdr, reladdr+relsize).
+   The first NRELATIVE entries are R_*_RELATIVE and are skipped without
+   inspection.  When lazy is non-zero the PLT lazy-binding path
+   (elf_machine_lazy_rel) is used, otherwise the full non-lazy path
+   (elf_machine_rel) is used.
+
+   Called by _ELF_DYNAMIC_DO_RELOC after all non-IRELATIVE relocations have
+   been processed for both .rela.dyn and .rela.plt, so that IRELATIVE
+   resolvers may call PLT stubs safely regardless of which section the linker
+   placed R_*_IRELATIVE in.  */
+static __always_inline void
+elf_dynamic_do_Rel_irelative (struct link_map *map,
+			      struct r_scope_elem *scope[],
+			      ElfW(Addr) reladdr, ElfW(Addr) relsize,
+			      __typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative,
+			      int lazy, int skip_ifunc)
+{
+# ifdef ELF_MACHINE_IRELATIVE
+  const ElfW(Rel) *r = (const ElfW(Rel) *) reladdr + nrelative;
+  const ElfW(Rel) *end = (const void *) (reladdr + relsize);
+  ElfW(Addr) l_addr = map->l_addr;
+  const ElfW(Sym) *const symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+
+  if (lazy)
+    {
+      for (; r < end; ++r)
+	{
+	  const ElfW (Sym) *sym = &symtab[ELFW (R_SYM) (r->r_info)];
+	  if (!elf_dynamic_is_Rel_irelative (r, sym))
+	    continue;
+	  elf_machine_lazy_rel (map, scope, l_addr, r, skip_ifunc);
+	}
+    }
+  else
+    {
+      if (map->l_info[VERSYMIDX (DT_VERSYM)])
+	{
+	  const ElfW(Half) *const version =
+	    (const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
+
+	  for (; r < end; ++r)
+	    {
+	      const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
+	      void *const r_addr_arg = (void *) (l_addr + r->r_offset);
+	      if (!elf_dynamic_is_Rel_irelative (r, sym))
+		continue;
+
+	      ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
+	      const struct r_found_version *rversion = &map->l_versions[ndx];
+	      elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg,
+			       skip_ifunc);
+	      elf_dynamic_Rel_audit_symbind (map, scope, r, sym, rversion,
+					     r_addr_arg);
+	    }
+	}
+      else
+	{
+	  for (; r < end; ++r)
+	    {
+	      const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
+	      void *const r_addr_arg = (void *) (l_addr + r->r_offset);
+	      if (!elf_dynamic_is_Rel_irelative (r, sym))
+		continue;
+
+	      elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg,
+			       skip_ifunc);
+	      elf_dynamic_Rel_audit_symbind (map, scope, r, sym, NULL,
+					     r_addr_arg);
+	    }
+	}
+    }
+# endif
+}
+
 #undef elf_dynamic_do_Rel
+#undef elf_dynamic_do_Rel_irelative
+#undef elf_dynamic_is_Rel_irelative
+#undef elf_dynamic_Rel_audit_symbind
 #undef Rel
 #undef elf_machine_rel
 #undef elf_machine_rel_relative
diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h
index a46f36b8d45..334e241f63c 100644
--- a/elf/dynamic-link.h
+++ b/elf/dynamic-link.h
@@ -78,7 +78,8 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
    consumes precisely the very end of the DT_REL*, or DT_JMPREL and DT_REL*
    are completely separate and there is a gap between them.  */
 
-# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, scope, do_lazy, skip_ifunc, test_rel) \
+# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, scope, do_lazy, skip_ifunc, \
+			       test_rel)				      \
   do {									      \
     struct { ElfW(Addr) start, size;					      \
 	     __typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative; int lazy; }  \
@@ -118,13 +119,34 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
 	  }								      \
       }									      \
 									      \
-      for (int ranges_index = 0; ranges_index < 2; ++ranges_index)	      \
-        elf_dynamic_do_##reloc ((map), scope,				      \
-				ranges[ranges_index].start,		      \
-				ranges[ranges_index].size,		      \
-				ranges[ranges_index].nrelative,		      \
-				ranges[ranges_index].lazy,		      \
-				skip_ifunc);				      \
+      /* Defer all IRELATIVE relocations until after all non-IRELATIVE	      \
+	 relocations (including PLT lazy-binding setup) have been processed   \
+	 for both sections.  This ensures IRELATIVE resolvers can call PLT    \
+	 stubs safely regardless of which section R_*_IRELATIVE was placed in \
+	 by the linker.  */						      \
+      if (!DO_RTLD_BOOTSTRAP)						      \
+	{								      \
+	  for (int ranges_index = 0; ranges_index < 2; ++ranges_index)	      \
+	    elf_dynamic_do_##reloc ((map), scope,			      \
+				    ranges[ranges_index].start,		      \
+				    ranges[ranges_index].size,		      \
+				    ranges[ranges_index].nrelative,	      \
+				    ranges[ranges_index].lazy);		      \
+	  for (int ranges_index = 0; ranges_index < 2; ++ranges_index)	      \
+	    elf_dynamic_do_##reloc##_irelative ((map), scope,		      \
+						ranges[ranges_index].start,   \
+						ranges[ranges_index].size,    \
+						ranges[ranges_index].nrelative,\
+						ranges[ranges_index].lazy,    \
+						skip_ifunc);		      \
+	}								      \
+      else								      \
+	for (int ranges_index = 0; ranges_index < 2; ++ranges_index)	      \
+	  elf_dynamic_do_##reloc ((map), scope,				      \
+				  ranges[ranges_index].start,		      \
+				  ranges[ranges_index].size,		      \
+				  ranges[ranges_index].nrelative,	      \
+				  ranges[ranges_index].lazy);		      \
   } while (0)
 
 # if ELF_MACHINE_NO_REL || ELF_MACHINE_NO_RELA
diff --git a/elf/tst-ifunc-plt-bindnow.c b/elf/tst-ifunc-plt-bindnow.c
new file mode 100644
index 00000000000..3c5465ecd79
--- /dev/null
+++ b/elf/tst-ifunc-plt-bindnow.c
@@ -0,0 +1,24 @@
+/* Test that IRELATIVE resolvers may call PLT functions with LD_BIND_NOW=1.
+   Copyright (C) 2019-2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* Same as tst-ifunc-plt, but the test is run with LD_BIND_NOW=1 so the
+   library is processed eagerly.  Under eager binding the JMP_SLOT entries
+   in .rela.plt are resolved by elf_machine_rel during relocation; this
+   exercises the eager path of the deferred IRELATIVE processing.  */
+
+#include "tst-ifunc-plt.c"
diff --git a/elf/tst-ifunc-plt-dep.c b/elf/tst-ifunc-plt-dep.c
new file mode 100644
index 00000000000..9cf2b1b0b24
--- /dev/null
+++ b/elf/tst-ifunc-plt-dep.c
@@ -0,0 +1,23 @@
+/* Dependency library for tst-ifunc-plt.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+int
+get_value (void)
+{
+  return 42;
+}
diff --git a/elf/tst-ifunc-plt-dlopen-bindnow.c b/elf/tst-ifunc-plt-dlopen-bindnow.c
new file mode 100644
index 00000000000..50c4f65a09d
--- /dev/null
+++ b/elf/tst-ifunc-plt-dlopen-bindnow.c
@@ -0,0 +1,45 @@
+/* Test that IRELATIVE resolvers may call PLT functions via dlopen + RTLD_NOW.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+
+/* Same as tst-ifunc-plt-dlopen, but uses RTLD_NOW to force eager
+   relocation of the loaded library.  This exercises the dlopen entry
+   point along the eager-binding path.  */
+
+#include <support/xdlfcn.h>
+#include <support/check.h>
+
+typedef int (*fn_t) (void);
+
+static int
+do_test (void)
+{
+  void *handle = xdlopen ("tst-ifunc-plt-lib.so", RTLD_NOW | RTLD_LOCAL);
+
+  fn_t compute_a = (fn_t) xdlsym (handle, "compute_a");
+  TEST_COMPARE (compute_a (), 1);
+
+  fn_t compute_b = (fn_t) xdlsym (handle, "compute_b");
+  TEST_COMPARE (compute_b (), 2);
+
+  xdlclose (handle);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-ifunc-plt-dlopen.c b/elf/tst-ifunc-plt-dlopen.c
new file mode 100644
index 00000000000..ca31b8a29d9
--- /dev/null
+++ b/elf/tst-ifunc-plt-dlopen.c
@@ -0,0 +1,46 @@
+/* Test that IRELATIVE resolvers may call PLT functions on a dlopen'd library.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+
+/* dlopen tst-ifunc-plt-lib.so with RTLD_LAZY and verify that the IFUNC
+   resolver (which calls get_value() via PLT) ran successfully.  This
+   exercises the same _dl_relocate_object code path as startup loading
+   but via the dlopen entry point.  */
+
+#include <support/xdlfcn.h>
+#include <support/check.h>
+
+typedef int (*fn_t) (void);
+
+static int
+do_test (void)
+{
+  void *handle = xdlopen ("tst-ifunc-plt-lib.so", RTLD_LAZY | RTLD_LOCAL);
+
+  fn_t compute_a = (fn_t) xdlsym (handle, "compute_a");
+  TEST_COMPARE (compute_a (), 1);
+
+  fn_t compute_b = (fn_t) xdlsym (handle, "compute_b");
+  TEST_COMPARE (compute_b (), 2);
+
+  xdlclose (handle);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-ifunc-plt-lib.c b/elf/tst-ifunc-plt-lib.c
new file mode 100644
index 00000000000..a9c100a8941
--- /dev/null
+++ b/elf/tst-ifunc-plt-lib.c
@@ -0,0 +1,56 @@
+/* Shared library for tst-ifunc-plt.
+   Two static IFUNCs whose resolvers both call get_value() via PLT.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+
+/* Both resolvers call get_value() via PLT (one JUMP_SLOT entry in
+   .rel{a}.plt).  This verifies that every IRELATIVE entry is deferred
+   until after .rela.plt has been processed, not just the first one.  */
+
+#include <stddef.h>
+
+extern int get_value (void);
+
+static int
+impl_a (void)
+{
+  return 1;
+}
+
+static int
+impl_b (void)
+{
+  return 2;
+}
+
+static int (*
+resolve_a (void)) (void)
+{
+  return get_value () == 42 ? impl_a : NULL;
+}
+
+static int (*
+resolve_b (void)) (void)
+{
+  return get_value () == 42 ? impl_b : NULL;
+}
+
+/* The test is only built for $(have-ifunc), so we can assume HAVE_GCC_IFUNC
+   here.  */
+int compute_a (void) __attribute__ ((ifunc ("resolve_a")));
+int compute_b (void) __attribute__ ((ifunc ("resolve_b")));
diff --git a/elf/tst-ifunc-plt.c b/elf/tst-ifunc-plt.c
new file mode 100644
index 00000000000..78488673e74
--- /dev/null
+++ b/elf/tst-ifunc-plt.c
@@ -0,0 +1,38 @@
+/* Test that IRELATIVE resolvers may call PLT functions during startup.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+
+/* tst-ifunc-plt-multi-lib.so defines two static IFUNCs (compute_a and
+   compute_b), each producing an R_*_IRELATIVE in .rel{a}.dyn, with both
+   resolvers calling get_value() via PLT.  The test verifies that both
+   IRELATIVEs are deferred until after .rel{a}.plt is processed.  */
+
+#include <support/check.h>
+
+extern int compute_a (void);
+extern int compute_b (void);
+
+static int
+do_test (void)
+{
+  TEST_COMPARE (compute_a (), 1);
+  TEST_COMPARE (compute_b (), 2);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
index 21af8bc56e8..15651c62f3d 100644
--- a/sysdeps/aarch64/dl-machine.h
+++ b/sysdeps/aarch64/dl-machine.h
@@ -119,6 +119,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
    | (((type) == R_AARCH64_COPY) * ELF_RTYPE_CLASS_COPY))
 
 #define ELF_MACHINE_JMP_SLOT	R_AARCH64_JUMP_SLOT
+#define ELF_MACHINE_IRELATIVE	R_AARCH64_IRELATIVE
 
 #define DL_PLATFORM_INIT dl_platform_init ()
 
diff --git a/sysdeps/arm/dl-machine.h b/sysdeps/arm/dl-machine.h
index e0065ce73c8..15cced693e3 100644
--- a/sysdeps/arm/dl-machine.h
+++ b/sysdeps/arm/dl-machine.h
@@ -190,6 +190,7 @@ _dl_start_user:\n\
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT	R_ARM_JUMP_SLOT
+#define ELF_MACHINE_IRELATIVE	R_ARM_IRELATIVE
 
 /* We define an initialization functions.  This is called very early in
    _dl_sysdep_start.  */
diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
index 6657f687917..dd49079d75c 100644
--- a/sysdeps/i386/dl-machine.h
+++ b/sysdeps/i386/dl-machine.h
@@ -190,6 +190,7 @@ _dl_start_user:\n\
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT	R_386_JMP_SLOT
+#define ELF_MACHINE_IRELATIVE	R_386_IRELATIVE
 
 /* We define an initialization functions.  This is called very early in
    _dl_sysdep_start.  */
diff --git a/sysdeps/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h
index d7872986367..e07a44a5a5b 100644
--- a/sysdeps/powerpc/powerpc32/dl-machine.h
+++ b/sysdeps/powerpc/powerpc32/dl-machine.h
@@ -146,6 +146,7 @@ __elf_preferred_address(struct link_map *loader, size_t maplength,
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT	R_PPC_JMP_SLOT
+#define ELF_MACHINE_IRELATIVE	R_PPC_IRELATIVE
 
 /* We define an initialization function to initialize HWCAP/HWCAP2 and
    platform data so it can be copied into the TCB later.  This is called
diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
index 6e3771c91cc..1f5d7a0170b 100644
--- a/sysdeps/powerpc/powerpc64/dl-machine.h
+++ b/sysdeps/powerpc/powerpc64/dl-machine.h
@@ -304,6 +304,7 @@ BODY_PREFIX "_dl_start_user:\n"						\
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT	R_PPC64_JMP_SLOT
+#define ELF_MACHINE_IRELATIVE	R_PPC64_IRELATIVE
 
 /* We define an initialization function to initialize HWCAP/HWCAP2 and
    platform data so it can be copied into the TCB later.  This is called
diff --git a/sysdeps/riscv/dl-machine.h b/sysdeps/riscv/dl-machine.h
index 8c7312ad981..babb52af20a 100644
--- a/sysdeps/riscv/dl-machine.h
+++ b/sysdeps/riscv/dl-machine.h
@@ -42,6 +42,7 @@
 #endif
 
 #define ELF_MACHINE_JMP_SLOT R_RISCV_JUMP_SLOT
+#define ELF_MACHINE_IRELATIVE R_RISCV_IRELATIVE
 
 #define elf_machine_type_class(type)				\
   ((ELF_RTYPE_CLASS_PLT * ((type) == ELF_MACHINE_JMP_SLOT	\
diff --git a/sysdeps/sparc/sparc32/dl-machine.h b/sysdeps/sparc/sparc32/dl-machine.h
index 73b347bfdf1..3771339a04a 100644
--- a/sysdeps/sparc/sparc32/dl-machine.h
+++ b/sysdeps/sparc/sparc32/dl-machine.h
@@ -156,6 +156,7 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT	R_SPARC_JMP_SLOT
+#define ELF_MACHINE_IRELATIVE	R_SPARC_IRELATIVE
 
 /* Undo the sub %sp, 6*4, %sp; add %sp, 22*4, %o0 below to get at the
    value we want in __libc_stack_end.  */
diff --git a/sysdeps/sparc/sparc64/dl-machine.h b/sysdeps/sparc/sparc64/dl-machine.h
index 856922680a1..8a1a38d1701 100644
--- a/sysdeps/sparc/sparc64/dl-machine.h
+++ b/sysdeps/sparc/sparc64/dl-machine.h
@@ -119,6 +119,7 @@ elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT	R_SPARC_JMP_SLOT
+#define ELF_MACHINE_IRELATIVE	R_SPARC_IRELATIVE
 
 /* Set up the loaded object described by L so its unrelocated PLT
    entries will jump to the on-demand fixup code in dl-runtime.c.  */
