diff --git a/SOURCES/SUPPORTED b/SOURCES/SUPPORTED
index a4bf79c6a6e6401b47a9711f91db0960cb295c72..fdf15fddf5178319f10d8517287b57f1b743f942 100644
--- a/SOURCES/SUPPORTED
+++ b/SOURCES/SUPPORTED
@@ -159,7 +159,8 @@ en_SG/ISO-8859-1 \
 en_US.UTF-8/UTF-8 \
 en_US/ISO-8859-1 \
 en_US.ISO-8859-15/ISO-8859-15 \
-en_US@ampm.UTF-8/UTF-8 \
+en_US@ampm/UTF-8 \
+en_US.UTF-8@ampm/UTF-8 \
 en_ZA.UTF-8/UTF-8 \
 en_ZA/ISO-8859-1 \
 en_ZM/UTF-8 \
diff --git a/SOURCES/build-locale-archive.c b/SOURCES/build-locale-archive.c
index 9183972375dbd1e4239fc52c955d1c210de60d42..3cb3b47580512be731be9497a5a23a06ce8f2bf6 100644
--- a/SOURCES/build-locale-archive.c
+++ b/SOURCES/build-locale-archive.c
@@ -448,7 +448,7 @@ fill_archive (struct locarhandle *tmpl_ah,
 		      char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
 
 #ifdef _DIRENT_HAVE_D_TYPE
-		      if (d_type == DT_UNKNOWN)
+		      if (d_type == DT_UNKNOWN || d_type == DT_LNK)
 #endif
 			{
 			  strcpy (stpcpy (stpcpy (fullname, fname), "/"),
diff --git a/SOURCES/glibc-rh1888660.patch b/SOURCES/glibc-rh1888660.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ec80b81a68154090e2842bbb8f91ce7bde9f2ba0
--- /dev/null
+++ b/SOURCES/glibc-rh1888660.patch
@@ -0,0 +1,767 @@
+This patch is a RHEL-8.7 backport of the following upstream commit:
+
+commit 52a103e237329b9f88a28513fe7506ffc3bd8ced
+Author: Arjun Shankar <arjun@redhat.com>
+Date:   Tue May 24 17:57:36 2022 +0200
+
+    Fix deadlock when pthread_atfork handler calls pthread_atfork or dlclose
+    
+    In multi-threaded programs, registering via pthread_atfork,
+    de-registering implicitly via dlclose, or running pthread_atfork
+    handlers during fork was protected by an internal lock.  This meant
+    that a pthread_atfork handler attempting to register another handler or
+    dlclose a dynamically loaded library would lead to a deadlock.
+    
+    This commit fixes the deadlock in the following way:
+    
+    During the execution of handlers at fork time, the atfork lock is
+    released prior to the execution of each handler and taken again upon its
+    return.  Any handler registrations or de-registrations that occurred
+    during the execution of the handler are accounted for before proceeding
+    with further handler execution.
+    
+    If a handler that hasn't been executed yet gets de-registered by another
+    handler during fork, it will not be executed.   If a handler gets
+    registered by another handler during fork, it will not be executed
+    during that particular fork.
+    
+    The possibility that handlers may now be registered or deregistered
+    during handler execution means that identifying the next handler to be
+    run after a given handler may register/de-register others requires some
+    bookkeeping.  The fork_handler struct has an additional field, 'id',
+    which is assigned sequentially during registration.  Thus, handlers are
+    executed in ascending order of 'id' during 'prepare', and descending
+    order of 'id' during parent/child handler execution after the fork.
+    
+    Two tests are included:
+    
+    * tst-atfork3: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+      This test exercises calling dlclose from prepare, parent, and child
+      handlers.
+    
+    * tst-atfork4: This test exercises calling pthread_atfork and dlclose
+      from the prepare handler.
+    
+    [BZ #24595, BZ #27054]
+    
+    Co-authored-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/nptl/Makefile b/nptl/Makefile
+index 70a3be23ecfcd9c9..76c914e23e8873f2 100644
+--- a/nptl/Makefile
++++ b/nptl/Makefile
+@@ -382,8 +382,17 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \
+ 	 tst-cancelx16 tst-cancelx17 tst-cancelx18 tst-cancelx20 tst-cancelx21 \
+ 	 tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 tst-cleanupx4
+ ifeq ($(build-shared),yes)
+-tests += tst-atfork2 tst-tls4 tst-_res1 tst-fini1 tst-compat-forwarder \
+-	 tst-audit-threads
++tests += \
++  tst-atfork2 \
++  tst-tls4 \
++  tst-_res1 \
++  tst-fini1 \
++  tst-compat-forwarder \
++  tst-audit-threads \
++  tst-atfork3 \
++  tst-atfork4 \
++# tests
++
+ tests-internal += tst-tls3 tst-tls3-malloc tst-tls5 tst-stackguard1
+ tests-nolibpthread += tst-fini1
+ ifeq ($(have-z-execstack),yes)
+@@ -391,18 +400,39 @@ tests += tst-execstack
+ endif
+ endif
+ 
+-modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \
+-		tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \
+-		tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \
+-		tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod \
+-		tst-join7mod tst-compat-forwarder-mod tst-audit-threads-mod1 \
+-		tst-audit-threads-mod2
++modules-names = \
++  tst-atfork2mod \
++  tst-tls3mod \
++  tst-tls4moda \
++  tst-tls4modb \
++  tst-tls5mod \
++  tst-tls5moda \
++  tst-tls5modb \
++  tst-tls5modc \
++  tst-tls5modd \
++  tst-tls5mode \
++  tst-tls5modf \
++  tst-stack4mod \
++  tst-_res1mod1 \
++  tst-_res1mod2 \
++  tst-execstack-mod \
++  tst-fini1mod \
++  tst-join7mod \
++  tst-compat-forwarder-mod \
++  tst-audit-threads-mod1 \
++  tst-audit-threads-mod2 \
++  tst-atfork3mod \
++  tst-atfork4mod \
++# module-names
++
+ extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) \
+ 		   tst-cleanup4aux.o tst-cleanupx4aux.o
+ test-extras += tst-cleanup4aux tst-cleanupx4aux
+ test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names)))
+ 
+ tst-atfork2mod.so-no-z-defs = yes
++tst-atfork3mod.so-no-z-defs = yes
++tst-atfork4mod.so-no-z-defs = yes
+ tst-tls3mod.so-no-z-defs = yes
+ tst-tls5mod.so-no-z-defs = yes
+ tst-tls5moda.so-no-z-defs = yes
+@@ -541,6 +571,14 @@ LDFLAGS-tst-atfork2 = -rdynamic
+ tst-atfork2-ENV = MALLOC_TRACE=$(objpfx)tst-atfork2.mtrace
+ $(objpfx)tst-atfork2mod.so: $(shared-thread-library)
+ 
++$(objpfx)tst-atfork3: $(libdl) $(shared-thread-library)
++LDFLAGS-tst-atfork3 = -rdynamic
++$(objpfx)tst-atfork3mod.so: $(shared-thread-library)
++
++$(objpfx)tst-atfork4: $(libdl) $(shared-thread-library)
++LDFLAGS-tst-atfork4 = -rdynamic
++$(objpfx)tst-atfork4mod.so: $(shared-thread-library)
++
+ tst-stack3-ENV = MALLOC_TRACE=$(objpfx)tst-stack3.mtrace
+ $(objpfx)tst-stack3-mem.out: $(objpfx)tst-stack3.out
+ 	$(common-objpfx)malloc/mtrace $(objpfx)tst-stack3.mtrace > $@; \
+@@ -640,6 +678,8 @@ $(objpfx)../libc.so: $(common-objpfx)libc.so ;
+ $(addprefix $(objpfx),$(tests-static) $(xtests-static)): $(objpfx)libpthread.a
+ 
+ $(objpfx)tst-atfork2.out: $(objpfx)tst-atfork2mod.so
++$(objpfx)tst-atfork3.out: $(objpfx)tst-atfork3mod.so
++$(objpfx)tst-atfork4.out: $(objpfx)tst-atfork4mod.so
+ else
+ $(addprefix $(objpfx),$(tests) $(test-srcs)): $(objpfx)libpthread.a
+ endif
+diff --git a/nptl/register-atfork.c b/nptl/register-atfork.c
+index 9edb7d4bbb49fbed..4c1e20ae8cab005f 100644
+--- a/nptl/register-atfork.c
++++ b/nptl/register-atfork.c
+@@ -21,6 +21,8 @@
+ #include <string.h>
+ #include <fork.h>
+ #include <atomic.h>
++#include <intprops.h>
++#include <stdio.h>
+ 
+ #define DYNARRAY_ELEMENT           struct fork_handler
+ #define DYNARRAY_STRUCT            fork_handler_list
+@@ -29,7 +31,7 @@
+ #include <malloc/dynarray-skeleton.c>
+ 
+ static struct fork_handler_list fork_handlers;
+-static bool fork_handler_init = false;
++static uint64_t fork_handler_counter;
+ 
+ static int atfork_lock = LLL_LOCK_INITIALIZER;
+ 
+@@ -39,11 +41,8 @@ __register_atfork (void (*prepare) (void), void (*parent) (void),
+ {
+   lll_lock (atfork_lock, LLL_PRIVATE);
+ 
+-  if (!fork_handler_init)
+-    {
+-      fork_handler_list_init (&fork_handlers);
+-      fork_handler_init = true;
+-    }
++  if (fork_handler_counter == 0)
++    fork_handler_list_init (&fork_handlers);
+ 
+   struct fork_handler *newp = fork_handler_list_emplace (&fork_handlers);
+   if (newp != NULL)
+@@ -52,6 +51,13 @@ __register_atfork (void (*prepare) (void), void (*parent) (void),
+       newp->parent_handler = parent;
+       newp->child_handler = child;
+       newp->dso_handle = dso_handle;
++
++      /* IDs assigned to handlers start at 1 and increment with handler
++         registration.  Un-registering a handlers discards the corresponding
++         ID.  It is not reused in future registrations.  */
++      if (INT_ADD_OVERFLOW (fork_handler_counter, 1))
++        __libc_fatal ("fork handler counter overflow");
++      newp->id = ++fork_handler_counter;
+     }
+ 
+   /* Release the lock.  */
+@@ -106,37 +112,111 @@ __unregister_atfork (void *dso_handle)
+   lll_unlock (atfork_lock, LLL_PRIVATE);
+ }
+ 
+-void
+-__run_fork_handlers (enum __run_fork_handler_type who, _Bool do_locking)
++uint64_t
++__run_prefork_handlers (_Bool do_locking)
+ {
+-  struct fork_handler *runp;
++  uint64_t lastrun;
+ 
+-  if (who == atfork_run_prepare)
++  if (do_locking)
++    lll_lock (atfork_lock, LLL_PRIVATE);
++
++  /* We run prepare handlers from last to first.  After fork, only
++     handlers up to the last handler found here (pre-fork) will be run.
++     Handlers registered during __run_prefork_handlers or
++     __run_postfork_handlers will be positioned after this last handler, and
++     since their prepare handlers won't be run now, their parent/child
++     handlers should also be ignored.  */
++  lastrun = fork_handler_counter;
++
++  size_t sl = fork_handler_list_size (&fork_handlers);
++  for (size_t i = sl; i > 0;)
+     {
+-      if (do_locking)
+-	lll_lock (atfork_lock, LLL_PRIVATE);
+-      size_t sl = fork_handler_list_size (&fork_handlers);
+-      for (size_t i = sl; i > 0; i--)
+-	{
+-	  runp = fork_handler_list_at (&fork_handlers, i - 1);
+-	  if (runp->prepare_handler != NULL)
+-	    runp->prepare_handler ();
+-	}
++      struct fork_handler *runp
++        = fork_handler_list_at (&fork_handlers, i - 1);
++
++      uint64_t id = runp->id;
++
++      if (runp->prepare_handler != NULL)
++        {
++          if (do_locking)
++            lll_unlock (atfork_lock, LLL_PRIVATE);
++
++          runp->prepare_handler ();
++
++          if (do_locking)
++            lll_lock (atfork_lock, LLL_PRIVATE);
++        }
++
++      /* We unlocked, ran the handler, and locked again.  In the
++         meanwhile, one or more deregistrations could have occurred leading
++         to the current (just run) handler being moved up the list or even
++         removed from the list itself.  Since handler IDs are guaranteed to
++         to be in increasing order, the next handler has to have:  */
++
++      /* A. An earlier position than the current one has.  */
++      i--;
++
++      /* B. A lower ID than the current one does.  The code below skips
++         any newly added handlers with higher IDs.  */
++      while (i > 0
++             && fork_handler_list_at (&fork_handlers, i - 1)->id >= id)
++        i--;
+     }
+-  else
++
++  return lastrun;
++}
++
++void
++__run_postfork_handlers (enum __run_fork_handler_type who, _Bool do_locking,
++                         uint64_t lastrun)
++{
++  size_t sl = fork_handler_list_size (&fork_handlers);
++  for (size_t i = 0; i < sl;)
+     {
+-      size_t sl = fork_handler_list_size (&fork_handlers);
+-      for (size_t i = 0; i < sl; i++)
+-	{
+-	  runp = fork_handler_list_at (&fork_handlers, i);
+-	  if (who == atfork_run_child && runp->child_handler)
+-	    runp->child_handler ();
+-	  else if (who == atfork_run_parent && runp->parent_handler)
+-	    runp->parent_handler ();
+-	}
++      struct fork_handler *runp = fork_handler_list_at (&fork_handlers, i);
++      uint64_t id = runp->id;
++
++      /* prepare handlers were not run for handlers with ID > LASTRUN.
++         Thus, parent/child handlers will also not be run.  */
++      if (id > lastrun)
++        break;
++
+       if (do_locking)
+-	lll_unlock (atfork_lock, LLL_PRIVATE);
++        lll_unlock (atfork_lock, LLL_PRIVATE);
++
++      if (who == atfork_run_child && runp->child_handler)
++        runp->child_handler ();
++      else if (who == atfork_run_parent && runp->parent_handler)
++        runp->parent_handler ();
++
++      if (do_locking)
++        lll_lock (atfork_lock, LLL_PRIVATE);
++
++      /* We unlocked, ran the handler, and locked again.  In the meanwhile,
++         one or more [de]registrations could have occurred.  Due to this,
++         the list size must be updated.  */
++      sl = fork_handler_list_size (&fork_handlers);
++
++      /* The just-run handler could also have moved up the list. */
++
++      if (sl > i && fork_handler_list_at (&fork_handlers, i)->id == id)
++        /* The position of the recently run handler hasn't changed.  The
++           next handler to be run is an easy increment away.  */
++        i++;
++      else
++        {
++          /* The next handler to be run is the first handler in the list
++             to have an ID higher than the current one.  */
++          for (i = 0; i < sl; i++)
++            {
++              if (fork_handler_list_at (&fork_handlers, i)->id > id)
++                break;
++            }
++        }
+     }
++
++  if (do_locking)
++    lll_unlock (atfork_lock, LLL_PRIVATE);
+ }
+ 
+ 
+diff --git a/nptl/tst-atfork3.c b/nptl/tst-atfork3.c
+new file mode 100644
+index 0000000000000000..bb2250e432ab79ad
+--- /dev/null
++++ b/nptl/tst-atfork3.c
+@@ -0,0 +1,118 @@
++/* Check if pthread_atfork handler can call dlclose (BZ#24595).
++   Copyright (C) 2022 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
++   <http://www.gnu.org/licenses/>.  */
++
++#include <stdio.h>
++#include <pthread.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <stdbool.h>
++
++#include <support/check.h>
++#include <support/xthread.h>
++#include <support/capture_subprocess.h>
++#include <support/xdlfcn.h>
++
++/* Check if pthread_atfork handlers do not deadlock when calling a function
++   that might alter the internal fork handle list, such as dlclose.
++
++   The test registers a callback set with pthread_atfork(), dlopen() a shared
++   library (nptl/tst-atfork3mod.c), calls an exported symbol from the library
++   (which in turn also registers atfork handlers), and calls fork to trigger
++   the callbacks.  */
++
++static void *handler;
++static bool run_dlclose_prepare;
++static bool run_dlclose_parent;
++static bool run_dlclose_child;
++
++static void
++prepare (void)
++{
++  if (run_dlclose_prepare)
++    xdlclose (handler);
++}
++
++static void
++parent (void)
++{
++  if (run_dlclose_parent)
++    xdlclose (handler);
++}
++
++static void
++child (void)
++{
++  if (run_dlclose_child)
++    xdlclose (handler);
++}
++
++static void
++proc_func (void *closure)
++{
++}
++
++static void
++do_test_generic (bool dlclose_prepare, bool dlclose_parent, bool dlclose_child)
++{
++  run_dlclose_prepare = dlclose_prepare;
++  run_dlclose_parent = dlclose_parent;
++  run_dlclose_child = dlclose_child;
++
++  handler = xdlopen ("tst-atfork3mod.so", RTLD_NOW);
++
++  int (*atfork3mod_func)(void);
++  atfork3mod_func = xdlsym (handler, "atfork3mod_func");
++
++  atfork3mod_func ();
++
++  struct support_capture_subprocess proc
++    = support_capture_subprocess (proc_func, NULL);
++  support_capture_subprocess_check (&proc, "tst-atfork3", 0, sc_allow_none);
++
++  handler = atfork3mod_func = NULL;
++
++  support_capture_subprocess_free (&proc);
++}
++
++static void *
++thread_func (void *closure)
++{
++  return NULL;
++}
++
++static int
++do_test (void)
++{
++  {
++    /* Make the process acts as multithread.  */
++    pthread_attr_t attr;
++    xpthread_attr_init (&attr);
++    xpthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
++    xpthread_create (&attr, thread_func, NULL);
++  }
++
++  TEST_COMPARE (pthread_atfork (prepare, parent, child), 0);
++
++  do_test_generic (true  /* prepare */, false /* parent */, false /* child */);
++  do_test_generic (false /* prepare */, true  /* parent */, false /* child */);
++  do_test_generic (false /* prepare */, false /* parent */, true  /* child */);
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/nptl/tst-atfork3mod.c b/nptl/tst-atfork3mod.c
+new file mode 100644
+index 0000000000000000..6d0658cb9efdecbc
+--- /dev/null
++++ b/nptl/tst-atfork3mod.c
+@@ -0,0 +1,44 @@
++/* Copyright (C) 2022 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
++   <http://www.gnu.org/licenses/>.  */
++
++#include <unistd.h>
++#include <stdlib.h>
++#include <pthread.h>
++
++#include <support/check.h>
++
++static void
++mod_prepare (void)
++{
++}
++
++static void
++mod_parent (void)
++{
++}
++
++static void
++mod_child (void)
++{
++}
++
++int atfork3mod_func (void)
++{
++  TEST_COMPARE (pthread_atfork (mod_prepare, mod_parent, mod_child), 0);
++
++  return 0;
++}
+diff --git a/nptl/tst-atfork4.c b/nptl/tst-atfork4.c
+new file mode 100644
+index 0000000000000000..52dc87e73b846ab9
+--- /dev/null
++++ b/nptl/tst-atfork4.c
+@@ -0,0 +1,128 @@
++/* pthread_atfork supports handlers that call pthread_atfork or dlclose.
++   Copyright (C) 2022 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/>.  */
++
++#include <support/xdlfcn.h>
++#include <stdio.h>
++#include <support/xthread.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <support/xunistd.h>
++#include <support/check.h>
++#include <stdlib.h>
++
++static void *
++thread_func (void *x)
++{
++  return NULL;
++}
++
++static unsigned int second_atfork_handler_runcount = 0;
++
++static void
++second_atfork_handler (void)
++{
++  second_atfork_handler_runcount++;
++}
++
++static void *h = NULL;
++
++static unsigned int atfork_handler_runcount = 0;
++
++static void
++prepare (void)
++{
++  /* These atfork handlers are registered while atfork handlers are being
++     executed and thus will not be executed during the corresponding
++     fork.  */
++  TEST_VERIFY_EXIT (pthread_atfork (second_atfork_handler,
++                                    second_atfork_handler,
++                                    second_atfork_handler) == 0);
++
++  /* This will de-register the atfork handlers registered by the dlopen'd
++     library and so they will not be executed.  */
++  if (h != NULL)
++    {
++      xdlclose (h);
++      h = NULL;
++    }
++
++  atfork_handler_runcount++;
++}
++
++static void
++after (void)
++{
++  atfork_handler_runcount++;
++}
++
++static int
++do_test (void)
++{
++  /* Make sure __libc_single_threaded is 0.  */
++  pthread_attr_t attr;
++  xpthread_attr_init (&attr);
++  xpthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
++  xpthread_create (&attr, thread_func, NULL);
++
++  void (*reg_atfork_handlers) (void);
++
++  h = xdlopen ("tst-atfork4mod.so", RTLD_LAZY);
++
++  reg_atfork_handlers = xdlsym (h, "reg_atfork_handlers");
++
++  reg_atfork_handlers ();
++
++  /* We register our atfork handlers *after* loading the module so that our
++     prepare handler is called first at fork, where we then dlclose the
++     module before its prepare handler has a chance to be called.  */
++  TEST_VERIFY_EXIT (pthread_atfork (prepare, after, after) == 0);
++
++  pid_t pid = xfork ();
++
++  /* Both the parent and the child processes should observe this.  */
++  TEST_VERIFY_EXIT (atfork_handler_runcount == 2);
++  TEST_VERIFY_EXIT (second_atfork_handler_runcount == 0);
++
++  if (pid > 0)
++    {
++      int childstat;
++
++      xwaitpid (-1, &childstat, 0);
++      TEST_VERIFY_EXIT (WIFEXITED (childstat)
++                        && WEXITSTATUS (childstat) == 0);
++
++      /* This time, the second set of atfork handlers should also be called
++         since the handlers are already in place before fork is called.  */
++
++      pid = xfork ();
++
++      TEST_VERIFY_EXIT (atfork_handler_runcount == 4);
++      TEST_VERIFY_EXIT (second_atfork_handler_runcount == 2);
++
++      if (pid > 0)
++        {
++          xwaitpid (-1, &childstat, 0);
++          TEST_VERIFY_EXIT (WIFEXITED (childstat)
++                            && WEXITSTATUS (childstat) == 0);
++        }
++    }
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/nptl/tst-atfork4mod.c b/nptl/tst-atfork4mod.c
+new file mode 100644
+index 0000000000000000..e111efeb185916e0
+--- /dev/null
++++ b/nptl/tst-atfork4mod.c
+@@ -0,0 +1,48 @@
++/* pthread_atfork supports handlers that call pthread_atfork or dlclose.
++   Copyright (C) 2022 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/>.  */
++
++#include <pthread.h>
++#include <stdlib.h>
++
++/* This dynamically loaded library simply registers its atfork handlers when
++   asked to.  The atfork handlers should never be executed because the
++   library is unloaded before fork is called by the test program.  */
++
++static void
++prepare (void)
++{
++  abort ();
++}
++
++static void
++parent (void)
++{
++  abort ();
++}
++
++static void
++child (void)
++{
++  abort ();
++}
++
++void
++reg_atfork_handlers (void)
++{
++  pthread_atfork (prepare, parent, child);
++}
+diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
+index b4d20fa652f4ba3b..1324b813136764fc 100644
+--- a/sysdeps/nptl/fork.c
++++ b/sysdeps/nptl/fork.c
+@@ -54,8 +54,9 @@ __libc_fork (void)
+      signal handlers.  POSIX requires that fork is async-signal-safe,
+      but our current fork implementation is not.  */
+   bool multiple_threads = THREAD_GETMEM (THREAD_SELF, header.multiple_threads);
++  uint64_t lastrun;
+ 
+-  __run_fork_handlers (atfork_run_prepare, multiple_threads);
++  lastrun = __run_prefork_handlers (multiple_threads);
+ 
+   /* If we are not running multiple threads, we do not have to
+      preserve lock state.  If fork runs from a signal handler, only
+@@ -129,7 +130,7 @@ __libc_fork (void)
+       __rtld_lock_initialize (GL(dl_load_tls_lock));
+ 
+       /* Run the handlers registered for the child.  */
+-      __run_fork_handlers (atfork_run_child, multiple_threads);
++      __run_postfork_handlers (atfork_run_child, multiple_threads, lastrun);
+     }
+   else
+     {
+@@ -144,7 +145,7 @@ __libc_fork (void)
+ 	}
+ 
+       /* Run the handlers registered for the parent.  */
+-      __run_fork_handlers (atfork_run_parent, multiple_threads);
++      __run_postfork_handlers (atfork_run_parent, multiple_threads, lastrun);
+     }
+ 
+   return pid;
+diff --git a/sysdeps/nptl/fork.h b/sysdeps/nptl/fork.h
+index bef2b7a8a6af8635..222c4f618970a455 100644
+--- a/sysdeps/nptl/fork.h
++++ b/sysdeps/nptl/fork.h
+@@ -31,6 +31,7 @@ struct fork_handler
+   void (*parent_handler) (void);
+   void (*child_handler) (void);
+   void *dso_handle;
++  uint64_t id;
+ };
+ 
+ /* Function to call to unregister fork handlers.  */
+@@ -44,19 +45,18 @@ enum __run_fork_handler_type
+   atfork_run_parent
+ };
+ 
+-/* Run the atfork handlers and lock/unlock the internal lock depending
+-   of the WHO argument:
++/* Run the atfork prepare handlers in the reverse order of registration and
++   return the ID of the last registered handler.  If DO_LOCKING is true, the
++   internal lock is held locked upon return.  */
++extern uint64_t __run_prefork_handlers (_Bool do_locking) attribute_hidden;
+ 
+-   - atfork_run_prepare: run all the PREPARE_HANDLER in reverse order of
+-			 insertion and locks the internal lock.
+-   - atfork_run_child: run all the CHILD_HANDLER and unlocks the internal
+-		       lock.
+-   - atfork_run_parent: run all the PARENT_HANDLER and unlocks the internal
+-			lock.
+-
+-   Perform locking only if DO_LOCKING.  */
+-extern void __run_fork_handlers (enum __run_fork_handler_type who,
+-				 _Bool do_locking) attribute_hidden;
++/* Given a handler type (parent or child), run all the atfork handlers in
++   the order of registration up to and including the handler with id equal
++   to LASTRUN.  If DO_LOCKING is true, the internal lock is unlocked prior
++   to return.  */
++extern void __run_postfork_handlers (enum __run_fork_handler_type who,
++                                     _Bool do_locking,
++                                     uint64_t lastrun) attribute_hidden;
+ 
+ /* C library side function to register new fork handlers.  */
+ extern int __register_atfork (void (*__prepare) (void),
diff --git a/SOURCES/glibc-rh1961109.patch b/SOURCES/glibc-rh1961109.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e47c4d7a9fa10824f5e69dec35baf391d62652d8
--- /dev/null
+++ b/SOURCES/glibc-rh1961109.patch
@@ -0,0 +1,165 @@
+commit f17164bd51db31f47fbbdae826c63b6d78184c45
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Tue May 18 07:21:33 2021 +0200
+
+    localedata: Use U+00AF MACRON in more EBCDIC charsets [BZ #27882]
+    
+    This updates IBM256, IBM277, IBM278, IBM280, IBM284, IBM297, IBM424
+    in the same way that IBM273 was updated for bug 23290.
+    
+    IBM256 and IBM424 still have holes after this change, so HAS_HOLES
+    is not updated.
+    
+    Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+
+diff --git a/iconvdata/ibm277.c b/iconvdata/ibm277.c
+index f93ca2acb8718dd5..0e337dbbdc06a02f 100644
+--- a/iconvdata/ibm277.c
++++ b/iconvdata/ibm277.c
+@@ -23,6 +23,6 @@
+ #define TABLES <ibm277.h>
+ 
+ #define CHARSET_NAME	"IBM277//"
+-#define HAS_HOLES	1	/* Not all 256 character are defined.  */
++#define HAS_HOLES	0
+ 
+ #include <8bit-gap.c>
+diff --git a/iconvdata/ibm278.c b/iconvdata/ibm278.c
+index 4263000760472913..7450fb8e5b846101 100644
+--- a/iconvdata/ibm278.c
++++ b/iconvdata/ibm278.c
+@@ -23,6 +23,6 @@
+ #define TABLES <ibm278.h>
+ 
+ #define CHARSET_NAME	"IBM278//"
+-#define HAS_HOLES	1	/* Not all 256 character are defined.  */
++#define HAS_HOLES	0
+ 
+ #include <8bit-gap.c>
+diff --git a/iconvdata/ibm280.c b/iconvdata/ibm280.c
+index 3efddd7dec2728d9..2ea5478e4e0d7007 100644
+--- a/iconvdata/ibm280.c
++++ b/iconvdata/ibm280.c
+@@ -23,6 +23,6 @@
+ #define TABLES <ibm280.h>
+ 
+ #define CHARSET_NAME	"IBM280//"
+-#define HAS_HOLES	1	/* Not all 256 character are defined.  */
++#define HAS_HOLES	0
+ 
+ #include <8bit-gap.c>
+diff --git a/iconvdata/ibm284.c b/iconvdata/ibm284.c
+index 57dab27d0cec4a33..8dbbc6344d18528f 100644
+--- a/iconvdata/ibm284.c
++++ b/iconvdata/ibm284.c
+@@ -23,6 +23,6 @@
+ #define TABLES <ibm284.h>
+ 
+ #define CHARSET_NAME	"IBM284//"
+-#define HAS_HOLES	1	/* Not all 256 character are defined.  */
++#define HAS_HOLES	0
+ 
+ #include <8bit-gap.c>
+diff --git a/iconvdata/ibm297.c b/iconvdata/ibm297.c
+index f355659afd4b4502..81e63ba1f28f1548 100644
+--- a/iconvdata/ibm297.c
++++ b/iconvdata/ibm297.c
+@@ -23,6 +23,6 @@
+ #define TABLES <ibm297.h>
+ 
+ #define CHARSET_NAME	"IBM297//"
+-#define HAS_HOLES	1	/* Not all 256 character are defined.  */
++#define HAS_HOLES	0
+ 
+ #include <8bit-gap.c>
+diff --git a/localedata/charmaps/IBM256 b/localedata/charmaps/IBM256
+index 5cfd2db5f436cd07..bdc1abf0ade3bfc4 100644
+--- a/localedata/charmaps/IBM256
++++ b/localedata/charmaps/IBM256
+@@ -194,7 +194,7 @@ CHARMAP
+ <U00BE>     /xb9         VULGAR FRACTION THREE QUARTERS
+ <U00AC>     /xba         NOT SIGN
+ <U007C>     /xbb         VERTICAL LINE
+-<U203E>     /xbc         OVERLINE
++<U00AF>     /xbc         MACRON
+ <U00A8>     /xbd         DIAERESIS
+ <U00B4>     /xbe         ACUTE ACCENT
+ <U2017>     /xbf         DOUBLE LOW LINE
+diff --git a/localedata/charmaps/IBM277 b/localedata/charmaps/IBM277
+index 1c0b5cb9fb659364..2f6e3992109a2b33 100644
+--- a/localedata/charmaps/IBM277
++++ b/localedata/charmaps/IBM277
+@@ -195,7 +195,7 @@ CHARMAP
+ <U00BE>     /xb9         VULGAR FRACTION THREE QUARTERS
+ <U00AC>     /xba         NOT SIGN
+ <U007C>     /xbb         VERTICAL LINE
+-<U203E>     /xbc         OVERLINE
++<U00AF>     /xbc         MACRON
+ <U00A8>     /xbd         DIAERESIS
+ <U00B4>     /xbe         ACUTE ACCENT
+ <U00D7>     /xbf         MULTIPLICATION SIGN
+diff --git a/localedata/charmaps/IBM278 b/localedata/charmaps/IBM278
+index 646961501c74c4df..bdfae7621028f003 100644
+--- a/localedata/charmaps/IBM278
++++ b/localedata/charmaps/IBM278
+@@ -196,7 +196,7 @@ CHARMAP
+ <U00BE>     /xb9         VULGAR FRACTION THREE QUARTERS
+ <U00AC>     /xba         NOT SIGN
+ <U007C>     /xbb         VERTICAL LINE
+-<U203E>     /xbc         OVERLINE
++<U00AF>     /xbc         MACRON
+ <U00A8>     /xbd         DIAERESIS
+ <U00B4>     /xbe         ACUTE ACCENT
+ <U00D7>     /xbf         MULTIPLICATION SIGN
+diff --git a/localedata/charmaps/IBM280 b/localedata/charmaps/IBM280
+index 5de3b3e7b96796c0..4c31242806b0ac19 100644
+--- a/localedata/charmaps/IBM280
++++ b/localedata/charmaps/IBM280
+@@ -195,7 +195,7 @@ CHARMAP
+ <U00BE>     /xb9         VULGAR FRACTION THREE QUARTERS
+ <U00AC>     /xba         NOT SIGN
+ <U007C>     /xbb         VERTICAL LINE
+-<U203E>     /xbc         OVERLINE
++<U00AF>     /xbc         MACRON
+ <U00A8>     /xbd         DIAERESIS
+ <U00B4>     /xbe         ACUTE ACCENT
+ <U00D7>     /xbf         MULTIPLICATION SIGN
+diff --git a/localedata/charmaps/IBM284 b/localedata/charmaps/IBM284
+index c64b2a65ab748540..46a8737a715e4e56 100644
+--- a/localedata/charmaps/IBM284
++++ b/localedata/charmaps/IBM284
+@@ -195,7 +195,7 @@ CHARMAP
+ <U00BE>     /xb9         VULGAR FRACTION THREE QUARTERS
+ <U005E>     /xba         CIRCUMFLEX ACCENT
+ <U0021>     /xbb         EXCLAMATION MARK
+-<U203E>     /xbc         OVERLINE
++<U00AF>     /xbc         MACRON
+ <U007E>     /xbd         TILDE
+ <U00B4>     /xbe         ACUTE ACCENT
+ <U00D7>     /xbf         MULTIPLICATION SIGN
+diff --git a/localedata/charmaps/IBM297 b/localedata/charmaps/IBM297
+index 33b74eee437241aa..14361ad418cf1bc7 100644
+--- a/localedata/charmaps/IBM297
++++ b/localedata/charmaps/IBM297
+@@ -195,7 +195,7 @@ CHARMAP
+ <U00BE>     /xb9         VULGAR FRACTION THREE QUARTERS
+ <U00AC>     /xba         NOT SIGN
+ <U007C>     /xbb         VERTICAL LINE
+-<U203E>     /xbc         OVERLINE
++<U00AF>     /xbc         MACRON
+ <U007E>     /xbd         TILDE
+ <U00B4>     /xbe         ACUTE ACCENT
+ <U00D7>     /xbf         MULTIPLICATION SIGN
+diff --git a/localedata/charmaps/IBM424 b/localedata/charmaps/IBM424
+index 883e43b8ae04ee4c..deca11e1b18ec0a6 100644
+--- a/localedata/charmaps/IBM424
++++ b/localedata/charmaps/IBM424
+@@ -175,7 +175,7 @@ CHARMAP
+ <U00BE>     /xb9         VULGAR FRACTION THREE QUARTERS
+ <U005B>     /xba         LEFT SQUARE BRACKET
+ <U005D>     /xbb         RIGHT SQUARE BRACKET
+-<U203E>     /xbc         OVERLINE
++<U00AF>     /xbc         MACRON
+ <U00A8>     /xbd         DIAERESIS
+ <U00B4>     /xbe         ACUTE ACCENT
+ <U00D7>     /xbf         MULTIPLICATION SIGN
diff --git a/SOURCES/glibc-rh1982608.patch b/SOURCES/glibc-rh1982608.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6f67ed6ddc341ec28384941a2be824cb7b97d0d9
--- /dev/null
+++ b/SOURCES/glibc-rh1982608.patch
@@ -0,0 +1,2216 @@
+This is a rebase of posix/glob.c from upstream (gnulib->glibc->rhel).
+
+Relevent upstream commits:
+
+7c477b57a31487eda516db02b9e04f22d1a6e6af posix/glob.c: update from gnulib
+  (This is the master commit to which we're syncing)
+
+gnulib commit 98f034a0c2ba8917c96f363de1a8d66244e411da
+  (This is the gnulib commit to which glibc upstream sync'd)
+
+Additional glibc upstream commits of note:
+84f7ce84474c1648ce96884f1c91ca7b97ca3fc2 posix: Add glob64 with 64-bit time_t support
+  (just posix/glob.c and sysdeps/gnu/glob64-lstat-compat.c)
+9a7ab0769b295cbf5232140401742a8f34bda3de hurd: Fix glob lstat compatibility
+4883360415f1ed772ba44decc501d59deb17bdf0 posix: Sync glob code with gnulib
+04986243d1af37ac0177ed2f9db0a066ebd2b212 Remove internal usage of extensible stat functions
+ddc650e9b3dc916eab417ce9f79e67337b05035c Fix use-after-free in glob when expanding ~user (bug 25414)
+
+
+diff -rup a/posix/glob-lstat-compat.c b/posix/glob-lstat-compat.c
+--- a/posix/glob-lstat-compat.c	2018-08-01 01:10:47.000000000 -0400
++++ b/posix/glob-lstat-compat.c	2022-05-02 22:49:06.504676711 -0400
+@@ -28,7 +28,8 @@
+ # define GLOB_ATTRIBUTE attribute_compat_text_section
+ 
+ /* Avoid calling gl_lstat with GLOB_ALTDIRFUNC.  */
+-# define GLOB_NO_LSTAT
++# define GLOB_LSTAT   gl_stat
++# define GLOB_LSTAT64 __stat64
+ 
+ # include <posix/glob.c>
+ 
+diff -rup a/posix/glob.c b/posix/glob.c
+--- a/posix/glob.c	2022-05-03 14:37:52.959042051 -0400
++++ b/posix/glob.c	2022-05-02 22:49:18.655134696 -0400
+@@ -1,4 +1,4 @@
+-/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
++/* Copyright (C) 1991-2022 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
+@@ -13,11 +13,22 @@
+ 
+    You should have received a copy of the GNU Lesser General Public
+    License along with the GNU C Library; if not, see
+-   <http://www.gnu.org/licenses/>.  */
++   <https://www.gnu.org/licenses/>.  */
++
++#ifndef _LIBC
++
++/* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
++   optimizes away the pattern == NULL test below.  */
++# define _GL_ARG_NONNULL(params)
++
++# include <libc-config.h>
++
++#endif
+ 
+ #include <glob.h>
+ 
+ #include <errno.h>
++#include <fcntl.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <stdbool.h>
+@@ -26,7 +37,7 @@
+ #include <assert.h>
+ #include <unistd.h>
+ 
+-#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
++#if defined _WIN32 && ! defined __CYGWIN__
+ # define WINDOWS32
+ #endif
+ 
+@@ -46,30 +57,38 @@
+ # define sysconf(id) __sysconf (id)
+ # define closedir(dir) __closedir (dir)
+ # define opendir(name) __opendir (name)
++# undef dirfd
++# define dirfd(str) __dirfd (str)
+ # define readdir(str) __readdir64 (str)
+ # define getpwnam_r(name, bufp, buf, len, res) \
+     __getpwnam_r (name, bufp, buf, len, res)
+-# ifndef __lstat64
+-#  define __lstat64(fname, buf) __lxstat64 (_STAT_VER, fname, buf)
++# define FLEXIBLE_ARRAY_MEMBER
++# ifndef struct_stat
++#  define struct_stat           struct stat
+ # endif
+-# ifndef __stat64
+-#  define __stat64(fname, buf) __xstat64 (_STAT_VER, fname, buf)
++# ifndef struct_stat64
++#  define struct_stat64         struct stat64
++# endif
++# ifndef GLOB_LSTAT
++#  define GLOB_LSTAT            gl_lstat
++# endif
++# ifndef GLOB_FSTATAT64
++#  define GLOB_FSTATAT64        __fstatat64
+ # endif
+-# define struct_stat64		struct stat64
+-# define FLEXIBLE_ARRAY_MEMBER
+ # include <shlib-compat.h>
+ #else /* !_LIBC */
+ # define __glob                 glob
+ # define __getlogin_r(buf, len) getlogin_r (buf, len)
+-# define __lstat64(fname, buf)  lstat (fname, buf)
+-# define __stat64(fname, buf)   stat (fname, buf)
+ # define __fxstatat64(_, d, f, st, flag) fstatat (d, f, st, flag)
+-# define struct_stat64          struct stat
+ # ifndef __MVS__
+ #  define __alloca              alloca
+ # endif
+ # define __readdir              readdir
+ # define COMPILE_GLOB64
++# define struct_stat            struct stat
++# define struct_stat64          struct stat
++# define GLOB_LSTAT             gl_lstat
++# define GLOB_FSTATAT64         fstatat
+ #endif /* _LIBC */
+ 
+ #include <fnmatch.h>
+@@ -80,7 +99,9 @@
+ 
+ static const char *next_brace_sub (const char *begin, int flags) __THROWNL;
+ 
+-typedef uint_fast8_t dirent_type;
++/* The type of ((struct dirent *) 0)->d_type is 'unsigned char' on most
++   platforms, but 'unsigned int' in the mingw from mingw.org.  */
++typedef uint_fast32_t dirent_type;
+ 
+ #if !defined _LIBC && !defined HAVE_STRUCT_DIRENT_D_TYPE
+ /* Any distinct values will do here.
+@@ -119,9 +140,9 @@ readdir_result_type (struct readdir_resu
+ /* Construct an initializer for a struct readdir_result object from a
+    struct dirent *.  No copy of the name is made.  */
+ #define READDIR_RESULT_INITIALIZER(source) \
+-  {					   \
+-    source->d_name,			   \
+-    D_TYPE_TO_RESULT (source)		   \
++  {                                        \
++    source->d_name,                        \
++    D_TYPE_TO_RESULT (source)              \
+   }
+ 
+ /* Call gl_readdir on STREAM.  This macro can be overridden to reduce
+@@ -186,22 +207,15 @@ glob_lstat (glob_t *pglob, int flags, co
+ {
+ /* Use on glob-lstat-compat.c to provide a compat symbol which does not
+    use lstat / gl_lstat.  */
+-#ifdef GLOB_NO_LSTAT
+-# define GL_LSTAT gl_stat
+-# define LSTAT64 __stat64
+-#else
+-# define GL_LSTAT gl_lstat
+-# define LSTAT64 __lstat64
+-#endif
+-
+   union
+   {
+-    struct stat st;
++    struct_stat st;
+     struct_stat64 st64;
+   } ust;
+   return (__glibc_unlikely (flags & GLOB_ALTDIRFUNC)
+-          ? pglob->GL_LSTAT (fullname, &ust.st)
+-          : LSTAT64 (fullname, &ust.st64));
++          ? pglob->GLOB_LSTAT (fullname, &ust.st)
++          : GLOB_FSTATAT64 (AT_FDCWD, fullname, &ust.st64,
++                            AT_SYMLINK_NOFOLLOW));
+ }
+ 
+ /* Set *R = A + B.  Return true if the answer is mathematically
+@@ -211,7 +225,7 @@ glob_lstat (glob_t *pglob, int flags, co
+ static bool
+ size_add_wrapv (size_t a, size_t b, size_t *r)
+ {
+-#if 5 <= __GNUC__ && !defined __ICC
++#if 7 <= __GNUC__ && !defined __ICC
+   return __builtin_add_overflow (a, b, r);
+ #else
+   *r = a + b;
+@@ -228,8 +242,8 @@ glob_use_alloca (size_t alloca_used, siz
+ }
+ 
+ static int glob_in_dir (const char *pattern, const char *directory,
+-			int flags, int (*errfunc) (const char *, int),
+-			glob_t *pglob, size_t alloca_used);
++                        int flags, int (*errfunc) (const char *, int),
++                        glob_t *pglob, size_t alloca_used);
+ static int prefix_array (const char *prefix, char **array, size_t n) __THROWNL;
+ static int collated_compare (const void *, const void *) __THROWNL;
+ 
+@@ -239,11 +253,12 @@ static int collated_compare (const void
+ static bool
+ is_dir (char const *filename, int flags, glob_t const *pglob)
+ {
+-  struct stat st;
++  struct_stat st;
+   struct_stat64 st64;
+   return (__glibc_unlikely (flags & GLOB_ALTDIRFUNC)
+           ? pglob->gl_stat (filename, &st) == 0 && S_ISDIR (st.st_mode)
+-          : __stat64 (filename, &st64) == 0 && S_ISDIR (st64.st_mode));
++          : (GLOB_FSTATAT64 (AT_FDCWD, filename, &st64, 0) == 0
++             && S_ISDIR (st64.st_mode)));
+ }
+ 
+ /* Find the end of the sub-pattern in a brace expression.  */
+@@ -254,17 +269,17 @@ next_brace_sub (const char *cp, int flag
+   while (*cp != '\0')
+     if ((flags & GLOB_NOESCAPE) == 0 && *cp == '\\')
+       {
+-	if (*++cp == '\0')
+-	  break;
+-	++cp;
++        if (*++cp == '\0')
++          break;
++        ++cp;
+       }
+     else
+       {
+-	if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
+-	  break;
++        if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
++          break;
+ 
+-	if (*cp++ == '{')
+-	  depth++;
++        if (*cp++ == '{')
++          depth++;
+       }
+ 
+   return *cp != '\0' ? cp : NULL;
+@@ -285,7 +300,7 @@ next_brace_sub (const char *cp, int flag
+ int
+ GLOB_ATTRIBUTE
+ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int),
+-	glob_t *pglob)
++        glob_t *pglob)
+ {
+   const char *filename;
+   char *dirname = NULL;
+@@ -319,22 +334,22 @@ __glob (const char *pattern, int flags,
+     {
+       pglob->gl_pathc = 0;
+       if (!(flags & GLOB_DOOFFS))
+-	pglob->gl_pathv = NULL;
++        pglob->gl_pathv = NULL;
+       else
+-	{
+-	  size_t i;
++        {
++          size_t i;
+ 
+-	  if (pglob->gl_offs >= ~((size_t) 0) / sizeof (char *))
+-	    return GLOB_NOSPACE;
++          if (pglob->gl_offs >= ~((size_t) 0) / sizeof (char *))
++            return GLOB_NOSPACE;
+ 
+-	  pglob->gl_pathv = (char **) malloc ((pglob->gl_offs + 1)
+-					      * sizeof (char *));
+-	  if (pglob->gl_pathv == NULL)
+-	    return GLOB_NOSPACE;
+-
+-	  for (i = 0; i <= pglob->gl_offs; ++i)
+-	    pglob->gl_pathv[i] = NULL;
+-	}
++          pglob->gl_pathv = (char **) malloc ((pglob->gl_offs + 1)
++                                              * sizeof (char *));
++          if (pglob->gl_pathv == NULL)
++            return GLOB_NOSPACE;
++
++          for (i = 0; i <= pglob->gl_offs; ++i)
++            pglob->gl_pathv[i] = NULL;
++        }
+     }
+ 
+   if (flags & GLOB_BRACE)
+@@ -342,129 +357,129 @@ __glob (const char *pattern, int flags,
+       const char *begin;
+ 
+       if (flags & GLOB_NOESCAPE)
+-	begin = strchr (pattern, '{');
++        begin = strchr (pattern, '{');
+       else
+-	{
+-	  begin = pattern;
+-	  while (1)
+-	    {
+-	      if (*begin == '\0')
+-		{
+-		  begin = NULL;
+-		  break;
+-		}
+-
+-	      if (*begin == '\\' && begin[1] != '\0')
+-		++begin;
+-	      else if (*begin == '{')
+-		break;
+-
+-	      ++begin;
+-	    }
+-	}
++        {
++          begin = pattern;
++          while (1)
++            {
++              if (*begin == '\0')
++                {
++                  begin = NULL;
++                  break;
++                }
++
++              if (*begin == '\\' && begin[1] != '\0')
++                ++begin;
++              else if (*begin == '{')
++                break;
++
++              ++begin;
++            }
++        }
+ 
+       if (begin != NULL)
+-	{
+-	  /* Allocate working buffer large enough for our work.  Note that
+-	    we have at least an opening and closing brace.  */
+-	  size_t firstc;
+-	  char *alt_start;
+-	  const char *p;
+-	  const char *next;
+-	  const char *rest;
+-	  size_t rest_len;
+-	  char *onealt;
+-	  size_t pattern_len = strlen (pattern) - 1;
+-	  int alloca_onealt = glob_use_alloca (alloca_used, pattern_len);
+-	  if (alloca_onealt)
+-	    onealt = alloca_account (pattern_len, alloca_used);
+-	  else
+-	    {
+-	      onealt = malloc (pattern_len);
+-	      if (onealt == NULL)
+-		return GLOB_NOSPACE;
+-	    }
+-
+-	  /* We know the prefix for all sub-patterns.  */
+-	  alt_start = mempcpy (onealt, pattern, begin - pattern);
+-
+-	  /* Find the first sub-pattern and at the same time find the
+-	     rest after the closing brace.  */
+-	  next = next_brace_sub (begin + 1, flags);
+-	  if (next == NULL)
+-	    {
+-	      /* It is an invalid expression.  */
+-	    illegal_brace:
+-	      if (__glibc_unlikely (!alloca_onealt))
+-		free (onealt);
+-	      flags &= ~GLOB_BRACE;
+-	      goto no_brace;
+-	    }
+-
+-	  /* Now find the end of the whole brace expression.  */
+-	  rest = next;
+-	  while (*rest != '}')
+-	    {
+-	      rest = next_brace_sub (rest + 1, flags);
+-	      if (rest == NULL)
+-		/* It is an illegal expression.  */
+-		goto illegal_brace;
+-	    }
+-	  /* Please note that we now can be sure the brace expression
+-	     is well-formed.  */
+-	  rest_len = strlen (++rest) + 1;
+-
+-	  /* We have a brace expression.  BEGIN points to the opening {,
+-	     NEXT points past the terminator of the first element, and END
+-	     points past the final }.  We will accumulate result names from
+-	     recursive runs for each brace alternative in the buffer using
+-	     GLOB_APPEND.  */
+-	  firstc = pglob->gl_pathc;
+-
+-	  p = begin + 1;
+-	  while (1)
+-	    {
+-	      int result;
+-
+-	      /* Construct the new glob expression.  */
+-	      mempcpy (mempcpy (alt_start, p, next - p), rest, rest_len);
+-
+-	      result = __glob (onealt,
+-			       ((flags & ~(GLOB_NOCHECK | GLOB_NOMAGIC))
+-				| GLOB_APPEND),
+-			       errfunc, pglob);
+-
+-	      /* If we got an error, return it.  */
+-	      if (result && result != GLOB_NOMATCH)
+-		{
+-		  if (__glibc_unlikely (!alloca_onealt))
+-		    free (onealt);
+-		  if (!(flags & GLOB_APPEND))
+-		    {
+-		      globfree (pglob);
+-		      pglob->gl_pathc = 0;
+-		    }
+-		  return result;
+-		}
+-
+-	      if (*next == '}')
+-		/* We saw the last entry.  */
+-		break;
+-
+-	      p = next + 1;
+-	      next = next_brace_sub (p, flags);
+-	      assert (next != NULL);
+-	    }
+-
+-	  if (__glibc_unlikely (!alloca_onealt))
+-	    free (onealt);
+-
+-	  if (pglob->gl_pathc != firstc)
+-	    /* We found some entries.  */
+-	    return 0;
+-	  else if (!(flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
+-	    return GLOB_NOMATCH;
+-	}
++        {
++          /* Allocate working buffer large enough for our work.  Note that
++             we have at least an opening and closing brace.  */
++          size_t firstc;
++          char *alt_start;
++          const char *p;
++          const char *next;
++          const char *rest;
++          size_t rest_len;
++          char *onealt;
++          size_t pattern_len = strlen (pattern) - 1;
++          int alloca_onealt = glob_use_alloca (alloca_used, pattern_len);
++          if (alloca_onealt)
++            onealt = alloca_account (pattern_len, alloca_used);
++          else
++            {
++              onealt = malloc (pattern_len);
++              if (onealt == NULL)
++                return GLOB_NOSPACE;
++            }
++
++          /* We know the prefix for all sub-patterns.  */
++          alt_start = mempcpy (onealt, pattern, begin - pattern);
++
++          /* Find the first sub-pattern and at the same time find the
++             rest after the closing brace.  */
++          next = next_brace_sub (begin + 1, flags);
++          if (next == NULL)
++            {
++              /* It is an invalid expression.  */
++            illegal_brace:
++              if (__glibc_unlikely (!alloca_onealt))
++                free (onealt);
++              flags &= ~GLOB_BRACE;
++              goto no_brace;
++            }
++
++          /* Now find the end of the whole brace expression.  */
++          rest = next;
++          while (*rest != '}')
++            {
++              rest = next_brace_sub (rest + 1, flags);
++              if (rest == NULL)
++                /* It is an illegal expression.  */
++                goto illegal_brace;
++            }
++          /* Please note that we now can be sure the brace expression
++             is well-formed.  */
++          rest_len = strlen (++rest) + 1;
++
++          /* We have a brace expression.  BEGIN points to the opening {,
++             NEXT points past the terminator of the first element, and END
++             points past the final }.  We will accumulate result names from
++             recursive runs for each brace alternative in the buffer using
++             GLOB_APPEND.  */
++          firstc = pglob->gl_pathc;
++
++          p = begin + 1;
++          while (1)
++            {
++              int result;
++
++              /* Construct the new glob expression.  */
++              mempcpy (mempcpy (alt_start, p, next - p), rest, rest_len);
++
++              result = __glob (onealt,
++                               ((flags & ~(GLOB_NOCHECK | GLOB_NOMAGIC))
++                                | GLOB_APPEND),
++                               errfunc, pglob);
++
++              /* If we got an error, return it.  */
++              if (result && result != GLOB_NOMATCH)
++                {
++                  if (__glibc_unlikely (!alloca_onealt))
++                    free (onealt);
++                  if (!(flags & GLOB_APPEND))
++                    {
++                      globfree (pglob);
++                      pglob->gl_pathc = 0;
++                    }
++                  return result;
++                }
++
++              if (*next == '}')
++                /* We saw the last entry.  */
++                break;
++
++              p = next + 1;
++              next = next_brace_sub (p, flags);
++              assert (next != NULL);
++            }
++
++          if (__glibc_unlikely (!alloca_onealt))
++            free (onealt);
++
++          if (pglob->gl_pathc != firstc)
++            /* We found some entries.  */
++            return 0;
++          else if (!(flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
++            return GLOB_NOMATCH;
++        }
+     }
+ 
+  no_brace:
+@@ -486,33 +501,33 @@ __glob (const char *pattern, int flags,
+   if (filename == NULL)
+     {
+       /* This can mean two things: a simple name or "~name".  The latter
+-	 case is nothing but a notation for a directory.  */
++         case is nothing but a notation for a directory.  */
+       if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && pattern[0] == '~')
+-	{
+-	  dirname = (char *) pattern;
+-	  dirlen = strlen (pattern);
+-
+-	  /* Set FILENAME to NULL as a special flag.  This is ugly but
+-	     other solutions would require much more code.  We test for
+-	     this special case below.  */
+-	  filename = NULL;
+-	}
++        {
++          dirname = (char *) pattern;
++          dirlen = strlen (pattern);
++
++          /* Set FILENAME to NULL as a special flag.  This is ugly but
++             other solutions would require much more code.  We test for
++             this special case below.  */
++          filename = NULL;
++        }
+       else
+-	{
+-	  if (__glibc_unlikely (pattern[0] == '\0'))
+-	    {
+-	      dirs.gl_pathv = NULL;
+-	      goto no_matches;
+-	    }
+-
+-	  filename = pattern;
+-	  dirname = (char *) ".";
+-	  dirlen = 0;
+-	}
++        {
++          if (__glibc_unlikely (pattern[0] == '\0'))
++            {
++              dirs.gl_pathv = NULL;
++              goto no_matches;
++            }
++
++          filename = pattern;
++          dirname = (char *) ".";
++          dirlen = 0;
++        }
+     }
+   else if (filename == pattern
+-	   || (filename == pattern + 1 && pattern[0] == '\\'
+-	       && (flags & GLOB_NOESCAPE) == 0))
++           || (filename == pattern + 1 && pattern[0] == '\\'
++               && (flags & GLOB_NOESCAPE) == 0))
+     {
+       /* "/pattern" or "\\/pattern".  */
+       dirname = (char *) "/";
+@@ -525,32 +540,32 @@ __glob (const char *pattern, int flags,
+       dirlen = filename - pattern;
+ #if defined __MSDOS__ || defined WINDOWS32
+       if (*filename == ':'
+-	  || (filename > pattern + 1 && filename[-1] == ':'))
+-	{
+-	  char *drive_spec;
+-
+-	  ++dirlen;
+-	  drive_spec = __alloca (dirlen + 1);
+-	  *((char *) mempcpy (drive_spec, pattern, dirlen)) = '\0';
+-	  /* For now, disallow wildcards in the drive spec, to
+-	     prevent infinite recursion in glob.  */
+-	  if (__glob_pattern_p (drive_spec, !(flags & GLOB_NOESCAPE)))
+-	    return GLOB_NOMATCH;
+-	  /* If this is "d:pattern", we need to copy ':' to DIRNAME
+-	     as well.  If it's "d:/pattern", don't remove the slash
+-	     from "d:/", since "d:" and "d:/" are not the same.*/
+-	}
++          || (filename > pattern + 1 && filename[-1] == ':'))
++        {
++          char *drive_spec;
++
++          ++dirlen;
++          drive_spec = __alloca (dirlen + 1);
++          *((char *) mempcpy (drive_spec, pattern, dirlen)) = '\0';
++          /* For now, disallow wildcards in the drive spec, to
++             prevent infinite recursion in glob.  */
++          if (__glob_pattern_p (drive_spec, !(flags & GLOB_NOESCAPE)))
++            return GLOB_NOMATCH;
++          /* If this is "d:pattern", we need to copy ':' to DIRNAME
++             as well.  If it's "d:/pattern", don't remove the slash
++             from "d:/", since "d:" and "d:/" are not the same.*/
++        }
+ #endif
+ 
+       if (glob_use_alloca (alloca_used, dirlen + 1))
+-	newp = alloca_account (dirlen + 1, alloca_used);
++        newp = alloca_account (dirlen + 1, alloca_used);
+       else
+-	{
+-	  newp = malloc (dirlen + 1);
+-	  if (newp == NULL)
+-	    return GLOB_NOSPACE;
+-	  malloc_dirname = 1;
+-	}
++        {
++          newp = malloc (dirlen + 1);
++          if (newp == NULL)
++            return GLOB_NOSPACE;
++          malloc_dirname = 1;
++        }
+       *((char *) mempcpy (newp, pattern, dirlen)) = '\0';
+       dirname = newp;
+       ++filename;
+@@ -566,363 +581,383 @@ __glob (const char *pattern, int flags,
+ 
+       if (filename[0] == '\0' && dirlen > 1 && !drive_root)
+         /* "pattern/".  Expand "pattern", appending slashes.  */
+-	{
+-	  int orig_flags = flags;
+-	  if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\')
+-	    {
+-	      /* "pattern\\/".  Remove the final backslash if it hasn't
+-		 been quoted.  */
+-	      char *p = (char *) &dirname[dirlen - 1];
+-
+-	      while (p > dirname && p[-1] == '\\') --p;
+-	      if ((&dirname[dirlen] - p) & 1)
+-		{
+-		  *(char *) &dirname[--dirlen] = '\0';
+-		  flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC);
+-		}
+-	    }
+-	  int val = __glob (dirname, flags | GLOB_MARK, errfunc, pglob);
+-	  if (val == 0)
+-	    pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK)
+-			       | (flags & GLOB_MARK));
+-	  else if (val == GLOB_NOMATCH && flags != orig_flags)
+-	    {
+-	      /* Make sure globfree (&dirs); is a nop.  */
+-	      dirs.gl_pathv = NULL;
+-	      flags = orig_flags;
+-	      oldcount = pglob->gl_pathc + pglob->gl_offs;
+-	      goto no_matches;
+-	    }
+-	  retval = val;
+-	  goto out;
+-	}
++        {
++          int orig_flags = flags;
++          if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\')
++            {
++              /* "pattern\\/".  Remove the final backslash if it hasn't
++                 been quoted.  */
++              char *p = (char *) &dirname[dirlen - 1];
++
++              while (p > dirname && p[-1] == '\\') --p;
++              if ((&dirname[dirlen] - p) & 1)
++                {
++                  *(char *) &dirname[--dirlen] = '\0';
++                  flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC);
++                }
++            }
++          int val = __glob (dirname, flags | GLOB_MARK, errfunc, pglob);
++          if (val == 0)
++            pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK)
++                               | (flags & GLOB_MARK));
++          else if (val == GLOB_NOMATCH && flags != orig_flags)
++            {
++              /* Make sure globfree (&dirs); is a nop.  */
++              dirs.gl_pathv = NULL;
++              flags = orig_flags;
++              oldcount = pglob->gl_pathc + pglob->gl_offs;
++              goto no_matches;
++            }
++          retval = val;
++          goto out;
++        }
+     }
+ 
+   if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~')
+     {
+       if (dirname[1] == '\0' || dirname[1] == '/'
+-	  || (!(flags & GLOB_NOESCAPE) && dirname[1] == '\\'
+-	      && (dirname[2] == '\0' || dirname[2] == '/')))
+-	{
+-	  /* Look up home directory.  */
+-	  char *home_dir = getenv ("HOME");
+-	  int malloc_home_dir = 0;
+-	  if (home_dir == NULL || home_dir[0] == '\0')
+-	    {
++          || (!(flags & GLOB_NOESCAPE) && dirname[1] == '\\'
++              && (dirname[2] == '\0' || dirname[2] == '/')))
++        {
++          /* Look up home directory.  */
++          char *home_dir = getenv ("HOME");
++          int malloc_home_dir = 0;
++          if (home_dir == NULL || home_dir[0] == '\0')
++            {
+ #ifdef WINDOWS32
+-	      /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give
+-		 preference to HOME, because the user can change HOME.  */
+-	      const char *home_drive = getenv ("HOMEDRIVE");
+-	      const char *home_path = getenv ("HOMEPATH");
+-
+-	      if (home_drive != NULL && home_path != NULL)
+-		{
+-		  size_t home_drive_len = strlen (home_drive);
+-		  size_t home_path_len = strlen (home_path);
+-		  char *mem = alloca (home_drive_len + home_path_len + 1);
+-
+-		  memcpy (mem, home_drive, home_drive_len);
+-		  memcpy (mem + home_drive_len, home_path, home_path_len + 1);
+-		  home_dir = mem;
+-		}
+-	      else
+-		home_dir = "c:/users/default"; /* poor default */
++              /* Windows NT defines HOMEDRIVE and HOMEPATH.  But give
++                 preference to HOME, because the user can change HOME.  */
++              const char *home_drive = getenv ("HOMEDRIVE");
++              const char *home_path = getenv ("HOMEPATH");
++
++              if (home_drive != NULL && home_path != NULL)
++                {
++                  size_t home_drive_len = strlen (home_drive);
++                  size_t home_path_len = strlen (home_path);
++                  char *mem = alloca (home_drive_len + home_path_len + 1);
++
++                  memcpy (mem, home_drive, home_drive_len);
++                  memcpy (mem + home_drive_len, home_path, home_path_len + 1);
++                  home_dir = mem;
++                }
++              else
++                home_dir = "c:/users/default"; /* poor default */
+ #else
+-	      int err;
+-	      struct passwd *p;
+-	      struct passwd pwbuf;
+-	      struct scratch_buffer s;
+-	      scratch_buffer_init (&s);
+-	      while (true)
+-		{
+-		  p = NULL;
+-		  err = __getlogin_r (s.data, s.length);
+-		  if (err == 0)
+-		    {
++              int err;
++              struct passwd *p;
++              struct passwd pwbuf;
++              struct scratch_buffer s;
++              scratch_buffer_init (&s);
++              while (true)
++                {
++                  p = NULL;
++                  err = __getlogin_r (s.data, s.length);
++                  if (err == 0)
++                    {
+ # if defined HAVE_GETPWNAM_R || defined _LIBC
+-		      size_t ssize = strlen (s.data) + 1;
+-		      char *sdata = s.data;
+-		      err = getpwnam_r (sdata, &pwbuf, sdata + ssize,
+-					s.length - ssize, &p);
++                      size_t ssize = strlen (s.data) + 1;
++                      char *sdata = s.data;
++                      err = getpwnam_r (sdata, &pwbuf, sdata + ssize,
++                                        s.length - ssize, &p);
+ # else
+-		      p = getpwnam (s.data);
+-		      if (p == NULL)
+-			err = errno;
++                      p = getpwnam (s.data);
++                      if (p == NULL)
++                        err = errno;
+ # endif
+-		    }
+-		  if (err != ERANGE)
+-		    break;
+-		  if (!scratch_buffer_grow (&s))
+-		    {
+-		      retval = GLOB_NOSPACE;
+-		      goto out;
+-		    }
+-		}
+-	      if (err == 0)
+-		{
+-		  home_dir = strdup (p->pw_dir);
+-		  malloc_home_dir = 1;
+-		}
+-	      scratch_buffer_free (&s);
+-	      if (err == 0 && home_dir == NULL)
+-		{
+-		  retval = GLOB_NOSPACE;
+-		  goto out;
+-		}
++                    }
++                  if (err != ERANGE)
++                    break;
++                  if (!scratch_buffer_grow (&s))
++                    {
++                      retval = GLOB_NOSPACE;
++                      goto out;
++                    }
++                }
++              if (err == 0)
++                {
++                  home_dir = strdup (p->pw_dir);
++                  malloc_home_dir = 1;
++                }
++              scratch_buffer_free (&s);
++              if (err == 0 && home_dir == NULL)
++                {
++                  retval = GLOB_NOSPACE;
++                  goto out;
++                }
+ #endif /* WINDOWS32 */
+-	    }
+-	  if (home_dir == NULL || home_dir[0] == '\0')
+-	    {
+-	      if (__glibc_unlikely (malloc_home_dir))
+-		free (home_dir);
+-	      if (flags & GLOB_TILDE_CHECK)
+-		{
+-		  retval = GLOB_NOMATCH;
+-		  goto out;
+-		}
+-	      else
+-		{
+-		  home_dir = (char *) "~"; /* No luck.  */
+-		  malloc_home_dir = 0;
+-		}
+-	    }
+-	  /* Now construct the full directory.  */
+-	  if (dirname[1] == '\0')
+-	    {
+-	      if (__glibc_unlikely (malloc_dirname))
+-		free (dirname);
+-
+-	      dirname = home_dir;
+-	      dirlen = strlen (dirname);
+-	      malloc_dirname = malloc_home_dir;
+-	    }
+-	  else
+-	    {
+-	      char *newp;
+-	      size_t home_len = strlen (home_dir);
+-	      int use_alloca = glob_use_alloca (alloca_used, home_len + dirlen);
+-	      if (use_alloca)
+-		newp = alloca_account (home_len + dirlen, alloca_used);
+-	      else
+-		{
+-		  newp = malloc (home_len + dirlen);
+-		  if (newp == NULL)
+-		    {
+-		      if (__glibc_unlikely (malloc_home_dir))
+-			free (home_dir);
+-		      retval = GLOB_NOSPACE;
+-		      goto out;
+-		    }
+-		}
+-
+-	      mempcpy (mempcpy (newp, home_dir, home_len),
+-		       &dirname[1], dirlen);
+-
+-	      if (__glibc_unlikely (malloc_dirname))
+-		free (dirname);
+-
+-	      dirname = newp;
+-	      dirlen += home_len - 1;
+-	      malloc_dirname = !use_alloca;
+-
+-	      if (__glibc_unlikely (malloc_home_dir))
+-		free (home_dir);
+-	    }
+-	  dirname_modified = 1;
+-	}
++            }
++          if (home_dir == NULL || home_dir[0] == '\0')
++            {
++              if (__glibc_unlikely (malloc_home_dir))
++                free (home_dir);
++              if (flags & GLOB_TILDE_CHECK)
++                {
++                  retval = GLOB_NOMATCH;
++                  goto out;
++                }
++              else
++                {
++                  home_dir = (char *) "~"; /* No luck.  */
++                  malloc_home_dir = 0;
++                }
++            }
++          /* Now construct the full directory.  */
++          if (dirname[1] == '\0')
++            {
++              if (__glibc_unlikely (malloc_dirname))
++                free (dirname);
++
++              dirname = home_dir;
++              dirlen = strlen (dirname);
++              malloc_dirname = malloc_home_dir;
++            }
++          else
++            {
++              char *newp;
++              size_t home_len = strlen (home_dir);
++              int use_alloca = glob_use_alloca (alloca_used, home_len + dirlen);
++              if (use_alloca)
++                newp = alloca_account (home_len + dirlen, alloca_used);
++              else
++                {
++                  newp = malloc (home_len + dirlen);
++                  if (newp == NULL)
++                    {
++                      if (__glibc_unlikely (malloc_home_dir))
++                        free (home_dir);
++                      retval = GLOB_NOSPACE;
++                      goto out;
++                    }
++                }
++
++              mempcpy (mempcpy (newp, home_dir, home_len),
++                       &dirname[1], dirlen);
++
++              if (__glibc_unlikely (malloc_dirname))
++                free (dirname);
++
++              dirname = newp;
++              dirlen += home_len - 1;
++              malloc_dirname = !use_alloca;
++
++              if (__glibc_unlikely (malloc_home_dir))
++                free (home_dir);
++            }
++          dirname_modified = 1;
++        }
+       else
+-	{
++        {
+ #ifndef WINDOWS32
+-	  char *end_name = strchr (dirname, '/');
+-	  char *user_name;
+-	  int malloc_user_name = 0;
+-	  char *unescape = NULL;
+-
+-	  if (!(flags & GLOB_NOESCAPE))
+-	    {
+-	      if (end_name == NULL)
+-		{
+-		  unescape = strchr (dirname, '\\');
+-		  if (unescape)
+-		    end_name = strchr (unescape, '\0');
+-		}
+-	      else
+-		unescape = memchr (dirname, '\\', end_name - dirname);
+-	    }
+-	  if (end_name == NULL)
+-	    user_name = dirname + 1;
+-	  else
+-	    {
+-	      char *newp;
+-	      if (glob_use_alloca (alloca_used, end_name - dirname))
+-		newp = alloca_account (end_name - dirname, alloca_used);
+-	      else
+-		{
+-		  newp = malloc (end_name - dirname);
+-		  if (newp == NULL)
+-		    {
+-		      retval = GLOB_NOSPACE;
+-		      goto out;
+-		    }
+-		  malloc_user_name = 1;
+-		}
+-	      if (unescape != NULL)
+-		{
+-		  char *p = mempcpy (newp, dirname + 1,
+-				     unescape - dirname - 1);
+-		  char *q = unescape;
+-		  while (q != end_name)
+-		    {
+-		      if (*q == '\\')
+-			{
+-			  if (q + 1 == end_name)
+-			    {
+-			      /* "~fo\\o\\" unescape to user_name "foo\\",
+-				 but "~fo\\o\\/" unescape to user_name
+-				 "foo".  */
+-			      if (filename == NULL)
+-				*p++ = '\\';
+-			      break;
+-			    }
+-			  ++q;
+-			}
+-		      *p++ = *q++;
+-		    }
+-		  *p = '\0';
+-		}
+-	      else
+-		*((char *) mempcpy (newp, dirname + 1, end_name - dirname - 1))
+-		  = '\0';
+-	      user_name = newp;
+-	    }
+-
+-	  /* Look up specific user's home directory.  */
+-	  {
+-	    struct passwd *p;
+-	    struct scratch_buffer pwtmpbuf;
+-	    scratch_buffer_init (&pwtmpbuf);
++          /* Recognize ~user as a shorthand for the specified user's home
++             directory.  */
++          char *end_name = strchr (dirname, '/');
++          char *user_name;
++          int malloc_user_name = 0;
++          char *unescape = NULL;
++
++          if (!(flags & GLOB_NOESCAPE))
++            {
++              if (end_name == NULL)
++                {
++                  unescape = strchr (dirname, '\\');
++                  if (unescape)
++                    end_name = strchr (unescape, '\0');
++                }
++              else
++                unescape = memchr (dirname, '\\', end_name - dirname);
++            }
++          if (end_name == NULL)
++            user_name = dirname + 1;
++          else
++            {
++              char *newp;
++              if (glob_use_alloca (alloca_used, end_name - dirname))
++                newp = alloca_account (end_name - dirname, alloca_used);
++              else
++                {
++                  newp = malloc (end_name - dirname);
++                  if (newp == NULL)
++                    {
++                      retval = GLOB_NOSPACE;
++                      goto out;
++                    }
++                  malloc_user_name = 1;
++                }
++              if (unescape != NULL)
++                {
++                  char *p = mempcpy (newp, dirname + 1,
++                                     unescape - dirname - 1);
++                  char *q = unescape;
++                  while (q != end_name)
++                    {
++                      if (*q == '\\')
++                        {
++                          if (q + 1 == end_name)
++                            {
++                              /* "~fo\\o\\" unescape to user_name "foo\\",
++                                 but "~fo\\o\\/" unescape to user_name
++                                 "foo".  */
++                              if (filename == NULL)
++                                *p++ = '\\';
++                              break;
++                            }
++                          ++q;
++                        }
++                      *p++ = *q++;
++                    }
++                  *p = '\0';
++                }
++              else
++                *((char *) mempcpy (newp, dirname + 1, end_name - dirname - 1))
++                  = '\0';
++              user_name = newp;
++            }
++
++          /* Look up specific user's home directory.  */
++          {
++            struct passwd *p;
++            struct scratch_buffer pwtmpbuf;
++            scratch_buffer_init (&pwtmpbuf);
+ 
+ #  if defined HAVE_GETPWNAM_R || defined _LIBC
+-	    struct passwd pwbuf;
++            struct passwd pwbuf;
+ 
+-	    while (getpwnam_r (user_name, &pwbuf,
+-			       pwtmpbuf.data, pwtmpbuf.length, &p)
+-		   == ERANGE)
+-	      {
+-		if (!scratch_buffer_grow (&pwtmpbuf))
+-		  {
+-		    retval = GLOB_NOSPACE;
+-		    goto out;
+-		  }
+-	      }
++            while (getpwnam_r (user_name, &pwbuf,
++                               pwtmpbuf.data, pwtmpbuf.length, &p)
++                   == ERANGE)
++              {
++                if (!scratch_buffer_grow (&pwtmpbuf))
++                  {
++                    retval = GLOB_NOSPACE;
++                    goto out;
++                  }
++              }
+ #  else
+-	    p = getpwnam (user_name);
++            p = getpwnam (user_name);
+ #  endif
+ 
+-	    if (__glibc_unlikely (malloc_user_name))
+-	      free (user_name);
++            if (__glibc_unlikely (malloc_user_name))
++              free (user_name);
+ 
+-	    /* If we found a home directory use this.  */
+-	    if (p != NULL)
+-	      {
+-		size_t home_len = strlen (p->pw_dir);
+-		size_t rest_len = end_name == NULL ? 0 : strlen (end_name);
+-		char *d, *newp;
+-		bool use_alloca = glob_use_alloca (alloca_used,
+-						   home_len + rest_len + 1);
+-
+-		if (use_alloca)
+-		  newp = alloca_account (home_len + rest_len + 1, alloca_used);
+-		else
+-		  {
+-		    newp = malloc (home_len + rest_len + 1);
+-		    if (newp == NULL)
+-		      {
+-			scratch_buffer_free (&pwtmpbuf);
+-			retval = GLOB_NOSPACE;
+-			goto out;
+-		      }
+-		  }
+-		d = mempcpy (newp, p->pw_dir, home_len);
+-		if (end_name != NULL)
+-		  d = mempcpy (d, end_name, rest_len);
+-		*d = '\0';
+-
+-		if (__glibc_unlikely (malloc_dirname))
+-		  free (dirname);
+-		dirname = newp;
+-		malloc_dirname = !use_alloca;
+-
+-		dirlen = home_len + rest_len;
+-		dirname_modified = 1;
+-	      }
+-	    else
+-	      {
+-		if (flags & GLOB_TILDE_CHECK)
+-		  {
+-		    /* We have to regard it as an error if we cannot find the
+-		       home directory.  */
+-		    retval = GLOB_NOMATCH;
+-		    goto out;
+-		  }
+-	      }
+-	    scratch_buffer_free (&pwtmpbuf);
+-	  }
+-#endif /* !WINDOWS32 */
+-	}
++            /* If we found a home directory use this.  */
++            if (p != NULL)
++              {
++                size_t home_len = strlen (p->pw_dir);
++                size_t rest_len = end_name == NULL ? 0 : strlen (end_name);
++                /* dirname contains end_name; we can't free it now.  */
++                char *prev_dirname =
++                  (__glibc_unlikely (malloc_dirname) ? dirname : NULL);
++                char *d;
++
++                malloc_dirname = 0;
++
++                if (glob_use_alloca (alloca_used, home_len + rest_len + 1))
++                  dirname = alloca_account (home_len + rest_len + 1,
++                                            alloca_used);
++                else
++                  {
++                    dirname = malloc (home_len + rest_len + 1);
++                    if (dirname == NULL)
++                      {
++                        free (prev_dirname);
++                        scratch_buffer_free (&pwtmpbuf);
++                        retval = GLOB_NOSPACE;
++                        goto out;
++                      }
++                    malloc_dirname = 1;
++                  }
++                d = mempcpy (dirname, p->pw_dir, home_len);
++                if (end_name != NULL)
++                  d = mempcpy (d, end_name, rest_len);
++                *d = '\0';
++
++                free (prev_dirname);
++
++                dirlen = home_len + rest_len;
++                dirname_modified = 1;
++              }
++            else
++              {
++                if (flags & GLOB_TILDE_CHECK)
++                  {
++                    /* We have to regard it as an error if we cannot find the
++                       home directory.  */
++                    retval = GLOB_NOMATCH;
++                    goto out;
++                  }
++              }
++            scratch_buffer_free (&pwtmpbuf);
++          }
++#else /* WINDOWS32 */
++          /* On native Windows, access to a user's home directory
++             (via GetUserProfileDirectory) or to a user's environment
++             variables (via ExpandEnvironmentStringsForUser) requires
++             the credentials of the user.  Therefore we cannot support
++             the ~user syntax on this platform.
++             Handling ~user specially (and treat it like plain ~) if
++             user is getenv ("USERNAME") would not be a good idea,
++             since it would make people think that ~user is supported
++             in general.  */
++          if (flags & GLOB_TILDE_CHECK)
++            {
++              retval = GLOB_NOMATCH;
++              goto out;
++            }
++#endif /* WINDOWS32 */
++        }
+     }
+ 
+   /* Now test whether we looked for "~" or "~NAME".  In this case we
+      can give the answer now.  */
+   if (filename == NULL)
+     {
+-	size_t newcount = pglob->gl_pathc + pglob->gl_offs;
+-	char **new_gl_pathv;
++      size_t newcount = pglob->gl_pathc + pglob->gl_offs;
++      char **new_gl_pathv;
++
++      if (newcount > SIZE_MAX / sizeof (char *) - 2)
++        {
++        nospace:
++          free (pglob->gl_pathv);
++          pglob->gl_pathv = NULL;
++          pglob->gl_pathc = 0;
++          retval = GLOB_NOSPACE;
++          goto out;
++        }
+ 
+-	if (newcount > SIZE_MAX / sizeof (char *) - 2)
+-	  {
+-	  nospace:
+-	    free (pglob->gl_pathv);
+-	    pglob->gl_pathv = NULL;
+-	    pglob->gl_pathc = 0;
+-	    retval = GLOB_NOSPACE;
+-	    goto out;
+-	  }
+-
+-	new_gl_pathv = realloc (pglob->gl_pathv,
+-				(newcount + 2) * sizeof (char *));
+-	if (new_gl_pathv == NULL)
+-	  goto nospace;
+-	pglob->gl_pathv = new_gl_pathv;
+-
+-	if (flags & GLOB_MARK && is_dir (dirname, flags, pglob))
+-	  {
+-	    char *p;
+-	    pglob->gl_pathv[newcount] = malloc (dirlen + 2);
+-	    if (pglob->gl_pathv[newcount] == NULL)
+-	      goto nospace;
+-	    p = mempcpy (pglob->gl_pathv[newcount], dirname, dirlen);
+-	    p[0] = '/';
+-	    p[1] = '\0';
+-	    if (__glibc_unlikely (malloc_dirname))
+-	      free (dirname);
+-	  }
+-	else
+-	  {
+-	    if (__glibc_unlikely (malloc_dirname))
+-	      pglob->gl_pathv[newcount] = dirname;
+-	    else
+-	      {
+-		pglob->gl_pathv[newcount] = strdup (dirname);
+-		if (pglob->gl_pathv[newcount] == NULL)
+-		  goto nospace;
+-	      }
+-	  }
+-	pglob->gl_pathv[++newcount] = NULL;
+-	++pglob->gl_pathc;
+-	pglob->gl_flags = flags;
++      new_gl_pathv = realloc (pglob->gl_pathv,
++                              (newcount + 2) * sizeof (char *));
++      if (new_gl_pathv == NULL)
++        goto nospace;
++      pglob->gl_pathv = new_gl_pathv;
++
++      if (flags & GLOB_MARK && is_dir (dirname, flags, pglob))
++        {
++          char *p;
++          pglob->gl_pathv[newcount] = malloc (dirlen + 2);
++          if (pglob->gl_pathv[newcount] == NULL)
++            goto nospace;
++          p = mempcpy (pglob->gl_pathv[newcount], dirname, dirlen);
++          p[0] = '/';
++          p[1] = '\0';
++          if (__glibc_unlikely (malloc_dirname))
++            free (dirname);
++        }
++      else
++        {
++          if (__glibc_unlikely (malloc_dirname))
++            pglob->gl_pathv[newcount] = dirname;
++          else
++            {
++              pglob->gl_pathv[newcount] = strdup (dirname);
++              if (pglob->gl_pathv[newcount] == NULL)
++                goto nospace;
++            }
++        }
++      pglob->gl_pathv[++newcount] = NULL;
++      ++pglob->gl_pathc;
++      pglob->gl_flags = flags;
+ 
+-	return 0;
++      return 0;
+     }
+ 
+   meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE));
+@@ -934,135 +969,135 @@ __glob (const char *pattern, int flags,
+   if (meta & (GLOBPAT_SPECIAL | GLOBPAT_BRACKET))
+     {
+       /* The directory name contains metacharacters, so we
+-	 have to glob for the directory, and then glob for
+-	 the pattern in each directory found.  */
++         have to glob for the directory, and then glob for
++         the pattern in each directory found.  */
+       size_t i;
+ 
+       if (!(flags & GLOB_NOESCAPE) && dirlen > 0 && dirname[dirlen - 1] == '\\')
+-	{
+-	  /* "foo\\/bar".  Remove the final backslash from dirname
+-	     if it has not been quoted.  */
+-	  char *p = (char *) &dirname[dirlen - 1];
+-
+-	  while (p > dirname && p[-1] == '\\') --p;
+-	  if ((&dirname[dirlen] - p) & 1)
+-	    *(char *) &dirname[--dirlen] = '\0';
+-	}
++        {
++          /* "foo\\/bar".  Remove the final backslash from dirname
++             if it has not been quoted.  */
++          char *p = (char *) &dirname[dirlen - 1];
++
++          while (p > dirname && p[-1] == '\\') --p;
++          if ((&dirname[dirlen] - p) & 1)
++            *(char *) &dirname[--dirlen] = '\0';
++        }
+ 
+       if (__glibc_unlikely ((flags & GLOB_ALTDIRFUNC) != 0))
+-	{
+-	  /* Use the alternative access functions also in the recursive
+-	     call.  */
+-	  dirs.gl_opendir = pglob->gl_opendir;
+-	  dirs.gl_readdir = pglob->gl_readdir;
+-	  dirs.gl_closedir = pglob->gl_closedir;
+-	  dirs.gl_stat = pglob->gl_stat;
+-	  dirs.gl_lstat = pglob->gl_lstat;
+-	}
++        {
++          /* Use the alternative access functions also in the recursive
++             call.  */
++          dirs.gl_opendir = pglob->gl_opendir;
++          dirs.gl_readdir = pglob->gl_readdir;
++          dirs.gl_closedir = pglob->gl_closedir;
++          dirs.gl_stat = pglob->gl_stat;
++          dirs.gl_lstat = pglob->gl_lstat;
++        }
+ 
+       status = __glob (dirname,
+-		       ((flags & (GLOB_ERR | GLOB_NOESCAPE | GLOB_ALTDIRFUNC))
+-			| GLOB_NOSORT | GLOB_ONLYDIR),
+-		       errfunc, &dirs);
++                       ((flags & (GLOB_ERR | GLOB_NOESCAPE | GLOB_ALTDIRFUNC))
++                        | GLOB_NOSORT | GLOB_ONLYDIR),
++                       errfunc, &dirs);
+       if (status != 0)
+-	{
+-	  if ((flags & GLOB_NOCHECK) == 0 || status != GLOB_NOMATCH)
+-	    {
+-	      retval = status;
+-	      goto out;
+-	    }
+-	  goto no_matches;
+-	}
++        {
++          if ((flags & GLOB_NOCHECK) == 0 || status != GLOB_NOMATCH)
++            {
++              retval = status;
++              goto out;
++            }
++          goto no_matches;
++        }
+ 
+       /* We have successfully globbed the preceding directory name.
+-	 For each name we found, call glob_in_dir on it and FILENAME,
+-	 appending the results to PGLOB.  */
++         For each name we found, call glob_in_dir on it and FILENAME,
++         appending the results to PGLOB.  */
+       for (i = 0; i < dirs.gl_pathc; ++i)
+-	{
+-	  size_t old_pathc;
++        {
++          size_t old_pathc;
+ 
+-	  old_pathc = pglob->gl_pathc;
+-	  status = glob_in_dir (filename, dirs.gl_pathv[i],
+-				((flags | GLOB_APPEND)
+-				 & ~(GLOB_NOCHECK | GLOB_NOMAGIC)),
+-				errfunc, pglob, alloca_used);
+-	  if (status == GLOB_NOMATCH)
+-	    /* No matches in this directory.  Try the next.  */
+-	    continue;
+-
+-	  if (status != 0)
+-	    {
+-	      globfree (&dirs);
+-	      globfree (pglob);
+-	      pglob->gl_pathc = 0;
+-	      retval = status;
+-	      goto out;
+-	    }
+-
+-	  /* Stick the directory on the front of each name.  */
+-	  if (prefix_array (dirs.gl_pathv[i],
+-			    &pglob->gl_pathv[old_pathc + pglob->gl_offs],
+-			    pglob->gl_pathc - old_pathc))
+-	    {
+-	      globfree (&dirs);
+-	      globfree (pglob);
+-	      pglob->gl_pathc = 0;
+-	      retval = GLOB_NOSPACE;
+-	      goto out;
+-	    }
+-	}
++          old_pathc = pglob->gl_pathc;
++          status = glob_in_dir (filename, dirs.gl_pathv[i],
++                                ((flags | GLOB_APPEND)
++                                 & ~(GLOB_NOCHECK | GLOB_NOMAGIC)),
++                                errfunc, pglob, alloca_used);
++          if (status == GLOB_NOMATCH)
++            /* No matches in this directory.  Try the next.  */
++            continue;
++
++          if (status != 0)
++            {
++              globfree (&dirs);
++              globfree (pglob);
++              pglob->gl_pathc = 0;
++              retval = status;
++              goto out;
++            }
++
++          /* Stick the directory on the front of each name.  */
++          if (prefix_array (dirs.gl_pathv[i],
++                            &pglob->gl_pathv[old_pathc + pglob->gl_offs],
++                            pglob->gl_pathc - old_pathc))
++            {
++              globfree (&dirs);
++              globfree (pglob);
++              pglob->gl_pathc = 0;
++              retval = GLOB_NOSPACE;
++              goto out;
++            }
++        }
+ 
+       flags |= GLOB_MAGCHAR;
+ 
+       /* We have ignored the GLOB_NOCHECK flag in the 'glob_in_dir' calls.
+-	 But if we have not found any matching entry and the GLOB_NOCHECK
+-	 flag was set we must return the input pattern itself.  */
++         But if we have not found any matching entry and the GLOB_NOCHECK
++         flag was set we must return the input pattern itself.  */
+       if (pglob->gl_pathc + pglob->gl_offs == oldcount)
+-	{
+-	no_matches:
+-	  /* No matches.  */
+-	  if (flags & GLOB_NOCHECK)
+-	    {
+-	      size_t newcount = pglob->gl_pathc + pglob->gl_offs;
+-	      char **new_gl_pathv;
+-
+-	      if (newcount > SIZE_MAX / sizeof (char *) - 2)
+-		{
+-		nospace2:
+-		  globfree (&dirs);
+-		  retval = GLOB_NOSPACE;
+-		  goto out;
+-		}
+-
+-	      new_gl_pathv = realloc (pglob->gl_pathv,
+-				      (newcount + 2) * sizeof (char *));
+-	      if (new_gl_pathv == NULL)
+-		goto nospace2;
+-	      pglob->gl_pathv = new_gl_pathv;
+-
+-	      pglob->gl_pathv[newcount] = strdup (pattern);
+-	      if (pglob->gl_pathv[newcount] == NULL)
+-		{
+-		  globfree (&dirs);
+-		  globfree (pglob);
+-		  pglob->gl_pathc = 0;
+-		  retval = GLOB_NOSPACE;
+-		  goto out;
+-		}
+-
+-	      ++pglob->gl_pathc;
+-	      ++newcount;
+-
+-	      pglob->gl_pathv[newcount] = NULL;
+-	      pglob->gl_flags = flags;
+-	    }
+-	  else
+-	    {
+-	      globfree (&dirs);
+-	      retval = GLOB_NOMATCH;
+-	      goto out;
+-	    }
+-	}
++        {
++        no_matches:
++          /* No matches.  */
++          if (flags & GLOB_NOCHECK)
++            {
++              size_t newcount = pglob->gl_pathc + pglob->gl_offs;
++              char **new_gl_pathv;
++
++              if (newcount > SIZE_MAX / sizeof (char *) - 2)
++                {
++                nospace2:
++                  globfree (&dirs);
++                  retval = GLOB_NOSPACE;
++                  goto out;
++                }
++
++              new_gl_pathv = realloc (pglob->gl_pathv,
++                                      (newcount + 2) * sizeof (char *));
++              if (new_gl_pathv == NULL)
++                goto nospace2;
++              pglob->gl_pathv = new_gl_pathv;
++
++              pglob->gl_pathv[newcount] = strdup (pattern);
++              if (pglob->gl_pathv[newcount] == NULL)
++                {
++                  globfree (&dirs);
++                  globfree (pglob);
++                  pglob->gl_pathc = 0;
++                  retval = GLOB_NOSPACE;
++                  goto out;
++                }
++
++              ++pglob->gl_pathc;
++              ++newcount;
++
++              pglob->gl_pathv[newcount] = NULL;
++              pglob->gl_flags = flags;
++            }
++          else
++            {
++              globfree (&dirs);
++              retval = GLOB_NOMATCH;
++              goto out;
++            }
++        }
+ 
+       globfree (&dirs);
+     }
+@@ -1072,57 +1107,57 @@ __glob (const char *pattern, int flags,
+       int orig_flags = flags;
+ 
+       if (meta & GLOBPAT_BACKSLASH)
+-	{
+-	  char *p = strchr (dirname, '\\'), *q;
+-	  /* We need to unescape the dirname string.  It is certainly
+-	     allocated by alloca, as otherwise filename would be NULL
+-	     or dirname wouldn't contain backslashes.  */
+-	  q = p;
+-	  do
+-	    {
+-	      if (*p == '\\')
+-		{
+-		  *q = *++p;
+-		  --dirlen;
+-		}
+-	      else
+-		*q = *p;
+-	      ++q;
+-	    }
+-	  while (*p++ != '\0');
+-	  dirname_modified = 1;
+-	}
++        {
++          char *p = strchr (dirname, '\\'), *q;
++          /* We need to unescape the dirname string.  It is certainly
++             allocated by alloca, as otherwise filename would be NULL
++             or dirname wouldn't contain backslashes.  */
++          q = p;
++          do
++            {
++              if (*p == '\\')
++                {
++                  *q = *++p;
++                  --dirlen;
++                }
++              else
++                *q = *p;
++              ++q;
++            }
++          while (*p++ != '\0');
++          dirname_modified = 1;
++        }
+       if (dirname_modified)
+-	flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC);
++        flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC);
+       status = glob_in_dir (filename, dirname, flags, errfunc, pglob,
+-			    alloca_used);
++                            alloca_used);
+       if (status != 0)
+-	{
+-	  if (status == GLOB_NOMATCH && flags != orig_flags
+-	      && pglob->gl_pathc + pglob->gl_offs == oldcount)
+-	    {
+-	      /* Make sure globfree (&dirs); is a nop.  */
+-	      dirs.gl_pathv = NULL;
+-	      flags = orig_flags;
+-	      goto no_matches;
+-	    }
+-	  retval = status;
+-	  goto out;
+-	}
++        {
++          if (status == GLOB_NOMATCH && flags != orig_flags
++              && pglob->gl_pathc + pglob->gl_offs == oldcount)
++            {
++              /* Make sure globfree (&dirs); is a nop.  */
++              dirs.gl_pathv = NULL;
++              flags = orig_flags;
++              goto no_matches;
++            }
++          retval = status;
++          goto out;
++        }
+ 
+       if (dirlen > 0)
+-	{
+-	  /* Stick the directory on the front of each name.  */
+-	  if (prefix_array (dirname,
+-			    &pglob->gl_pathv[old_pathc + pglob->gl_offs],
+-			    pglob->gl_pathc - old_pathc))
+-	    {
+-	      globfree (pglob);
+-	      pglob->gl_pathc = 0;
+-	      retval = GLOB_NOSPACE;
+-	      goto out;
+-	    }
+-	}
++        {
++          /* Stick the directory on the front of each name.  */
++          if (prefix_array (dirname,
++                            &pglob->gl_pathv[old_pathc + pglob->gl_offs],
++                            pglob->gl_pathc - old_pathc))
++            {
++              globfree (pglob);
++              pglob->gl_pathc = 0;
++              retval = GLOB_NOSPACE;
++              goto out;
++            }
++        }
+     }
+ 
+   if (flags & GLOB_MARK)
+@@ -1131,28 +1166,28 @@ __glob (const char *pattern, int flags,
+       size_t i;
+ 
+       for (i = oldcount; i < pglob->gl_pathc + pglob->gl_offs; ++i)
+-	if (is_dir (pglob->gl_pathv[i], flags, pglob))
+-	  {
+-	    size_t len = strlen (pglob->gl_pathv[i]) + 2;
+-	    char *new = realloc (pglob->gl_pathv[i], len);
+-	    if (new == NULL)
+-	      {
+-		globfree (pglob);
+-		pglob->gl_pathc = 0;
+-		retval = GLOB_NOSPACE;
+-		goto out;
+-	      }
+-	    strcpy (&new[len - 2], "/");
+-	    pglob->gl_pathv[i] = new;
+-	  }
++        if (is_dir (pglob->gl_pathv[i], flags, pglob))
++          {
++            size_t len = strlen (pglob->gl_pathv[i]) + 2;
++            char *new = realloc (pglob->gl_pathv[i], len);
++            if (new == NULL)
++              {
++                globfree (pglob);
++                pglob->gl_pathc = 0;
++                retval = GLOB_NOSPACE;
++                goto out;
++              }
++            strcpy (&new[len - 2], "/");
++            pglob->gl_pathv[i] = new;
++          }
+     }
+ 
+   if (!(flags & GLOB_NOSORT))
+     {
+       /* Sort the vector.  */
+       qsort (&pglob->gl_pathv[oldcount],
+-	     pglob->gl_pathc + pglob->gl_offs - oldcount,
+-	     sizeof (char *), collated_compare);
++             pglob->gl_pathc + pglob->gl_offs - oldcount,
++             sizeof (char *), collated_compare);
+     }
+ 
+  out:
+@@ -1204,14 +1239,14 @@ prefix_array (const char *dirname, char
+   if (dirlen > 1)
+     {
+       if (dirname[dirlen - 1] == '/' && dirname[dirlen - 2] == ':')
+-	/* DIRNAME is "d:/".  Don't prepend the slash from DIRNAME.  */
+-	--dirlen;
++        /* DIRNAME is "d:/".  Don't prepend the slash from DIRNAME.  */
++        --dirlen;
+       else if (dirname[dirlen - 1] == ':')
+-	{
+-	  /* DIRNAME is "d:".  Use ':' instead of '/'.  */
+-	  --dirlen;
+-	  dirsep_char = ':';
+-	}
++        {
++          /* DIRNAME is "d:".  Use ':' instead of '/'.  */
++          --dirlen;
++          dirsep_char = ':';
++        }
+     }
+ #endif
+ 
+@@ -1220,16 +1255,16 @@ prefix_array (const char *dirname, char
+       size_t eltlen = strlen (array[i]) + 1;
+       char *new = malloc (dirlen + 1 + eltlen);
+       if (new == NULL)
+-	{
+-	  while (i > 0)
+-	    free (array[--i]);
+-	  return 1;
+-	}
++        {
++          while (i > 0)
++            free (array[--i]);
++          return 1;
++        }
+ 
+       {
+-	char *endp = mempcpy (new, dirname, dirlen);
+-	*endp++ = dirsep_char;
+-	mempcpy (endp, array[i], eltlen);
++        char *endp = mempcpy (new, dirname, dirlen);
++        *endp++ = dirsep_char;
++        mempcpy (endp, array[i], eltlen);
+       }
+       free (array[i]);
+       array[i] = new;
+@@ -1244,11 +1279,13 @@ prefix_array (const char *dirname, char
+    The GLOB_APPEND flag is assumed to be set (always appends).  */
+ static int
+ glob_in_dir (const char *pattern, const char *directory, int flags,
+-	     int (*errfunc) (const char *, int),
+-	     glob_t *pglob, size_t alloca_used)
++             int (*errfunc) (const char *, int),
++             glob_t *pglob, size_t alloca_used)
+ {
+   size_t dirlen = strlen (directory);
+   void *stream = NULL;
++  struct scratch_buffer s;
++  scratch_buffer_init (&s);
+ # define GLOBNAMES_MEMBERS(nnames) \
+     struct globnames *next; size_t count; char *name[nnames];
+   struct globnames { GLOBNAMES_MEMBERS (FLEXIBLE_ARRAY_MEMBER) };
+@@ -1273,8 +1310,8 @@ glob_in_dir (const char *pattern, const
+   if (meta == GLOBPAT_NONE && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
+     {
+       /* We need not do any tests.  The PATTERN contains no meta
+-	 characters and we must not return an error therefore the
+-	 result will always contain exactly one name.  */
++         characters and we must not return an error therefore the
++         result will always contain exactly one name.  */
+       flags |= GLOB_NOCHECK;
+     }
+   else if (meta == GLOBPAT_NONE)
+@@ -1288,102 +1325,127 @@ glob_in_dir (const char *pattern, const
+       if (alloca_fullname)
+         fullname = alloca_account (fullsize, alloca_used);
+       else
+-	{
+-	  fullname = malloc (fullsize);
+-	  if (fullname == NULL)
+-	    return GLOB_NOSPACE;
+-	}
++        {
++          fullname = malloc (fullsize);
++          if (fullname == NULL)
++            return GLOB_NOSPACE;
++        }
+ 
+       mempcpy (mempcpy (mempcpy (fullname, directory, dirlen),
+-			"/", 1),
+-	       pattern, patlen + 1);
++                        "/", 1),
++               pattern, patlen + 1);
+       if (glob_lstat (pglob, flags, fullname) == 0
+-	  || errno == EOVERFLOW)
+-	/* We found this file to be existing.  Now tell the rest
+-	   of the function to copy this name into the result.  */
+-	flags |= GLOB_NOCHECK;
++          || errno == EOVERFLOW)
++        /* We found this file to be existing.  Now tell the rest
++           of the function to copy this name into the result.  */
++        flags |= GLOB_NOCHECK;
+ 
+       if (__glibc_unlikely (!alloca_fullname))
+-	free (fullname);
++        free (fullname);
+     }
+   else
+     {
+       stream = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)
+-		? (*pglob->gl_opendir) (directory)
+-		: opendir (directory));
++                ? (*pglob->gl_opendir) (directory)
++                : opendir (directory));
+       if (stream == NULL)
+-	{
+-	  if (errno != ENOTDIR
+-	      && ((errfunc != NULL && (*errfunc) (directory, errno))
+-		  || (flags & GLOB_ERR)))
+-	    return GLOB_ABORTED;
+-	}
++        {
++          if (errno != ENOTDIR
++              && ((errfunc != NULL && (*errfunc) (directory, errno))
++                  || (flags & GLOB_ERR)))
++            return GLOB_ABORTED;
++        }
+       else
+-	{
+-	  int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0)
+-			   | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0));
+-	  flags |= GLOB_MAGCHAR;
+-
+-	  while (1)
+-	    {
+-	      struct readdir_result d;
+-	      {
+-		if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
+-		  d = convert_dirent (GL_READDIR (pglob, stream));
+-		else
+-		  {
++        {
++          int dfd = dirfd (stream);
++          int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0)
++                           | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0));
++          flags |= GLOB_MAGCHAR;
++
++          while (1)
++            {
++              struct readdir_result d;
++              {
++                if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0))
++                  d = convert_dirent (GL_READDIR (pglob, stream));
++                else
++                  {
+ #ifdef COMPILE_GLOB64
+-		    d = convert_dirent (__readdir (stream));
++                    d = convert_dirent (__readdir (stream));
+ #else
+-		    d = convert_dirent64 (__readdir64 (stream));
++                    d = convert_dirent64 (__readdir64 (stream));
+ #endif
+-		  }
+-	      }
+-	      if (d.name == NULL)
+-		break;
+-
+-	      /* If we shall match only directories use the information
+-		 provided by the dirent call if possible.  */
+-	      if (flags & GLOB_ONLYDIR)
+-		switch (readdir_result_type (d))
+-		  {
+-		  case DT_DIR: case DT_LNK: case DT_UNKNOWN: break;
+-		  default: continue;
+-		  }
+-
+-	      if (fnmatch (pattern, d.name, fnm_flags) == 0)
+-		{
+-		  if (cur == names->count)
+-		    {
+-		      struct globnames *newnames;
+-		      size_t count = names->count * 2;
+-		      size_t nameoff = offsetof (struct globnames, name);
+-		      size_t size = FLEXSIZEOF (struct globnames, name,
+-						count * sizeof (char *));
+-		      if ((SIZE_MAX - nameoff) / 2 / sizeof (char *)
+-			  < names->count)
+-			goto memory_error;
+-		      if (glob_use_alloca (alloca_used, size))
+-			newnames = names_alloca
+-			  = alloca_account (size, alloca_used);
+-		      else if ((newnames = malloc (size))
+-			       == NULL)
+-			goto memory_error;
+-		      newnames->count = count;
+-		      newnames->next = names;
+-		      names = newnames;
+-		      cur = 0;
+-		    }
+-		  names->name[cur] = strdup (d.name);
+-		  if (names->name[cur] == NULL)
+-		    goto memory_error;
+-		  ++cur;
+-		  ++nfound;
+-		  if (SIZE_MAX - pglob->gl_offs <= nfound)
+-		    goto memory_error;
+-		}
+-	    }
+-	}
++                  }
++              }
++              if (d.name == NULL)
++                break;
++
++              /* If we shall match only directories use the information
++                 provided by the dirent call if possible.  */
++              if (flags & GLOB_ONLYDIR)
++                switch (readdir_result_type (d))
++                  {
++                  default: continue;
++                  case DT_DIR: break;
++                  case DT_LNK: case DT_UNKNOWN:
++                    /* The filesystem was too lazy to give us a hint,
++                       so we have to do it the hard way.  */
++                    if (__glibc_unlikely (dfd < 0 || flags & GLOB_ALTDIRFUNC))
++                      {
++                        size_t namelen = strlen (d.name);
++                        size_t need = dirlen + 1 + namelen + 1;
++                        if (s.length < need
++                            && !scratch_buffer_set_array_size (&s, need, 1))
++                          goto memory_error;
++                        char *p = mempcpy (s.data, directory, dirlen);
++                        *p = '/';
++                        p += p[-1] != '/';
++                        memcpy (p, d.name, namelen + 1);
++                        if (! is_dir (s.data, flags, pglob))
++                          continue;
++                      }
++                    else
++                      {
++                        struct_stat64 st64;
++                        if (! (GLOB_FSTATAT64 (dfd, d.name, &st64, 0) == 0
++                               && S_ISDIR (st64.st_mode)))
++                          continue;
++                      }
++                  }
++
++              if (fnmatch (pattern, d.name, fnm_flags) == 0)
++                {
++                  if (cur == names->count)
++                    {
++                      struct globnames *newnames;
++                      size_t count = names->count * 2;
++                      size_t nameoff = offsetof (struct globnames, name);
++                      size_t size = FLEXSIZEOF (struct globnames, name,
++                                                count * sizeof (char *));
++                      if ((SIZE_MAX - nameoff) / 2 / sizeof (char *)
++                          < names->count)
++                        goto memory_error;
++                      if (glob_use_alloca (alloca_used, size))
++                        newnames = names_alloca
++                          = alloca_account (size, alloca_used);
++                      else if ((newnames = malloc (size))
++                               == NULL)
++                        goto memory_error;
++                      newnames->count = count;
++                      newnames->next = names;
++                      names = newnames;
++                      cur = 0;
++                    }
++                  names->name[cur] = strdup (d.name);
++                  if (names->name[cur] == NULL)
++                    goto memory_error;
++                  ++cur;
++                  ++nfound;
++                  if (SIZE_MAX - pglob->gl_offs <= nfound)
++                    goto memory_error;
++                }
++            }
++        }
+     }
+ 
+   if (nfound == 0 && (flags & GLOB_NOCHECK))
+@@ -1392,7 +1454,7 @@ glob_in_dir (const char *pattern, const
+       nfound = 1;
+       names->name[cur] = malloc (len + 1);
+       if (names->name[cur] == NULL)
+-	goto memory_error;
++        goto memory_error;
+       *((char *) mempcpy (names->name[cur++], pattern, len)) = '\0';
+     }
+ 
+@@ -1403,82 +1465,83 @@ glob_in_dir (const char *pattern, const
+       result = 0;
+ 
+       if (SIZE_MAX / sizeof (char *) - pglob->gl_pathc
+-	  < pglob->gl_offs + nfound + 1)
+-	goto memory_error;
++          < pglob->gl_offs + nfound + 1)
++        goto memory_error;
+ 
+       new_gl_pathv
+-	= realloc (pglob->gl_pathv,
+-		   (pglob->gl_pathc + pglob->gl_offs + nfound + 1)
+-		    * sizeof (char *));
++        = realloc (pglob->gl_pathv,
++                   (pglob->gl_pathc + pglob->gl_offs + nfound + 1)
++                    * sizeof (char *));
+ 
+       if (new_gl_pathv == NULL)
+-	{
+-	memory_error:
+-	  while (1)
+-	    {
+-	      struct globnames *old = names;
+-	      for (size_t i = 0; i < cur; ++i)
+-		free (names->name[i]);
+-	      names = names->next;
+-	      /* NB: we will not leak memory here if we exit without
+-		 freeing the current block assigned to OLD.  At least
+-		 the very first block is always allocated on the stack
+-		 and this is the block assigned to OLD here.  */
+-	      if (names == NULL)
+-		{
+-		  assert (old == init_names);
+-		  break;
+-		}
+-	      cur = names->count;
+-	      if (old == names_alloca)
+-		names_alloca = names;
+-	      else
+-		free (old);
+-	    }
+-	  result = GLOB_NOSPACE;
+-	}
++        {
++        memory_error:
++          while (1)
++            {
++              struct globnames *old = names;
++              for (size_t i = 0; i < cur; ++i)
++                free (names->name[i]);
++              names = names->next;
++              /* NB: we will not leak memory here if we exit without
++                 freeing the current block assigned to OLD.  At least
++                 the very first block is always allocated on the stack
++                 and this is the block assigned to OLD here.  */
++              if (names == NULL)
++                {
++                  assert (old == init_names);
++                  break;
++                }
++              cur = names->count;
++              if (old == names_alloca)
++                names_alloca = names;
++              else
++                free (old);
++            }
++          result = GLOB_NOSPACE;
++        }
+       else
+-	{
+-	  while (1)
+-	    {
+-	      struct globnames *old = names;
+-	      for (size_t i = 0; i < cur; ++i)
+-		new_gl_pathv[pglob->gl_offs + pglob->gl_pathc++]
+-		  = names->name[i];
+-	      names = names->next;
+-	      /* NB: we will not leak memory here if we exit without
+-		 freeing the current block assigned to OLD.  At least
+-		 the very first block is always allocated on the stack
+-		 and this is the block assigned to OLD here.  */
+-	      if (names == NULL)
+-		{
+-		  assert (old == init_names);
+-		  break;
+-		}
+-	      cur = names->count;
+-	      if (old == names_alloca)
+-		names_alloca = names;
+-	      else
+-		free (old);
+-	    }
++        {
++          while (1)
++            {
++              struct globnames *old = names;
++              for (size_t i = 0; i < cur; ++i)
++                new_gl_pathv[pglob->gl_offs + pglob->gl_pathc++]
++                  = names->name[i];
++              names = names->next;
++              /* NB: we will not leak memory here if we exit without
++                 freeing the current block assigned to OLD.  At least
++                 the very first block is always allocated on the stack
++                 and this is the block assigned to OLD here.  */
++              if (names == NULL)
++                {
++                  assert (old == init_names);
++                  break;
++                }
++              cur = names->count;
++              if (old == names_alloca)
++                names_alloca = names;
++              else
++                free (old);
++            }
+ 
+-	  pglob->gl_pathv = new_gl_pathv;
++          pglob->gl_pathv = new_gl_pathv;
+ 
+-	  pglob->gl_pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
++          pglob->gl_pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+ 
+-	  pglob->gl_flags = flags;
+-	}
++          pglob->gl_flags = flags;
++        }
+     }
+ 
+   if (stream != NULL)
+     {
+       save = errno;
+       if (__glibc_unlikely (flags & GLOB_ALTDIRFUNC))
+-	(*pglob->gl_closedir) (stream);
++        (*pglob->gl_closedir) (stream);
+       else
+-	closedir (stream);
++        closedir (stream);
+       __set_errno (save);
+     }
+ 
++  scratch_buffer_free (&s);
+   return result;
+ }
+diff -rup a/sysdeps/gnu/glob-lstat-compat.c b/sysdeps/gnu/glob-lstat-compat.c
+--- a/sysdeps/gnu/glob-lstat-compat.c	2018-08-01 01:10:47.000000000 -0400
++++ b/sysdeps/gnu/glob-lstat-compat.c	2022-05-02 17:51:04.167557574 -0400
+@@ -29,7 +29,8 @@
+ #define GLOB_ATTRIBUTE attribute_compat_text_section
+ 
+ /* Avoid calling gl_lstat with GLOB_ALTDIRFUNC.  */
+-#define GLOB_NO_LSTAT
++#define GLOB_LSTAT   gl_stat
++#define GLOB_LSTAT64 __stat64
+ 
+ #include <posix/glob.c>
+ 
+diff -rup a/sysdeps/unix/sysv/linux/glob-lstat-compat.c b/sysdeps/unix/sysv/linux/glob-lstat-compat.c
+--- a/sysdeps/unix/sysv/linux/glob-lstat-compat.c	2018-08-01 01:10:47.000000000 -0400
++++ b/sysdeps/unix/sysv/linux/glob-lstat-compat.c	2022-05-02 23:05:45.197297341 -0400
+@@ -30,7 +30,12 @@
+ #define GLOB_ATTRIBUTE attribute_compat_text_section
+ 
+ /* Avoid calling gl_lstat with GLOB_ALTDIRFUNC.  */
+-#define GLOB_NO_LSTAT
++# define COMPILE_GLOB64	1
++# define struct_stat    struct stat
++# define struct_stat64  struct stat64
++# define GLOB_LSTAT     gl_stat
++# define GLOB_STAT64    __stat64
++# define GLOB_LSTAT64   __stat64
+ 
+ #include <posix/glob.c>
+ 
diff --git a/SOURCES/glibc-rh2033684-1.patch b/SOURCES/glibc-rh2033684-1.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8734628d6447ba9501ee0e3dee67ec02684a113d
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-1.patch
@@ -0,0 +1,27 @@
+commit 2a08b6e8331a611dc29325bfa6e29fecc9a3a46e
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Thu Dec 10 16:47:02 2020 +0530
+
+    Warn on unsupported fortification levels
+    
+    Make the _FORTIFY_SOURCE macro soup in features.h warn about
+    unsupported fortification levels.  For example, it will warn about
+    _FORTIFY_SOURCE=3 and over with an indication of which level has been
+    selected.
+    
+    Co-authored-by: Paul Eggert <eggert@cs.ucla.edu>
+
+diff --git a/include/features.h b/include/features.h
+index 5bed0a499605a3a2..ea7673ee115bcf0a 100644
+--- a/include/features.h
++++ b/include/features.h
+@@ -382,6 +382,9 @@
+ # elif !__GNUC_PREREQ (4, 1)
+ #  warning _FORTIFY_SOURCE requires GCC 4.1 or later
+ # elif _FORTIFY_SOURCE > 1
++#  if _FORTIFY_SOURCE > 2
++#   warning _FORTIFY_SOURCE > 2 is treated like 2 on this platform
++#  endif
+ #  define __USE_FORTIFY_LEVEL 2
+ # else
+ #  define __USE_FORTIFY_LEVEL 1
diff --git a/SOURCES/glibc-rh2033684-10.patch b/SOURCES/glibc-rh2033684-10.patch
new file mode 100644
index 0000000000000000000000000000000000000000..94b45192c0c2e596452976ac8d5a2f4fe5f0b33d
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-10.patch
@@ -0,0 +1,90 @@
+commit 2bbd07c715275eb6c616988925738a0517180d57
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Fri Dec 17 18:35:44 2021 +0530
+
+    fortify: Fix spurious warning with realpath
+    
+    The length and object size arguments were swapped around for realpath.
+    Also add a smoke test so that any changes in this area get caught in
+    future.
+    
+    Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+    Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+
+diff --git a/debug/Makefile b/debug/Makefile
+index 81361438fc3d2aa9..b43f42ee3851f360 100644
+--- a/debug/Makefile
++++ b/debug/Makefile
+@@ -108,6 +108,7 @@ CFLAGS-tst-longjmp_chk2.c += -fexceptions -fasynchronous-unwind-tables
+ CPPFLAGS-tst-longjmp_chk2.c += -D_FORTIFY_SOURCE=1
+ CFLAGS-tst-longjmp_chk3.c += -fexceptions -fasynchronous-unwind-tables
+ CPPFLAGS-tst-longjmp_chk3.c += -D_FORTIFY_SOURCE=1
++CPPFLAGS-tst-realpath-chk.c += -D_FORTIFY_SOURCE=2
+ 
+ # We know these tests have problems with format strings, this is what
+ # we are testing.  Disable that warning.  They are also testing
+@@ -155,7 +156,7 @@ tests = backtrace-tst tst-longjmp_chk tst-chk1 tst-chk2 tst-chk3 \
+ 	tst-lfschk1 tst-lfschk2 tst-lfschk3 test-strcpy_chk test-stpcpy_chk \
+ 	tst-chk4 tst-chk5 tst-chk6 tst-chk7 tst-chk8 tst-lfschk4 tst-lfschk5 \
+ 	tst-lfschk6 tst-longjmp_chk2 tst-backtrace2 tst-backtrace3 \
+-	tst-backtrace4 tst-backtrace5 tst-backtrace6
++	tst-backtrace4 tst-backtrace5 tst-backtrace6 tst-realpath-chk
+ 
+ ifeq ($(have-ssp),yes)
+ tests += tst-ssp-1
+diff --git a/debug/tst-realpath-chk.c b/debug/tst-realpath-chk.c
+new file mode 100644
+index 0000000000000000..a8fcb327c43fb34d
+--- /dev/null
++++ b/debug/tst-realpath-chk.c
+@@ -0,0 +1,37 @@
++/* Smoke test to verify that realpath does not cause spurious warnings.
++   Copyright The GNU Toolchain Authors.
++   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/>.  */
++
++#include <limits.h>
++#include <stdlib.h>
++
++#include <support/check.h>
++#include <support/support.h>
++
++static int
++do_test (void)
++{
++#ifdef PATH_MAX
++  char buf[PATH_MAX + 1];
++  char *res = realpath (".", buf);
++  TEST_VERIFY (res == buf);
++#endif
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/stdlib/bits/stdlib.h b/stdlib/bits/stdlib.h
+index 7ea364a276497720..81ec9bdb32215e3b 100644
+--- a/stdlib/bits/stdlib.h
++++ b/stdlib/bits/stdlib.h
+@@ -42,7 +42,7 @@ __NTH (realpath (const char *__restrict __name, char *__restrict __resolved))
+     return __realpath_alias (__name, __resolved);
+ 
+ #if defined _LIBC_LIMITS_H_ && defined PATH_MAX
+-  if (__glibc_unsafe_len (sz, sizeof (char), PATH_MAX))
++  if (__glibc_unsafe_len (PATH_MAX, sizeof (char), sz))
+     return __realpath_chk_warn (__name, __resolved, sz);
+ #endif
+   return __realpath_chk (__name, __resolved, sz);
diff --git a/SOURCES/glibc-rh2033684-11.patch b/SOURCES/glibc-rh2033684-11.patch
new file mode 100644
index 0000000000000000000000000000000000000000..41ce66cf5c17a37e1a61e4d2e9ae2d11a335d5d2
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-11.patch
@@ -0,0 +1,41 @@
+commit 86bf0feb0e3ec8e37872f72499d6ae33406561d7
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Wed Jan 12 18:46:28 2022 +0530
+
+    Enable _FORTIFY_SOURCE=3 for gcc 12 and above
+    
+    gcc 12 now has support for the __builtin_dynamic_object_size builtin.
+    Adapt the macro checks to enable _FORTIFY_SOURCE=3 on gcc 12 and above.
+    
+    Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/include/features.h b/include/features.h
+index fe9fe16d034fad1b..2e9ca6ec2f4a0380 100644
+--- a/include/features.h
++++ b/include/features.h
+@@ -381,7 +381,9 @@
+ #  warning _FORTIFY_SOURCE requires compiling with optimization (-O)
+ # elif !__GNUC_PREREQ (4, 1)
+ #  warning _FORTIFY_SOURCE requires GCC 4.1 or later
+-# elif _FORTIFY_SOURCE > 2 && __glibc_clang_prereq (9, 0)
++# elif _FORTIFY_SOURCE > 2 && (__glibc_clang_prereq (9, 0)		      \
++			       || __GNUC_PREREQ (12, 0))
++
+ #  if _FORTIFY_SOURCE > 3
+ #   warning _FORTIFY_SOURCE > 3 is treated like 3 on this platform
+ #  endif
+diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
+index 147339957c4ad490..a17ae0ed87e6163f 100644
+--- a/misc/sys/cdefs.h
++++ b/misc/sys/cdefs.h
+@@ -124,7 +124,8 @@
+ #define __bos0(ptr) __builtin_object_size (ptr, 0)
+ 
+ /* Use __builtin_dynamic_object_size at _FORTIFY_SOURCE=3 when available.  */
+-#if __USE_FORTIFY_LEVEL == 3 && __glibc_clang_prereq (9, 0)
++#if __USE_FORTIFY_LEVEL == 3 && (__glibc_clang_prereq (9, 0)		      \
++				 || __GNUC_PREREQ (12, 0))
+ # define __glibc_objsize0(__o) __builtin_dynamic_object_size (__o, 0)
+ # define __glibc_objsize(__o) __builtin_dynamic_object_size (__o, 1)
+ #else
diff --git a/SOURCES/glibc-rh2033684-12.patch b/SOURCES/glibc-rh2033684-12.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9c9c98fd2cda69a04a47cd8b83f356e4dee58a99
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-12.patch
@@ -0,0 +1,295 @@
+commit db27f1251b008280a29d540b4f8ab2a38a0d80af
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Wed Jan 12 23:34:23 2022 +0530
+
+    debug: Autogenerate _FORTIFY_SOURCE tests
+    
+    Rename debug/tst-chk1.c to debug/tst-fortify.c and add make hackery to
+    autogenerate tests with different macros enabled to build and run the
+    same test with different configurations as well as different
+    fortification levels.
+    
+    The change also ends up expanding the -lfs tests to include
+    _FORTIFY_SOURCE=3.
+    
+    Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+# Conflicts:
+#	debug/Makefile
+
+diff --git a/Makerules b/Makerules
+index 5d6434c74bf9bfe5..05a549eb0f259113 100644
+--- a/Makerules
++++ b/Makerules
+@@ -444,6 +444,12 @@ $(objpfx)%$o: $(objpfx)%.c $(before-compile); $$(compile-command.c)
+ endef
+ object-suffixes-left := $(all-object-suffixes)
+ include $(o-iterator)
++
++define o-iterator-doit
++$(objpfx)%$o: $(objpfx)%.cc $(before-compile); $$(compile-command.cc)
++endef
++object-suffixes-left := $(all-object-suffixes)
++include $(o-iterator)
+ endif
+ 
+ # Generate version maps, but wait until sysdep-subdirs is known
+diff --git a/debug/Makefile b/debug/Makefile
+index b43f42ee3851f360..c92fd23dda1a7279 100644
+--- a/debug/Makefile
++++ b/debug/Makefile
+@@ -1,4 +1,5 @@
+-# Copyright (C) 1998-2018 Free Software Foundation, Inc.
++# Copyright (C) 1998-2022 Free Software Foundation, Inc.
++# Copyright The GNU Toolchain Authors.
+ # This file is part of the GNU C Library.
+ 
+ # The GNU C Library is free software; you can redistribute it and/or
+@@ -110,32 +111,60 @@ CFLAGS-tst-longjmp_chk3.c += -fexceptions -fasynchronous-unwind-tables
+ CPPFLAGS-tst-longjmp_chk3.c += -D_FORTIFY_SOURCE=1
+ CPPFLAGS-tst-realpath-chk.c += -D_FORTIFY_SOURCE=2
+ 
++# _FORTIFY_SOURCE tests.
++# Auto-generate tests for _FORTIFY_SOURCE for different levels, compilers and
++# preprocessor conditions based on tst-fortify.c.
++#
++# To add a new test condition, define a cflags-$(cond) make variable to set
++# CFLAGS for the file.
++
++tests-all-chk = tst-fortify
++tests-c-chk =
++tests-cc-chk =
++
++CFLAGS-tst-fortify.c += -Wno-format -Wno-deprecated-declarations -Wno-error
++
++# No additional flags for the default tests.
++define cflags-default
++endef
++
++define cflags-lfs
++CFLAGS-tst-fortify-$(1)-lfs-$(2).$(1) += -D_FILE_OFFSET_BITS=64
++endef
++
+ # We know these tests have problems with format strings, this is what
+ # we are testing.  Disable that warning.  They are also testing
+ # deprecated functions (notably gets) so disable that warning as well.
+ # And they also generate warnings from warning attributes, which
+ # cannot be disabled via pragmas, so require -Wno-error to be used.
+-CFLAGS-tst-chk1.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-chk2.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-chk3.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-chk4.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-chk5.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-chk6.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-chk7.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-chk8.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-lfschk1.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-lfschk2.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-lfschk3.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-lfschk4.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-lfschk5.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+-CFLAGS-tst-lfschk6.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+-LDLIBS-tst-chk4 = -lstdc++
+-LDLIBS-tst-chk5 = -lstdc++
+-LDLIBS-tst-chk6 = -lstdc++
+-LDLIBS-tst-chk8 = -lstdc++
+-LDLIBS-tst-lfschk4 = -lstdc++
+-LDLIBS-tst-lfschk5 = -lstdc++
+-LDLIBS-tst-lfschk6 = -lstdc++
++define gen-chk-test
++tests-$(1)-chk += tst-fortify-$(1)-$(2)-$(3)
++CFLAGS-tst-fortify-$(1)-$(2)-$(3).$(1) += -D_FORTIFY_SOURCE=$(3) -Wno-format \
++					  -Wno-deprecated-declarations \
++					  -Wno-error
++$(eval $(call cflags-$(2),$(1),$(3)))
++$(objpfx)tst-fortify-$(1)-$(2)-$(3).$(1): tst-fortify.c Makefile
++	( echo "/* Autogenerated from Makefile.  */"; \
++	  echo ""; \
++	  echo "#include \"tst-fortify.c\"" ) > $$@.tmp
++	mv $$@.tmp $$@
++endef
++
++chk-extensions = c cc
++chk-types = default lfs
++chk-levels = 1 2 3
++
++$(foreach e,$(chk-extensions), \
++  $(foreach t,$(chk-types), \
++    $(foreach l,$(chk-levels), \
++      $(eval $(call gen-chk-test,$(e),$(t),$(l))))))
++
++tests-all-chk += $(tests-c-chk) $(tests-cc-chk)
++
++define link-cc
++LDLIBS-$(1) = -lstdc++
++endef
++$(foreach t,$(tests-cc-chk), $(eval $(call link-cc,$(t))))
+ 
+ # backtrace_symbols only works if we link with -rdynamic.  backtrace
+ # requires unwind tables on most architectures.
+@@ -152,19 +181,25 @@ LDFLAGS-tst-backtrace6 = -rdynamic
+ 
+ CFLAGS-tst-ssp-1.c += -fstack-protector-all
+ 
+-tests = backtrace-tst tst-longjmp_chk tst-chk1 tst-chk2 tst-chk3 \
+-	tst-lfschk1 tst-lfschk2 tst-lfschk3 test-strcpy_chk test-stpcpy_chk \
+-	tst-chk4 tst-chk5 tst-chk6 tst-chk7 tst-chk8 tst-lfschk4 tst-lfschk5 \
+-	tst-lfschk6 tst-longjmp_chk2 tst-backtrace2 tst-backtrace3 \
+-	tst-backtrace4 tst-backtrace5 tst-backtrace6 tst-realpath-chk
++tests = backtrace-tst \
++	tst-longjmp_chk \
++	test-strcpy_chk \
++	test-stpcpy_chk \
++	tst-longjmp_chk2 \
++	tst-backtrace2 \
++	tst-backtrace3 \
++	tst-backtrace4 \
++	tst-backtrace5 \
++	tst-backtrace6 \
++	tst-realpath-chk \
++	$(tests-all-chk)
+ 
+ ifeq ($(have-ssp),yes)
+ tests += tst-ssp-1
+ endif
+ 
+ ifeq (,$(CXX))
+-tests-unsupported = tst-chk4 tst-chk5 tst-chk6 tst-chk8 \
+-		    tst-lfschk4 tst-lfschk5 tst-lfschk6
++tests-unsupported = $(tests-cc-chk)
+ endif
+ 
+ extra-libs = libSegFault libpcprofile
+@@ -191,20 +226,10 @@ ifeq ($(run-built-tests),yes)
+ LOCALES := de_DE.UTF-8
+ include ../gen-locales.mk
+ 
+-$(objpfx)tst-chk1.out: $(gen-locales)
+-$(objpfx)tst-chk2.out: $(gen-locales)
+-$(objpfx)tst-chk3.out: $(gen-locales)
+-$(objpfx)tst-chk4.out: $(gen-locales)
+-$(objpfx)tst-chk5.out: $(gen-locales)
+-$(objpfx)tst-chk6.out: $(gen-locales)
+-$(objpfx)tst-chk7.out: $(gen-locales)
+-$(objpfx)tst-chk8.out: $(gen-locales)
+-$(objpfx)tst-lfschk1.out: $(gen-locales)
+-$(objpfx)tst-lfschk2.out: $(gen-locales)
+-$(objpfx)tst-lfschk3.out: $(gen-locales)
+-$(objpfx)tst-lfschk4.out: $(gen-locales)
+-$(objpfx)tst-lfschk5.out: $(gen-locales)
+-$(objpfx)tst-lfschk6.out: $(gen-locales)
++define chk-gen-locales
++$(objpfx)$(1).out: $(gen-locales)
++endef
++$(foreach t, $(tests-all-chk), $(eval $(call chk-gen-locales,$(t))))
+ endif
+ 
+ sLIBdir := $(shell echo $(slibdir) | sed 's,lib\(\|64\)$$,\\\\$$LIB,')
+diff --git a/debug/tst-chk2.c b/debug/tst-chk2.c
+deleted file mode 100644
+index be37ce2d22f0760a..0000000000000000
+--- a/debug/tst-chk2.c
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FORTIFY_SOURCE 1
+-#include "tst-chk1.c"
+diff --git a/debug/tst-chk3.c b/debug/tst-chk3.c
+deleted file mode 100644
+index 38b8e4fb360ba722..0000000000000000
+--- a/debug/tst-chk3.c
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FORTIFY_SOURCE 2
+-#include "tst-chk1.c"
+diff --git a/debug/tst-chk4.cc b/debug/tst-chk4.cc
+deleted file mode 100644
+index c82e6aac86038791..0000000000000000
+--- a/debug/tst-chk4.cc
++++ /dev/null
+@@ -1 +0,0 @@
+-#include "tst-chk1.c"
+diff --git a/debug/tst-chk5.cc b/debug/tst-chk5.cc
+deleted file mode 100644
+index be37ce2d22f0760a..0000000000000000
+--- a/debug/tst-chk5.cc
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FORTIFY_SOURCE 1
+-#include "tst-chk1.c"
+diff --git a/debug/tst-chk6.cc b/debug/tst-chk6.cc
+deleted file mode 100644
+index 38b8e4fb360ba722..0000000000000000
+--- a/debug/tst-chk6.cc
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FORTIFY_SOURCE 2
+-#include "tst-chk1.c"
+diff --git a/debug/tst-chk7.c b/debug/tst-chk7.c
+deleted file mode 100644
+index 2a7b32381268135c..0000000000000000
+--- a/debug/tst-chk7.c
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FORTIFY_SOURCE 3
+-#include "tst-chk1.c"
+diff --git a/debug/tst-chk8.cc b/debug/tst-chk8.cc
+deleted file mode 100644
+index 2a7b32381268135c..0000000000000000
+--- a/debug/tst-chk8.cc
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FORTIFY_SOURCE 3
+-#include "tst-chk1.c"
+diff --git a/debug/tst-chk1.c b/debug/tst-fortify.c
+similarity index 100%
+rename from debug/tst-chk1.c
+rename to debug/tst-fortify.c
+diff --git a/debug/tst-lfschk1.c b/debug/tst-lfschk1.c
+deleted file mode 100644
+index f3e6d47d5e4484c3..0000000000000000
+--- a/debug/tst-lfschk1.c
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FILE_OFFSET_BITS 64
+-#include "tst-chk1.c"
+diff --git a/debug/tst-lfschk2.c b/debug/tst-lfschk2.c
+deleted file mode 100644
+index 95d4db1d32d2eeb3..0000000000000000
+--- a/debug/tst-lfschk2.c
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FILE_OFFSET_BITS 64
+-#include "tst-chk2.c"
+diff --git a/debug/tst-lfschk3.c b/debug/tst-lfschk3.c
+deleted file mode 100644
+index 50a1ae1258f1553d..0000000000000000
+--- a/debug/tst-lfschk3.c
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FILE_OFFSET_BITS 64
+-#include "tst-chk3.c"
+diff --git a/debug/tst-lfschk4.cc b/debug/tst-lfschk4.cc
+deleted file mode 100644
+index f3e6d47d5e4484c3..0000000000000000
+--- a/debug/tst-lfschk4.cc
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FILE_OFFSET_BITS 64
+-#include "tst-chk1.c"
+diff --git a/debug/tst-lfschk5.cc b/debug/tst-lfschk5.cc
+deleted file mode 100644
+index 95d4db1d32d2eeb3..0000000000000000
+--- a/debug/tst-lfschk5.cc
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FILE_OFFSET_BITS 64
+-#include "tst-chk2.c"
+diff --git a/debug/tst-lfschk6.cc b/debug/tst-lfschk6.cc
+deleted file mode 100644
+index 50a1ae1258f1553d..0000000000000000
+--- a/debug/tst-lfschk6.cc
++++ /dev/null
+@@ -1,2 +0,0 @@
+-#define _FILE_OFFSET_BITS 64
+-#include "tst-chk3.c"
diff --git a/SOURCES/glibc-rh2033684-2.patch b/SOURCES/glibc-rh2033684-2.patch
new file mode 100644
index 0000000000000000000000000000000000000000..4651008f4d01e62fab50368ccd4534f200bbc885
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-2.patch
@@ -0,0 +1,101 @@
+commit c43c5796121bc5bcc0867f02e5536874aa8196c1
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Wed Dec 30 11:54:00 2020 +0530
+
+    Introduce _FORTIFY_SOURCE=3
+    
+    Introduce a new _FORTIFY_SOURCE level of 3 to enable additional
+    fortifications that may have a noticeable performance impact, allowing
+    more fortification coverage at the cost of some performance.
+    
+    With llvm 9.0 or later, this will replace the use of
+    __builtin_object_size with __builtin_dynamic_object_size.
+    
+    __builtin_dynamic_object_size
+    -----------------------------
+    
+    __builtin_dynamic_object_size is an LLVM builtin that is similar to
+    __builtin_object_size.  In addition to what __builtin_object_size
+    does, i.e. replace the builtin call with a constant object size,
+    __builtin_dynamic_object_size will replace the call site with an
+    expression that evaluates to the object size, thus expanding its
+    applicability.  In practice, __builtin_dynamic_object_size evaluates
+    these expressions through malloc/calloc calls that it can associate
+    with the object being evaluated.
+    
+    A simple motivating example is below; -D_FORTIFY_SOURCE=2 would miss
+    this and emit memcpy, but -D_FORTIFY_SOURCE=3 with the help of
+    __builtin_dynamic_object_size is able to emit __memcpy_chk with the
+    allocation size expression passed into the function:
+    
+    void *copy_obj (const void *src, size_t alloc, size_t copysize)
+    {
+      void *obj = malloc (alloc);
+      memcpy (obj, src, copysize);
+      return obj;
+    }
+    
+    Limitations
+    -----------
+    
+    If the object was allocated elsewhere that the compiler cannot see, or
+    if it was allocated in the function with a function that the compiler
+    does not recognize as an allocator then __builtin_dynamic_object_size
+    also returns -1.
+    
+    Further, the expression used to compute object size may be non-trivial
+    and may potentially incur a noticeable performance impact.  These
+    fortifications are hence enabled at a new _FORTIFY_SOURCE level to
+    allow developers to make a choice on the tradeoff according to their
+    environment.
+
+diff --git a/include/features.h b/include/features.h
+index ea7673ee115bcf0a..fe9fe16d034fad1b 100644
+--- a/include/features.h
++++ b/include/features.h
+@@ -381,6 +381,11 @@
+ #  warning _FORTIFY_SOURCE requires compiling with optimization (-O)
+ # elif !__GNUC_PREREQ (4, 1)
+ #  warning _FORTIFY_SOURCE requires GCC 4.1 or later
++# elif _FORTIFY_SOURCE > 2 && __glibc_clang_prereq (9, 0)
++#  if _FORTIFY_SOURCE > 3
++#   warning _FORTIFY_SOURCE > 3 is treated like 3 on this platform
++#  endif
++#  define __USE_FORTIFY_LEVEL 3
+ # elif _FORTIFY_SOURCE > 1
+ #  if _FORTIFY_SOURCE > 2
+ #   warning _FORTIFY_SOURCE > 2 is treated like 2 on this platform
+diff --git a/manual/creature.texi b/manual/creature.texi
+index 8876b2ab779c988f..64f361f27a7d6cdf 100644
+--- a/manual/creature.texi
++++ b/manual/creature.texi
+@@ -247,7 +247,8 @@ included.
+ @standards{GNU, (none)}
+ If this macro is defined to @math{1}, security hardening is added to
+ various library functions.  If defined to @math{2}, even stricter
+-checks are applied.
++checks are applied. If defined to @math{3}, @theglibc{} may also use
++checks that may have an additional performance overhead.
+ @end defvr
+ 
+ @defvr Macro _REENTRANT
+diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
+index 3f6fe3cc8563b493..1e39307b0ebcf38f 100644
+--- a/misc/sys/cdefs.h
++++ b/misc/sys/cdefs.h
+@@ -123,6 +123,15 @@
+ #define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
+ #define __bos0(ptr) __builtin_object_size (ptr, 0)
+ 
++/* Use __builtin_dynamic_object_size at _FORTIFY_SOURCE=3 when available.  */
++#if __USE_FORTIFY_LEVEL == 3 && __glibc_clang_prereq (9, 0)
++# define __glibc_objsize0(__o) __builtin_dynamic_object_size (__o, 0)
++# define __glibc_objsize(__o) __builtin_dynamic_object_size (__o, 1)
++#else
++# define __glibc_objsize0(__o) __bos0 (__o)
++# define __glibc_objsize(__o) __bos (__o)
++#endif
++
+ #if __GNUC_PREREQ (4,3)
+ # define __warndecl(name, msg) \
+   extern void name (void) __attribute__((__warning__ (msg)))
diff --git a/SOURCES/glibc-rh2033684-3.patch b/SOURCES/glibc-rh2033684-3.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b8d009356cf758326a49dcddd05377aa000b4923
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-3.patch
@@ -0,0 +1,43 @@
+commit 7163ace3318d666d40771f5c8e7c4a148827070f
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Thu Nov 12 12:09:56 2020 +0530
+
+    Use __builtin___stpncpy_chk when available
+    
+    The builtin has been available in gcc since 4.7.0 and in clang since
+    2.6.  This fixes stpncpy fortification with clang since it does a
+    better job of plugging in __stpncpy_chk in the right place than the
+    header hackery.
+    
+    This has been tested by building and running all tests with gcc 10.2.1
+    and also with clang tip as of a few days ago (just the tests in debug/
+    since running all tests don't work with clang at the moment) to make
+    sure that both compilers pass the stpncpy tests.
+
+diff --git a/string/bits/string_fortified.h b/string/bits/string_fortified.h
+index a07ab0dbc8c8dd5b..4ed6755a6c1ca247 100644
+--- a/string/bits/string_fortified.h
++++ b/string/bits/string_fortified.h
+@@ -106,7 +106,13 @@ __NTH (strncpy (char *__restrict __dest, const char *__restrict __src,
+   return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
+ }
+ 
+-/* XXX We have no corresponding builtin yet.  */
++#if __GNUC_PREREQ (4, 7) || __glibc_clang_prereq (2, 6)
++__fortify_function char *
++__NTH (stpncpy (char *__dest, const char *__src, size_t __n))
++{
++  return __builtin___stpncpy_chk (__dest, __src, __n, __bos (__dest));
++}
++#else
+ extern char *__stpncpy_chk (char *__dest, const char *__src, size_t __n,
+ 			    size_t __destlen) __THROW;
+ extern char *__REDIRECT_NTH (__stpncpy_alias, (char *__dest, const char *__src,
+@@ -120,6 +126,7 @@ __NTH (stpncpy (char *__dest, const char *__src, size_t __n))
+     return __stpncpy_chk (__dest, __src, __n, __bos (__dest));
+   return __stpncpy_alias (__dest, __src, __n);
+ }
++#endif
+ 
+ 
+ __fortify_function char *
diff --git a/SOURCES/glibc-rh2033684-4.patch b/SOURCES/glibc-rh2033684-4.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ebd0c338a25f3713823c8314b7dceb97683d4657
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-4.patch
@@ -0,0 +1,161 @@
+commit 2a3224c53653214cbba2ec23424702193c80ea3b
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Wed Dec 30 11:09:58 2020 +0530
+
+    string: Enable __FORTIFY_LEVEL=3
+    
+    This change enhances fortified string functions to use
+    __builtin_dynamic_object_size under _FORTIFY_SOURCE=3 whenever the
+    compiler supports it.
+
+# Conflicts:
+#	string/bits/string_fortified.h
+
+Conflict resolved to retain __GNUC_PREREQ (5,0) macro check in RHEL-8
+glibc.
+
+diff --git a/include/string.h b/include/string.h
+index 4d622f1c0305e78e..bbc97082661caf42 100644
+--- a/include/string.h
++++ b/include/string.h
+@@ -119,10 +119,11 @@ libc_hidden_proto (__ffs)
+ void __explicit_bzero_chk_internal (void *, size_t, size_t)
+   __THROW __nonnull ((1)) attribute_hidden;
+ # define explicit_bzero(buf, len) \
+-  __explicit_bzero_chk_internal (buf, len, __bos0 (buf))
++  __explicit_bzero_chk_internal (buf, len, __glibc_objsize0 (buf))
+ #elif !IS_IN (nonlib)
+ void __explicit_bzero_chk (void *, size_t, size_t) __THROW __nonnull ((1));
+-# define explicit_bzero(buf, len) __explicit_bzero_chk (buf, len, __bos0 (buf))
++# define explicit_bzero(buf, len) __explicit_bzero_chk (buf, len,	      \
++							__glibc_objsize0 (buf))
+ #endif
+ 
+ libc_hidden_builtin_proto (memchr)
+diff --git a/string/bits/string_fortified.h b/string/bits/string_fortified.h
+index 4ed6755a6c1ca247..27ec273ec41cd81c 100644
+--- a/string/bits/string_fortified.h
++++ b/string/bits/string_fortified.h
+@@ -31,13 +31,15 @@ __fortify_function void *
+ __NTH (memcpy (void *__restrict __dest, const void *__restrict __src,
+ 	       size_t __len))
+ {
+-  return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));
++  return __builtin___memcpy_chk (__dest, __src, __len,
++				 __glibc_objsize0 (__dest));
+ }
+ 
+ __fortify_function void *
+ __NTH (memmove (void *__dest, const void *__src, size_t __len))
+ {
+-  return __builtin___memmove_chk (__dest, __src, __len, __bos0 (__dest));
++  return __builtin___memmove_chk (__dest, __src, __len,
++				  __glibc_objsize0 (__dest));
+ }
+ 
+ #ifdef __USE_GNU
+@@ -45,7 +47,8 @@ __fortify_function void *
+ __NTH (mempcpy (void *__restrict __dest, const void *__restrict __src,
+ 		size_t __len))
+ {
+-  return __builtin___mempcpy_chk (__dest, __src, __len, __bos0 (__dest));
++  return __builtin___mempcpy_chk (__dest, __src, __len,
++				  __glibc_objsize0 (__dest));
+ }
+ #endif
+ 
+@@ -68,7 +71,8 @@ __NTH (memset (void *__dest, int __ch, size_t __len))
+       return __dest;
+     }
+ #endif
+-  return __builtin___memset_chk (__dest, __ch, __len, __bos0 (__dest));
++  return __builtin___memset_chk (__dest, __ch, __len,
++				 __glibc_objsize0 (__dest));
+ }
+ 
+ #ifdef __USE_MISC
+@@ -80,21 +84,21 @@ void __explicit_bzero_chk (void *__dest, size_t __len, size_t __destlen)
+ __fortify_function void
+ __NTH (explicit_bzero (void *__dest, size_t __len))
+ {
+-  __explicit_bzero_chk (__dest, __len, __bos0 (__dest));
++  __explicit_bzero_chk (__dest, __len, __glibc_objsize0 (__dest));
+ }
+ #endif
+ 
+ __fortify_function char *
+ __NTH (strcpy (char *__restrict __dest, const char *__restrict __src))
+ {
+-  return __builtin___strcpy_chk (__dest, __src, __bos (__dest));
++  return __builtin___strcpy_chk (__dest, __src, __glibc_objsize (__dest));
+ }
+ 
+ #ifdef __USE_GNU
+ __fortify_function char *
+ __NTH (stpcpy (char *__restrict __dest, const char *__restrict __src))
+ {
+-  return __builtin___stpcpy_chk (__dest, __src, __bos (__dest));
++  return __builtin___stpcpy_chk (__dest, __src, __glibc_objsize (__dest));
+ }
+ #endif
+ 
+@@ -103,14 +107,16 @@ __fortify_function char *
+ __NTH (strncpy (char *__restrict __dest, const char *__restrict __src,
+ 		size_t __len))
+ {
+-  return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
++  return __builtin___strncpy_chk (__dest, __src, __len,
++				  __glibc_objsize (__dest));
+ }
+ 
+ #if __GNUC_PREREQ (4, 7) || __glibc_clang_prereq (2, 6)
+ __fortify_function char *
+ __NTH (stpncpy (char *__dest, const char *__src, size_t __n))
+ {
+-  return __builtin___stpncpy_chk (__dest, __src, __n, __bos (__dest));
++  return __builtin___stpncpy_chk (__dest, __src, __n,
++				  __glibc_objsize (__dest));
+ }
+ #else
+ extern char *__stpncpy_chk (char *__dest, const char *__src, size_t __n,
+@@ -132,7 +138,7 @@ __NTH (stpncpy (char *__dest, const char *__src, size_t __n))
+ __fortify_function char *
+ __NTH (strcat (char *__restrict __dest, const char *__restrict __src))
+ {
+-  return __builtin___strcat_chk (__dest, __src, __bos (__dest));
++  return __builtin___strcat_chk (__dest, __src, __glibc_objsize (__dest));
+ }
+ 
+ 
+@@ -140,7 +146,8 @@ __fortify_function char *
+ __NTH (strncat (char *__restrict __dest, const char *__restrict __src,
+ 		size_t __len))
+ {
+-  return __builtin___strncat_chk (__dest, __src, __len, __bos (__dest));
++  return __builtin___strncat_chk (__dest, __src, __len,
++				  __glibc_objsize (__dest));
+ }
+ 
+ #endif /* bits/string_fortified.h */
+diff --git a/string/bits/strings_fortified.h b/string/bits/strings_fortified.h
+index d9b2804525cfa994..871515bd2cba1f8a 100644
+--- a/string/bits/strings_fortified.h
++++ b/string/bits/strings_fortified.h
+@@ -22,13 +22,15 @@
+ __fortify_function void
+ __NTH (bcopy (const void *__src, void *__dest, size_t __len))
+ {
+-  (void) __builtin___memmove_chk (__dest, __src, __len, __bos0 (__dest));
++  (void) __builtin___memmove_chk (__dest, __src, __len,
++				  __glibc_objsize0 (__dest));
+ }
+ 
+ __fortify_function void
+ __NTH (bzero (void *__dest, size_t __len))
+ {
+-  (void) __builtin___memset_chk (__dest, '\0', __len, __bos0 (__dest));
++  (void) __builtin___memset_chk (__dest, '\0', __len,
++				 __glibc_objsize0 (__dest));
+ }
+ 
+ #endif
diff --git a/SOURCES/glibc-rh2033684-5.patch b/SOURCES/glibc-rh2033684-5.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8c1f7f30c1e8ae328ae95014037f0bb76914aa36
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-5.patch
@@ -0,0 +1,963 @@
+commit f9de8bfe1a731c309b91d175b4f6f4aeb786effa
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Tue Dec 15 23:50:09 2020 +0530
+
+    nonstring: Enable __FORTIFY_LEVEL=3
+    
+    Use __builtin_dynamic_object_size in the remaining functions that
+    don't have compiler builtins as is the case for string functions.
+
+diff --git a/io/bits/poll2.h b/io/bits/poll2.h
+index 7e8406b87d6319f8..f47fd9ad0945234f 100644
+--- a/io/bits/poll2.h
++++ b/io/bits/poll2.h
+@@ -35,12 +35,13 @@ extern int __REDIRECT (__poll_chk_warn, (struct pollfd *__fds, nfds_t __nfds,
+ __fortify_function int
+ poll (struct pollfd *__fds, nfds_t __nfds, int __timeout)
+ {
+-  if (__bos (__fds) != (__SIZE_TYPE__) -1)
++  if (__glibc_objsize (__fds) != (__SIZE_TYPE__) -1)
+     {
+       if (! __builtin_constant_p (__nfds))
+-	return __poll_chk (__fds, __nfds, __timeout, __bos (__fds));
+-      else if (__bos (__fds) / sizeof (*__fds) < __nfds)
+-	return __poll_chk_warn (__fds, __nfds, __timeout, __bos (__fds));
++	return __poll_chk (__fds, __nfds, __timeout, __glibc_objsize (__fds));
++      else if (__glibc_objsize (__fds) / sizeof (*__fds) < __nfds)
++	return __poll_chk_warn (__fds, __nfds, __timeout,
++				__glibc_objsize (__fds));
+     }
+ 
+   return __poll_alias (__fds, __nfds, __timeout);
+@@ -65,13 +66,14 @@ __fortify_function int
+ ppoll (struct pollfd *__fds, nfds_t __nfds, const struct timespec *__timeout,
+        const __sigset_t *__ss)
+ {
+-  if (__bos (__fds) != (__SIZE_TYPE__) -1)
++  if (__glibc_objsize (__fds) != (__SIZE_TYPE__) -1)
+     {
+       if (! __builtin_constant_p (__nfds))
+-	return __ppoll_chk (__fds, __nfds, __timeout, __ss, __bos (__fds));
+-      else if (__bos (__fds) / sizeof (*__fds) < __nfds)
++	return __ppoll_chk (__fds, __nfds, __timeout, __ss,
++			    __glibc_objsize (__fds));
++      else if (__glibc_objsize (__fds) / sizeof (*__fds) < __nfds)
+ 	return __ppoll_chk_warn (__fds, __nfds, __timeout, __ss,
+-				 __bos (__fds));
++				 __glibc_objsize (__fds));
+     }
+ 
+   return __ppoll_alias (__fds, __nfds, __timeout, __ss);
+diff --git a/libio/bits/stdio.h b/libio/bits/stdio.h
+index 4ab919031f77a960..1372d4bf70c43d53 100644
+--- a/libio/bits/stdio.h
++++ b/libio/bits/stdio.h
+@@ -31,7 +31,7 @@
+ 
+ 
+ #ifdef __USE_EXTERN_INLINES
+-/* For -D_FORTIFY_SOURCE{,=2} bits/stdio2.h will define a different
++/* For -D_FORTIFY_SOURCE{,=2,=3} bits/stdio2.h will define a different
+    inline.  */
+ # if !(__USE_FORTIFY_LEVEL > 0 && defined __fortify_function)
+ /* Write formatted output to stdout from argument list ARG.  */
+diff --git a/libio/bits/stdio2.h b/libio/bits/stdio2.h
+index 11651506a67daea0..2cd69f44cfadfc9f 100644
+--- a/libio/bits/stdio2.h
++++ b/libio/bits/stdio2.h
+@@ -34,12 +34,13 @@ __fortify_function int
+ __NTH (sprintf (char *__restrict __s, const char *__restrict __fmt, ...))
+ {
+   return __builtin___sprintf_chk (__s, __USE_FORTIFY_LEVEL - 1,
+-				  __bos (__s), __fmt, __va_arg_pack ());
++				  __glibc_objsize (__s), __fmt,
++				  __va_arg_pack ());
+ }
+ #elif !defined __cplusplus
+ # define sprintf(str, ...) \
+-  __builtin___sprintf_chk (str, __USE_FORTIFY_LEVEL - 1, __bos (str), \
+-			   __VA_ARGS__)
++  __builtin___sprintf_chk (str, __USE_FORTIFY_LEVEL - 1,		      \
++			   __glibc_objsize (str), __VA_ARGS__)
+ #endif
+ 
+ __fortify_function int
+@@ -47,7 +48,7 @@ __NTH (vsprintf (char *__restrict __s, const char *__restrict __fmt,
+ 		 __gnuc_va_list __ap))
+ {
+   return __builtin___vsprintf_chk (__s, __USE_FORTIFY_LEVEL - 1,
+-				   __bos (__s), __fmt, __ap);
++				   __glibc_objsize (__s), __fmt, __ap);
+ }
+ 
+ #if defined __USE_ISOC99 || defined __USE_UNIX98
+@@ -65,12 +66,13 @@ __NTH (snprintf (char *__restrict __s, size_t __n,
+ 		 const char *__restrict __fmt, ...))
+ {
+   return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1,
+-				   __bos (__s), __fmt, __va_arg_pack ());
++				   __glibc_objsize (__s), __fmt,
++				   __va_arg_pack ());
+ }
+ # elif !defined __cplusplus
+ #  define snprintf(str, len, ...) \
+-  __builtin___snprintf_chk (str, len, __USE_FORTIFY_LEVEL - 1, __bos (str), \
+-			    __VA_ARGS__)
++  __builtin___snprintf_chk (str, len, __USE_FORTIFY_LEVEL - 1,		      \
++			    __glibc_objsize (str), __VA_ARGS__)
+ # endif
+ 
+ __fortify_function int
+@@ -78,7 +80,7 @@ __NTH (vsnprintf (char *__restrict __s, size_t __n,
+ 		  const char *__restrict __fmt, __gnuc_va_list __ap))
+ {
+   return __builtin___vsnprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1,
+-				    __bos (__s), __fmt, __ap);
++				    __glibc_objsize (__s), __fmt, __ap);
+ }
+ 
+ #endif
+@@ -234,8 +236,8 @@ extern char *__REDIRECT (__gets_warn, (char *__str), gets)
+ __fortify_function __wur char *
+ gets (char *__str)
+ {
+-  if (__bos (__str) != (size_t) -1)
+-    return __gets_chk (__str, __bos (__str));
++  if (__glibc_objsize (__str) != (size_t) -1)
++    return __gets_chk (__str, __glibc_objsize (__str));
+   return __gets_warn (__str);
+ }
+ #endif
+@@ -254,13 +256,13 @@ extern char *__REDIRECT (__fgets_chk_warn,
+ __fortify_function __wur char *
+ fgets (char *__restrict __s, int __n, FILE *__restrict __stream)
+ {
+-  if (__bos (__s) != (size_t) -1)
++  if (__glibc_objsize (__s) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n) || __n <= 0)
+-	return __fgets_chk (__s, __bos (__s), __n, __stream);
++	return __fgets_chk (__s, __glibc_objsize (__s), __n, __stream);
+ 
+-      if ((size_t) __n > __bos (__s))
+-	return __fgets_chk_warn (__s, __bos (__s), __n, __stream);
++      if ((size_t) __n > __glibc_objsize (__s))
++	return __fgets_chk_warn (__s, __glibc_objsize (__s), __n, __stream);
+     }
+   return __fgets_alias (__s, __n, __stream);
+ }
+@@ -284,15 +286,17 @@ __fortify_function __wur size_t
+ fread (void *__restrict __ptr, size_t __size, size_t __n,
+        FILE *__restrict __stream)
+ {
+-  if (__bos0 (__ptr) != (size_t) -1)
++  if (__glibc_objsize0 (__ptr) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__size)
+ 	  || !__builtin_constant_p (__n)
+ 	  || (__size | __n) >= (((size_t) 1) << (8 * sizeof (size_t) / 2)))
+-	return __fread_chk (__ptr, __bos0 (__ptr), __size, __n, __stream);
++	return __fread_chk (__ptr, __glibc_objsize0 (__ptr), __size, __n,
++			    __stream);
+ 
+-      if (__size * __n > __bos0 (__ptr))
+-	return __fread_chk_warn (__ptr, __bos0 (__ptr), __size, __n, __stream);
++      if (__size * __n > __glibc_objsize0 (__ptr))
++	return __fread_chk_warn (__ptr, __glibc_objsize0 (__ptr), __size, __n,
++				 __stream);
+     }
+   return __fread_alias (__ptr, __size, __n, __stream);
+ }
+@@ -312,13 +316,15 @@ extern char *__REDIRECT (__fgets_unlocked_chk_warn,
+ __fortify_function __wur char *
+ fgets_unlocked (char *__restrict __s, int __n, FILE *__restrict __stream)
+ {
+-  if (__bos (__s) != (size_t) -1)
++  if (__glibc_objsize (__s) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n) || __n <= 0)
+-	return __fgets_unlocked_chk (__s, __bos (__s), __n, __stream);
++	return __fgets_unlocked_chk (__s, __glibc_objsize (__s), __n,
++				     __stream);
+ 
+-      if ((size_t) __n > __bos (__s))
+-	return __fgets_unlocked_chk_warn (__s, __bos (__s), __n, __stream);
++      if ((size_t) __n > __glibc_objsize (__s))
++	return __fgets_unlocked_chk_warn (__s, __glibc_objsize (__s), __n,
++					  __stream);
+     }
+   return __fgets_unlocked_alias (__s, __n, __stream);
+ }
+@@ -345,17 +351,17 @@ __fortify_function __wur size_t
+ fread_unlocked (void *__restrict __ptr, size_t __size, size_t __n,
+ 		FILE *__restrict __stream)
+ {
+-  if (__bos0 (__ptr) != (size_t) -1)
++  if (__glibc_objsize0 (__ptr) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__size)
+ 	  || !__builtin_constant_p (__n)
+ 	  || (__size | __n) >= (((size_t) 1) << (8 * sizeof (size_t) / 2)))
+-	return __fread_unlocked_chk (__ptr, __bos0 (__ptr), __size, __n,
+-				     __stream);
++	return __fread_unlocked_chk (__ptr, __glibc_objsize0 (__ptr), __size,
++				     __n, __stream);
+ 
+-      if (__size * __n > __bos0 (__ptr))
+-	return __fread_unlocked_chk_warn (__ptr, __bos0 (__ptr), __size, __n,
+-					  __stream);
++      if (__size * __n > __glibc_objsize0 (__ptr))
++	return __fread_unlocked_chk_warn (__ptr, __glibc_objsize0 (__ptr),
++					  __size, __n, __stream);
+     }
+ 
+ # ifdef __USE_EXTERN_INLINES
+diff --git a/posix/bits/unistd.h b/posix/bits/unistd.h
+index 9a749dccf8de65cd..a0c4dcfe9c61a7b8 100644
+--- a/posix/bits/unistd.h
++++ b/posix/bits/unistd.h
+@@ -33,13 +33,14 @@ extern ssize_t __REDIRECT (__read_chk_warn,
+ __fortify_function __wur ssize_t
+ read (int __fd, void *__buf, size_t __nbytes)
+ {
+-  if (__bos0 (__buf) != (size_t) -1)
++  if (__glibc_objsize0 (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__nbytes))
+-	return __read_chk (__fd, __buf, __nbytes, __bos0 (__buf));
++	return __read_chk (__fd, __buf, __nbytes, __glibc_objsize0 (__buf));
+ 
+-      if (__nbytes > __bos0 (__buf))
+-	return __read_chk_warn (__fd, __buf, __nbytes, __bos0 (__buf));
++      if (__nbytes > __glibc_objsize0 (__buf))
++	return __read_chk_warn (__fd, __buf, __nbytes,
++				__glibc_objsize0 (__buf));
+     }
+   return __read_alias (__fd, __buf, __nbytes);
+ }
+@@ -71,14 +72,15 @@ extern ssize_t __REDIRECT (__pread64_chk_warn,
+ __fortify_function __wur ssize_t
+ pread (int __fd, void *__buf, size_t __nbytes, __off_t __offset)
+ {
+-  if (__bos0 (__buf) != (size_t) -1)
++  if (__glibc_objsize0 (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__nbytes))
+-	return __pread_chk (__fd, __buf, __nbytes, __offset, __bos0 (__buf));
++	return __pread_chk (__fd, __buf, __nbytes, __offset,
++			    __glibc_objsize0 (__buf));
+ 
+-      if ( __nbytes > __bos0 (__buf))
++      if ( __nbytes > __glibc_objsize0 (__buf))
+ 	return __pread_chk_warn (__fd, __buf, __nbytes, __offset,
+-				 __bos0 (__buf));
++				 __glibc_objsize0 (__buf));
+     }
+   return __pread_alias (__fd, __buf, __nbytes, __offset);
+ }
+@@ -86,14 +88,15 @@ pread (int __fd, void *__buf, size_t __nbytes, __off_t __offset)
+ __fortify_function __wur ssize_t
+ pread (int __fd, void *__buf, size_t __nbytes, __off64_t __offset)
+ {
+-  if (__bos0 (__buf) != (size_t) -1)
++  if (__glibc_objsize0 (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__nbytes))
+-	return __pread64_chk (__fd, __buf, __nbytes, __offset, __bos0 (__buf));
++	return __pread64_chk (__fd, __buf, __nbytes, __offset,
++			      __glibc_objsize0 (__buf));
+ 
+-      if ( __nbytes > __bos0 (__buf))
++      if ( __nbytes > __glibc_objsize0 (__buf))
+ 	return __pread64_chk_warn (__fd, __buf, __nbytes, __offset,
+-				   __bos0 (__buf));
++				   __glibc_objsize0 (__buf));
+     }
+ 
+   return __pread64_alias (__fd, __buf, __nbytes, __offset);
+@@ -104,14 +107,15 @@ pread (int __fd, void *__buf, size_t __nbytes, __off64_t __offset)
+ __fortify_function __wur ssize_t
+ pread64 (int __fd, void *__buf, size_t __nbytes, __off64_t __offset)
+ {
+-  if (__bos0 (__buf) != (size_t) -1)
++  if (__glibc_objsize0 (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__nbytes))
+-	return __pread64_chk (__fd, __buf, __nbytes, __offset, __bos0 (__buf));
++	return __pread64_chk (__fd, __buf, __nbytes, __offset,
++			      __glibc_objsize0 (__buf));
+ 
+-      if ( __nbytes > __bos0 (__buf))
++      if ( __nbytes > __glibc_objsize0 (__buf))
+ 	return __pread64_chk_warn (__fd, __buf, __nbytes, __offset,
+-				   __bos0 (__buf));
++				   __glibc_objsize0 (__buf));
+     }
+ 
+   return __pread64_alias (__fd, __buf, __nbytes, __offset);
+@@ -139,13 +143,14 @@ __fortify_function __nonnull ((1, 2)) __wur ssize_t
+ __NTH (readlink (const char *__restrict __path, char *__restrict __buf,
+ 		 size_t __len))
+ {
+-  if (__bos (__buf) != (size_t) -1)
++  if (__glibc_objsize (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__len))
+-	return __readlink_chk (__path, __buf, __len, __bos (__buf));
++	return __readlink_chk (__path, __buf, __len, __glibc_objsize (__buf));
+ 
+-      if ( __len > __bos (__buf))
+-	return __readlink_chk_warn (__path, __buf, __len, __bos (__buf));
++      if ( __len > __glibc_objsize (__buf))
++	return __readlink_chk_warn (__path, __buf, __len,
++				    __glibc_objsize (__buf));
+     }
+   return __readlink_alias (__path, __buf, __len);
+ }
+@@ -173,14 +178,15 @@ __fortify_function __nonnull ((2, 3)) __wur ssize_t
+ __NTH (readlinkat (int __fd, const char *__restrict __path,
+ 		   char *__restrict __buf, size_t __len))
+ {
+-  if (__bos (__buf) != (size_t) -1)
++  if (__glibc_objsize (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__len))
+-	return __readlinkat_chk (__fd, __path, __buf, __len, __bos (__buf));
++	return __readlinkat_chk (__fd, __path, __buf, __len,
++				 __glibc_objsize (__buf));
+ 
+-      if (__len > __bos (__buf))
++      if (__len > __glibc_objsize (__buf))
+ 	return __readlinkat_chk_warn (__fd, __path, __buf, __len,
+-				      __bos (__buf));
++				      __glibc_objsize (__buf));
+     }
+   return __readlinkat_alias (__fd, __path, __buf, __len);
+ }
+@@ -199,13 +205,13 @@ extern char *__REDIRECT_NTH (__getcwd_chk_warn,
+ __fortify_function __wur char *
+ __NTH (getcwd (char *__buf, size_t __size))
+ {
+-  if (__bos (__buf) != (size_t) -1)
++  if (__glibc_objsize (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__size))
+-	return __getcwd_chk (__buf, __size, __bos (__buf));
++	return __getcwd_chk (__buf, __size, __glibc_objsize (__buf));
+ 
+-      if (__size > __bos (__buf))
+-	return __getcwd_chk_warn (__buf, __size, __bos (__buf));
++      if (__size > __glibc_objsize (__buf))
++	return __getcwd_chk_warn (__buf, __size, __glibc_objsize (__buf));
+     }
+   return __getcwd_alias (__buf, __size);
+ }
+@@ -220,8 +226,8 @@ extern char *__REDIRECT_NTH (__getwd_warn, (char *__buf), getwd)
+ __fortify_function __nonnull ((1)) __attribute_deprecated__ __wur char *
+ __NTH (getwd (char *__buf))
+ {
+-  if (__bos (__buf) != (size_t) -1)
+-    return __getwd_chk (__buf, __bos (__buf));
++  if (__glibc_objsize (__buf) != (size_t) -1)
++    return __getwd_chk (__buf, __glibc_objsize (__buf));
+   return __getwd_warn (__buf);
+ }
+ #endif
+@@ -239,13 +245,14 @@ extern size_t __REDIRECT_NTH (__confstr_chk_warn,
+ __fortify_function size_t
+ __NTH (confstr (int __name, char *__buf, size_t __len))
+ {
+-  if (__bos (__buf) != (size_t) -1)
++  if (__glibc_objsize (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__len))
+-	return __confstr_chk (__name, __buf, __len, __bos (__buf));
++	return __confstr_chk (__name, __buf, __len, __glibc_objsize (__buf));
+ 
+-      if (__bos (__buf) < __len)
+-	return __confstr_chk_warn (__name, __buf, __len, __bos (__buf));
++      if (__glibc_objsize (__buf) < __len)
++	return __confstr_chk_warn (__name, __buf, __len,
++				   __glibc_objsize (__buf));
+     }
+   return __confstr_alias (__name, __buf, __len);
+ }
+@@ -264,13 +271,13 @@ extern int __REDIRECT_NTH (__getgroups_chk_warn,
+ __fortify_function int
+ __NTH (getgroups (int __size, __gid_t __list[]))
+ {
+-  if (__bos (__list) != (size_t) -1)
++  if (__glibc_objsize (__list) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__size) || __size < 0)
+-	return __getgroups_chk (__size, __list, __bos (__list));
++	return __getgroups_chk (__size, __list, __glibc_objsize (__list));
+ 
+-      if (__size * sizeof (__gid_t) > __bos (__list))
+-	return __getgroups_chk_warn (__size, __list, __bos (__list));
++      if (__size * sizeof (__gid_t) > __glibc_objsize (__list))
++	return __getgroups_chk_warn (__size, __list, __glibc_objsize (__list));
+     }
+   return __getgroups_alias (__size, __list);
+ }
+@@ -290,13 +297,15 @@ extern int __REDIRECT_NTH (__ttyname_r_chk_warn,
+ __fortify_function int
+ __NTH (ttyname_r (int __fd, char *__buf, size_t __buflen))
+ {
+-  if (__bos (__buf) != (size_t) -1)
++  if (__glibc_objsize (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__buflen))
+-	return __ttyname_r_chk (__fd, __buf, __buflen, __bos (__buf));
++	return __ttyname_r_chk (__fd, __buf, __buflen,
++				__glibc_objsize (__buf));
+ 
+-      if (__buflen > __bos (__buf))
+-	return __ttyname_r_chk_warn (__fd, __buf, __buflen, __bos (__buf));
++      if (__buflen > __glibc_objsize (__buf))
++	return __ttyname_r_chk_warn (__fd, __buf, __buflen,
++				     __glibc_objsize (__buf));
+     }
+   return __ttyname_r_alias (__fd, __buf, __buflen);
+ }
+@@ -316,13 +325,14 @@ extern int __REDIRECT (__getlogin_r_chk_warn,
+ __fortify_function int
+ getlogin_r (char *__buf, size_t __buflen)
+ {
+-  if (__bos (__buf) != (size_t) -1)
++  if (__glibc_objsize (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__buflen))
+-	return __getlogin_r_chk (__buf, __buflen, __bos (__buf));
++	return __getlogin_r_chk (__buf, __buflen, __glibc_objsize (__buf));
+ 
+-      if (__buflen > __bos (__buf))
+-	return __getlogin_r_chk_warn (__buf, __buflen, __bos (__buf));
++      if (__buflen > __glibc_objsize (__buf))
++	return __getlogin_r_chk_warn (__buf, __buflen,
++				      __glibc_objsize (__buf));
+     }
+   return __getlogin_r_alias (__buf, __buflen);
+ }
+@@ -343,13 +353,14 @@ extern int __REDIRECT_NTH (__gethostname_chk_warn,
+ __fortify_function int
+ __NTH (gethostname (char *__buf, size_t __buflen))
+ {
+-  if (__bos (__buf) != (size_t) -1)
++  if (__glibc_objsize (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__buflen))
+-	return __gethostname_chk (__buf, __buflen, __bos (__buf));
++	return __gethostname_chk (__buf, __buflen, __glibc_objsize (__buf));
+ 
+-      if (__buflen > __bos (__buf))
+-	return __gethostname_chk_warn (__buf, __buflen, __bos (__buf));
++      if (__buflen > __glibc_objsize (__buf))
++	return __gethostname_chk_warn (__buf, __buflen,
++				       __glibc_objsize (__buf));
+     }
+   return __gethostname_alias (__buf, __buflen);
+ }
+@@ -372,13 +383,14 @@ extern int __REDIRECT_NTH (__getdomainname_chk_warn,
+ __fortify_function int
+ __NTH (getdomainname (char *__buf, size_t __buflen))
+ {
+-  if (__bos (__buf) != (size_t) -1)
++  if (__glibc_objsize (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__buflen))
+-	return __getdomainname_chk (__buf, __buflen, __bos (__buf));
++	return __getdomainname_chk (__buf, __buflen, __glibc_objsize (__buf));
+ 
+-      if (__buflen > __bos (__buf))
+-	return __getdomainname_chk_warn (__buf, __buflen, __bos (__buf));
++      if (__buflen > __glibc_objsize (__buf))
++	return __getdomainname_chk_warn (__buf, __buflen,
++					 __glibc_objsize (__buf));
+     }
+   return __getdomainname_alias (__buf, __buflen);
+ }
+diff --git a/socket/bits/socket2.h b/socket/bits/socket2.h
+index a129e697352fd7cb..729e5a4cc1f4cb92 100644
+--- a/socket/bits/socket2.h
++++ b/socket/bits/socket2.h
+@@ -33,13 +33,15 @@ extern ssize_t __REDIRECT (__recv_chk_warn,
+ __fortify_function ssize_t
+ recv (int __fd, void *__buf, size_t __n, int __flags)
+ {
+-  if (__bos0 (__buf) != (size_t) -1)
++  if (__glibc_objsize0 (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n))
+-	return __recv_chk (__fd, __buf, __n, __bos0 (__buf), __flags);
++	return __recv_chk (__fd, __buf, __n, __glibc_objsize0 (__buf),
++			   __flags);
+ 
+-      if (__n > __bos0 (__buf))
+-	return __recv_chk_warn (__fd, __buf, __n, __bos0 (__buf), __flags);
++      if (__n > __glibc_objsize0 (__buf))
++	return __recv_chk_warn (__fd, __buf, __n, __glibc_objsize0 (__buf),
++				__flags);
+     }
+   return __recv_alias (__fd, __buf, __n, __flags);
+ }
+@@ -64,14 +66,14 @@ __fortify_function ssize_t
+ recvfrom (int __fd, void *__restrict __buf, size_t __n, int __flags,
+ 	  __SOCKADDR_ARG __addr, socklen_t *__restrict __addr_len)
+ {
+-  if (__bos0 (__buf) != (size_t) -1)
++  if (__glibc_objsize0 (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n))
+-	return __recvfrom_chk (__fd, __buf, __n, __bos0 (__buf), __flags,
+-			       __addr, __addr_len);
+-      if (__n > __bos0 (__buf))
+-	return __recvfrom_chk_warn (__fd, __buf, __n, __bos0 (__buf), __flags,
+-				    __addr, __addr_len);
++	return __recvfrom_chk (__fd, __buf, __n, __glibc_objsize0 (__buf),
++			       __flags, __addr, __addr_len);
++      if (__n > __glibc_objsize0 (__buf))
++	return __recvfrom_chk_warn (__fd, __buf, __n, __glibc_objsize0 (__buf),
++				    __flags, __addr, __addr_len);
+     }
+   return __recvfrom_alias (__fd, __buf, __n, __flags, __addr, __addr_len);
+ }
+diff --git a/stdlib/bits/stdlib.h b/stdlib/bits/stdlib.h
+index 53c379b99ae9d5fe..5e4114ded33f2033 100644
+--- a/stdlib/bits/stdlib.h
++++ b/stdlib/bits/stdlib.h
+@@ -36,13 +36,14 @@ extern char *__REDIRECT_NTH (__realpath_chk_warn,
+ __fortify_function __wur char *
+ __NTH (realpath (const char *__restrict __name, char *__restrict __resolved))
+ {
+-  if (__bos (__resolved) != (size_t) -1)
++  if (__glibc_objsize (__resolved) != (size_t) -1)
+     {
+ #if defined _LIBC_LIMITS_H_ && defined PATH_MAX
+-      if (__bos (__resolved) < PATH_MAX)
+-	return __realpath_chk_warn (__name, __resolved, __bos (__resolved));
++      if (__glibc_objsize (__resolved) < PATH_MAX)
++	return __realpath_chk_warn (__name, __resolved,
++				    __glibc_objsize (__resolved));
+ #endif
+-      return __realpath_chk (__name, __resolved, __bos (__resolved));
++      return __realpath_chk (__name, __resolved, __glibc_objsize (__resolved));
+     }
+ 
+   return __realpath_alias (__name, __resolved);
+@@ -63,12 +64,14 @@ extern int __REDIRECT_NTH (__ptsname_r_chk_warn,
+ __fortify_function int
+ __NTH (ptsname_r (int __fd, char *__buf, size_t __buflen))
+ {
+-  if (__bos (__buf) != (size_t) -1)
++  if (__glibc_objsize (__buf) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__buflen))
+-	return __ptsname_r_chk (__fd, __buf, __buflen, __bos (__buf));
+-      if (__buflen > __bos (__buf))
+-	return __ptsname_r_chk_warn (__fd, __buf, __buflen, __bos (__buf));
++	return __ptsname_r_chk (__fd, __buf, __buflen,
++				__glibc_objsize (__buf));
++      if (__buflen > __glibc_objsize (__buf))
++	return __ptsname_r_chk_warn (__fd, __buf, __buflen,
++				     __glibc_objsize (__buf));
+     }
+   return __ptsname_r_alias (__fd, __buf, __buflen);
+ }
+@@ -89,8 +92,9 @@ __NTH (wctomb (char *__s, wchar_t __wchar))
+ #if defined MB_LEN_MAX && MB_LEN_MAX != __STDLIB_MB_LEN_MAX
+ # error "Assumed value of MB_LEN_MAX wrong"
+ #endif
+-  if (__bos (__s) != (size_t) -1 && __STDLIB_MB_LEN_MAX > __bos (__s))
+-    return __wctomb_chk (__s, __wchar, __bos (__s));
++  if (__glibc_objsize (__s) != (size_t) -1
++      && __STDLIB_MB_LEN_MAX > __glibc_objsize (__s))
++    return __wctomb_chk (__s, __wchar, __glibc_objsize (__s));
+   return __wctomb_alias (__s, __wchar);
+ }
+ 
+@@ -113,15 +117,16 @@ __fortify_function size_t
+ __NTH (mbstowcs (wchar_t *__restrict __dst, const char *__restrict __src,
+ 		 size_t __len))
+ {
+-  if (__bos (__dst) != (size_t) -1)
++  if (__glibc_objsize (__dst) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__len))
+ 	return __mbstowcs_chk (__dst, __src, __len,
+-			       __bos (__dst) / sizeof (wchar_t));
++			       __glibc_objsize (__dst) / sizeof (wchar_t));
+ 
+-      if (__len > __bos (__dst) / sizeof (wchar_t))
++      if (__len > __glibc_objsize (__dst) / sizeof (wchar_t))
+ 	return __mbstowcs_chk_warn (__dst, __src, __len,
+-				     __bos (__dst) / sizeof (wchar_t));
++				    (__glibc_objsize (__dst)
++				     / sizeof (wchar_t)));
+     }
+   return __mbstowcs_alias (__dst, __src, __len);
+ }
+@@ -144,12 +149,13 @@ __fortify_function size_t
+ __NTH (wcstombs (char *__restrict __dst, const wchar_t *__restrict __src,
+ 		 size_t __len))
+ {
+-  if (__bos (__dst) != (size_t) -1)
++  if (__glibc_objsize (__dst) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__len))
+-	return __wcstombs_chk (__dst, __src, __len, __bos (__dst));
+-      if (__len > __bos (__dst))
+-	return __wcstombs_chk_warn (__dst, __src, __len, __bos (__dst));
++	return __wcstombs_chk (__dst, __src, __len, __glibc_objsize (__dst));
++      if (__len > __glibc_objsize (__dst))
++	return __wcstombs_chk_warn (__dst, __src, __len,
++				    __glibc_objsize (__dst));
+     }
+   return __wcstombs_alias (__dst, __src, __len);
+ }
+diff --git a/wcsmbs/bits/wchar2.h b/wcsmbs/bits/wchar2.h
+index d62b86de3e288d53..838ba877ee4b4afe 100644
+--- a/wcsmbs/bits/wchar2.h
++++ b/wcsmbs/bits/wchar2.h
+@@ -39,15 +39,15 @@ __fortify_function wchar_t *
+ __NTH (wmemcpy (wchar_t *__restrict __s1, const wchar_t *__restrict __s2,
+ 		size_t __n))
+ {
+-  if (__bos0 (__s1) != (size_t) -1)
++  if (__glibc_objsize0 (__s1) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n))
+ 	return __wmemcpy_chk (__s1, __s2, __n,
+-			      __bos0 (__s1) / sizeof (wchar_t));
++			      __glibc_objsize0 (__s1) / sizeof (wchar_t));
+ 
+-      if (__n > __bos0 (__s1) / sizeof (wchar_t))
++      if (__n > __glibc_objsize0 (__s1) / sizeof (wchar_t))
+ 	return __wmemcpy_chk_warn (__s1, __s2, __n,
+-				   __bos0 (__s1) / sizeof (wchar_t));
++				   __glibc_objsize0 (__s1) / sizeof (wchar_t));
+     }
+   return __wmemcpy_alias (__s1, __s2, __n);
+ }
+@@ -67,15 +67,16 @@ extern wchar_t *__REDIRECT_NTH (__wmemmove_chk_warn,
+ __fortify_function wchar_t *
+ __NTH (wmemmove (wchar_t *__s1, const wchar_t *__s2, size_t __n))
+ {
+-  if (__bos0 (__s1) != (size_t) -1)
++  if (__glibc_objsize0 (__s1) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n))
+ 	return __wmemmove_chk (__s1, __s2, __n,
+-			       __bos0 (__s1) / sizeof (wchar_t));
++			       __glibc_objsize0 (__s1) / sizeof (wchar_t));
+ 
+-      if (__n > __bos0 (__s1) / sizeof (wchar_t))
++      if (__n > __glibc_objsize0 (__s1) / sizeof (wchar_t))
+ 	return __wmemmove_chk_warn (__s1, __s2, __n,
+-				    __bos0 (__s1) / sizeof (wchar_t));
++				    (__glibc_objsize0 (__s1)
++				     / sizeof (wchar_t)));
+     }
+   return __wmemmove_alias (__s1, __s2, __n);
+ }
+@@ -100,15 +101,16 @@ __fortify_function wchar_t *
+ __NTH (wmempcpy (wchar_t *__restrict __s1, const wchar_t *__restrict __s2,
+ 		 size_t __n))
+ {
+-  if (__bos0 (__s1) != (size_t) -1)
++  if (__glibc_objsize0 (__s1) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n))
+ 	return __wmempcpy_chk (__s1, __s2, __n,
+-			       __bos0 (__s1) / sizeof (wchar_t));
++			       __glibc_objsize0 (__s1) / sizeof (wchar_t));
+ 
+-      if (__n > __bos0 (__s1) / sizeof (wchar_t))
++      if (__n > __glibc_objsize0 (__s1) / sizeof (wchar_t))
+ 	return __wmempcpy_chk_warn (__s1, __s2, __n,
+-				    __bos0 (__s1) / sizeof (wchar_t));
++				    (__glibc_objsize0 (__s1)
++				     / sizeof (wchar_t)));
+     }
+   return __wmempcpy_alias (__s1, __s2, __n);
+ }
+@@ -128,14 +130,15 @@ extern wchar_t *__REDIRECT_NTH (__wmemset_chk_warn,
+ __fortify_function wchar_t *
+ __NTH (wmemset (wchar_t *__s, wchar_t __c, size_t __n))
+ {
+-  if (__bos0 (__s) != (size_t) -1)
++  if (__glibc_objsize0 (__s) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n))
+-	return __wmemset_chk (__s, __c, __n, __bos0 (__s) / sizeof (wchar_t));
++	return __wmemset_chk (__s, __c, __n,
++			      __glibc_objsize0 (__s) / sizeof (wchar_t));
+ 
+-      if (__n > __bos0 (__s) / sizeof (wchar_t))
++      if (__n > __glibc_objsize0 (__s) / sizeof (wchar_t))
+ 	return __wmemset_chk_warn (__s, __c, __n,
+-				   __bos0 (__s) / sizeof (wchar_t));
++				   __glibc_objsize0 (__s) / sizeof (wchar_t));
+     }
+   return __wmemset_alias (__s, __c, __n);
+ }
+@@ -151,8 +154,9 @@ extern wchar_t *__REDIRECT_NTH (__wcscpy_alias,
+ __fortify_function wchar_t *
+ __NTH (wcscpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src))
+ {
+-  if (__bos (__dest) != (size_t) -1)
+-    return __wcscpy_chk (__dest, __src, __bos (__dest) / sizeof (wchar_t));
++  if (__glibc_objsize (__dest) != (size_t) -1)
++    return __wcscpy_chk (__dest, __src,
++			 __glibc_objsize (__dest) / sizeof (wchar_t));
+   return __wcscpy_alias (__dest, __src);
+ }
+ 
+@@ -167,8 +171,9 @@ extern wchar_t *__REDIRECT_NTH (__wcpcpy_alias,
+ __fortify_function wchar_t *
+ __NTH (wcpcpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src))
+ {
+-  if (__bos (__dest) != (size_t) -1)
+-    return __wcpcpy_chk (__dest, __src, __bos (__dest) / sizeof (wchar_t));
++  if (__glibc_objsize (__dest) != (size_t) -1)
++    return __wcpcpy_chk (__dest, __src,
++			 __glibc_objsize (__dest) / sizeof (wchar_t));
+   return __wcpcpy_alias (__dest, __src);
+ }
+ 
+@@ -191,14 +196,15 @@ __fortify_function wchar_t *
+ __NTH (wcsncpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
+ 		size_t __n))
+ {
+-  if (__bos (__dest) != (size_t) -1)
++  if (__glibc_objsize (__dest) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n))
+ 	return __wcsncpy_chk (__dest, __src, __n,
+-			      __bos (__dest) / sizeof (wchar_t));
+-      if (__n > __bos (__dest) / sizeof (wchar_t))
++			      __glibc_objsize (__dest) / sizeof (wchar_t));
++      if (__n > __glibc_objsize (__dest) / sizeof (wchar_t))
+ 	return __wcsncpy_chk_warn (__dest, __src, __n,
+-				   __bos (__dest) / sizeof (wchar_t));
++				   (__glibc_objsize (__dest)
++				    / sizeof (wchar_t)));
+     }
+   return __wcsncpy_alias (__dest, __src, __n);
+ }
+@@ -222,14 +228,15 @@ __fortify_function wchar_t *
+ __NTH (wcpncpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
+ 		size_t __n))
+ {
+-  if (__bos (__dest) != (size_t) -1)
++  if (__glibc_objsize (__dest) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n))
+ 	return __wcpncpy_chk (__dest, __src, __n,
+-			      __bos (__dest) / sizeof (wchar_t));
+-      if (__n > __bos (__dest) / sizeof (wchar_t))
++			      __glibc_objsize (__dest) / sizeof (wchar_t));
++      if (__n > __glibc_objsize (__dest) / sizeof (wchar_t))
+ 	return __wcpncpy_chk_warn (__dest, __src, __n,
+-				   __bos (__dest) / sizeof (wchar_t));
++				   (__glibc_objsize (__dest)
++				    / sizeof (wchar_t)));
+     }
+   return __wcpncpy_alias (__dest, __src, __n);
+ }
+@@ -245,8 +252,9 @@ extern wchar_t *__REDIRECT_NTH (__wcscat_alias,
+ __fortify_function wchar_t *
+ __NTH (wcscat (wchar_t *__restrict __dest, const wchar_t *__restrict __src))
+ {
+-  if (__bos (__dest) != (size_t) -1)
+-    return __wcscat_chk (__dest, __src, __bos (__dest) / sizeof (wchar_t));
++  if (__glibc_objsize (__dest) != (size_t) -1)
++    return __wcscat_chk (__dest, __src,
++			 __glibc_objsize (__dest) / sizeof (wchar_t));
+   return __wcscat_alias (__dest, __src);
+ }
+ 
+@@ -263,9 +271,9 @@ __fortify_function wchar_t *
+ __NTH (wcsncat (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
+ 		size_t __n))
+ {
+-  if (__bos (__dest) != (size_t) -1)
++  if (__glibc_objsize (__dest) != (size_t) -1)
+     return __wcsncat_chk (__dest, __src, __n,
+-			  __bos (__dest) / sizeof (wchar_t));
++			  __glibc_objsize (__dest) / sizeof (wchar_t));
+   return __wcsncat_alias (__dest, __src, __n);
+ }
+ 
+@@ -285,18 +293,18 @@ __fortify_function int
+ __NTH (swprintf (wchar_t *__restrict __s, size_t __n,
+ 		 const wchar_t *__restrict __fmt, ...))
+ {
+-  if (__bos (__s) != (size_t) -1 || __USE_FORTIFY_LEVEL > 1)
++  if (__glibc_objsize (__s) != (size_t) -1 || __USE_FORTIFY_LEVEL > 1)
+     return __swprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1,
+-			   __bos (__s) / sizeof (wchar_t),
++			   __glibc_objsize (__s) / sizeof (wchar_t),
+ 			   __fmt, __va_arg_pack ());
+   return __swprintf_alias (__s, __n, __fmt, __va_arg_pack ());
+ }
+ #elif !defined __cplusplus
+ /* XXX We might want to have support in gcc for swprintf.  */
+ # define swprintf(s, n, ...) \
+-  (__bos (s) != (size_t) -1 || __USE_FORTIFY_LEVEL > 1			      \
++  (__glibc_objsize (s) != (size_t) -1 || __USE_FORTIFY_LEVEL > 1		      \
+    ? __swprintf_chk (s, n, __USE_FORTIFY_LEVEL - 1,			      \
+-		     __bos (s) / sizeof (wchar_t), __VA_ARGS__)		      \
++		     __glibc_objsize (s) / sizeof (wchar_t), __VA_ARGS__)	      \
+    : swprintf (s, n, __VA_ARGS__))
+ #endif
+ 
+@@ -315,9 +323,10 @@ __fortify_function int
+ __NTH (vswprintf (wchar_t *__restrict __s, size_t __n,
+ 		  const wchar_t *__restrict __fmt, __gnuc_va_list __ap))
+ {
+-  if (__bos (__s) != (size_t) -1 || __USE_FORTIFY_LEVEL > 1)
++  if (__glibc_objsize (__s) != (size_t) -1 || __USE_FORTIFY_LEVEL > 1)
+     return __vswprintf_chk (__s, __n,  __USE_FORTIFY_LEVEL - 1,
+-			    __bos (__s) / sizeof (wchar_t), __fmt, __ap);
++			    __glibc_objsize (__s) / sizeof (wchar_t), __fmt,
++			    __ap);
+   return __vswprintf_alias (__s, __n, __fmt, __ap);
+ }
+ 
+@@ -383,14 +392,15 @@ extern wchar_t *__REDIRECT (__fgetws_chk_warn,
+ __fortify_function __wur wchar_t *
+ fgetws (wchar_t *__restrict __s, int __n, __FILE *__restrict __stream)
+ {
+-  if (__bos (__s) != (size_t) -1)
++  if (__glibc_objsize (__s) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n) || __n <= 0)
+-	return __fgetws_chk (__s, __bos (__s) / sizeof (wchar_t),
++	return __fgetws_chk (__s, __glibc_objsize (__s) / sizeof (wchar_t),
+ 			     __n, __stream);
+ 
+-      if ((size_t) __n > __bos (__s) / sizeof (wchar_t))
+-	return __fgetws_chk_warn (__s, __bos (__s) / sizeof (wchar_t),
++      if ((size_t) __n > __glibc_objsize (__s) / sizeof (wchar_t))
++	return __fgetws_chk_warn (__s,
++				  __glibc_objsize (__s) / sizeof (wchar_t),
+ 				  __n, __stream);
+     }
+   return __fgetws_alias (__s, __n, __stream);
+@@ -414,14 +424,17 @@ extern wchar_t *__REDIRECT (__fgetws_unlocked_chk_warn,
+ __fortify_function __wur wchar_t *
+ fgetws_unlocked (wchar_t *__restrict __s, int __n, __FILE *__restrict __stream)
+ {
+-  if (__bos (__s) != (size_t) -1)
++  if (__glibc_objsize (__s) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__n) || __n <= 0)
+-	return __fgetws_unlocked_chk (__s, __bos (__s) / sizeof (wchar_t),
++	return __fgetws_unlocked_chk (__s,
++				      __glibc_objsize (__s) / sizeof (wchar_t),
+ 				      __n, __stream);
+ 
+-      if ((size_t) __n > __bos (__s) / sizeof (wchar_t))
+-	return __fgetws_unlocked_chk_warn (__s, __bos (__s) / sizeof (wchar_t),
++      if ((size_t) __n > __glibc_objsize (__s) / sizeof (wchar_t))
++	return __fgetws_unlocked_chk_warn (__s,
++					   (__glibc_objsize (__s)
++					    / sizeof (wchar_t)),
+ 					   __n, __stream);
+     }
+   return __fgetws_unlocked_alias (__s, __n, __stream);
+@@ -447,8 +460,9 @@ __NTH (wcrtomb (char *__restrict __s, wchar_t __wchar,
+ #if defined MB_LEN_MAX && MB_LEN_MAX != __WCHAR_MB_LEN_MAX
+ # error "Assumed value of MB_LEN_MAX wrong"
+ #endif
+-  if (__bos (__s) != (size_t) -1 && __WCHAR_MB_LEN_MAX > __bos (__s))
+-    return __wcrtomb_chk (__s, __wchar, __ps, __bos (__s));
++  if (__glibc_objsize (__s) != (size_t) -1
++      && __WCHAR_MB_LEN_MAX > __glibc_objsize (__s))
++    return __wcrtomb_chk (__s, __wchar, __ps, __glibc_objsize (__s));
+   return __wcrtomb_alias (__s, __wchar, __ps);
+ }
+ 
+@@ -474,15 +488,16 @@ __fortify_function size_t
+ __NTH (mbsrtowcs (wchar_t *__restrict __dst, const char **__restrict __src,
+ 		  size_t __len, mbstate_t *__restrict __ps))
+ {
+-  if (__bos (__dst) != (size_t) -1)
++  if (__glibc_objsize (__dst) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__len))
+ 	return __mbsrtowcs_chk (__dst, __src, __len, __ps,
+-				__bos (__dst) / sizeof (wchar_t));
++				__glibc_objsize (__dst) / sizeof (wchar_t));
+ 
+-      if (__len > __bos (__dst) / sizeof (wchar_t))
++      if (__len > __glibc_objsize (__dst) / sizeof (wchar_t))
+ 	return __mbsrtowcs_chk_warn (__dst, __src, __len, __ps,
+-				     __bos (__dst) / sizeof (wchar_t));
++				     (__glibc_objsize (__dst)
++				      / sizeof (wchar_t)));
+     }
+   return __mbsrtowcs_alias (__dst, __src, __len, __ps);
+ }
+@@ -508,13 +523,15 @@ __fortify_function size_t
+ __NTH (wcsrtombs (char *__restrict __dst, const wchar_t **__restrict __src,
+ 		  size_t __len, mbstate_t *__restrict __ps))
+ {
+-  if (__bos (__dst) != (size_t) -1)
++  if (__glibc_objsize (__dst) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__len))
+-	return __wcsrtombs_chk (__dst, __src, __len, __ps, __bos (__dst));
++	return __wcsrtombs_chk (__dst, __src, __len, __ps,
++				__glibc_objsize (__dst));
+ 
+-      if (__len > __bos (__dst))
+-	return __wcsrtombs_chk_warn (__dst, __src, __len, __ps, __bos (__dst));
++      if (__len > __glibc_objsize (__dst))
++	return __wcsrtombs_chk_warn (__dst, __src, __len, __ps,
++				     __glibc_objsize (__dst));
+     }
+   return __wcsrtombs_alias (__dst, __src, __len, __ps);
+ }
+@@ -542,15 +559,16 @@ __fortify_function size_t
+ __NTH (mbsnrtowcs (wchar_t *__restrict __dst, const char **__restrict __src,
+ 		   size_t __nmc, size_t __len, mbstate_t *__restrict __ps))
+ {
+-  if (__bos (__dst) != (size_t) -1)
++  if (__glibc_objsize (__dst) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__len))
+ 	return __mbsnrtowcs_chk (__dst, __src, __nmc, __len, __ps,
+-				 __bos (__dst) / sizeof (wchar_t));
++				 __glibc_objsize (__dst) / sizeof (wchar_t));
+ 
+-      if (__len > __bos (__dst) / sizeof (wchar_t))
++      if (__len > __glibc_objsize (__dst) / sizeof (wchar_t))
+ 	return __mbsnrtowcs_chk_warn (__dst, __src, __nmc, __len, __ps,
+-				      __bos (__dst) / sizeof (wchar_t));
++				      (__glibc_objsize (__dst)
++				       / sizeof (wchar_t)));
+     }
+   return __mbsnrtowcs_alias (__dst, __src, __nmc, __len, __ps);
+ }
+@@ -578,15 +596,15 @@ __fortify_function size_t
+ __NTH (wcsnrtombs (char *__restrict __dst, const wchar_t **__restrict __src,
+ 		   size_t __nwc, size_t __len, mbstate_t *__restrict __ps))
+ {
+-  if (__bos (__dst) != (size_t) -1)
++  if (__glibc_objsize (__dst) != (size_t) -1)
+     {
+       if (!__builtin_constant_p (__len))
+ 	return __wcsnrtombs_chk (__dst, __src, __nwc, __len, __ps,
+-				 __bos (__dst));
++				 __glibc_objsize (__dst));
+ 
+-      if (__len > __bos (__dst))
++      if (__len > __glibc_objsize (__dst))
+ 	return __wcsnrtombs_chk_warn (__dst, __src, __nwc, __len, __ps,
+-				      __bos (__dst));
++				      __glibc_objsize (__dst));
+     }
+   return __wcsnrtombs_alias (__dst, __src, __nwc, __len, __ps);
+ }
diff --git a/SOURCES/glibc-rh2033684-6.patch b/SOURCES/glibc-rh2033684-6.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b183d70bedb4f00b67360e75f271c1c1d26204a1
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-6.patch
@@ -0,0 +1,1037 @@
+commit a643f60c53876be0d57b4b7373770e6cb356fd13
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Wed Oct 20 18:12:41 2021 +0530
+
+    Make sure that the fortified function conditionals are constant
+    
+    In _FORTIFY_SOURCE=3, the size expression may be non-constant,
+    resulting in branches in the inline functions remaining intact and
+    causing a tiny overhead.  Clang (and in future, gcc) make sure that
+    the -1 case is always safe, i.e. any comparison of the generated
+    expression with (size_t)-1 is always false so that bit is taken care
+    of.  The rest is avoidable since we want the _chk variant whenever we
+    have a size expression and it's not -1.
+    
+    Rework the conditionals in a uniform way to clearly indicate two
+    conditions at compile time:
+    
+    - Either the size is unknown (-1) or we know at compile time that the
+      operation length is less than the object size.  We can call the
+      original function in this case.  It could be that either the length,
+      object size or both are non-constant, but the compiler, through
+      range analysis, is able to fold the *comparison* to a constant.
+    
+    - The size and length are known and the compiler can see at compile
+      time that operation length > object size.  This is valid grounds for
+      a warning at compile time, followed by emitting the _chk variant.
+    
+    For everything else, emit the _chk variant.
+    
+    This simplifies most of the fortified function implementations and at
+    the same time, ensures that only one call from _chk or the regular
+    function is emitted.
+    
+    Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/io/bits/poll2.h b/io/bits/poll2.h
+index f47fd9ad0945234f..6f4dae77e5e2d0d3 100644
+--- a/io/bits/poll2.h
++++ b/io/bits/poll2.h
+@@ -35,16 +35,9 @@ extern int __REDIRECT (__poll_chk_warn, (struct pollfd *__fds, nfds_t __nfds,
+ __fortify_function int
+ poll (struct pollfd *__fds, nfds_t __nfds, int __timeout)
+ {
+-  if (__glibc_objsize (__fds) != (__SIZE_TYPE__) -1)
+-    {
+-      if (! __builtin_constant_p (__nfds))
+-	return __poll_chk (__fds, __nfds, __timeout, __glibc_objsize (__fds));
+-      else if (__glibc_objsize (__fds) / sizeof (*__fds) < __nfds)
+-	return __poll_chk_warn (__fds, __nfds, __timeout,
+-				__glibc_objsize (__fds));
+-    }
+-
+-  return __poll_alias (__fds, __nfds, __timeout);
++  return __glibc_fortify (poll, __nfds, sizeof (*__fds),
++			  __glibc_objsize (__fds),
++			  __fds, __nfds, __timeout);
+ }
+ 
+ 
+@@ -66,17 +59,9 @@ __fortify_function int
+ ppoll (struct pollfd *__fds, nfds_t __nfds, const struct timespec *__timeout,
+        const __sigset_t *__ss)
+ {
+-  if (__glibc_objsize (__fds) != (__SIZE_TYPE__) -1)
+-    {
+-      if (! __builtin_constant_p (__nfds))
+-	return __ppoll_chk (__fds, __nfds, __timeout, __ss,
+-			    __glibc_objsize (__fds));
+-      else if (__glibc_objsize (__fds) / sizeof (*__fds) < __nfds)
+-	return __ppoll_chk_warn (__fds, __nfds, __timeout, __ss,
+-				 __glibc_objsize (__fds));
+-    }
+-
+-  return __ppoll_alias (__fds, __nfds, __timeout, __ss);
++  return __glibc_fortify (ppoll, __nfds, sizeof (*__fds),
++			  __glibc_objsize (__fds),
++			  __fds, __nfds, __timeout, __ss);
+ }
+ #endif
+ 
+diff --git a/libio/bits/stdio2.h b/libio/bits/stdio2.h
+index 2cd69f44cfadfc9f..4630fe0256b1a562 100644
+--- a/libio/bits/stdio2.h
++++ b/libio/bits/stdio2.h
+@@ -256,15 +256,12 @@ extern char *__REDIRECT (__fgets_chk_warn,
+ __fortify_function __wur char *
+ fgets (char *__restrict __s, int __n, FILE *__restrict __stream)
+ {
+-  if (__glibc_objsize (__s) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n) || __n <= 0)
+-	return __fgets_chk (__s, __glibc_objsize (__s), __n, __stream);
+-
+-      if ((size_t) __n > __glibc_objsize (__s))
+-	return __fgets_chk_warn (__s, __glibc_objsize (__s), __n, __stream);
+-    }
+-  return __fgets_alias (__s, __n, __stream);
++  size_t sz = __glibc_objsize (__s);
++  if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz))
++    return __fgets_alias (__s, __n, __stream);
++  if (__glibc_unsafe_len (__n, sizeof (char), sz))
++    return __fgets_chk_warn (__s, sz, __n, __stream);
++  return __fgets_chk (__s, sz, __n, __stream);
+ }
+ 
+ extern size_t __fread_chk (void *__restrict __ptr, size_t __ptrlen,
+@@ -286,19 +283,12 @@ __fortify_function __wur size_t
+ fread (void *__restrict __ptr, size_t __size, size_t __n,
+        FILE *__restrict __stream)
+ {
+-  if (__glibc_objsize0 (__ptr) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__size)
+-	  || !__builtin_constant_p (__n)
+-	  || (__size | __n) >= (((size_t) 1) << (8 * sizeof (size_t) / 2)))
+-	return __fread_chk (__ptr, __glibc_objsize0 (__ptr), __size, __n,
+-			    __stream);
+-
+-      if (__size * __n > __glibc_objsize0 (__ptr))
+-	return __fread_chk_warn (__ptr, __glibc_objsize0 (__ptr), __size, __n,
+-				 __stream);
+-    }
+-  return __fread_alias (__ptr, __size, __n, __stream);
++  size_t sz = __glibc_objsize0 (__ptr);
++  if (__glibc_safe_or_unknown_len (__n, __size, sz))
++    return __fread_alias (__ptr, __size, __n, __stream);
++  if (__glibc_unsafe_len (__n, __size, sz))
++    return __fread_chk_warn (__ptr, sz, __size, __n, __stream);
++  return __fread_chk (__ptr, sz, __size, __n, __stream);
+ }
+ 
+ #ifdef __USE_GNU
+@@ -316,17 +306,12 @@ extern char *__REDIRECT (__fgets_unlocked_chk_warn,
+ __fortify_function __wur char *
+ fgets_unlocked (char *__restrict __s, int __n, FILE *__restrict __stream)
+ {
+-  if (__glibc_objsize (__s) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n) || __n <= 0)
+-	return __fgets_unlocked_chk (__s, __glibc_objsize (__s), __n,
+-				     __stream);
+-
+-      if ((size_t) __n > __glibc_objsize (__s))
+-	return __fgets_unlocked_chk_warn (__s, __glibc_objsize (__s), __n,
+-					  __stream);
+-    }
+-  return __fgets_unlocked_alias (__s, __n, __stream);
++  size_t sz = __glibc_objsize (__s);
++  if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz))
++    return __fgets_unlocked_alias (__s, __n, __stream);
++  if (__glibc_unsafe_len (__n, sizeof (char), sz))
++    return __fgets_unlocked_chk_warn (__s, sz, __n, __stream);
++  return __fgets_unlocked_chk (__s, sz, __n, __stream);
+ }
+ #endif
+ 
+@@ -351,41 +336,36 @@ __fortify_function __wur size_t
+ fread_unlocked (void *__restrict __ptr, size_t __size, size_t __n,
+ 		FILE *__restrict __stream)
+ {
+-  if (__glibc_objsize0 (__ptr) != (size_t) -1)
++  size_t sz = __glibc_objsize0 (__ptr);
++  if (__glibc_safe_or_unknown_len (__n, __size, sz))
+     {
+-      if (!__builtin_constant_p (__size)
+-	  || !__builtin_constant_p (__n)
+-	  || (__size | __n) >= (((size_t) 1) << (8 * sizeof (size_t) / 2)))
+-	return __fread_unlocked_chk (__ptr, __glibc_objsize0 (__ptr), __size,
+-				     __n, __stream);
+-
+-      if (__size * __n > __glibc_objsize0 (__ptr))
+-	return __fread_unlocked_chk_warn (__ptr, __glibc_objsize0 (__ptr),
+-					  __size, __n, __stream);
+-    }
+-
+ # ifdef __USE_EXTERN_INLINES
+-  if (__builtin_constant_p (__size)
+-      && __builtin_constant_p (__n)
+-      && (__size | __n) < (((size_t) 1) << (8 * sizeof (size_t) / 2))
+-      && __size * __n <= 8)
+-    {
+-      size_t __cnt = __size * __n;
+-      char *__cptr = (char *) __ptr;
+-      if (__cnt == 0)
+-	return 0;
+-
+-      for (; __cnt > 0; --__cnt)
++      if (__builtin_constant_p (__size)
++	  && __builtin_constant_p (__n)
++	  && (__size | __n) < (((size_t) 1) << (8 * sizeof (size_t) / 2))
++	  && __size * __n <= 8)
+ 	{
+-	  int __c = getc_unlocked (__stream);
+-	  if (__c == EOF)
+-	    break;
+-	  *__cptr++ = __c;
++	  size_t __cnt = __size * __n;
++	  char *__cptr = (char *) __ptr;
++	  if (__cnt == 0)
++	    return 0;
++
++	  for (; __cnt > 0; --__cnt)
++	    {
++	      int __c = getc_unlocked (__stream);
++	      if (__c == EOF)
++		break;
++	      *__cptr++ = __c;
++	    }
++	  return (__cptr - (char *) __ptr) / __size;
+ 	}
+-      return (__cptr - (char *) __ptr) / __size;
+-    }
+ # endif
+-  return __fread_unlocked_alias (__ptr, __size, __n, __stream);
++      return __fread_unlocked_alias (__ptr, __size, __n, __stream);
++    }
++  if (__glibc_unsafe_len (__n, __size, sz))
++    return __fread_unlocked_chk_warn (__ptr, sz, __size, __n, __stream);
++  return __fread_unlocked_chk (__ptr, sz, __size, __n, __stream);
++
+ }
+ #endif
+ 
+diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
+index 1e39307b0ebcf38f..17b84a2e6c69d961 100644
+--- a/misc/sys/cdefs.h
++++ b/misc/sys/cdefs.h
+@@ -132,6 +132,53 @@
+ # define __glibc_objsize(__o) __bos (__o)
+ #endif
+ 
++/* Compile time conditions to choose between the regular, _chk and _chk_warn
++   variants.  These conditions should get evaluated to constant and optimized
++   away.  */
++
++#define __glibc_safe_len_cond(__l, __s, __osz) ((__l) <= (__osz) / (__s))
++#define __glibc_unsigned_or_positive(__l) \
++  ((__typeof (__l)) 0 < (__typeof (__l)) -1				      \
++   || (__builtin_constant_p (__l) && (__l) > 0))
++
++/* Length is known to be safe at compile time if the __L * __S <= __OBJSZ
++   condition can be folded to a constant and if it is true.  The -1 check is
++   redundant because since it implies that __glibc_safe_len_cond is true.  */
++#define __glibc_safe_or_unknown_len(__l, __s, __osz) \
++  (__glibc_unsigned_or_positive (__l)					      \
++   && __builtin_constant_p (__glibc_safe_len_cond ((__SIZE_TYPE__) (__l),     \
++						   __s, __osz))		      \
++   && __glibc_safe_len_cond ((__SIZE_TYPE__) (__l), __s, __osz))
++
++/* Conversely, we know at compile time that the length is safe if the
++   __L * __S <= __OBJSZ condition can be folded to a constant and if it is
++   false.  */
++#define __glibc_unsafe_len(__l, __s, __osz) \
++  (__glibc_unsigned_or_positive (__l)					      \
++   && __builtin_constant_p (__glibc_safe_len_cond ((__SIZE_TYPE__) (__l),     \
++						   __s, __osz))		      \
++   && !__glibc_safe_len_cond ((__SIZE_TYPE__) (__l), __s, __osz))
++
++/* Fortify function f.  __f_alias, __f_chk and __f_chk_warn must be
++   declared.  */
++
++#define __glibc_fortify(f, __l, __s, __osz, ...) \
++  (__glibc_safe_or_unknown_len (__l, __s, __osz)			      \
++   ? __ ## f ## _alias (__VA_ARGS__)					      \
++   : (__glibc_unsafe_len (__l, __s, __osz)				      \
++      ? __ ## f ## _chk_warn (__VA_ARGS__, __osz)			      \
++      : __ ## f ## _chk (__VA_ARGS__, __osz)))			      \
++
++/* Fortify function f, where object size argument passed to f is the number of
++   elements and not total size.  */
++
++#define __glibc_fortify_n(f, __l, __s, __osz, ...) \
++  (__glibc_safe_or_unknown_len (__l, __s, __osz)			      \
++   ? __ ## f ## _alias (__VA_ARGS__)					      \
++   : (__glibc_unsafe_len (__l, __s, __osz)				      \
++      ? __ ## f ## _chk_warn (__VA_ARGS__, (__osz) / (__s))		      \
++      : __ ## f ## _chk (__VA_ARGS__, (__osz) / (__s))))		      \
++
+ #if __GNUC_PREREQ (4,3)
+ # define __warndecl(name, msg) \
+   extern void name (void) __attribute__((__warning__ (msg)))
+diff --git a/posix/bits/unistd.h b/posix/bits/unistd.h
+index a0c4dcfe9c61a7b8..a456d1723547db70 100644
+--- a/posix/bits/unistd.h
++++ b/posix/bits/unistd.h
+@@ -33,16 +33,9 @@ extern ssize_t __REDIRECT (__read_chk_warn,
+ __fortify_function __wur ssize_t
+ read (int __fd, void *__buf, size_t __nbytes)
+ {
+-  if (__glibc_objsize0 (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__nbytes))
+-	return __read_chk (__fd, __buf, __nbytes, __glibc_objsize0 (__buf));
+-
+-      if (__nbytes > __glibc_objsize0 (__buf))
+-	return __read_chk_warn (__fd, __buf, __nbytes,
+-				__glibc_objsize0 (__buf));
+-    }
+-  return __read_alias (__fd, __buf, __nbytes);
++  return __glibc_fortify (read, __nbytes, sizeof (char),
++			  __glibc_objsize0 (__buf),
++			  __fd, __buf, __nbytes);
+ }
+ 
+ #ifdef __USE_UNIX98
+@@ -72,34 +65,17 @@ extern ssize_t __REDIRECT (__pread64_chk_warn,
+ __fortify_function __wur ssize_t
+ pread (int __fd, void *__buf, size_t __nbytes, __off_t __offset)
+ {
+-  if (__glibc_objsize0 (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__nbytes))
+-	return __pread_chk (__fd, __buf, __nbytes, __offset,
+-			    __glibc_objsize0 (__buf));
+-
+-      if ( __nbytes > __glibc_objsize0 (__buf))
+-	return __pread_chk_warn (__fd, __buf, __nbytes, __offset,
+-				 __glibc_objsize0 (__buf));
+-    }
+-  return __pread_alias (__fd, __buf, __nbytes, __offset);
++  return __glibc_fortify (pread, __nbytes, sizeof (char),
++			  __glibc_objsize0 (__buf),
++			  __fd, __buf, __nbytes, __offset);
+ }
+ # else
+ __fortify_function __wur ssize_t
+ pread (int __fd, void *__buf, size_t __nbytes, __off64_t __offset)
+ {
+-  if (__glibc_objsize0 (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__nbytes))
+-	return __pread64_chk (__fd, __buf, __nbytes, __offset,
+-			      __glibc_objsize0 (__buf));
+-
+-      if ( __nbytes > __glibc_objsize0 (__buf))
+-	return __pread64_chk_warn (__fd, __buf, __nbytes, __offset,
+-				   __glibc_objsize0 (__buf));
+-    }
+-
+-  return __pread64_alias (__fd, __buf, __nbytes, __offset);
++  return __glibc_fortify (pread64, __nbytes, sizeof (char),
++			  __glibc_objsize0 (__buf),
++			  __fd, __buf, __nbytes, __offset);
+ }
+ # endif
+ 
+@@ -107,18 +83,9 @@ pread (int __fd, void *__buf, size_t __nbytes, __off64_t __offset)
+ __fortify_function __wur ssize_t
+ pread64 (int __fd, void *__buf, size_t __nbytes, __off64_t __offset)
+ {
+-  if (__glibc_objsize0 (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__nbytes))
+-	return __pread64_chk (__fd, __buf, __nbytes, __offset,
+-			      __glibc_objsize0 (__buf));
+-
+-      if ( __nbytes > __glibc_objsize0 (__buf))
+-	return __pread64_chk_warn (__fd, __buf, __nbytes, __offset,
+-				   __glibc_objsize0 (__buf));
+-    }
+-
+-  return __pread64_alias (__fd, __buf, __nbytes, __offset);
++  return __glibc_fortify (pread64, __nbytes, sizeof (char),
++			  __glibc_objsize0 (__buf),
++			  __fd, __buf, __nbytes, __offset);
+ }
+ # endif
+ #endif
+@@ -143,16 +110,9 @@ __fortify_function __nonnull ((1, 2)) __wur ssize_t
+ __NTH (readlink (const char *__restrict __path, char *__restrict __buf,
+ 		 size_t __len))
+ {
+-  if (__glibc_objsize (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__len))
+-	return __readlink_chk (__path, __buf, __len, __glibc_objsize (__buf));
+-
+-      if ( __len > __glibc_objsize (__buf))
+-	return __readlink_chk_warn (__path, __buf, __len,
+-				    __glibc_objsize (__buf));
+-    }
+-  return __readlink_alias (__path, __buf, __len);
++  return __glibc_fortify (readlink, __len, sizeof (char),
++			  __glibc_objsize (__buf),
++			  __path, __buf, __len);
+ }
+ #endif
+ 
+@@ -178,17 +138,9 @@ __fortify_function __nonnull ((2, 3)) __wur ssize_t
+ __NTH (readlinkat (int __fd, const char *__restrict __path,
+ 		   char *__restrict __buf, size_t __len))
+ {
+-  if (__glibc_objsize (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__len))
+-	return __readlinkat_chk (__fd, __path, __buf, __len,
+-				 __glibc_objsize (__buf));
+-
+-      if (__len > __glibc_objsize (__buf))
+-	return __readlinkat_chk_warn (__fd, __path, __buf, __len,
+-				      __glibc_objsize (__buf));
+-    }
+-  return __readlinkat_alias (__fd, __path, __buf, __len);
++  return __glibc_fortify (readlinkat, __len, sizeof (char),
++			  __glibc_objsize (__buf),
++			  __fd, __path, __buf, __len);
+ }
+ #endif
+ 
+@@ -205,15 +157,9 @@ extern char *__REDIRECT_NTH (__getcwd_chk_warn,
+ __fortify_function __wur char *
+ __NTH (getcwd (char *__buf, size_t __size))
+ {
+-  if (__glibc_objsize (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__size))
+-	return __getcwd_chk (__buf, __size, __glibc_objsize (__buf));
+-
+-      if (__size > __glibc_objsize (__buf))
+-	return __getcwd_chk_warn (__buf, __size, __glibc_objsize (__buf));
+-    }
+-  return __getcwd_alias (__buf, __size);
++  return __glibc_fortify (getcwd, __size, sizeof (char),
++			  __glibc_objsize (__buf),
++			  __buf, __size);
+ }
+ 
+ #if defined __USE_MISC || defined __USE_XOPEN_EXTENDED
+@@ -245,16 +191,9 @@ extern size_t __REDIRECT_NTH (__confstr_chk_warn,
+ __fortify_function size_t
+ __NTH (confstr (int __name, char *__buf, size_t __len))
+ {
+-  if (__glibc_objsize (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__len))
+-	return __confstr_chk (__name, __buf, __len, __glibc_objsize (__buf));
+-
+-      if (__glibc_objsize (__buf) < __len)
+-	return __confstr_chk_warn (__name, __buf, __len,
+-				   __glibc_objsize (__buf));
+-    }
+-  return __confstr_alias (__name, __buf, __len);
++  return __glibc_fortify (confstr, __len, sizeof (char),
++			  __glibc_objsize (__buf),
++			  __name, __buf, __len);
+ }
+ 
+ 
+@@ -271,15 +210,9 @@ extern int __REDIRECT_NTH (__getgroups_chk_warn,
+ __fortify_function int
+ __NTH (getgroups (int __size, __gid_t __list[]))
+ {
+-  if (__glibc_objsize (__list) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__size) || __size < 0)
+-	return __getgroups_chk (__size, __list, __glibc_objsize (__list));
+-
+-      if (__size * sizeof (__gid_t) > __glibc_objsize (__list))
+-	return __getgroups_chk_warn (__size, __list, __glibc_objsize (__list));
+-    }
+-  return __getgroups_alias (__size, __list);
++  return __glibc_fortify (getgroups, __size, sizeof (__gid_t),
++			  __glibc_objsize (__list),
++			  __size, __list);
+ }
+ 
+ 
+@@ -297,17 +230,9 @@ extern int __REDIRECT_NTH (__ttyname_r_chk_warn,
+ __fortify_function int
+ __NTH (ttyname_r (int __fd, char *__buf, size_t __buflen))
+ {
+-  if (__glibc_objsize (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__buflen))
+-	return __ttyname_r_chk (__fd, __buf, __buflen,
+-				__glibc_objsize (__buf));
+-
+-      if (__buflen > __glibc_objsize (__buf))
+-	return __ttyname_r_chk_warn (__fd, __buf, __buflen,
+-				     __glibc_objsize (__buf));
+-    }
+-  return __ttyname_r_alias (__fd, __buf, __buflen);
++  return __glibc_fortify (ttyname_r, __buflen, sizeof (char),
++			  __glibc_objsize (__buf),
++			  __fd, __buf, __buflen);
+ }
+ 
+ 
+@@ -325,16 +250,9 @@ extern int __REDIRECT (__getlogin_r_chk_warn,
+ __fortify_function int
+ getlogin_r (char *__buf, size_t __buflen)
+ {
+-  if (__glibc_objsize (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__buflen))
+-	return __getlogin_r_chk (__buf, __buflen, __glibc_objsize (__buf));
+-
+-      if (__buflen > __glibc_objsize (__buf))
+-	return __getlogin_r_chk_warn (__buf, __buflen,
+-				      __glibc_objsize (__buf));
+-    }
+-  return __getlogin_r_alias (__buf, __buflen);
++  return __glibc_fortify (getlogin_r, __buflen, sizeof (char),
++			  __glibc_objsize (__buf),
++			  __buf, __buflen);
+ }
+ #endif
+ 
+@@ -353,16 +271,9 @@ extern int __REDIRECT_NTH (__gethostname_chk_warn,
+ __fortify_function int
+ __NTH (gethostname (char *__buf, size_t __buflen))
+ {
+-  if (__glibc_objsize (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__buflen))
+-	return __gethostname_chk (__buf, __buflen, __glibc_objsize (__buf));
+-
+-      if (__buflen > __glibc_objsize (__buf))
+-	return __gethostname_chk_warn (__buf, __buflen,
+-				       __glibc_objsize (__buf));
+-    }
+-  return __gethostname_alias (__buf, __buflen);
++  return __glibc_fortify (gethostname, __buflen, sizeof (char),
++			  __glibc_objsize (__buf),
++			  __buf, __buflen);
+ }
+ #endif
+ 
+@@ -383,15 +294,8 @@ extern int __REDIRECT_NTH (__getdomainname_chk_warn,
+ __fortify_function int
+ __NTH (getdomainname (char *__buf, size_t __buflen))
+ {
+-  if (__glibc_objsize (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__buflen))
+-	return __getdomainname_chk (__buf, __buflen, __glibc_objsize (__buf));
+-
+-      if (__buflen > __glibc_objsize (__buf))
+-	return __getdomainname_chk_warn (__buf, __buflen,
+-					 __glibc_objsize (__buf));
+-    }
+-  return __getdomainname_alias (__buf, __buflen);
++  return __glibc_fortify (getdomainname, __buflen, sizeof (char),
++			  __glibc_objsize (__buf),
++			  __buf, __buflen);
+ }
+ #endif
+diff --git a/socket/bits/socket2.h b/socket/bits/socket2.h
+index 729e5a4cc1f4cb92..68fe5435b3b29c2a 100644
+--- a/socket/bits/socket2.h
++++ b/socket/bits/socket2.h
+@@ -33,17 +33,12 @@ extern ssize_t __REDIRECT (__recv_chk_warn,
+ __fortify_function ssize_t
+ recv (int __fd, void *__buf, size_t __n, int __flags)
+ {
+-  if (__glibc_objsize0 (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n))
+-	return __recv_chk (__fd, __buf, __n, __glibc_objsize0 (__buf),
+-			   __flags);
+-
+-      if (__n > __glibc_objsize0 (__buf))
+-	return __recv_chk_warn (__fd, __buf, __n, __glibc_objsize0 (__buf),
+-				__flags);
+-    }
+-  return __recv_alias (__fd, __buf, __n, __flags);
++  size_t sz = __glibc_objsize0 (__buf);
++  if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz))
++    return __recv_alias (__fd, __buf, __n, __flags);
++  if (__glibc_unsafe_len (__n, sizeof (char), sz))
++    return __recv_chk_warn (__fd, __buf, __n, sz, __flags);
++  return __recv_chk (__fd, __buf, __n, sz, __flags);
+ }
+ 
+ extern ssize_t __recvfrom_chk (int __fd, void *__restrict __buf, size_t __n,
+@@ -66,14 +61,11 @@ __fortify_function ssize_t
+ recvfrom (int __fd, void *__restrict __buf, size_t __n, int __flags,
+ 	  __SOCKADDR_ARG __addr, socklen_t *__restrict __addr_len)
+ {
+-  if (__glibc_objsize0 (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n))
+-	return __recvfrom_chk (__fd, __buf, __n, __glibc_objsize0 (__buf),
+-			       __flags, __addr, __addr_len);
+-      if (__n > __glibc_objsize0 (__buf))
+-	return __recvfrom_chk_warn (__fd, __buf, __n, __glibc_objsize0 (__buf),
+-				    __flags, __addr, __addr_len);
+-    }
+-  return __recvfrom_alias (__fd, __buf, __n, __flags, __addr, __addr_len);
++  size_t sz = __glibc_objsize0 (__buf);
++  if (__glibc_safe_or_unknown_len (__n, sizeof (char), sz))
++    return __recvfrom_alias (__fd, __buf, __n, __flags, __addr, __addr_len);
++  if (__glibc_unsafe_len (__n, sizeof (char), sz))
++    return __recvfrom_chk_warn (__fd, __buf, __n, sz, __flags, __addr,
++				__addr_len);
++  return __recvfrom_chk (__fd, __buf, __n, sz, __flags, __addr, __addr_len);
+ }
+diff --git a/stdlib/bits/stdlib.h b/stdlib/bits/stdlib.h
+index 5e4114ded33f2033..7ea364a276497720 100644
+--- a/stdlib/bits/stdlib.h
++++ b/stdlib/bits/stdlib.h
+@@ -36,17 +36,16 @@ extern char *__REDIRECT_NTH (__realpath_chk_warn,
+ __fortify_function __wur char *
+ __NTH (realpath (const char *__restrict __name, char *__restrict __resolved))
+ {
+-  if (__glibc_objsize (__resolved) != (size_t) -1)
+-    {
++  size_t sz = __glibc_objsize (__resolved);
++
++  if (sz == (size_t) -1)
++    return __realpath_alias (__name, __resolved);
++
+ #if defined _LIBC_LIMITS_H_ && defined PATH_MAX
+-      if (__glibc_objsize (__resolved) < PATH_MAX)
+-	return __realpath_chk_warn (__name, __resolved,
+-				    __glibc_objsize (__resolved));
++  if (__glibc_unsafe_len (sz, sizeof (char), PATH_MAX))
++    return __realpath_chk_warn (__name, __resolved, sz);
+ #endif
+-      return __realpath_chk (__name, __resolved, __glibc_objsize (__resolved));
+-    }
+-
+-  return __realpath_alias (__name, __resolved);
++  return __realpath_chk (__name, __resolved, sz);
+ }
+ 
+ 
+@@ -64,16 +63,9 @@ extern int __REDIRECT_NTH (__ptsname_r_chk_warn,
+ __fortify_function int
+ __NTH (ptsname_r (int __fd, char *__buf, size_t __buflen))
+ {
+-  if (__glibc_objsize (__buf) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__buflen))
+-	return __ptsname_r_chk (__fd, __buf, __buflen,
+-				__glibc_objsize (__buf));
+-      if (__buflen > __glibc_objsize (__buf))
+-	return __ptsname_r_chk_warn (__fd, __buf, __buflen,
+-				     __glibc_objsize (__buf));
+-    }
+-  return __ptsname_r_alias (__fd, __buf, __buflen);
++  return __glibc_fortify (ptsname_r, __buflen, sizeof (char),
++			  __glibc_objsize (__buf),
++			  __fd, __buf, __buflen);
+ }
+ 
+ 
+@@ -117,18 +109,9 @@ __fortify_function size_t
+ __NTH (mbstowcs (wchar_t *__restrict __dst, const char *__restrict __src,
+ 		 size_t __len))
+ {
+-  if (__glibc_objsize (__dst) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__len))
+-	return __mbstowcs_chk (__dst, __src, __len,
+-			       __glibc_objsize (__dst) / sizeof (wchar_t));
+-
+-      if (__len > __glibc_objsize (__dst) / sizeof (wchar_t))
+-	return __mbstowcs_chk_warn (__dst, __src, __len,
+-				    (__glibc_objsize (__dst)
+-				     / sizeof (wchar_t)));
+-    }
+-  return __mbstowcs_alias (__dst, __src, __len);
++  return __glibc_fortify_n (mbstowcs, __len, sizeof (wchar_t),
++			    __glibc_objsize (__dst),
++			    __dst, __src, __len);
+ }
+ 
+ 
+@@ -149,13 +132,7 @@ __fortify_function size_t
+ __NTH (wcstombs (char *__restrict __dst, const wchar_t *__restrict __src,
+ 		 size_t __len))
+ {
+-  if (__glibc_objsize (__dst) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__len))
+-	return __wcstombs_chk (__dst, __src, __len, __glibc_objsize (__dst));
+-      if (__len > __glibc_objsize (__dst))
+-	return __wcstombs_chk_warn (__dst, __src, __len,
+-				    __glibc_objsize (__dst));
+-    }
+-  return __wcstombs_alias (__dst, __src, __len);
++  return __glibc_fortify (wcstombs, __len, sizeof (char),
++			  __glibc_objsize (__dst),
++			  __dst, __src, __len);
+ }
+diff --git a/wcsmbs/bits/wchar2.h b/wcsmbs/bits/wchar2.h
+index 838ba877ee4b4afe..f82bba481981e4fb 100644
+--- a/wcsmbs/bits/wchar2.h
++++ b/wcsmbs/bits/wchar2.h
+@@ -39,17 +39,9 @@ __fortify_function wchar_t *
+ __NTH (wmemcpy (wchar_t *__restrict __s1, const wchar_t *__restrict __s2,
+ 		size_t __n))
+ {
+-  if (__glibc_objsize0 (__s1) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n))
+-	return __wmemcpy_chk (__s1, __s2, __n,
+-			      __glibc_objsize0 (__s1) / sizeof (wchar_t));
+-
+-      if (__n > __glibc_objsize0 (__s1) / sizeof (wchar_t))
+-	return __wmemcpy_chk_warn (__s1, __s2, __n,
+-				   __glibc_objsize0 (__s1) / sizeof (wchar_t));
+-    }
+-  return __wmemcpy_alias (__s1, __s2, __n);
++  return __glibc_fortify_n (wmemcpy, __n, sizeof (wchar_t),
++			    __glibc_objsize0 (__s1),
++			    __s1, __s2, __n);
+ }
+ 
+ 
+@@ -67,18 +59,9 @@ extern wchar_t *__REDIRECT_NTH (__wmemmove_chk_warn,
+ __fortify_function wchar_t *
+ __NTH (wmemmove (wchar_t *__s1, const wchar_t *__s2, size_t __n))
+ {
+-  if (__glibc_objsize0 (__s1) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n))
+-	return __wmemmove_chk (__s1, __s2, __n,
+-			       __glibc_objsize0 (__s1) / sizeof (wchar_t));
+-
+-      if (__n > __glibc_objsize0 (__s1) / sizeof (wchar_t))
+-	return __wmemmove_chk_warn (__s1, __s2, __n,
+-				    (__glibc_objsize0 (__s1)
+-				     / sizeof (wchar_t)));
+-    }
+-  return __wmemmove_alias (__s1, __s2, __n);
++  return __glibc_fortify_n (wmemmove, __n, sizeof (wchar_t),
++			    __glibc_objsize0 (__s1),
++			    __s1, __s2, __n);
+ }
+ 
+ 
+@@ -101,18 +84,9 @@ __fortify_function wchar_t *
+ __NTH (wmempcpy (wchar_t *__restrict __s1, const wchar_t *__restrict __s2,
+ 		 size_t __n))
+ {
+-  if (__glibc_objsize0 (__s1) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n))
+-	return __wmempcpy_chk (__s1, __s2, __n,
+-			       __glibc_objsize0 (__s1) / sizeof (wchar_t));
+-
+-      if (__n > __glibc_objsize0 (__s1) / sizeof (wchar_t))
+-	return __wmempcpy_chk_warn (__s1, __s2, __n,
+-				    (__glibc_objsize0 (__s1)
+-				     / sizeof (wchar_t)));
+-    }
+-  return __wmempcpy_alias (__s1, __s2, __n);
++  return __glibc_fortify_n (wmempcpy, __n, sizeof (wchar_t),
++			    __glibc_objsize0 (__s1),
++			    __s1, __s2, __n);
+ }
+ #endif
+ 
+@@ -130,17 +104,9 @@ extern wchar_t *__REDIRECT_NTH (__wmemset_chk_warn,
+ __fortify_function wchar_t *
+ __NTH (wmemset (wchar_t *__s, wchar_t __c, size_t __n))
+ {
+-  if (__glibc_objsize0 (__s) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n))
+-	return __wmemset_chk (__s, __c, __n,
+-			      __glibc_objsize0 (__s) / sizeof (wchar_t));
+-
+-      if (__n > __glibc_objsize0 (__s) / sizeof (wchar_t))
+-	return __wmemset_chk_warn (__s, __c, __n,
+-				   __glibc_objsize0 (__s) / sizeof (wchar_t));
+-    }
+-  return __wmemset_alias (__s, __c, __n);
++  return __glibc_fortify_n (wmemset, __n, sizeof (wchar_t),
++			    __glibc_objsize0 (__s),
++			    __s, __c, __n);
+ }
+ 
+ 
+@@ -154,9 +120,9 @@ extern wchar_t *__REDIRECT_NTH (__wcscpy_alias,
+ __fortify_function wchar_t *
+ __NTH (wcscpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src))
+ {
+-  if (__glibc_objsize (__dest) != (size_t) -1)
+-    return __wcscpy_chk (__dest, __src,
+-			 __glibc_objsize (__dest) / sizeof (wchar_t));
++  size_t sz = __glibc_objsize (__dest);
++  if (sz != (size_t) -1)
++    return __wcscpy_chk (__dest, __src, sz / sizeof (wchar_t));
+   return __wcscpy_alias (__dest, __src);
+ }
+ 
+@@ -171,9 +137,9 @@ extern wchar_t *__REDIRECT_NTH (__wcpcpy_alias,
+ __fortify_function wchar_t *
+ __NTH (wcpcpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src))
+ {
+-  if (__glibc_objsize (__dest) != (size_t) -1)
+-    return __wcpcpy_chk (__dest, __src,
+-			 __glibc_objsize (__dest) / sizeof (wchar_t));
++  size_t sz = __glibc_objsize (__dest);
++  if (sz != (size_t) -1)
++    return __wcpcpy_chk (__dest, __src, sz / sizeof (wchar_t));
+   return __wcpcpy_alias (__dest, __src);
+ }
+ 
+@@ -196,17 +162,9 @@ __fortify_function wchar_t *
+ __NTH (wcsncpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
+ 		size_t __n))
+ {
+-  if (__glibc_objsize (__dest) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n))
+-	return __wcsncpy_chk (__dest, __src, __n,
+-			      __glibc_objsize (__dest) / sizeof (wchar_t));
+-      if (__n > __glibc_objsize (__dest) / sizeof (wchar_t))
+-	return __wcsncpy_chk_warn (__dest, __src, __n,
+-				   (__glibc_objsize (__dest)
+-				    / sizeof (wchar_t)));
+-    }
+-  return __wcsncpy_alias (__dest, __src, __n);
++  return __glibc_fortify_n (wcsncpy, __n, sizeof (wchar_t),
++			    __glibc_objsize (__dest),
++			    __dest, __src, __n);
+ }
+ 
+ 
+@@ -228,17 +186,9 @@ __fortify_function wchar_t *
+ __NTH (wcpncpy (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
+ 		size_t __n))
+ {
+-  if (__glibc_objsize (__dest) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n))
+-	return __wcpncpy_chk (__dest, __src, __n,
+-			      __glibc_objsize (__dest) / sizeof (wchar_t));
+-      if (__n > __glibc_objsize (__dest) / sizeof (wchar_t))
+-	return __wcpncpy_chk_warn (__dest, __src, __n,
+-				   (__glibc_objsize (__dest)
+-				    / sizeof (wchar_t)));
+-    }
+-  return __wcpncpy_alias (__dest, __src, __n);
++  return __glibc_fortify_n (wcpncpy, __n, sizeof (wchar_t),
++			    __glibc_objsize (__dest),
++			    __dest, __src, __n);
+ }
+ 
+ 
+@@ -252,9 +202,9 @@ extern wchar_t *__REDIRECT_NTH (__wcscat_alias,
+ __fortify_function wchar_t *
+ __NTH (wcscat (wchar_t *__restrict __dest, const wchar_t *__restrict __src))
+ {
+-  if (__glibc_objsize (__dest) != (size_t) -1)
+-    return __wcscat_chk (__dest, __src,
+-			 __glibc_objsize (__dest) / sizeof (wchar_t));
++  size_t sz = __glibc_objsize (__dest);
++  if (sz != (size_t) -1)
++    return __wcscat_chk (__dest, __src, sz / sizeof (wchar_t));
+   return __wcscat_alias (__dest, __src);
+ }
+ 
+@@ -271,9 +221,9 @@ __fortify_function wchar_t *
+ __NTH (wcsncat (wchar_t *__restrict __dest, const wchar_t *__restrict __src,
+ 		size_t __n))
+ {
+-  if (__glibc_objsize (__dest) != (size_t) -1)
+-    return __wcsncat_chk (__dest, __src, __n,
+-			  __glibc_objsize (__dest) / sizeof (wchar_t));
++  size_t sz = __glibc_objsize (__dest);
++  if (sz != (size_t) -1)
++    return __wcsncat_chk (__dest, __src, __n, sz / sizeof (wchar_t));
+   return __wcsncat_alias (__dest, __src, __n);
+ }
+ 
+@@ -293,10 +243,10 @@ __fortify_function int
+ __NTH (swprintf (wchar_t *__restrict __s, size_t __n,
+ 		 const wchar_t *__restrict __fmt, ...))
+ {
+-  if (__glibc_objsize (__s) != (size_t) -1 || __USE_FORTIFY_LEVEL > 1)
++  size_t sz = __glibc_objsize (__s);
++  if (sz != (size_t) -1 || __USE_FORTIFY_LEVEL > 1)
+     return __swprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1,
+-			   __glibc_objsize (__s) / sizeof (wchar_t),
+-			   __fmt, __va_arg_pack ());
++			   sz / sizeof (wchar_t), __fmt, __va_arg_pack ());
+   return __swprintf_alias (__s, __n, __fmt, __va_arg_pack ());
+ }
+ #elif !defined __cplusplus
+@@ -323,10 +273,10 @@ __fortify_function int
+ __NTH (vswprintf (wchar_t *__restrict __s, size_t __n,
+ 		  const wchar_t *__restrict __fmt, __gnuc_va_list __ap))
+ {
+-  if (__glibc_objsize (__s) != (size_t) -1 || __USE_FORTIFY_LEVEL > 1)
++  size_t sz = __glibc_objsize (__s);
++  if (sz != (size_t) -1 || __USE_FORTIFY_LEVEL > 1)
+     return __vswprintf_chk (__s, __n,  __USE_FORTIFY_LEVEL - 1,
+-			    __glibc_objsize (__s) / sizeof (wchar_t), __fmt,
+-			    __ap);
++			    sz / sizeof (wchar_t), __fmt, __ap);
+   return __vswprintf_alias (__s, __n, __fmt, __ap);
+ }
+ 
+@@ -392,18 +342,12 @@ extern wchar_t *__REDIRECT (__fgetws_chk_warn,
+ __fortify_function __wur wchar_t *
+ fgetws (wchar_t *__restrict __s, int __n, __FILE *__restrict __stream)
+ {
+-  if (__glibc_objsize (__s) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n) || __n <= 0)
+-	return __fgetws_chk (__s, __glibc_objsize (__s) / sizeof (wchar_t),
+-			     __n, __stream);
+-
+-      if ((size_t) __n > __glibc_objsize (__s) / sizeof (wchar_t))
+-	return __fgetws_chk_warn (__s,
+-				  __glibc_objsize (__s) / sizeof (wchar_t),
+-				  __n, __stream);
+-    }
+-  return __fgetws_alias (__s, __n, __stream);
++  size_t sz = __glibc_objsize (__s);
++  if (__glibc_safe_or_unknown_len (__n, sizeof (wchar_t), sz))
++    return __fgetws_alias (__s, __n, __stream);
++  if (__glibc_unsafe_len (__n, sizeof (wchar_t), sz))
++    return __fgetws_chk_warn (__s, sz / sizeof (wchar_t), __n, __stream);
++  return __fgetws_chk (__s, sz / sizeof (wchar_t), __n, __stream);
+ }
+ 
+ #ifdef __USE_GNU
+@@ -424,20 +368,13 @@ extern wchar_t *__REDIRECT (__fgetws_unlocked_chk_warn,
+ __fortify_function __wur wchar_t *
+ fgetws_unlocked (wchar_t *__restrict __s, int __n, __FILE *__restrict __stream)
+ {
+-  if (__glibc_objsize (__s) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__n) || __n <= 0)
+-	return __fgetws_unlocked_chk (__s,
+-				      __glibc_objsize (__s) / sizeof (wchar_t),
+-				      __n, __stream);
+-
+-      if ((size_t) __n > __glibc_objsize (__s) / sizeof (wchar_t))
+-	return __fgetws_unlocked_chk_warn (__s,
+-					   (__glibc_objsize (__s)
+-					    / sizeof (wchar_t)),
+-					   __n, __stream);
+-    }
+-  return __fgetws_unlocked_alias (__s, __n, __stream);
++  size_t sz = __glibc_objsize (__s);
++  if (__glibc_safe_or_unknown_len (__n, sizeof (wchar_t), sz))
++    return __fgetws_unlocked_alias (__s, __n, __stream);
++  if (__glibc_unsafe_len (__n, sizeof (wchar_t), sz))
++    return __fgetws_unlocked_chk_warn (__s, sz / sizeof (wchar_t), __n,
++				       __stream);
++  return __fgetws_unlocked_chk (__s, sz / sizeof (wchar_t), __n, __stream);
+ }
+ #endif
+ 
+@@ -488,18 +425,9 @@ __fortify_function size_t
+ __NTH (mbsrtowcs (wchar_t *__restrict __dst, const char **__restrict __src,
+ 		  size_t __len, mbstate_t *__restrict __ps))
+ {
+-  if (__glibc_objsize (__dst) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__len))
+-	return __mbsrtowcs_chk (__dst, __src, __len, __ps,
+-				__glibc_objsize (__dst) / sizeof (wchar_t));
+-
+-      if (__len > __glibc_objsize (__dst) / sizeof (wchar_t))
+-	return __mbsrtowcs_chk_warn (__dst, __src, __len, __ps,
+-				     (__glibc_objsize (__dst)
+-				      / sizeof (wchar_t)));
+-    }
+-  return __mbsrtowcs_alias (__dst, __src, __len, __ps);
++  return __glibc_fortify_n (mbsrtowcs, __len, sizeof (wchar_t),
++			    __glibc_objsize (__dst),
++			    __dst, __src, __len, __ps);
+ }
+ 
+ 
+@@ -523,17 +451,9 @@ __fortify_function size_t
+ __NTH (wcsrtombs (char *__restrict __dst, const wchar_t **__restrict __src,
+ 		  size_t __len, mbstate_t *__restrict __ps))
+ {
+-  if (__glibc_objsize (__dst) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__len))
+-	return __wcsrtombs_chk (__dst, __src, __len, __ps,
+-				__glibc_objsize (__dst));
+-
+-      if (__len > __glibc_objsize (__dst))
+-	return __wcsrtombs_chk_warn (__dst, __src, __len, __ps,
+-				     __glibc_objsize (__dst));
+-    }
+-  return __wcsrtombs_alias (__dst, __src, __len, __ps);
++  return __glibc_fortify (wcsrtombs, __len, sizeof (char),
++			  __glibc_objsize (__dst),
++			  __dst, __src, __len, __ps);
+ }
+ 
+ 
+@@ -559,18 +479,9 @@ __fortify_function size_t
+ __NTH (mbsnrtowcs (wchar_t *__restrict __dst, const char **__restrict __src,
+ 		   size_t __nmc, size_t __len, mbstate_t *__restrict __ps))
+ {
+-  if (__glibc_objsize (__dst) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__len))
+-	return __mbsnrtowcs_chk (__dst, __src, __nmc, __len, __ps,
+-				 __glibc_objsize (__dst) / sizeof (wchar_t));
+-
+-      if (__len > __glibc_objsize (__dst) / sizeof (wchar_t))
+-	return __mbsnrtowcs_chk_warn (__dst, __src, __nmc, __len, __ps,
+-				      (__glibc_objsize (__dst)
+-				       / sizeof (wchar_t)));
+-    }
+-  return __mbsnrtowcs_alias (__dst, __src, __nmc, __len, __ps);
++  return __glibc_fortify_n (mbsnrtowcs, __len, sizeof (wchar_t),
++			    __glibc_objsize (__dst),
++			    __dst, __src, __nmc, __len, __ps);
+ }
+ 
+ 
+@@ -596,16 +507,8 @@ __fortify_function size_t
+ __NTH (wcsnrtombs (char *__restrict __dst, const wchar_t **__restrict __src,
+ 		   size_t __nwc, size_t __len, mbstate_t *__restrict __ps))
+ {
+-  if (__glibc_objsize (__dst) != (size_t) -1)
+-    {
+-      if (!__builtin_constant_p (__len))
+-	return __wcsnrtombs_chk (__dst, __src, __nwc, __len, __ps,
+-				 __glibc_objsize (__dst));
+-
+-      if (__len > __glibc_objsize (__dst))
+-	return __wcsnrtombs_chk_warn (__dst, __src, __nwc, __len, __ps,
+-				      __glibc_objsize (__dst));
+-    }
+-  return __wcsnrtombs_alias (__dst, __src, __nwc, __len, __ps);
++  return __glibc_fortify (wcsnrtombs, __len, sizeof (char),
++			  __glibc_objsize (__dst),
++			  __dst, __src, __nwc, __len, __ps);
+ }
+ #endif
diff --git a/SOURCES/glibc-rh2033684-7.patch b/SOURCES/glibc-rh2033684-7.patch
new file mode 100644
index 0000000000000000000000000000000000000000..7cb18a6887bbd0af7c87a24919c3704854f95a7f
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-7.patch
@@ -0,0 +1,43 @@
+commit fadf75c370494da6a02274ebe79e45b2f22ebbd0
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Mon Feb 10 14:37:10 2020 +0100
+
+    debug: Add missing locale dependencies of fortify tests
+    
+    The missing dependencies result in failures like this if make check
+    is invoked with sufficient parallelism for the debug subdirectory:
+    
+    FAIL: debug/tst-chk2
+    FAIL: debug/tst-chk3
+    FAIL: debug/tst-chk4
+    FAIL: debug/tst-chk5
+    FAIL: debug/tst-chk6
+    FAIL: debug/tst-lfschk1
+    FAIL: debug/tst-lfschk2
+    FAIL: debug/tst-lfschk3
+    FAIL: debug/tst-lfschk4
+    FAIL: debug/tst-lfschk5
+    FAIL: debug/tst-lfschk6
+
+diff --git a/debug/Makefile b/debug/Makefile
+index 506cebc3c4ca19ff..5e45c9b41077f2fd 100644
+--- a/debug/Makefile
++++ b/debug/Makefile
+@@ -188,6 +188,17 @@ LOCALES := de_DE.UTF-8
+ include ../gen-locales.mk
+ 
+ $(objpfx)tst-chk1.out: $(gen-locales)
++$(objpfx)tst-chk2.out: $(gen-locales)
++$(objpfx)tst-chk3.out: $(gen-locales)
++$(objpfx)tst-chk4.out: $(gen-locales)
++$(objpfx)tst-chk5.out: $(gen-locales)
++$(objpfx)tst-chk6.out: $(gen-locales)
++$(objpfx)tst-lfschk1.out: $(gen-locales)
++$(objpfx)tst-lfschk2.out: $(gen-locales)
++$(objpfx)tst-lfschk3.out: $(gen-locales)
++$(objpfx)tst-lfschk4.out: $(gen-locales)
++$(objpfx)tst-lfschk5.out: $(gen-locales)
++$(objpfx)tst-lfschk6.out: $(gen-locales)
+ endif
+ 
+ sLIBdir := $(shell echo $(slibdir) | sed 's,lib\(\|64\)$$,\\\\$$LIB,')
diff --git a/SOURCES/glibc-rh2033684-8.patch b/SOURCES/glibc-rh2033684-8.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5b90b20b66f30c9782c6af4b50d434e90da51928
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-8.patch
@@ -0,0 +1,357 @@
+commit ad6f2a010c2ce759936de4747f6e0d53991912f8
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Wed Oct 20 18:13:05 2021 +0530
+
+    debug: Add tests for _FORTIFY_SOURCE=3
+    
+    Add some testing coverage for _FORTIFY_SOURCE=3.
+    
+    Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/debug/Makefile b/debug/Makefile
+index 5e45c9b41077f2fd..81361438fc3d2aa9 100644
+--- a/debug/Makefile
++++ b/debug/Makefile
+@@ -120,6 +120,8 @@ CFLAGS-tst-chk3.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+ CFLAGS-tst-chk4.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+ CFLAGS-tst-chk5.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+ CFLAGS-tst-chk6.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
++CFLAGS-tst-chk7.c += -Wno-format -Wno-deprecated-declarations -Wno-error
++CFLAGS-tst-chk8.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+ CFLAGS-tst-lfschk1.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+ CFLAGS-tst-lfschk2.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+ CFLAGS-tst-lfschk3.c += -Wno-format -Wno-deprecated-declarations -Wno-error
+@@ -129,6 +131,7 @@ CFLAGS-tst-lfschk6.cc += -Wno-format -Wno-deprecated-declarations -Wno-error
+ LDLIBS-tst-chk4 = -lstdc++
+ LDLIBS-tst-chk5 = -lstdc++
+ LDLIBS-tst-chk6 = -lstdc++
++LDLIBS-tst-chk8 = -lstdc++
+ LDLIBS-tst-lfschk4 = -lstdc++
+ LDLIBS-tst-lfschk5 = -lstdc++
+ LDLIBS-tst-lfschk6 = -lstdc++
+@@ -150,16 +153,16 @@ CFLAGS-tst-ssp-1.c += -fstack-protector-all
+ 
+ tests = backtrace-tst tst-longjmp_chk tst-chk1 tst-chk2 tst-chk3 \
+ 	tst-lfschk1 tst-lfschk2 tst-lfschk3 test-strcpy_chk test-stpcpy_chk \
+-	tst-chk4 tst-chk5 tst-chk6 tst-lfschk4 tst-lfschk5 tst-lfschk6 \
+-	tst-longjmp_chk2 tst-backtrace2 tst-backtrace3 tst-backtrace4 \
+-	tst-backtrace5 tst-backtrace6
++	tst-chk4 tst-chk5 tst-chk6 tst-chk7 tst-chk8 tst-lfschk4 tst-lfschk5 \
++	tst-lfschk6 tst-longjmp_chk2 tst-backtrace2 tst-backtrace3 \
++	tst-backtrace4 tst-backtrace5 tst-backtrace6
+ 
+ ifeq ($(have-ssp),yes)
+ tests += tst-ssp-1
+ endif
+ 
+ ifeq (,$(CXX))
+-tests-unsupported = tst-chk4 tst-chk5 tst-chk6 \
++tests-unsupported = tst-chk4 tst-chk5 tst-chk6 tst-chk8 \
+ 		    tst-lfschk4 tst-lfschk5 tst-lfschk6
+ endif
+ 
+@@ -193,6 +196,8 @@ $(objpfx)tst-chk3.out: $(gen-locales)
+ $(objpfx)tst-chk4.out: $(gen-locales)
+ $(objpfx)tst-chk5.out: $(gen-locales)
+ $(objpfx)tst-chk6.out: $(gen-locales)
++$(objpfx)tst-chk7.out: $(gen-locales)
++$(objpfx)tst-chk8.out: $(gen-locales)
+ $(objpfx)tst-lfschk1.out: $(gen-locales)
+ $(objpfx)tst-lfschk2.out: $(gen-locales)
+ $(objpfx)tst-lfschk3.out: $(gen-locales)
+diff --git a/debug/tst-chk1.c b/debug/tst-chk1.c
+index ca2b524b2fa6404c..5e76081255316a93 100644
+--- a/debug/tst-chk1.c
++++ b/debug/tst-chk1.c
+@@ -83,8 +83,14 @@ handler (int sig)
+     _exit (127);
+ }
+ 
++#if __USE_FORTIFY_LEVEL == 3
++volatile size_t buf_size = 10;
++#else
+ char buf[10];
+ wchar_t wbuf[10];
++#define buf_size sizeof (buf)
++#endif
++
+ volatile size_t l0;
+ volatile char *p;
+ volatile wchar_t *wp;
+@@ -123,6 +129,10 @@ int num2 = 987654;
+ static int
+ do_test (void)
+ {
++#if __USE_FORTIFY_LEVEL == 3
++  char *buf = (char *) malloc (buf_size);
++  wchar_t *wbuf = (wchar_t *) malloc (buf_size * sizeof (wchar_t));
++#endif
+   set_fortify_handler (handler);
+ 
+   struct A { char buf1[9]; char buf2[1]; } a;
+@@ -947,93 +957,93 @@ do_test (void)
+ 
+   rewind (stdin);
+ 
+-  if (fgets (buf, sizeof (buf), stdin) != buf
++  if (fgets (buf, buf_size, stdin) != buf
+       || memcmp (buf, "abcdefgh\n", 10))
+     FAIL ();
+-  if (fgets (buf, sizeof (buf), stdin) != buf || memcmp (buf, "ABCDEFGHI", 10))
++  if (fgets (buf, buf_size, stdin) != buf || memcmp (buf, "ABCDEFGHI", 10))
+     FAIL ();
+ 
+   rewind (stdin);
+ 
+-  if (fgets (buf, l0 + sizeof (buf), stdin) != buf
++  if (fgets (buf, l0 + buf_size, stdin) != buf
+       || memcmp (buf, "abcdefgh\n", 10))
+     FAIL ();
+ 
+ #if __USE_FORTIFY_LEVEL >= 1
+   CHK_FAIL_START
+-  if (fgets (buf, sizeof (buf) + 1, stdin) != buf)
++  if (fgets (buf, buf_size + 1, stdin) != buf)
+     FAIL ();
+   CHK_FAIL_END
+ 
+   CHK_FAIL_START
+-  if (fgets (buf, l0 + sizeof (buf) + 1, stdin) != buf)
++  if (fgets (buf, l0 + buf_size + 1, stdin) != buf)
+     FAIL ();
+   CHK_FAIL_END
+ #endif
+ 
+   rewind (stdin);
+ 
+-  if (fgets_unlocked (buf, sizeof (buf), stdin) != buf
++  if (fgets_unlocked (buf, buf_size, stdin) != buf
+       || memcmp (buf, "abcdefgh\n", 10))
+     FAIL ();
+-  if (fgets_unlocked (buf, sizeof (buf), stdin) != buf
++  if (fgets_unlocked (buf, buf_size, stdin) != buf
+       || memcmp (buf, "ABCDEFGHI", 10))
+     FAIL ();
+ 
+   rewind (stdin);
+ 
+-  if (fgets_unlocked (buf, l0 + sizeof (buf), stdin) != buf
++  if (fgets_unlocked (buf, l0 + buf_size, stdin) != buf
+       || memcmp (buf, "abcdefgh\n", 10))
+     FAIL ();
+ 
+ #if __USE_FORTIFY_LEVEL >= 1
+   CHK_FAIL_START
+-  if (fgets_unlocked (buf, sizeof (buf) + 1, stdin) != buf)
++  if (fgets_unlocked (buf, buf_size + 1, stdin) != buf)
+     FAIL ();
+   CHK_FAIL_END
+ 
+   CHK_FAIL_START
+-  if (fgets_unlocked (buf, l0 + sizeof (buf) + 1, stdin) != buf)
++  if (fgets_unlocked (buf, l0 + buf_size + 1, stdin) != buf)
+     FAIL ();
+   CHK_FAIL_END
+ #endif
+ 
+   rewind (stdin);
+ 
+-  if (fread (buf, 1, sizeof (buf), stdin) != sizeof (buf)
++  if (fread (buf, 1, buf_size, stdin) != buf_size
+       || memcmp (buf, "abcdefgh\nA", 10))
+     FAIL ();
+-  if (fread (buf, sizeof (buf), 1, stdin) != 1
++  if (fread (buf, buf_size, 1, stdin) != 1
+       || memcmp (buf, "BCDEFGHI\na", 10))
+     FAIL ();
+ 
+   rewind (stdin);
+ 
+-  if (fread (buf, l0 + 1, sizeof (buf), stdin) != sizeof (buf)
++  if (fread (buf, l0 + 1, buf_size, stdin) != buf_size
+       || memcmp (buf, "abcdefgh\nA", 10))
+     FAIL ();
+-  if (fread (buf, sizeof (buf), l0 + 1, stdin) != 1
++  if (fread (buf, buf_size, l0 + 1, stdin) != 1
+       || memcmp (buf, "BCDEFGHI\na", 10))
+     FAIL ();
+ 
+ #if __USE_FORTIFY_LEVEL >= 1
+   CHK_FAIL_START
+-  if (fread (buf, 1, sizeof (buf) + 1, stdin) != sizeof (buf) + 1)
++  if (fread (buf, 1, buf_size + 1, stdin) != buf_size + 1)
+     FAIL ();
+   CHK_FAIL_END
+ 
+   CHK_FAIL_START
+-  if (fread (buf, sizeof (buf) + 1, l0 + 1, stdin) != 1)
++  if (fread (buf, buf_size + 1, l0 + 1, stdin) != 1)
+     FAIL ();
+   CHK_FAIL_END
+ #endif
+ 
+   rewind (stdin);
+ 
+-  if (fread_unlocked (buf, 1, sizeof (buf), stdin) != sizeof (buf)
++  if (fread_unlocked (buf, 1, buf_size, stdin) != buf_size
+       || memcmp (buf, "abcdefgh\nA", 10))
+     FAIL ();
+-  if (fread_unlocked (buf, sizeof (buf), 1, stdin) != 1
++  if (fread_unlocked (buf, buf_size, 1, stdin) != 1
+       || memcmp (buf, "BCDEFGHI\na", 10))
+     FAIL ();
+ 
+@@ -1048,100 +1058,100 @@ do_test (void)
+ 
+   rewind (stdin);
+ 
+-  if (fread_unlocked (buf, l0 + 1, sizeof (buf), stdin) != sizeof (buf)
++  if (fread_unlocked (buf, l0 + 1, buf_size, stdin) != buf_size
+       || memcmp (buf, "abcdefgh\nA", 10))
+     FAIL ();
+-  if (fread_unlocked (buf, sizeof (buf), l0 + 1, stdin) != 1
++  if (fread_unlocked (buf, buf_size, l0 + 1, stdin) != 1
+       || memcmp (buf, "BCDEFGHI\na", 10))
+     FAIL ();
+ 
+ #if __USE_FORTIFY_LEVEL >= 1
+   CHK_FAIL_START
+-  if (fread_unlocked (buf, 1, sizeof (buf) + 1, stdin) != sizeof (buf) + 1)
++  if (fread_unlocked (buf, 1, buf_size + 1, stdin) != buf_size + 1)
+     FAIL ();
+   CHK_FAIL_END
+ 
+   CHK_FAIL_START
+-  if (fread_unlocked (buf, sizeof (buf) + 1, l0 + 1, stdin) != 1)
++  if (fread_unlocked (buf, buf_size + 1, l0 + 1, stdin) != 1)
+     FAIL ();
+   CHK_FAIL_END
+ #endif
+ 
+   lseek (fileno (stdin), 0, SEEK_SET);
+ 
+-  if (read (fileno (stdin), buf, sizeof (buf) - 1) != sizeof (buf) - 1
++  if (read (fileno (stdin), buf, buf_size - 1) != buf_size - 1
+       || memcmp (buf, "abcdefgh\n", 9))
+     FAIL ();
+-  if (read (fileno (stdin), buf, sizeof (buf) - 1) != sizeof (buf) - 1
++  if (read (fileno (stdin), buf, buf_size - 1) != buf_size - 1
+       || memcmp (buf, "ABCDEFGHI", 9))
+     FAIL ();
+ 
+   lseek (fileno (stdin), 0, SEEK_SET);
+ 
+-  if (read (fileno (stdin), buf, l0 + sizeof (buf) - 1) != sizeof (buf) - 1
++  if (read (fileno (stdin), buf, l0 + buf_size - 1) != buf_size - 1
+       || memcmp (buf, "abcdefgh\n", 9))
+     FAIL ();
+ 
+ #if __USE_FORTIFY_LEVEL >= 1
+   CHK_FAIL_START
+-  if (read (fileno (stdin), buf, sizeof (buf) + 1) != sizeof (buf) + 1)
++  if (read (fileno (stdin), buf, buf_size + 1) != buf_size + 1)
+     FAIL ();
+   CHK_FAIL_END
+ 
+   CHK_FAIL_START
+-  if (read (fileno (stdin), buf, l0 + sizeof (buf) + 1) != sizeof (buf) + 1)
++  if (read (fileno (stdin), buf, l0 + buf_size + 1) != buf_size + 1)
+     FAIL ();
+   CHK_FAIL_END
+ #endif
+ 
+-  if (pread (fileno (stdin), buf, sizeof (buf) - 1, sizeof (buf) - 2)
+-      != sizeof (buf) - 1
++  if (pread (fileno (stdin), buf, buf_size - 1, buf_size - 2)
++      != buf_size - 1
+       || memcmp (buf, "\nABCDEFGH", 9))
+     FAIL ();
+-  if (pread (fileno (stdin), buf, sizeof (buf) - 1, 0) != sizeof (buf) - 1
++  if (pread (fileno (stdin), buf, buf_size - 1, 0) != buf_size - 1
+       || memcmp (buf, "abcdefgh\n", 9))
+     FAIL ();
+-  if (pread (fileno (stdin), buf, l0 + sizeof (buf) - 1, sizeof (buf) - 3)
+-      != sizeof (buf) - 1
++  if (pread (fileno (stdin), buf, l0 + buf_size - 1, buf_size - 3)
++      != buf_size - 1
+       || memcmp (buf, "h\nABCDEFG", 9))
+     FAIL ();
+ 
+ #if __USE_FORTIFY_LEVEL >= 1
+   CHK_FAIL_START
+-  if (pread (fileno (stdin), buf, sizeof (buf) + 1, 2 * sizeof (buf))
+-      != sizeof (buf) + 1)
++  if (pread (fileno (stdin), buf, buf_size + 1, 2 * buf_size)
++      != buf_size + 1)
+     FAIL ();
+   CHK_FAIL_END
+ 
+   CHK_FAIL_START
+-  if (pread (fileno (stdin), buf, l0 + sizeof (buf) + 1, 2 * sizeof (buf))
+-      != sizeof (buf) + 1)
++  if (pread (fileno (stdin), buf, l0 + buf_size + 1, 2 * buf_size)
++      != buf_size + 1)
+     FAIL ();
+   CHK_FAIL_END
+ #endif
+ 
+-  if (pread64 (fileno (stdin), buf, sizeof (buf) - 1, sizeof (buf) - 2)
+-      != sizeof (buf) - 1
++  if (pread64 (fileno (stdin), buf, buf_size - 1, buf_size - 2)
++      != buf_size - 1
+       || memcmp (buf, "\nABCDEFGH", 9))
+     FAIL ();
+-  if (pread64 (fileno (stdin), buf, sizeof (buf) - 1, 0) != sizeof (buf) - 1
++  if (pread64 (fileno (stdin), buf, buf_size - 1, 0) != buf_size - 1
+       || memcmp (buf, "abcdefgh\n", 9))
+     FAIL ();
+-  if (pread64 (fileno (stdin), buf, l0 + sizeof (buf) - 1, sizeof (buf) - 3)
+-      != sizeof (buf) - 1
++  if (pread64 (fileno (stdin), buf, l0 + buf_size - 1, buf_size - 3)
++      != buf_size - 1
+       || memcmp (buf, "h\nABCDEFG", 9))
+     FAIL ();
+ 
+ #if __USE_FORTIFY_LEVEL >= 1
+   CHK_FAIL_START
+-  if (pread64 (fileno (stdin), buf, sizeof (buf) + 1, 2 * sizeof (buf))
+-      != sizeof (buf) + 1)
++  if (pread64 (fileno (stdin), buf, buf_size + 1, 2 * buf_size)
++      != buf_size + 1)
+     FAIL ();
+   CHK_FAIL_END
+ 
+   CHK_FAIL_START
+-  if (pread64 (fileno (stdin), buf, l0 + sizeof (buf) + 1, 2 * sizeof (buf))
+-      != sizeof (buf) + 1)
++  if (pread64 (fileno (stdin), buf, l0 + buf_size + 1, 2 * buf_size)
++      != buf_size + 1)
+     FAIL ();
+   CHK_FAIL_END
+ #endif
+@@ -1179,7 +1189,7 @@ do_test (void)
+   CHK_FAIL2_END
+ 
+   CHK_FAIL2_START
+-  snprintf (buf, sizeof (buf), "%3$d\n", 1, 2, 3, 4);
++  snprintf (buf, buf_size, "%3$d\n", 1, 2, 3, 4);
+   CHK_FAIL2_END
+ 
+   int sp[2];
+diff --git a/debug/tst-chk7.c b/debug/tst-chk7.c
+new file mode 100644
+index 0000000000000000..2a7b32381268135c
+--- /dev/null
++++ b/debug/tst-chk7.c
+@@ -0,0 +1,2 @@
++#define _FORTIFY_SOURCE 3
++#include "tst-chk1.c"
+diff --git a/debug/tst-chk8.cc b/debug/tst-chk8.cc
+new file mode 100644
+index 0000000000000000..2a7b32381268135c
+--- /dev/null
++++ b/debug/tst-chk8.cc
+@@ -0,0 +1,2 @@
++#define _FORTIFY_SOURCE 3
++#include "tst-chk1.c"
diff --git a/SOURCES/glibc-rh2033684-9.patch b/SOURCES/glibc-rh2033684-9.patch
new file mode 100644
index 0000000000000000000000000000000000000000..467ece4493b766bc27c7c0a33814d69e9cfb65e9
--- /dev/null
+++ b/SOURCES/glibc-rh2033684-9.patch
@@ -0,0 +1,23 @@
+commit ae23fa3e5fe24daf94fc7f8e5268bb8ceeda7477
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Thu Dec 16 07:19:14 2021 +0530
+
+    __glibc_unsafe_len: Fix comment
+    
+    We know that the length is *unsafe*.
+    
+    Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+
+diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
+index 17b84a2e6c69d961..147339957c4ad490 100644
+--- a/misc/sys/cdefs.h
++++ b/misc/sys/cdefs.h
+@@ -150,7 +150,7 @@
+ 						   __s, __osz))		      \
+    && __glibc_safe_len_cond ((__SIZE_TYPE__) (__l), __s, __osz))
+ 
+-/* Conversely, we know at compile time that the length is safe if the
++/* Conversely, we know at compile time that the length is unsafe if the
+    __L * __S <= __OBJSZ condition can be folded to a constant and if it is
+    false.  */
+ #define __glibc_unsafe_len(__l, __s, __osz) \
diff --git a/SOURCES/glibc-rh2037416-1.patch b/SOURCES/glibc-rh2037416-1.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3fddefec5c038b3a3ec6c4c31d2f3e3c2141cf40
--- /dev/null
+++ b/SOURCES/glibc-rh2037416-1.patch
@@ -0,0 +1,136 @@
+From 07b427296b8d59f439144029d9a948f6c1ce0a31 Mon Sep 17 00:00:00 2001
+From: Wilco Dijkstra <wdijkstr@arm.com>
+Date: Tue, 10 Aug 2021 13:30:27 +0100
+Subject: [PATCH] [1/5] AArch64: Improve A64FX memset for small sizes
+
+Improve performance of small memsets by reducing instruction counts and
+improving code alignment. Bench-memset shows 35-45% performance gain for
+small sizes.
+
+Reviewed-by: Naohiro Tamura <naohirot@fujitsu.com>
+---
+ sysdeps/aarch64/multiarch/memset_a64fx.S | 96 +++++++++---------------
+ 1 file changed, 36 insertions(+), 60 deletions(-)
+
+diff --git a/sysdeps/aarch64/multiarch/memset_a64fx.S b/sysdeps/aarch64/multiarch/memset_a64fx.S
+index ce54e5418b..cf3d402ef6 100644
+--- a/sysdeps/aarch64/multiarch/memset_a64fx.S
++++ b/sysdeps/aarch64/multiarch/memset_a64fx.S
+@@ -51,78 +51,54 @@
+ 	.endm
+ 
+ 	.macro st1b_unroll first=0, last=7
+-	st1b	z0.b, p0, [dst, #\first, mul vl]
++	st1b	z0.b, p0, [dst, \first, mul vl]
+ 	.if \last-\first
+ 	st1b_unroll "(\first+1)", \last
+ 	.endif
+ 	.endm
+ 
+-	.macro shortcut_for_small_size exit
+-	// if rest <= vector_length * 2
+-	whilelo	p0.b, xzr, count
+-	whilelo	p1.b, vector_length, count
+-	b.last	1f
+-	st1b	z0.b, p0, [dstin, #0, mul vl]
+-	st1b	z0.b, p1, [dstin, #1, mul vl]
+-	ret
+-1:	// if rest > vector_length * 8
+-	cmp	count, vector_length, lsl 3	// vector_length * 8
+-	b.hi	\exit
+-	// if rest <= vector_length * 4
+-	lsl	tmp1, vector_length, 1	// vector_length * 2
+-	whilelo	p2.b, tmp1, count
+-	incb	tmp1
+-	whilelo	p3.b, tmp1, count
+-	b.last	1f
+-	st1b	z0.b, p0, [dstin, #0, mul vl]
+-	st1b	z0.b, p1, [dstin, #1, mul vl]
+-	st1b	z0.b, p2, [dstin, #2, mul vl]
+-	st1b	z0.b, p3, [dstin, #3, mul vl]
+-	ret
+-1:	// if rest <= vector_length * 8
+-	lsl	tmp1, vector_length, 2	// vector_length * 4
+-	whilelo	p4.b, tmp1, count
+-	incb	tmp1
+-	whilelo	p5.b, tmp1, count
+-	b.last	1f
+-	st1b	z0.b, p0, [dstin, #0, mul vl]
+-	st1b	z0.b, p1, [dstin, #1, mul vl]
+-	st1b	z0.b, p2, [dstin, #2, mul vl]
+-	st1b	z0.b, p3, [dstin, #3, mul vl]
+-	st1b	z0.b, p4, [dstin, #4, mul vl]
+-	st1b	z0.b, p5, [dstin, #5, mul vl]
+-	ret
+-1:	lsl	tmp1, vector_length, 2	// vector_length * 4
+-	incb	tmp1			// vector_length * 5
+-	incb	tmp1			// vector_length * 6
+-	whilelo	p6.b, tmp1, count
+-	incb	tmp1
+-	whilelo	p7.b, tmp1, count
+-	st1b	z0.b, p0, [dstin, #0, mul vl]
+-	st1b	z0.b, p1, [dstin, #1, mul vl]
+-	st1b	z0.b, p2, [dstin, #2, mul vl]
+-	st1b	z0.b, p3, [dstin, #3, mul vl]
+-	st1b	z0.b, p4, [dstin, #4, mul vl]
+-	st1b	z0.b, p5, [dstin, #5, mul vl]
+-	st1b	z0.b, p6, [dstin, #6, mul vl]
+-	st1b	z0.b, p7, [dstin, #7, mul vl]
+-	ret
+-	.endm
+ 
+-ENTRY (MEMSET)
++#undef BTI_C
++#define BTI_C
+ 
++ENTRY (MEMSET)
+ 	PTR_ARG (0)
+ 	SIZE_ARG (2)
+ 
+-	cbnz	count, 1f
+-	ret
+-1:	dup	z0.b, valw
+ 	cntb	vector_length
+-	// shortcut for less than vector_length * 8
+-	// gives a free ptrue to p0.b for n >= vector_length
+-	shortcut_for_small_size L(vl_agnostic)
+-	// end of shortcut
++	dup	z0.b, valw
++	whilelo	p0.b, vector_length, count
++	b.last	1f
++	whilelo	p1.b, xzr, count
++	st1b	z0.b, p1, [dstin, 0, mul vl]
++	st1b	z0.b, p0, [dstin, 1, mul vl]
++	ret
++
++	// count >= vector_length * 2
++1:	cmp	count, vector_length, lsl 2
++	add	dstend, dstin, count
++	b.hi	1f
++	st1b	z0.b, p0, [dstin, 0, mul vl]
++	st1b	z0.b, p0, [dstin, 1, mul vl]
++	st1b	z0.b, p0, [dstend, -2, mul vl]
++	st1b	z0.b, p0, [dstend, -1, mul vl]
++	ret
++
++	// count > vector_length * 4
++1:	lsl	tmp1, vector_length, 3
++	cmp	count, tmp1
++	b.hi	L(vl_agnostic)
++	st1b	z0.b, p0, [dstin, 0, mul vl]
++	st1b	z0.b, p0, [dstin, 1, mul vl]
++	st1b	z0.b, p0, [dstin, 2, mul vl]
++	st1b	z0.b, p0, [dstin, 3, mul vl]
++	st1b	z0.b, p0, [dstend, -4, mul vl]
++	st1b	z0.b, p0, [dstend, -3, mul vl]
++	st1b	z0.b, p0, [dstend, -2, mul vl]
++	st1b	z0.b, p0, [dstend, -1, mul vl]
++	ret
+ 
++	.p2align 4
+ L(vl_agnostic): // VL Agnostic
+ 	mov	rest, count
+ 	mov	dst, dstin
+-- 
+2.31.1
+
diff --git a/SOURCES/glibc-rh2037416-2.patch b/SOURCES/glibc-rh2037416-2.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e991e91964ce152e4dbd43cd07ab4ab0d136d616
--- /dev/null
+++ b/SOURCES/glibc-rh2037416-2.patch
@@ -0,0 +1,131 @@
+From 9bc2ed8f46d80859a5596789cc9e8cc2de84b0e7 Mon Sep 17 00:00:00 2001
+From: Wilco Dijkstra <wdijkstr@arm.com>
+Date: Tue, 10 Aug 2021 13:39:37 +0100
+Subject: [PATCH] [2/5] AArch64: Improve A64FX memset for large sizes
+
+Improve performance of large memsets. Simplify alignment code. For zero memset
+use DC ZVA, which almost doubles performance. For non-zero memsets use the
+unroll8 loop which is about 10% faster.
+
+Reviewed-by: Naohiro Tamura <naohirot@fujitsu.com>
+---
+ sysdeps/aarch64/multiarch/memset_a64fx.S | 85 +++++++-----------------
+ 1 file changed, 25 insertions(+), 60 deletions(-)
+
+diff --git a/sysdeps/aarch64/multiarch/memset_a64fx.S b/sysdeps/aarch64/multiarch/memset_a64fx.S
+index cf3d402ef6..75cf43ae79 100644
+--- a/sysdeps/aarch64/multiarch/memset_a64fx.S
++++ b/sysdeps/aarch64/multiarch/memset_a64fx.S
+@@ -27,14 +27,11 @@
+  */
+ 
+ #define L1_SIZE		(64*1024)	// L1 64KB
+-#define L2_SIZE         (8*1024*1024)	// L2 8MB - 1MB
++#define L2_SIZE         (8*1024*1024)	// L2 8MB
+ #define CACHE_LINE_SIZE	256
+ #define PF_DIST_L1	(CACHE_LINE_SIZE * 16)	// Prefetch distance L1
+-#define ZF_DIST		(CACHE_LINE_SIZE * 21)	// Zerofill distance
+-#define rest		x8
++#define rest		x2
+ #define vector_length	x9
+-#define vl_remainder	x10	// vector_length remainder
+-#define cl_remainder	x11	// CACHE_LINE_SIZE remainder
+ 
+ #if HAVE_AARCH64_SVE_ASM
+ # if IS_IN (libc)
+@@ -42,14 +39,6 @@
+ 
+ 	.arch armv8.2-a+sve
+ 
+-	.macro dc_zva times
+-	dc	zva, tmp1
+-	add	tmp1, tmp1, CACHE_LINE_SIZE
+-	.if \times-1
+-	dc_zva "(\times-1)"
+-	.endif
+-	.endm
+-
+ 	.macro st1b_unroll first=0, last=7
+ 	st1b	z0.b, p0, [dst, \first, mul vl]
+ 	.if \last-\first
+@@ -188,54 +177,30 @@ L(L1_prefetch): // if rest >= L1_SIZE
+ 	cbnz	rest, L(unroll32)
+ 	ret
+ 
+-L(L2):
+-	// align dst address at vector_length byte boundary
+-	sub	tmp1, vector_length, 1
+-	ands	tmp2, dst, tmp1
+-	// if vl_remainder == 0
+-	b.eq	1f
+-	sub	vl_remainder, vector_length, tmp2
+-	// process remainder until the first vector_length boundary
+-	whilelt	p2.b, xzr, vl_remainder
+-	st1b	z0.b, p2, [dst]
+-	add	dst, dst, vl_remainder
+-	sub	rest, rest, vl_remainder
+-	// align dstin address at CACHE_LINE_SIZE byte boundary
+-1:	mov	tmp1, CACHE_LINE_SIZE
+-	ands	tmp2, dst, CACHE_LINE_SIZE - 1
+-	// if cl_remainder == 0
+-	b.eq	L(L2_dc_zva)
+-	sub	cl_remainder, tmp1, tmp2
+-	// process remainder until the first CACHE_LINE_SIZE boundary
+-	mov	tmp1, xzr       // index
+-2:	whilelt	p2.b, tmp1, cl_remainder
+-	st1b	z0.b, p2, [dst, tmp1]
+-	incb	tmp1
+-	cmp	tmp1, cl_remainder
+-	b.lo	2b
+-	add	dst, dst, cl_remainder
+-	sub	rest, rest, cl_remainder
+-
+-L(L2_dc_zva):
+-	// zero fill
+-	mov	tmp1, dst
+-	dc_zva	(ZF_DIST / CACHE_LINE_SIZE) - 1
+-	mov	zva_len, ZF_DIST
+-	add	tmp1, zva_len, CACHE_LINE_SIZE * 2
+-	// unroll
++	// count >= L2_SIZE
+ 	.p2align 3
+-1:	st1b_unroll 0, 3
+-	add	tmp2, dst, zva_len
+-	dc	 zva, tmp2
+-	st1b_unroll 4, 7
+-	add	tmp2, tmp2, CACHE_LINE_SIZE
+-	dc	zva, tmp2
+-	add	dst, dst, CACHE_LINE_SIZE * 2
+-	sub	rest, rest, CACHE_LINE_SIZE * 2
+-	cmp	rest, tmp1	// ZF_DIST + CACHE_LINE_SIZE * 2
+-	b.ge	1b
+-	cbnz	rest, L(unroll8)
+-	ret
++L(L2):
++	tst	valw, 255
++	b.ne	L(unroll8)
++        // align dst to CACHE_LINE_SIZE byte boundary
++	and	tmp2, dst, CACHE_LINE_SIZE - 1
++	st1b	z0.b, p0, [dst, 0, mul vl]
++	st1b	z0.b, p0, [dst, 1, mul vl]
++	st1b	z0.b, p0, [dst, 2, mul vl]
++	st1b	z0.b, p0, [dst, 3, mul vl]
++	sub	dst, dst, tmp2
++	add	count, count, tmp2
++
++	// clear cachelines using DC ZVA
++	sub	count, count, CACHE_LINE_SIZE * 2
++	.p2align 4
++1:	add	dst, dst, CACHE_LINE_SIZE
++	dc	zva, dst
++	subs	count, count, CACHE_LINE_SIZE
++	b.hi	1b
++	add	count, count, CACHE_LINE_SIZE
++	add	dst, dst, CACHE_LINE_SIZE
++	b	L(last)
+ 
+ END (MEMSET)
+ libc_hidden_builtin_def (MEMSET)
+-- 
+2.31.1
+
diff --git a/SOURCES/glibc-rh2037416-3.patch b/SOURCES/glibc-rh2037416-3.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3ac7aa2f437a0d191c9ad562d7467de2cd9097e9
--- /dev/null
+++ b/SOURCES/glibc-rh2037416-3.patch
@@ -0,0 +1,80 @@
+From 186092c6ba8825598ffdbf15dbf0823c771f560d Mon Sep 17 00:00:00 2001
+From: Wilco Dijkstra <wdijkstr@arm.com>
+Date: Tue, 10 Aug 2021 13:42:07 +0100
+Subject: [PATCH] [3/5] AArch64: Improve A64FX memset for remaining bytes
+
+Simplify handling of remaining bytes. Avoid lots of taken branches and complex
+whilelo computations, instead unconditionally write vectors from the end.
+
+Reviewed-by: Naohiro Tamura <naohirot@fujitsu.com>
+---
+ sysdeps/aarch64/multiarch/memset_a64fx.S | 46 +++++++-----------------
+ 1 file changed, 13 insertions(+), 33 deletions(-)
+
+diff --git a/sysdeps/aarch64/multiarch/memset_a64fx.S b/sysdeps/aarch64/multiarch/memset_a64fx.S
+index 75cf43ae79..337c86be6f 100644
+--- a/sysdeps/aarch64/multiarch/memset_a64fx.S
++++ b/sysdeps/aarch64/multiarch/memset_a64fx.S
+@@ -130,38 +130,19 @@ L(unroll8):
+ 	b	1b
+ 
+ L(last):
+-	whilelo	p0.b, xzr, rest
+-	whilelo	p1.b, vector_length, rest
+-	b.last	1f
+-	st1b	z0.b, p0, [dst, #0, mul vl]
+-	st1b	z0.b, p1, [dst, #1, mul vl]
+-	ret
+-1:	lsl	tmp1, vector_length, 1	// vector_length * 2
+-	whilelo	p2.b, tmp1, rest
+-	incb	tmp1
+-	whilelo	p3.b, tmp1, rest
+-	b.last	1f
+-	st1b	z0.b, p0, [dst, #0, mul vl]
+-	st1b	z0.b, p1, [dst, #1, mul vl]
+-	st1b	z0.b, p2, [dst, #2, mul vl]
+-	st1b	z0.b, p3, [dst, #3, mul vl]
+-	ret
+-1:	lsl	tmp1, vector_length, 2	// vector_length * 4
+-	whilelo	p4.b, tmp1, rest
+-	incb	tmp1
+-	whilelo	p5.b, tmp1, rest
+-	incb	tmp1
+-	whilelo	p6.b, tmp1, rest
+-	incb	tmp1
+-	whilelo	p7.b, tmp1, rest
+-	st1b	z0.b, p0, [dst, #0, mul vl]
+-	st1b	z0.b, p1, [dst, #1, mul vl]
+-	st1b	z0.b, p2, [dst, #2, mul vl]
+-	st1b	z0.b, p3, [dst, #3, mul vl]
+-	st1b	z0.b, p4, [dst, #4, mul vl]
+-	st1b	z0.b, p5, [dst, #5, mul vl]
+-	st1b	z0.b, p6, [dst, #6, mul vl]
+-	st1b	z0.b, p7, [dst, #7, mul vl]
++	cmp	count, vector_length, lsl 1
++	b.ls	2f
++	add	tmp2, vector_length, vector_length, lsl 2
++	cmp	count, tmp2
++	b.ls	5f
++	st1b	z0.b, p0, [dstend, -8, mul vl]
++	st1b	z0.b, p0, [dstend, -7, mul vl]
++	st1b	z0.b, p0, [dstend, -6, mul vl]
++5:	st1b	z0.b, p0, [dstend, -5, mul vl]
++	st1b	z0.b, p0, [dstend, -4, mul vl]
++	st1b	z0.b, p0, [dstend, -3, mul vl]
++2:	st1b	z0.b, p0, [dstend, -2, mul vl]
++	st1b	z0.b, p0, [dstend, -1, mul vl]
+ 	ret
+ 
+ L(L1_prefetch): // if rest >= L1_SIZE
+@@ -199,7 +180,6 @@ L(L2):
+ 	subs	count, count, CACHE_LINE_SIZE
+ 	b.hi	1b
+ 	add	count, count, CACHE_LINE_SIZE
+-	add	dst, dst, CACHE_LINE_SIZE
+ 	b	L(last)
+ 
+ END (MEMSET)
+-- 
+2.31.1
+
diff --git a/SOURCES/glibc-rh2037416-4.patch b/SOURCES/glibc-rh2037416-4.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e057eebaa2d1b55ef624488ecc94e143b1d52a0f
--- /dev/null
+++ b/SOURCES/glibc-rh2037416-4.patch
@@ -0,0 +1,51 @@
+From e69d9981f858a38e19304e6ff5ebdf89f2cb0ba0 Mon Sep 17 00:00:00 2001
+From: Wilco Dijkstra <wdijkstr@arm.com>
+Date: Tue, 10 Aug 2021 13:44:27 +0100
+Subject: [PATCH] [4/5] AArch64: Improve A64FX memset by removing unroll32
+
+Remove unroll32 code since it doesn't improve performance.
+
+Reviewed-by: Naohiro Tamura <naohirot@fujitsu.com>
+---
+ sysdeps/aarch64/multiarch/memset_a64fx.S | 18 +-----------------
+ 1 file changed, 1 insertion(+), 17 deletions(-)
+
+diff --git a/sysdeps/aarch64/multiarch/memset_a64fx.S b/sysdeps/aarch64/multiarch/memset_a64fx.S
+index 337c86be6f..ef0315658a 100644
+--- a/sysdeps/aarch64/multiarch/memset_a64fx.S
++++ b/sysdeps/aarch64/multiarch/memset_a64fx.S
+@@ -102,22 +102,6 @@ L(vl_agnostic): // VL Agnostic
+ 	ccmp	vector_length, tmp1, 0, cs
+ 	b.eq	L(L1_prefetch)
+ 
+-L(unroll32):
+-	lsl	tmp1, vector_length, 3	// vector_length * 8
+-	lsl	tmp2, vector_length, 5	// vector_length * 32
+-	.p2align 3
+-1:	cmp	rest, tmp2
+-	b.cc	L(unroll8)
+-	st1b_unroll
+-	add	dst, dst, tmp1
+-	st1b_unroll
+-	add	dst, dst, tmp1
+-	st1b_unroll
+-	add	dst, dst, tmp1
+-	st1b_unroll
+-	add	dst, dst, tmp1
+-	sub	rest, rest, tmp2
+-	b	1b
+ 
+ L(unroll8):
+ 	lsl	tmp1, vector_length, 3
+@@ -155,7 +139,7 @@ L(L1_prefetch): // if rest >= L1_SIZE
+ 	sub	rest, rest, CACHE_LINE_SIZE * 2
+ 	cmp	rest, L1_SIZE
+ 	b.ge	1b
+-	cbnz	rest, L(unroll32)
++	cbnz	rest, L(unroll8)
+ 	ret
+ 
+ 	// count >= L2_SIZE
+-- 
+2.31.1
+
diff --git a/SOURCES/glibc-rh2037416-5.patch b/SOURCES/glibc-rh2037416-5.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c92c2cf21376b348bb4f03fcd6325ef5745db940
--- /dev/null
+++ b/SOURCES/glibc-rh2037416-5.patch
@@ -0,0 +1,96 @@
+From a5db6a5cae6a92d1675c013e5c8d972768721576 Mon Sep 17 00:00:00 2001
+From: Wilco Dijkstra <wdijkstr@arm.com>
+Date: Tue, 10 Aug 2021 13:46:20 +0100
+Subject: [PATCH] [5/5] AArch64: Improve A64FX memset medium loops
+
+Simplify the code for memsets smaller than L1. Improve the unroll8 and
+L1_prefetch loops.
+
+Reviewed-by: Naohiro Tamura <naohirot@fujitsu.com>
+---
+ sysdeps/aarch64/multiarch/memset_a64fx.S | 45 ++++++++++--------------
+ 1 file changed, 19 insertions(+), 26 deletions(-)
+
+diff --git a/sysdeps/aarch64/multiarch/memset_a64fx.S b/sysdeps/aarch64/multiarch/memset_a64fx.S
+index ef0315658a..7bf759b6a7 100644
+--- a/sysdeps/aarch64/multiarch/memset_a64fx.S
++++ b/sysdeps/aarch64/multiarch/memset_a64fx.S
+@@ -30,7 +30,6 @@
+ #define L2_SIZE         (8*1024*1024)	// L2 8MB
+ #define CACHE_LINE_SIZE	256
+ #define PF_DIST_L1	(CACHE_LINE_SIZE * 16)	// Prefetch distance L1
+-#define rest		x2
+ #define vector_length	x9
+ 
+ #if HAVE_AARCH64_SVE_ASM
+@@ -89,29 +88,19 @@ ENTRY (MEMSET)
+ 
+ 	.p2align 4
+ L(vl_agnostic): // VL Agnostic
+-	mov	rest, count
+ 	mov	dst, dstin
+-	add	dstend, dstin, count
+-	// if rest >= L2_SIZE && vector_length == 64 then L(L2)
+-	mov	tmp1, 64
+-	cmp	rest, L2_SIZE
+-	ccmp	vector_length, tmp1, 0, cs
+-	b.eq	L(L2)
+-	// if rest >= L1_SIZE && vector_length == 64 then L(L1_prefetch)
+-	cmp	rest, L1_SIZE
+-	ccmp	vector_length, tmp1, 0, cs
+-	b.eq	L(L1_prefetch)
+-
++	cmp	count, L1_SIZE
++	b.hi	L(L1_prefetch)
+ 
++	// count >= 8 * vector_length
+ L(unroll8):
+-	lsl	tmp1, vector_length, 3
+-	.p2align 3
+-1:	cmp	rest, tmp1
+-	b.cc	L(last)
+-	st1b_unroll
++	sub	count, count, tmp1
++	.p2align 4
++1:	st1b_unroll 0, 7
+ 	add	dst, dst, tmp1
+-	sub	rest, rest, tmp1
+-	b	1b
++	subs	count, count, tmp1
++	b.hi	1b
++	add	count, count, tmp1
+ 
+ L(last):
+ 	cmp	count, vector_length, lsl 1
+@@ -129,18 +118,22 @@ L(last):
+ 	st1b	z0.b, p0, [dstend, -1, mul vl]
+ 	ret
+ 
+-L(L1_prefetch): // if rest >= L1_SIZE
++	// count >= L1_SIZE
+ 	.p2align 3
++L(L1_prefetch):
++	cmp	count, L2_SIZE
++	b.hs	L(L2)
++	cmp	vector_length, 64
++	b.ne	L(unroll8)
+ 1:	st1b_unroll 0, 3
+ 	prfm	pstl1keep, [dst, PF_DIST_L1]
+ 	st1b_unroll 4, 7
+ 	prfm	pstl1keep, [dst, PF_DIST_L1 + CACHE_LINE_SIZE]
+ 	add	dst, dst, CACHE_LINE_SIZE * 2
+-	sub	rest, rest, CACHE_LINE_SIZE * 2
+-	cmp	rest, L1_SIZE
+-	b.ge	1b
+-	cbnz	rest, L(unroll8)
+-	ret
++	sub	count, count, CACHE_LINE_SIZE * 2
++	cmp	count, PF_DIST_L1
++	b.hs	1b
++	b	L(unroll8)
+ 
+ 	// count >= L2_SIZE
+ 	.p2align 3
+-- 
+2.31.1
+
diff --git a/SOURCES/glibc-rh2037416-6.patch b/SOURCES/glibc-rh2037416-6.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b2522ad2367ddedb5d803da018ecf53ecfae5709
--- /dev/null
+++ b/SOURCES/glibc-rh2037416-6.patch
@@ -0,0 +1,39 @@
+From 1d9f99ce1b3788d1897cb53a76d57e973111b8fe Mon Sep 17 00:00:00 2001
+From: Naohiro Tamura <naohirot@fujitsu.com>
+Date: Fri, 27 Aug 2021 05:03:04 +0000
+Subject: [PATCH] AArch64: Update A64FX memset not to degrade at 16KB
+
+This patch updates unroll8 code so as not to degrade at the peak
+performance 16KB for both FX1000 and FX700.
+
+Inserted 2 instructions at the beginning of the unroll8 loop,
+cmp and branch, are a workaround that is found heuristically.
+
+Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
+---
+ sysdeps/aarch64/multiarch/memset_a64fx.S | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/sysdeps/aarch64/multiarch/memset_a64fx.S b/sysdeps/aarch64/multiarch/memset_a64fx.S
+index 7bf759b6a7..f7dfdaace7 100644
+--- a/sysdeps/aarch64/multiarch/memset_a64fx.S
++++ b/sysdeps/aarch64/multiarch/memset_a64fx.S
+@@ -96,7 +96,14 @@ L(vl_agnostic): // VL Agnostic
+ L(unroll8):
+ 	sub	count, count, tmp1
+ 	.p2align 4
+-1:	st1b_unroll 0, 7
++	// The 2 instructions at the beginning of the following loop,
++	// cmp and branch, are a workaround so as not to degrade at
++	// the peak performance 16KB.
++	// It is found heuristically and the branch condition, b.ne,
++	// is chosen intentionally never to jump.
++1:	cmp	xzr, xzr
++	b.ne	1b
++	st1b_unroll 0, 7
+ 	add	dst, dst, tmp1
+ 	subs	count, count, tmp1
+ 	b.hi	1b
+-- 
+2.31.1
+
diff --git a/SOURCES/glibc-rh2037416-7.patch b/SOURCES/glibc-rh2037416-7.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e57fef7079eeb6fdf327d1d9acb2b02201329948
--- /dev/null
+++ b/SOURCES/glibc-rh2037416-7.patch
@@ -0,0 +1,32 @@
+From 381b29616abb82babc8163bdf516c6da87544b35 Mon Sep 17 00:00:00 2001
+From: Naohiro Tamura <naohirot@fujitsu.com>
+Date: Fri, 24 Sep 2021 07:49:59 +0000
+Subject: [PATCH] aarch64: Disable A64FX memcpy/memmove BTI unconditionally
+
+This patch disables A64FX memcpy/memmove BTI instruction insertion
+unconditionally such as A64FX memset patch [1] for performance.
+
+[1] commit 07b427296b8d59f439144029d9a948f6c1ce0a31
+
+Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
+---
+ sysdeps/aarch64/multiarch/memcpy_a64fx.S | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/sysdeps/aarch64/multiarch/memcpy_a64fx.S b/sysdeps/aarch64/multiarch/memcpy_a64fx.S
+index 65528405bb..ae7464e09f 100644
+--- a/sysdeps/aarch64/multiarch/memcpy_a64fx.S
++++ b/sysdeps/aarch64/multiarch/memcpy_a64fx.S
+@@ -19,6 +19,9 @@
+ 
+ #include <sysdep.h>
+ 
++#undef BTI_C
++#define BTI_C
++
+ /* Assumptions:
+  *
+  * ARMv8.2-a, AArch64, unaligned accesses, sve
+-- 
+2.31.1
+
diff --git a/SOURCES/glibc-rh2037416-8.patch b/SOURCES/glibc-rh2037416-8.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6a6833254eb915ac2fa595cfc29035f89664b4d7
--- /dev/null
+++ b/SOURCES/glibc-rh2037416-8.patch
@@ -0,0 +1,630 @@
+From b31bd11454fade731e5158b1aea40b133ae19926 Mon Sep 17 00:00:00 2001
+From: Wilco Dijkstra <wdijkstr@arm.com>
+Date: Thu, 2 Dec 2021 18:33:26 +0000
+Subject: [PATCH] AArch64: Improve A64FX memcpy
+
+v2 is a complete rewrite of the A64FX memcpy. Performance is improved
+by streamlining the code, aligning all large copies and using a single
+unrolled loop for all sizes. The code size for memcpy and memmove goes
+down from 1796 bytes to 868 bytes. Performance is better in all cases:
+bench-memcpy-random is 2.3% faster overall, bench-memcpy-large is ~33%
+faster for large sizes, bench-memcpy-walk is 25% faster for small sizes
+and 20% for the largest sizes. The geomean of all tests in bench-memcpy
+is 5.1% faster, and total time is reduced by 4%.
+
+Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
+---
+ sysdeps/aarch64/multiarch/memcpy_a64fx.S | 546 ++++++++++-------------
+ 1 file changed, 225 insertions(+), 321 deletions(-)
+
+diff --git a/sysdeps/aarch64/multiarch/memcpy_a64fx.S b/sysdeps/aarch64/multiarch/memcpy_a64fx.S
+index ae7464e09f..0b306925e6 100644
+--- a/sysdeps/aarch64/multiarch/memcpy_a64fx.S
++++ b/sysdeps/aarch64/multiarch/memcpy_a64fx.S
+@@ -28,20 +28,15 @@
+  *
+  */
+ 
+-#define L2_SIZE		(8*1024*1024)/2	// L2 8MB/2
+-#define CACHE_LINE_SIZE	256
+-#define ZF_DIST		(CACHE_LINE_SIZE * 21)	// Zerofill distance
+-#define dest		x0
+-#define src		x1
+-#define n		x2	// size
+-#define tmp1		x3
+-#define tmp2		x4
+-#define tmp3		x5
+-#define rest		x6
+-#define dest_ptr	x7
+-#define src_ptr		x8
+-#define vector_length	x9
+-#define cl_remainder	x10	// CACHE_LINE_SIZE remainder
++#define dstin	x0
++#define src	x1
++#define n	x2
++#define dst	x3
++#define dstend	x4
++#define srcend	x5
++#define tmp	x6
++#define vlen	x7
++#define vlen8	x8
+ 
+ #if HAVE_AARCH64_SVE_ASM
+ # if IS_IN (libc)
+@@ -50,45 +45,37 @@
+ 
+ 	.arch armv8.2-a+sve
+ 
+-	.macro dc_zva times
+-	dc	zva, tmp1
+-	add	tmp1, tmp1, CACHE_LINE_SIZE
+-	.if \times-1
+-	dc_zva "(\times-1)"
+-	.endif
+-	.endm
+-
+ 	.macro ld1b_unroll8
+-	ld1b	z0.b, p0/z, [src_ptr, #0, mul vl]
+-	ld1b	z1.b, p0/z, [src_ptr, #1, mul vl]
+-	ld1b	z2.b, p0/z, [src_ptr, #2, mul vl]
+-	ld1b	z3.b, p0/z, [src_ptr, #3, mul vl]
+-	ld1b	z4.b, p0/z, [src_ptr, #4, mul vl]
+-	ld1b	z5.b, p0/z, [src_ptr, #5, mul vl]
+-	ld1b	z6.b, p0/z, [src_ptr, #6, mul vl]
+-	ld1b	z7.b, p0/z, [src_ptr, #7, mul vl]
++	ld1b	z0.b, p0/z, [src, 0, mul vl]
++	ld1b	z1.b, p0/z, [src, 1, mul vl]
++	ld1b	z2.b, p0/z, [src, 2, mul vl]
++	ld1b	z3.b, p0/z, [src, 3, mul vl]
++	ld1b	z4.b, p0/z, [src, 4, mul vl]
++	ld1b	z5.b, p0/z, [src, 5, mul vl]
++	ld1b	z6.b, p0/z, [src, 6, mul vl]
++	ld1b	z7.b, p0/z, [src, 7, mul vl]
+ 	.endm
+ 
+ 	.macro stld1b_unroll4a
+-	st1b	z0.b, p0,   [dest_ptr, #0, mul vl]
+-	st1b	z1.b, p0,   [dest_ptr, #1, mul vl]
+-	ld1b	z0.b, p0/z, [src_ptr,  #0, mul vl]
+-	ld1b	z1.b, p0/z, [src_ptr,  #1, mul vl]
+-	st1b	z2.b, p0,   [dest_ptr, #2, mul vl]
+-	st1b	z3.b, p0,   [dest_ptr, #3, mul vl]
+-	ld1b	z2.b, p0/z, [src_ptr,  #2, mul vl]
+-	ld1b	z3.b, p0/z, [src_ptr,  #3, mul vl]
++	st1b	z0.b, p0,   [dst, 0, mul vl]
++	st1b	z1.b, p0,   [dst, 1, mul vl]
++	ld1b	z0.b, p0/z, [src, 0, mul vl]
++	ld1b	z1.b, p0/z, [src, 1, mul vl]
++	st1b	z2.b, p0,   [dst, 2, mul vl]
++	st1b	z3.b, p0,   [dst, 3, mul vl]
++	ld1b	z2.b, p0/z, [src, 2, mul vl]
++	ld1b	z3.b, p0/z, [src, 3, mul vl]
+ 	.endm
+ 
+ 	.macro stld1b_unroll4b
+-	st1b	z4.b, p0,   [dest_ptr, #4, mul vl]
+-	st1b	z5.b, p0,   [dest_ptr, #5, mul vl]
+-	ld1b	z4.b, p0/z, [src_ptr,  #4, mul vl]
+-	ld1b	z5.b, p0/z, [src_ptr,  #5, mul vl]
+-	st1b	z6.b, p0,   [dest_ptr, #6, mul vl]
+-	st1b	z7.b, p0,   [dest_ptr, #7, mul vl]
+-	ld1b	z6.b, p0/z, [src_ptr,  #6, mul vl]
+-	ld1b	z7.b, p0/z, [src_ptr,  #7, mul vl]
++	st1b	z4.b, p0,   [dst, 4, mul vl]
++	st1b	z5.b, p0,   [dst, 5, mul vl]
++	ld1b	z4.b, p0/z, [src, 4, mul vl]
++	ld1b	z5.b, p0/z, [src, 5, mul vl]
++	st1b	z6.b, p0,   [dst, 6, mul vl]
++	st1b	z7.b, p0,   [dst, 7, mul vl]
++	ld1b	z6.b, p0/z, [src, 6, mul vl]
++	ld1b	z7.b, p0/z, [src, 7, mul vl]
+ 	.endm
+ 
+ 	.macro stld1b_unroll8
+@@ -97,87 +84,18 @@
+ 	.endm
+ 
+ 	.macro st1b_unroll8
+-	st1b	z0.b, p0, [dest_ptr, #0, mul vl]
+-	st1b	z1.b, p0, [dest_ptr, #1, mul vl]
+-	st1b	z2.b, p0, [dest_ptr, #2, mul vl]
+-	st1b	z3.b, p0, [dest_ptr, #3, mul vl]
+-	st1b	z4.b, p0, [dest_ptr, #4, mul vl]
+-	st1b	z5.b, p0, [dest_ptr, #5, mul vl]
+-	st1b	z6.b, p0, [dest_ptr, #6, mul vl]
+-	st1b	z7.b, p0, [dest_ptr, #7, mul vl]
++	st1b	z0.b, p0, [dst, 0, mul vl]
++	st1b	z1.b, p0, [dst, 1, mul vl]
++	st1b	z2.b, p0, [dst, 2, mul vl]
++	st1b	z3.b, p0, [dst, 3, mul vl]
++	st1b	z4.b, p0, [dst, 4, mul vl]
++	st1b	z5.b, p0, [dst, 5, mul vl]
++	st1b	z6.b, p0, [dst, 6, mul vl]
++	st1b	z7.b, p0, [dst, 7, mul vl]
+ 	.endm
+ 
+-	.macro shortcut_for_small_size exit
+-	// if rest <= vector_length * 2
+-	whilelo	p0.b, xzr, n
+-	whilelo	p1.b, vector_length, n
+-	b.last	1f
+-	ld1b	z0.b, p0/z, [src, #0, mul vl]
+-	ld1b	z1.b, p1/z, [src, #1, mul vl]
+-	st1b	z0.b, p0, [dest, #0, mul vl]
+-	st1b	z1.b, p1, [dest, #1, mul vl]
+-	ret
+-1:	// if rest > vector_length * 8
+-	cmp	n, vector_length, lsl 3 // vector_length * 8
+-	b.hi	\exit
+-	// if rest <= vector_length * 4
+-	lsl	tmp1, vector_length, 1  // vector_length * 2
+-	whilelo	p2.b, tmp1, n
+-	incb	tmp1
+-	whilelo	p3.b, tmp1, n
+-	b.last	1f
+-	ld1b	z0.b, p0/z, [src, #0, mul vl]
+-	ld1b	z1.b, p1/z, [src, #1, mul vl]
+-	ld1b	z2.b, p2/z, [src, #2, mul vl]
+-	ld1b	z3.b, p3/z, [src, #3, mul vl]
+-	st1b	z0.b, p0, [dest, #0, mul vl]
+-	st1b	z1.b, p1, [dest, #1, mul vl]
+-	st1b	z2.b, p2, [dest, #2, mul vl]
+-	st1b	z3.b, p3, [dest, #3, mul vl]
+-	ret
+-1:	// if rest <= vector_length * 8
+-	lsl	tmp1, vector_length, 2  // vector_length * 4
+-	whilelo	p4.b, tmp1, n
+-	incb	tmp1
+-	whilelo	p5.b, tmp1, n
+-	b.last	1f
+-	ld1b	z0.b, p0/z, [src, #0, mul vl]
+-	ld1b	z1.b, p1/z, [src, #1, mul vl]
+-	ld1b	z2.b, p2/z, [src, #2, mul vl]
+-	ld1b	z3.b, p3/z, [src, #3, mul vl]
+-	ld1b	z4.b, p4/z, [src, #4, mul vl]
+-	ld1b	z5.b, p5/z, [src, #5, mul vl]
+-	st1b	z0.b, p0, [dest, #0, mul vl]
+-	st1b	z1.b, p1, [dest, #1, mul vl]
+-	st1b	z2.b, p2, [dest, #2, mul vl]
+-	st1b	z3.b, p3, [dest, #3, mul vl]
+-	st1b	z4.b, p4, [dest, #4, mul vl]
+-	st1b	z5.b, p5, [dest, #5, mul vl]
+-	ret
+-1:	lsl	tmp1, vector_length, 2	// vector_length * 4
+-	incb	tmp1			// vector_length * 5
+-	incb	tmp1			// vector_length * 6
+-	whilelo	p6.b, tmp1, n
+-	incb	tmp1
+-	whilelo	p7.b, tmp1, n
+-	ld1b	z0.b, p0/z, [src, #0, mul vl]
+-	ld1b	z1.b, p1/z, [src, #1, mul vl]
+-	ld1b	z2.b, p2/z, [src, #2, mul vl]
+-	ld1b	z3.b, p3/z, [src, #3, mul vl]
+-	ld1b	z4.b, p4/z, [src, #4, mul vl]
+-	ld1b	z5.b, p5/z, [src, #5, mul vl]
+-	ld1b	z6.b, p6/z, [src, #6, mul vl]
+-	ld1b	z7.b, p7/z, [src, #7, mul vl]
+-	st1b	z0.b, p0, [dest, #0, mul vl]
+-	st1b	z1.b, p1, [dest, #1, mul vl]
+-	st1b	z2.b, p2, [dest, #2, mul vl]
+-	st1b	z3.b, p3, [dest, #3, mul vl]
+-	st1b	z4.b, p4, [dest, #4, mul vl]
+-	st1b	z5.b, p5, [dest, #5, mul vl]
+-	st1b	z6.b, p6, [dest, #6, mul vl]
+-	st1b	z7.b, p7, [dest, #7, mul vl]
+-	ret
+-	.endm
++#undef BTI_C
++#define BTI_C
+ 
+ ENTRY (MEMCPY)
+ 
+@@ -185,223 +103,209 @@ ENTRY (MEMCPY)
+ 	PTR_ARG (1)
+ 	SIZE_ARG (2)
+ 
+-L(memcpy):
+-	cntb	vector_length
+-	// shortcut for less than vector_length * 8
+-	// gives a free ptrue to p0.b for n >= vector_length
+-	shortcut_for_small_size L(vl_agnostic)
+-	// end of shortcut
+-
+-L(vl_agnostic): // VL Agnostic
+-	mov	rest, n
+-	mov	dest_ptr, dest
+-	mov	src_ptr, src
+-	// if rest >= L2_SIZE && vector_length == 64 then L(L2)
+-	mov	tmp1, 64
+-	cmp	rest, L2_SIZE
+-	ccmp	vector_length, tmp1, 0, cs
+-	b.eq	L(L2)
+-
+-L(unroll8): // unrolling and software pipeline
+-	lsl	tmp1, vector_length, 3	// vector_length * 8
+-	.p2align 3
+-	cmp	 rest, tmp1
+-	b.cc	L(last)
++	cntb	vlen
++	cmp	n, vlen, lsl 1
++	b.hi	L(copy_small)
++	whilelo	p1.b, vlen, n
++	whilelo	p0.b, xzr, n
++	ld1b	z0.b, p0/z, [src, 0, mul vl]
++	ld1b	z1.b, p1/z, [src, 1, mul vl]
++	st1b	z0.b, p0, [dstin, 0, mul vl]
++	st1b	z1.b, p1, [dstin, 1, mul vl]
++	ret
++
++	.p2align 4
++
++L(copy_small):
++	cmp	n, vlen, lsl 3
++	b.hi	L(copy_large)
++	add	dstend, dstin, n
++	add	srcend, src, n
++	cmp	n, vlen, lsl 2
++	b.hi	1f
++
++	/* Copy 2-4 vectors.  */
++	ptrue	p0.b
++	ld1b	z0.b, p0/z, [src, 0, mul vl]
++	ld1b	z1.b, p0/z, [src, 1, mul vl]
++	ld1b	z2.b, p0/z, [srcend, -2, mul vl]
++	ld1b	z3.b, p0/z, [srcend, -1, mul vl]
++	st1b	z0.b, p0, [dstin, 0, mul vl]
++	st1b	z1.b, p0, [dstin, 1, mul vl]
++	st1b	z2.b, p0, [dstend, -2, mul vl]
++	st1b	z3.b, p0, [dstend, -1, mul vl]
++	ret
++
++	.p2align 4
++	/* Copy 4-8 vectors.  */
++1:	ptrue	p0.b
++	ld1b	z0.b, p0/z, [src, 0, mul vl]
++	ld1b	z1.b, p0/z, [src, 1, mul vl]
++	ld1b	z2.b, p0/z, [src, 2, mul vl]
++	ld1b	z3.b, p0/z, [src, 3, mul vl]
++	ld1b	z4.b, p0/z, [srcend, -4, mul vl]
++	ld1b	z5.b, p0/z, [srcend, -3, mul vl]
++	ld1b	z6.b, p0/z, [srcend, -2, mul vl]
++	ld1b	z7.b, p0/z, [srcend, -1, mul vl]
++	st1b	z0.b, p0, [dstin, 0, mul vl]
++	st1b	z1.b, p0, [dstin, 1, mul vl]
++	st1b	z2.b, p0, [dstin, 2, mul vl]
++	st1b	z3.b, p0, [dstin, 3, mul vl]
++	st1b	z4.b, p0, [dstend, -4, mul vl]
++	st1b	z5.b, p0, [dstend, -3, mul vl]
++	st1b	z6.b, p0, [dstend, -2, mul vl]
++	st1b	z7.b, p0, [dstend, -1, mul vl]
++	ret
++
++	.p2align 4
++	/* At least 8 vectors - always align to vector length for
++	   higher and consistent write performance.  */
++L(copy_large):
++	sub	tmp, vlen, 1
++	and	tmp, dstin, tmp
++	sub	tmp, vlen, tmp
++	whilelo	p1.b, xzr, tmp
++	ld1b	z1.b, p1/z, [src]
++	st1b	z1.b, p1, [dstin]
++	add	dst, dstin, tmp
++	add	src, src, tmp
++	sub	n, n, tmp
++	ptrue	p0.b
++
++	lsl	vlen8, vlen, 3
++	subs	n, n, vlen8
++	b.ls	3f
+ 	ld1b_unroll8
+-	add	src_ptr, src_ptr, tmp1
+-	sub	rest, rest, tmp1
+-	cmp	rest, tmp1
+-	b.cc	2f
+-	.p2align 3
++	add	src, src, vlen8
++	subs	n, n, vlen8
++	b.ls	2f
++
++	.p2align 4
++	/* 8x unrolled and software pipelined loop.  */
+ 1:	stld1b_unroll8
+-	add	dest_ptr, dest_ptr, tmp1
+-	add	src_ptr, src_ptr, tmp1
+-	sub	rest, rest, tmp1
+-	cmp	rest, tmp1
+-	b.ge	1b
++	add	dst, dst, vlen8
++	add	src, src, vlen8
++	subs	n, n, vlen8
++	b.hi	1b
+ 2:	st1b_unroll8
+-	add	dest_ptr, dest_ptr, tmp1
+-
+-	.p2align 3
+-L(last):
+-	whilelo	p0.b, xzr, rest
+-	whilelo	p1.b, vector_length, rest
+-	b.last	1f
+-	ld1b	z0.b, p0/z, [src_ptr, #0, mul vl]
+-	ld1b	z1.b, p1/z, [src_ptr, #1, mul vl]
+-	st1b	z0.b, p0, [dest_ptr, #0, mul vl]
+-	st1b	z1.b, p1, [dest_ptr, #1, mul vl]
+-	ret
+-1:	lsl	tmp1, vector_length, 1	// vector_length * 2
+-	whilelo	p2.b, tmp1, rest
+-	incb	tmp1
+-	whilelo	p3.b, tmp1, rest
+-	b.last	1f
+-	ld1b	z0.b, p0/z, [src_ptr, #0, mul vl]
+-	ld1b	z1.b, p1/z, [src_ptr, #1, mul vl]
+-	ld1b	z2.b, p2/z, [src_ptr, #2, mul vl]
+-	ld1b	z3.b, p3/z, [src_ptr, #3, mul vl]
+-	st1b	z0.b, p0, [dest_ptr, #0, mul vl]
+-	st1b	z1.b, p1, [dest_ptr, #1, mul vl]
+-	st1b	z2.b, p2, [dest_ptr, #2, mul vl]
+-	st1b	z3.b, p3, [dest_ptr, #3, mul vl]
++	add	dst, dst, vlen8
++3:	add	n, n, vlen8
++
++	/* Move last 0-8 vectors.  */
++L(last_bytes):
++	cmp	n, vlen, lsl 1
++	b.hi	1f
++	whilelo	p0.b, xzr, n
++	whilelo	p1.b, vlen, n
++	ld1b	z0.b, p0/z, [src, 0, mul vl]
++	ld1b	z1.b, p1/z, [src, 1, mul vl]
++	st1b	z0.b, p0, [dst, 0, mul vl]
++	st1b	z1.b, p1, [dst, 1, mul vl]
+ 	ret
+-1:	lsl	tmp1, vector_length, 2	// vector_length * 4
+-	whilelo	p4.b, tmp1, rest
+-	incb	tmp1
+-	whilelo	p5.b, tmp1, rest
+-	incb	tmp1
+-	whilelo	p6.b, tmp1, rest
+-	incb	tmp1
+-	whilelo	p7.b, tmp1, rest
+-	ld1b	z0.b, p0/z, [src_ptr, #0, mul vl]
+-	ld1b	z1.b, p1/z, [src_ptr, #1, mul vl]
+-	ld1b	z2.b, p2/z, [src_ptr, #2, mul vl]
+-	ld1b	z3.b, p3/z, [src_ptr, #3, mul vl]
+-	ld1b	z4.b, p4/z, [src_ptr, #4, mul vl]
+-	ld1b	z5.b, p5/z, [src_ptr, #5, mul vl]
+-	ld1b	z6.b, p6/z, [src_ptr, #6, mul vl]
+-	ld1b	z7.b, p7/z, [src_ptr, #7, mul vl]
+-	st1b	z0.b, p0, [dest_ptr, #0, mul vl]
+-	st1b	z1.b, p1, [dest_ptr, #1, mul vl]
+-	st1b	z2.b, p2, [dest_ptr, #2, mul vl]
+-	st1b	z3.b, p3, [dest_ptr, #3, mul vl]
+-	st1b	z4.b, p4, [dest_ptr, #4, mul vl]
+-	st1b	z5.b, p5, [dest_ptr, #5, mul vl]
+-	st1b	z6.b, p6, [dest_ptr, #6, mul vl]
+-	st1b	z7.b, p7, [dest_ptr, #7, mul vl]
++
++	.p2align 4
++
++1:	add	srcend, src, n
++	add	dstend, dst, n
++	ld1b	z0.b, p0/z, [src, 0, mul vl]
++	ld1b	z1.b, p0/z, [src, 1, mul vl]
++	ld1b	z2.b, p0/z, [srcend, -2, mul vl]
++	ld1b	z3.b, p0/z, [srcend, -1, mul vl]
++	cmp	n, vlen, lsl 2
++	b.hi	1f
++
++	st1b	z0.b, p0, [dst, 0, mul vl]
++	st1b	z1.b, p0, [dst, 1, mul vl]
++	st1b	z2.b, p0, [dstend, -2, mul vl]
++	st1b	z3.b, p0, [dstend, -1, mul vl]
+ 	ret
+ 
+-L(L2):
+-	// align dest address at CACHE_LINE_SIZE byte boundary
+-	mov	tmp1, CACHE_LINE_SIZE
+-	ands	tmp2, dest_ptr, CACHE_LINE_SIZE - 1
+-	// if cl_remainder == 0
+-	b.eq	L(L2_dc_zva)
+-	sub	cl_remainder, tmp1, tmp2
+-	// process remainder until the first CACHE_LINE_SIZE boundary
+-	whilelo	p1.b, xzr, cl_remainder	// keep p0.b all true
+-	whilelo	p2.b, vector_length, cl_remainder
+-	b.last	1f
+-	ld1b	z1.b, p1/z, [src_ptr, #0, mul vl]
+-	ld1b	z2.b, p2/z, [src_ptr, #1, mul vl]
+-	st1b	z1.b, p1, [dest_ptr, #0, mul vl]
+-	st1b	z2.b, p2, [dest_ptr, #1, mul vl]
+-	b	2f
+-1:	lsl	tmp1, vector_length, 1	// vector_length * 2
+-	whilelo	p3.b, tmp1, cl_remainder
+-	incb	tmp1
+-	whilelo	p4.b, tmp1, cl_remainder
+-	ld1b	z1.b, p1/z, [src_ptr, #0, mul vl]
+-	ld1b	z2.b, p2/z, [src_ptr, #1, mul vl]
+-	ld1b	z3.b, p3/z, [src_ptr, #2, mul vl]
+-	ld1b	z4.b, p4/z, [src_ptr, #3, mul vl]
+-	st1b	z1.b, p1, [dest_ptr, #0, mul vl]
+-	st1b	z2.b, p2, [dest_ptr, #1, mul vl]
+-	st1b	z3.b, p3, [dest_ptr, #2, mul vl]
+-	st1b	z4.b, p4, [dest_ptr, #3, mul vl]
+-2:	add	dest_ptr, dest_ptr, cl_remainder
+-	add	src_ptr, src_ptr, cl_remainder
+-	sub	rest, rest, cl_remainder
+-
+-L(L2_dc_zva):
+-	// zero fill
+-	and	tmp1, dest, 0xffffffffffffff
+-	and	tmp2, src, 0xffffffffffffff
+-	subs	tmp1, tmp1, tmp2	// diff
+-	b.ge	1f
+-	neg	tmp1, tmp1
+-1:	mov	tmp3, ZF_DIST + CACHE_LINE_SIZE * 2
+-	cmp	tmp1, tmp3
+-	b.lo	L(unroll8)
+-	mov	tmp1, dest_ptr
+-	dc_zva	(ZF_DIST / CACHE_LINE_SIZE) - 1
+-	// unroll
+-	ld1b_unroll8	// this line has to be after "b.lo L(unroll8)"
+-	add	 src_ptr, src_ptr, CACHE_LINE_SIZE * 2
+-	sub	 rest, rest, CACHE_LINE_SIZE * 2
+-	mov	 tmp1, ZF_DIST
+-	.p2align 3
+-1:	stld1b_unroll4a
+-	add	tmp2, dest_ptr, tmp1	// dest_ptr + ZF_DIST
+-	dc	zva, tmp2
+-	stld1b_unroll4b
+-	add	tmp2, tmp2, CACHE_LINE_SIZE
+-	dc	zva, tmp2
+-	add	dest_ptr, dest_ptr, CACHE_LINE_SIZE * 2
+-	add	src_ptr, src_ptr, CACHE_LINE_SIZE * 2
+-	sub	rest, rest, CACHE_LINE_SIZE * 2
+-	cmp	rest, tmp3	// ZF_DIST + CACHE_LINE_SIZE * 2
+-	b.ge	1b
+-	st1b_unroll8
+-	add	dest_ptr, dest_ptr, CACHE_LINE_SIZE * 2
+-	b	L(unroll8)
++1:	ld1b	z4.b, p0/z, [src, 2, mul vl]
++	ld1b	z5.b, p0/z, [src, 3, mul vl]
++	ld1b	z6.b, p0/z, [srcend, -4, mul vl]
++	ld1b	z7.b, p0/z, [srcend, -3, mul vl]
++	st1b	z0.b, p0, [dst, 0, mul vl]
++	st1b	z1.b, p0, [dst, 1, mul vl]
++	st1b	z4.b, p0, [dst, 2, mul vl]
++	st1b	z5.b, p0, [dst, 3, mul vl]
++	st1b	z6.b, p0, [dstend, -4, mul vl]
++	st1b	z7.b, p0, [dstend, -3, mul vl]
++	st1b	z2.b, p0, [dstend, -2, mul vl]
++	st1b	z3.b, p0, [dstend, -1, mul vl]
++	ret
+ 
+ END (MEMCPY)
+ libc_hidden_builtin_def (MEMCPY)
+ 
+ 
+-ENTRY (MEMMOVE)
++ENTRY_ALIGN (MEMMOVE, 4)
+ 
+ 	PTR_ARG (0)
+ 	PTR_ARG (1)
+ 	SIZE_ARG (2)
+ 
+-	// remove tag address
+-	// dest has to be immutable because it is the return value
+-	// src has to be immutable because it is used in L(bwd_last)
+-	and	tmp2, dest, 0xffffffffffffff	// save dest_notag into tmp2
+-	and	tmp3, src, 0xffffffffffffff	// save src_notag intp tmp3
+-	cmp	n, 0
+-	ccmp	tmp2, tmp3, 4, ne
+-	b.ne	1f
++	/* Fast case for up to 2 vectors.  */
++	cntb	vlen
++	cmp	n, vlen, lsl 1
++	b.hi	1f
++	whilelo	p0.b, xzr, n
++	whilelo	p1.b, vlen, n
++	ld1b	z0.b, p0/z, [src, 0, mul vl]
++	ld1b	z1.b, p1/z, [src, 1, mul vl]
++	st1b	z0.b, p0, [dstin, 0, mul vl]
++	st1b	z1.b, p1, [dstin, 1, mul vl]
++L(full_overlap):
+ 	ret
+-1:	cntb	vector_length
+-	// shortcut for less than vector_length * 8
+-	// gives a free ptrue to p0.b for n >= vector_length
+-	// tmp2 and tmp3 should not be used in this macro to keep
+-	// notag addresses
+-	shortcut_for_small_size L(dispatch)
+-	// end of shortcut
+-
+-L(dispatch):
+-	// tmp2 = dest_notag, tmp3 = src_notag
+-	// diff = dest_notag - src_notag
+-	sub	tmp1, tmp2, tmp3
+-	// if diff <= 0 || diff >= n then memcpy
+-	cmp	tmp1, 0
+-	ccmp	tmp1, n, 2, gt
+-	b.cs	L(vl_agnostic)
+-
+-L(bwd_start):
+-	mov	rest, n
+-	add	dest_ptr, dest, n	// dest_end
+-	add	src_ptr, src, n		// src_end
+-
+-L(bwd_unroll8): // unrolling and software pipeline
+-	lsl	tmp1, vector_length, 3	// vector_length * 8
+-	.p2align 3
+-	cmp	rest, tmp1
+-	b.cc	L(bwd_last)
+-	sub	src_ptr, src_ptr, tmp1
++
++	.p2align 4
++	/* Check for overlapping moves. Return if there is a full overlap.
++	   Small moves up to 8 vectors use the overlap-safe copy_small code.
++	   Non-overlapping or overlapping moves with dst < src use memcpy.
++	   Overlapping moves with dst > src use a backward copy loop.  */
++1:	sub	tmp, dstin, src
++	ands	tmp, tmp, 0xffffffffffffff	/* Clear special tag bits.  */
++	b.eq	L(full_overlap)
++	cmp	n, vlen, lsl 3
++	b.ls	L(copy_small)
++	cmp	tmp, n
++	b.hs	L(copy_large)
++
++	/* Align to vector length.  */
++	add	dst, dstin, n
++	sub	tmp, vlen, 1
++	ands	tmp, dst, tmp
++	csel	tmp, tmp, vlen, ne
++	whilelo	p1.b, xzr, tmp
++	sub	n, n, tmp
++	ld1b	z1.b, p1/z, [src, n]
++	st1b	z1.b, p1, [dstin, n]
++	add	src, src, n
++	add	dst, dstin, n
++
++	ptrue	p0.b
++	lsl	vlen8, vlen, 3
++	subs	n, n, vlen8
++	b.ls	3f
++	sub	src, src, vlen8
+ 	ld1b_unroll8
+-	sub	rest, rest, tmp1
+-	cmp	rest, tmp1
+-	b.cc	2f
+-	.p2align 3
+-1:	sub	src_ptr, src_ptr, tmp1
+-	sub	dest_ptr, dest_ptr, tmp1
++	subs	n, n, vlen8
++	b.ls	2f
++
++	.p2align 4
++	/* 8x unrolled and software pipelined backward copy loop.  */
++1:	sub	src, src, vlen8
++	sub	dst, dst, vlen8
+ 	stld1b_unroll8
+-	sub	rest, rest, tmp1
+-	cmp	rest, tmp1
+-	b.ge	1b
+-2:	sub	dest_ptr, dest_ptr, tmp1
++	subs	n, n, vlen8
++	b.hi	1b
++2:	sub	dst, dst, vlen8
+ 	st1b_unroll8
++3:	add	n, n, vlen8
+ 
+-L(bwd_last):
+-	mov	dest_ptr, dest
+-	mov	src_ptr, src
+-	b	L(last)
++	/* Adjust src/dst for last 0-8 vectors.  */
++	sub	src, src, n
++	mov	dst, dstin
++	b	L(last_bytes)
+ 
+ END (MEMMOVE)
+ libc_hidden_builtin_def (MEMMOVE)
+-- 
+2.31.1
+
diff --git a/SOURCES/glibc-rh2047981-1.patch b/SOURCES/glibc-rh2047981-1.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e1a085d66afb58a4dbc354c3e7f2031b509e6dbe
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-1.patch
@@ -0,0 +1,98 @@
+commit eb77a1fccc7e60cea32245c11288c7f1d92545fa
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Wed Oct 16 18:19:51 2019 +0200
+
+    dlfcn: Remove remnants of caller sensitivity from dlinfo
+    
+    dlinfo operates on a specific handle, which means that there is no
+    caller sensivity involved.
+
+diff --git a/dlfcn/dlinfo.c b/dlfcn/dlinfo.c
+index 964572cc670ceba4..23ef3f57ca41afdf 100644
+--- a/dlfcn/dlinfo.c
++++ b/dlfcn/dlinfo.c
+@@ -26,7 +26,7 @@
+ int
+ dlinfo (void *handle, int request, void *arg)
+ {
+-  return __dlinfo (handle, request, arg, RETURN_ADDRESS (0));
++  return __dlinfo (handle, request, arg);
+ }
+ 
+ #else
+@@ -35,7 +35,6 @@ dlinfo (void *handle, int request, void *arg)
+ 
+ struct dlinfo_args
+ {
+-  ElfW(Addr) caller;
+   void *handle;
+   int request;
+   void *arg;
+@@ -47,24 +46,6 @@ dlinfo_doit (void *argsblock)
+   struct dlinfo_args *const args = argsblock;
+   struct link_map *l = args->handle;
+ 
+-# if 0
+-  if (args->handle == RTLD_SELF)
+-    {
+-      Lmid_t nsid;
+-
+-      /* Find the highest-addressed object that CALLER is not below.  */
+-      for (nsid = 0; nsid < DL_NNS; ++nsid)
+-	for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
+-	  if (caller >= l->l_map_start && caller < l->l_map_end
+-	      && (l->l_contiguous || _dl_addr_inside_object (l, caller)))
+-	    break;
+-
+-      if (l == NULL)
+-	_dl_signal_error (0, NULL, NULL, N_("\
+-RTLD_SELF used in code not dynamically loaded"));
+-    }
+-# endif
+-
+   switch (args->request)
+     {
+     case RTLD_DI_CONFIGADDR:
+@@ -108,16 +89,14 @@ RTLD_SELF used in code not dynamically loaded"));
+ }
+ 
+ int
+-__dlinfo (void *handle, int request, void *arg DL_CALLER_DECL)
++__dlinfo (void *handle, int request, void *arg)
+ {
+ # ifdef SHARED
+   if (!rtld_active ())
+-    return _dlfcn_hook->dlinfo (handle, request, arg,
+-				DL_CALLER);
++    return _dlfcn_hook->dlinfo (handle, request, arg);
+ # endif
+ 
+-  struct dlinfo_args args = { (ElfW(Addr)) DL_CALLER,
+-			      handle, request, arg };
++  struct dlinfo_args args = { handle, request, arg };
+   return _dlerror_run (&dlinfo_doit, &args) ? -1 : 0;
+ }
+ # ifdef SHARED
+diff --git a/include/dlfcn.h b/include/dlfcn.h
+index 0dc57dbe2217cfe7..93dd369ab12a5745 100644
+--- a/include/dlfcn.h
++++ b/include/dlfcn.h
+@@ -117,7 +117,7 @@ struct dlfcn_hook
+   int (*dladdr) (const void *address, Dl_info *info);
+   int (*dladdr1) (const void *address, Dl_info *info,
+ 		  void **extra_info, int flags);
+-  int (*dlinfo) (void *handle, int request, void *arg, void *dl_caller);
++  int (*dlinfo) (void *handle, int request, void *arg);
+   void *(*dlmopen) (Lmid_t nsid, const char *file, int mode, void *dl_caller);
+   void *pad[4];
+ };
+@@ -143,8 +143,7 @@ extern int __dladdr (const void *address, Dl_info *info)
+ extern int __dladdr1 (const void *address, Dl_info *info,
+ 		      void **extra_info, int flags)
+      attribute_hidden;
+-extern int __dlinfo (void *handle, int request, void *arg DL_CALLER_DECL)
+-     attribute_hidden;
++extern int __dlinfo (void *handle, int request, void *arg) attribute_hidden;
+ 
+ #ifndef SHARED
+ struct link_map;
diff --git a/SOURCES/glibc-rh2047981-10.patch b/SOURCES/glibc-rh2047981-10.patch
new file mode 100644
index 0000000000000000000000000000000000000000..00b7a710778b83b265e04b55dae95e6f5c673945
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-10.patch
@@ -0,0 +1,31 @@
+commit 88361b408b9dbd313f15413cc2e6be0f1cafb01a
+Author: H.J. Lu <hjl.tools@gmail.com>
+Date:   Tue Aug 17 19:36:04 2021 -0700
+
+    elf: Copy l_addr/l_ld when adding ld.so to a new namespace
+    
+    When add ld.so to a new namespace, we don't actually load ld.so.  We
+    create a new link map and refers the real one for almost everything.
+    Copy l_addr and l_ld from the real ld.so link map to avoid GDB warning:
+    
+    warning: .dynamic section for ".../elf/ld-linux-x86-64.so.2" is not at the expected address (wrong library or version mismatch?)
+    
+    when handling shared library loaded by dlmopen.
+    
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/elf/dl-load.c b/elf/dl-load.c
+index cdb5d4b5b67f1ca1..303e6594f9af9b7e 100644
+--- a/elf/dl-load.c
++++ b/elf/dl-load.c
+@@ -932,6 +932,10 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
+       /* Refer to the real descriptor.  */
+       l->l_real = &GL(dl_rtld_map);
+ 
++      /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen().  */
++      l->l_addr = l->l_real->l_addr;
++      l->l_ld = l->l_real->l_ld;
++
+       /* No need to bump the refcount of the real object, ld.so will
+ 	 never be unloaded.  */
+       __close_nocancel (fd);
diff --git a/SOURCES/glibc-rh2047981-11.patch b/SOURCES/glibc-rh2047981-11.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a0cde6c045a4f91dc3c444e41502615784200b71
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-11.patch
@@ -0,0 +1,45 @@
+commit 1e1ecea62e899acb58c3fdf3b320a0833ddd0dff
+Author: H.J. Lu <hjl.tools@gmail.com>
+Date:   Thu Sep 30 10:29:17 2021 -0700
+
+    elf: Replace nsid with args.nsid [BZ #27609]
+    
+    commit ec935dea6332cb22f9881cd1162bad156173f4b0
+    Author: Florian Weimer <fweimer@redhat.com>
+    Date:   Fri Apr 24 22:31:15 2020 +0200
+    
+        elf: Implement __libc_early_init
+    
+    has
+    
+    @@ -856,6 +876,11 @@ no more namespaces available for dlmopen()"));
+       /* See if an error occurred during loading.  */
+       if (__glibc_unlikely (exception.errstring != NULL))
+         {
+    +      /* Avoid keeping around a dangling reference to the libc.so link
+    +   map in case it has been cached in libc_map.  */
+    +      if (!args.libc_already_loaded)
+    +  GL(dl_ns)[nsid].libc_map = NULL;
+    +
+    
+    do_dlopen calls _dl_open with nsid == __LM_ID_CALLER (-2), which calls
+    dl_open_worker with args.nsid = nsid.  dl_open_worker updates args.nsid
+    if it is __LM_ID_CALLER.  After dl_open_worker returns, it is wrong to
+    use nsid.
+    
+    Replace nsid with args.nsid after dl_open_worker returns.  This fixes
+    BZ #27609.
+
+diff --git a/elf/dl-open.c b/elf/dl-open.c
+index 661a2172d1789b26..b5a4da04907d8d29 100644
+--- a/elf/dl-open.c
++++ b/elf/dl-open.c
+@@ -916,7 +916,7 @@ no more namespaces available for dlmopen()"));
+       /* Avoid keeping around a dangling reference to the libc.so link
+ 	 map in case it has been cached in libc_map.  */
+       if (!args.libc_already_loaded)
+-	GL(dl_ns)[nsid].libc_map = NULL;
++	GL(dl_ns)[args.nsid].libc_map = NULL;
+ 
+       /* Remove the object from memory.  It may be in an inconsistent
+ 	 state if relocation failed, for example.  */
diff --git a/SOURCES/glibc-rh2047981-12.patch b/SOURCES/glibc-rh2047981-12.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8588aaae7d06343f1a993e25f2c7e3287faf79e0
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-12.patch
@@ -0,0 +1,607 @@
+This is a partial backport of this commit with only the 'scope'
+refactoring required to have access to the outer scope value
+to use with RESOLVE_MAP to implement la_symbind for BIND_NOW.
+
+We do not backport this entire patch because the nested function
+changes have significant impact on code generation and would
+require furhter backports to support and maintain.
+
+commit 490e6c62aa31a8aa5c4a059f6e646ede121edf0a
+Author: Fangrui Song <maskray@google.com>
+Date:   Thu Oct 7 11:55:02 2021 -0700
+
+    elf: Avoid nested functions in the loader [BZ #27220]
+    
+    dynamic-link.h is included more than once in some elf/ files (rtld.c,
+    dl-conflict.c, dl-reloc.c, dl-reloc-static-pie.c) and uses GCC nested
+    functions. This harms readability and the nested functions usage
+    is the biggest obstacle prevents Clang build (Clang doesn't support GCC
+    nested functions).
+    
+    The key idea for unnesting is to add extra parameters (struct link_map
+    *and struct r_scope_elm *[]) to RESOLVE_MAP,
+    ELF_MACHINE_BEFORE_RTLD_RELOC, ELF_DYNAMIC_RELOCATE, elf_machine_rel[a],
+    elf_machine_lazy_rel, and elf_machine_runtime_setup. (This is inspired
+    by Stan Shebs' ppc64/x86-64 implementation in the
+    google/grte/v5-2.27/master which uses mixed extra parameters and static
+    variables.)
+    
+    Future simplification:
+    * If mips elf_machine_runtime_setup no longer needs RESOLVE_GOTSYM,
+      elf_machine_runtime_setup can drop the `scope` parameter.
+    * If TLSDESC no longer need to be in elf_machine_lazy_rel,
+      elf_machine_lazy_rel can drop the `scope` parameter.
+    
+    Tested on aarch64, i386, x86-64, powerpc64le, powerpc64, powerpc32,
+    sparc64, sparcv9, s390x, s390, hppa, ia64, armhf, alpha, and mips64.
+    In addition, tested build-many-glibcs.py with {arc,csky,microblaze,nios2}-linux-gnu
+    and riscv64-linux-gnu-rv64imafdc-lp64d.
+    
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/elf/dl-conflict.c b/elf/dl-conflict.c
+index 70f14b04cd383048..31d87ac846427752 100644
+--- a/elf/dl-conflict.c
++++ b/elf/dl-conflict.c
+@@ -40,7 +40,7 @@ _dl_resolve_conflicts (struct link_map *l, ElfW(Rela) *conflict,
+        data.  */
+ 
+     /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code.  */
+-#define RESOLVE_MAP(ref, version, flags) (*ref = NULL, NULL)
++#define RESOLVE_MAP(map, scope, ref, version, flags) (*ref = NULL, NULL)
+ #define RESOLVE(ref, version, flags) (*ref = NULL, 0)
+ #define RESOLVE_CONFLICT_FIND_MAP(map, r_offset) \
+   do {									      \
+@@ -67,8 +67,8 @@ _dl_resolve_conflicts (struct link_map *l, ElfW(Rela) *conflict,
+     GL(dl_num_cache_relocations) += conflictend - conflict;
+ 
+     for (; conflict < conflictend; ++conflict)
+-      elf_machine_rela (l, conflict, NULL, NULL, (void *) conflict->r_offset,
+-			0);
++      elf_machine_rela (l, NULL, conflict, NULL, NULL,
++			(void *) conflict->r_offset, 0);
+   }
+ #endif
+ }
+diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
+index ab1ce0eacced9d2b..1efbf515c3c1c16d 100644
+--- a/elf/dl-reloc-static-pie.c
++++ b/elf/dl-reloc-static-pie.c
+@@ -30,7 +30,7 @@ _dl_relocate_static_pie (void)
+ 
+ # define STATIC_PIE_BOOTSTRAP
+ # define BOOTSTRAP_MAP (main_map)
+-# define RESOLVE_MAP(sym, version, flags) BOOTSTRAP_MAP
++# define RESOLVE_MAP(map, scope, sym, version, flags) BOOTSTRAP_MAP
+ # include "dynamic-link.h"
+ 
+   /* Figure out the run-time load address of static PIE.  */
+@@ -46,7 +46,7 @@ _dl_relocate_static_pie (void)
+ 
+   /* Relocate ourselves so we can do normal function calls and
+      data access using the global offset table.  */
+-  ELF_DYNAMIC_RELOCATE (main_map, 0, 0, 0);
++  ELF_DYNAMIC_RELOCATE (main_map, NULL, 0, 0, 0);
+   main_map->l_relocated = 1;
+ 
+   /* Initialize _r_debug.  */
+diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
+index c6139b89d4ecddc8..19de5de067a5ef07 100644
+--- a/elf/dl-reloc.c
++++ b/elf/dl-reloc.c
+@@ -250,7 +250,7 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
+     const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
+ 
+     /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code.  */
+-#define RESOLVE_MAP(ref, version, r_type) \
++#define RESOLVE_MAP(l, scope, ref, version, r_type) \
+     ((ELFW(ST_BIND) ((*ref)->st_info) != STB_LOCAL			      \
+       && __glibc_likely (!dl_symbol_visibility_binds_local_p (*ref)))	      \
+      ? ((__builtin_expect ((*ref) == l->l_lookup_cache.sym, 0)		      \
+@@ -275,7 +275,7 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
+ 
+ #include "dynamic-link.h"
+ 
+-    ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling, skip_ifunc);
++    ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc);
+ 
+ #ifndef PROF
+     if (__glibc_unlikely (consider_profiling)
+diff --git a/elf/do-rel.h b/elf/do-rel.h
+index 19cb5d236ee30698..0b04d1a0bf28b9f4 100644
+--- a/elf/do-rel.h
++++ b/elf/do-rel.h
+@@ -38,7 +38,7 @@
+    than fully resolved now.  */
+ 
+ auto inline void __attribute__ ((always_inline))
+-elf_dynamic_do_Rel (struct link_map *map,
++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)
+@@ -68,13 +68,13 @@ elf_dynamic_do_Rel (struct link_map *map,
+ 	  }
+ 	else
+ # endif
+-	  elf_machine_lazy_rel (map, l_addr, r, skip_ifunc);
++	  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, l_addr, r2, skip_ifunc);
++	    elf_machine_lazy_rel (map, scope, l_addr, r2, skip_ifunc);
+ # endif
+     }
+   else
+@@ -134,7 +134,7 @@ elf_dynamic_do_Rel (struct link_map *map,
+ #endif
+ 
+ 	      ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
+-	      elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)],
++	      elf_machine_rel (map, scope, r, &symtab[ELFW(R_SYM) (r->r_info)],
+ 			       &map->l_versions[ndx],
+ 			       (void *) (l_addr + r->r_offset), skip_ifunc);
+ 	    }
+@@ -146,7 +146,7 @@ elf_dynamic_do_Rel (struct link_map *map,
+ 		{
+ 		  ElfW(Half) ndx
+ 		    = version[ELFW(R_SYM) (r2->r_info)] & 0x7fff;
+-		  elf_machine_rel (map, r2,
++		  elf_machine_rel (map, scope, r2,
+ 				   &symtab[ELFW(R_SYM) (r2->r_info)],
+ 				   &map->l_versions[ndx],
+ 				   (void *) (l_addr + r2->r_offset),
+@@ -167,14 +167,14 @@ elf_dynamic_do_Rel (struct link_map *map,
+ 	      }
+ 	    else
+ # endif
+-	      elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], NULL,
++	      elf_machine_rel (map, scope, r, &symtab[ELFW(R_SYM) (r->r_info)], NULL,
+ 			       (void *) (l_addr + r->r_offset), 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_rel (map, r2, &symtab[ELFW(R_SYM) (r2->r_info)],
++		elf_machine_rel (map, scope, r2, &symtab[ELFW(R_SYM) (r2->r_info)],
+ 				 NULL, (void *) (l_addr + r2->r_offset),
+ 				 skip_ifunc);
+ # endif
+diff --git a/elf/dynamic-link.h b/elf/dynamic-link.h
+index 2fc3c91b7defe84e..357a2e3c6825e0fc 100644
+--- a/elf/dynamic-link.h
++++ b/elf/dynamic-link.h
+@@ -60,8 +60,9 @@ int _dl_try_allocate_static_tls (struct link_map *map, bool optional)
+    unaligned cases.  */
+ # if ! ELF_MACHINE_NO_REL
+ auto inline void __attribute__((always_inline))
+-elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc,
+-		 const ElfW(Sym) *sym, const struct r_found_version *version,
++elf_machine_rel (struct link_map *map, struct r_scope_elem *scope[],
++		 const ElfW(Rel) *reloc, const ElfW(Sym) *sym,
++		 const struct r_found_version *version,
+ 		 void *const reloc_addr, int skip_ifunc);
+ auto inline void __attribute__((always_inline))
+ elf_machine_rel_relative (ElfW(Addr) l_addr, const ElfW(Rel) *reloc,
+@@ -69,8 +70,9 @@ elf_machine_rel_relative (ElfW(Addr) l_addr, const ElfW(Rel) *reloc,
+ # endif
+ # if ! ELF_MACHINE_NO_RELA
+ auto inline void __attribute__((always_inline))
+-elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
+-		  const ElfW(Sym) *sym, const struct r_found_version *version,
++elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
++		  const ElfW(Rela) *reloc, const ElfW(Sym) *sym,
++		  const struct r_found_version *version,
+ 		  void *const reloc_addr, int skip_ifunc);
+ auto inline void __attribute__((always_inline))
+ elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
+@@ -78,12 +80,12 @@ elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
+ # endif
+ # if ELF_MACHINE_NO_RELA || defined ELF_MACHINE_PLT_REL
+ auto inline void __attribute__((always_inline))
+-elf_machine_lazy_rel (struct link_map *map,
++elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
+ 		      ElfW(Addr) l_addr, const ElfW(Rel) *reloc,
+ 		      int skip_ifunc);
+ # else
+ auto inline void __attribute__((always_inline))
+-elf_machine_lazy_rel (struct link_map *map,
++elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
+ 		      ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
+ 		      int skip_ifunc);
+ # endif
+@@ -114,7 +116,7 @@ elf_machine_lazy_rel (struct link_map *map,
+    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, 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; }  \
+@@ -152,13 +154,13 @@ elf_machine_lazy_rel (struct link_map *map,
+       }									      \
+ 									      \
+     if (ELF_DURING_STARTUP)						      \
+-      elf_dynamic_do_##reloc ((map), ranges[0].start, ranges[0].size,	      \
++      elf_dynamic_do_##reloc ((map), scope, ranges[0].start, ranges[0].size,  \
+ 			      ranges[0].nrelative, 0, skip_ifunc);	      \
+     else								      \
+       {									      \
+ 	int ranges_index;						      \
+ 	for (ranges_index = 0; ranges_index < 2; ++ranges_index)	      \
+-	  elf_dynamic_do_##reloc ((map),				      \
++	  elf_dynamic_do_##reloc ((map), scope,				      \
+ 				  ranges[ranges_index].start,		      \
+ 				  ranges[ranges_index].size,		      \
+ 				  ranges[ranges_index].nrelative,	      \
+@@ -175,29 +177,29 @@ elf_machine_lazy_rel (struct link_map *map,
+ 
+ # if ! ELF_MACHINE_NO_REL
+ #  include "do-rel.h"
+-#  define ELF_DYNAMIC_DO_REL(map, lazy, skip_ifunc) \
+-  _ELF_DYNAMIC_DO_RELOC (REL, Rel, map, lazy, skip_ifunc, _ELF_CHECK_REL)
++#  define ELF_DYNAMIC_DO_REL(map, scope, lazy, skip_ifunc) \
++  _ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, skip_ifunc, _ELF_CHECK_REL)
+ # else
+-#  define ELF_DYNAMIC_DO_REL(map, lazy, skip_ifunc) /* Nothing to do.  */
++#  define ELF_DYNAMIC_DO_REL(map, scope, lazy, skip_ifunc) /* Nothing to do.  */
+ # endif
+ 
+ # if ! ELF_MACHINE_NO_RELA
+ #  define DO_RELA
+ #  include "do-rel.h"
+-#  define ELF_DYNAMIC_DO_RELA(map, lazy, skip_ifunc) \
+-  _ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, lazy, skip_ifunc, _ELF_CHECK_REL)
++#  define ELF_DYNAMIC_DO_RELA(map, scope, lazy, skip_ifunc) \
++  _ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, skip_ifunc, _ELF_CHECK_REL)
+ # else
+-#  define ELF_DYNAMIC_DO_RELA(map, lazy, skip_ifunc) /* Nothing to do.  */
++#  define ELF_DYNAMIC_DO_RELA(map, scope, lazy, skip_ifunc) /* Nothing to do.  */
+ # endif
+ 
+ /* This can't just be an inline function because GCC is too dumb
+    to inline functions containing inlines themselves.  */
+-# define ELF_DYNAMIC_RELOCATE(map, lazy, consider_profile, skip_ifunc) \
++# define ELF_DYNAMIC_RELOCATE(map, scope, lazy, consider_profile, skip_ifunc) \
+   do {									      \
+-    int edr_lazy = elf_machine_runtime_setup ((map), (lazy),		      \
++    int edr_lazy = elf_machine_runtime_setup ((map), (scope), (lazy),	      \
+ 					      (consider_profile));	      \
+-    ELF_DYNAMIC_DO_REL ((map), edr_lazy, skip_ifunc);			      \
+-    ELF_DYNAMIC_DO_RELA ((map), edr_lazy, skip_ifunc);			      \
++    ELF_DYNAMIC_DO_REL ((map), (scope), edr_lazy, skip_ifunc);		      \
++    ELF_DYNAMIC_DO_RELA ((map), (scope), edr_lazy, skip_ifunc);		      \
+   } while (0)
+ 
+ #endif
+diff --git a/elf/rtld.c b/elf/rtld.c
+index e107af4014d43777..f3836b8a78faaf27 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -514,7 +514,7 @@ _dl_start (void *arg)
+      is trivial: always the map of ld.so itself.  */
+ #define RTLD_BOOTSTRAP
+ #define BOOTSTRAP_MAP (&bootstrap_map)
+-#define RESOLVE_MAP(sym, version, flags) BOOTSTRAP_MAP
++#define RESOLVE_MAP(map, scope, sym, version, flags) BOOTSTRAP_MAP
+ #include "dynamic-link.h"
+ 
+ #ifdef DONT_USE_BOOTSTRAP_MAP
+@@ -560,7 +560,7 @@ _dl_start (void *arg)
+       /* Relocate ourselves so we can do normal function calls and
+ 	 data access using the global offset table.  */
+ 
+-      ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0, 0);
++      ELF_DYNAMIC_RELOCATE (&bootstrap_map, NULL, 0, 0, 0);
+     }
+   bootstrap_map.l_relocated = 1;
+ 
+diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
+index 3fd3c8a265d012b1..5eab544afe2717f7 100644
+--- a/sysdeps/aarch64/dl-machine.h
++++ b/sysdeps/aarch64/dl-machine.h
+@@ -65,7 +65,8 @@ elf_machine_load_address (void)
+    entries will jump to the on-demand fixup code in dl-runtime.c.  */
+ 
+ static inline int __attribute__ ((unused))
+-elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
++elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
++			   int lazy, int profile)
+ {
+   if (l->l_info[DT_JMPREL] && lazy)
+     {
+@@ -242,8 +243,9 @@ elf_machine_plt_value (struct link_map *map,
+ 
+ auto inline void
+ __attribute__ ((always_inline))
+-elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
+-		  const ElfW(Sym) *sym, const struct r_found_version *version,
++elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
++		  const ElfW(Rela) *reloc, const ElfW(Sym) *sym,
++		  const struct r_found_version *version,
+ 		  void *const reloc_addr_arg, int skip_ifunc)
+ {
+   ElfW(Addr) *const reloc_addr = reloc_addr_arg;
+@@ -256,7 +258,8 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
+   else
+     {
+       const ElfW(Sym) *const refsym = sym;
+-      struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
++      struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version,
++					      r_type);
+       ElfW(Addr) value = SYMBOL_ADDRESS (sym_map, sym, true);
+ 
+       if (sym != NULL
+@@ -381,7 +384,7 @@ elf_machine_rela_relative (ElfW(Addr) l_addr,
+ 
+ inline void
+ __attribute__ ((always_inline))
+-elf_machine_lazy_rel (struct link_map *map,
++elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
+ 		      ElfW(Addr) l_addr,
+ 		      const ElfW(Rela) *reloc,
+ 		      int skip_ifunc)
+@@ -408,7 +411,7 @@ elf_machine_lazy_rel (struct link_map *map,
+ 		    (const void *)D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
+ 		  version = &map->l_versions[vernum[symndx] & 0x7fff];
+ 		}
+-	      elf_machine_rela (map, reloc, sym, version, reloc_addr,
++	      elf_machine_rela (map, scope, reloc, sym, version, reloc_addr,
+ 				skip_ifunc);
+ 	      return;
+ 	    }
+@@ -435,7 +438,7 @@ elf_machine_lazy_rel (struct link_map *map,
+ 
+       /* Always initialize TLS descriptors completely, because lazy
+ 	 initialization requires synchronization at every TLS access.  */
+-      elf_machine_rela (map, reloc, sym, version, reloc_addr, skip_ifunc);
++      elf_machine_rela (map, scope, reloc, sym, version, reloc_addr, skip_ifunc);
+     }
+   else if (__glibc_unlikely (r_type == AARCH64_R(IRELATIVE)))
+     {
+diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
+index 3a30671591284d79..5ba95b9e4af49942 100644
+--- a/sysdeps/i386/dl-machine.h
++++ b/sysdeps/i386/dl-machine.h
+@@ -61,7 +61,8 @@ elf_machine_load_address (void)
+    entries will jump to the on-demand fixup code in dl-runtime.c.  */
+ 
+ static inline int __attribute__ ((unused, always_inline))
+-elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
++elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
++			   int lazy, int profile)
+ {
+   Elf32_Addr *got;
+   extern void _dl_runtime_resolve (Elf32_Word) attribute_hidden;
+@@ -293,8 +294,9 @@ elf_machine_plt_value (struct link_map *map, const Elf32_Rel *reloc,
+ 
+ auto inline void
+ __attribute ((always_inline))
+-elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
+-		 const Elf32_Sym *sym, const struct r_found_version *version,
++elf_machine_rel (struct link_map *map, struct r_scope_elem *scope[],
++		 const Elf32_Rel *reloc, const Elf32_Sym *sym,
++		 const struct r_found_version *version,
+ 		 void *const reloc_addr_arg, int skip_ifunc)
+ {
+   Elf32_Addr *const reloc_addr = reloc_addr_arg;
+@@ -327,7 +329,8 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
+ # ifndef RTLD_BOOTSTRAP
+       const Elf32_Sym *const refsym = sym;
+ # endif
+-      struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
++      struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version,
++					      r_type);
+       Elf32_Addr value = SYMBOL_ADDRESS (sym_map, sym, true);
+ 
+       if (sym != NULL
+@@ -493,8 +496,9 @@ elf_machine_rel (struct link_map *map, const Elf32_Rel *reloc,
+ # ifndef RTLD_BOOTSTRAP
+ auto inline void
+ __attribute__ ((always_inline))
+-elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
+-		  const Elf32_Sym *sym, const struct r_found_version *version,
++elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
++		  const Elf32_Rela *reloc, const Elf32_Sym *sym,
++		  const struct r_found_version *version,
+ 		  void *const reloc_addr_arg, int skip_ifunc)
+ {
+   Elf32_Addr *const reloc_addr = reloc_addr_arg;
+@@ -507,7 +511,8 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
+ #  ifndef RESOLVE_CONFLICT_FIND_MAP
+       const Elf32_Sym *const refsym = sym;
+ #  endif
+-      struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
++      struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version,
++					      r_type);
+       Elf32_Addr value = SYMBOL_ADDRESS (sym_map, sym, true);
+ 
+       if (sym != NULL
+@@ -661,7 +666,7 @@ elf_machine_rela_relative (Elf32_Addr l_addr, const Elf32_Rela *reloc,
+ 
+ auto inline void
+ __attribute__ ((always_inline))
+-elf_machine_lazy_rel (struct link_map *map,
++elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
+ 		      Elf32_Addr l_addr, const Elf32_Rel *reloc,
+ 		      int skip_ifunc)
+ {
+@@ -696,13 +701,13 @@ elf_machine_lazy_rel (struct link_map *map,
+ 	  const ElfW(Half) *const version =
+ 	    (const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
+ 	  ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
+-	  elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)],
++	  elf_machine_rel (map, scope, r, &symtab[ELFW(R_SYM) (r->r_info)],
+ 			   &map->l_versions[ndx],
+ 			   (void *) (l_addr + r->r_offset), skip_ifunc);
+ 	}
+ # ifndef RTLD_BOOTSTRAP
+       else
+-	elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], NULL,
++	elf_machine_rel (map, scope, r, &symtab[ELFW(R_SYM) (r->r_info)], NULL,
+ 			 (void *) (l_addr + r->r_offset), skip_ifunc);
+ # endif
+     }
+@@ -721,7 +726,7 @@ elf_machine_lazy_rel (struct link_map *map,
+ 
+ auto inline void
+ __attribute__ ((always_inline))
+-elf_machine_lazy_rela (struct link_map *map,
++elf_machine_lazy_rela (struct link_map *map, struct r_scope_elem *scope[],
+ 		       Elf32_Addr l_addr, const Elf32_Rela *reloc,
+ 		       int skip_ifunc)
+ {
+@@ -745,7 +750,8 @@ elf_machine_lazy_rela (struct link_map *map,
+ 
+       /* Always initialize TLS descriptors completely at load time, in
+ 	 case static TLS is allocated for it that requires locking.  */
+-      elf_machine_rela (map, reloc, sym, version, reloc_addr, skip_ifunc);
++      elf_machine_rela (map, scope, reloc, sym, version, reloc_addr,
++			skip_ifunc);
+     }
+   else if (__glibc_unlikely (r_type == R_386_IRELATIVE))
+     {
+diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
+index 99a83d0c82ea0a9c..35996bb9173da231 100644
+--- a/sysdeps/powerpc/powerpc64/dl-machine.h
++++ b/sysdeps/powerpc/powerpc64/dl-machine.h
+@@ -345,7 +345,8 @@ dl_platform_init (void)
+ /* Set up the loaded object described by MAP so its unrelocated PLT
+    entries will jump to the on-demand fixup code in dl-runtime.c.  */
+ static inline int __attribute__ ((always_inline))
+-elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
++elf_machine_runtime_setup (struct link_map *map, struct r_scope_elem *scope[],
++			   int lazy, int profile)
+ {
+   if (map->l_info[DT_JMPREL])
+     {
+@@ -679,7 +680,7 @@ resolve_ifunc (Elf64_Addr value,
+ /* Perform the relocation specified by RELOC and SYM (which is fully
+    resolved).  MAP is the object containing the reloc.  */
+ auto inline void __attribute__ ((always_inline))
+-elf_machine_rela (struct link_map *map,
++elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
+ 		  const Elf64_Rela *reloc,
+ 		  const Elf64_Sym *sym,
+ 		  const struct r_found_version *version,
+@@ -707,7 +708,7 @@ elf_machine_rela (struct link_map *map,
+ 
+   /* We need SYM_MAP even in the absence of TLS, for elf_machine_fixup_plt
+      and STT_GNU_IFUNC.  */
+-  struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
++  struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version, r_type);
+   Elf64_Addr value = SYMBOL_ADDRESS (sym_map, sym, true) + reloc->r_addend;
+ 
+   if (sym != NULL
+@@ -1036,7 +1037,7 @@ elf_machine_rela (struct link_map *map,
+ }
+ 
+ auto inline void __attribute__ ((always_inline))
+-elf_machine_lazy_rel (struct link_map *map,
++elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
+ 		      Elf64_Addr l_addr, const Elf64_Rela *reloc,
+ 		      int skip_ifunc)
+ {
+diff --git a/sysdeps/s390/s390-64/dl-machine.h b/sysdeps/s390/s390-64/dl-machine.h
+index f22db7860b4da3ec..36327c40a1972dd7 100644
+--- a/sysdeps/s390/s390-64/dl-machine.h
++++ b/sysdeps/s390/s390-64/dl-machine.h
+@@ -75,7 +75,8 @@ elf_machine_load_address (void)
+    entries will jump to the on-demand fixup code in dl-runtime.c.  */
+ 
+ static inline int __attribute__ ((unused))
+-elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
++elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
++			   int lazy, int profile)
+ {
+   extern void _dl_runtime_resolve (Elf64_Word);
+   extern void _dl_runtime_profile (Elf64_Word);
+@@ -270,8 +271,9 @@ elf_machine_plt_value (struct link_map *map, const Elf64_Rela *reloc,
+ 
+ auto inline void
+ __attribute__ ((always_inline))
+-elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc,
+-		  const Elf64_Sym *sym, const struct r_found_version *version,
++elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
++		  const Elf64_Rela *reloc, const Elf64_Sym *sym,
++		  const struct r_found_version *version,
+ 		  void *const reloc_addr_arg, int skip_ifunc)
+ {
+   Elf64_Addr *const reloc_addr = reloc_addr_arg;
+@@ -304,7 +306,8 @@ elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc,
+       /* Only needed for R_390_COPY below.  */
+       const Elf64_Sym *const refsym = sym;
+ #endif
+-      struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
++      struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version,
++					      r_type);
+       Elf64_Addr value = SYMBOL_ADDRESS (sym_map, sym, true);
+ 
+       if (sym != NULL
+@@ -449,7 +452,7 @@ elf_machine_rela_relative (Elf64_Addr l_addr, const Elf64_Rela *reloc,
+ 
+ auto inline void
+ __attribute__ ((always_inline))
+-elf_machine_lazy_rel (struct link_map *map,
++elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
+ 		      Elf64_Addr l_addr, const Elf64_Rela *reloc,
+ 		      int skip_ifunc)
+ {
+diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h
+index b94d3b39ec1dca64..5262aa69c06aa8db 100644
+--- a/sysdeps/x86_64/dl-machine.h
++++ b/sysdeps/x86_64/dl-machine.h
+@@ -62,7 +62,8 @@ elf_machine_load_address (void)
+    entries will jump to the on-demand fixup code in dl-runtime.c.  */
+ 
+ static inline int __attribute__ ((unused, always_inline))
+-elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
++elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
++			   int lazy, int profile)
+ {
+   Elf64_Addr *got;
+   extern void _dl_runtime_resolve_fxsave (ElfW(Word)) attribute_hidden;
+@@ -260,8 +261,9 @@ elf_machine_plt_value (struct link_map *map, const ElfW(Rela) *reloc,
+ 
+ auto inline void
+ __attribute__ ((always_inline))
+-elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
+-		  const ElfW(Sym) *sym, const struct r_found_version *version,
++elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
++		  const ElfW(Rela) *reloc, const ElfW(Sym) *sym,
++		  const struct r_found_version *version,
+ 		  void *const reloc_addr_arg, int skip_ifunc)
+ {
+   ElfW(Addr) *const reloc_addr = reloc_addr_arg;
+@@ -300,7 +302,7 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
+ # ifndef RTLD_BOOTSTRAP
+       const ElfW(Sym) *const refsym = sym;
+ # endif
+-      struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
++      struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version, r_type);
+       ElfW(Addr) value = SYMBOL_ADDRESS (sym_map, sym, true);
+ 
+       if (sym != NULL
+@@ -539,7 +541,7 @@ elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
+ 
+ auto inline void
+ __attribute ((always_inline))
+-elf_machine_lazy_rel (struct link_map *map,
++elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
+ 		      ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
+ 		      int skip_ifunc)
+ {
+@@ -573,7 +575,7 @@ elf_machine_lazy_rel (struct link_map *map,
+ 
+       /* Always initialize TLS descriptors completely at load time, in
+ 	 case static TLS is allocated for it that requires locking.  */
+-      elf_machine_rela (map, reloc, sym, version, reloc_addr, skip_ifunc);
++      elf_machine_rela (map, scope, reloc, sym, version, reloc_addr, skip_ifunc);
+     }
+   else if (__glibc_unlikely (r_type == R_X86_64_IRELATIVE))
+     {
diff --git a/SOURCES/glibc-rh2047981-13.patch b/SOURCES/glibc-rh2047981-13.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d67e40f55952fe93c306ba0b212e0a06aa9153ed
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-13.patch
@@ -0,0 +1,65 @@
+commit 54816ae98d57930b7c945f17485714a5574bfe47
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Thu Jul 29 11:13:57 2021 -0300
+
+    elf: Move LAV_CURRENT to link_lavcurrent.h
+    
+    No functional change.
+
+diff --git a/bits/link_lavcurrent.h b/bits/link_lavcurrent.h
+new file mode 100644
+index 0000000000000000..44fbea1e8060997f
+--- /dev/null
++++ b/bits/link_lavcurrent.h
+@@ -0,0 +1,25 @@
++/* Data structure for communication from the run-time dynamic linker for
++   loaded ELF shared objects.  LAV_CURRENT definition.
++   Copyright (C) 2021 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/>.  */
++
++#ifndef _LINK_H
++# error "Never include <bits/link_lavcurrent.h> directly; use <link.h> instead."
++#endif
++
++/* Version numbers for la_version handshake interface.  */
++#define LAV_CURRENT	1
+diff --git a/elf/Makefile b/elf/Makefile
+index 6262a4a65cfd2148..b9751e8bd87c4f71 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -21,7 +21,7 @@ subdir		:= elf
+ 
+ include ../Makeconfig
+ 
+-headers		= elf.h bits/elfclass.h link.h bits/link.h
++headers		= elf.h bits/elfclass.h link.h bits/link.h bits/link_lavcurrent.h
+ routines	= $(all-dl-routines) dl-support dl-iteratephdr \
+ 		  dl-addr dl-addr-obj enbl-secure dl-profstub \
+ 		  dl-origin dl-libc dl-sym dl-sysdep dl-error \
+diff --git a/elf/link.h b/elf/link.h
+index c67a50dd8ee9187e..cbda60b4135997f6 100644
+--- a/elf/link.h
++++ b/elf/link.h
+@@ -96,7 +96,7 @@ struct link_map
+ #ifdef __USE_GNU
+ 
+ /* Version numbers for la_version handshake interface.  */
+-#define LAV_CURRENT	1
++#include <bits/link_lavcurrent.h>
+ 
+ /* Activity types signaled through la_activity.  */
+ enum
diff --git a/SOURCES/glibc-rh2047981-14.patch b/SOURCES/glibc-rh2047981-14.patch
new file mode 100644
index 0000000000000000000000000000000000000000..1d1295c178bf8c226a1711ab415d18fba22b76c4
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-14.patch
@@ -0,0 +1,388 @@
+Added $(objpfx)tst-audit18: $(libdl) in elf/Makefile since
+we still have $(libdl) in RHEL8.
+
+commit ed3ce71f5c64c5f07cbde0ef03554ea8950d8f2c
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Thu Nov 11 09:28:21 2021 -0300
+
+    elf: Move la_activity (LA_ACT_ADD) after _dl_add_to_namespace_list() (BZ #28062)
+    
+    It ensures that the the namespace is guaranteed to not be empty.
+    
+    Checked on x86_64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+Conflicts:
+	elf/Makefile
+	elf/dl-load.c
+		Conflict with missing MAP_ANON removal.
+
+diff --git a/elf/Makefile b/elf/Makefile
+index b9751e8bd87c4f71..2312184692433313 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -219,6 +219,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
+ 	 tst-dlmopen-dlerror \
+ 	 tst-dlmopen-gethostbyname \
+ 	 tst-audit17 \
++	 tst-audit18 \
+ #	 reldep9
+ tests-internal += loadtest unload unload2 circleload1 \
+ 	 neededtest neededtest2 neededtest3 neededtest4 \
+@@ -354,6 +355,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
+ 		libmarkermod5-5 tst-tls20mod-bad tst-tls21mod \
+ 		tst-dlmopen-dlerror-mod \
+ 		tst-dlmopen-gethostbyname-mod \
++		tst-auditmod18 \
++		tst-audit18mod \
+ 
+ # Most modules build with _ISOMAC defined, but those filtered out
+ # depend on internal headers.
+@@ -1539,6 +1542,11 @@ $(objpfx)tst-auditmod17.so: $(objpfx)tst-auditmod17.os
+ CFLAGS-.os += $(call elide-stack-protector,.os,tst-auditmod17)
+ tst-audit17-ENV = LD_AUDIT=$(objpfx)tst-auditmod17.so
+ 
++$(objpfx)tst-audit18: $(libdl)
++$(objpfx)tst-audit18.out: $(objpfx)tst-auditmod18.so \
++			  $(objpfx)tst-audit18mod.so
++tst-audit18-ARGS = -- $(host-test-program-cmd)
++
+ # tst-sonamemove links against an older implementation of the library.
+ LDFLAGS-tst-sonamemove-linkmod1.so = \
+   -Wl,--version-script=tst-sonamemove-linkmod1.map \
+diff --git a/elf/dl-load.c b/elf/dl-load.c
+index 303e6594f9af9b7e..de5aef5777045da5 100644
+--- a/elf/dl-load.c
++++ b/elf/dl-load.c
+@@ -978,42 +978,6 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
+     }
+ #endif
+ 
+-  /* Signal that we are going to add new objects.  */
+-  if (r->r_state == RT_CONSISTENT)
+-    {
+-#ifdef SHARED
+-      /* Auditing checkpoint: we are going to add new objects.  */
+-      if ((mode & __RTLD_AUDIT) == 0
+-	  && __glibc_unlikely (GLRO(dl_naudit) > 0))
+-	{
+-	  struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
+-	  /* Do not call the functions for any auditing object.  */
+-	  if (head->l_auditing == 0)
+-	    {
+-	      struct audit_ifaces *afct = GLRO(dl_audit);
+-	      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-		{
+-		  if (afct->activity != NULL)
+-		    afct->activity (&link_map_audit_state (head, cnt)->cookie,
+-				    LA_ACT_ADD);
+-
+-		  afct = afct->next;
+-		}
+-	    }
+-	}
+-#endif
+-
+-      /* Notify the debugger we have added some objects.  We need to
+-	 call _dl_debug_initialize in a static program in case dynamic
+-	 linking has not been used before.  */
+-      r->r_state = RT_ADD;
+-      _dl_debug_state ();
+-      LIBC_PROBE (map_start, 2, nsid, r);
+-      make_consistent = true;
+-    }
+-  else
+-    assert (r->r_state == RT_ADD);
+-
+   /* Enter the new object in the list of loaded objects.  */
+   l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
+   if (__glibc_unlikely (l == NULL))
+@@ -1432,6 +1396,44 @@ cannot enable executable stack as shared object requires");
+   /* Now that the object is fully initialized add it to the object list.  */
+   _dl_add_to_namespace_list (l, nsid);
+ 
++  /* Signal that we are going to add new objects.  */
++  if (r->r_state == RT_CONSISTENT)
++    {
++#ifdef SHARED
++      /* Auditing checkpoint: we are going to add new objects.  Since this
++         is called after _dl_add_to_namespace_list the namespace is guaranteed
++	 to not be empty.  */
++      if ((mode & __RTLD_AUDIT) == 0
++	  && __glibc_unlikely (GLRO(dl_naudit) > 0))
++	{
++	  struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
++	  /* Do not call the functions for any auditing object.  */
++	  if (head->l_auditing == 0)
++	    {
++	      struct audit_ifaces *afct = GLRO(dl_audit);
++	      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++		{
++		  if (afct->activity != NULL)
++		    afct->activity (&link_map_audit_state (head, cnt)->cookie,
++				    LA_ACT_ADD);
++
++		  afct = afct->next;
++		}
++	    }
++	}
++#endif
++
++      /* Notify the debugger we have added some objects.  We need to
++	 call _dl_debug_initialize in a static program in case dynamic
++	 linking has not been used before.  */
++      r->r_state = RT_ADD;
++      _dl_debug_state ();
++      LIBC_PROBE (map_start, 2, nsid, r);
++      make_consistent = true;
++    }
++  else
++    assert (r->r_state == RT_ADD);
++
+ #ifdef SHARED
+   /* Auditing checkpoint: we have a new object.  */
+   if (__glibc_unlikely (GLRO(dl_naudit) > 0)
+diff --git a/elf/tst-audit18.c b/elf/tst-audit18.c
+new file mode 100644
+index 0000000000000000..ef784908f60d50aa
+--- /dev/null
++++ b/elf/tst-audit18.c
+@@ -0,0 +1,129 @@
++/* Check DT_AUDIT with dlmopen.
++   Copyright (C) 2021 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/>.  */
++
++#include <array_length.h>
++#include <getopt.h>
++#include <string.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <gnu/lib-names.h>
++#include <support/capture_subprocess.h>
++#include <support/check.h>
++#include <support/xdlfcn.h>
++#include <support/xstdio.h>
++#include <support/support.h>
++
++static int restart;
++#define CMDLINE_OPTIONS \
++  { "restart", no_argument, &restart, 1 },
++
++static int
++handle_restart (void)
++{
++  {
++    void *h = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
++
++    pid_t (*s) (void) = xdlsym (h, "getpid");
++    TEST_COMPARE (s (), getpid ());
++
++    xdlclose (h);
++  }
++
++  {
++    void *h = xdlmopen (LM_ID_NEWLM, "tst-audit18mod.so", RTLD_NOW);
++
++    int (*foo) (void) = xdlsym (h, "foo");
++    TEST_COMPARE (foo (), 10);
++
++    xdlclose (h);
++  }
++
++  return 0;
++}
++
++static int
++do_test (int argc, char *argv[])
++{
++  /* We must have either:
++     - One our fource parameters left if called initially:
++       + path to ld.so         optional
++       + "--library-path"      optional
++       + the library path      optional
++       + the application name  */
++
++  if (restart)
++    return handle_restart ();
++
++  char *spargv[9];
++  int i = 0;
++  for (; i < argc - 1; i++)
++    spargv[i] = argv[i + 1];
++  spargv[i++] = (char *) "--direct";
++  spargv[i++] = (char *) "--restart";
++  spargv[i] = NULL;
++
++  setenv ("LD_AUDIT", "tst-auditmod18.so", 0);
++  struct support_capture_subprocess result
++    = support_capture_subprogram (spargv[0], spargv);
++  support_capture_subprocess_check (&result, "tst-audit18", 0, sc_allow_stderr);
++
++  struct
++  {
++    const char *name;
++    bool found;
++  } audit_iface[] =
++  {
++    { "la_version", false },
++    { "la_objsearch", false },
++    { "la_activity", false },
++    { "la_objopen", false },
++    { "la_objclose", false },
++    { "la_preinit", false },
++#if __WORDSIZE == 32
++    { "la_symbind32", false },
++#elif __WORDSIZE == 64
++    { "la_symbind64", false },
++#endif
++  };
++
++  /* Some hooks are called more than once but the test only check if any
++     is called at least once.  */
++  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
++  TEST_VERIFY (out != NULL);
++  char *buffer = NULL;
++  size_t buffer_length = 0;
++  while (xgetline (&buffer, &buffer_length, out))
++    {
++      for (int i = 0; i < array_length (audit_iface); i++)
++	if (strncmp (buffer, audit_iface[i].name,
++		     strlen (audit_iface[i].name)) == 0)
++	  audit_iface[i].found = true;
++    }
++  free (buffer);
++  xfclose (out);
++
++  for (int i = 0; i < array_length (audit_iface); i++)
++    TEST_COMPARE (audit_iface[i].found, true);
++
++  support_capture_subprocess_free (&result);
++
++  return 0;
++}
++
++#define TEST_FUNCTION_ARGV do_test
++#include <support/test-driver.c>
+diff --git a/elf/tst-audit18mod.c b/elf/tst-audit18mod.c
+new file mode 100644
+index 0000000000000000..096a9167c9f8353f
+--- /dev/null
++++ b/elf/tst-audit18mod.c
+@@ -0,0 +1,23 @@
++/* Check DT_AUDIT with dlmopen.
++   Copyright (C) 2021 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
++foo (void)
++{
++  return 10;
++}
+diff --git a/elf/tst-auditmod18.c b/elf/tst-auditmod18.c
+new file mode 100644
+index 0000000000000000..182992e9fdb1620c
+--- /dev/null
++++ b/elf/tst-auditmod18.c
+@@ -0,0 +1,73 @@
++/* Check DT_AUDIT with dlmopen.
++   Copyright (C) 2021 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/>.  */
++
++#include <stdio.h>
++#include <link.h>
++
++unsigned int
++la_version (unsigned int version)
++{
++  fprintf (stderr, "%s\n", __func__);
++  return LAV_CURRENT;
++}
++
++char *
++la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag)
++{
++  fprintf (stderr, "%s\n", __func__);
++  return (char *) name;
++}
++
++void
++la_activity (uintptr_t *cookie, unsigned int flag)
++{
++  fprintf (stderr, "%s\n", __func__);
++}
++
++unsigned int
++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
++{
++  fprintf (stderr, "%s\n", __func__);
++  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
++}
++
++unsigned int
++la_objclose (uintptr_t *cookie)
++{
++  fprintf (stderr, "%s\n", __func__);
++  return 0;
++}
++
++void
++la_preinit (uintptr_t *cookie)
++{
++  fprintf (stderr, "%s\n", __func__);
++}
++
++uintptr_t
++#if __ELF_NATIVE_CLASS == 32
++la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
++              uintptr_t *defcook, unsigned int *flags, const char *symname)
++#else
++la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
++              uintptr_t *defcook, unsigned int *flags, const char *symname)
++#endif
++{
++  fprintf (stderr, "%s\n", __func__);
++  return sym->st_value;
++}
diff --git a/SOURCES/glibc-rh2047981-15.patch b/SOURCES/glibc-rh2047981-15.patch
new file mode 100644
index 0000000000000000000000000000000000000000..7da5392ebc42c882de643341ef9f7df481238c7d
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-15.patch
@@ -0,0 +1,160 @@
+commit aee6e90f93e285016b6cd9c8bd00402c19ba271b
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Mon Jul 19 15:47:51 2021 -0300
+
+    elf: Add _dl_audit_objopen
+    
+    It consolidates the code required to call la_objopen audit callback.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+Conflicts:
+	elf/Makefile
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 2312184692433313..08a32a712a34f2cc 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -64,7 +64,8 @@ elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
+ # interpreter and operating independent of libc.
+ rtld-routines	= rtld $(all-dl-routines) dl-sysdep dl-environ dl-minimal \
+   dl-error-minimal dl-conflict dl-hwcaps dl-hwcaps_split dl-hwcaps-subdirs \
+-  dl-usage dl-diagnostics dl-diagnostics-kernel dl-diagnostics-cpu
++  dl-usage dl-diagnostics dl-diagnostics-kernel dl-diagnostics-cpu \
++  dl-audit
+ all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
+ 
+ CFLAGS-dl-runtime.c += -fexceptions -fasynchronous-unwind-tables
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+new file mode 100644
+index 0000000000000000..4066dfe85146b9d4
+--- /dev/null
++++ b/elf/dl-audit.c
+@@ -0,0 +1,39 @@
++/* Audit common functions.
++   Copyright (C) 2021 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/>.  */
++
++#include <ldsodefs.h>
++
++void
++_dl_audit_objopen (struct link_map *l, Lmid_t nsid)
++{
++  if (__glibc_likely (GLRO(dl_naudit) == 0))
++    return;
++
++  struct audit_ifaces *afct = GLRO(dl_audit);
++  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++    {
++      if (afct->objopen != NULL)
++	{
++	  struct auditstate *state = link_map_audit_state (l, cnt);
++	  state->bindflags = afct->objopen (l, nsid, &state->cookie);
++	  l->l_audit_any_plt |= state->bindflags != 0;
++	}
++
++      afct = afct->next;
++   }
++}
+diff --git a/elf/dl-load.c b/elf/dl-load.c
+index de5aef5777045da5..c11b1d1781e9b40b 100644
+--- a/elf/dl-load.c
++++ b/elf/dl-load.c
+@@ -1436,22 +1436,8 @@ cannot enable executable stack as shared object requires");
+ 
+ #ifdef SHARED
+   /* Auditing checkpoint: we have a new object.  */
+-  if (__glibc_unlikely (GLRO(dl_naudit) > 0)
+-      && !GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
+-    {
+-      struct audit_ifaces *afct = GLRO(dl_audit);
+-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-	{
+-	  if (afct->objopen != NULL)
+-	    {
+-	      struct auditstate *state = link_map_audit_state (l, cnt);
+-	      state->bindflags = afct->objopen (l, nsid, &state->cookie);
+-	      l->l_audit_any_plt |= state->bindflags != 0;
+-	    }
+-
+-	  afct = afct->next;
+-	}
+-    }
++  if (!GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
++    _dl_audit_objopen (l, nsid);
+ #endif
+ 
+   return l;
+diff --git a/elf/rtld.c b/elf/rtld.c
+index f3836b8a78faaf27..1982e42390760e0a 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -1075,25 +1075,6 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
+   dlmargs.map->l_auditing = 1;
+ }
+ 
+-/* Notify the the audit modules that the object MAP has already been
+-   loaded.  */
+-static void
+-notify_audit_modules_of_loaded_object (struct link_map *map)
+-{
+-  struct audit_ifaces *afct = GLRO(dl_audit);
+-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-    {
+-      if (afct->objopen != NULL)
+-	{
+-	  struct auditstate *state = link_map_audit_state (map, cnt);
+-	  state->bindflags = afct->objopen (map, LM_ID_BASE, &state->cookie);
+-	  map->l_audit_any_plt |= state->bindflags != 0;
+-	}
+-
+-      afct = afct->next;
+-    }
+-}
+-
+ /* Load all audit modules.  */
+ static void
+ load_audit_modules (struct link_map *main_map, struct audit_list *audit_list)
+@@ -1112,8 +1093,8 @@ load_audit_modules (struct link_map *main_map, struct audit_list *audit_list)
+      program and the dynamic linker itself).  */
+   if (GLRO(dl_naudit) > 0)
+     {
+-      notify_audit_modules_of_loaded_object (main_map);
+-      notify_audit_modules_of_loaded_object (&GL(dl_rtld_map));
++      _dl_audit_objopen (main_map, LM_ID_BASE);
++      _dl_audit_objopen (&GL(dl_rtld_map), LM_ID_BASE);
+     }
+ }
+ 
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index 2dd6f0c3c4aaaef5..410f070e28b74bdf 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -1268,6 +1268,11 @@ link_map_audit_state (struct link_map *l, size_t index)
+ {
+   return &l->l_audit[index];
+ }
++
++/* Call the la_objopen from the audit modules for the link_map L on the
++   namespace identification NSID.  */
++void _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
++  attribute_hidden;
+ #endif /* SHARED */
+ 
+ __END_DECLS
diff --git a/SOURCES/glibc-rh2047981-16.patch b/SOURCES/glibc-rh2047981-16.patch
new file mode 100644
index 0000000000000000000000000000000000000000..eec1516b2b2790b16616b5c270ae4a3f959f35d7
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-16.patch
@@ -0,0 +1,253 @@
+commit 3dac3959a5cb585b065cef2cb8a8d909c907e202
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Tue Jul 20 11:03:34 2021 -0300
+
+    elf: Add _dl_audit_activity_map and _dl_audit_activity_nsid
+    
+    It consolidates the code required to call la_activity audit
+    callback.
+    
+    Also for a new Lmid_t the namespace link_map list are empty, so it
+    requires to check if before using it.  This can happen for when audit
+    module is used along with dlmopen.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+index 4066dfe85146b9d4..74b87f4b39be75e1 100644
+--- a/elf/dl-audit.c
++++ b/elf/dl-audit.c
+@@ -18,6 +18,32 @@
+ 
+ #include <ldsodefs.h>
+ 
++void
++_dl_audit_activity_map (struct link_map *l, int action)
++{
++  struct audit_ifaces *afct = GLRO(dl_audit);
++  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++    {
++      if (afct->activity != NULL)
++	afct->activity (&link_map_audit_state (l, cnt)->cookie, action);
++      afct = afct->next;
++    }
++}
++
++void
++_dl_audit_activity_nsid (Lmid_t nsid, int action)
++{
++  /* If head is NULL, the namespace has become empty, and the audit interface
++     does not give us a way to signal LA_ACT_CONSISTENT for it because the
++     first loaded module is used to identify the namespace.  */
++  struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
++  if (__glibc_likely (GLRO(dl_naudit) == 0)
++      || head == NULL || head->l_auditing)
++    return;
++
++  _dl_audit_activity_map (head, action);
++}
++
+ void
+ _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
+ {
+diff --git a/elf/dl-close.c b/elf/dl-close.c
+index 698bda929c0eab6c..1ba594b600c4c87a 100644
+--- a/elf/dl-close.c
++++ b/elf/dl-close.c
+@@ -478,25 +478,7 @@ _dl_close_worker (struct link_map *map, bool force)
+ 
+ #ifdef SHARED
+   /* Auditing checkpoint: we will start deleting objects.  */
+-  if (__glibc_unlikely (do_audit))
+-    {
+-      struct link_map *head = ns->_ns_loaded;
+-      struct audit_ifaces *afct = GLRO(dl_audit);
+-      /* Do not call the functions for any auditing object.  */
+-      if (head->l_auditing == 0)
+-	{
+-	  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-	    {
+-	      if (afct->activity != NULL)
+-		{
+-		  struct auditstate *state = link_map_audit_state (head, cnt);
+-		  afct->activity (&state->cookie, LA_ACT_DELETE);
+-		}
+-
+-	      afct = afct->next;
+-	    }
+-	}
+-    }
++  _dl_audit_activity_nsid (nsid, LA_ACT_DELETE);
+ #endif
+ 
+   /* Notify the debugger we are about to remove some loaded objects.  */
+@@ -791,32 +773,9 @@ _dl_close_worker (struct link_map *map, bool force)
+   __rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
+ 
+ #ifdef SHARED
+-  /* Auditing checkpoint: we have deleted all objects.  */
+-  if (__glibc_unlikely (do_audit))
+-    {
+-      struct link_map *head = ns->_ns_loaded;
+-      /* If head is NULL, the namespace has become empty, and the
+-	 audit interface does not give us a way to signal
+-	 LA_ACT_CONSISTENT for it because the first loaded module is
+-	 used to identify the namespace.
+-
+-	 Furthermore, do not notify auditors of the cleanup of a
+-	 failed audit module loading attempt.  */
+-      if (head != NULL && head->l_auditing == 0)
+-	{
+-	  struct audit_ifaces *afct = GLRO(dl_audit);
+-	  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-	    {
+-	      if (afct->activity != NULL)
+-		{
+-		  struct auditstate *state = link_map_audit_state (head, cnt);
+-		  afct->activity (&state->cookie, LA_ACT_CONSISTENT);
+-		}
+-
+-	      afct = afct->next;
+-	    }
+-	}
+-    }
++  /* Auditing checkpoint: we have deleted all objects.  Also, do not notify
++     auditors of the cleanup of a failed audit module loading attempt.  */
++  _dl_audit_activity_nsid (nsid, LA_ACT_CONSISTENT);
+ #endif
+ 
+   if (__builtin_expect (ns->_ns_loaded == NULL, 0)
+diff --git a/elf/dl-load.c b/elf/dl-load.c
+index c11b1d1781e9b40b..8a18c761bb753e37 100644
+--- a/elf/dl-load.c
++++ b/elf/dl-load.c
+@@ -1403,24 +1403,8 @@ cannot enable executable stack as shared object requires");
+       /* Auditing checkpoint: we are going to add new objects.  Since this
+          is called after _dl_add_to_namespace_list the namespace is guaranteed
+ 	 to not be empty.  */
+-      if ((mode & __RTLD_AUDIT) == 0
+-	  && __glibc_unlikely (GLRO(dl_naudit) > 0))
+-	{
+-	  struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
+-	  /* Do not call the functions for any auditing object.  */
+-	  if (head->l_auditing == 0)
+-	    {
+-	      struct audit_ifaces *afct = GLRO(dl_audit);
+-	      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-		{
+-		  if (afct->activity != NULL)
+-		    afct->activity (&link_map_audit_state (head, cnt)->cookie,
+-				    LA_ACT_ADD);
+-
+-		  afct = afct->next;
+-		}
+-	    }
+-	}
++      if ((mode & __RTLD_AUDIT) == 0)
++	_dl_audit_activity_nsid (nsid, LA_ACT_ADD);
+ #endif
+ 
+       /* Notify the debugger we have added some objects.  We need to
+diff --git a/elf/dl-open.c b/elf/dl-open.c
+index b5a4da04907d8d29..660a56b2fb2639cd 100644
+--- a/elf/dl-open.c
++++ b/elf/dl-open.c
+@@ -598,25 +598,7 @@ dl_open_worker_begin (void *a)
+ 
+ #ifdef SHARED
+   /* Auditing checkpoint: we have added all objects.  */
+-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+-    {
+-      struct link_map *head = GL(dl_ns)[new->l_ns]._ns_loaded;
+-      /* Do not call the functions for any auditing object.  */
+-      if (head->l_auditing == 0)
+-	{
+-	  struct audit_ifaces *afct = GLRO(dl_audit);
+-	  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-	    {
+-	      if (afct->activity != NULL)
+-		{
+-		  struct auditstate *state = link_map_audit_state (head, cnt);
+-		  afct->activity (&state->cookie, LA_ACT_CONSISTENT);
+-		}
+-
+-	      afct = afct->next;
+-	    }
+-	}
+-    }
++  _dl_audit_activity_nsid (new->l_ns, LA_ACT_CONSISTENT);
+ #endif
+ 
+   /* Notify the debugger all new objects are now ready to go.  */
+diff --git a/elf/rtld.c b/elf/rtld.c
+index 1982e42390760e0a..767acd122262b824 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -1799,18 +1799,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
+ 
+   /* Auditing checkpoint: we are ready to signal that the initial map
+      is being constructed.  */
+-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+-    {
+-      struct audit_ifaces *afct = GLRO(dl_audit);
+-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-	{
+-	  if (afct->activity != NULL)
+-	    afct->activity (&link_map_audit_state (main_map, cnt)->cookie,
+-			    LA_ACT_ADD);
+-
+-	  afct = afct->next;
+-	}
+-    }
++  _dl_audit_activity_map (main_map, LA_ACT_ADD);
+ 
+   /* We have two ways to specify objects to preload: via environment
+      variable and via the file /etc/ld.so.preload.  The latter can also
+@@ -2484,23 +2473,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
+ 
+ #ifdef SHARED
+   /* Auditing checkpoint: we have added all objects.  */
+-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+-    {
+-      struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+-      /* Do not call the functions for any auditing object.  */
+-      if (head->l_auditing == 0)
+-	{
+-	  struct audit_ifaces *afct = GLRO(dl_audit);
+-	  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-	    {
+-	      if (afct->activity != NULL)
+-		afct->activity (&link_map_audit_state (head, cnt)->cookie,
+-				LA_ACT_CONSISTENT);
+-
+-	      afct = afct->next;
+-	    }
+-	}
+-    }
++  _dl_audit_activity_nsid (LM_ID_BASE, LA_ACT_CONSISTENT);
+ #endif
+ 
+   /* Notify the debugger all new objects are now ready to go.  We must re-get
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index 410f070e28b74bdf..05737342d6287233 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -1269,6 +1269,16 @@ link_map_audit_state (struct link_map *l, size_t index)
+   return &l->l_audit[index];
+ }
+ 
++/* Call the la_activity from the audit modules from the link map L and issues
++   the ACTION argument.  */
++void _dl_audit_activity_map (struct link_map *l, int action)
++  attribute_hidden;
++
++/* Call the la_activity from the audit modules from the link map from the
++   namespace NSID and issues the ACTION argument.  */
++void _dl_audit_activity_nsid (Lmid_t nsid, int action)
++  attribute_hidden;
++
+ /* Call the la_objopen from the audit modules for the link_map L on the
+    namespace identification NSID.  */
+ void _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
diff --git a/SOURCES/glibc-rh2047981-17.patch b/SOURCES/glibc-rh2047981-17.patch
new file mode 100644
index 0000000000000000000000000000000000000000..4a22e82df25a262ec97e6d685894c4dd2f6896e6
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-17.patch
@@ -0,0 +1,156 @@
+commit c91008d3490e4e3ce29520068405f081f0d368ca
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Tue Jul 20 13:47:36 2021 -0300
+
+    elf: Add _dl_audit_objsearch
+    
+    It consolidates the code required to call la_objsearch audit
+    callback.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+index 74b87f4b39be75e1..5682427220569d90 100644
+--- a/elf/dl-audit.c
++++ b/elf/dl-audit.c
+@@ -44,6 +44,28 @@ _dl_audit_activity_nsid (Lmid_t nsid, int action)
+   _dl_audit_activity_map (head, action);
+ }
+ 
++const char *
++_dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
++{
++  if (l == NULL || l->l_auditing || code == 0)
++    return name;
++
++  struct audit_ifaces *afct = GLRO(dl_audit);
++  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++    {
++      if (afct->objsearch != NULL)
++	{
++	  struct auditstate *state = link_map_audit_state (l, cnt);
++	  name = afct->objsearch (name, &state->cookie, code);
++	  if (name == NULL)
++	    return NULL;
++	}
++      afct = afct->next;
++   }
++
++  return name;
++}
++
+ void
+ _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
+ {
+diff --git a/elf/dl-load.c b/elf/dl-load.c
+index 8a18c761bb753e37..1613217a236c7fc3 100644
+--- a/elf/dl-load.c
++++ b/elf/dl-load.c
+@@ -1517,32 +1517,20 @@ open_verify (const char *name, int fd,
+ 
+ #ifdef SHARED
+   /* Give the auditing libraries a chance.  */
+-  if (__glibc_unlikely (GLRO(dl_naudit) > 0) && whatcode != 0
+-      && loader->l_auditing == 0)
++  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+     {
+       const char *original_name = name;
+-      struct audit_ifaces *afct = GLRO(dl_audit);
+-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-	{
+-	  if (afct->objsearch != NULL)
+-	    {
+-	      struct auditstate *state = link_map_audit_state (loader, cnt);
+-	      name = afct->objsearch (name, &state->cookie, whatcode);
+-	      if (name == NULL)
+-		/* Ignore the path.  */
+-		return -1;
+-	    }
+-
+-	  afct = afct->next;
+-	}
++      name = _dl_audit_objsearch (name, loader, whatcode);
++      if (name == NULL)
++	return -1;
+ 
+       if (fd != -1 && name != original_name && strcmp (name, original_name))
+-        {
+-          /* An audit library changed what we're supposed to open,
+-             so FD no longer matches it.  */
+-          __close_nocancel (fd);
+-          fd = -1;
+-        }
++	{
++	  /* An audit library changed what we're supposed to open,
++	     so FD no longer matches it.  */
++	  __close_nocancel (fd);
++	  fd = -1;
++	}
+     }
+ #endif
+ 
+@@ -1992,36 +1980,17 @@ _dl_map_object (struct link_map *loader, const char *name,
+ #ifdef SHARED
+   /* Give the auditing libraries a chance to change the name before we
+      try anything.  */
+-  if (__glibc_unlikely (GLRO(dl_naudit) > 0)
+-      && (loader == NULL || loader->l_auditing == 0))
++  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+     {
+-      struct audit_ifaces *afct = GLRO(dl_audit);
+-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++      const char *before = name;
++      name = _dl_audit_objsearch (name, loader, LA_SER_ORIG);
++      if (name == NULL)
+ 	{
+-	  if (afct->objsearch != NULL)
+-	    {
+-	      const char *before = name;
+-	      struct auditstate *state = link_map_audit_state (loader, cnt);
+-	      name = afct->objsearch (name, &state->cookie, LA_SER_ORIG);
+-	      if (name == NULL)
+-		{
+-		  /* Do not try anything further.  */
+-		  fd = -1;
+-		  goto no_file;
+-		}
+-	      if (before != name && strcmp (before, name) != 0)
+-		{
+-		  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+-		    _dl_debug_printf ("audit changed filename %s -> %s\n",
+-				      before, name);
+-
+-		  if (origname == NULL)
+-		    origname = before;
+-		}
+-	    }
+-
+-	  afct = afct->next;
++	  fd = -1;
++	  goto no_file;
+ 	}
++      if (before != name && strcmp (before, name) != 0)
++	origname = before;
+     }
+ #endif
+ 
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index 05737342d6287233..da83e717e8cd8e0b 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -1269,6 +1269,13 @@ link_map_audit_state (struct link_map *l, size_t index)
+   return &l->l_audit[index];
+ }
+ 
++/* Call the la_objsearch from the audit modules from the link map L.  If
++   ORIGNAME is non NULL, it is updated with the revious name prior calling
++   la_objsearch.  */
++const char *_dl_audit_objsearch (const char *name, struct link_map *l,
++				 unsigned int code)
++   attribute_hidden;
++
+ /* Call the la_activity from the audit modules from the link map L and issues
+    the ACTION argument.  */
+ void _dl_audit_activity_map (struct link_map *l, int action)
diff --git a/SOURCES/glibc-rh2047981-18.patch b/SOURCES/glibc-rh2047981-18.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b8662956ba9eec162ef1b304c981689118e260b8
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-18.patch
@@ -0,0 +1,122 @@
+commit 311c9ee54ea963ff69bd3a2e6981c37e893b4c3e
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Tue Jul 20 14:04:51 2021 -0300
+
+    elf: Add _dl_audit_objclose
+    
+    It consolidates the code required to call la_objclose audit
+    callback.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+index 5682427220569d90..cb1c3de93cba447b 100644
+--- a/elf/dl-audit.c
++++ b/elf/dl-audit.c
+@@ -85,3 +85,24 @@ _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
+       afct = afct->next;
+    }
+ }
++
++void
++_dl_audit_objclose (struct link_map *l)
++{
++  if (__glibc_likely (GLRO(dl_naudit) == 0)
++      || GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
++    return;
++
++  struct audit_ifaces *afct = GLRO(dl_audit);
++  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++    {
++      if (afct->objclose != NULL)
++	{
++	  struct auditstate *state= link_map_audit_state (l, cnt);
++	  /* Return value is ignored.  */
++	  afct->objclose (&state->cookie);
++	}
++
++      afct = afct->next;
++    }
++}
+diff --git a/elf/dl-close.c b/elf/dl-close.c
+index 1ba594b600c4c87a..74ca9a85dd309780 100644
+--- a/elf/dl-close.c
++++ b/elf/dl-close.c
+@@ -266,9 +266,6 @@ _dl_close_worker (struct link_map *map, bool force)
+ 		 used + (nsid == LM_ID_BASE), true);
+ 
+   /* Call all termination functions at once.  */
+-#ifdef SHARED
+-  bool do_audit = GLRO(dl_naudit) > 0 && !ns->_ns_loaded->l_auditing;
+-#endif
+   bool unload_any = false;
+   bool scope_mem_left = false;
+   unsigned int unload_global = 0;
+@@ -302,22 +299,7 @@ _dl_close_worker (struct link_map *map, bool force)
+ 
+ #ifdef SHARED
+ 	  /* Auditing checkpoint: we remove an object.  */
+-	  if (__glibc_unlikely (do_audit))
+-	    {
+-	      struct audit_ifaces *afct = GLRO(dl_audit);
+-	      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-		{
+-		  if (afct->objclose != NULL)
+-		    {
+-		      struct auditstate *state
+-			= link_map_audit_state (imap, cnt);
+-		      /* Return value is ignored.  */
+-		      (void) afct->objclose (&state->cookie);
+-		    }
+-
+-		  afct = afct->next;
+-		}
+-	    }
++	  _dl_audit_objclose (imap);
+ #endif
+ 
+ 	  /* This object must not be used anymore.  */
+diff --git a/elf/dl-fini.c b/elf/dl-fini.c
+index 915ceb104e1c81d6..e102d93647cb8c47 100644
+--- a/elf/dl-fini.c
++++ b/elf/dl-fini.c
+@@ -146,21 +146,7 @@ _dl_fini (void)
+ 
+ #ifdef SHARED
+ 		  /* Auditing checkpoint: another object closed.  */
+-		  if (!do_audit && __builtin_expect (GLRO(dl_naudit) > 0, 0))
+-		    {
+-		      struct audit_ifaces *afct = GLRO(dl_audit);
+-		      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-			{
+-			  if (afct->objclose != NULL)
+-			    {
+-			      struct auditstate *state
+-				= link_map_audit_state (l, cnt);
+-			      /* Return value is ignored.  */
+-			      (void) afct->objclose (&state->cookie);
+-			    }
+-			  afct = afct->next;
+-			}
+-		    }
++		  _dl_audit_objclose (l);
+ #endif
+ 		}
+ 
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index da83e717e8cd8e0b..3db25c5be1acf871 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -1290,6 +1290,10 @@ void _dl_audit_activity_nsid (Lmid_t nsid, int action)
+    namespace identification NSID.  */
+ void _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
+   attribute_hidden;
++
++/* Call the la_objclose from the audit modules for the link_map L.  */
++void _dl_audit_objclose (struct link_map *l)
++  attribute_hidden;
+ #endif /* SHARED */
+ 
+ __END_DECLS
diff --git a/SOURCES/glibc-rh2047981-19.patch b/SOURCES/glibc-rh2047981-19.patch
new file mode 100644
index 0000000000000000000000000000000000000000..2c554053ea55e61e7e1718b71d0fbc84eea97b31
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-19.patch
@@ -0,0 +1,333 @@
+commit cda4f265c65fb6c4ce38ca1cf0a7e527c5e77cd5
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Tue Jul 20 15:58:35 2021 -0300
+
+    elf: Add _dl_audit_symbind_alt and _dl_audit_symbind
+    
+    It consolidates the code required to call la_symbind{32,64} audit
+    callback.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+diff --git a/elf/Versions b/elf/Versions
+index be88c48e6d45a937..c5d4342cf1f5124c 100644
+--- a/elf/Versions
++++ b/elf/Versions
+@@ -59,6 +59,7 @@ ld {
+     _dl_argv; _dl_find_dso_for_object; _dl_get_tls_static_info;
+     _dl_deallocate_tls; _dl_make_stack_executable;
+     _dl_rtld_di_serinfo; _dl_starting_up; _dl_fatal_printf;
++    _dl_audit_symbind_alt;
+     _rtld_global; _rtld_global_ro;
+ 
+     # Only here for gdb while a better method is developed.
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+index cb1c3de93cba447b..a21530f30bc5524b 100644
+--- a/elf/dl-audit.c
++++ b/elf/dl-audit.c
+@@ -16,6 +16,7 @@
+    License along with the GNU C Library; if not, see
+    <https://www.gnu.org/licenses/>.  */
+ 
++#include <assert.h>
+ #include <ldsodefs.h>
+ 
+ void
+@@ -106,3 +107,124 @@ _dl_audit_objclose (struct link_map *l)
+       afct = afct->next;
+     }
+ }
++
++void
++_dl_audit_symbind_alt (struct link_map *l, const ElfW(Sym) *ref, void **value,
++		       lookup_t result)
++{
++  if ((l->l_audit_any_plt | result->l_audit_any_plt) == 0)
++    return;
++
++  const char *strtab = (const char *) D_PTR (result, l_info[DT_STRTAB]);
++  /* Compute index of the symbol entry in the symbol table of the DSO with
++     the definition.  */
++  unsigned int ndx = (ref - (ElfW(Sym) *) D_PTR (result, l_info[DT_SYMTAB]));
++
++  unsigned int altvalue = 0;
++  /* Synthesize a symbol record where the st_value field is the result.  */
++  ElfW(Sym) sym = *ref;
++  sym.st_value = (ElfW(Addr)) *value;
++
++  struct audit_ifaces *afct = GLRO(dl_audit);
++  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++    {
++      struct auditstate *match_audit = link_map_audit_state (l, cnt);
++      struct auditstate *result_audit = link_map_audit_state (result, cnt);
++      if (afct->symbind != NULL
++	  && ((match_audit->bindflags & LA_FLG_BINDFROM) != 0
++	      || ((result_audit->bindflags & LA_FLG_BINDTO)
++		  != 0)))
++	{
++	  unsigned int flags = altvalue | LA_SYMB_DLSYM;
++	  uintptr_t new_value = afct->symbind (&sym, ndx,
++					       &match_audit->cookie,
++					       &result_audit->cookie,
++					       &flags, strtab + ref->st_name);
++	  if (new_value != (uintptr_t) sym.st_value)
++	    {
++	      altvalue = LA_SYMB_ALTVALUE;
++	      sym.st_value = new_value;
++	    }
++
++	  afct = afct->next;
++	}
++
++      *value = (void *) sym.st_value;
++    }
++}
++rtld_hidden_def (_dl_audit_symbind_alt)
++
++void
++_dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
++		   const ElfW(Sym) *defsym, DL_FIXUP_VALUE_TYPE *value,
++		   lookup_t result)
++{
++  reloc_result->bound = result;
++  /* Compute index of the symbol entry in the symbol table of the DSO with the
++     definition.  */
++  reloc_result->boundndx = (defsym - (ElfW(Sym) *) D_PTR (result,
++							  l_info[DT_SYMTAB]));
++
++  if ((l->l_audit_any_plt | result->l_audit_any_plt) == 0)
++    {
++      /* Set all bits since this symbol binding is not interesting.  */
++      reloc_result->enterexit = (1u << DL_NNS) - 1;
++      return;
++    }
++
++  /* Synthesize a symbol record where the st_value field is the result.  */
++  ElfW(Sym) sym = *defsym;
++  sym.st_value = DL_FIXUP_VALUE_ADDR (*value);
++
++  /* Keep track whether there is any interest in tracing the call in the lower
++     two bits.  */
++  assert (DL_NNS * 2 <= sizeof (reloc_result->flags) * 8);
++  assert ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) == 3);
++  reloc_result->enterexit = LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT;
++
++  const char *strtab2 = (const void *) D_PTR (result, l_info[DT_STRTAB]);
++
++  unsigned int flags = 0;
++  struct audit_ifaces *afct = GLRO(dl_audit);
++  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++    {
++      /* XXX Check whether both DSOs must request action or only one */
++      struct auditstate *l_state = link_map_audit_state (l, cnt);
++      struct auditstate *result_state = link_map_audit_state (result, cnt);
++      if ((l_state->bindflags & LA_FLG_BINDFROM) != 0
++	  && (result_state->bindflags & LA_FLG_BINDTO) != 0)
++	{
++	  if (afct->symbind != NULL)
++	    {
++	      uintptr_t new_value = afct->symbind (&sym,
++						   reloc_result->boundndx,
++						   &l_state->cookie,
++						   &result_state->cookie,
++						   &flags,
++						   strtab2 + defsym->st_name);
++	      if (new_value != (uintptr_t) sym.st_value)
++		{
++		  flags |= LA_SYMB_ALTVALUE;
++		  sym.st_value = new_value;
++		}
++	    }
++
++	  /* Remember the results for every audit library and store a summary
++	     in the first two bits.  */
++	  reloc_result->enterexit &= flags & (LA_SYMB_NOPLTENTER
++					      | LA_SYMB_NOPLTEXIT);
++	  reloc_result->enterexit |= ((flags & (LA_SYMB_NOPLTENTER
++						| LA_SYMB_NOPLTEXIT))
++				      << ((cnt + 1) * 2));
++	}
++      else
++	/* If the bind flags say this auditor is not interested, set the bits
++	   manually.  */
++	reloc_result->enterexit |= ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)
++				    << ((cnt + 1) * 2));
++      afct = afct->next;
++    }
++
++  reloc_result->flags = flags;
++  *value = DL_FIXUP_ADDR_VALUE (sym.st_value);
++}
+diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
+index 4ccd7c30678fafad..d4840a7c17441126 100644
+--- a/elf/dl-runtime.c
++++ b/elf/dl-runtime.c
+@@ -296,84 +296,7 @@ _dl_profile_fixup (
+ 	 auditing libraries the possibility to change the value and
+ 	 tell us whether further auditing is wanted.  */
+       if (defsym != NULL && GLRO(dl_naudit) > 0)
+-	{
+-	  reloc_result->bound = result;
+-	  /* Compute index of the symbol entry in the symbol table of
+-	     the DSO with the definition.  */
+-	  reloc_result->boundndx = (defsym
+-				    - (ElfW(Sym) *) D_PTR (result,
+-							   l_info[DT_SYMTAB]));
+-
+-	  /* Determine whether any of the two participating DSOs is
+-	     interested in auditing.  */
+-	  if ((l->l_audit_any_plt | result->l_audit_any_plt) != 0)
+-	    {
+-	      unsigned int flags = 0;
+-	      struct audit_ifaces *afct = GLRO(dl_audit);
+-	      /* Synthesize a symbol record where the st_value field is
+-		 the result.  */
+-	      ElfW(Sym) sym = *defsym;
+-	      sym.st_value = DL_FIXUP_VALUE_ADDR (value);
+-
+-	      /* Keep track whether there is any interest in tracing
+-		 the call in the lower two bits.  */
+-	      assert (DL_NNS * 2 <= sizeof (reloc_result->flags) * 8);
+-	      assert ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) == 3);
+-	      reloc_result->enterexit = LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT;
+-
+-	      const char *strtab2 = (const void *) D_PTR (result,
+-							  l_info[DT_STRTAB]);
+-
+-	      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-		{
+-		  /* XXX Check whether both DSOs must request action or
+-		     only one */
+-		  struct auditstate *l_state = link_map_audit_state (l, cnt);
+-		  struct auditstate *result_state
+-		    = link_map_audit_state (result, cnt);
+-		  if ((l_state->bindflags & LA_FLG_BINDFROM) != 0
+-		      && (result_state->bindflags & LA_FLG_BINDTO) != 0)
+-		    {
+-		      if (afct->symbind != NULL)
+-			{
+-			  uintptr_t new_value
+-			    = afct->symbind (&sym, reloc_result->boundndx,
+-					     &l_state->cookie,
+-					     &result_state->cookie,
+-					     &flags,
+-					     strtab2 + defsym->st_name);
+-			  if (new_value != (uintptr_t) sym.st_value)
+-			    {
+-			      flags |= LA_SYMB_ALTVALUE;
+-			      sym.st_value = new_value;
+-			    }
+-			}
+-
+-		      /* Remember the results for every audit library and
+-			 store a summary in the first two bits.  */
+-		      reloc_result->enterexit
+-			&= flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT);
+-		      reloc_result->enterexit
+-			|= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT))
+-			    << ((cnt + 1) * 2));
+-		    }
+-		  else
+-		    /* If the bind flags say this auditor is not interested,
+-		       set the bits manually.  */
+-		    reloc_result->enterexit
+-		      |= ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)
+-			  << ((cnt + 1) * 2));
+-
+-		  afct = afct->next;
+-		}
+-
+-	      reloc_result->flags = flags;
+-	      value = DL_FIXUP_ADDR_VALUE (sym.st_value);
+-	    }
+-	  else
+-	    /* Set all bits since this symbol binding is not interesting.  */
+-	    reloc_result->enterexit = (1u << DL_NNS) - 1;
+-	}
++	_dl_audit_symbind (l, reloc_result, defsym, &value, result);
+ #endif
+ 
+       /* Store the result for later runs.  */
+diff --git a/elf/dl-sym-post.h b/elf/dl-sym-post.h
+index 4c4f574633497789..f33934c92047f293 100644
+--- a/elf/dl-sym-post.h
++++ b/elf/dl-sym-post.h
+@@ -52,54 +52,9 @@ _dl_sym_post (lookup_t result, const ElfW(Sym) *ref, void *value,
+      tell us whether further auditing is wanted.  */
+   if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+     {
+-      const char *strtab = (const char *) D_PTR (result,
+-                                                 l_info[DT_STRTAB]);
+-      /* Compute index of the symbol entry in the symbol table of
+-         the DSO with the definition.  */
+-      unsigned int ndx = (ref - (ElfW(Sym) *) D_PTR (result,
+-                                                     l_info[DT_SYMTAB]));
+-
+       if (match == NULL)
+         match = _dl_sym_find_caller_link_map (caller);
+-
+-      if ((match->l_audit_any_plt | result->l_audit_any_plt) != 0)
+-        {
+-          unsigned int altvalue = 0;
+-          struct audit_ifaces *afct = GLRO(dl_audit);
+-          /* Synthesize a symbol record where the st_value field is
+-             the result.  */
+-          ElfW(Sym) sym = *ref;
+-          sym.st_value = (ElfW(Addr)) value;
+-
+-          for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-            {
+-              struct auditstate *match_audit
+-                = link_map_audit_state (match, cnt);
+-              struct auditstate *result_audit
+-                = link_map_audit_state (result, cnt);
+-              if (afct->symbind != NULL
+-                  && ((match_audit->bindflags & LA_FLG_BINDFROM) != 0
+-                      || ((result_audit->bindflags & LA_FLG_BINDTO)
+-                          != 0)))
+-                {
+-                  unsigned int flags = altvalue | LA_SYMB_DLSYM;
+-                  uintptr_t new_value
+-                    = afct->symbind (&sym, ndx,
+-                                     &match_audit->cookie,
+-                                     &result_audit->cookie,
+-                                     &flags, strtab + ref->st_name);
+-                  if (new_value != (uintptr_t) sym.st_value)
+-                    {
+-                      altvalue = LA_SYMB_ALTVALUE;
+-                      sym.st_value = new_value;
+-                    }
+-                }
+-
+-              afct = afct->next;
+-            }
+-
+-          value = (void *) sym.st_value;
+-        }
++      _dl_audit_symbind_alt (match, ref, &value, result);
+     }
+ #endif
+   return value;
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index 3db25c5be1acf871..fa55c3bde10de52e 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -1294,6 +1294,16 @@ void _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
+ /* Call the la_objclose from the audit modules for the link_map L.  */
+ void _dl_audit_objclose (struct link_map *l)
+   attribute_hidden;
++
++/* Call the la_symbind{32,64} from the audit modules for the link_map L.  */
++void _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
++			const ElfW(Sym) *defsym, DL_FIXUP_VALUE_TYPE *value,
++			lookup_t result)
++  attribute_hidden;
++/* Same as _dl_audit_symbind, but also sets LA_SYMB_DLSYM flag.  */
++void _dl_audit_symbind_alt (struct link_map *l, const ElfW(Sym) *ref,
++			    void **value, lookup_t result);
++rtld_hidden_proto (_dl_audit_symbind_alt)
+ #endif /* SHARED */
+ 
+ __END_DECLS
diff --git a/SOURCES/glibc-rh2047981-2.patch b/SOURCES/glibc-rh2047981-2.patch
new file mode 100644
index 0000000000000000000000000000000000000000..02bc4039e05252d53cf7c587dfe564a25ea82205
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-2.patch
@@ -0,0 +1,70 @@
+commit acdcca72940e060270e4e54d9c0457398110f409
+Author: John David Anglin <danglin@gcc.gnu.org>
+Date:   Mon Mar 30 21:58:06 2020 +0000
+
+    Add new file missed in previous hppa commit.
+
+diff --git a/sysdeps/hppa/dl-runtime.c b/sysdeps/hppa/dl-runtime.c
+new file mode 100644
+index 0000000000000000..885a3f1837cbc56d
+--- /dev/null
++++ b/sysdeps/hppa/dl-runtime.c
+@@ -0,0 +1,58 @@
++/* On-demand PLT fixup for shared objects.  HPPA version.
++   Copyright (C) 2019 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, write to the Free
++   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
++   02111-1307 USA.  */
++
++/* Clear PA_GP_RELOC bit in relocation offset.  */
++#define reloc_offset (reloc_arg & ~PA_GP_RELOC)
++#define reloc_index  (reloc_arg & ~PA_GP_RELOC) / sizeof (PLTREL)
++
++#include <elf/dl-runtime.c>
++
++/* The caller has encountered a partially relocated function descriptor.
++   The gp of the descriptor has been updated, but not the ip.  We find
++   the function descriptor again and compute the relocation offset and
++   return that to the caller.  The caller will continue on to call
++   _dl_fixup with the relocation offset.  */
++
++ElfW(Word)
++attribute_hidden __attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
++_dl_fix_reloc_arg (struct fdesc *fptr, struct link_map *l)
++{
++  Elf32_Addr l_addr, iplt, jmprel, end_jmprel, r_type;
++  const Elf32_Rela *reloc;
++
++  l_addr = l->l_addr;
++  jmprel = D_PTR(l, l_info[DT_JMPREL]);
++  end_jmprel = jmprel + l->l_info[DT_PLTRELSZ]->d_un.d_val;
++
++  /* Look for the entry...  */
++  for (iplt = jmprel; iplt < end_jmprel; iplt += sizeof (Elf32_Rela))
++    {
++      reloc = (const Elf32_Rela *) iplt;
++      r_type = ELF32_R_TYPE (reloc->r_info);
++
++      if (__builtin_expect (r_type == R_PARISC_IPLT, 1)
++	  && fptr == (struct fdesc *) (reloc->r_offset + l_addr))
++	/* Found entry. Return the reloc offset.  */
++	return iplt - jmprel;
++    }
++
++  /* Crash if we weren't passed a valid function pointer.  */
++  ABORT_INSTRUCTION;
++  return 0;
++}
diff --git a/SOURCES/glibc-rh2047981-20.patch b/SOURCES/glibc-rh2047981-20.patch
new file mode 100644
index 0000000000000000000000000000000000000000..bce1f3dd00c225d57bb9f3a7aecc44a44594c0e0
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-20.patch
@@ -0,0 +1,113 @@
+commit 0b98a8748759e88b58927882a8714109abe0a2d6
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Thu Jul 22 17:10:57 2021 -0300
+
+    elf: Add _dl_audit_preinit
+    
+    It consolidates the code required to call la_preinit audit
+    callback.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+Conflicts:
+	csu/libc-start.c
+		Rework to existing init call code.
+
+diff --git a/csu/libc-start.c b/csu/libc-start.c
+index fd0f8640eaeae34c..ae703cfa620163fd 100644
+--- a/csu/libc-start.c
++++ b/csu/libc-start.c
+@@ -265,32 +265,20 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
+ #ifdef SHARED
+   if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
+     GLRO(dl_debug_printf) ("\ninitialize program: %s\n\n", argv[0]);
+-#endif
++
+   if (init)
+     (*init) (argc, argv, __environ MAIN_AUXVEC_PARAM);
+ 
+-#ifdef SHARED
+   /* Auditing checkpoint: we have a new object.  */
+-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+-    {
+-      struct audit_ifaces *afct = GLRO(dl_audit);
+-      struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-	{
+-	  if (afct->preinit != NULL)
+-	    afct->preinit (&link_map_audit_state (head, cnt)->cookie);
+-
+-	  afct = afct->next;
+-	}
+-    }
+-#endif
++  _dl_audit_preinit (GL(dl_ns)[LM_ID_BASE]._ns_loaded);
+ 
+-#ifdef SHARED
+   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS))
+     GLRO(dl_debug_printf) ("\ntransferring control: %s\n\n", argv[0]);
+-#endif
+ 
+-#ifndef SHARED
++#else /* !SHARED */
++  if (init)
++    (*init) (argc, argv, __environ MAIN_AUXVEC_PARAM);
++
+   _dl_debug_initialize (0, LM_ID_BASE);
+ #endif
+ #ifdef HAVE_CLEANUP_JMP_BUF
+diff --git a/elf/Versions b/elf/Versions
+index c5d4342cf1f5124c..35ac181bdb099af8 100644
+--- a/elf/Versions
++++ b/elf/Versions
+@@ -59,7 +59,7 @@ ld {
+     _dl_argv; _dl_find_dso_for_object; _dl_get_tls_static_info;
+     _dl_deallocate_tls; _dl_make_stack_executable;
+     _dl_rtld_di_serinfo; _dl_starting_up; _dl_fatal_printf;
+-    _dl_audit_symbind_alt;
++    _dl_audit_symbind_alt; _dl_audit_preinit;
+     _rtld_global; _rtld_global_ro;
+ 
+     # Only here for gdb while a better method is developed.
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+index a21530f30bc5524b..0b6fac8e48877c93 100644
+--- a/elf/dl-audit.c
++++ b/elf/dl-audit.c
+@@ -108,6 +108,21 @@ _dl_audit_objclose (struct link_map *l)
+     }
+ }
+ 
++void
++_dl_audit_preinit (struct link_map *l)
++{
++  if (__glibc_likely (GLRO(dl_naudit) == 0))
++    return;
++
++  struct audit_ifaces *afct = GLRO(dl_audit);
++  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++    {
++      if (afct->preinit != NULL)
++	afct->preinit (&link_map_audit_state (l, cnt)->cookie);
++      afct = afct->next;
++    }
++}
++
+ void
+ _dl_audit_symbind_alt (struct link_map *l, const ElfW(Sym) *ref, void **value,
+ 		       lookup_t result)
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index fa55c3bde10de52e..03676b474c3d37a3 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -1295,6 +1295,9 @@ void _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
+ void _dl_audit_objclose (struct link_map *l)
+   attribute_hidden;
+ 
++/* Call the la_preinit from the audit modules for the link_map L.  */
++void _dl_audit_preinit (struct link_map *l);
++
+ /* Call the la_symbind{32,64} from the audit modules for the link_map L.  */
+ void _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
+ 			const ElfW(Sym) *defsym, DL_FIXUP_VALUE_TYPE *value,
diff --git a/SOURCES/glibc-rh2047981-21.patch b/SOURCES/glibc-rh2047981-21.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ea5003f29abe39ff227b6831792412dc1f24ad5a
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-21.patch
@@ -0,0 +1,205 @@
+commit eff687e8462b0eaf65992a6031b54a4b1cd16796
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Thu Jul 22 17:45:33 2021 -0300
+
+    elf: Add _dl_audit_pltenter
+    
+    It consolidates the code required to call la_pltenter audit
+    callback.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+index 0b6fac8e48877c93..15250c67e8ac1658 100644
+--- a/elf/dl-audit.c
++++ b/elf/dl-audit.c
+@@ -17,7 +17,9 @@
+    <https://www.gnu.org/licenses/>.  */
+ 
+ #include <assert.h>
++#include <link.h>
+ #include <ldsodefs.h>
++#include <dl-machine.h>
+ 
+ void
+ _dl_audit_activity_map (struct link_map *l, int action)
+@@ -243,3 +245,78 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
+   reloc_result->flags = flags;
+   *value = DL_FIXUP_ADDR_VALUE (sym.st_value);
+ }
++
++void
++_dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
++		    DL_FIXUP_VALUE_TYPE *value, void *regs, long int *framesize)
++{
++  /* Don't do anything if no auditor wants to intercept this call.  */
++  if (GLRO(dl_naudit) == 0
++      || (reloc_result->enterexit & LA_SYMB_NOPLTENTER))
++    return;
++
++  /* Sanity check:  DL_FIXUP_VALUE_CODE_ADDR (value) should have been
++     initialized earlier in this function or in another thread.  */
++  assert (DL_FIXUP_VALUE_CODE_ADDR (*value) != 0);
++  ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
++					    l_info[DT_SYMTAB])
++		       + reloc_result->boundndx);
++
++  /* Set up the sym parameter.  */
++  ElfW(Sym) sym = *defsym;
++  sym.st_value = DL_FIXUP_VALUE_ADDR (*value);
++
++  /* Get the symbol name.  */
++  const char *strtab = (const void *) D_PTR (reloc_result->bound,
++					     l_info[DT_STRTAB]);
++  const char *symname = strtab + sym.st_name;
++
++  /* Keep track of overwritten addresses.  */
++  unsigned int flags = reloc_result->flags;
++
++  struct audit_ifaces *afct = GLRO(dl_audit);
++  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++    {
++      if (afct->ARCH_LA_PLTENTER != NULL
++	  && (reloc_result->enterexit
++	      & (LA_SYMB_NOPLTENTER << (2 * (cnt + 1)))) == 0)
++	{
++	  long int new_framesize = -1;
++	  struct auditstate *l_state = link_map_audit_state (l, cnt);
++	  struct auditstate *bound_state
++	    = link_map_audit_state (reloc_result->bound, cnt);
++	  uintptr_t new_value
++	    = afct->ARCH_LA_PLTENTER (&sym, reloc_result->boundndx,
++				      &l_state->cookie, &bound_state->cookie,
++				      regs, &flags, symname, &new_framesize);
++	  if (new_value != (uintptr_t) sym.st_value)
++	    {
++	      flags |= LA_SYMB_ALTVALUE;
++	      sym.st_value = new_value;
++	    }
++
++	  /* Remember the results for every audit library and store a summary
++	     in the first two bits.  */
++	  reloc_result->enterexit |= ((flags & (LA_SYMB_NOPLTENTER
++						| LA_SYMB_NOPLTEXIT))
++				      << (2 * (cnt + 1)));
++
++	  if ((reloc_result->enterexit & (LA_SYMB_NOPLTEXIT
++					  << (2 * (cnt + 1))))
++	      == 0 && new_framesize != -1 && *framesize != -2)
++	    {
++	      /* If this is the first call providing information, use it.  */
++	      if (*framesize == -1)
++		*framesize = new_framesize;
++	      /* If two pltenter calls provide conflicting information, use
++		 the larger value.  */
++	      else if (new_framesize != *framesize)
++		*framesize = MAX (new_framesize, *framesize);
++	    }
++	}
++
++      afct = afct->next;
++    }
++
++  *value = DL_FIXUP_ADDR_VALUE (sym.st_value);
++}
+diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
+index d4840a7c17441126..b46f7d7376e65361 100644
+--- a/elf/dl-runtime.c
++++ b/elf/dl-runtime.c
+@@ -319,78 +319,7 @@ _dl_profile_fixup (
+ #ifdef SHARED
+   /* Auditing checkpoint: report the PLT entering and allow the
+      auditors to change the value.  */
+-  if (GLRO(dl_naudit) > 0
+-      /* Don't do anything if no auditor wants to intercept this call.  */
+-      && (reloc_result->enterexit & LA_SYMB_NOPLTENTER) == 0)
+-    {
+-      /* Sanity check:  DL_FIXUP_VALUE_CODE_ADDR (value) should have been
+-	 initialized earlier in this function or in another thread.  */
+-      assert (DL_FIXUP_VALUE_CODE_ADDR (value) != 0);
+-      ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
+-						l_info[DT_SYMTAB])
+-			   + reloc_result->boundndx);
+-
+-      /* Set up the sym parameter.  */
+-      ElfW(Sym) sym = *defsym;
+-      sym.st_value = DL_FIXUP_VALUE_ADDR (value);
+-
+-      /* Get the symbol name.  */
+-      const char *strtab = (const void *) D_PTR (reloc_result->bound,
+-						 l_info[DT_STRTAB]);
+-      const char *symname = strtab + sym.st_name;
+-
+-      /* Keep track of overwritten addresses.  */
+-      unsigned int flags = reloc_result->flags;
+-
+-      struct audit_ifaces *afct = GLRO(dl_audit);
+-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-	{
+-	  if (afct->ARCH_LA_PLTENTER != NULL
+-	      && (reloc_result->enterexit
+-		  & (LA_SYMB_NOPLTENTER << (2 * (cnt + 1)))) == 0)
+-	    {
+-	      long int new_framesize = -1;
+-	      struct auditstate *l_state = link_map_audit_state (l, cnt);
+-	      struct auditstate *bound_state
+-		= link_map_audit_state (reloc_result->bound, cnt);
+-	      uintptr_t new_value
+-		= afct->ARCH_LA_PLTENTER (&sym, reloc_result->boundndx,
+-					  &l_state->cookie,
+-					  &bound_state->cookie,
+-					  regs, &flags, symname,
+-					  &new_framesize);
+-	      if (new_value != (uintptr_t) sym.st_value)
+-		{
+-		  flags |= LA_SYMB_ALTVALUE;
+-		  sym.st_value = new_value;
+-		}
+-
+-	      /* Remember the results for every audit library and
+-		 store a summary in the first two bits.  */
+-	      reloc_result->enterexit
+-		|= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT))
+-		    << (2 * (cnt + 1)));
+-
+-	      if ((reloc_result->enterexit & (LA_SYMB_NOPLTEXIT
+-					      << (2 * (cnt + 1))))
+-		  == 0 && new_framesize != -1 && framesize != -2)
+-		{
+-		  /* If this is the first call providing information,
+-		     use it.  */
+-		  if (framesize == -1)
+-		    framesize = new_framesize;
+-		  /* If two pltenter calls provide conflicting information,
+-		     use the larger value.  */
+-		  else if (new_framesize != framesize)
+-		    framesize = MAX (new_framesize, framesize);
+-		}
+-	    }
+-
+-	  afct = afct->next;
+-	}
+-
+-      value = DL_FIXUP_ADDR_VALUE (sym.st_value);
+-    }
++  _dl_audit_pltenter (l, reloc_result, &value, regs, &framesize);
+ #endif
+ 
+   /* Store the frame size information.  */
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index 03676b474c3d37a3..47a9dee5b1c0ca63 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -1307,6 +1307,10 @@ void _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
+ void _dl_audit_symbind_alt (struct link_map *l, const ElfW(Sym) *ref,
+ 			    void **value, lookup_t result);
+ rtld_hidden_proto (_dl_audit_symbind_alt)
++void _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
++			 DL_FIXUP_VALUE_TYPE *value, void *regs,
++			 long int *framesize)
++  attribute_hidden;
+ #endif /* SHARED */
+ 
+ __END_DECLS
diff --git a/SOURCES/glibc-rh2047981-22.patch b/SOURCES/glibc-rh2047981-22.patch
new file mode 100644
index 0000000000000000000000000000000000000000..17c35d5c96293e5d1e6166177e5fe522f9fe745d
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-22.patch
@@ -0,0 +1,795 @@
+commit 8c0664e2b861fd3789602cc0b0b1922b0e20cb3a
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Thu Jul 22 18:02:42 2021 -0300
+
+    elf: Add _dl_audit_pltexit
+    
+    It consolidates the code required to call la_pltexit audit
+    callback.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+Conflicts:
+	nptl/tst-atfork4mod.c
+	sysdeps/powerpc/fpu/s_fmaf.S
+	sysdeps/powerpc/powerpc32/power4/multiarch/wcscpy-ppc32.c
+	sysdeps/powerpc/powerpc64/power5+/fpu/s_floor.S
+		Without d6d89608ac8cf2b37c75debad1fff653f6939f90 we
+		don't have dl-machine-rel.h so git picks a match for
+		all four files above, instead we modify dl-machine.h
+		for the targets:
+			sysdeps/i386/dl-machine.h
+			sysdeps/arm/dl-machine.h
+			sysdeps/mips/dl-machine.h
+		The fourth is the generic file and without it we
+		add the PLTREL macro to each target:
+			sysdeps/aarch64/dl-machine.h
+			sysdeps/powerpc/powerpc32/dl-machine.h
+			sysdeps/powerpc/powerpc64/dl-machine.h
+			sysdeps/s390/s390-32/dl-machine.h
+			sysdeps/s390/s390-64/dl-machine.h
+			sysdeps/x86_64/dl-machine.h
+	sysdeps/s390/s390-32/dl-trampoline.h
+	sysdeps/s390/s390-64/dl-trampoline.h
+
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+index 15250c67e8ac1658..152712b12fed6de2 100644
+--- a/elf/dl-audit.c
++++ b/elf/dl-audit.c
+@@ -20,6 +20,8 @@
+ #include <link.h>
+ #include <ldsodefs.h>
+ #include <dl-machine.h>
++#include <dl-runtime.h>
++#include <dl-fixup-attribute.h>
+ 
+ void
+ _dl_audit_activity_map (struct link_map *l, int action)
+@@ -320,3 +322,48 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
+ 
+   *value = DL_FIXUP_ADDR_VALUE (sym.st_value);
+ }
++
++void
++DL_ARCH_FIXUP_ATTRIBUTE
++_dl_audit_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
++		   const void *inregs, void *outregs)
++{
++  const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);
++
++  /* This is the address in the array where we store the result of previous
++     relocations.  */
++  // XXX Maybe the bound information must be stored on the stack since
++  // XXX with bind_not a new value could have been stored in the meantime.
++  struct reloc_result *reloc_result =
++    &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))];
++  ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
++					    l_info[DT_SYMTAB])
++		       + reloc_result->boundndx);
++
++  /* Set up the sym parameter.  */
++  ElfW(Sym) sym = *defsym;
++  sym.st_value = DL_FIXUP_VALUE_ADDR (reloc_result->addr);
++
++  /* Get the symbol name.  */
++  const char *strtab = (const void *) D_PTR (reloc_result->bound,
++					     l_info[DT_STRTAB]);
++  const char *symname = strtab + sym.st_name;
++
++  struct audit_ifaces *afct = GLRO(dl_audit);
++  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++    {
++      if (afct->ARCH_LA_PLTEXIT != NULL
++	  && (reloc_result->enterexit
++	      & (LA_SYMB_NOPLTEXIT >> (2 * cnt))) == 0)
++	{
++	  struct auditstate *l_state = link_map_audit_state (l, cnt);
++	  struct auditstate *bound_state
++	    = link_map_audit_state (reloc_result->bound, cnt);
++	  afct->ARCH_LA_PLTEXIT (&sym, reloc_result->boundndx,
++				 &l_state->cookie, &bound_state->cookie,
++				 inregs, outregs, symname);
++	}
++
++      afct = afct->next;
++    }
++}
+diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
+index b46f7d7376e65361..ec0b2164825fa538 100644
+--- a/elf/dl-runtime.c
++++ b/elf/dl-runtime.c
+@@ -16,8 +16,6 @@
+    License along with the GNU C Library; if not, see
+    <http://www.gnu.org/licenses/>.  */
+ 
+-#define IN_DL_RUNTIME 1		/* This can be tested in dl-machine.h.  */
+-
+ #include <alloca.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+@@ -30,19 +28,6 @@
+ #include <dl-runtime.h>
+ 
+ 
+-#if (!ELF_MACHINE_NO_RELA && !defined ELF_MACHINE_PLT_REL) \
+-    || ELF_MACHINE_NO_REL
+-# define PLTREL  ElfW(Rela)
+-#else
+-# define PLTREL  ElfW(Rel)
+-#endif
+-
+-/* The fixup functions might have need special attributes.  If none
+-   are provided define the macro as empty.  */
+-#ifndef ARCH_FIXUP_ATTRIBUTE
+-# define ARCH_FIXUP_ATTRIBUTE
+-#endif
+-
+ /* This function is called through a special trampoline from the PLT the
+    first time each PLT entry is called.  We must perform the relocation
+    specified in the PLT of the given shared object, and return the resolved
+@@ -51,7 +36,7 @@
+    function.  */
+ 
+ DL_FIXUP_VALUE_TYPE
+-attribute_hidden __attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
++attribute_hidden __attribute ((noinline)) DL_ARCH_FIXUP_ATTRIBUTE
+ _dl_fixup (
+ # ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
+ 	   ELF_MACHINE_RUNTIME_FIXUP_ARGS,
+@@ -147,7 +132,8 @@ _dl_fixup (
+ 
+ #ifndef PROF
+ DL_FIXUP_VALUE_TYPE
+-__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
++__attribute ((noinline))
++DL_ARCH_FIXUP_ATTRIBUTE
+ _dl_profile_fixup (
+ #ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
+ 		   ELF_MACHINE_RUNTIME_FIXUP_ARGS,
+@@ -331,52 +317,3 @@ _dl_profile_fixup (
+ }
+ 
+ #endif /* PROF */
+-
+-
+-#include <stdio.h>
+-void
+-ARCH_FIXUP_ATTRIBUTE
+-_dl_call_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
+-		  const void *inregs, void *outregs)
+-{
+-#ifdef SHARED
+-  const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);
+-
+-  /* This is the address in the array where we store the result of previous
+-     relocations.  */
+-  // XXX Maybe the bound information must be stored on the stack since
+-  // XXX with bind_not a new value could have been stored in the meantime.
+-  struct reloc_result *reloc_result =
+-    &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))];
+-  ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
+-					    l_info[DT_SYMTAB])
+-		       + reloc_result->boundndx);
+-
+-  /* Set up the sym parameter.  */
+-  ElfW(Sym) sym = *defsym;
+-  sym.st_value = DL_FIXUP_VALUE_ADDR (reloc_result->addr);
+-
+-  /* Get the symbol name.  */
+-  const char *strtab = (const void *) D_PTR (reloc_result->bound,
+-					     l_info[DT_STRTAB]);
+-  const char *symname = strtab + sym.st_name;
+-
+-  struct audit_ifaces *afct = GLRO(dl_audit);
+-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+-    {
+-      if (afct->ARCH_LA_PLTEXIT != NULL
+-	  && (reloc_result->enterexit
+-	      & (LA_SYMB_NOPLTEXIT >> (2 * cnt))) == 0)
+-	{
+-	  struct auditstate *l_state = link_map_audit_state (l, cnt);
+-	  struct auditstate *bound_state
+-	    = link_map_audit_state (reloc_result->bound, cnt);
+-	  afct->ARCH_LA_PLTEXIT (&sym, reloc_result->boundndx,
+-				 &l_state->cookie, &bound_state->cookie,
+-				 inregs, outregs, symname);
+-	}
+-
+-      afct = afct->next;
+-    }
+-#endif
+-}
+diff --git a/elf/dl-support.c b/elf/dl-support.c
+index 3e5531138eaa18f8..e9943e889ef447ad 100644
+--- a/elf/dl-support.c
++++ b/elf/dl-support.c
+@@ -399,3 +399,11 @@ _dl_get_dl_main_map (void)
+   return &_dl_main_map;
+ }
+ #endif
++
++/* This is used by _dl_runtime_profile, not used on static code.  */
++void
++DL_ARCH_FIXUP_ATTRIBUTE
++_dl_audit_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
++		   const void *inregs, void *outregs)
++{
++}
+diff --git a/sysdeps/aarch64/dl-machine.h b/sysdeps/aarch64/dl-machine.h
+index 5eab544afe2717f7..c13d896a57811c7d 100644
+--- a/sysdeps/aarch64/dl-machine.h
++++ b/sysdeps/aarch64/dl-machine.h
+@@ -196,6 +196,7 @@ _dl_start_user:								\n\
+ /* AArch64 uses RELA not REL */
+ #define ELF_MACHINE_NO_REL 1
+ #define ELF_MACHINE_NO_RELA 0
++#define PLTREL ElfW(Rela)
+ 
+ #define DL_PLATFORM_INIT dl_platform_init ()
+ 
+diff --git a/sysdeps/aarch64/dl-trampoline.S b/sysdeps/aarch64/dl-trampoline.S
+index a86d0722d4a0415b..18740398e63fdf97 100644
+--- a/sysdeps/aarch64/dl-trampoline.S
++++ b/sysdeps/aarch64/dl-trampoline.S
+@@ -277,7 +277,7 @@ _dl_runtime_profile:
+ 	ldp	x0, x1, [x29, #OFFSET_SAVED_CALL_X0]
+ 	add	x2, x29, #OFFSET_RG
+ 	add	x3, x29, #OFFSET_RV
+-	bl	_dl_call_pltexit
++	bl	_dl_audit_pltexit
+ 
+ 	ldp	x0, x1, [x29, #OFFSET_RV + DL_OFFSET_RV_X0]
+ 	ldp	d0, d1, [x29, #OFFSET_RV + DL_OFFSET_RV_D0 + 16*0]
+diff --git a/sysdeps/alpha/dl-trampoline.S b/sysdeps/alpha/dl-trampoline.S
+index b326b37acedb5eaa..3acf5dec8d9585da 100644
+--- a/sysdeps/alpha/dl-trampoline.S
++++ b/sysdeps/alpha/dl-trampoline.S
+@@ -187,7 +187,7 @@ _dl_runtime_profile_new:
+ 	jsr	$26, ($27), 0
+ 	ldgp	$29, 0($26)
+ 
+-	/* Set up for call to _dl_call_pltexit.  */
++	/* Set up for call to _dl_audit_pltexit.  */
+ 	ldq	$16, 16*8($15)
+ 	ldq	$17, 17*8($15)
+ 	stq	$0, 16*8($15)
+@@ -196,7 +196,7 @@ _dl_runtime_profile_new:
+ 	lda	$19, 16*8($15)
+ 	stt	$f0, 18*8($15)
+ 	stt	$f1, 19*8($15)
+-	bsr	$26, _dl_call_pltexit	!samegp
++	bsr	$26, _dl_audit_pltexit	!samegp
+ 
+ 	mov	$15, $30
+ 	cfi_def_cfa_register (30)
+@@ -518,7 +518,7 @@ _dl_runtime_profile_old:
+ 	jsr	$26, ($27), 0
+ 	ldgp	$29, 0($26)
+ 
+-	/* Set up for call to _dl_call_pltexit.  */
++	/* Set up for call to _dl_audit_pltexit.  */
+ 	ldq	$16, 48*8($15)
+ 	ldq	$17, 49*8($15)
+ 	stq	$0, 46*8($15)
+@@ -527,7 +527,7 @@ _dl_runtime_profile_old:
+ 	lda	$19, 46*8($15)
+ 	stt	$f0, 48*8($15)
+ 	stt	$f1, 49*8($15)
+-	bsr	$26, _dl_call_pltexit	!samegp
++	bsr	$26, _dl_audit_pltexit	!samegp
+ 
+ 	mov	$15, $30
+ 	cfi_def_cfa_register (30)
+diff --git a/sysdeps/arm/dl-machine.h b/sysdeps/arm/dl-machine.h
+index 1a4fd3f17b6df7da..9b5d0567df984c5d 100644
+--- a/sysdeps/arm/dl-machine.h
++++ b/sysdeps/arm/dl-machine.h
+@@ -260,6 +260,8 @@ _dl_start_user:\n\
+    Prelinked libraries may use Elf32_Rela though.  */
+ #define ELF_MACHINE_PLT_REL 1
+ 
++#define PLTREL ElfW(Rel)
++
+ /* We define an initialization functions.  This is called very early in
+    _dl_sysdep_start.  */
+ #define DL_PLATFORM_INIT dl_platform_init ()
+diff --git a/sysdeps/arm/dl-trampoline.S b/sysdeps/arm/dl-trampoline.S
+index c731b012869a9cbc..ced1b1cb1017d677 100644
+--- a/sysdeps/arm/dl-trampoline.S
++++ b/sysdeps/arm/dl-trampoline.S
+@@ -194,7 +194,7 @@ _dl_runtime_profile:
+ 	ldmia	ip, {r0,r1}
+ 	add	r2, r7, #72
+ 	add	r3, r7, #0
+-	bl	_dl_call_pltexit
++	bl	_dl_audit_pltexit
+ 
+ 	@ Return to caller.
+ 	ldmia	r7, {r0-r3}
+diff --git a/sysdeps/generic/dl-fixup-attribute.h b/sysdeps/generic/dl-fixup-attribute.h
+new file mode 100644
+index 0000000000000000..aa92169b709b3fea
+--- /dev/null
++++ b/sysdeps/generic/dl-fixup-attribute.h
+@@ -0,0 +1,24 @@
++/* ABI specifics for lazy resolution functions.
++   Copyright (C) 2021 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/>.  */
++
++#ifndef _DL_FIXUP_ATTRIBUTE_H
++#define _DL_FIXUP_ATTRIBUTE_H
++
++#define DL_ARCH_FIXUP_ATTRIBUTE
++
++#endif
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index 47a9dee5b1c0ca63..29b77b35175c1116 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -35,6 +35,7 @@
+ #include <link.h>
+ #include <dl-lookupcfg.h>
+ #include <dl-sysdep.h>
++#include <dl-fixup-attribute.h>
+ #include <libc-lock.h>
+ #include <hp-timing.h>
+ #include <tls.h>
+@@ -1311,6 +1312,11 @@ void _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
+ 			 DL_FIXUP_VALUE_TYPE *value, void *regs,
+ 			 long int *framesize)
+   attribute_hidden;
++void DL_ARCH_FIXUP_ATTRIBUTE _dl_audit_pltexit (struct link_map *l,
++						ElfW(Word) reloc_arg,
++						const void *inregs,
++						void *outregs)
++  attribute_hidden;
+ #endif /* SHARED */
+ 
+ __END_DECLS
+diff --git a/sysdeps/hppa/dl-runtime.c b/sysdeps/hppa/dl-runtime.c
+index 2d061b150f0602c1..4c323131f937094b 100644
+--- a/sysdeps/hppa/dl-runtime.c
++++ b/sysdeps/hppa/dl-runtime.c
+@@ -26,7 +26,7 @@
+    _dl_fixup with the relocation offset.  */
+ 
+ ElfW(Word)
+-attribute_hidden __attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
++attribute_hidden __attribute ((noinline)) DL_ARCH_FIXUP_ATTRIBUTE
+ _dl_fix_reloc_arg (struct fdesc *fptr, struct link_map *l)
+ {
+   Elf32_Addr l_addr, iplt, jmprel, end_jmprel, r_type;
+diff --git a/sysdeps/hppa/dl-trampoline.S b/sysdeps/hppa/dl-trampoline.S
+index 7ee4331cc2e7deff..3c83c8542f4fc63f 100644
+--- a/sysdeps/hppa/dl-trampoline.S
++++ b/sysdeps/hppa/dl-trampoline.S
+@@ -275,7 +275,7 @@ L(cont):
+ 	ldw	-4(%sp),%r1
+ 	copy	%r1, %sp
+ 
+-	/* Arguments to _dl_call_pltexit */
++	/* Arguments to _dl_audit_pltexit */
+ 	ldw	-116(%sp), %r26		/* (1) got[1] == struct link_map */
+ 	ldw	-120(%sp), %r25		/* (2) reloc offsets */
+ 	ldo	-56(%sp), %r24		/* (3) *La_hppa_regs */
+@@ -287,8 +287,8 @@ L(cont):
+ 	ldo	-128(%sp), %r1
+ 	fstd	%fr4,0(%r1)
+ 
+-	/* Call _dl_call_pltexit */
+-	bl	_dl_call_pltexit,%rp
++	/* Call _dl_audit_pltexit */
++	bl	_dl_audit_pltexit,%rp
+ 	nop
+ 
+ 	/* Restore *La_hppa_retval */
+diff --git a/sysdeps/i386/dl-fixup-attribute.h b/sysdeps/i386/dl-fixup-attribute.h
+new file mode 100644
+index 0000000000000000..c10e9936f4db7254
+--- /dev/null
++++ b/sysdeps/i386/dl-fixup-attribute.h
+@@ -0,0 +1,30 @@
++/* ABI specifics for lazy resolution functions.  i386 version.
++   Copyright (C) 2021 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/>.  */
++
++#ifndef _DL_FIXUP_ATTRIBUTE_H
++#define _DL_FIXUP_ATTRIBUTE_H
++
++/* We cannot use this scheme for profiling because the _mcount call destroys
++   the passed register information.  */
++#ifndef PROF
++# define DL_ARCH_FIXUP_ATTRIBUTE __attribute__ ((regparm (3), stdcall, unused))
++#else
++# define DL_ARCH_FIXUP_ATTRIBUTE
++#endif
++
++#endif
+diff --git a/sysdeps/i386/dl-machine.h b/sysdeps/i386/dl-machine.h
+index 5ba95b9e4af49942..30c3464fc4ac19d8 100644
+--- a/sysdeps/i386/dl-machine.h
++++ b/sysdeps/i386/dl-machine.h
+@@ -119,29 +119,6 @@ elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
+   return lazy;
+ }
+ 
+-#ifdef IN_DL_RUNTIME
+-
+-# ifndef PROF
+-/* We add a declaration of this function here so that in dl-runtime.c
+-   the ELF_MACHINE_RUNTIME_TRAMPOLINE macro really can pass the parameters
+-   in registers.
+-
+-   We cannot use this scheme for profiling because the _mcount call
+-   destroys the passed register information.  */
+-#define ARCH_FIXUP_ATTRIBUTE __attribute__ ((regparm (3), stdcall, unused))
+-
+-extern ElfW(Addr) _dl_fixup (struct link_map *l,
+-			     ElfW(Word) reloc_offset)
+-     ARCH_FIXUP_ATTRIBUTE;
+-extern ElfW(Addr) _dl_profile_fixup (struct link_map *l,
+-				     ElfW(Word) reloc_offset,
+-				     ElfW(Addr) retaddr, void *regs,
+-				     long int *framesizep)
+-     ARCH_FIXUP_ATTRIBUTE;
+-# endif
+-
+-#endif
+-
+ /* Mask identifying addresses reserved for the user program,
+    where the dynamic linker should not map anything.  */
+ #define ELF_MACHINE_USER_ADDRESS_MASK	0xf8000000UL
+@@ -240,6 +217,8 @@ _dl_start_user:\n\
+    Prelinked libraries may use Elf32_Rela though.  */
+ #define ELF_MACHINE_PLT_REL 1
+ 
++#define PLTREL ElfW(Rel)
++
+ /* We define an initialization functions.  This is called very early in
+    _dl_sysdep_start.  */
+ #define DL_PLATFORM_INIT dl_platform_init ()
+diff --git a/sysdeps/i386/dl-trampoline.S b/sysdeps/i386/dl-trampoline.S
+index 6dc03192168ae2f3..a738b291a79bf8c2 100644
+--- a/sysdeps/i386/dl-trampoline.S
++++ b/sysdeps/i386/dl-trampoline.S
+@@ -265,7 +265,7 @@ _dl_runtime_profile:
+ 	movl (LRV_SIZE + 4 + LR_SIZE)(%esp), %eax
+ 	# PLT1
+ 	movl (LRV_SIZE + 4 + LR_SIZE + 4)(%esp), %edx
+-	call _dl_call_pltexit
++	call _dl_audit_pltexit
+ 	movl LRV_EAX_OFFSET(%esp), %eax
+ 	movl LRV_EDX_OFFSET(%esp), %edx
+ 	fldt LRV_ST1_OFFSET(%esp)
+diff --git a/sysdeps/ia64/dl-trampoline.S b/sysdeps/ia64/dl-trampoline.S
+index fc24c425bfe6907b..caeca3afcd7db6b6 100644
+--- a/sysdeps/ia64/dl-trampoline.S
++++ b/sysdeps/ia64/dl-trampoline.S
+@@ -133,7 +133,7 @@ END(_dl_runtime_resolve)
+ 
+ 
+ /* The fourth argument to _dl_profile_fixup and the third one to
+-   _dl_call_pltexit are a pointer to La_ia64_regs:
++   _dl_audit_pltexit are a pointer to La_ia64_regs:
+ 
+    8byte r8
+    8byte r9
+@@ -159,7 +159,7 @@ END(_dl_runtime_resolve)
+    8byte sp
+ 
+    The fifth argument to _dl_profile_fixup is a pointer to long int.
+-   The fourth argument to _dl_call_pltexit is a pointer to
++   The fourth argument to _dl_audit_pltexit is a pointer to
+    La_ia64_retval:
+ 
+    8byte r8
+@@ -261,7 +261,7 @@ ENTRY(_dl_runtime_profile)
+ 	}
+ 	{ .mii
+ 	  mov r18 = ar.unat	/* save it in La_ia64_regs */
+-	  mov loc7 = out3	/* save it for _dl_call_pltexit */
++	  mov loc7 = out3	/* save it for _dl_audit_pltexit */
+ 	  mov loc5 = r11	/* preserve language specific register */
+ 	}
+ 	{ .mmi
+@@ -272,7 +272,7 @@ ENTRY(_dl_runtime_profile)
+ 	}
+ 	{ .mii
+ 	  mov ar.unat = r17	/* restore it for function call */
+-	  mov loc8 = r16	/* save it for _dl_call_pltexit */
++	  mov loc8 = r16	/* save it for _dl_audit_pltexit */
+ 	  nop.i 0x0
+ 	}
+ 	{ .mmi
+@@ -291,7 +291,7 @@ ENTRY(_dl_runtime_profile)
+ 	{ .mmi
+ 	  stf.spill [r2] = f14, 32
+ 	  stf.spill [r3] = f15, 24
+-	  mov loc9 = out1	/* save it for _dl_call_pltexit */
++	  mov loc9 = out1	/* save it for _dl_audit_pltexit */
+ 	  ;;
+ 	}
+ 	{ .mmb
+@@ -426,7 +426,7 @@ ENTRY(_dl_runtime_profile)
+ 	  br.call.sptk.many b0 = b6
+ 	}
+ 	{ .mii
+-	  /* Prepare stack for _dl_call_pltexit. Loc10 has the original
++	  /* Prepare stack for _dl_audit_pltexit. Loc10 has the original
+ 	     stack pointer.  */
+ 	  adds r12 = -PLTEXIT_FRAME_SIZE, loc10
+ 	  adds r2 = -(PLTEXIT_FRAME_SIZE - 16), loc10
+@@ -461,14 +461,14 @@ ENTRY(_dl_runtime_profile)
+ 	{ .mmi
+ 	  stf.spill [r2] = f12, 32
+ 	  stf.spill [r3] = f13, 32
+-	  /* We need to restore gp for _dl_call_pltexit. */
++	  /* We need to restore gp for _dl_audit_pltexit. */
+ 	  mov gp = loc11
+ 	  ;;
+ 	}
+ 	{ .mmb
+ 	  stf.spill [r2] = f14
+ 	  stf.spill [r3] = f15
+-	  br.call.sptk.many b0 = _dl_call_pltexit
++	  br.call.sptk.many b0 = _dl_audit_pltexit
+ 	}
+ 	{ .mmi
+ 	  /* Load all the non-floating and floating return values. Skip
+diff --git a/sysdeps/m68k/dl-trampoline.S b/sysdeps/m68k/dl-trampoline.S
+index 7e1eace26b4a519d..27282ca8a6b1dada 100644
+--- a/sysdeps/m68k/dl-trampoline.S
++++ b/sysdeps/m68k/dl-trampoline.S
+@@ -202,7 +202,7 @@ _dl_runtime_profile:
+ 	cfi_adjust_cfa_offset (4)
+ 	move.l (32+FPSPACE)(%sp), -(%sp)
+ 	cfi_adjust_cfa_offset (4)
+-	jbsr _dl_call_pltexit
++	jbsr _dl_audit_pltexit
+ 	lea 16(%sp), %sp
+ 	cfi_adjust_cfa_offset (-16)
+ 	move.l (%sp)+, %d0
+diff --git a/sysdeps/mips/dl-machine.h b/sysdeps/mips/dl-machine.h
+index b41e10647d81843b..d4bd8b62f4b036a3 100644
+--- a/sysdeps/mips/dl-machine.h
++++ b/sysdeps/mips/dl-machine.h
+@@ -63,6 +63,7 @@
+ #define ELF_MACHINE_PLT_REL 1
+ #define ELF_MACHINE_NO_REL 0
+ #define ELF_MACHINE_NO_RELA 0
++#define PLTREL ElfW(Rel)
+ 
+ /* Translate a processor specific dynamic tag to the index
+    in l_info array.  */
+diff --git a/sysdeps/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h
+index 31c7f3f95a2ce1b2..84322595793dc8bb 100644
+--- a/sysdeps/powerpc/powerpc32/dl-machine.h
++++ b/sysdeps/powerpc/powerpc32/dl-machine.h
+@@ -150,6 +150,7 @@ __elf_preferred_address(struct link_map *loader, size_t maplength,
+ /* The PowerPC never uses REL relocations.  */
+ #define ELF_MACHINE_NO_REL 1
+ #define ELF_MACHINE_NO_RELA 0
++#define PLTREL ElfW(Rela)
+ 
+ /* 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 35996bb9173da231..3af1f708378f9a3c 100644
+--- a/sysdeps/powerpc/powerpc64/dl-machine.h
++++ b/sysdeps/powerpc/powerpc64/dl-machine.h
+@@ -297,6 +297,7 @@ BODY_PREFIX "_dl_start_user:\n"						\
+ /* The PowerPC never uses REL relocations.  */
+ #define ELF_MACHINE_NO_REL 1
+ #define ELF_MACHINE_NO_RELA 0
++#define PLTREL ElfW(Rela)
+ 
+ /* 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-trampoline.S b/sysdeps/powerpc/powerpc64/dl-trampoline.S
+index aa141dc44b980d9b..23290d32360507fd 100644
+--- a/sysdeps/powerpc/powerpc64/dl-trampoline.S
++++ b/sysdeps/powerpc/powerpc64/dl-trampoline.S
+@@ -197,7 +197,7 @@ END(_dl_runtime_resolve)
+ #ifndef PROF
+ ENTRY (_dl_profile_resolve, 4)
+ /* Spill r30, r31 to preserve the link_map* and reloc_addr, in case we
+-   need to call _dl_call_pltexit.  */
++   need to call _dl_audit_pltexit.  */
+ 	std	r31,-8(r1)
+ 	std	r30,-16(r1)
+ /* We need to save the registers used to pass parameters, ie. r3 thru
+@@ -452,7 +452,7 @@ L(restoreFXR2):
+ L(callpltexit):
+ 	addi	r5,r1,INT_PARMS
+ 	addi	r6,r1,INT_RTN
+-	bl	JUMPTARGET(_dl_call_pltexit)
++	bl	JUMPTARGET(_dl_audit_pltexit)
+ #ifndef SHARED
+ 	nop
+ #endif
+diff --git a/sysdeps/s390/s390-32/dl-machine.h b/sysdeps/s390/s390-32/dl-machine.h
+index ded41adff80346b6..2f3bb085ae2b6794 100644
+--- a/sysdeps/s390/s390-32/dl-machine.h
++++ b/sysdeps/s390/s390-32/dl-machine.h
+@@ -279,6 +279,7 @@ _dl_start_user:\n\
+ /* The S390 never uses Elf32_Rel relocations.  */
+ #define ELF_MACHINE_NO_REL 1
+ #define ELF_MACHINE_NO_RELA 0
++#define PLTREL ElfW(Rela)
+ 
+ /* We define an initialization functions.  This is called very early in
+    _dl_sysdep_start.  */
+diff --git a/sysdeps/s390/s390-32/dl-trampoline.h b/sysdeps/s390/s390-32/dl-trampoline.h
+index d36c002743bf2f0c..c447a41f067c462b 100644
+--- a/sysdeps/s390/s390-32/dl-trampoline.h
++++ b/sysdeps/s390/s390-32/dl-trampoline.h
+@@ -207,7 +207,7 @@ _dl_runtime_profile:
+ 	basr   %r1,0
+ 5:	l      %r14,7f-5b(%r1)
+ 	la     %r5,40(%r12)		# pointer to struct La_s390_32_retval
+-	bas    %r14,0(%r14,%r1)		# call _dl_call_pltexit
++	bas    %r14,0(%r14,%r1)		# call _dl_audit_pltexit
+ 
+ 	lr     %r15,%r12		# remove stack frame
+ 	cfi_def_cfa_register (15)
+@@ -224,7 +224,7 @@ _dl_runtime_profile:
+ 	br     %r14
+ 
+ 6:	.long  _dl_profile_fixup - 0b
+-7:	.long  _dl_call_pltexit - 5b
++7:	.long  _dl_audit_pltexit - 5b
+ 	cfi_endproc
+ 	.size _dl_runtime_profile, .-_dl_runtime_profile
+ #endif
+diff --git a/sysdeps/s390/s390-64/dl-machine.h b/sysdeps/s390/s390-64/dl-machine.h
+index 36327c40a1972dd7..033e7c9916e751f4 100644
+--- a/sysdeps/s390/s390-64/dl-machine.h
++++ b/sysdeps/s390/s390-64/dl-machine.h
+@@ -228,6 +228,7 @@ _dl_start_user:\n\
+ /* The 64 bit S/390 never uses Elf64_Rel relocations.  */
+ #define ELF_MACHINE_NO_REL 1
+ #define ELF_MACHINE_NO_RELA 0
++#define PLTREL ElfW(Rela)
+ 
+ /* We define an initialization functions.  This is called very early in
+    _dl_sysdep_start.  */
+diff --git a/sysdeps/s390/s390-64/dl-trampoline.h b/sysdeps/s390/s390-64/dl-trampoline.h
+index d313fd521db0b859..18534d629ebc00e2 100644
+--- a/sysdeps/s390/s390-64/dl-trampoline.h
++++ b/sysdeps/s390/s390-64/dl-trampoline.h
+@@ -203,7 +203,7 @@ _dl_runtime_profile:
+ 	lmg    %r2,%r4,48(%r12)		# r2, r3: load arguments saved by PLT
+ 					# r4: pointer to struct La_s390_64_regs
+ 	la     %r5,72(%r12)		# pointer to struct La_s390_64_retval
+-	brasl  %r14,_dl_call_pltexit
++	brasl  %r14,_dl_audit_pltexit
+ 
+ 	lgr    %r15,%r12		# remove stack frame
+ 	cfi_def_cfa_register (15)
+diff --git a/sysdeps/sh/dl-trampoline.S b/sysdeps/sh/dl-trampoline.S
+index 0c8f84d26d3015ca..73f865f2af4e2d48 100644
+--- a/sysdeps/sh/dl-trampoline.S
++++ b/sysdeps/sh/dl-trampoline.S
+@@ -423,8 +423,8 @@ _dl_runtime_profile:
+ 	.align 2
+ #ifdef SHARED
+ 7:	.long _GLOBAL_OFFSET_TABLE_
+-8:	.long _dl_call_pltexit@GOTOFF
++8:	.long _dl_audit_pltexit@GOTOFF
+ #else
+-8:	.long _dl_call_pltexit
++8:	.long _dl_audit_pltexit
+ #endif
+ 	.size _dl_runtime_profile, .-_dl_runtime_profile
+diff --git a/sysdeps/sparc/sparc32/dl-trampoline.S b/sysdeps/sparc/sparc32/dl-trampoline.S
+index 098ffcfacc55d0b6..18ef2f0d3655b3de 100644
+--- a/sysdeps/sparc/sparc32/dl-trampoline.S
++++ b/sysdeps/sparc/sparc32/dl-trampoline.S
+@@ -127,7 +127,7 @@ _dl_profile_invoke:
+ 	mov	%l5, %o0
+ 	mov	%l6, %o1
+ 	add	%sp, (11 * 8), %o2
+-	call	_dl_call_pltexit
++	call	_dl_audit_pltexit
+ 	 add	%sp, ( 9 * 8), %o3
+ 
+ 	ldd	[%sp + ( 9 * 8)], %i0
+diff --git a/sysdeps/sparc/sparc64/dl-trampoline.S b/sysdeps/sparc/sparc64/dl-trampoline.S
+index 4948b88b9640691d..9c18ceb131c9a25b 100644
+--- a/sysdeps/sparc/sparc64/dl-trampoline.S
++++ b/sysdeps/sparc/sparc64/dl-trampoline.S
+@@ -196,7 +196,7 @@ _dl_profile_invoke:
+ 	mov	%l5, %o0
+ 	mov	%l6, %o1
+ 	add	%sp, STACK_BIAS + (24 * 8), %o2
+-	call	_dl_call_pltexit
++	call	_dl_audit_pltexit
+ 	 add	%sp, STACK_BIAS + (16 * 8), %o3
+ 
+ 	ldx	[%sp + STACK_BIAS + (16 * 8)], %i0
+diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h
+index 5262aa69c06aa8db..d30317980882ac51 100644
+--- a/sysdeps/x86_64/dl-machine.h
++++ b/sysdeps/x86_64/dl-machine.h
+@@ -210,6 +210,7 @@ _dl_start_user:\n\
+ /* The x86-64 never uses Elf64_Rel/Elf32_Rel relocations.  */
+ #define ELF_MACHINE_NO_REL 1
+ #define ELF_MACHINE_NO_RELA 0
++#define PLTREL ElfW(Rela)
+ 
+ /* We define an initialization function.  This is called very early in
+    _dl_sysdep_start.  */
+diff --git a/sysdeps/x86_64/dl-runtime.h b/sysdeps/x86_64/dl-runtime.h
+index 3fa61d7a4697cf3f..379f8bd4dea8ef97 100644
+--- a/sysdeps/x86_64/dl-runtime.h
++++ b/sysdeps/x86_64/dl-runtime.h
+@@ -18,7 +18,7 @@
+    02111-1307 USA.  */
+ 
+ /* The ABI calls for the PLT stubs to pass the index of the relocation
+-   and not its offset.  In _dl_profile_fixup and _dl_call_pltexit we
++   and not its offset.  In _dl_profile_fixup and _dl_audit_pltexit we
+    also use the index.  Therefore it is wasteful to compute the offset
+    in the trampoline just to reverse the operation immediately
+    afterwards.  */
+diff --git a/sysdeps/x86_64/dl-trampoline.h b/sysdeps/x86_64/dl-trampoline.h
+index a28b1e73a4b187ba..256dfbb64df9f03d 100644
+--- a/sysdeps/x86_64/dl-trampoline.h
++++ b/sysdeps/x86_64/dl-trampoline.h
+@@ -388,7 +388,7 @@ _dl_runtime_profile:
+ 	jns 3f
+ 
+ 	/* There's nothing in the frame size, so there
+-	   will be no call to the _dl_call_pltexit. */
++	   will be no call to the _dl_audit_pltexit. */
+ 
+ 	/* Get back registers content.  */
+ 	movq LR_RCX_OFFSET(%rsp), %rcx
+@@ -436,7 +436,7 @@ _dl_runtime_profile:
+ 	mov 24(%rbx), %RSP_LP	# Drop the copied stack content
+ 
+ 	/* Now we have to prepare the La_x86_64_retval structure for the
+-	   _dl_call_pltexit.  The La_x86_64_regs is being pointed by rsp now,
++	   _dl_audit_pltexit.  The La_x86_64_regs is being pointed by rsp now,
+ 	   so we just need to allocate the sizeof(La_x86_64_retval) space on
+ 	   the stack, since the alignment has already been taken care of. */
+ # ifdef RESTORE_AVX
+@@ -491,7 +491,7 @@ _dl_runtime_profile:
+ 	movq 24(%rbx), %rdx	# La_x86_64_regs argument to %rdx.
+ 	movq 40(%rbx), %rsi	# Copy args pushed by PLT in register.
+ 	movq 32(%rbx), %rdi	# %rdi: link_map, %rsi: reloc_index
+-	call _dl_call_pltexit
++	call _dl_audit_pltexit
+ 
+ 	/* Restore return registers.  */
+ 	movq LRV_RAX_OFFSET(%rsp), %rax
diff --git a/SOURCES/glibc-rh2047981-23.patch b/SOURCES/glibc-rh2047981-23.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b2e83f86f7f781aa87d53985ce5e954a59b4099f
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-23.patch
@@ -0,0 +1,449 @@
+Added $(objpfx)tst-audit19a: $(libdl) to elf/Makefile since
+we still need $(libdl) in RHEL8.
+
+commit 063f9ba220f434c7f30dd65c4cff17c0c458a7cf
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Wed Jun 30 10:24:09 2021 -0300
+
+    elf: Avoid unnecessary slowdown from profiling with audit (BZ#15533)
+    
+    The rtld-audit interfaces introduces a slowdown due to enabling
+    profiling instrumentation (as if LD_AUDIT implied LD_PROFILE).
+    However, instrumenting is only necessary if one of audit libraries
+    provides PLT callbacks (la_pltenter or la_pltexit symbols).  Otherwise,
+    the slowdown can be avoided.
+    
+    The following patch adjusts the logic that enables profiling to iterate
+    over all audit modules and check if any of those provides a PLT hook.
+    To keep la_symbind to work even without PLT callbacks, _dl_fixup now
+    calls the audit callback if the modules implements it.
+    
+    Co-authored-by: Alexander Monakov <amonakov@ispras.ru>
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+Conflicts:
+	elf/Makefile
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 08a32a712a34f2cc..0cc03ffe2984ee50 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -221,12 +221,14 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
+ 	 tst-dlmopen-gethostbyname \
+ 	 tst-audit17 \
+ 	 tst-audit18 \
++	 tst-audit19b \
+ #	 reldep9
+ tests-internal += loadtest unload unload2 circleload1 \
+ 	 neededtest neededtest2 neededtest3 neededtest4 \
+ 	 tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \
+ 	 tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym \
+-	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split
++	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split \
++	 tst-audit19a
+ tests-container += tst-pldd tst-preload-pthread-libc
+ ifeq ($(build-hardcoded-path-in-tests),yes)
+ tests += tst-dlopen-aout
+@@ -358,6 +360,9 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
+ 		tst-dlmopen-gethostbyname-mod \
+ 		tst-auditmod18 \
+ 		tst-audit18mod \
++		tst-auditmod19a \
++		tst-auditmod19b \
++		tst-audit19bmod \
+ 
+ # Most modules build with _ISOMAC defined, but those filtered out
+ # depend on internal headers.
+@@ -1548,6 +1553,14 @@ $(objpfx)tst-audit18.out: $(objpfx)tst-auditmod18.so \
+ 			  $(objpfx)tst-audit18mod.so
+ tst-audit18-ARGS = -- $(host-test-program-cmd)
+ 
++$(objpfx)tst-audit19a: $(libdl)
++$(objpfx)tst-audit19a.out: $(objpfx)tst-auditmod19a.so
++tst-audit19a-ENV = LD_AUDIT=$(objpfx)tst-auditmod19a.so
++
++$(objpfx)tst-audit19b.out: $(objpfx)tst-auditmod19b.so
++$(objpfx)tst-audit19b: $(objpfx)tst-audit19bmod.so
++tst-audit19b-ARGS = -- $(host-test-program-cmd)
++
+ # tst-sonamemove links against an older implementation of the library.
+ LDFLAGS-tst-sonamemove-linkmod1.so = \
+   -Wl,--version-script=tst-sonamemove-linkmod1.map \
+diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
+index 19de5de067a5ef07..7a84b1fa8c3a7fdd 100644
+--- a/elf/dl-reloc.c
++++ b/elf/dl-reloc.c
+@@ -178,12 +178,28 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
+   int skip_ifunc = reloc_mode & __RTLD_NOIFUNC;
+ 
+ #ifdef SHARED
++  bool consider_symbind = false;
+   /* If we are auditing, install the same handlers we need for profiling.  */
+   if ((reloc_mode & __RTLD_AUDIT) == 0)
+-    consider_profiling |= GLRO(dl_audit) != NULL;
++    {
++      struct audit_ifaces *afct = GLRO(dl_audit);
++      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
++	{
++	  /* Profiling is needed only if PLT hooks are provided.  */
++	  if (afct->ARCH_LA_PLTENTER != NULL
++	      || afct->ARCH_LA_PLTEXIT != NULL)
++	    consider_profiling = 1;
++	  if (afct->symbind != NULL)
++	    consider_symbind = true;
++
++	  afct = afct->next;
++	}
++    }
+ #elif defined PROF
+   /* Never use dynamic linker profiling for gprof profiling code.  */
+ # define consider_profiling 0
++#else
++# define consider_symbind 0
+ #endif
+ 
+   if (l->l_relocated)
+@@ -278,7 +294,7 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
+     ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc);
+ 
+ #ifndef PROF
+-    if (__glibc_unlikely (consider_profiling)
++    if ((consider_profiling || consider_symbind)
+ 	&& l->l_info[DT_PLTRELSZ] != NULL)
+       {
+ 	/* Allocate the array which will contain the already found
+diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
+index ec0b2164825fa538..71ec65264ff780fb 100644
+--- a/elf/dl-runtime.c
++++ b/elf/dl-runtime.c
+@@ -123,6 +123,37 @@ _dl_fixup (
+       && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0))
+     value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
+ 
++#ifdef SHARED
++  /* Auditing checkpoint: we have a new binding.  Provide the auditing
++     libraries the possibility to change the value and tell us whether further
++     auditing is wanted.
++     The l_reloc_result is only allocated if there is an audit module which
++     provides a la_symbind.  */
++  if (l->l_reloc_result != NULL)
++    {
++      /* This is the address in the array where we store the result of previous
++	 relocations.  */
++      struct reloc_result *reloc_result
++	= &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))];
++      unsigned int init = atomic_load_acquire (&reloc_result->init);
++      if (init == 0)
++	{
++	  _dl_audit_symbind (l, reloc_result, sym, &value, result);
++
++	  /* Store the result for later runs.  */
++	  if (__glibc_likely (! GLRO(dl_bind_not)))
++	    {
++	      reloc_result->addr = value;
++	      /* Guarantee all previous writes complete before init is
++		 updated.  See CONCURRENCY NOTES below.  */
++	      atomic_store_release (&reloc_result->init, 1);
++	    }
++	}
++      else
++	value = reloc_result->addr;
++    }
++#endif
++
+   /* Finally, fix up the plt itself.  */
+   if (__glibc_unlikely (GLRO(dl_bind_not)))
+     return value;
+diff --git a/elf/rtld.c b/elf/rtld.c
+index 767acd122262b824..2994578ba3a5f911 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -1027,13 +1027,7 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
+     "la_objsearch\0"
+     "la_objopen\0"
+     "la_preinit\0"
+-#if __ELF_NATIVE_CLASS == 32
+-    "la_symbind32\0"
+-#elif __ELF_NATIVE_CLASS == 64
+-    "la_symbind64\0"
+-#else
+-# error "__ELF_NATIVE_CLASS must be defined"
+-#endif
++    LA_SYMBIND "\0"
+ #define STRING(s) __STRING (s)
+     "la_" STRING (ARCH_LA_PLTENTER) "\0"
+     "la_" STRING (ARCH_LA_PLTEXIT) "\0"
+diff --git a/elf/tst-audit19a.c b/elf/tst-audit19a.c
+new file mode 100644
+index 0000000000000000..035cde9351c2711b
+--- /dev/null
++++ b/elf/tst-audit19a.c
+@@ -0,0 +1,38 @@
++/* Check if DT_AUDIT a module without la_plt{enter,exit} symbols does not incur
++   in profiling (BZ#15533).
++   Copyright (C) 2021 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/>.  */
++
++#include <link.h>
++#include <support/xdlfcn.h>
++#include <support/check.h>
++
++static int
++do_test (void)
++{
++  void *h = xdlopen ("tst-auditmod19a.so", RTLD_NOW);
++
++  struct link_map *lmap;
++  TEST_VERIFY_EXIT (dlinfo (h, RTLD_DI_LINKMAP, &lmap) == 0);
++
++  /* The internal array is only allocated if profiling is enabled.  */
++  TEST_VERIFY (lmap->l_reloc_result == NULL);
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/elf/tst-audit19b.c b/elf/tst-audit19b.c
+new file mode 100644
+index 0000000000000000..da015734f24e0d79
+--- /dev/null
++++ b/elf/tst-audit19b.c
+@@ -0,0 +1,94 @@
++/* Check if DT_AUDIT a module with la_plt{enter,exit} call la_symbind
++   for lazy resolution.
++   Copyright (C) 2021 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/>.  */
++
++#include <getopt.h>
++#include <support/capture_subprocess.h>
++#include <support/check.h>
++#include <support/xstdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stdbool.h>
++
++static int restart;
++#define CMDLINE_OPTIONS \
++  { "restart", no_argument, &restart, 1 },
++
++int tst_audit18bmod1_func (void);
++
++static int
++handle_restart (void)
++{
++  TEST_COMPARE (tst_audit18bmod1_func (), 10);
++  return 0;
++}
++
++static inline bool
++startswith (const char *str, const char *pre)
++{
++  size_t lenpre = strlen (pre);
++  size_t lenstr = strlen (str);
++  return lenstr < lenpre ? false : memcmp (pre, str, lenpre) == 0;
++}
++
++static int
++do_test (int argc, char *argv[])
++{
++  /* We must have either:
++     - One our fource parameters left if called initially:
++       + path to ld.so         optional
++       + "--library-path"      optional
++       + the library path      optional
++       + the application name  */
++
++  if (restart)
++    return handle_restart ();
++
++  char *spargv[9];
++  int i = 0;
++  for (; i < argc - 1; i++)
++    spargv[i] = argv[i + 1];
++  spargv[i++] = (char *) "--direct";
++  spargv[i++] = (char *) "--restart";
++  spargv[i] = NULL;
++
++  setenv ("LD_AUDIT", "tst-auditmod18b.so", 0);
++  struct support_capture_subprocess result
++    = support_capture_subprogram (spargv[0], spargv);
++  support_capture_subprocess_check (&result, "tst-audit18b", 0, sc_allow_stderr);
++
++  bool find_symbind = false;
++
++  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
++  TEST_VERIFY (out != NULL);
++  char *buffer = NULL;
++  size_t buffer_length = 0;
++  while (xgetline (&buffer, &buffer_length, out))
++    if (startswith (buffer, "la_symbind: tst_audit18bmod1_func") == 0)
++      find_symbind = true;
++
++  TEST_COMPARE (find_symbind, true);
++
++  free (buffer);
++  xfclose (out);
++
++  return 0;
++}
++
++#define TEST_FUNCTION_ARGV do_test
++#include <support/test-driver.c>
+diff --git a/elf/tst-audit19bmod.c b/elf/tst-audit19bmod.c
+new file mode 100644
+index 0000000000000000..9ffdcd8f3ffbc38e
+--- /dev/null
++++ b/elf/tst-audit19bmod.c
+@@ -0,0 +1,23 @@
++/* Extra module for tst-audit18b.
++   Copyright (C) 2021 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
++tst_audit18bmod1_func (void)
++{
++  return 10;
++}
+diff --git a/elf/tst-auditmod19a.c b/elf/tst-auditmod19a.c
+new file mode 100644
+index 0000000000000000..f58204099457743d
+--- /dev/null
++++ b/elf/tst-auditmod19a.c
+@@ -0,0 +1,25 @@
++/* Audit module for tst-audit18a.
++   Copyright (C) 2021 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/>.  */
++
++#include <link.h>
++
++unsigned int
++la_version (unsigned int version)
++{
++  return LAV_CURRENT;
++}
+diff --git a/elf/tst-auditmod19b.c b/elf/tst-auditmod19b.c
+new file mode 100644
+index 0000000000000000..e2248b2a75946746
+--- /dev/null
++++ b/elf/tst-auditmod19b.c
+@@ -0,0 +1,46 @@
++/* Audit module for tst-audit18b.
++   Copyright (C) 2021 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/>.  */
++
++#include <link.h>
++#include <string.h>
++#include <stdio.h>
++
++unsigned int
++la_version (unsigned int version)
++{
++  return LAV_CURRENT;
++}
++
++unsigned int
++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
++{
++  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
++}
++
++uintptr_t
++#if __ELF_NATIVE_CLASS == 32
++la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
++	      uintptr_t *defcook, unsigned int *flags, const char *symname)
++#else
++la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
++	      uintptr_t *defcook, unsigned int *flags, const char *symname)
++#endif
++{
++  fprintf (stderr, "la_symbind: %s\n", symname);
++  return sym->st_value;
++}
+diff --git a/include/link.h b/include/link.h
+index cdd011f59445e490..dd491989beb41353 100644
+--- a/include/link.h
++++ b/include/link.h
+@@ -353,8 +353,10 @@ struct link_map
+ 
+ #if __ELF_NATIVE_CLASS == 32
+ # define symbind symbind32
++# define LA_SYMBIND "la_symbind32"
+ #elif __ELF_NATIVE_CLASS == 64
+ # define symbind symbind64
++# define LA_SYMBIND "la_symbind64"
+ #else
+ # error "__ELF_NATIVE_CLASS must be defined"
+ #endif
diff --git a/SOURCES/glibc-rh2047981-24.patch b/SOURCES/glibc-rh2047981-24.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c6fc26a5fff3f01684d372f65309d564f735ce0f
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-24.patch
@@ -0,0 +1,296 @@
+Added $(libdl) to $(objpfx)tst-audit-tlsdesc-dlopen in elf/Makefile
+since we still need $(libdl) in RHEL8.
+
+commit d1b38173c9255b1a4ae00018ad9b35404a7c74d0
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Wed Jun 30 15:51:31 2021 -0300
+
+    elf: Add audit tests for modules with TLSDESC
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 0cc03ffe2984ee50..d8d9734df0fea9a8 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -375,6 +375,22 @@ modules-names += tst-gnu2-tls1mod
+ $(objpfx)tst-gnu2-tls1: $(objpfx)tst-gnu2-tls1mod.so
+ tst-gnu2-tls1mod.so-no-z-defs = yes
+ CFLAGS-tst-gnu2-tls1mod.c += -mtls-dialect=gnu2
++
++tests += tst-audit-tlsdesc tst-audit-tlsdesc-dlopen
++modules-names += tst-audit-tlsdesc-mod1 tst-audit-tlsdesc-mod2 tst-auditmod-tlsdesc
++$(objpfx)tst-audit-tlsdesc: $(objpfx)tst-audit-tlsdesc-mod1.so \
++			    $(objpfx)tst-audit-tlsdesc-mod2.so \
++			    $(shared-thread-library)
++CFLAGS-tst-audit-tlsdesc-mod1.c += -mtls-dialect=gnu2
++CFLAGS-tst-audit-tlsdesc-mod2.c += -mtls-dialect=gnu2
++$(objpfx)tst-audit-tlsdesc-dlopen: $(shared-thread-library) $(libdl)
++$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-audit-tlsdesc-mod1.so \
++				       $(objpfx)tst-audit-tlsdesc-mod2.so
++$(objpfx)tst-audit-tlsdesc-mod1.so: $(objpfx)tst-audit-tlsdesc-mod2.so
++$(objpfx)tst-audit-tlsdesc.out: $(objpfx)tst-auditmod-tlsdesc.so
++tst-audit-tlsdesc-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
++$(objpfx)tst-audit-tlsdesc-dlopen.out: $(objpfx)tst-auditmod-tlsdesc.so
++tst-audit-tlsdesc-dlopen-ENV = LD_AUDIT=$(objpfx)tst-auditmod-tlsdesc.so
+ endif
+ ifeq (yes,$(have-protected-data))
+ modules-names += tst-protected1moda tst-protected1modb
+diff --git a/elf/tst-audit-tlsdesc-dlopen.c b/elf/tst-audit-tlsdesc-dlopen.c
+new file mode 100644
+index 0000000000000000..9c16bb087aca1b77
+--- /dev/null
++++ b/elf/tst-audit-tlsdesc-dlopen.c
+@@ -0,0 +1,67 @@
++/* DT_AUDIT with modules with TLSDESC.
++   Copyright (C) 2021 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/>.  */
++
++#include <support/check.h>
++#include <support/xthread.h>
++#include <support/xdlfcn.h>
++
++static void *
++thr_func (void *mod)
++{
++  int* (*get_global1)(void) = xdlsym (mod, "get_global1");
++  int* (*get_global2)(void) = xdlsym (mod, "get_global2");
++  void (*set_global2)(int) = xdlsym (mod, "set_global2");
++  int* (*get_local1)(void) = xdlsym (mod, "get_local1");
++  int* (*get_local2)(void) = xdlsym (mod, "get_local2");
++
++  int *global1 = get_global1 ();
++  TEST_COMPARE (*global1, 0);
++  ++*global1;
++
++  int *global2 = get_global2 ();
++  TEST_COMPARE (*global2, 0);
++  ++*global2;
++  TEST_COMPARE (*global2, 1);
++
++  set_global2 (10);
++  TEST_COMPARE (*global2, 10);
++
++  int *local1 = get_local1 ();
++  TEST_COMPARE (*local1, 0);
++  ++*local1;
++
++  int *local2 = get_local2 ();
++  TEST_COMPARE (*local2, 0);
++  ++*local2;
++
++  return 0;
++}
++
++static int
++do_test (void)
++{
++  void *mod = xdlopen ("tst-audit-tlsdesc-mod1.so", RTLD_LAZY);
++
++  pthread_t thr = xpthread_create (NULL, thr_func, mod);
++  void *r = xpthread_join (thr);
++  TEST_VERIFY (r == NULL);
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/elf/tst-audit-tlsdesc-mod1.c b/elf/tst-audit-tlsdesc-mod1.c
+new file mode 100644
+index 0000000000000000..61c7dd99a2fb5e28
+--- /dev/null
++++ b/elf/tst-audit-tlsdesc-mod1.c
+@@ -0,0 +1,41 @@
++/* DT_AUDIT with modules with TLSDESC.
++   Copyright (C) 2021 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/>.  */
++
++__thread int global1;
++
++int *
++get_global1 (void)
++{
++  return &global1;
++}
++
++static __thread int local1;
++
++void *
++get_local1 (void)
++{
++  return &local1;
++}
++
++extern __thread int global2;
++
++void
++set_global2 (int v)
++{
++  global2 = v;
++}
+diff --git a/elf/tst-audit-tlsdesc-mod2.c b/elf/tst-audit-tlsdesc-mod2.c
+new file mode 100644
+index 0000000000000000..28aef635f688ee03
+--- /dev/null
++++ b/elf/tst-audit-tlsdesc-mod2.c
+@@ -0,0 +1,33 @@
++/* DT_AUDIT with modules with TLSDESC.
++   Copyright (C) 2021 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/>.  */
++
++__thread int global2;
++
++int *
++get_global2 (void)
++{
++  return &global2;
++}
++
++static __thread int local2;
++
++void *
++get_local2 (void)
++{
++  return &local2;
++}
+diff --git a/elf/tst-audit-tlsdesc.c b/elf/tst-audit-tlsdesc.c
+new file mode 100644
+index 0000000000000000..3c8be81c95528f47
+--- /dev/null
++++ b/elf/tst-audit-tlsdesc.c
+@@ -0,0 +1,60 @@
++/* DT_AUDIT with modules with TLSDESC.
++   Copyright (C) 2021 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/>.  */
++
++#include <support/check.h>
++#include <support/xthread.h>
++
++extern __thread int global1;
++extern __thread int global2;
++void *get_local1 (void);
++void set_global2 (int v);
++void *get_local2 (void);
++
++static void *
++thr_func (void *clousure)
++{
++  TEST_COMPARE (global1, 0);
++  ++global1;
++  TEST_COMPARE (global2, 0);
++  ++global2;
++  TEST_COMPARE (global2, 1);
++
++  set_global2 (10);
++  TEST_COMPARE (global2, 10);
++
++  int *local1 = get_local1 ();
++  TEST_COMPARE (*local1, 0);
++  ++*local1;
++
++  int *local2 = get_local2 ();
++  TEST_COMPARE (*local2, 0);
++  ++*local2;
++
++  return 0;
++}
++
++static int
++do_test (void)
++{
++  pthread_t thr = xpthread_create (NULL, thr_func, NULL);
++  void *r = xpthread_join (thr);
++  TEST_VERIFY (r == NULL);
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/elf/tst-auditmod-tlsdesc.c b/elf/tst-auditmod-tlsdesc.c
+new file mode 100644
+index 0000000000000000..e4b835d1f1fb6f73
+--- /dev/null
++++ b/elf/tst-auditmod-tlsdesc.c
+@@ -0,0 +1,25 @@
++/* DT_AUDIT with modules with TLSDESC.
++   Copyright (C) 2021 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/>.  */
++
++#include <link.h>
++
++unsigned int
++la_version (unsigned int version)
++{
++  return LAV_CURRENT;
++}
diff --git a/SOURCES/glibc-rh2047981-25.patch b/SOURCES/glibc-rh2047981-25.patch
new file mode 100644
index 0000000000000000000000000000000000000000..14cbb8d19b60e343a031fe3df7200a00a94a8d7d
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-25.patch
@@ -0,0 +1,313 @@
+commit f0e23d34a7bdf6b90fba954ee741419171ac41b2
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Mon Jul 19 18:42:26 2021 -0300
+
+    elf: Issue audit la_objopen for vDSO
+    
+    The vDSO is is listed in the link_map chain, but is never the subject of
+    an la_objopen call.  A new internal flag __RTLD_VDSO is added that
+    acts as __RTLD_OPENEXEC to allocate the required 'struct auditstate'
+    extra space for the 'struct link_map'.
+    
+    The return value from the callback is currently ignored, since there
+    is no PLT call involved by glibc when using the vDSO, neither the vDSO
+    are exported directly.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+Conflicts:
+	elf/Makefile
+
+diff --git a/elf/Makefile b/elf/Makefile
+index d8d9734df0fea9a8..f047c1cce0c55da0 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -222,6 +222,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
+ 	 tst-audit17 \
+ 	 tst-audit18 \
+ 	 tst-audit19b \
++	 tst-audit22 \
+ #	 reldep9
+ tests-internal += loadtest unload unload2 circleload1 \
+ 	 neededtest neededtest2 neededtest3 neededtest4 \
+@@ -363,6 +364,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
+ 		tst-auditmod19a \
+ 		tst-auditmod19b \
+ 		tst-audit19bmod \
++		tst-auditmod22 \
+ 
+ # Most modules build with _ISOMAC defined, but those filtered out
+ # depend on internal headers.
+@@ -1577,6 +1579,9 @@ $(objpfx)tst-audit19b.out: $(objpfx)tst-auditmod19b.so
+ $(objpfx)tst-audit19b: $(objpfx)tst-audit19bmod.so
+ tst-audit19b-ARGS = -- $(host-test-program-cmd)
+ 
++$(objpfx)tst-audit22.out: $(objpfx)tst-auditmod22.so
++tst-audit22-ARGS = -- $(host-test-program-cmd)
++
+ # tst-sonamemove links against an older implementation of the library.
+ LDFLAGS-tst-sonamemove-linkmod1.so = \
+   -Wl,--version-script=tst-sonamemove-linkmod1.map \
+diff --git a/elf/dl-object.c b/elf/dl-object.c
+index 05a7750c65305771..3be309ecf1b5d4e2 100644
+--- a/elf/dl-object.c
++++ b/elf/dl-object.c
+@@ -59,16 +59,19 @@ _dl_new_object (char *realname, const char *libname, int type,
+ {
+ #ifdef SHARED
+   unsigned int naudit;
+-  if (__glibc_unlikely ((mode & __RTLD_OPENEXEC) != 0))
++  if (__glibc_unlikely ((mode & (__RTLD_OPENEXEC | __RTLD_VDSO)) != 0))
+     {
+-      assert (type == lt_executable);
+-      assert (nsid == LM_ID_BASE);
++      if (mode & __RTLD_OPENEXEC)
++	{
++	  assert (type == lt_executable);
++	  assert (nsid == LM_ID_BASE);
+ 
+-      /* Ignore the specified libname for the main executable.  It is
+-	 only known with an explicit loader invocation.  */
+-      libname = "";
++	  /* Ignore the specified libname for the main executable.  It is
++	     only known with an explicit loader invocation.  */
++	  libname = "";
++	}
+ 
+-      /* We create the map for the executable before we know whether
++      /* We create the map for the executable and vDSO before we know whether
+ 	 we have auditing libraries and if yes, how many.  Assume the
+ 	 worst.  */
+       naudit = DL_NNS;
+diff --git a/elf/rtld.c b/elf/rtld.c
+index 2994578ba3a5f911..efcbeac6c24c4b7b 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -1917,6 +1917,12 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
+       assert (i == npreloads);
+     }
+ 
++#ifdef NEED_DL_SYSINFO_DSO
++  /* Now that the audit modules are opened, call la_objopen for the vDSO.  */
++  if (GLRO(dl_sysinfo_map) != NULL)
++    _dl_audit_objopen (GLRO(dl_sysinfo_map), LM_ID_BASE);
++#endif
++
+   /* Load all the libraries specified by DT_NEEDED entries.  If LD_PRELOAD
+      specified some libraries to load, these are inserted before the actual
+      dependencies in the executable's searchlist for symbol resolution.  */
+diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
+index 34b1d5e8c37c2610..d2b35a080b57c183 100644
+--- a/elf/setup-vdso.h
++++ b/elf/setup-vdso.h
+@@ -30,7 +30,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
+      We just want our data structures to describe it as if we had just
+      mapped and relocated it normally.  */
+   struct link_map *l = _dl_new_object ((char *) "", "", lt_library, NULL,
+-				       0, LM_ID_BASE);
++				       __RTLD_VDSO, LM_ID_BASE);
+   if (__glibc_likely (l != NULL))
+     {
+       static ElfW(Dyn) dyn_temp[DL_RO_DYN_TEMP_CNT] attribute_relro;
+diff --git a/elf/tst-audit22.c b/elf/tst-audit22.c
+new file mode 100644
+index 0000000000000000..18fd22a760ddc3d8
+--- /dev/null
++++ b/elf/tst-audit22.c
+@@ -0,0 +1,124 @@
++/* Check DTAUDIT and vDSO interaction.
++   Copyright (C) 2021 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/>.  */
++
++#include <errno.h>
++#include <getopt.h>
++#include <limits.h>
++#include <inttypes.h>
++#include <string.h>
++#include <stdlib.h>
++#include <support/capture_subprocess.h>
++#include <support/check.h>
++#include <support/xstdio.h>
++#include <support/support.h>
++#include <sys/auxv.h>
++
++static int restart;
++#define CMDLINE_OPTIONS \
++  { "restart", no_argument, &restart, 1 },
++
++static uintptr_t vdso_addr;
++
++static int
++handle_restart (void)
++{
++  fprintf (stderr, "vdso: %p\n", (void*) vdso_addr);
++  return 0;
++}
++
++static uintptr_t
++parse_address (const char *str)
++{
++  void *r;
++  TEST_COMPARE (sscanf (str, "%p\n", &r), 1);
++  return (uintptr_t) r;
++}
++
++static inline bool
++startswith (const char *str, const char *pre)
++{
++  size_t lenpre = strlen (pre);
++  size_t lenstr = strlen (str);
++  return lenstr >= lenpre && memcmp (pre, str, lenpre) == 0;
++}
++
++static int
++do_test (int argc, char *argv[])
++{
++  vdso_addr = getauxval (AT_SYSINFO_EHDR);
++  if (vdso_addr == 0)
++    FAIL_UNSUPPORTED ("getauxval (AT_SYSINFO_EHDR) returned 0");
++
++  /* We must have either:
++     - One our fource parameters left if called initially:
++       + path to ld.so         optional
++       + "--library-path"      optional
++       + the library path      optional
++       + the application name  */
++  if (restart)
++    return handle_restart ();
++
++  char *spargv[9];
++  int i = 0;
++  for (; i < argc - 1; i++)
++    spargv[i] = argv[i + 1];
++  spargv[i++] = (char *) "--direct";
++  spargv[i++] = (char *) "--restart";
++  spargv[i] = NULL;
++
++  setenv ("LD_AUDIT", "tst-auditmod22.so", 0);
++  struct support_capture_subprocess result
++    = support_capture_subprogram (spargv[0], spargv);
++  support_capture_subprocess_check (&result, "tst-audit22", 0, sc_allow_stderr);
++
++  /* The respawned process should always print the vDSO address (otherwise it
++     will fails as unsupported).  However, on some architectures the audit
++     module might see the vDSO with l_addr being 0, meaning a fixed mapping
++     (linux-gate.so).  In this case we don't check its value against
++     AT_SYSINFO_EHDR one.  */
++  uintptr_t vdso_process = 0;
++  bool vdso_audit_found = false;
++  uintptr_t vdso_audit = 0;
++
++  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
++  TEST_VERIFY (out != NULL);
++  char *buffer = NULL;
++  size_t buffer_length = 0;
++  while (xgetline (&buffer, &buffer_length, out))
++    {
++      if (startswith (buffer, "vdso: "))
++	vdso_process = parse_address (buffer + strlen ("vdso: "));
++      else if (startswith (buffer, "vdso found: "))
++	{
++	  vdso_audit = parse_address (buffer + strlen ("vdso found: "));
++          vdso_audit_found = true;
++	}
++    }
++
++  TEST_COMPARE (vdso_audit_found, true);
++  if (vdso_audit != 0)
++    TEST_COMPARE (vdso_process, vdso_audit);
++
++  free (buffer);
++  xfclose (out);
++
++  return 0;
++}
++
++#define TEST_FUNCTION_ARGV do_test
++#include <support/test-driver.c>
+diff --git a/elf/tst-auditmod22.c b/elf/tst-auditmod22.c
+new file mode 100644
+index 0000000000000000..8e05ce8cbb215dd5
+--- /dev/null
++++ b/elf/tst-auditmod22.c
+@@ -0,0 +1,51 @@
++/* Check DTAUDIT and vDSO interaction.
++   Copyright (C) 2021 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/>.  */
++
++#include <link.h>
++#include <inttypes.h>
++#include <stdbool.h>
++#include <string.h>
++#include <stdio.h>
++#include <sys/auxv.h>
++
++static inline bool
++startswith (const char *str, const char *pre)
++{
++  size_t lenpre = strlen (pre);
++  size_t lenstr = strlen (str);
++  return lenstr < lenpre ? false : memcmp (pre, str, lenpre) == 0;
++}
++
++unsigned int
++la_version (unsigned int version)
++{
++  return LAV_CURRENT;
++}
++
++unsigned int
++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
++{
++  /* The linux-gate.so is placed at a fixed address, thus l_addr being 0,
++     and it might be the value reported as the AT_SYSINFO_EHDR.  */
++  if (map->l_addr == 0 && startswith (map->l_name, "linux-gate.so"))
++    fprintf (stderr, "vdso found: %p\n", NULL);
++  else if (map->l_addr == getauxval (AT_SYSINFO_EHDR))
++    fprintf (stderr, "vdso found: %p\n", (void*) map->l_addr);
++
++  return 0;
++}
+diff --git a/include/dlfcn.h b/include/dlfcn.h
+index 109586a1d968b630..a39cc9c69f55a56a 100644
+--- a/include/dlfcn.h
++++ b/include/dlfcn.h
+@@ -12,6 +12,8 @@
+ #define __RTLD_AUDIT	0x08000000
+ #define __RTLD_SECURE	0x04000000 /* Apply additional security checks.  */
+ #define __RTLD_NOIFUNC	0x02000000 /* Suppress calling ifunc functions.  */
++#define __RTLD_VDSO	0x01000000 /* Tell _dl_new_object the object is
++				      system-loaded.  */
+ 
+ #define __LM_ID_CALLER	-2
+ 
diff --git a/SOURCES/glibc-rh2047981-26.patch b/SOURCES/glibc-rh2047981-26.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b05628f13910a9a782f94e7bab9fc37a26e08db1
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-26.patch
@@ -0,0 +1,170 @@
+Added $(objpfx)tst-auditmod20: $(libdl) in elf/Makefile since
+we still have $(libdl) in RHEL8.
+
+commit 484e672ddabe0a919a692520e6ac8f2580866235
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Wed Jun 30 17:33:57 2021 -0300
+
+    elf: Do not fail for failed dlmopen on audit modules (BZ #28061)
+    
+    The dl_main sets the LM_ID_BASE to RT_ADD just before starting to
+    add load new shared objects.  The state is set to RT_CONSISTENT just
+    after all objects are loaded.
+    
+    However if a audit modules tries to dlmopen an inexistent module,
+    the _dl_open will assert that the namespace is in an inconsistent
+    state.
+    
+    This is different than dlopen, since first it will not use
+    LM_ID_BASE and second _dl_map_object_from_fd is the sole responsible
+    to set and reset the r_state value.
+    
+    So the assert on _dl_open can not really be seen if the state is
+    consistent, since _dt_main resets it.  This patch removes the assert.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Florian Weimer <fweimer@redhat.com>
+
+Conflicts:
+	elf/dl-open.c
+		Uses dl_debug_initialize instead of dl_debug_update.
+
+diff --git a/elf/Makefile b/elf/Makefile
+index f047c1cce0c55da0..7c7b9e1937d3e41c 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -222,6 +222,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
+ 	 tst-audit17 \
+ 	 tst-audit18 \
+ 	 tst-audit19b \
++	 tst-audit20 \
+ 	 tst-audit22 \
+ #	 reldep9
+ tests-internal += loadtest unload unload2 circleload1 \
+@@ -364,6 +365,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
+ 		tst-auditmod19a \
+ 		tst-auditmod19b \
+ 		tst-audit19bmod \
++		tst-auditmod20 \
+ 		tst-auditmod22 \
+ 
+ # Most modules build with _ISOMAC defined, but those filtered out
+@@ -1579,6 +1581,10 @@ $(objpfx)tst-audit19b.out: $(objpfx)tst-auditmod19b.so
+ $(objpfx)tst-audit19b: $(objpfx)tst-audit19bmod.so
+ tst-audit19b-ARGS = -- $(host-test-program-cmd)
+ 
++$(objpfx)tst-audit20.out: $(objpfx)tst-auditmod20.so
++tst-audit20-ENV = LD_AUDIT=$(objpfx)tst-auditmod20.so
++$(objpfx)tst-auditmod20.so: $(libdl)
++
+ $(objpfx)tst-audit22.out: $(objpfx)tst-auditmod22.so
+ tst-audit22-ARGS = -- $(host-test-program-cmd)
+ 
+diff --git a/elf/dl-open.c b/elf/dl-open.c
+index 660a56b2fb2639cd..6b85e9ab4e249f86 100644
+--- a/elf/dl-open.c
++++ b/elf/dl-open.c
+@@ -911,8 +911,6 @@ no more namespaces available for dlmopen()"));
+ 	     the flag here.  */
+ 	}
+ 
+-      assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
+-
+       /* Release the lock.  */
+       __rtld_lock_unlock_recursive (GL(dl_load_lock));
+ 
+diff --git a/elf/tst-audit20.c b/elf/tst-audit20.c
+new file mode 100644
+index 0000000000000000..6f39ccee865b012b
+--- /dev/null
++++ b/elf/tst-audit20.c
+@@ -0,0 +1,25 @@
++/* Check dlopen failure on audit modules.
++   Copyright (C) 2021 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/>.  */
++
++static int
++do_test (void)
++{
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/elf/tst-auditmod20.c b/elf/tst-auditmod20.c
+new file mode 100644
+index 0000000000000000..c57e50ee4e88dd6b
+--- /dev/null
++++ b/elf/tst-auditmod20.c
+@@ -0,0 +1,57 @@
++/* Check dlopen failure on audit modules.
++   Copyright (C) 2021 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/>.  */
++
++#include <dlfcn.h>
++#include <link.h>
++#include <stdlib.h>
++
++unsigned int
++la_version (unsigned int v)
++{
++  return LAV_CURRENT;
++}
++
++static void
++check (void)
++{
++  {
++    void *mod = dlopen ("nonexistent.so", RTLD_NOW);
++    if (mod != NULL)
++      abort ();
++  }
++
++  {
++    void *mod = dlmopen (LM_ID_BASE, "nonexistent.so", RTLD_NOW);
++    if (mod != NULL)
++      abort ();
++  }
++}
++
++void
++la_activity (uintptr_t *cookie, unsigned int flag)
++{
++  if (flag != LA_ACT_CONSISTENT)
++    return;
++  check ();
++}
++
++void
++la_preinit (uintptr_t *cookie)
++{
++  check ();
++}
diff --git a/SOURCES/glibc-rh2047981-27.patch b/SOURCES/glibc-rh2047981-27.patch
new file mode 100644
index 0000000000000000000000000000000000000000..08f14489d08ebf1d381c17c9105c13c2a43f8319
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-27.patch
@@ -0,0 +1,557 @@
+commit 28713c06129f8f64f88c423266e6ff2880216509
+Author: H.J. Lu <hjl.tools@gmail.com>
+Date:   Mon Dec 13 09:43:52 2021 -0800
+
+    elf: Sort tests and modules-names
+    
+    Sort tests and modules-names to reduce future conflicts.
+
+Conflicts:
+	elf/Makefile
+		Complete rewrite of sorted lists.
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 7c7b9e1937d3e41c..914cb5ad2f2c3aea 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -185,46 +185,130 @@ tests-static += tst-tls9-static
+ tst-tls9-static-ENV = \
+        LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn
+ 
+-tests += restest1 preloadtest loadfail multiload origtest resolvfail \
+-	 constload1 order noload filter \
+-	 reldep reldep2 reldep3 reldep4 nodelete nodelete2 \
+-	 nodlopen nodlopen2 lateglobal initfirst global \
+-	 restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \
+-	 tst-tls4 tst-tls5 \
+-	 tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-tls15 \
+-	 tst-tls16 tst-tls17 tst-tls18 tst-tls19 tst-tls-dlinfo \
+-	 tst-align tst-align2 \
+-	 tst-dlmodcount tst-dlopenrpath tst-deep1 \
+-	 tst-dlmopen1 tst-dlmopen3 \
+-	 unload3 unload4 unload5 unload6 unload7 unload8 tst-global1 order2 \
+-	 tst-audit1 tst-audit2 tst-audit8 tst-audit9 \
+-	 tst-addr1 tst-thrlock \
+-	 tst-unique1 tst-unique2 $(if $(CXX),tst-unique3 tst-unique4 \
+-	 tst-nodelete tst-dlopen-nodelete-reloc) \
+-	 tst-initorder tst-initorder2 tst-relsort1 tst-null-argv \
+-	 tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \
+-	 tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
+-	 tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
+-	 tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
+-	 tst-audit13 \
+-	 tst-sonamemove-link tst-sonamemove-dlopen \
+-	 tst-auditmany tst-initfinilazyfail \
+-	 tst-dlopenfail tst-dlopenfail-2 \
+-	 tst-filterobj tst-filterobj-dlopen tst-auxobj tst-auxobj-dlopen \
+-	 tst-audit14 tst-audit15 tst-audit16 \
+-	 tst-tls-ie tst-tls-ie-dlmopen \
+-	 argv0test \
+-	 tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask \
+-	 tst-tls20 tst-tls21 \
+-	 tst-rtld-run-static \
+-	 tst-dlmopen-dlerror \
+-	 tst-dlmopen-gethostbyname \
+-	 tst-audit17 \
+-	 tst-audit18 \
+-	 tst-audit19b \
+-	 tst-audit20 \
+-	 tst-audit22 \
++tests += \
++  argv0test \
++  constload1 \
++  dblload \
++  dblunload \
++  filter \
++  global \
++  initfirst \
++  lateglobal \
++  loadfail \
++  multiload \
++  next \
++  nodelete \
++  nodelete2 \
++  nodlopen \
++  nodlopen2 \
++  noload \
++  order \
++  order2 \
++  origtest \
++  preloadtest \
++  reldep \
++  reldep2 \
++  reldep3 \
++  reldep4 \
++  reldep5 \
++  reldep6 \
++  reldep7 \
++  reldep8 \
++  resolvfail \
++  restest1 \
++  restest2 \
++  tst-absolute-sym \
++  tst-absolute-zero \
++  tst-addr1 \
++  tst-align \
++  tst-align2 \
++  tst-audit1 \
++  tst-audit11 \
++  tst-audit12 \
++  tst-audit13 \
++  tst-audit14 \
++  tst-audit15 \
++  tst-audit16 \
++  tst-audit17 \
++  tst-audit18 \
++  tst-audit19b \
++  tst-audit2 \
++  tst-audit20 \
++  tst-audit22 \
++  tst-audit8 \
++  tst-audit9 \
++  tst-auditmany \
++  tst-auxobj \
++  tst-auxobj-dlopen \
++  tst-big-note \
++  tst-debug1 \
++  tst-deep1 \
++  tst-dlmodcount \
++  tst-dlmopen1 \
++  tst-dlmopen3 \
++  tst-dlmopen-dlerror \
++  tst-dlmopen-gethostbyname \
++  tst-dlopenfail \
++  tst-dlopenfail-2 \
++  tst-dlopenrpath \
++  tst-dlsym-error \
++  tst-filterobj \
++  tst-filterobj-dlopen \
++  tst-glibc-hwcaps \
++  tst-glibc-hwcaps-mask \
++  tst-glibc-hwcaps-prepend \
++  tst-global1 \
++  tst-initfinilazyfail \
++  tst-initorder \
++  tst-initorder2 \
++  tst-latepthread \
++  tst-main1 \
++  tst-nodelete2 \
++  tst-nodelete-dlclose \
++  tst-nodelete-opened \
++  tst-noload \
++  tst-null-argv \
++  tst-relsort1 \
++  tst-rtld-run-static \
++  tst-sonamemove-dlopen \
++  tst-sonamemove-link \
++  tst-thrlock \
++  tst-tls10 \
++  tst-tls11 \
++  tst-tls12 \
++  tst-tls13 \
++  tst-tls14 \
++  tst-tls15 \
++  tst-tls16 \
++  tst-tls17 \
++  tst-tls18 \
++  tst-tls19 \
++  tst-tls20 \
++  tst-tls21 \
++  tst-tls4 \
++  tst-tls5 \
++  tst-tlsalign \
++  tst-tlsalign-extern \
++  tst-tls-dlinfo \
++  tst-tls-ie \
++  tst-tls-ie-dlmopen \
++  tst-tls-manydynamic \
++  tst-unique1 \
++  tst-unique2 \
++  unload3 \
++  unload4 \
++  unload5 \
++  unload6 \
++  unload7 \
++  unload8 \
+ #	 reldep9
++tests-cxx = \
++  tst-dlopen-nodelete-reloc \
++  tst-nodelete \
++  tst-unique3 \
++  tst-unique4 \
++
++tests += $(if $(CXX),$(tests-cxx))
+ tests-internal += loadtest unload unload2 circleload1 \
+ 	 neededtest neededtest2 neededtest3 neededtest4 \
+ 	 tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \
+@@ -266,107 +350,269 @@ tst-tls-many-dynamic-modules-dep-bad = \
+ extra-test-objs += $(tlsmod17a-modules:=.os) $(tlsmod18a-modules:=.os) \
+ 		   tst-tlsalign-vars.o
+ test-extras += tst-tlsmod17a tst-tlsmod18a tst-tlsalign-vars
+-modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
+-		testobj1_1 failobj constload2 constload3 unloadmod \
+-		dep1 dep2 dep3 dep4 vismod1 vismod2 vismod3 \
+-		nodelmod1 nodelmod2 nodelmod3 nodelmod4 \
+-		nodel2mod1 nodel2mod2 nodel2mod3 \
+-		nodlopenmod nodlopenmod2 filtmod1 filtmod2 \
+-		reldepmod1 reldepmod2 reldepmod3 reldepmod4 nextmod1 nextmod2 \
+-		reldep4mod1 reldep4mod2 reldep4mod3 reldep4mod4 \
+-		neededobj1 neededobj2 neededobj3 neededobj4 \
+-		neededobj5 neededobj6 firstobj globalmod1 \
+-		unload2mod unload2dep ltglobmod1 ltglobmod2 pathoptobj \
+-		dblloadmod1 dblloadmod2 dblloadmod3 reldepmod5 reldepmod6 \
+-		reldep6mod0 reldep6mod1 reldep6mod2 reldep6mod3 reldep6mod4 \
+-		reldep7mod1 reldep7mod2 \
+-		tst-tlsmod1 tst-tlsmod2 tst-tlsmod3 tst-tlsmod4 \
+-		tst-tlsmod5 tst-tlsmod6 tst-tlsmod7 tst-tlsmod8 \
+-		tst-tlsmod9 tst-tlsmod10 tst-tlsmod11 tst-tlsmod12 \
+-		tst-tlsmod13 tst-tlsmod13a tst-tlsmod14a tst-tlsmod14b \
+-		tst-tlsmod15a tst-tlsmod15b tst-tlsmod16a tst-tlsmod16b \
+-		$(tlsmod17a-modules) tst-tlsmod17b $(tlsmod18a-modules) \
+-		tst-tls19mod1 tst-tls19mod2 tst-tls19mod3 \
+-		circlemod1 circlemod1a circlemod2 circlemod2a \
+-		circlemod3 circlemod3a \
+-		reldep8mod1 reldep8mod2 reldep8mod3 \
+-		reldep9mod1 reldep9mod2 reldep9mod3 \
+-		tst-alignmod tst-alignmod2 \
+-		$(modules-execstack-$(have-z-execstack)) \
+-		tst-dlopenrpathmod tst-deep1mod1 tst-deep1mod2 tst-deep1mod3 \
+-		tst-dlmopen1mod tst-auditmod1 \
+-		unload3mod1 unload3mod2 unload3mod3 unload3mod4 \
+-		unload4mod1 unload4mod2 unload4mod3 unload4mod4 \
+-		unload6mod1 unload6mod2 unload6mod3 \
+-		unload7mod1 unload7mod2 \
+-		unload8mod1 unload8mod1x unload8mod2 unload8mod3 \
+-		order2mod1 order2mod2 order2mod3 order2mod4 \
+-		tst-unique1mod1 tst-unique1mod2 \
+-		tst-unique2mod1 tst-unique2mod2 \
+-		tst-auditmod9a tst-auditmod9b \
+-		$(if $(CXX),tst-unique3lib tst-unique3lib2 tst-unique4lib \
+-		  tst-nodelete-uniquemod tst-nodelete-rtldmod \
+-		  tst-nodelete-zmod \
+-                  tst-dlopen-nodelete-reloc-mod1 \
+-		  tst-dlopen-nodelete-reloc-mod2 \
+-	          tst-dlopen-nodelete-reloc-mod3 \
+-		  tst-dlopen-nodelete-reloc-mod4 \
+-		  tst-dlopen-nodelete-reloc-mod5 \
+-	          tst-dlopen-nodelete-reloc-mod6 \
+-	          tst-dlopen-nodelete-reloc-mod7 \
+-	          tst-dlopen-nodelete-reloc-mod8 \
+-	          tst-dlopen-nodelete-reloc-mod9 \
+-	          tst-dlopen-nodelete-reloc-mod10 \
+-	          tst-dlopen-nodelete-reloc-mod11 \
+-	          tst-dlopen-nodelete-reloc-mod12 \
+-	          tst-dlopen-nodelete-reloc-mod13 \
+-	          tst-dlopen-nodelete-reloc-mod14 \
+-	          tst-dlopen-nodelete-reloc-mod15 \
+-	          tst-dlopen-nodelete-reloc-mod16 \
+-		  tst-dlopen-nodelete-reloc-mod17) \
+-		tst-initordera1 tst-initorderb1 \
+-		tst-initordera2 tst-initorderb2 \
+-		tst-initordera3 tst-initordera4 \
+-		tst-initorder2a tst-initorder2b tst-initorder2c \
+-		tst-initorder2d \
+-		tst-relsort1mod1 tst-relsort1mod2 tst-array2dep \
+-		tst-array5dep tst-null-argv-lib \
+-		tst-tlsalign-lib tst-nodelete-opened-lib tst-nodelete2mod \
+-		tst-audit11mod1 tst-audit11mod2 tst-auditmod11 \
+-		tst-audit12mod1 tst-audit12mod2 tst-audit12mod3 tst-auditmod12 \
+-		tst-latepthreadmod $(tst-tls-many-dynamic-modules) \
+-		$(tst-tls-many-dynamic-modules-dep) \
+-		$(tst-tls-many-dynamic-modules-dep-bad) \
+-		tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin \
+-		tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \
+-		tst-absolute-zero-lib tst-big-note-lib \
+-		tst-audit13mod1 tst-sonamemove-linkmod1 \
+-		tst-sonamemove-runmod1 tst-sonamemove-runmod2 \
+-		tst-auditmanymod1 tst-auditmanymod2 tst-auditmanymod3 \
+-		tst-auditmanymod4 tst-auditmanymod5 tst-auditmanymod6 \
+-		tst-auditmanymod7 tst-auditmanymod8 tst-auditmanymod9 \
+-		tst-initlazyfailmod tst-finilazyfailmod \
+-		tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2 \
+-		tst-dlopenfailmod3 \
+-		tst-filterobj-flt tst-filterobj-aux tst-filterobj-filtee \
+-		tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3 \
+-		tst-tls-ie-mod0 tst-tls-ie-mod1 tst-tls-ie-mod2 \
+-		tst-tls-ie-mod3 tst-tls-ie-mod4 tst-tls-ie-mod5 \
+-		tst-tls-ie-mod6 libmarkermod1-1 libmarkermod1-2 libmarkermod1-3 \
+-		libmarkermod2-1 libmarkermod2-2 \
+-		libmarkermod3-1 libmarkermod3-2 libmarkermod3-3 \
+-		libmarkermod4-1 libmarkermod4-2 libmarkermod4-3 libmarkermod4-4 \
+-		libmarkermod5-1 libmarkermod5-2 libmarkermod5-3 libmarkermod5-4 \
+-		libmarkermod5-5 tst-tls20mod-bad tst-tls21mod \
+-		tst-dlmopen-dlerror-mod \
+-		tst-dlmopen-gethostbyname-mod \
+-		tst-auditmod18 \
+-		tst-audit18mod \
+-		tst-auditmod19a \
+-		tst-auditmod19b \
+-		tst-audit19bmod \
+-		tst-auditmod20 \
+-		tst-auditmod22 \
++modules-names = \
++  circlemod1 \
++  circlemod1a \
++  circlemod2 \
++  circlemod2a \
++  circlemod3 \
++  circlemod3a \
++  constload2 \
++  constload3 \
++  dblloadmod1 \
++  dblloadmod2 \
++  dblloadmod3 \
++  dep1 \
++  dep2 \
++  dep3 \
++  dep4 \
++  failobj \
++  filtmod1 \
++  filtmod2 \
++  firstobj \
++  globalmod1 \
++  libmarkermod1-1 \
++  libmarkermod1-2 \
++  libmarkermod1-3 \
++  libmarkermod2-1 \
++  libmarkermod2-2 \
++  libmarkermod3-1 \
++  libmarkermod3-2 \
++  libmarkermod3-3 \
++  libmarkermod4-1 \
++  libmarkermod4-2 \
++  libmarkermod4-3 \
++  libmarkermod4-4 \
++  libmarkermod5-1 \
++  libmarkermod5-2 \
++  libmarkermod5-3 \
++  libmarkermod5-4 \
++  libmarkermod5-5 \
++  ltglobmod1 \
++  ltglobmod2 \
++  neededobj1 \
++  neededobj2 \
++  neededobj3 \
++  neededobj4 \
++  neededobj5 \
++  neededobj6 \
++  nextmod1 \
++  nextmod2 \
++  nodel2mod1 \
++  nodel2mod2 \
++  nodel2mod3 \
++  nodelmod1 \
++  nodelmod2 \
++  nodelmod3 \
++  nodelmod4 \
++  nodlopenmod \
++  nodlopenmod2 \
++  order2mod1 \
++  order2mod2 \
++  order2mod3 \
++  order2mod4 \
++  pathoptobj \
++  reldep4mod1 \
++  reldep4mod2 \
++  reldep4mod3 \
++  reldep4mod4 \
++  reldep6mod0 \
++  reldep6mod1 \
++  reldep6mod2 \
++  reldep6mod3 \
++  reldep6mod4 \
++  reldep7mod1 \
++  reldep7mod2 \
++  reldep8mod1 \
++  reldep8mod2 \
++  reldep8mod3 \
++  reldep9mod1 \
++  reldep9mod2 \
++  reldep9mod3 \
++  reldepmod1 \
++  reldepmod2 \
++  reldepmod3 \
++  reldepmod4 \
++  reldepmod5 \
++  reldepmod6 \
++  testobj1 \
++  testobj1_1 \
++  testobj2 \
++  testobj3 \
++  testobj4 \
++  testobj5 \
++  testobj6 \
++  tst-absolute-sym-lib \
++  tst-absolute-zero-lib \
++  tst-alignmod \
++  tst-alignmod2 \
++  tst-array2dep \
++  tst-array5dep \
++  tst-audit11mod1 \
++  tst-audit11mod2 \
++  tst-audit12mod1 \
++  tst-audit12mod2 \
++  tst-audit12mod3 \
++  tst-audit13mod1 \
++  tst-audit18mod \
++  tst-audit19bmod \
++  tst-auditlogmod-1 \
++  tst-auditlogmod-2 \
++  tst-auditlogmod-3 \
++  tst-auditmanymod1 \
++  tst-auditmanymod2 \
++  tst-auditmanymod3 \
++  tst-auditmanymod4 \
++  tst-auditmanymod5 \
++  tst-auditmanymod6 \
++  tst-auditmanymod7 \
++  tst-auditmanymod8 \
++  tst-auditmanymod9 \
++  tst-auditmod1 \
++  tst-auditmod9a \
++  tst-auditmod9b \
++  tst-auditmod11 \
++  tst-auditmod12 \
++  tst-auditmod18 \
++  tst-auditmod19a \
++  tst-auditmod19b \
++  tst-auditmod20 \
++  tst-auditmod22 \
++  tst-big-note-lib \
++  tst-deep1mod1 \
++  tst-deep1mod2 \
++  tst-deep1mod3 \
++  tst-dlmopen1mod \
++  tst-dlmopen-dlerror-mod \
++  tst-dlmopen-gethostbyname-mod \
++  tst-dlopenfaillinkmod \
++  tst-dlopenfailmod1 \
++  tst-dlopenfailmod2 \
++  tst-dlopenfailmod3 \
++  tst-dlopenrpathmod \
++  tst-filterobj-aux \
++  tst-filterobj-filtee \
++  tst-filterobj-flt \
++  tst-finilazyfailmod \
++  tst-initlazyfailmod \
++  tst-initorder2a \
++  tst-initorder2b \
++  tst-initorder2c \
++  tst-initorder2d \
++  tst-initordera1 \
++  tst-initordera2 \
++  tst-initordera3 \
++  tst-initordera4 \
++  tst-initorderb1 \
++  tst-initorderb2 \
++  tst-latepthreadmod \
++  tst-libc_dlvsym-dso \
++  tst-main1mod \
++  tst-nodelete2mod \
++  tst-nodelete-dlclose-dso \
++  tst-nodelete-dlclose-plugin \
++  tst-nodelete-opened-lib \
++  tst-null-argv-lib \
++  tst-relsort1mod1 \
++  tst-relsort1mod2 \
++  tst-sonamemove-linkmod1 \
++  tst-sonamemove-runmod1 \
++  tst-sonamemove-runmod2 \
++  tst-tls19mod1 \
++  tst-tls19mod2 \
++  tst-tls19mod3 \
++  tst-tls20mod-bad \
++  tst-tls21mod \
++  tst-tlsalign-lib \
++  tst-tls-ie-mod0 \
++  tst-tls-ie-mod1 \
++  tst-tls-ie-mod2 \
++  tst-tls-ie-mod3 \
++  tst-tls-ie-mod4 \
++  tst-tls-ie-mod5 \
++  tst-tls-ie-mod6 \
++  tst-tlsmod1 \
++  tst-tlsmod10 \
++  tst-tlsmod11 \
++  tst-tlsmod12 \
++  tst-tlsmod13 \
++  tst-tlsmod13a \
++  tst-tlsmod14a \
++  tst-tlsmod14b \
++  tst-tlsmod15a \
++  tst-tlsmod15b \
++  tst-tlsmod16a \
++  tst-tlsmod16b \
++  tst-tlsmod17b \
++  tst-tlsmod2 \
++  tst-tlsmod3 \
++  tst-tlsmod4 \
++  tst-tlsmod5 \
++  tst-tlsmod6 \
++  tst-tlsmod7 \
++  tst-tlsmod8 \
++  tst-tlsmod9 \
++  tst-unique1mod1 \
++  tst-unique1mod2 \
++  tst-unique2mod1 \
++  tst-unique2mod2 \
++  unload2dep \
++  unload2mod \
++  unload3mod1 \
++  unload3mod2 \
++  unload3mod3 \
++  unload3mod4 \
++  unload4mod1 \
++  unload4mod2 \
++  unload4mod3 \
++  unload4mod4 \
++  unload6mod1 \
++  unload6mod2 \
++  unload6mod3 \
++  unload7mod1 \
++  unload7mod2 \
++  unload8mod1 \
++  unload8mod1x \
++  unload8mod2 \
++  unload8mod3 \
++  unloadmod \
++  vismod1 \
++  vismod2 \
++  vismod3 \
++
++modules-names-cxx = \
++  tst-dlopen-nodelete-reloc-mod1 \
++  tst-dlopen-nodelete-reloc-mod10 \
++  tst-dlopen-nodelete-reloc-mod11 \
++  tst-dlopen-nodelete-reloc-mod12 \
++  tst-dlopen-nodelete-reloc-mod13 \
++  tst-dlopen-nodelete-reloc-mod14 \
++  tst-dlopen-nodelete-reloc-mod15 \
++  tst-dlopen-nodelete-reloc-mod16 \
++  tst-dlopen-nodelete-reloc-mod17 \
++  tst-dlopen-nodelete-reloc-mod2 \
++  tst-dlopen-nodelete-reloc-mod3 \
++  tst-dlopen-nodelete-reloc-mod4 \
++  tst-dlopen-nodelete-reloc-mod5 \
++  tst-dlopen-nodelete-reloc-mod6 \
++  tst-dlopen-nodelete-reloc-mod7 \
++  tst-dlopen-nodelete-reloc-mod8 \
++  tst-dlopen-nodelete-reloc-mod9 \
++  tst-nodelete-rtldmod \
++  tst-nodelete-uniquemod \
++  tst-nodelete-zmod \
++  tst-unique3lib \
++  tst-unique3lib2 \
++  tst-unique4lib \
++
++modules-names += \
++  $(if $(CXX),$(modules-names-cxx)) \
++  $(modules-execstack-$(have-z-execstack)) \
++  $(tst-tls-many-dynamic-modules) \
++  $(tst-tls-many-dynamic-modules-dep) \
++  $(tst-tls-many-dynamic-modules-dep-bad) \
++  $(tlsmod17a-modules) \
++  $(tlsmod18a-modules) \
+ 
+ # Most modules build with _ISOMAC defined, but those filtered out
+ # depend on internal headers.
diff --git a/SOURCES/glibc-rh2047981-28.patch b/SOURCES/glibc-rh2047981-28.patch
new file mode 100644
index 0000000000000000000000000000000000000000..9fb9d98b0f7eaecb60f79b27756602367471a5b9
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-28.patch
@@ -0,0 +1,77 @@
+commit bfb5ed5df3dd4d9507b4922248dc445b690d19c0
+Author: H.J. Lu <hjl.tools@gmail.com>
+Date:   Fri Oct 15 10:44:49 2021 -0700
+
+    elf: Also try DT_RUNPATH for LD_AUDIT dlopen [BZ #28455]
+    
+    DT_RUNPATH is only used to find the immediate dependencies of the
+    executable or shared object containing the DT_RUNPATH entry.  Update
+    LD_AUDIT dlopen call to try the DT_RUNPATH entry of the executable.
+    
+    Add tst-audit14a, which is copied from tst-audit14, to DT_RUNPATH and
+    build tst-audit14 with -Wl,--disable-new-dtags to test DT_RPATH.
+    
+    This partially fixes BZ #28455.
+
+Conflicts:
+	elf/Makefile
+		Rewrite test inclusion to use older stdout pattern.
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 914cb5ad2f2c3aea..4ec4e9a049156755 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -227,6 +227,7 @@ tests += \
+   tst-audit12 \
+   tst-audit13 \
+   tst-audit14 \
++  tst-audit14a \
+   tst-audit15 \
+   tst-audit16 \
+   tst-audit17 \
+@@ -1788,9 +1789,11 @@ $(objpfx)tst-auditmany.out: $(objpfx)tst-auditmanymod1.so \
+ tst-auditmany-ENV = \
+   LD_AUDIT=tst-auditmanymod1.so:tst-auditmanymod2.so:tst-auditmanymod3.so:tst-auditmanymod4.so:tst-auditmanymod5.so:tst-auditmanymod6.so:tst-auditmanymod7.so:tst-auditmanymod8.so:tst-auditmanymod9.so
+ 
+-LDFLAGS-tst-audit14 = -Wl,--audit=tst-auditlogmod-1.so
++LDFLAGS-tst-audit14 = -Wl,--audit=tst-auditlogmod-1.so,--disable-new-dtags
+ $(objpfx)tst-auditlogmod-1.so: $(libsupport)
+ $(objpfx)tst-audit14.out: $(objpfx)tst-auditlogmod-1.so
++LDFLAGS-tst-audit14a = -Wl,--audit=tst-auditlogmod-1.so,--enable-new-dtags
++$(objpfx)tst-audit14a.out: $(objpfx)tst-auditlogmod-1.so
+ LDFLAGS-tst-audit15 = \
+   -Wl,--audit=tst-auditlogmod-1.so,--depaudit=tst-auditlogmod-2.so
+ $(objpfx)tst-auditlogmod-2.so: $(libsupport)
+diff --git a/elf/dl-load.c b/elf/dl-load.c
+index 1613217a236c7fc3..0b45e6e3db31c70d 100644
+--- a/elf/dl-load.c
++++ b/elf/dl-load.c
+@@ -2042,6 +2042,21 @@ _dl_map_object (struct link_map *loader, const char *name,
+ 			    &main_map->l_rpath_dirs,
+ 			    &realname, &fb, loader ?: main_map, LA_SER_RUNPATH,
+ 			    &found_other_class);
++
++	  /* Also try DT_RUNPATH in the executable for LD_AUDIT dlopen
++	     call.  */
++	  if (__glibc_unlikely (mode & __RTLD_AUDIT)
++	      && fd == -1 && !did_main_map
++	      && main_map != NULL && main_map->l_type != lt_loaded)
++	    {
++	      struct r_search_path_struct l_rpath_dirs;
++	      l_rpath_dirs.dirs = NULL;
++	      if (cache_rpath (main_map, &l_rpath_dirs,
++			       DT_RUNPATH, "RUNPATH"))
++		fd = open_path (name, namelen, mode, &l_rpath_dirs,
++				&realname, &fb, loader ?: main_map,
++				LA_SER_RUNPATH, &found_other_class);
++	    }
+ 	}
+ 
+       /* Try the LD_LIBRARY_PATH environment variable.  */
+diff --git a/elf/tst-audit14a.c b/elf/tst-audit14a.c
+new file mode 100644
+index 0000000000000000..c6232eacf2946e4e
+--- /dev/null
++++ b/elf/tst-audit14a.c
+@@ -0,0 +1 @@
++#include "tst-audit14.c"
diff --git a/SOURCES/glibc-rh2047981-29.patch b/SOURCES/glibc-rh2047981-29.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3581baae6840ef361a424672dc2356af2060d8a1
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-29.patch
@@ -0,0 +1,42 @@
+commit f4f70c2895e3d325188a42c10eb7bb4335be6773
+Author: H.J. Lu <hjl.tools@gmail.com>
+Date:   Tue Jan 4 06:58:34 2022 -0800
+
+    elf: Add a comment after trailing backslashes
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 4ec4e9a049156755..53faca4585220048 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -308,6 +308,7 @@ tests-cxx = \
+   tst-nodelete \
+   tst-unique3 \
+   tst-unique4 \
++# tests-cxx
+ 
+ tests += $(if $(CXX),$(tests-cxx))
+ tests-internal += loadtest unload unload2 circleload1 \
+@@ -580,6 +581,7 @@ modules-names = \
+   vismod1 \
+   vismod2 \
+   vismod3 \
++# modules-names
+ 
+ modules-names-cxx = \
+   tst-dlopen-nodelete-reloc-mod1 \
+@@ -605,6 +607,7 @@ modules-names-cxx = \
+   tst-unique3lib \
+   tst-unique3lib2 \
+   tst-unique4lib \
++# modules-names-cxx
+ 
+ modules-names += \
+   $(if $(CXX),$(modules-names-cxx)) \
+@@ -614,6 +617,7 @@ modules-names += \
+   $(tst-tls-many-dynamic-modules-dep-bad) \
+   $(tlsmod17a-modules) \
+   $(tlsmod18a-modules) \
++# modules-names
+ 
+ # Most modules build with _ISOMAC defined, but those filtered out
+ # depend on internal headers.
diff --git a/SOURCES/glibc-rh2047981-3.patch b/SOURCES/glibc-rh2047981-3.patch
new file mode 100644
index 0000000000000000000000000000000000000000..aa0aaafb94e0ed4d13c0316edf7b41913c1872bf
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-3.patch
@@ -0,0 +1,245 @@
+commit 8dbb7a08ec52057819db4ee234f9429ab99eb4ae
+Author: Vineet Gupta <vgupta@synopsys.com>
+Date:   Wed May 27 12:54:21 2020 -0700
+
+    dl-runtime: reloc_{offset,index} now functions arch overide'able
+    
+    The existing macros are fragile and expect local variables with a
+    certain name. Fix this by defining them as functions with default
+    implementation in a new header dl-runtime.h which arches can override
+    if need be.
+    
+    This came up during ARC port review, hence the need for argument pltgot
+    in reloc_index() which is not needed by existing ports.
+    
+    This patch potentially only affects hppa/x86 ports,
+    build tested for both those configs and a few more.
+    
+    Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+
+diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
+index 72b03e000dcf190e..4ccd7c30678fafad 100644
+--- a/elf/dl-runtime.c
++++ b/elf/dl-runtime.c
+@@ -27,6 +27,7 @@
+ #include "dynamic-link.h"
+ #include <tls.h>
+ #include <dl-irel.h>
++#include <dl-runtime.h>
+ 
+ 
+ #if (!ELF_MACHINE_NO_RELA && !defined ELF_MACHINE_PLT_REL) \
+@@ -42,13 +43,6 @@
+ # define ARCH_FIXUP_ATTRIBUTE
+ #endif
+ 
+-#ifndef reloc_offset
+-# define reloc_offset reloc_arg
+-# define reloc_index  reloc_arg / sizeof (PLTREL)
+-#endif
+-
+-
+-
+ /* This function is called through a special trampoline from the PLT the
+    first time each PLT entry is called.  We must perform the relocation
+    specified in the PLT of the given shared object, and return the resolved
+@@ -68,8 +62,11 @@ _dl_fixup (
+     = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
+   const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
+ 
++  const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);
++
+   const PLTREL *const reloc
+-    = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
++    = (const void *) (D_PTR (l, l_info[DT_JMPREL])
++		      + reloc_offset (pltgot, reloc_arg));
+   const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
+   const ElfW(Sym) *refsym = sym;
+   void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
+@@ -180,9 +177,12 @@ _dl_profile_fixup (
+ 			l, reloc_arg);
+     }
+ 
++  const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);
++
+   /* This is the address in the array where we store the result of previous
+      relocations.  */
+-  struct reloc_result *reloc_result = &l->l_reloc_result[reloc_index];
++  struct reloc_result *reloc_result
++    = &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))];
+ 
+  /* CONCURRENCY NOTES:
+ 
+@@ -219,8 +219,11 @@ _dl_profile_fixup (
+ 	= (const void *) D_PTR (l, l_info[DT_SYMTAB]);
+       const char *strtab = (const char *) D_PTR (l, l_info[DT_STRTAB]);
+ 
++      const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);
++
+       const PLTREL *const reloc
+-	= (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
++	= (const void *) (D_PTR (l, l_info[DT_JMPREL])
++			  + reloc_offset (pltgot, reloc_arg));
+       const ElfW(Sym) *refsym = &symtab[ELFW(R_SYM) (reloc->r_info)];
+       const ElfW(Sym) *defsym = refsym;
+       lookup_t result;
+@@ -485,11 +488,14 @@ _dl_call_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
+ 		  const void *inregs, void *outregs)
+ {
+ #ifdef SHARED
++  const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);
++
+   /* This is the address in the array where we store the result of previous
+      relocations.  */
+   // XXX Maybe the bound information must be stored on the stack since
+   // XXX with bind_not a new value could have been stored in the meantime.
+-  struct reloc_result *reloc_result = &l->l_reloc_result[reloc_index];
++  struct reloc_result *reloc_result =
++    &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))];
+   ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
+ 					    l_info[DT_SYMTAB])
+ 		       + reloc_result->boundndx);
+diff --git a/elf/dl-runtime.h b/elf/dl-runtime.h
+new file mode 100644
+index 0000000000000000..78f1da77fb4ed905
+--- /dev/null
++++ b/elf/dl-runtime.h
+@@ -0,0 +1,30 @@
++/* Helpers for On-demand PLT fixup for shared objects.  Generic version.
++   Copyright (C) 2020 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, write to the Free
++   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
++   02111-1307 USA.  */
++
++static inline uintptr_t
++reloc_offset (uintptr_t plt0, uintptr_t pltn)
++{
++  return pltn;
++}
++
++static inline uintptr_t
++reloc_index (uintptr_t plt0, uintptr_t pltn, size_t size)
++{
++  return pltn / size;
++}
+diff --git a/sysdeps/hppa/dl-runtime.c b/sysdeps/hppa/dl-runtime.c
+index 885a3f1837cbc56d..2d061b150f0602c1 100644
+--- a/sysdeps/hppa/dl-runtime.c
++++ b/sysdeps/hppa/dl-runtime.c
+@@ -17,10 +17,6 @@
+    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307 USA.  */
+ 
+-/* Clear PA_GP_RELOC bit in relocation offset.  */
+-#define reloc_offset (reloc_arg & ~PA_GP_RELOC)
+-#define reloc_index  (reloc_arg & ~PA_GP_RELOC) / sizeof (PLTREL)
+-
+ #include <elf/dl-runtime.c>
+ 
+ /* The caller has encountered a partially relocated function descriptor.
+diff --git a/sysdeps/hppa/dl-runtime.h b/sysdeps/hppa/dl-runtime.h
+new file mode 100644
+index 0000000000000000..6983aa0ae9b4296c
+--- /dev/null
++++ b/sysdeps/hppa/dl-runtime.h
+@@ -0,0 +1,31 @@
++/* Helpers for On-demand PLT fixup for shared objects.  HPAA version.
++   Copyright (C) 2020 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, write to the Free
++   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
++   02111-1307 USA.  */
++
++/* Clear PA_GP_RELOC bit in relocation offset.  */
++static inline uintptr_t
++reloc_offset (uintptr_t plt0, uintptr_t pltn)
++{
++  return pltn & ~PA_GP_RELOC;
++}
++
++static inline uintptr_t
++reloc_index (uintptr_t plt0, uintptr_t pltn, size_t size)
++{
++  return (pltn & ~PA_GP_RELOC )/ size;
++}
+diff --git a/sysdeps/x86_64/dl-runtime.c b/sysdeps/x86_64/dl-runtime.c
+deleted file mode 100644
+index b625d1e88257b018..0000000000000000
+--- a/sysdeps/x86_64/dl-runtime.c
++++ /dev/null
+@@ -1,9 +0,0 @@
+-/* The ABI calls for the PLT stubs to pass the index of the relocation
+-   and not its offset.  In _dl_profile_fixup and _dl_call_pltexit we
+-   also use the index.  Therefore it is wasteful to compute the offset
+-   in the trampoline just to reverse the operation immediately
+-   afterwards.  */
+-#define reloc_offset reloc_arg * sizeof (PLTREL)
+-#define reloc_index  reloc_arg
+-
+-#include <elf/dl-runtime.c>
+diff --git a/sysdeps/x86_64/dl-runtime.h b/sysdeps/x86_64/dl-runtime.h
+new file mode 100644
+index 0000000000000000..3fa61d7a4697cf3f
+--- /dev/null
++++ b/sysdeps/x86_64/dl-runtime.h
+@@ -0,0 +1,35 @@
++/* Helpers for On-demand PLT fixup for shared objects.  x86_64 version.
++   Copyright (C) 2020 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, write to the Free
++   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
++   02111-1307 USA.  */
++
++/* The ABI calls for the PLT stubs to pass the index of the relocation
++   and not its offset.  In _dl_profile_fixup and _dl_call_pltexit we
++   also use the index.  Therefore it is wasteful to compute the offset
++   in the trampoline just to reverse the operation immediately
++   afterwards.  */
++static inline uintptr_t
++reloc_offset (uintptr_t plt0, uintptr_t pltn)
++{
++  return pltn * sizeof (ElfW(Rela));
++}
++
++static inline uintptr_t
++reloc_index (uintptr_t plt0, uintptr_t pltn, size_t size)
++{
++  return pltn;
++}
diff --git a/SOURCES/glibc-rh2047981-30.patch b/SOURCES/glibc-rh2047981-30.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d52225fefc562851cd6a55c12fc3619f71f403e7
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-30.patch
@@ -0,0 +1,520 @@
+commit 7de01e60c200c431d3469deb784da8fd4508fc15
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Fri Jan 14 20:16:05 2022 +0100
+
+    elf/Makefile: Reflow and sort most variable assignments
+    
+    Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
+
+Conflicts:
+	elf/Makefile
+		Complete rewrite of reflow.
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 53faca4585220048..954cd08c199f5037 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -21,20 +21,60 @@ subdir		:= elf
+ 
+ include ../Makeconfig
+ 
+-headers		= elf.h bits/elfclass.h link.h bits/link.h bits/link_lavcurrent.h
+-routines	= $(all-dl-routines) dl-support dl-iteratephdr \
+-		  dl-addr dl-addr-obj enbl-secure dl-profstub \
+-		  dl-origin dl-libc dl-sym dl-sysdep dl-error \
+-		  dl-reloc-static-pie libc_early_init
++headers	= \
++  bits/elfclass.h \
++  bits/link.h \
++  bits/link_lavcurrent.h \
++  elf.h \
++  link.h \
++  # headers
++
++routines = \
++  $(all-dl-routines) \
++  dl-addr \
++  dl-addr-obj \
++  dl-error \
++  dl-iteratephdr \
++  dl-libc \
++  dl-origin \
++  dl-profstub \
++  dl-reloc-static-pie \
++  dl-support \
++  dl-sym \
++  dl-sysdep \
++  enbl-secure \
++  libc_early_init \
++  # routines
+ 
+ # The core dynamic linking functions are in libc for the static and
+ # profiled libraries.
+-dl-routines	= $(addprefix dl-,load lookup object reloc deps \
+-				  runtime init fini debug misc \
+-				  version profile tls origin scope \
+-				  execstack open close trampoline \
+-				  exception sort-maps lookup-direct \
+-				  call-libc-early-init write)
++dl-routines = \
++  dl-call-libc-early-init \
++  dl-close \
++  dl-debug \
++  dl-deps \
++  dl-exception \
++  dl-execstack \
++  dl-fini \
++  dl-init \
++  dl-load \
++  dl-lookup \
++  dl-lookup-direct \
++  dl-misc \
++  dl-object \
++  dl-open \
++  dl-origin \
++  dl-profile \
++  dl-reloc \
++  dl-runtime \
++  dl-scope \
++  dl-sort-maps \
++  dl-tls \
++  dl-trampoline \
++  dl-version \
++  dl-write \
++  # dl-routines
++
+ ifeq (yes,$(use-ldconfig))
+ dl-routines += dl-cache
+ endif
+@@ -57,15 +97,36 @@ endif
+ 
+ all-dl-routines = $(dl-routines) $(sysdep-dl-routines)
+ # But they are absent from the shared libc, because that code is in ld.so.
+-elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
+-		    dl-sysdep dl-exception dl-reloc-static-pie
++elide-routines.os = \
++  $(all-dl-routines) \
++  dl-exception \
++  dl-origin \
++  dl-reloc-static-pie \
++  dl-support \
++  dl-sysdep \
++  enbl-secure \
++  # elide-routines.os
+ 
+ # ld.so uses those routines, plus some special stuff for being the program
+ # interpreter and operating independent of libc.
+-rtld-routines	= rtld $(all-dl-routines) dl-sysdep dl-environ dl-minimal \
+-  dl-error-minimal dl-conflict dl-hwcaps dl-hwcaps_split dl-hwcaps-subdirs \
+-  dl-usage dl-diagnostics dl-diagnostics-kernel dl-diagnostics-cpu \
+-  dl-audit
++rtld-routines = \
++  $(all-dl-routines) \
++  dl-audit \
++  dl-conflict \
++  dl-diagnostics \
++  dl-diagnostics-cpu \
++  dl-diagnostics-kernel \
++  dl-environ \
++  dl-error-minimal \
++  dl-hwcaps \
++  dl-hwcaps-subdirs \
++  dl-hwcaps_split \
++  dl-minimal \
++  dl-sysdep \
++  dl-usage \
++  rtld \
++  # rtld-routines
++
+ all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
+ 
+ CFLAGS-dl-runtime.c += -fexceptions -fasynchronous-unwind-tables
+@@ -98,8 +159,18 @@ ld-map		= $(common-objpfx)ld.map
+ endif
+ 
+ ifeq (yes,$(build-shared))
+-extra-objs	= $(all-rtld-routines:%=%.os) soinit.os sofini.os interp.os
+-generated	+= librtld.os dl-allobjs.os ld.so ldd
++extra-objs = \
++  $(all-rtld-routines:%=%.os) \
++  sofini.os \
++  soinit.os \
++  interp.os \
++  # extra-objs
++generated += \
++  dl-allobjs.os \
++  ldd \
++  ld.so \
++  librtld.os \
++  # generated
+ install-others	= $(inst_rtlddir)/$(rtld-installed-name) $(inst_bindir)/ld.so
+ install-bin-script = ldd
+ endif
+@@ -117,8 +188,15 @@ others-static	+= ldconfig
+ others		+= ldconfig
+ install-rootsbin += ldconfig
+ 
+-ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon static-stubs \
+-  stringtable
++ldconfig-modules := \
++  cache \
++  chroot_canon \
++  readlib \
++  static-stubs \
++  stringtable \
++  xmalloc \
++  xstrdup \
++  # ldconfig-modules
+ extra-objs	+= $(ldconfig-modules:=.o)
+ others-extras   = $(ldconfig-modules)
+ endif
+@@ -153,20 +231,34 @@ $(inst_auditdir)/sotruss-lib.so: $(objpfx)sotruss-lib.so $(+force)
+ 	$(do-install-program)
+ endif
+ 
+-tests-static-normal := tst-leaks1-static tst-array1-static tst-array5-static \
+-	       tst-dl-iter-static \
+-	       tst-tlsalign-static tst-tlsalign-extern-static \
+-	       tst-linkall-static tst-env-setuid tst-env-setuid-tunables \
+-	       tst-dst-static
+-tests-static-internal := tst-tls1-static tst-tls2-static \
+-	       tst-ptrguard1-static tst-stackguard1-static \
+-	       tst-tls1-static-non-pie tst-libc_dlvsym-static
++tests-static-normal := \
++  tst-array1-static \
++  tst-array5-static \
++  tst-dl-iter-static \
++  tst-dst-static \
++  tst-env-setuid \
++  tst-env-setuid-tunables \
++  tst-leaks1-static \
++  tst-linkall-static \
++  tst-tlsalign-extern-static \
++  tst-tlsalign-static \
++  # tests-static-normal
++
++tests-static-internal := \
++  tst-libc_dlvsym-static \
++  tst-ptrguard1-static \
++  tst-stackguard1-static \
++  tst-tls1-static \
++  tst-tls1-static-non-pie \
++  tst-tls2-static \
++  # tests-static-internal
+ 
+ CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
+ tst-tls1-static-non-pie-no-pie = yes
+ 
+ tests-container = \
+-			  tst-ldconfig-bad-aux-cache
++  tst-ldconfig-bad-aux-cache \
++  # tests-container
+ 
+ ifeq (no,$(build-hardcoded-path-in-tests))
+ # This is an ld.so.cache test, and RPATH/RUNPATH in the executable
+@@ -174,14 +266,31 @@ ifeq (no,$(build-hardcoded-path-in-tests))
+ tests-container += tst-glibc-hwcaps-prepend-cache
+ endif
+ 
+-tests := tst-tls9 tst-leaks1 \
+-	tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 \
+-	tst-auxv tst-stringtable
+-tests-internal := tst-tls1 tst-tls2 $(tests-static-internal)
++tests := \
++  tst-array1 \
++  tst-array2 \
++  tst-array3 \
++  tst-array4 \
++  tst-array5 \
++  tst-auxv \
++  tst-leaks1 \
++  tst-stringtable \
++  tst-tls9 \
++  # tests
++
++tests-internal := \
++  $(tests-static-internal) \
++  tst-tls1 \
++  tst-tls2 \
++  # tests-internal
++
+ tests-static := $(tests-static-normal) $(tests-static-internal)
+ 
+ ifeq (yes,$(build-shared))
+-tests-static += tst-tls9-static
++tests-static += \
++  tst-tls9-static \
++  # tests-static
++
+ tst-tls9-static-ENV = \
+        LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn
+ 
+@@ -302,37 +411,71 @@ tests += \
+   unload6 \
+   unload7 \
+   unload8 \
+-#	 reldep9
++  # tests
+ tests-cxx = \
+   tst-dlopen-nodelete-reloc \
+   tst-nodelete \
+   tst-unique3 \
+   tst-unique4 \
+-# tests-cxx
++  # tests-cxx
+ 
+ tests += $(if $(CXX),$(tests-cxx))
+-tests-internal += loadtest unload unload2 circleload1 \
+-	 neededtest neededtest2 neededtest3 neededtest4 \
+-	 tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \
+-	 tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym \
+-	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split \
+-	 tst-audit19a
+-tests-container += tst-pldd tst-preload-pthread-libc
++
++tests-internal += \
++  circleload1 \
++  loadtest \
++  neededtest \
++  neededtest2 \
++  neededtest3 \
++  neededtest4 \
++  tst-audit19a \
++  tst-create_format1 \
++  tst-dl-hwcaps_split \
++  tst-dlmopen2 \
++  tst-libc_dlvsym \
++  tst-ptrguard1 \
++  tst-stackguard1 \
++  tst-tls-surplus \
++  tst-tls3 \
++  tst-tls6 \
++  tst-tls7 \
++  tst-tls8 \
++  unload \
++  unload2 \
++  # tests-internal
++
++tests-container += \
++  tst-pldd \
++  tst-preload-pthread-libc
++  # tests-container
++
+ ifeq ($(build-hardcoded-path-in-tests),yes)
+ tests += tst-dlopen-aout
+ tst-dlopen-aout-no-pie = yes
+ endif
+-test-srcs = tst-pathopt
++test-srcs = \
++  tst-pathopt
++  # tests-srcs
++
+ selinux-enabled := $(shell cat /selinux/enforce 2> /dev/null)
++
+ ifneq ($(selinux-enabled),1)
+-tests-execstack-yes = tst-execstack tst-execstack-needed tst-execstack-prog
++tests-execstack-yes = \
++  tst-execstack \
++  tst-execstack-needed \
++  tst-execstack-prog \
++  # tests-execstack-yes
+ endif
+ endif
+ tests += $(tests-execstack-$(have-z-execstack))
+ ifeq ($(run-built-tests),yes)
+-tests-special += $(objpfx)tst-leaks1-mem.out \
+-		 $(objpfx)tst-leaks1-static-mem.out $(objpfx)noload-mem.out \
+-		 $(objpfx)tst-ldconfig-X.out $(objpfx)tst-rtld-help.out
++tests-special += \
++  $(objpfx)noload-mem.out \
++  $(objpfx)tst-ldconfig-X.out \
++  $(objpfx)tst-leaks1-mem.out \
++  $(objpfx)tst-leaks1-static-mem.out \
++  $(objpfx)tst-rtld-help.out \
++  # tests-special
+ endif
+ tlsmod17a-suffixes = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+ tlsmod18a-suffixes = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+@@ -349,9 +492,16 @@ tst-tls-many-dynamic-modules-dep = \
+ tst-tls-many-dynamic-modules-dep-bad-suffixes = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+ tst-tls-many-dynamic-modules-dep-bad = \
+   $(foreach n,$(tst-tls-many-dynamic-modules-dep-bad-suffixes),tst-tls-manydynamic$(n)mod-dep-bad)
+-extra-test-objs += $(tlsmod17a-modules:=.os) $(tlsmod18a-modules:=.os) \
+-		   tst-tlsalign-vars.o
+-test-extras += tst-tlsmod17a tst-tlsmod18a tst-tlsalign-vars
++extra-test-objs += \
++  $(tlsmod17a-modules:=.os) \
++  $(tlsmod18a-modules:=.os) \
++  tst-tlsalign-vars.o \
++  # extra-test-objs
++test-extras += \
++  tst-tlsalign-vars \
++  tst-tlsmod17a \
++  tst-tlsmod18a \
++  # test-extras
+ modules-names = \
+   circlemod1 \
+   circlemod1a \
+@@ -607,17 +757,17 @@ modules-names-cxx = \
+   tst-unique3lib \
+   tst-unique3lib2 \
+   tst-unique4lib \
+-# modules-names-cxx
++  # modules-names-cxx
+ 
+ modules-names += \
+   $(if $(CXX),$(modules-names-cxx)) \
+   $(modules-execstack-$(have-z-execstack)) \
++  $(tlsmod17a-modules) \
++  $(tlsmod18a-modules) \
+   $(tst-tls-many-dynamic-modules) \
+   $(tst-tls-many-dynamic-modules-dep) \
+   $(tst-tls-many-dynamic-modules-dep-bad) \
+-  $(tlsmod17a-modules) \
+-  $(tlsmod18a-modules) \
+-# modules-names
++  # modules-names
+ 
+ # Most modules build with _ISOMAC defined, but those filtered out
+ # depend on internal headers.
+@@ -680,54 +830,103 @@ modules-names-nobuild := filtmod1
+ tests += $(tests-static)
+ 
+ ifneq (no,$(multi-arch))
+-tests-ifuncstatic := ifuncmain1static ifuncmain1picstatic \
+-		ifuncmain2static ifuncmain2picstatic \
+-		ifuncmain4static ifuncmain4picstatic \
+-		ifuncmain5static ifuncmain5picstatic \
+-		ifuncmain7static ifuncmain7picstatic
++tests-ifuncstatic := \
++  ifuncmain1static \
++  ifuncmain1picstatic \
++  ifuncmain2static \
++  ifuncmain2picstatic \
++  ifuncmain4static \
++  ifuncmain4picstatic \
++  ifuncmain5static \
++  ifuncmain5picstatic \
++  ifuncmain7static \
++  ifuncmain7picstatic \
++  # tests-ifuncstatic
+ tests-static += $(tests-ifuncstatic)
+ tests-internal += $(tests-ifuncstatic)
+ ifeq (yes,$(build-shared))
+ tests-internal += \
+-	 ifuncmain1 ifuncmain1pic ifuncmain1vis ifuncmain1vispic \
+-	 ifuncmain1staticpic \
+-	 ifuncmain2 ifuncmain2pic ifuncmain3 ifuncmain4 \
+-	 ifuncmain5 ifuncmain5pic ifuncmain5staticpic \
+-	 ifuncmain7 ifuncmain7pic
+-ifunc-test-modules = ifuncdep1 ifuncdep1pic ifuncdep2 ifuncdep2pic \
+-		     ifuncdep5 ifuncdep5pic
++  ifuncmain1 \
++  ifuncmain1pic \
++  ifuncmain1staticpic \
++  ifuncmain1vis \
++  ifuncmain1vispic \
++  ifuncmain2 \
++  ifuncmain2pic \
++  ifuncmain3 \
++  ifuncmain4 \
++  ifuncmain5 \
++  ifuncmain5pic \
++  ifuncmain5staticpic \
++  ifuncmain7 \
++  ifuncmain7pic \
++  # tests-internal
++ifunc-test-modules = \
++  ifuncdep1 \
++  ifuncdep1pic \
++  ifuncdep2 \
++  ifuncdep2pic \
++  ifuncdep5 \
++  ifuncdep5pic \
++  # ifunc-test-modules
+ extra-test-objs += $(ifunc-test-modules:=.o)
+ test-internal-extras += $(ifunc-test-modules)
+ ifeq (yes,$(have-fpie))
+-ifunc-pie-tests = ifuncmain1pie ifuncmain1vispie ifuncmain1staticpie \
+-		  ifuncmain5pie ifuncmain6pie ifuncmain7pie
++ifunc-pie-tests = \
++  ifuncmain1pie \
++  ifuncmain1staticpie \
++  ifuncmain1vispie \
++  ifuncmain5pie \
++  ifuncmain6pie \
++  ifuncmain7pie \
++  # ifunc-pie-tests
+ tests-internal += $(ifunc-pie-tests)
+ tests-pie += $(ifunc-pie-tests)
+ endif
+-modules-names += ifuncmod1 ifuncmod3 ifuncmod5 ifuncmod6
++modules-names += \
++  ifuncmod1 \
++  ifuncmod3 \
++  ifuncmod5 \
++  ifuncmod6 \
++  # modules-names
+ endif
+ endif
+ 
+ ifeq (yes,$(build-shared))
+ ifeq ($(run-built-tests),yes)
+-tests-special += $(objpfx)tst-pathopt.out $(objpfx)tst-rtld-load-self.out \
+-		 $(objpfx)tst-rtld-preload.out $(objpfx)argv0test.out \
+-		 $(objpfx)tst-rtld-help.out
++tests-special += \
++  $(objpfx)argv0test.out \
++  $(objpfx)tst-pathopt.out \
++  $(objpfx)tst-rtld-help.out \
++  $(objpfx)tst-rtld-load-self.out \
++  $(objpfx)tst-rtld-preload.out \
++  # tests-special
+ endif
+-tests-special += $(objpfx)check-textrel.out $(objpfx)check-execstack.out \
+-		 $(objpfx)check-wx-segment.out \
+-		 $(objpfx)check-localplt.out $(objpfx)check-initfini.out
++tests-special += \
++  $(objpfx)check-execstack.out \
++  $(objpfx)check-initfini.out \
++  $(objpfx)check-localplt.out \
++  $(objpfx)check-textrel.out \
++  $(objpfx)check-wx-segment.out \
++  # tests-special
+ endif
+ 
+ ifeq ($(run-built-tests),yes)
+-tests-special += $(objpfx)order-cmp.out $(objpfx)tst-array1-cmp.out \
+-		 $(objpfx)tst-array1-static-cmp.out \
+-		 $(objpfx)tst-array2-cmp.out $(objpfx)tst-array3-cmp.out \
+-		 $(objpfx)tst-array4-cmp.out $(objpfx)tst-array5-cmp.out \
+-		 $(objpfx)tst-array5-static-cmp.out $(objpfx)order2-cmp.out \
+-		 $(objpfx)tst-initorder-cmp.out \
+-		 $(objpfx)tst-initorder2-cmp.out $(objpfx)tst-unused-dep.out \
+-		 $(objpfx)tst-unused-dep-cmp.out
++tests-special += \
++  $(objpfx)order-cmp.out \
++  $(objpfx)order2-cmp.out \
++  $(objpfx)tst-array1-cmp.out \
++  $(objpfx)tst-array1-static-cmp.out \
++  $(objpfx)tst-array2-cmp.out \
++  $(objpfx)tst-array3-cmp.out \
++  $(objpfx)tst-array4-cmp.out \
++  $(objpfx)tst-array5-cmp.out \
++  $(objpfx)tst-array5-static-cmp.out \
++  $(objpfx)tst-initorder-cmp.out \
++  $(objpfx)tst-initorder2-cmp.out \
++  $(objpfx)tst-unused-dep-cmp.out \
++  $(objpfx)tst-unused-dep.out \
++  # tests-special
+ endif
+ 
+ check-abi: $(objpfx)check-abi-ld.out
+@@ -807,6 +1006,7 @@ rtld-stubbed-symbols = \
+   free \
+   malloc \
+   realloc \
++  # rtld-stubbed-symbols
+ 
+ # The GCC arguments that implement $(rtld-stubbed-symbols).
+ rtld-stubbed-symbols-args = \
diff --git a/SOURCES/glibc-rh2047981-31.patch b/SOURCES/glibc-rh2047981-31.patch
new file mode 100644
index 0000000000000000000000000000000000000000..48de026f8f19a13a04a0336b74a8dd1452ab9731
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-31.patch
@@ -0,0 +1,440 @@
+Added $(objpfx)tst-audit23: $(libdl) to elf/Makefile since
+we still need $(libdl) in RHEL8.
+
+commit 5fa11a2bc94c912c3b25860065086902674537ba
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Mon Jan 24 10:46:15 2022 -0300
+
+    elf: Add la_activity during application exit
+    
+    la_activity is not called during application exit, even though
+    la_objclose is.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+    Tested-by: Carlos O'Donell <carlos@redhat.com>
+
+Conflicts:
+	elf/Makefile
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 954cd08c199f5037..e4955c9f575f9015 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -345,6 +345,7 @@ tests += \
+   tst-audit2 \
+   tst-audit20 \
+   tst-audit22 \
++  tst-audit23 \
+   tst-audit8 \
+   tst-audit9 \
+   tst-auditmany \
+@@ -608,6 +609,7 @@ modules-names = \
+   tst-audit13mod1 \
+   tst-audit18mod \
+   tst-audit19bmod \
++  tst-audit23mod \
+   tst-auditlogmod-1 \
+   tst-auditlogmod-2 \
+   tst-auditlogmod-3 \
+@@ -630,6 +632,7 @@ modules-names = \
+   tst-auditmod19b \
+   tst-auditmod20 \
+   tst-auditmod22 \
++  tst-auditmod23 \
+   tst-big-note-lib \
+   tst-deep1mod1 \
+   tst-deep1mod2 \
+@@ -2041,6 +2044,11 @@ $(objpfx)tst-auditmod20.so: $(libdl)
+ $(objpfx)tst-audit22.out: $(objpfx)tst-auditmod22.so
+ tst-audit22-ARGS = -- $(host-test-program-cmd)
+ 
++$(objpfx)tst-audit23: $(libdl)
++$(objpfx)tst-audit23.out: $(objpfx)tst-auditmod23.so \
++			  $(objpfx)tst-audit23mod.so
++tst-audit23-ARGS = -- $(host-test-program-cmd)
++
+ # tst-sonamemove links against an older implementation of the library.
+ LDFLAGS-tst-sonamemove-linkmod1.so = \
+   -Wl,--version-script=tst-sonamemove-linkmod1.map \
+diff --git a/elf/dl-fini.c b/elf/dl-fini.c
+index e102d93647cb8c47..eea9d8aad736a99e 100644
+--- a/elf/dl-fini.c
++++ b/elf/dl-fini.c
+@@ -63,6 +63,10 @@ _dl_fini (void)
+ 	__rtld_lock_unlock_recursive (GL(dl_load_lock));
+       else
+ 	{
++#ifdef SHARED
++	  _dl_audit_activity_nsid (ns, LA_ACT_DELETE);
++#endif
++
+ 	  /* Now we can allocate an array to hold all the pointers and
+ 	     copy the pointers in.  */
+ 	  struct link_map *maps[nloaded];
+@@ -153,6 +157,10 @@ _dl_fini (void)
+ 	      /* Correct the previous increment.  */
+ 	      --l->l_direct_opencount;
+ 	    }
++
++#ifdef SHARED
++	  _dl_audit_activity_nsid (ns, LA_ACT_CONSISTENT);
++#endif
+ 	}
+     }
+ 
+diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c
+new file mode 100644
+index 0000000000000000..4904cf1340a97ee1
+--- /dev/null
++++ b/elf/tst-audit23.c
+@@ -0,0 +1,239 @@
++/* Check for expected la_objopen and la_objeclose for all objects.
++   Copyright (C) 2022 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/>.  */
++
++#include <array_length.h>
++#include <errno.h>
++#include <getopt.h>
++#include <link.h>
++#include <limits.h>
++#include <inttypes.h>
++#include <gnu/lib-names.h>
++#include <string.h>
++#include <stdlib.h>
++#include <support/capture_subprocess.h>
++#include <support/check.h>
++#include <support/xstdio.h>
++#include <support/xdlfcn.h>
++#include <support/support.h>
++
++static int restart;
++#define CMDLINE_OPTIONS \
++  { "restart", no_argument, &restart, 1 },
++
++static int
++handle_restart (void)
++{
++  xdlopen ("tst-audit23mod.so", RTLD_NOW);
++  xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
++
++  return 0;
++}
++
++static inline bool
++startswith (const char *str, const char *pre)
++{
++  size_t lenpre = strlen (pre);
++  size_t lenstr = strlen (str);
++  return lenstr >= lenpre && memcmp (pre, str, lenpre) == 0;
++}
++
++static inline bool
++is_vdso (const char *str)
++{
++  return startswith (str, "linux-gate")
++	 || startswith (str, "linux-vdso");
++}
++
++static int
++do_test (int argc, char *argv[])
++{
++  /* We must have either:
++     - One or four parameters left if called initially:
++       + path to ld.so         optional
++       + "--library-path"      optional
++       + the library path      optional
++       + the application name  */
++  if (restart)
++    return handle_restart ();
++
++  char *spargv[9];
++  TEST_VERIFY_EXIT (((argc - 1) + 3) < array_length (spargv));
++  int i = 0;
++  for (; i < argc - 1; i++)
++    spargv[i] = argv[i + 1];
++  spargv[i++] = (char *) "--direct";
++  spargv[i++] = (char *) "--restart";
++  spargv[i] = NULL;
++
++  setenv ("LD_AUDIT", "tst-auditmod23.so", 0);
++  struct support_capture_subprocess result
++    = support_capture_subprogram (spargv[0], spargv);
++  support_capture_subprocess_check (&result, "tst-audit22", 0, sc_allow_stderr);
++
++  /* The expected la_objopen/la_objclose:
++     1. executable
++     2. loader
++     3. libc.so
++     4. tst-audit23mod.so
++     5. libc.so (LM_ID_NEWLM).
++     6. vdso (optional and ignored).  */
++  enum { max_objs = 6 };
++  struct la_obj_t
++  {
++    char *lname;
++    uintptr_t laddr;
++    Lmid_t lmid;
++    bool closed;
++  } objs[max_objs] = { [0 ... max_objs-1] = { .closed = false } };
++  size_t nobjs = 0;
++
++  /* The expected namespaces are one for the audit module, one for the
++     application, and another for the dlmopen on handle_restart.  */
++  enum { max_ns = 3 };
++  uintptr_t acts[max_ns] = { 0 };
++  size_t nacts = 0;
++  int last_act = -1;
++  uintptr_t last_act_cookie = -1;
++  bool seen_first_objclose = false;
++
++  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
++  TEST_VERIFY (out != NULL);
++  char *buffer = NULL;
++  size_t buffer_length = 0;
++  while (xgetline (&buffer, &buffer_length, out))
++    {
++      if (startswith (buffer, "la_activity: "))
++	{
++	  uintptr_t cookie;
++	  int this_act;
++	  int r = sscanf (buffer, "la_activity: %d %"SCNxPTR"", &this_act,
++			  &cookie);
++	  TEST_COMPARE (r, 2);
++
++	  /* The cookie identifies the object at the head of the link map,
++	     so we only add a new namespace if it changes from the previous
++	     one.  This works since dlmopen is the last in the test body.  */
++	  if (cookie != last_act_cookie && last_act_cookie != -1)
++	    TEST_COMPARE (last_act, LA_ACT_CONSISTENT);
++
++	  if (this_act == LA_ACT_ADD && acts[nacts] != cookie)
++	    {
++	      acts[nacts++] = cookie;
++	      last_act_cookie = cookie;
++	    }
++	  /* The LA_ACT_DELETE is called in the reverse order of LA_ACT_ADD
++	     at program termination (if the tests adds a dlclose or a library
++	     with extra dependencies this will need to be adapted).  */
++	  else if (this_act == LA_ACT_DELETE)
++	    {
++	      last_act_cookie = acts[--nacts];
++	      TEST_COMPARE (acts[nacts], cookie);
++	      acts[nacts] = 0;
++	    }
++	  else if (this_act == LA_ACT_CONSISTENT)
++	    {
++	      TEST_COMPARE (cookie, last_act_cookie);
++
++	      /* LA_ACT_DELETE must always be followed by an la_objclose.  */
++	      if (last_act == LA_ACT_DELETE)
++		TEST_COMPARE (seen_first_objclose, true);
++	      else
++		TEST_COMPARE (last_act, LA_ACT_ADD);
++	    }
++
++	  last_act = this_act;
++	  seen_first_objclose = false;
++	}
++      else if (startswith (buffer, "la_objopen: "))
++	{
++	  char *lname;
++	  uintptr_t laddr;
++	  Lmid_t lmid;
++	  uintptr_t cookie;
++	  int r = sscanf (buffer, "la_objopen: %"SCNxPTR"  %ms %"SCNxPTR" %ld",
++			  &cookie, &lname, &laddr, &lmid);
++	  TEST_COMPARE (r, 4);
++
++	  /* la_objclose is not triggered by vDSO because glibc does not
++	     unload it.  */
++	  if (is_vdso (lname))
++	    continue;
++	  if (nobjs == max_objs)
++	    FAIL_EXIT1 ("non expected la_objopen: %s %"PRIxPTR" %ld",
++			lname, laddr, lmid);
++	  objs[nobjs].lname = lname;
++	  objs[nobjs].laddr = laddr;
++	  objs[nobjs].lmid = lmid;
++	  objs[nobjs].closed = false;
++	  nobjs++;
++
++	  /* This indirectly checks that la_objopen always comes before
++	     la_objclose btween la_activity calls.  */
++	  seen_first_objclose = false;
++	}
++      else if (startswith (buffer, "la_objclose: "))
++	{
++	  char *lname;
++	  uintptr_t laddr;
++	  Lmid_t lmid;
++	  uintptr_t cookie;
++	  int r = sscanf (buffer, "la_objclose: %"SCNxPTR" %ms %"SCNxPTR" %ld",
++			  &cookie, &lname, &laddr, &lmid);
++	  TEST_COMPARE (r, 4);
++
++	  for (size_t i = 0; i < nobjs; i++)
++	    {
++	      if (strcmp (lname, objs[i].lname) == 0 && lmid == objs[i].lmid)
++		{
++		  TEST_COMPARE (objs[i].closed, false);
++		  objs[i].closed = true;
++		  break;
++		}
++	    }
++
++	  /* la_objclose should be called after la_activity(LA_ACT_DELETE) for
++	     the closed object's namespace.  */
++	  TEST_COMPARE (last_act, LA_ACT_DELETE);
++	  if (!seen_first_objclose)
++	    {
++	      TEST_COMPARE (last_act_cookie, cookie);
++	      seen_first_objclose = true;
++	    }
++	}
++    }
++
++  for (size_t i = 0; i < nobjs; i++)
++    {
++      TEST_COMPARE (objs[i].closed, true);
++      free (objs[i].lname);
++    }
++
++  /* la_activity(LA_ACT_CONSISTENT) should be the last callback received.
++     Since only one link map may be not-CONSISTENT at a time, this also
++     ensures la_activity(LA_ACT_CONSISTENT) is the last callback received
++     for every namespace.  */
++  TEST_COMPARE (last_act, LA_ACT_CONSISTENT);
++
++  free (buffer);
++  xfclose (out);
++
++  return 0;
++}
++
++#define TEST_FUNCTION_ARGV do_test
++#include <support/test-driver.c>
+diff --git a/elf/tst-audit23mod.c b/elf/tst-audit23mod.c
+new file mode 100644
+index 0000000000000000..30315687037d25e8
+--- /dev/null
++++ b/elf/tst-audit23mod.c
+@@ -0,0 +1,23 @@
++/* Extra module for tst-audit23
++   Copyright (C) 2022 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
++foo (void)
++{
++  return 0;
++}
+diff --git a/elf/tst-auditmod23.c b/elf/tst-auditmod23.c
+new file mode 100644
+index 0000000000000000..d7c60d7a5cbc4f8a
+--- /dev/null
++++ b/elf/tst-auditmod23.c
+@@ -0,0 +1,74 @@
++/* Audit module loaded by tst-audit23.
++   Copyright (C) 2022 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/>.  */
++
++#include <link.h>
++#include <inttypes.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/auxv.h>
++
++unsigned int
++la_version (unsigned int version)
++{
++  return LAV_CURRENT;
++}
++
++struct map_desc_t
++{
++  char *lname;
++  uintptr_t laddr;
++  Lmid_t lmid;
++};
++
++void
++la_activity (uintptr_t *cookie, unsigned int flag)
++{
++  fprintf (stderr, "%s: %d %"PRIxPTR"\n", __func__, flag, (uintptr_t) cookie);
++}
++
++unsigned int
++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
++{
++  const char *l_name = map->l_name[0] == '\0' ? "mainapp" : map->l_name;
++  fprintf (stderr, "%s: %"PRIxPTR" %s %"PRIxPTR" %ld\n", __func__,
++	   (uintptr_t) cookie, l_name, map->l_addr, lmid);
++
++  struct map_desc_t *map_desc = malloc (sizeof (struct map_desc_t));
++  if (map_desc == NULL)
++    abort ();
++
++  map_desc->lname = strdup (l_name);
++  map_desc->laddr = map->l_addr;
++  map_desc->lmid = lmid;
++
++  *cookie = (uintptr_t) map_desc;
++
++  return 0;
++}
++
++unsigned int
++la_objclose (uintptr_t *cookie)
++{
++  struct map_desc_t *map_desc = (struct map_desc_t *) *cookie;
++  fprintf (stderr, "%s: %"PRIxPTR" %s %"PRIxPTR" %ld\n", __func__,
++	   (uintptr_t) cookie, map_desc->lname, map_desc->laddr,
++	   map_desc->lmid);
++
++  return 0;
++}
diff --git a/SOURCES/glibc-rh2047981-32.patch b/SOURCES/glibc-rh2047981-32.patch
new file mode 100644
index 0000000000000000000000000000000000000000..2706fe8caad948402fa4cccf88d3c2215a020065
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-32.patch
@@ -0,0 +1,298 @@
+commit 254d3d5aef2fd8430c469e1938209ac100ebf132
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Mon Jan 24 10:46:16 2022 -0300
+
+    elf: Fix initial-exec TLS access on audit modules (BZ #28096)
+    
+    For audit modules and dependencies with initial-exec TLS, we can not
+    set the initial TLS image on default loader initialization because it
+    would already be set by the audit setup.  However, subsequent thread
+    creation would need to follow the default behaviour.
+    
+    This patch fixes it by setting l_auditing link_map field not only
+    for the audit modules, but also for all its dependencies.  This is
+    used on _dl_allocate_tls_init to avoid the static TLS initialization
+    at load time.
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
+    
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+    Tested-by: Carlos O'Donell <carlos@redhat.com>
+
+diff --git a/elf/Makefile b/elf/Makefile
+index e4955c9f575f9015..3f5f72257a5fbea4 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -344,6 +344,7 @@ tests += \
+   tst-audit19b \
+   tst-audit2 \
+   tst-audit20 \
++  tst-audit21 \
+   tst-audit22 \
+   tst-audit23 \
+   tst-audit8 \
+@@ -631,6 +632,8 @@ modules-names = \
+   tst-auditmod19a \
+   tst-auditmod19b \
+   tst-auditmod20 \
++  tst-auditmod21a \
++  tst-auditmod21b \
+   tst-auditmod22 \
+   tst-auditmod23 \
+   tst-big-note-lib \
+@@ -2041,6 +2044,11 @@ $(objpfx)tst-audit20.out: $(objpfx)tst-auditmod20.so
+ tst-audit20-ENV = LD_AUDIT=$(objpfx)tst-auditmod20.so
+ $(objpfx)tst-auditmod20.so: $(libdl)
+ 
++$(objpfx)tst-audit21: $(shared-thread-library)
++$(objpfx)tst-audit21.out: $(objpfx)tst-auditmod21a.so
++$(objpfx)tst-auditmod21a.so: $(objpfx)tst-auditmod21b.so
++tst-audit21-ENV = LD_AUDIT=$(objpfx)tst-auditmod21a.so
++
+ $(objpfx)tst-audit22.out: $(objpfx)tst-auditmod22.so
+ tst-audit22-ARGS = -- $(host-test-program-cmd)
+ 
+diff --git a/elf/dl-tls.c b/elf/dl-tls.c
+index 7865fc390c3f3f0a..a918e9a6f585eb72 100644
+--- a/elf/dl-tls.c
++++ b/elf/dl-tls.c
+@@ -514,8 +514,12 @@ _dl_resize_dtv (dtv_t *dtv, size_t max_modid)
+ }
+ 
+ 
++/* Allocate initial TLS.  RESULT should be a non-NULL pointer to storage
++   for the TLS space.  The DTV may be resized, and so this function may
++   call malloc to allocate that space.  The loader's GL(dl_load_tls_lock)
++   is taken when manipulating global TLS-related data in the loader.  */
+ void *
+-_dl_allocate_tls_init (void *result)
++_dl_allocate_tls_init (void *result, bool init_tls)
+ {
+   if (result == NULL)
+     /* The memory allocation failed.  */
+@@ -588,7 +592,14 @@ _dl_allocate_tls_init (void *result)
+ 	     some platforms use in static programs requires it.  */
+ 	  dtv[map->l_tls_modid].pointer.val = dest;
+ 
+-	  /* Copy the initialization image and clear the BSS part.  */
++	  /* Copy the initialization image and clear the BSS part.  For
++	     audit modules or dependencies with initial-exec TLS, we can not
++	     set the initial TLS image on default loader initialization
++	     because it would already be set by the audit setup.  However,
++	     subsequent thread creation would need to follow the default
++	     behaviour.   */
++	  if (map->l_ns != LM_ID_BASE && !init_tls)
++	    continue;
+ 	  memset (__mempcpy (dest, map->l_tls_initimage,
+ 			     map->l_tls_initimage_size), '\0',
+ 		  map->l_tls_blocksize - map->l_tls_initimage_size);
+@@ -615,7 +626,7 @@ _dl_allocate_tls (void *mem)
+ {
+   return _dl_allocate_tls_init (mem == NULL
+ 				? _dl_allocate_tls_storage ()
+-				: allocate_dtv (mem));
++				: allocate_dtv (mem), true);
+ }
+ rtld_hidden_def (_dl_allocate_tls)
+ 
+diff --git a/elf/rtld.c b/elf/rtld.c
+index efcbeac6c24c4b7b..caa980dbda3d1a72 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -2421,7 +2421,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
+      into the main thread's TLS area, which we allocated above.
+      Note: thread-local variables must only be accessed after completing
+      the next step.  */
+-  _dl_allocate_tls_init (tcbp);
++  _dl_allocate_tls_init (tcbp, false);
+ 
+   /* And finally install it for the main thread.  */
+   if (! tls_init_tp_called)
+diff --git a/elf/tst-audit21.c b/elf/tst-audit21.c
+new file mode 100644
+index 0000000000000000..3a47ab64d44421ee
+--- /dev/null
++++ b/elf/tst-audit21.c
+@@ -0,0 +1,42 @@
++/* Check LD_AUDIT with static TLS.
++   Copyright (C) 2022 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/>.  */
++
++#include <ctype.h>
++#include <support/xthread.h>
++#include <support/check.h>
++
++static volatile __thread int out __attribute__ ((tls_model ("initial-exec")));
++
++static void *
++tf (void *arg)
++{
++  TEST_COMPARE (out, 0);
++  out = isspace (' ');
++  return NULL;
++}
++
++int main (int argc, char *argv[])
++{
++  TEST_COMPARE (out, 0);
++  out = isspace (' ');
++
++  pthread_t t = xpthread_create (NULL, tf, NULL);
++  xpthread_join (t);
++
++  return 0;
++}
+diff --git a/elf/tst-auditmod21a.c b/elf/tst-auditmod21a.c
+new file mode 100644
+index 0000000000000000..f6d51b5c0531c49d
+--- /dev/null
++++ b/elf/tst-auditmod21a.c
+@@ -0,0 +1,80 @@
++/* Check LD_AUDIT with static TLS.
++   Copyright (C) 2022 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/>.  */
++
++#include <ctype.h>
++#include <stdlib.h>
++#include <link.h>
++
++#define tls_ie __attribute__ ((tls_model ("initial-exec")))
++
++__thread int tls_var0 tls_ie;
++__thread int tls_var1 tls_ie = 0x10;
++
++/* Defined at tst-auditmod21b.so  */
++extern __thread int tls_var2;
++extern __thread int tls_var3;
++
++static volatile int out;
++
++static void
++call_libc (void)
++{
++  /* isspace accesses the initial-exec glibc TLS variables, which are
++     setup in glibc initialization.  */
++  out = isspace (' ');
++}
++
++unsigned int
++la_version (unsigned int v)
++{
++  tls_var0 = 0x1;
++  if (tls_var1 != 0x10)
++    abort ();
++  tls_var1 = 0x20;
++
++  tls_var2 = 0x2;
++  if (tls_var3 != 0x20)
++    abort ();
++  tls_var3 = 0x40;
++
++  call_libc ();
++
++  return LAV_CURRENT;
++}
++
++unsigned int
++la_objopen (struct link_map* map, Lmid_t lmid, uintptr_t* cookie)
++{
++  call_libc ();
++  *cookie = (uintptr_t) map;
++  return 0;
++}
++
++void
++la_activity (uintptr_t* cookie, unsigned int flag)
++{
++  if (tls_var0 != 0x1 || tls_var1 != 0x20)
++    abort ();
++  call_libc ();
++}
++
++void
++la_preinit (uintptr_t* cookie)
++{
++  call_libc ();
++}
+diff --git a/elf/tst-auditmod21b.c b/elf/tst-auditmod21b.c
+new file mode 100644
+index 0000000000000000..6ba5335b7514c674
+--- /dev/null
++++ b/elf/tst-auditmod21b.c
+@@ -0,0 +1,22 @@
++/* Check LD_AUDIT with static TLS.
++   Copyright (C) 2022 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/>.  */
++
++#define tls_ie __attribute__ ((tls_model ("initial-exec")))
++
++__thread int tls_var2 tls_ie;
++__thread int tls_var3 tls_ie = 0x20;
+diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
+index 5fa45b19987717e1..58170d9da2bf0fa6 100644
+--- a/nptl/allocatestack.c
++++ b/nptl/allocatestack.c
+@@ -244,7 +244,7 @@ get_cached_stack (size_t *sizep, void **memp)
+   memset (dtv, '\0', (dtv[-1].counter + 1) * sizeof (dtv_t));
+ 
+   /* Re-initialize the TLS.  */
+-  _dl_allocate_tls_init (TLS_TPADJ (result));
++  _dl_allocate_tls_init (TLS_TPADJ (result), true);
+ 
+   return result;
+ }
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index 29b77b35175c1116..73f4863fd43922b9 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -1182,7 +1182,7 @@ extern void _dl_allocate_static_tls (struct link_map *map) attribute_hidden;
+ /* These are internal entry points to the two halves of _dl_allocate_tls,
+    only used within rtld.c itself at startup time.  */
+ extern void *_dl_allocate_tls_storage (void) attribute_hidden;
+-extern void *_dl_allocate_tls_init (void *);
++extern void *_dl_allocate_tls_init (void *, bool);
+ rtld_hidden_proto (_dl_allocate_tls_init)
+ 
+ /* Deallocate memory allocated with _dl_allocate_tls.  */
diff --git a/SOURCES/glibc-rh2047981-33.patch b/SOURCES/glibc-rh2047981-33.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6ce117c3ea1f02c5391a5c53e7f54b67d2bccd37
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-33.patch
@@ -0,0 +1,1777 @@
+commit 32612615c58b394c3eb09f020f31310797ad3854
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Mon Jan 24 10:46:17 2022 -0300
+
+    elf: Issue la_symbind for bind-now (BZ #23734)
+    
+    The audit symbind callback is not called for binaries built with
+    -Wl,-z,now or when LD_BIND_NOW=1 is used, nor the PLT tracking callbacks
+    (plt_enter and plt_exit) since this would change the expected
+    program semantics (where no PLT is expected) and would have performance
+    implications (such as for BZ#15533).
+    
+    LAV_CURRENT is also bumped to indicate the audit ABI change (where
+    la_symbind flags are set by the loader to indicate no possible PLT
+    trace).
+    
+    To handle powerpc64 ELFv1 function descriptor, _dl_audit_symbind
+    requires to know whether bind-now is used so the symbol value is
+    updated to function text segment instead of the OPD (for lazy binding
+    this is done by PPC64_LOAD_FUNCPTR on _dl_runtime_resolve).
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, aarch64-linux-gnu,
+    powerpc64-linux-gnu.
+    
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+    Tested-by: Carlos O'Donell <carlos@redhat.com>
+
+Conflicts:
+	elf/Makefile
+
+diff --git a/bits/link_lavcurrent.h b/bits/link_lavcurrent.h
+index 44fbea1e8060997f..c48835d12b512355 100644
+--- a/bits/link_lavcurrent.h
++++ b/bits/link_lavcurrent.h
+@@ -22,4 +22,4 @@
+ #endif
+ 
+ /* Version numbers for la_version handshake interface.  */
+-#define LAV_CURRENT	1
++#define LAV_CURRENT	2
+diff --git a/elf/Makefile b/elf/Makefile
+index 3f5f72257a5fbea4..78147ed2dbcaf4c0 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -347,6 +347,12 @@ tests += \
+   tst-audit21 \
+   tst-audit22 \
+   tst-audit23 \
++  tst-audit24a \
++  tst-audit24b \
++  tst-audit24c \
++  tst-audit24d \
++  tst-audit25a \
++  tst-audit25b \
+   tst-audit8 \
+   tst-audit9 \
+   tst-auditmany \
+@@ -611,6 +617,18 @@ modules-names = \
+   tst-audit18mod \
+   tst-audit19bmod \
+   tst-audit23mod \
++  tst-audit24amod1 \
++  tst-audit24amod2 \
++  tst-audit24bmod1 \
++  tst-audit24bmod2 \
++  tst-audit24dmod1 \
++  tst-audit24dmod2 \
++  tst-audit24dmod3 \
++  tst-audit24dmod4 \
++  tst-audit25mod1 \
++  tst-audit25mod2 \
++  tst-audit25mod3 \
++  tst-audit25mod4 \
+   tst-auditlogmod-1 \
+   tst-auditlogmod-2 \
+   tst-auditlogmod-3 \
+@@ -636,6 +654,11 @@ modules-names = \
+   tst-auditmod21b \
+   tst-auditmod22 \
+   tst-auditmod23 \
++  tst-auditmod24a \
++  tst-auditmod24b \
++  tst-auditmod24c \
++  tst-auditmod24d \
++  tst-auditmod25 \
+   tst-big-note-lib \
+   tst-deep1mod1 \
+   tst-deep1mod2 \
+@@ -831,7 +854,8 @@ modules-execstack-yes = tst-execstack-mod
+ extra-test-objs += $(addsuffix .os,$(strip $(modules-names)))
+ 
+ # filtmod1.so has a special rule
+-modules-names-nobuild := filtmod1
++modules-names-nobuild := filtmod1 \
++			 tst-audit24bmod1 tst-audit24bmod2.so
+ 
+ tests += $(tests-static)
+ 
+@@ -2057,6 +2081,69 @@ $(objpfx)tst-audit23.out: $(objpfx)tst-auditmod23.so \
+ 			  $(objpfx)tst-audit23mod.so
+ tst-audit23-ARGS = -- $(host-test-program-cmd)
+ 
++$(objpfx)tst-audit24a.out: $(objpfx)tst-auditmod24a.so
++$(objpfx)tst-audit24a: $(objpfx)tst-audit24amod1.so \
++		       $(objpfx)tst-audit24amod2.so
++tst-audit24a-ENV = LD_AUDIT=$(objpfx)tst-auditmod24a.so
++LDFLAGS-tst-audit24a = -Wl,-z,now
++
++$(objpfx)tst-audit24b.out: $(objpfx)tst-auditmod24b.so
++$(objpfx)tst-audit24b: $(objpfx)tst-audit24bmod1.so \
++		       $(objpfx)tst-audit24bmod2.so
++$(objpfx)tst-audit24bmod1: $(objpfx)tst-audit24bmod2.so
++# The test checks if a library without .gnu.version correctly calls the
++# audit callbacks.  So it uses an explicit link rule to avoid linking
++# against libc.so.
++$(objpfx)tst-audit24bmod1.so: $(objpfx)tst-audit24bmod1.os
++	$(CC) -nostdlib -nostartfiles -shared -o $@.new $(objpfx)tst-audit24bmod1.os \
++	  -Wl,-z,now
++	$(call after-link,$@.new)
++	mv -f $@.new $@
++CFLAGS-.os += $(call elide-stack-protector,.os,tst-audit24bmod1)
++$(objpfx)tst-audit24bmod2.so: $(objpfx)tst-audit24bmod2.os
++	$(CC) -nostdlib -nostartfiles -shared -o $@.new $(objpfx)tst-audit24bmod2.os
++	$(call after-link,$@.new)
++	mv -f $@.new $@
++CFLAGS-.os += $(call elide-stack-protector,.os,tst-audit24bmod2)
++tst-audit24b-ENV = LD_AUDIT=$(objpfx)tst-auditmod24b.so
++LDFLAGS-tst-audit24b = -Wl,-z,now
++
++# Same as tst-audit24a, but tests LD_BIND_NOW
++$(objpfx)tst-audit24c.out: $(objpfx)tst-auditmod24c.so
++$(objpfx)tst-audit24c: $(objpfx)tst-audit24amod1.so \
++		       $(objpfx)tst-audit24amod2.so
++tst-audit24c-ENV = LD_BIND_NOW=1 LD_AUDIT=$(objpfx)tst-auditmod24c.so
++LDFLAGS-tst-audit24b = -Wl,-z,lazy
++
++$(objpfx)tst-audit24d.out: $(objpfx)tst-auditmod24d.so
++$(objpfx)tst-audit24d: $(objpfx)tst-audit24dmod1.so \
++		       $(objpfx)tst-audit24dmod2.so
++$(objpfx)tst-audit24dmod1.so: $(objpfx)tst-audit24dmod3.so
++LDFLAGS-tst-audit24dmod1.so = -Wl,-z,now
++$(objpfx)tst-audit24dmod2.so: $(objpfx)tst-audit24dmod4.so
++LDFLAGS-tst-audit24dmod2.so = -Wl,-z,lazy
++tst-audit24d-ENV = LD_AUDIT=$(objpfx)tst-auditmod24d.so
++LDFLAGS-tst-audit24d = -Wl,-z,lazy
++
++$(objpfx)tst-audit25a.out: $(objpfx)tst-auditmod25.so
++$(objpfx)tst-audit25a: $(objpfx)tst-audit25mod1.so \
++		       $(objpfx)tst-audit25mod2.so \
++		       $(objpfx)tst-audit25mod3.so \
++		       $(objpfx)tst-audit25mod4.so
++$(objpfx)tst-audit25mod1.so: $(objpfx)tst-audit25mod3.so
++LDFLAGS-tst-audit25mod1.so = -Wl,-z,now
++$(objpfx)tst-audit25mod2.so: $(objpfx)tst-audit25mod4.so
++LDFLAGS-tst-audit25mod2.so = -Wl,-z,lazy
++tst-audit25a-ARGS = -- $(host-test-program-cmd)
++
++$(objpfx)tst-audit25b.out: $(objpfx)tst-auditmod25.so
++$(objpfx)tst-audit25b: $(objpfx)tst-audit25mod1.so \
++		       $(objpfx)tst-audit25mod2.so \
++		       $(objpfx)tst-audit25mod3.so \
++		       $(objpfx)tst-audit25mod4.so
++LDFLAGS-tst-audit25b = -Wl,-z,now
++tst-audit25b-ARGS = -- $(host-test-program-cmd)
++
+ # tst-sonamemove links against an older implementation of the library.
+ LDFLAGS-tst-sonamemove-linkmod1.so = \
+   -Wl,--version-script=tst-sonamemove-linkmod1.map \
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+index 152712b12fed6de2..72a50717ef60a357 100644
+--- a/elf/dl-audit.c
++++ b/elf/dl-audit.c
+@@ -178,16 +178,23 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
+ 		   const ElfW(Sym) *defsym, DL_FIXUP_VALUE_TYPE *value,
+ 		   lookup_t result)
+ {
+-  reloc_result->bound = result;
+-  /* Compute index of the symbol entry in the symbol table of the DSO with the
+-     definition.  */
+-  reloc_result->boundndx = (defsym - (ElfW(Sym) *) D_PTR (result,
+-							  l_info[DT_SYMTAB]));
++  bool for_jmp_slot = reloc_result == NULL;
++
++  /* Compute index of the symbol entry in the symbol table of the DSO
++     with the definition.  */
++  unsigned int boundndx = defsym - (ElfW(Sym) *) D_PTR (result,
++							l_info[DT_SYMTAB]);
++  if (!for_jmp_slot)
++    {
++      reloc_result->bound = result;
++      reloc_result->boundndx = boundndx;
++    }
+ 
+   if ((l->l_audit_any_plt | result->l_audit_any_plt) == 0)
+     {
+       /* Set all bits since this symbol binding is not interesting.  */
+-      reloc_result->enterexit = (1u << DL_NNS) - 1;
++      if (!for_jmp_slot)
++	reloc_result->enterexit = (1u << DL_NNS) - 1;
+       return;
+     }
+ 
+@@ -199,12 +206,13 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
+      two bits.  */
+   assert (DL_NNS * 2 <= sizeof (reloc_result->flags) * 8);
+   assert ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) == 3);
+-  reloc_result->enterexit = LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT;
++  uint32_t enterexit = LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT;
+ 
+   const char *strtab2 = (const void *) D_PTR (result, l_info[DT_STRTAB]);
+ 
+   unsigned int flags = 0;
+   struct audit_ifaces *afct = GLRO(dl_audit);
++  uintptr_t new_value = (uintptr_t) sym.st_value;
+   for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+     {
+       /* XXX Check whether both DSOs must request action or only one */
+@@ -215,37 +223,41 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
+ 	{
+ 	  if (afct->symbind != NULL)
+ 	    {
+-	      uintptr_t new_value = afct->symbind (&sym,
+-						   reloc_result->boundndx,
+-						   &l_state->cookie,
+-						   &result_state->cookie,
+-						   &flags,
+-						   strtab2 + defsym->st_name);
++	      flags |= for_jmp_slot ? LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT
++				    : 0;
++	      new_value = afct->symbind (&sym, boundndx,
++					 &l_state->cookie,
++					 &result_state->cookie, &flags,
++					 strtab2 + defsym->st_name);
+ 	      if (new_value != (uintptr_t) sym.st_value)
+ 		{
+ 		  flags |= LA_SYMB_ALTVALUE;
+-		  sym.st_value = new_value;
++		  sym.st_value = for_jmp_slot
++		    ? DL_FIXUP_BINDNOW_ADDR_VALUE (new_value) : new_value;
+ 		}
+ 	    }
+ 
+ 	  /* Remember the results for every audit library and store a summary
+ 	     in the first two bits.  */
+-	  reloc_result->enterexit &= flags & (LA_SYMB_NOPLTENTER
+-					      | LA_SYMB_NOPLTEXIT);
+-	  reloc_result->enterexit |= ((flags & (LA_SYMB_NOPLTENTER
+-						| LA_SYMB_NOPLTEXIT))
+-				      << ((cnt + 1) * 2));
++	  enterexit &= flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT);
++	  enterexit |= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT))
++			<< ((cnt + 1) * 2));
+ 	}
+       else
+ 	/* If the bind flags say this auditor is not interested, set the bits
+ 	   manually.  */
+-	reloc_result->enterexit |= ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)
+-				    << ((cnt + 1) * 2));
++	enterexit |= ((LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)
++		      << ((cnt + 1) * 2));
+       afct = afct->next;
+     }
+ 
+-  reloc_result->flags = flags;
+-  *value = DL_FIXUP_ADDR_VALUE (sym.st_value);
++  if (!for_jmp_slot)
++    {
++      reloc_result->enterexit = enterexit;
++      reloc_result->flags = flags;
++    }
++
++  DL_FIXUP_BINDNOW_RELOC (value, new_value, sym.st_value);
+ }
+ 
+ void
+diff --git a/elf/do-rel.h b/elf/do-rel.h
+index 0b04d1a0bf28b9f4..43c80e1c0067d9ca 100644
+--- a/elf/do-rel.h
++++ b/elf/do-rel.h
+@@ -16,6 +16,8 @@
+    License along with the GNU C Library; if not, see
+    <http://www.gnu.org/licenses/>.  */
+ 
++#include <ldsodefs.h>
++
+ /* This file may be included twice, to define both
+    `elf_dynamic_do_rel' and `elf_dynamic_do_rela'.  */
+ 
+@@ -123,6 +125,10 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
+ 
+ 	  for (; r < end; ++r)
+ 	    {
++	      ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
++	      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 && !defined RTLD_BOOTSTRAP
+ 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
+ 		{
+@@ -133,10 +139,19 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
+ 		}
+ #endif
+ 
+-	      ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
+-	      elf_machine_rel (map, scope, r, &symtab[ELFW(R_SYM) (r->r_info)],
+-			       &map->l_versions[ndx],
+-			       (void *) (l_addr + r->r_offset), skip_ifunc);
++	      elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg,
++			       skip_ifunc);
++#if defined SHARED && !defined RTLD_BOOTSTRAP
++	      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, sym, r_addr_arg, sym_map);
++		}
++#endif
+ 	    }
+ 
+ #if defined ELF_MACHINE_IRELATIVE && !defined RTLD_BOOTSTRAP
+@@ -158,17 +173,33 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
+       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);
+ # ifdef ELF_MACHINE_IRELATIVE
+-	    if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
+-	      {
+-		if (r2 == NULL)
+-		  r2 = r;
+-		end2 = r;
+-	      }
+-	    else
++	      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, &symtab[ELFW(R_SYM) (r->r_info)], NULL,
+-			       (void *) (l_addr + r->r_offset), skip_ifunc);
++	      elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg,
++			       skip_ifunc);
++# if defined SHARED && !defined RTLD_BOOTSTRAP
++	      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 , sym,r_addr_arg, sym_map);
++		}
++# endif
++	    }
+ 
+ # ifdef ELF_MACHINE_IRELATIVE
+ 	  if (r2 != NULL)
+diff --git a/elf/sotruss-lib.c b/elf/sotruss-lib.c
+index f0a7e55599d76714..e1ac53f327a7571b 100644
+--- a/elf/sotruss-lib.c
++++ b/elf/sotruss-lib.c
+@@ -17,6 +17,7 @@
+    License along with the GNU C Library; if not, see
+    <http://www.gnu.org/licenses/>.  */
+ 
++#include <err.h>
+ #include <error.h>
+ #include <fcntl.h>
+ #include <stdio.h>
+@@ -232,6 +233,12 @@ uintptr_t
+ la_symbind (Elf_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+ 	    uintptr_t *defcook, unsigned int *flags, const char *symname)
+ {
++  if (*flags & LA_SYMB_NOPLTENTER)
++    warnx ("cannot trace PLT enter (bind-now enabled)");
++
++  if (do_exit && *flags & LA_SYMB_NOPLTEXIT)
++    warnx ("cannot trace PLT exit (bind-now enabled)");
++
+   if (!do_exit)
+     *flags = LA_SYMB_NOPLTEXIT;
+ 
+diff --git a/elf/tst-audit24a.c b/elf/tst-audit24a.c
+new file mode 100644
+index 0000000000000000..a1781c9b45f18fa0
+--- /dev/null
++++ b/elf/tst-audit24a.c
+@@ -0,0 +1,36 @@
++/* LD_AUDIT test for la_symbind and bind-now.
++   Copyright (C) 2022 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/>.  */
++
++#include <support/check.h>
++#include <support/support.h>
++
++int tst_audit24amod1_func1 (void);
++int tst_audit24amod1_func2 (void);
++int tst_audit24amod2_func1 (void);
++
++int
++do_test (void)
++{
++  TEST_COMPARE (tst_audit24amod1_func1 (), 1);
++  TEST_COMPARE (tst_audit24amod1_func2 (), 2);
++  TEST_COMPARE (tst_audit24amod2_func1 (), 10);
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/elf/tst-audit24amod1.c b/elf/tst-audit24amod1.c
+new file mode 100644
+index 0000000000000000..0289a4abefbc7bbb
+--- /dev/null
++++ b/elf/tst-audit24amod1.c
+@@ -0,0 +1,31 @@
++/* Module used by tst-audit24a.
++   Copyright (C) 2022 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/>.  */
++
++#include <stdlib.h>
++
++_Noreturn int
++tst_audit24amod1_func1 (void)
++{
++  abort ();
++}
++
++int
++tst_audit24amod1_func2 (void)
++{
++  return 2;
++}
+diff --git a/elf/tst-audit24amod2.c b/elf/tst-audit24amod2.c
+new file mode 100644
+index 0000000000000000..1562afc9dfc1b9b3
+--- /dev/null
++++ b/elf/tst-audit24amod2.c
+@@ -0,0 +1,25 @@
++/* Module used by tst-audit24a.
++   Copyright (C) 2022 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/>.  */
++
++#include <stdlib.h>
++
++_Noreturn int
++tst_audit24amod2_func1 (void)
++{
++  abort ();
++}
+diff --git a/elf/tst-audit24b.c b/elf/tst-audit24b.c
+new file mode 100644
+index 0000000000000000..567bee52c27f4361
+--- /dev/null
++++ b/elf/tst-audit24b.c
+@@ -0,0 +1,37 @@
++/* LD_AUDIT test for la_symbind and bind-now.
++   Copyright (C) 2022 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/>.  */
++
++/* This is similar to tst-audit24a, with the difference this modules
++   does not have the .gnu.version section header.  */
++
++#include <support/check.h>
++#include <support/support.h>
++
++int tst_audit24bmod1_func1 (void);
++int tst_audit24bmod1_func2 (void);
++
++int
++do_test (void)
++{
++  TEST_COMPARE (tst_audit24bmod1_func1 (), 1);
++  TEST_COMPARE (tst_audit24bmod1_func2 (), 2);
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/elf/tst-audit24bmod1.c b/elf/tst-audit24bmod1.c
+new file mode 100644
+index 0000000000000000..57ce14a01bf72fb6
+--- /dev/null
++++ b/elf/tst-audit24bmod1.c
+@@ -0,0 +1,31 @@
++/* Module used by tst-audit24c.
++   Copyright (C) 2022 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 tst_audit24bmod2_func1 (void);
++
++int
++tst_audit24bmod1_func1 (void)
++{
++  return -1;
++}
++
++int
++tst_audit24bmod1_func2 (void)
++{
++  return tst_audit24bmod2_func1 ();
++}
+diff --git a/elf/tst-audit24bmod2.c b/elf/tst-audit24bmod2.c
+new file mode 100644
+index 0000000000000000..b298ce0a05bf2db2
+--- /dev/null
++++ b/elf/tst-audit24bmod2.c
+@@ -0,0 +1,23 @@
++/* Module used by tst-audit24b.
++   Copyright (C) 2022 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
++tst_audit24bmod2_func1 (void)
++{
++  return -1;
++}
+diff --git a/elf/tst-audit24c.c b/elf/tst-audit24c.c
+new file mode 100644
+index 0000000000000000..46ed328756067276
+--- /dev/null
++++ b/elf/tst-audit24c.c
+@@ -0,0 +1,2 @@
++/* It tests LD_BIND_NOW=1 instead of linking with -Wl,-z,now  */
++#include "tst-audit24a.c"
+diff --git a/elf/tst-audit24d.c b/elf/tst-audit24d.c
+new file mode 100644
+index 0000000000000000..543f3b86a6bbdead
+--- /dev/null
++++ b/elf/tst-audit24d.c
+@@ -0,0 +1,36 @@
++/* LD_AUDIT test for la_symbind and bind-now.
++   Copyright (C) 2022 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/>.  */
++
++#include <support/check.h>
++#include <support/support.h>
++
++int tst_audit24dmod1_func1 (void);
++int tst_audit24dmod1_func2 (void);
++int tst_audit24dmod2_func1 (void);
++
++int
++do_test (void)
++{
++  TEST_COMPARE (tst_audit24dmod1_func1 (), 1);
++  TEST_COMPARE (tst_audit24dmod1_func2 (), 32);
++  TEST_COMPARE (tst_audit24dmod2_func1 (), 10);
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/elf/tst-audit24dmod1.c b/elf/tst-audit24dmod1.c
+new file mode 100644
+index 0000000000000000..e563f69d638ac3f5
+--- /dev/null
++++ b/elf/tst-audit24dmod1.c
+@@ -0,0 +1,33 @@
++/* Module used by tst-audit24d.
++   Copyright (C) 2022 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/>.  */
++
++#include <stdlib.h>
++
++int tst_audit24dmod3_func1 (void);
++
++_Noreturn int
++tst_audit24dmod1_func1 (void)
++{
++  abort ();
++}
++
++int
++tst_audit24dmod1_func2 (void)
++{
++  return 2 + tst_audit24dmod3_func1 ();;
++}
+diff --git a/elf/tst-audit24dmod2.c b/elf/tst-audit24dmod2.c
+new file mode 100644
+index 0000000000000000..03fe9381281e5790
+--- /dev/null
++++ b/elf/tst-audit24dmod2.c
+@@ -0,0 +1,28 @@
++/* Module for tst-audit24d.
++   Copyright (C) 2022 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/>.  */
++
++#include <stdlib.h>
++
++int tst_audit24dmod4_func1 (void);
++
++_Noreturn int
++tst_audit24dmod2_func1 (void)
++{
++  tst_audit24dmod4_func1 ();
++  abort ();
++}
+diff --git a/elf/tst-audit24dmod3.c b/elf/tst-audit24dmod3.c
+new file mode 100644
+index 0000000000000000..106d517d2887d76c
+--- /dev/null
++++ b/elf/tst-audit24dmod3.c
+@@ -0,0 +1,31 @@
++/* Module for tst-audit24d.
++   Copyright (C) 2022 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/>.  */
++
++#include <stdlib.h>
++
++_Noreturn int
++tst_audit24dmod3_func1 (void)
++{
++  abort ();
++}
++
++int
++tst_audit24dmod3_func2 (void)
++{
++  return 4;
++}
+diff --git a/elf/tst-audit24dmod4.c b/elf/tst-audit24dmod4.c
+new file mode 100644
+index 0000000000000000..1da3b46917ba1083
+--- /dev/null
++++ b/elf/tst-audit24dmod4.c
+@@ -0,0 +1,25 @@
++/* Module for tst-audit24d.
++   Copyright (C) 2022 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/>.  */
++
++#include <stdlib.h>
++
++_Noreturn int
++tst_audit24dmod4_func1 (void)
++{
++  abort ();
++}
+diff --git a/elf/tst-audit25a.c b/elf/tst-audit25a.c
+new file mode 100644
+index 0000000000000000..49173e862516e876
+--- /dev/null
++++ b/elf/tst-audit25a.c
+@@ -0,0 +1,129 @@
++/* Check LD_AUDIT and LD_BIND_NOW.
++   Copyright (C) 2022 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/>.  */
++
++#include <array_length.h>
++#include <errno.h>
++#include <getopt.h>
++#include <limits.h>
++#include <inttypes.h>
++#include <string.h>
++#include <stdlib.h>
++#include <support/capture_subprocess.h>
++#include <support/check.h>
++#include <support/xstdio.h>
++#include <support/support.h>
++#include <sys/auxv.h>
++
++static int restart;
++#define CMDLINE_OPTIONS \
++  { "restart", no_argument, &restart, 1 },
++
++void tst_audit25mod1_func1 (void);
++void tst_audit25mod1_func2 (void);
++void tst_audit25mod2_func1 (void);
++void tst_audit25mod2_func2 (void);
++
++static int
++handle_restart (void)
++{
++  tst_audit25mod1_func1 ();
++  tst_audit25mod1_func2 ();
++  tst_audit25mod2_func1 ();
++  tst_audit25mod2_func2 ();
++
++  return 0;
++}
++
++static inline bool
++startswith (const char *str, const char *pre)
++{
++  size_t lenpre = strlen (pre);
++  size_t lenstr = strlen (str);
++  return lenstr < lenpre ? false : memcmp (pre, str, lenpre) == 0;
++}
++
++static int
++do_test (int argc, char *argv[])
++{
++  /* We must have either:
++     - One or four parameters left if called initially:
++       + path to ld.so         optional
++       + "--library-path"      optional
++       + the library path      optional
++       + the application name  */
++
++  if (restart)
++    return handle_restart ();
++
++  setenv ("LD_AUDIT", "tst-auditmod25.so", 0);
++
++  char *spargv[9];
++  int i = 0;
++  for (; i < argc - 1; i++)
++    spargv[i] = argv[i + 1];
++  spargv[i++] = (char *) "--direct";
++  spargv[i++] = (char *) "--restart";
++  spargv[i] = NULL;
++  TEST_VERIFY_EXIT (i < array_length (spargv));
++
++  {
++    struct support_capture_subprocess result
++      = support_capture_subprogram (spargv[0], spargv);
++    support_capture_subprocess_check (&result, "tst-audit25a", 0,
++				      sc_allow_stderr);
++
++    /* tst-audit25a is build with -Wl,-z,lazy and tst-audit25mod1 with
++       -Wl,-z,now; so only tst_audit25mod3_func1 should be expected to
++       have LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT.  */
++    TEST_COMPARE_STRING (result.err.buffer,
++			 "la_symbind: tst_audit25mod3_func1 1\n"
++			 "la_symbind: tst_audit25mod1_func1 0\n"
++			 "la_symbind: tst_audit25mod1_func2 0\n"
++			 "la_symbind: tst_audit25mod2_func1 0\n"
++			 "la_symbind: tst_audit25mod4_func1 0\n"
++			 "la_symbind: tst_audit25mod2_func2 0\n");
++
++    support_capture_subprocess_free (&result);
++  }
++
++  {
++    setenv ("LD_BIND_NOW", "1", 0);
++    struct support_capture_subprocess result
++      = support_capture_subprogram (spargv[0], spargv);
++    support_capture_subprocess_check (&result, "tst-audit25a", 0,
++				      sc_allow_stderr);
++
++    /* With LD_BIND_NOW all symbols are expected to have
++       LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT.  Also the resolution
++       order is done in breadth-first order.  */
++    TEST_COMPARE_STRING (result.err.buffer,
++			 "la_symbind: tst_audit25mod4_func1 1\n"
++			 "la_symbind: tst_audit25mod3_func1 1\n"
++			 "la_symbind: tst_audit25mod1_func1 1\n"
++			 "la_symbind: tst_audit25mod2_func1 1\n"
++			 "la_symbind: tst_audit25mod1_func2 1\n"
++			 "la_symbind: tst_audit25mod2_func2 1\n");
++
++    support_capture_subprocess_free (&result);
++  }
++
++  return 0;
++}
++
++#define TEST_FUNCTION_ARGV do_test
++#include <support/test-driver.c>
+diff --git a/elf/tst-audit25b.c b/elf/tst-audit25b.c
+new file mode 100644
+index 0000000000000000..a56638d501f9bff5
+--- /dev/null
++++ b/elf/tst-audit25b.c
+@@ -0,0 +1,128 @@
++/* Check LD_AUDIT and LD_BIND_NOW.
++   Copyright (C) 2022 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/>.  */
++
++#include <errno.h>
++#include <getopt.h>
++#include <limits.h>
++#include <inttypes.h>
++#include <string.h>
++#include <stdlib.h>
++#include <support/capture_subprocess.h>
++#include <support/check.h>
++#include <support/xstdio.h>
++#include <support/support.h>
++#include <sys/auxv.h>
++
++static int restart;
++#define CMDLINE_OPTIONS \
++  { "restart", no_argument, &restart, 1 },
++
++void tst_audit25mod1_func1 (void);
++void tst_audit25mod1_func2 (void);
++void tst_audit25mod2_func1 (void);
++void tst_audit25mod2_func2 (void);
++
++static int
++handle_restart (void)
++{
++  tst_audit25mod1_func1 ();
++  tst_audit25mod1_func2 ();
++  tst_audit25mod2_func1 ();
++  tst_audit25mod2_func2 ();
++
++  return 0;
++}
++
++static inline bool
++startswith (const char *str, const char *pre)
++{
++  size_t lenpre = strlen (pre);
++  size_t lenstr = strlen (str);
++  return lenstr >= lenpre && memcmp (pre, str, lenpre) == 0;
++}
++
++static int
++do_test (int argc, char *argv[])
++{
++  /* We must have either:
++     - One or four parameters left if called initially:
++       + path to ld.so         optional
++       + "--library-path"      optional
++       + the library path      optional
++       + the application name  */
++
++  if (restart)
++    return handle_restart ();
++
++  setenv ("LD_AUDIT", "tst-auditmod25.so", 0);
++
++  char *spargv[9];
++  int i = 0;
++  for (; i < argc - 1; i++)
++    spargv[i] = argv[i + 1];
++  spargv[i++] = (char *) "--direct";
++  spargv[i++] = (char *) "--restart";
++  spargv[i] = NULL;
++
++  {
++    struct support_capture_subprocess result
++      = support_capture_subprogram (spargv[0], spargv);
++    support_capture_subprocess_check (&result, "tst-audit25a", 0,
++				      sc_allow_stderr);
++
++    /* tst-audit25a and tst-audit25mod1 are built with -Wl,-z,now, but
++       tst-audit25mod2 is built with -Wl,-z,lazy.  So only
++       tst_audit25mod4_func1 (called by tst_audit25mod2_func1) should not
++       have LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT.  */
++    TEST_COMPARE_STRING (result.err.buffer,
++			 "la_symbind: tst_audit25mod3_func1 1\n"
++			 "la_symbind: tst_audit25mod1_func1 1\n"
++			 "la_symbind: tst_audit25mod2_func1 1\n"
++			 "la_symbind: tst_audit25mod1_func2 1\n"
++			 "la_symbind: tst_audit25mod2_func2 1\n"
++			 "la_symbind: tst_audit25mod4_func1 0\n");
++
++    support_capture_subprocess_free (&result);
++  }
++
++  {
++    setenv ("LD_BIND_NOW", "1", 0);
++    struct support_capture_subprocess result
++      = support_capture_subprogram (spargv[0], spargv);
++    support_capture_subprocess_check (&result, "tst-audit25a", 0,
++				      sc_allow_stderr);
++
++    /* With LD_BIND_NOW all symbols are expected to have
++       LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT.  Also the resolution
++       order is done in breadth-first order.  */
++    TEST_COMPARE_STRING (result.err.buffer,
++			 "la_symbind: tst_audit25mod4_func1 1\n"
++			 "la_symbind: tst_audit25mod3_func1 1\n"
++			 "la_symbind: tst_audit25mod1_func1 1\n"
++			 "la_symbind: tst_audit25mod2_func1 1\n"
++			 "la_symbind: tst_audit25mod1_func2 1\n"
++			 "la_symbind: tst_audit25mod2_func2 1\n");
++
++    support_capture_subprocess_free (&result);
++  }
++
++  return 0;
++}
++
++#define TEST_FUNCTION_ARGV do_test
++#include <support/test-driver.c>
+diff --git a/elf/tst-audit25mod1.c b/elf/tst-audit25mod1.c
+new file mode 100644
+index 0000000000000000..a132e34a9b2cf51f
+--- /dev/null
++++ b/elf/tst-audit25mod1.c
+@@ -0,0 +1,30 @@
++/* Module used by tst-audit25.
++   Copyright (C) 2022 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/>.  */
++
++void tst_audit25mod3_func1 (void);
++
++void
++tst_audit25mod1_func1 (void)
++{
++  tst_audit25mod3_func1 ();
++}
++
++void
++tst_audit25mod1_func2 (void)
++{
++}
+diff --git a/elf/tst-audit25mod2.c b/elf/tst-audit25mod2.c
+new file mode 100644
+index 0000000000000000..92da26fa80b202c2
+--- /dev/null
++++ b/elf/tst-audit25mod2.c
+@@ -0,0 +1,30 @@
++/* Module used by tst-audit25.
++   Copyright (C) 2022 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/>.  */
++
++void tst_audit25mod4_func1 (void);
++
++void
++tst_audit25mod2_func1 (void)
++{
++  tst_audit25mod4_func1 ();
++}
++
++void
++tst_audit25mod2_func2 (void)
++{
++}
+diff --git a/elf/tst-audit25mod3.c b/elf/tst-audit25mod3.c
+new file mode 100644
+index 0000000000000000..af83e8919083adef
+--- /dev/null
++++ b/elf/tst-audit25mod3.c
+@@ -0,0 +1,22 @@
++/* Module used by tst-audit25.
++   Copyright (C) 2022 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/>.  */
++
++void
++tst_audit25mod3_func1 (void)
++{
++}
+diff --git a/elf/tst-audit25mod4.c b/elf/tst-audit25mod4.c
+new file mode 100644
+index 0000000000000000..6cdf34357582da16
+--- /dev/null
++++ b/elf/tst-audit25mod4.c
+@@ -0,0 +1,22 @@
++/* Module used by tst-audit25.
++   Copyright (C) 2022 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/>.  */
++
++void
++tst_audit25mod4_func1 (void)
++{
++}
+diff --git a/elf/tst-auditmod24.h b/elf/tst-auditmod24.h
+new file mode 100644
+index 0000000000000000..5fdbfef12dac2b2a
+--- /dev/null
++++ b/elf/tst-auditmod24.h
+@@ -0,0 +1,29 @@
++/* Auxiliary functions for tst-audit24x.
++   Copyright (C) 2022 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/>.  */
++
++#ifndef _TST_AUDITMOD24_H
++#define _TST_AUDITMOD24_H
++
++static void
++test_symbind_flags (unsigned int flags)
++{
++  if ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT)) == 0)
++    abort ();
++}
++
++#endif
+diff --git a/elf/tst-auditmod24a.c b/elf/tst-auditmod24a.c
+new file mode 100644
+index 0000000000000000..d8e88f3984af1707
+--- /dev/null
++++ b/elf/tst-auditmod24a.c
+@@ -0,0 +1,114 @@
++/* Audit modules for tst-audit24a.
++   Copyright (C) 2022 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/>.  */
++
++#include <link.h>
++#include <inttypes.h>
++#include <stdlib.h>
++#include <string.h>
++#include <tst-auditmod24.h>
++
++#define AUDIT24_COOKIE     0x1
++#define AUDIT24MOD1_COOKIE 0x2
++#define AUDIT24MOD2_COOKIE 0x3
++
++#ifndef TEST_NAME
++# define TEST_NAME "tst-audit24a"
++#endif
++#ifndef TEST_MOD
++# define TEST_MOD TEST_NAME
++#endif
++#ifndef TEST_FUNC
++# define TEST_FUNC "tst_audit24a"
++#endif
++
++unsigned int
++la_version (unsigned int version)
++{
++  return LAV_CURRENT;
++}
++
++unsigned int
++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
++{
++  const char *p = strrchr (map->l_name, '/');
++  const char *l_name = p == NULL ? TEST_NAME : p + 1;
++
++  uintptr_t ck = -1;
++  if (strcmp (l_name, TEST_MOD "mod1.so") == 0)
++    ck = AUDIT24MOD1_COOKIE;
++  else if (strcmp (l_name, TEST_MOD "mod2.so") == 0)
++    ck = AUDIT24MOD2_COOKIE;
++  else if (strcmp (l_name, TEST_NAME) == 0)
++    ck = AUDIT24_COOKIE;
++
++  *cookie = ck;
++  return ck == -1 ? 0 : LA_FLG_BINDFROM | LA_FLG_BINDTO;
++}
++
++static int
++tst_func1 (void)
++{
++  return 1;
++}
++
++static int
++tst_func2 (void)
++{
++  return 10;
++}
++
++#if __ELF_NATIVE_CLASS == 64
++uintptr_t
++la_symbind64 (Elf64_Sym *sym, unsigned int ndx,
++	      uintptr_t *refcook, uintptr_t *defcook,
++	      unsigned int *flags, const char *symname)
++#else
++uintptr_t
++la_symbind32 (Elf32_Sym *sym, unsigned int ndx,
++	      uintptr_t *refcook, uintptr_t *defcook,
++	      unsigned int *flags, const char *symname)
++#endif
++{
++  if (*refcook == AUDIT24_COOKIE)
++    {
++      if (*defcook == AUDIT24MOD1_COOKIE)
++	{
++	  /* Check if bind-now symbols are advertised to not call the PLT
++	     hooks.  */
++	  test_symbind_flags (*flags);
++
++	  if (strcmp (symname, TEST_FUNC "mod1_func1") == 0)
++	    return (uintptr_t) tst_func1;
++	  else if (strcmp (symname, TEST_FUNC "mod1_func2") == 0)
++	    return sym->st_value;
++	  abort ();
++	}
++      if (*defcook == AUDIT24MOD2_COOKIE
++	  && (strcmp (symname, TEST_FUNC "mod2_func1") == 0))
++	{
++	  test_symbind_flags (*flags);
++
++	  return (uintptr_t) tst_func2;
++	}
++
++      /* malloc functions.  */
++      return sym->st_value;
++    }
++
++  abort ();
++}
+diff --git a/elf/tst-auditmod24b.c b/elf/tst-auditmod24b.c
+new file mode 100644
+index 0000000000000000..e98f6d5ec528fe03
+--- /dev/null
++++ b/elf/tst-auditmod24b.c
+@@ -0,0 +1,104 @@
++/* Audit modules for tst-audit24b.
++   Copyright (C) 2022 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/>.  */
++
++#include <link.h>
++#include <inttypes.h>
++#include <stdlib.h>
++#include <string.h>
++#include <tst-auditmod24.h>
++
++#define TEST_NAME "tst-audit24b"
++#define TEST_FUNC "tst_audit24b"
++
++#define AUDIT24_COOKIE     0x1
++#define AUDIT24MOD1_COOKIE 0x2
++#define AUDIT24MOD2_COOKIE 0x3
++
++unsigned int
++la_version (unsigned int version)
++{
++  return LAV_CURRENT;
++}
++
++unsigned int
++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
++{
++  const char *p = strrchr (map->l_name, '/');
++  const char *l_name = p == NULL ? TEST_NAME : p + 1;
++
++  uintptr_t ck = -1;
++  if (strcmp (l_name, TEST_NAME "mod1.so") == 0)
++    ck = AUDIT24MOD1_COOKIE;
++  else if (strcmp (l_name, TEST_NAME "mod2.so") == 0)
++    ck = AUDIT24MOD2_COOKIE;
++  else if (strcmp (l_name, TEST_NAME) == 0)
++    ck = AUDIT24_COOKIE;
++
++  *cookie = ck;
++  return ck == -1 ? 0 : LA_FLG_BINDFROM | LA_FLG_BINDTO;
++}
++
++static int
++tst_func1 (void)
++{
++  return 1;
++}
++
++static int
++tst_func2 (void)
++{
++  return 2;
++}
++
++#if __ELF_NATIVE_CLASS == 64
++uintptr_t
++la_symbind64 (Elf64_Sym *sym, unsigned int ndx,
++	      uintptr_t *refcook, uintptr_t *defcook,
++	      unsigned int *flags, const char *symname)
++#else
++uintptr_t
++la_symbind32 (Elf32_Sym *sym, unsigned int ndx,
++	      uintptr_t *refcook, uintptr_t *defcook,
++	      unsigned int *flags, const char *symname)
++#endif
++{
++  if (*refcook == AUDIT24_COOKIE)
++    {
++      if (*defcook == AUDIT24MOD1_COOKIE)
++	  {
++	    if (strcmp (symname, TEST_FUNC "mod1_func1") == 0)
++	      return (uintptr_t) tst_func1;
++	    else if (strcmp (symname, TEST_FUNC "mod1_func2") == 0)
++	      return sym->st_value;
++	    abort ();
++	  }
++      /* malloc functions.  */
++      return sym->st_value;
++    }
++  else if (*refcook == AUDIT24MOD1_COOKIE)
++    {
++      if (*defcook == AUDIT24MOD2_COOKIE
++	  && (strcmp (symname, TEST_FUNC "mod2_func1") == 0))
++	{
++	  test_symbind_flags (*flags);
++	  return (uintptr_t) tst_func2;
++	}
++    }
++
++  abort ();
++}
+diff --git a/elf/tst-auditmod24c.c b/elf/tst-auditmod24c.c
+new file mode 100644
+index 0000000000000000..67e62c9d332f48a7
+--- /dev/null
++++ b/elf/tst-auditmod24c.c
+@@ -0,0 +1,3 @@
++#define TEST_NAME "tst-audit24c"
++#define TEST_MOD  "tst-audit24a"
++#include "tst-auditmod24a.c"
+diff --git a/elf/tst-auditmod24d.c b/elf/tst-auditmod24d.c
+new file mode 100644
+index 0000000000000000..8c803ecc0a48f21b
+--- /dev/null
++++ b/elf/tst-auditmod24d.c
+@@ -0,0 +1,120 @@
++/* Audit module for tst-audit24d.
++   Copyright (C) 2022 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/>.  */
++
++#include <link.h>
++#include <inttypes.h>
++#include <stdlib.h>
++#include <string.h>
++#include <tst-auditmod24.h>
++
++#define AUDIT24_COOKIE     0x0
++#define AUDIT24MOD1_COOKIE 0x1
++#define AUDIT24MOD2_COOKIE 0x2
++#define AUDIT24MOD3_COOKIE 0x3
++#define AUDIT24MOD4_COOKIE 0x4
++
++unsigned int
++la_version (unsigned int version)
++{
++  return LAV_CURRENT;
++}
++
++unsigned int
++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
++{
++  const char *p = strrchr (map->l_name, '/');
++  const char *l_name = p == NULL ? "tst-audit24d" : p + 1;
++
++  uintptr_t ck = -1;
++  if (strcmp (l_name, "tst-audit24dmod1.so") == 0)
++    ck = AUDIT24MOD1_COOKIE;
++  else if (strcmp (l_name, "tst-audit24dmod2.so") == 0)
++    ck = AUDIT24MOD2_COOKIE;
++  else if (strcmp (l_name, "tst-audit24dmod3.so") == 0)
++    ck = AUDIT24MOD3_COOKIE;
++  else if (strcmp (l_name, "tst-audit24dmod.so") == 0)
++    ck = AUDIT24MOD4_COOKIE;
++  else if (strcmp (l_name, "tst-audit24d") == 0)
++    ck = AUDIT24_COOKIE;
++
++  *cookie = ck;
++  return ck == -1 ? 0 : LA_FLG_BINDFROM | LA_FLG_BINDTO;
++}
++
++static int
++tst_audit24dmod1_func1 (void)
++{
++  return 1;
++}
++
++static int
++tst_audit24dmod2_func1 (void)
++{
++  return 10;
++}
++
++static int
++tst_audit24dmod3_func1 (void)
++{
++  return 30;
++}
++
++#include <stdio.h>
++
++#if __ELF_NATIVE_CLASS == 64
++uintptr_t
++la_symbind64 (Elf64_Sym *sym, unsigned int ndx,
++	      uintptr_t *refcook, uintptr_t *defcook,
++	      unsigned int *flags, const char *symname)
++#else
++uintptr_t
++la_symbind32 (Elf32_Sym *sym, unsigned int ndx,
++	      uintptr_t *refcook, uintptr_t *defcook,
++	      unsigned int *flags, const char *symname)
++#endif
++{
++  if (*refcook == AUDIT24_COOKIE)
++    {
++      if (*defcook == AUDIT24MOD1_COOKIE)
++	  {
++	    if (strcmp (symname, "tst_audit24dmod1_func1") == 0)
++	      return (uintptr_t) tst_audit24dmod1_func1;
++	    else if (strcmp (symname, "tst_audit24dmod1_func2") == 0)
++	      return sym->st_value;
++	    abort ();
++	  }
++      if (*defcook == AUDIT24MOD2_COOKIE
++	  && (strcmp (symname, "tst_audit24dmod2_func1") == 0))
++	return (uintptr_t) tst_audit24dmod2_func1;
++
++      /* malloc functions.  */
++      return sym->st_value;
++    }
++  else if (*refcook == AUDIT24MOD1_COOKIE)
++    {
++      if (*defcook == AUDIT24MOD3_COOKIE
++	  && strcmp (symname, "tst_audit24dmod3_func1") == 0)
++	{
++	  test_symbind_flags (*flags);
++
++	  return (uintptr_t) tst_audit24dmod3_func1;
++	}
++    }
++
++  abort ();
++}
+diff --git a/elf/tst-auditmod25.c b/elf/tst-auditmod25.c
+new file mode 100644
+index 0000000000000000..526f5c54bc2c3b8c
+--- /dev/null
++++ b/elf/tst-auditmod25.c
+@@ -0,0 +1,79 @@
++/* Audit modules for tst-audit25a.
++   Copyright (C) 2022 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/>.  */
++
++#include <link.h>
++#include <inttypes.h>
++#include <stdlib.h>
++#include <string.h>
++#include <stdio.h>
++
++#define AUDIT25_COOKIE     0x1
++#define AUDIT25MOD1_COOKIE 0x2
++#define AUDIT25MOD2_COOKIE 0x3
++#define AUDIT25MOD3_COOKIE 0x2
++#define AUDIT25MOD4_COOKIE 0x3
++
++#define TEST_NAME "tst-audit25"
++#define TEST_MOD  "tst-audit25"
++#define TEST_FUNC "tst_audit25"
++
++unsigned int
++la_version (unsigned int version)
++{
++  return LAV_CURRENT;
++}
++
++unsigned int
++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
++{
++  const char *p = strrchr (map->l_name, '/');
++  const char *l_name = p == NULL ? TEST_NAME : p + 1;
++
++  uintptr_t ck = -1;
++  if (strcmp (l_name, TEST_MOD "mod1.so") == 0)
++    ck = AUDIT25MOD1_COOKIE;
++  else if (strcmp (l_name, TEST_MOD "mod2.so") == 0)
++    ck = AUDIT25MOD2_COOKIE;
++  else if (strcmp (l_name, TEST_MOD "mod3.so") == 0)
++    ck = AUDIT25MOD3_COOKIE;
++  else if (strcmp (l_name, TEST_MOD "mod4.so") == 0)
++    ck = AUDIT25MOD4_COOKIE;
++  else if (strncmp (l_name, TEST_NAME, strlen (TEST_NAME)) == 0)
++    ck = AUDIT25_COOKIE;
++
++  *cookie = ck;
++  return ck == -1 ? 0 : LA_FLG_BINDFROM | LA_FLG_BINDTO;
++}
++
++#if __ELF_NATIVE_CLASS == 64
++uintptr_t
++la_symbind64 (Elf64_Sym *sym, unsigned int ndx,
++	      uintptr_t *refcook, uintptr_t *defcook,
++	      unsigned int *flags, const char *symname)
++#else
++uintptr_t
++la_symbind32 (Elf32_Sym *sym, unsigned int ndx,
++	      uintptr_t *refcook, uintptr_t *defcook,
++	      unsigned int *flags, const char *symname)
++#endif
++{
++  if (*refcook != -1 && *defcook != -1)
++    fprintf (stderr, "la_symbind: %s %u\n", symname,
++	     *flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) ? 1 : 0);
++  return sym->st_value;
++}
+diff --git a/sysdeps/generic/dl-lookupcfg.h b/sysdeps/generic/dl-lookupcfg.h
+index e7d37170147aba83..7412c6391b0c3e02 100644
+--- a/sysdeps/generic/dl-lookupcfg.h
++++ b/sysdeps/generic/dl-lookupcfg.h
+@@ -26,3 +26,6 @@
+ #define DL_FIXUP_VALUE_CODE_ADDR(value) (value)
+ #define DL_FIXUP_VALUE_ADDR(value) (value)
+ #define DL_FIXUP_ADDR_VALUE(addr) (addr)
++#define DL_FIXUP_BINDNOW_ADDR_VALUE(addr) (addr)
++#define DL_FIXUP_BINDNOW_RELOC(value, new_value, st_value) \
++  (*value) = st_value;
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index 73f4863fd43922b9..d4f70211c34d1c59 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -1299,7 +1299,10 @@ void _dl_audit_objclose (struct link_map *l)
+ /* Call the la_preinit from the audit modules for the link_map L.  */
+ void _dl_audit_preinit (struct link_map *l);
+ 
+-/* Call the la_symbind{32,64} from the audit modules for the link_map L.  */
++/* Call the la_symbind{32,64} from the audit modules for the link_map L.  If
++   RELOC_RESULT is NULL it assumes the symbol to be bind-now and will set
++   the flags with LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT prior calling
++   la_symbind{32,64}.  */
+ void _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
+ 			const ElfW(Sym) *defsym, DL_FIXUP_VALUE_TYPE *value,
+ 			lookup_t result)
+diff --git a/sysdeps/hppa/dl-lookupcfg.h b/sysdeps/hppa/dl-lookupcfg.h
+index 38db345936cb6335..c3fea1fe5776b17a 100644
+--- a/sysdeps/hppa/dl-lookupcfg.h
++++ b/sysdeps/hppa/dl-lookupcfg.h
+@@ -80,3 +80,6 @@ void attribute_hidden _dl_unmap (struct link_map *map);
+ #define DL_FIXUP_VALUE_CODE_ADDR(value) ((value).ip)
+ #define DL_FIXUP_VALUE_ADDR(value) ((uintptr_t) &(value))
+ #define DL_FIXUP_ADDR_VALUE(addr) (*(struct fdesc *) (addr))
++#define DL_FIXUP_BINDNOW_ADDR_VALUE(addr) (addr)
++#define DL_FIXUP_BINDNOW_RELOC(value, new_value, st_value) \
++  (*value) = *(struct fdesc *) (st_value)
+diff --git a/sysdeps/ia64/dl-lookupcfg.h b/sysdeps/ia64/dl-lookupcfg.h
+index 48f91202c43f8fda..97ad4b70794135a2 100644
+--- a/sysdeps/ia64/dl-lookupcfg.h
++++ b/sysdeps/ia64/dl-lookupcfg.h
+@@ -74,3 +74,6 @@ extern void attribute_hidden _dl_unmap (struct link_map *map);
+ 
+ #define DL_FIXUP_VALUE_ADDR(value) ((uintptr_t) &(value))
+ #define DL_FIXUP_ADDR_VALUE(addr) (*(struct fdesc *) (addr))
++#define DL_FIXUP_BINDNOW_ADDR_VALUE(addr) (addr)
++#define DL_FIXUP_BINDNOW_RELOC(value, new_value, st_value) \
++  (*value) = *(struct fdesc *) (st_value)
+diff --git a/sysdeps/powerpc/dl-lookupcfg.h b/sysdeps/powerpc/dl-lookupcfg.h
+new file mode 100644
+index 0000000000000000..25abcc1d12b15bfc
+--- /dev/null
++++ b/sysdeps/powerpc/dl-lookupcfg.h
+@@ -0,0 +1,39 @@
++/* Configuration of lookup functions.  PowerPC version.
++   Copyright (C) 2022 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/>.  */
++
++#define DL_FIXUP_VALUE_TYPE ElfW(Addr)
++#define DL_FIXUP_MAKE_VALUE(map, addr) (addr)
++#define DL_FIXUP_VALUE_CODE_ADDR(value) (value)
++#define DL_FIXUP_VALUE_ADDR(value) (value)
++#define DL_FIXUP_ADDR_VALUE(addr) (addr)
++#if __WORDSIZE == 64 && _CALL_ELF == 1
++/* We need to correctly set the audit modules value for bind-now.  */
++# define DL_FIXUP_BINDNOW_ADDR_VALUE(addr) \
++ (((Elf64_FuncDesc *)(addr))->fd_func)
++# define DL_FIXUP_BINDNOW_RELOC(value, new_value, st_value)	\
++ ({								\
++    Elf64_FuncDesc *opd = (Elf64_FuncDesc *) (value);		\
++    opd->fd_func = (st_value);					\
++    if ((new_value) != (uintptr_t) (st_value))			\
++     opd->fd_toc = ((Elf64_FuncDesc *)(new_value))->fd_toc;	\
++  })
++#else
++# define DL_FIXUP_BINDNOW_ADDR_VALUE(addr) (addr)
++# define DL_FIXUP_BINDNOW_RELOC(value, new_value, st_value)	\
++  (*value) = st_value;
++#endif
diff --git a/SOURCES/glibc-rh2047981-34.patch b/SOURCES/glibc-rh2047981-34.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8809b8224dd5f19c721b857e7e4101725e66fa8f
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-34.patch
@@ -0,0 +1,1042 @@
+commit ce9a68c57c260c8417afc93972849ac9ad243ec4
+Author: Ben Woodard <woodard@redhat.com>
+Date:   Mon Jan 24 10:46:18 2022 -0300
+
+    elf: Fix runtime linker auditing on aarch64 (BZ #26643)
+    
+    The rtld audit support show two problems on aarch64:
+    
+      1. _dl_runtime_resolve does not preserve x8, the indirect result
+          location register, which might generate wrong result calls
+          depending of the function signature.
+    
+      2. The NEON Q registers pushed onto the stack by _dl_runtime_resolve
+         were twice the size of D registers extracted from the stack frame by
+         _dl_runtime_profile.
+    
+    While 2. might result in wrong information passed on the PLT tracing,
+    1. generates wrong runtime behaviour.
+    
+    The aarch64 rtld audit support is changed to:
+    
+      * Both La_aarch64_regs and La_aarch64_retval are expanded to include
+        both x8 and the full sized NEON V registers, as defined by the
+        ABI.
+    
+      * dl_runtime_profile needed to extract registers saved by
+        _dl_runtime_resolve and put them into the new correctly sized
+        La_aarch64_regs structure.
+    
+      * The LAV_CURRENT check is change to only accept new audit modules
+        to avoid the undefined behavior of not save/restore x8.
+    
+      * Different than other architectures, audit modules older than
+        LAV_CURRENT are rejected (both La_aarch64_regs and La_aarch64_retval
+        changed their layout and there are no requirements to support multiple
+        audit interface with the inherent aarch64 issues).
+    
+      * A new field is also reserved on both La_aarch64_regs and
+        La_aarch64_retval to support variant pcs symbols.
+    
+    Similar to x86, a new La_aarch64_vector type to represent the NEON
+    register is added on the La_aarch64_regs (so each type can be accessed
+    directly).
+    
+    Since LAV_CURRENT was already bumped to support bind-now, there is
+    no need to increase it again.
+    
+    Checked on aarch64-linux-gnu.
+    
+    Co-authored-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+    Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+    Tested-by: Carlos O'Donell <carlos@redhat.com>
+
+Conflicts:
+	elf/rtld.c
+	sysdeps/aarch64/Makefile
+		Rewrite slightly for inclusion in elf/ testing.
+
+diff --git a/elf/rtld.c b/elf/rtld.c
+index caa980dbda3d1a72..aee5ca357f66121e 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -49,6 +49,7 @@
+ #include <gnu/lib-names.h>
+ #include <dl-tunables.h>
+ #include <dl-execve.h>
++#include <dl-audit-check.h>
+ 
+ #include <assert.h>
+ 
+@@ -1002,7 +1003,7 @@ file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
+       return;
+     }
+ 
+-  if (lav > LAV_CURRENT)
++  if (!_dl_audit_check_version (lav))
+     {
+       _dl_debug_printf ("\
+ ERROR: audit interface '%s' requires version %d (maximum supported version %d); ignored.\n",
+diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
+index 3ec78fefc6dd5797..794ea7d13ae2737f 100644
+--- a/sysdeps/aarch64/Makefile
++++ b/sysdeps/aarch64/Makefile
+@@ -4,6 +4,26 @@ ifeq ($(subdir),elf)
+ sysdep-dl-routines += tlsdesc dl-tlsdesc
+ gen-as-const-headers += dl-link.sym
+ 
++tests += tst-audit26 \
++	 tst-audit27
++
++modules-names += \
++    tst-audit26mod \
++    tst-auditmod26 \
++    tst-audit27mod \
++    tst-auditmod27
++
++$(objpfx)tst-audit26: $(objpfx)tst-audit26mod.so \
++		      $(objpfx)tst-auditmod26.so
++LDFLAGS-tst-audit26 += -Wl,-z,lazy
++tst-audit26-ENV = LD_AUDIT=$(objpfx)tst-auditmod26.so
++
++$(objpfx)tst-audit27: $(objpfx)tst-audit27mod.so \
++		      $(objpfx)tst-auditmod27.so
++$(objpfx)tst-audit27mod.so: $(libsupport)
++LDFLAGS-tst-audit27 += -Wl,-z,lazy
++tst-audit27-ENV = LD_AUDIT=$(objpfx)tst-auditmod27.so
++
+ ifeq (yes,$(aarch64-variant-pcs))
+ tests += tst-vpcs
+ modules-names += tst-vpcs-mod
+diff --git a/sysdeps/aarch64/bits/link.h b/sysdeps/aarch64/bits/link.h
+index 5a7fc1ccd494b2a7..f4f844bfefdaf2f5 100644
+--- a/sysdeps/aarch64/bits/link.h
++++ b/sysdeps/aarch64/bits/link.h
+@@ -20,23 +20,31 @@
+ # error "Never include <bits/link.h> directly; use <link.h> instead."
+ #endif
+ 
++typedef union
++{
++  float s;
++  double d;
++  long double q;
++} La_aarch64_vector;
++
+ /* Registers for entry into PLT on AArch64.  */
+ typedef struct La_aarch64_regs
+ {
+-  uint64_t lr_xreg[8];
+-  uint64_t lr_dreg[8];
+-  uint64_t lr_sp;
+-  uint64_t lr_lr;
++  uint64_t          lr_xreg[9];
++  La_aarch64_vector lr_vreg[8];
++  uint64_t          lr_sp;
++  uint64_t          lr_lr;
++  void              *lr_vpcs;
+ } La_aarch64_regs;
+ 
+ /* Return values for calls from PLT on AArch64.  */
+ typedef struct La_aarch64_retval
+ {
+-  /* Up to two integer registers can be used for a return value.  */
+-  uint64_t lrv_xreg[2];
+-  /* Up to four D registers can be used for a return value.  */
+-  uint64_t lrv_dreg[4];
+-
++  /* Up to eight integer registers can be used for a return value.  */
++  uint64_t          lrv_xreg[8];
++  /* Up to eight V registers can be used for a return value.  */
++  La_aarch64_vector lrv_vreg[8];
++  void              *lrv_vpcs;
+ } La_aarch64_retval;
+ __BEGIN_DECLS
+ 
+diff --git a/sysdeps/aarch64/dl-audit-check.h b/sysdeps/aarch64/dl-audit-check.h
+new file mode 100644
+index 0000000000000000..e324339a1d4abec3
+--- /dev/null
++++ b/sysdeps/aarch64/dl-audit-check.h
+@@ -0,0 +1,28 @@
++/* rtld-audit version check.  AArch64 version.
++   Copyright (C) 2022 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/>.  */
++
++static inline bool
++_dl_audit_check_version (unsigned int lav)
++{
++  /* Audit version 1 do not save x8 or NEON registers, which required
++     changing La_aarch64_regs and La_aarch64_retval layout (BZ#26643).  The
++     missing indirect result save/restore makes _dl_runtime_profile
++     potentially trigger undefined behavior if the function returns a large
++     struct (even when PLT trace is not requested).  */
++  return lav == LAV_CURRENT;
++}
+diff --git a/sysdeps/aarch64/dl-link.sym b/sysdeps/aarch64/dl-link.sym
+index d67d28b40ce7d4ff..cb4dcdcbed0db492 100644
+--- a/sysdeps/aarch64/dl-link.sym
++++ b/sysdeps/aarch64/dl-link.sym
+@@ -7,9 +7,11 @@ DL_SIZEOF_RG		sizeof(struct La_aarch64_regs)
+ DL_SIZEOF_RV		sizeof(struct La_aarch64_retval)
+ 
+ DL_OFFSET_RG_X0		offsetof(struct La_aarch64_regs, lr_xreg)
+-DL_OFFSET_RG_D0		offsetof(struct La_aarch64_regs, lr_dreg)
++DL_OFFSET_RG_V0		offsetof(struct La_aarch64_regs, lr_vreg)
+ DL_OFFSET_RG_SP		offsetof(struct La_aarch64_regs, lr_sp)
+ DL_OFFSET_RG_LR		offsetof(struct La_aarch64_regs, lr_lr)
++DL_OFFSET_RG_VPCS       offsetof(struct La_aarch64_regs, lr_vpcs)
+ 
+ DL_OFFSET_RV_X0		offsetof(struct La_aarch64_retval, lrv_xreg)
+-DL_OFFSET_RV_D0		offsetof(struct La_aarch64_retval, lrv_dreg)
++DL_OFFSET_RV_V0		offsetof(struct La_aarch64_retval, lrv_vreg)
++DL_OFFSET_RV_VPCS       offsetof(struct La_aarch64_retval, lrv_vpcs)
+diff --git a/sysdeps/aarch64/dl-trampoline.S b/sysdeps/aarch64/dl-trampoline.S
+index 18740398e63fdf97..a83e7fc5f97047e2 100644
+--- a/sysdeps/aarch64/dl-trampoline.S
++++ b/sysdeps/aarch64/dl-trampoline.S
+@@ -44,7 +44,8 @@ _dl_runtime_resolve:
+ 
+ 	cfi_rel_offset (lr, 8)
+ 
+-	/* Save arguments.  */
++	/* Note: Saving x9 is not required by the ABI but the assembler requires
++	   the immediate values of operand 3 to be a multiple of 16 */
+ 	stp	x8, x9, [sp, #-(80+8*16)]!
+ 	cfi_adjust_cfa_offset (80+8*16)
+ 	cfi_rel_offset (x8, 0)
+@@ -135,7 +136,7 @@ _dl_runtime_profile:
+ 	   Stack frame layout:
+ 	   [sp,   #...] lr
+ 	   [sp,   #...] &PLTGOT[n]
+-	   [sp,    #96] La_aarch64_regs
++	   [sp,   #256] La_aarch64_regs
+ 	   [sp,    #48] La_aarch64_retval
+ 	   [sp,    #40] frame size return from pltenter
+ 	   [sp,    #32] dl_profile_call saved x1
+@@ -176,19 +177,25 @@ _dl_runtime_profile:
+ 	stp	x6, x7, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*3]
+ 	cfi_rel_offset (x6, OFFSET_RG + DL_OFFSET_RG_X0 + 16*3 + 0)
+ 	cfi_rel_offset (x7, OFFSET_RG + DL_OFFSET_RG_X0 + 16*3 + 8)
+-
+-	stp	d0, d1, [X29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*0]
+-	cfi_rel_offset (d0, OFFSET_RG + DL_OFFSET_RG_D0 + 16*0)
+-	cfi_rel_offset (d1, OFFSET_RG + DL_OFFSET_RG_D0 + 16*0 + 8)
+-	stp	d2, d3, [X29, #OFFSET_RG+ DL_OFFSET_RG_D0 + 16*1]
+-	cfi_rel_offset (d2, OFFSET_RG + DL_OFFSET_RG_D0 + 16*1 + 0)
+-	cfi_rel_offset (d3, OFFSET_RG + DL_OFFSET_RG_D0 + 16*1 + 8)
+-	stp	d4, d5, [X29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*2]
+-	cfi_rel_offset (d4, OFFSET_RG + DL_OFFSET_RG_D0 + 16*2 + 0)
+-	cfi_rel_offset (d5, OFFSET_RG + DL_OFFSET_RG_D0 + 16*2 + 8)
+-	stp	d6, d7, [X29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*3]
+-	cfi_rel_offset (d6, OFFSET_RG + DL_OFFSET_RG_D0 + 16*3 + 0)
+-	cfi_rel_offset (d7, OFFSET_RG + DL_OFFSET_RG_D0 + 16*3 + 8)
++	str	x8, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*4 + 0]
++	cfi_rel_offset (x8, OFFSET_RG + DL_OFFSET_RG_X0 + 16*4 + 0)
++	/* Note 8 bytes of padding is in the stack frame for alignment */
++
++	stp	q0, q1, [X29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*0]
++	cfi_rel_offset (q0, OFFSET_RG + DL_OFFSET_RG_V0 + 32*0)
++	cfi_rel_offset (q1, OFFSET_RG + DL_OFFSET_RG_V0 + 32*0 + 16)
++	stp	q2, q3, [X29, #OFFSET_RG+ DL_OFFSET_RG_V0 + 32*1]
++	cfi_rel_offset (q2, OFFSET_RG + DL_OFFSET_RG_V0 + 32*1 + 0)
++	cfi_rel_offset (q3, OFFSET_RG + DL_OFFSET_RG_V0 + 32*1 + 16)
++	stp	q4, q5, [X29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*2]
++	cfi_rel_offset (q4, OFFSET_RG + DL_OFFSET_RG_V0 + 32*2 + 0)
++	cfi_rel_offset (q5, OFFSET_RG + DL_OFFSET_RG_V0 + 32*2 + 16)
++	stp	q6, q7, [X29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*3]
++	cfi_rel_offset (q6, OFFSET_RG + DL_OFFSET_RG_V0 + 32*3 + 0)
++	cfi_rel_offset (q7, OFFSET_RG + DL_OFFSET_RG_V0 + 32*3 + 16)
++
++	/* No APCS extension supported.  */
++	str	xzr,    [X29, #OFFSET_RG + DL_OFFSET_RG_VPCS]
+ 
+ 	add     x0, x29, #SF_SIZE + 16
+ 	ldr	x1, [x29, #OFFSET_LR]
+@@ -227,10 +234,11 @@ _dl_runtime_profile:
+ 	ldp	x2, x3, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*1]
+ 	ldp	x4, x5, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*2]
+ 	ldp	x6, x7, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*3]
+-	ldp	d0, d1, [x29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*0]
+-	ldp	d2, d3, [x29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*1]
+-	ldp	d4, d5, [x29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*2]
+-	ldp	d6, d7, [x29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*3]
++	ldr	x8,     [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*4]
++	ldp	q0, q1, [x29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*0]
++	ldp	q2, q3, [x29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*1]
++	ldp	q4, q5, [x29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*2]
++	ldp	q6, q7, [x29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*3]
+ 
+ 	cfi_def_cfa_register (sp)
+ 	ldp	x29, x30, [x29, #0]
+@@ -264,14 +272,22 @@ _dl_runtime_profile:
+ 	ldp	x2, x3, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*1]
+ 	ldp	x4, x5, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*2]
+ 	ldp	x6, x7, [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*3]
+-	ldp	d0, d1, [x29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*0]
+-	ldp	d2, d3, [x29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*1]
+-	ldp	d4, d5, [x29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*2]
+-	ldp	d6, d7, [x29, #OFFSET_RG + DL_OFFSET_RG_D0 + 16*3]
++	ldr	x8,     [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*4]
++	ldp	q0, q1, [x29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*0]
++	ldp	q2, q3, [x29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*1]
++	ldp	q4, q5, [x29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*2]
++	ldp	q6, q7, [x29, #OFFSET_RG + DL_OFFSET_RG_V0 + 32*3]
+ 	blr	ip0
+-	stp	x0, x1, [x29, #OFFSET_RV + DL_OFFSET_RV_X0]
+-	stp	d0, d1, [x29, #OFFSET_RV + DL_OFFSET_RV_D0 + 16*0]
+-	stp	d2, d3, [x29, #OFFSET_RV + DL_OFFSET_RV_D0 + 16*1]
++	stp	x0, x1, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*0]
++	stp	x2, x3, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*1]
++	stp	x4, x5, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*2]
++	stp	x6, x7, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*3]
++	str	x8,     [x29, #OFFSET_RG + DL_OFFSET_RG_X0 + 16*4]
++	stp	q0, q1, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*0]
++	stp	q2, q3, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*1]
++	stp	q4, q5, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*2]
++	stp	q6, q7, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*3]
++	str	xzr,    [X29, #OFFSET_RV + DL_OFFSET_RG_VPCS]
+ 
+ 	/* Setup call to pltexit  */
+ 	ldp	x0, x1, [x29, #OFFSET_SAVED_CALL_X0]
+@@ -279,9 +295,16 @@ _dl_runtime_profile:
+ 	add	x3, x29, #OFFSET_RV
+ 	bl	_dl_audit_pltexit
+ 
+-	ldp	x0, x1, [x29, #OFFSET_RV + DL_OFFSET_RV_X0]
+-	ldp	d0, d1, [x29, #OFFSET_RV + DL_OFFSET_RV_D0 + 16*0]
+-	ldp	d2, d3, [x29, #OFFSET_RV + DL_OFFSET_RV_D0 + 16*1]
++	ldp	x0, x1, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*0]
++	ldp	x2, x3, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*1]
++	ldp	x4, x5, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*2]
++	ldp	x6, x7, [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*3]
++	ldr	x8,     [x29, #OFFSET_RV + DL_OFFSET_RV_X0 + 16*4]
++	ldp	q0, q1, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*0]
++	ldp	q2, q3, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*1]
++	ldp	q4, q5, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*2]
++	ldp	q6, q7, [x29, #OFFSET_RV + DL_OFFSET_RV_V0 + 32*3]
++
+ 	/* LR from within La_aarch64_reg */
+ 	ldr	lr, [x29, #OFFSET_RG + DL_OFFSET_RG_LR]
+ 	cfi_restore(lr)
+diff --git a/sysdeps/aarch64/tst-audit26.c b/sysdeps/aarch64/tst-audit26.c
+new file mode 100644
+index 0000000000000000..46de8acd219cb8bc
+--- /dev/null
++++ b/sysdeps/aarch64/tst-audit26.c
+@@ -0,0 +1,37 @@
++/* Check LD_AUDIT for aarch64 ABI specifics.
++   Copyright (C) 2022 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/>.  */
++
++#include <array_length.h>
++#include <string.h>
++#include <support/check.h>
++#include "tst-audit26mod.h"
++
++int
++do_test (void)
++{
++  /* Returning a large struct uses 'x8' as indirect result location.  */
++  struct large_struct r = tst_audit26_func (ARG1, ARG2, ARG3);
++
++  struct large_struct e = set_large_struct (ARG1, ARG2, ARG3);
++
++  TEST_COMPARE_BLOB (r.a, sizeof (r.a), e.a, sizeof (e.a));
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/sysdeps/aarch64/tst-audit26mod.c b/sysdeps/aarch64/tst-audit26mod.c
+new file mode 100644
+index 0000000000000000..67d5ffce7288b34c
+--- /dev/null
++++ b/sysdeps/aarch64/tst-audit26mod.c
+@@ -0,0 +1,33 @@
++/* Check LD_AUDIT for aarch64 ABI specifics.
++   Copyright (C) 2022 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/>.  */
++
++#include <stdlib.h>
++#include "tst-audit26mod.h"
++
++struct large_struct
++tst_audit26_func (char a, short b, long int c)
++{
++  if (a != ARG1)
++    abort ();
++  if (b != ARG2)
++    abort ();
++  if (c != ARG3)
++    abort ();
++
++  return set_large_struct (a, b, c);
++}
+diff --git a/sysdeps/aarch64/tst-audit26mod.h b/sysdeps/aarch64/tst-audit26mod.h
+new file mode 100644
+index 0000000000000000..f80409f96bae6c82
+--- /dev/null
++++ b/sysdeps/aarch64/tst-audit26mod.h
+@@ -0,0 +1,50 @@
++/* Check LD_AUDIT for aarch64 specific ABI.
++   Copyright (C) 2022 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/>.  */
++
++#ifndef _TST_AUDIT27MOD_H
++#define _TST_AUDIT27MOD_H 1
++
++#include <array_length.h>
++
++struct large_struct
++{
++  char a[16];
++  short b[8];
++  long int c[4];
++};
++
++static inline struct large_struct
++set_large_struct (char a, short b, long int c)
++{
++  struct large_struct r;
++  for (int i = 0; i < array_length (r.a); i++)
++    r.a[i] = a;
++  for (int i = 0; i < array_length (r.b); i++)
++    r.b[i] = b;
++  for (int i = 0; i < array_length (r.c); i++)
++    r.c[i] = c;
++  return r;
++}
++
++#define ARG1 0x12
++#define ARG2 0x1234
++#define ARG3 0x12345678
++
++struct large_struct tst_audit26_func (char a, short b, long int c);
++
++#endif
+diff --git a/sysdeps/aarch64/tst-audit27.c b/sysdeps/aarch64/tst-audit27.c
+new file mode 100644
+index 0000000000000000..5ebc09771f845af0
+--- /dev/null
++++ b/sysdeps/aarch64/tst-audit27.c
+@@ -0,0 +1,64 @@
++/* Check LD_AUDIT for aarch64 ABI specifics.
++   Copyright (C) 2022 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/>.  */
++
++#include <array_length.h>
++#include <string.h>
++#include <support/check.h>
++#include "tst-audit27mod.h"
++
++int
++do_test (void)
++{
++  {
++    float r = tst_audit27_func_float (FUNC_FLOAT_ARG0, FUNC_FLOAT_ARG1,
++				      FUNC_FLOAT_ARG2, FUNC_FLOAT_ARG3,
++				      FUNC_FLOAT_ARG4, FUNC_FLOAT_ARG5,
++				      FUNC_FLOAT_ARG6, FUNC_FLOAT_ARG7);
++    if (r != FUNC_FLOAT_RET)
++      FAIL_EXIT1 ("tst_audit27_func_float() returned %a, expected %a",
++		  r, FUNC_FLOAT_RET);
++  }
++
++  {
++    double r = tst_audit27_func_double (FUNC_DOUBLE_ARG0, FUNC_DOUBLE_ARG1,
++					FUNC_DOUBLE_ARG2, FUNC_DOUBLE_ARG3,
++					FUNC_DOUBLE_ARG4, FUNC_DOUBLE_ARG5,
++					FUNC_DOUBLE_ARG6, FUNC_DOUBLE_ARG7);
++    if (r != FUNC_DOUBLE_RET)
++      FAIL_EXIT1 ("tst_audit27_func_double() returned %la, expected %la",
++		  r, FUNC_DOUBLE_RET);
++  }
++
++  {
++    long double r = tst_audit27_func_ldouble (FUNC_LDOUBLE_ARG0,
++					      FUNC_LDOUBLE_ARG1,
++					      FUNC_LDOUBLE_ARG2,
++					      FUNC_LDOUBLE_ARG3,
++					      FUNC_LDOUBLE_ARG4,
++					      FUNC_LDOUBLE_ARG5,
++					      FUNC_LDOUBLE_ARG6,
++					      FUNC_LDOUBLE_ARG7);
++    if (r != FUNC_LDOUBLE_RET)
++      FAIL_EXIT1 ("tst_audit27_func_ldouble() returned %La, expected %La",
++		  r, FUNC_LDOUBLE_RET);
++  }
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/sysdeps/aarch64/tst-audit27mod.c b/sysdeps/aarch64/tst-audit27mod.c
+new file mode 100644
+index 0000000000000000..922b518f0af4b97b
+--- /dev/null
++++ b/sysdeps/aarch64/tst-audit27mod.c
+@@ -0,0 +1,95 @@
++/* Check LD_AUDIT for aarch64 ABI specifics.
++   Copyright (C) 2022 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/>.  */
++
++#include <array_length.h>
++#include <stdlib.h>
++#include <support/check.h>
++#include "tst-audit27mod.h"
++
++float
++tst_audit27_func_float (float a0, float a1, float a2, float a3, float a4,
++			float a5, float a6, float a7)
++{
++  if (a0 != FUNC_FLOAT_ARG0)
++    FAIL_EXIT1 ("a0: %a != %a", a0, FUNC_FLOAT_ARG0);
++  if (a1 != FUNC_FLOAT_ARG1)
++    FAIL_EXIT1 ("a1: %a != %a", a1, FUNC_FLOAT_ARG1);
++  if (a2 != FUNC_FLOAT_ARG2)
++    FAIL_EXIT1 ("a2: %a != %a", a2, FUNC_FLOAT_ARG2);
++  if (a3 != FUNC_FLOAT_ARG3)
++    FAIL_EXIT1 ("a3: %a != %a", a3, FUNC_FLOAT_ARG3);
++  if (a4 != FUNC_FLOAT_ARG4)
++    FAIL_EXIT1 ("a4: %a != %a", a4, FUNC_FLOAT_ARG4);
++  if (a5 != FUNC_FLOAT_ARG5)
++    FAIL_EXIT1 ("a5: %a != %a", a5, FUNC_FLOAT_ARG5);
++  if (a6 != FUNC_FLOAT_ARG6)
++    FAIL_EXIT1 ("a6: %a != %a", a6, FUNC_FLOAT_ARG6);
++  if (a7 != FUNC_FLOAT_ARG7)
++    FAIL_EXIT1 ("a7: %a != %a", a7, FUNC_FLOAT_ARG7);
++
++  return FUNC_FLOAT_RET;
++}
++
++double
++tst_audit27_func_double (double a0, double a1, double a2, double a3, double a4,
++			 double a5, double a6, double a7)
++{
++  if (a0 != FUNC_DOUBLE_ARG0)
++    FAIL_EXIT1 ("a0: %la != %la", a0, FUNC_DOUBLE_ARG0);
++  if (a1 != FUNC_DOUBLE_ARG1)
++    FAIL_EXIT1 ("a1: %la != %la", a1, FUNC_DOUBLE_ARG1);
++  if (a2 != FUNC_DOUBLE_ARG2)
++    FAIL_EXIT1 ("a2: %la != %la", a2, FUNC_DOUBLE_ARG2);
++  if (a3 != FUNC_DOUBLE_ARG3)
++    FAIL_EXIT1 ("a3: %la != %la", a3, FUNC_DOUBLE_ARG3);
++  if (a4 != FUNC_DOUBLE_ARG4)
++    FAIL_EXIT1 ("a4: %la != %la", a4, FUNC_DOUBLE_ARG4);
++  if (a5 != FUNC_DOUBLE_ARG5)
++    FAIL_EXIT1 ("a5: %la != %la", a5, FUNC_DOUBLE_ARG5);
++  if (a6 != FUNC_DOUBLE_ARG6)
++    FAIL_EXIT1 ("a6: %la != %la", a6, FUNC_DOUBLE_ARG6);
++  if (a7 != FUNC_DOUBLE_ARG7)
++    FAIL_EXIT1 ("a7: %la != %la", a7, FUNC_DOUBLE_ARG7);
++
++  return FUNC_DOUBLE_RET;
++}
++
++long double
++tst_audit27_func_ldouble (long double a0, long double a1, long double a2,
++			  long double a3, long double a4, long double a5,
++			  long double a6, long double a7)
++{
++  if (a0 != FUNC_LDOUBLE_ARG0)
++    FAIL_EXIT1 ("a0: %La != %La", a0, FUNC_LDOUBLE_ARG0);
++  if (a1 != FUNC_LDOUBLE_ARG1)
++    FAIL_EXIT1 ("a1: %La != %La", a1, FUNC_LDOUBLE_ARG1);
++  if (a2 != FUNC_LDOUBLE_ARG2)
++    FAIL_EXIT1 ("a2: %La != %La", a2, FUNC_LDOUBLE_ARG2);
++  if (a3 != FUNC_LDOUBLE_ARG3)
++    FAIL_EXIT1 ("a3: %La != %La", a3, FUNC_LDOUBLE_ARG3);
++  if (a4 != FUNC_LDOUBLE_ARG4)
++    FAIL_EXIT1 ("a4: %La != %La", a4, FUNC_LDOUBLE_ARG4);
++  if (a5 != FUNC_LDOUBLE_ARG5)
++    FAIL_EXIT1 ("a5: %La != %La", a5, FUNC_LDOUBLE_ARG5);
++  if (a6 != FUNC_LDOUBLE_ARG6)
++    FAIL_EXIT1 ("a6: %La != %La", a6, FUNC_LDOUBLE_ARG6);
++  if (a7 != FUNC_LDOUBLE_ARG7)
++    FAIL_EXIT1 ("a7: %La != %La", a7, FUNC_LDOUBLE_ARG7);
++
++  return FUNC_LDOUBLE_RET;
++}
+diff --git a/sysdeps/aarch64/tst-audit27mod.h b/sysdeps/aarch64/tst-audit27mod.h
+new file mode 100644
+index 0000000000000000..1709d222ca251e3b
+--- /dev/null
++++ b/sysdeps/aarch64/tst-audit27mod.h
+@@ -0,0 +1,67 @@
++/* Check LD_AUDIT for aarch64 specific ABI.
++   Copyright (C) 2022 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/>.  */
++
++#ifndef _TST_AUDIT27MOD_H
++#define _TST_AUDIT27MOD_H 1
++
++#include <float.h>
++
++#define FUNC_FLOAT_ARG0 FLT_MIN
++#define FUNC_FLOAT_ARG1 FLT_MAX
++#define FUNC_FLOAT_ARG2 FLT_EPSILON
++#define FUNC_FLOAT_ARG3 FLT_TRUE_MIN
++#define FUNC_FLOAT_ARG4 0.0f
++#define FUNC_FLOAT_ARG5 1.0f
++#define FUNC_FLOAT_ARG6 2.0f
++#define FUNC_FLOAT_ARG7 3.0f
++#define FUNC_FLOAT_RET  4.0f
++
++float
++tst_audit27_func_float (float a0, float a1, float a2, float a3, float a4,
++			float a5, float a6, float a7);
++
++#define FUNC_DOUBLE_ARG0 DBL_MIN
++#define FUNC_DOUBLE_ARG1 DBL_MAX
++#define FUNC_DOUBLE_ARG2 DBL_EPSILON
++#define FUNC_DOUBLE_ARG3 DBL_TRUE_MIN
++#define FUNC_DOUBLE_ARG4 0.0
++#define FUNC_DOUBLE_ARG5 1.0
++#define FUNC_DOUBLE_ARG6 2.0
++#define FUNC_DOUBLE_ARG7 3.0
++#define FUNC_DOUBLE_RET  0x1.fffffe0000001p+127
++
++double
++tst_audit27_func_double (double a0, double a1, double a2, double a3, double a4,
++			 double a5, double a6, double a7);
++
++#define FUNC_LDOUBLE_ARG0 DBL_MAX + 1.0L
++#define FUNC_LDOUBLE_ARG1 DBL_MAX + 2.0L
++#define FUNC_LDOUBLE_ARG2 DBL_MAX + 3.0L
++#define FUNC_LDOUBLE_ARG3 DBL_MAX + 4.0L
++#define FUNC_LDOUBLE_ARG4 DBL_MAX + 5.0L
++#define FUNC_LDOUBLE_ARG5 DBL_MAX + 6.0L
++#define FUNC_LDOUBLE_ARG6 DBL_MAX + 7.0L
++#define FUNC_LDOUBLE_ARG7 DBL_MAX + 8.0L
++#define FUNC_LDOUBLE_RET  0x1.fffffffffffff000000000000001p+1023L
++
++long double
++tst_audit27_func_ldouble (long double a0, long double a1, long double a2,
++			  long double a3, long double a4, long double a5,
++			  long double a6, long double a7);
++
++#endif
+diff --git a/sysdeps/aarch64/tst-auditmod26.c b/sysdeps/aarch64/tst-auditmod26.c
+new file mode 100644
+index 0000000000000000..b03b6baed9aeb528
+--- /dev/null
++++ b/sysdeps/aarch64/tst-auditmod26.c
+@@ -0,0 +1,103 @@
++/* Check LD_AUDIT for aarch64 specific ABI.
++   Copyright (C) 2022 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/>.  */
++
++#include <assert.h>
++#include <link.h>
++#include <string.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include "tst-audit26mod.h"
++
++#define TEST_NAME  "tst-audit26"
++
++#define AUDIT26_COOKIE 0
++
++unsigned int
++la_version (unsigned int v)
++{
++  return v;
++}
++
++unsigned int
++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
++{
++  const char *p = strrchr (map->l_name, '/');
++  const char *l_name = p == NULL ? map->l_name : p + 1;
++  uintptr_t ck = -1;
++  if (strncmp (l_name, TEST_NAME, strlen (TEST_NAME)) == 0)
++    ck = AUDIT26_COOKIE;
++  *cookie = ck;
++  printf ("objopen: %ld, %s [cookie=%ld]\n", lmid, l_name, ck);
++  return ck == -1 ? 0 : LA_FLG_BINDFROM | LA_FLG_BINDTO;
++}
++
++ElfW(Addr)
++la_aarch64_gnu_pltenter (ElfW(Sym) *sym __attribute__ ((unused)),
++                         unsigned int ndx __attribute__ ((unused)),
++                         uintptr_t *refcook, uintptr_t *defcook,
++                         La_aarch64_regs *regs, unsigned int *flags,
++                         const char *symname, long int *framesizep)
++{
++  printf ("pltenter: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n",
++	  symname, (long int) sym->st_value, ndx, *flags);
++
++  if (strcmp (symname, "tst_audit26_func") == 0)
++    {
++      assert (regs->lr_xreg[0] == ARG1);
++      assert (regs->lr_xreg[1] == ARG2);
++      assert (regs->lr_xreg[2] == ARG3);
++    }
++  else
++    abort ();
++
++  assert (regs->lr_vpcs == 0);
++
++  /* Clobber 'x8'.  */
++  asm volatile ("mov x8, -1" : : : "x8");
++
++  *framesizep = 1024;
++
++  return sym->st_value;
++}
++
++unsigned int
++la_aarch64_gnu_pltexit (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
++                        uintptr_t *defcook,
++                        const struct La_aarch64_regs *inregs,
++                        struct La_aarch64_retval *outregs, const char *symname)
++{
++  printf ("pltexit: symname=%s, st_value=%#lx, ndx=%u\n",
++	  symname, (long int) sym->st_value, ndx);
++
++  if (strcmp (symname, "tst_audit26_func") == 0)
++    {
++      assert (inregs->lr_xreg[0] == ARG1);
++      assert (inregs->lr_xreg[1] == ARG2);
++      assert (inregs->lr_xreg[2] == ARG3);
++    }
++  else
++    abort ();
++
++  assert (inregs->lr_vpcs == 0);
++  assert (outregs->lrv_vpcs == 0);
++
++  /* Clobber 'x8'.  */
++  asm volatile ("mov x8, -1" : : : "x8");
++
++  return 0;
++}
+diff --git a/sysdeps/aarch64/tst-auditmod27.c b/sysdeps/aarch64/tst-auditmod27.c
+new file mode 100644
+index 0000000000000000..21132c2985dab7b2
+--- /dev/null
++++ b/sysdeps/aarch64/tst-auditmod27.c
+@@ -0,0 +1,180 @@
++/* Check LD_AUDIT for aarch64 specific ABI.
++   Copyright (C) 2022 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/>.  */
++
++#include <assert.h>
++#include <link.h>
++#include <string.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include "tst-audit27mod.h"
++
++#define TEST_NAME  "tst-audit27"
++
++#define AUDIT27_COOKIE 0
++
++unsigned int
++la_version (unsigned int v)
++{
++  return v;
++}
++
++unsigned int
++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
++{
++  const char *p = strrchr (map->l_name, '/');
++  const char *l_name = p == NULL ? map->l_name : p + 1;
++  uintptr_t ck = -1;
++  if (strncmp (l_name, TEST_NAME, strlen (TEST_NAME)) == 0)
++    ck = AUDIT27_COOKIE;
++  *cookie = ck;
++  printf ("objopen: %ld, %s [%ld]\n", lmid, l_name, ck);
++  return ck == -1 ? 0 : LA_FLG_BINDFROM | LA_FLG_BINDTO;
++}
++
++ElfW(Addr)
++la_aarch64_gnu_pltenter (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
++			 uintptr_t *defcook, La_aarch64_regs *regs,
++			 unsigned int *flags, const char *symname,
++			 long int *framesizep)
++{
++  printf ("pltenter: symname=%s, st_value=%#lx, ndx=%u, flags=%u\n",
++	  symname, (long int) sym->st_value, ndx, *flags);
++
++  if (strcmp (symname, "tst_audit27_func_float") == 0)
++    {
++      assert (regs->lr_vreg[0].s == FUNC_FLOAT_ARG0);
++      assert (regs->lr_vreg[1].s == FUNC_FLOAT_ARG1);
++      assert (regs->lr_vreg[2].s == FUNC_FLOAT_ARG2);
++      assert (regs->lr_vreg[3].s == FUNC_FLOAT_ARG3);
++      assert (regs->lr_vreg[4].s == FUNC_FLOAT_ARG4);
++      assert (regs->lr_vreg[5].s == FUNC_FLOAT_ARG5);
++      assert (regs->lr_vreg[6].s == FUNC_FLOAT_ARG6);
++      assert (regs->lr_vreg[7].s == FUNC_FLOAT_ARG7);
++    }
++  else if (strcmp (symname, "tst_audit27_func_double") == 0)
++    {
++      assert (regs->lr_vreg[0].d == FUNC_DOUBLE_ARG0);
++      assert (regs->lr_vreg[1].d == FUNC_DOUBLE_ARG1);
++      assert (regs->lr_vreg[2].d == FUNC_DOUBLE_ARG2);
++      assert (regs->lr_vreg[3].d == FUNC_DOUBLE_ARG3);
++      assert (regs->lr_vreg[4].d == FUNC_DOUBLE_ARG4);
++      assert (regs->lr_vreg[5].d == FUNC_DOUBLE_ARG5);
++      assert (regs->lr_vreg[6].d == FUNC_DOUBLE_ARG6);
++      assert (regs->lr_vreg[7].d == FUNC_DOUBLE_ARG7);
++    }
++  else if (strcmp (symname, "tst_audit27_func_ldouble") == 0)
++    {
++      assert (regs->lr_vreg[0].q == FUNC_LDOUBLE_ARG0);
++      assert (regs->lr_vreg[1].q == FUNC_LDOUBLE_ARG1);
++      assert (regs->lr_vreg[2].q == FUNC_LDOUBLE_ARG2);
++      assert (regs->lr_vreg[3].q == FUNC_LDOUBLE_ARG3);
++      assert (regs->lr_vreg[4].q == FUNC_LDOUBLE_ARG4);
++      assert (regs->lr_vreg[5].q == FUNC_LDOUBLE_ARG5);
++      assert (regs->lr_vreg[6].q == FUNC_LDOUBLE_ARG6);
++      assert (regs->lr_vreg[7].q == FUNC_LDOUBLE_ARG7);
++    }
++  else
++    abort ();
++
++  assert (regs->lr_vpcs == 0);
++
++  /* Clobber the q registers on exit.  */
++  uint8_t v = 0xff;
++  asm volatile ("dup v0.8b, %w0" : : "r" (v) : "v0");
++  asm volatile ("dup v1.8b, %w0" : : "r" (v) : "v1");
++  asm volatile ("dup v2.8b, %w0" : : "r" (v) : "v2");
++  asm volatile ("dup v3.8b, %w0" : : "r" (v) : "v3");
++  asm volatile ("dup v4.8b, %w0" : : "r" (v) : "v4");
++  asm volatile ("dup v5.8b, %w0" : : "r" (v) : "v5");
++  asm volatile ("dup v6.8b, %w0" : : "r" (v) : "v6");
++  asm volatile ("dup v7.8b, %w0" : : "r" (v) : "v7");
++
++  *framesizep = 1024;
++
++  return sym->st_value;
++}
++
++unsigned int
++la_aarch64_gnu_pltexit (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
++                        uintptr_t *defcook,
++			const struct La_aarch64_regs *inregs,
++                        struct La_aarch64_retval *outregs,
++			const char *symname)
++{
++  printf ("pltexit: symname=%s, st_value=%#lx, ndx=%u\n",
++	  symname, (long int) sym->st_value, ndx);
++
++  if (strcmp (symname, "tst_audit27_func_float") == 0)
++    {
++      assert (inregs->lr_vreg[0].s == FUNC_FLOAT_ARG0);
++      assert (inregs->lr_vreg[1].s == FUNC_FLOAT_ARG1);
++      assert (inregs->lr_vreg[2].s == FUNC_FLOAT_ARG2);
++      assert (inregs->lr_vreg[3].s == FUNC_FLOAT_ARG3);
++      assert (inregs->lr_vreg[4].s == FUNC_FLOAT_ARG4);
++      assert (inregs->lr_vreg[5].s == FUNC_FLOAT_ARG5);
++      assert (inregs->lr_vreg[6].s == FUNC_FLOAT_ARG6);
++      assert (inregs->lr_vreg[7].s == FUNC_FLOAT_ARG7);
++
++      assert (outregs->lrv_vreg[0].s == FUNC_FLOAT_RET);
++    }
++  else if (strcmp (symname, "tst_audit27_func_double") == 0)
++    {
++      assert (inregs->lr_vreg[0].d == FUNC_DOUBLE_ARG0);
++      assert (inregs->lr_vreg[1].d == FUNC_DOUBLE_ARG1);
++      assert (inregs->lr_vreg[2].d == FUNC_DOUBLE_ARG2);
++      assert (inregs->lr_vreg[3].d == FUNC_DOUBLE_ARG3);
++      assert (inregs->lr_vreg[4].d == FUNC_DOUBLE_ARG4);
++      assert (inregs->lr_vreg[5].d == FUNC_DOUBLE_ARG5);
++      assert (inregs->lr_vreg[6].d == FUNC_DOUBLE_ARG6);
++      assert (inregs->lr_vreg[7].d == FUNC_DOUBLE_ARG7);
++
++      assert (outregs->lrv_vreg[0].d == FUNC_DOUBLE_RET);
++    }
++  else if (strcmp (symname, "tst_audit27_func_ldouble") == 0)
++    {
++      assert (inregs->lr_vreg[0].q == FUNC_LDOUBLE_ARG0);
++      assert (inregs->lr_vreg[1].q == FUNC_LDOUBLE_ARG1);
++      assert (inregs->lr_vreg[2].q == FUNC_LDOUBLE_ARG2);
++      assert (inregs->lr_vreg[3].q == FUNC_LDOUBLE_ARG3);
++      assert (inregs->lr_vreg[4].q == FUNC_LDOUBLE_ARG4);
++      assert (inregs->lr_vreg[5].q == FUNC_LDOUBLE_ARG5);
++      assert (inregs->lr_vreg[6].q == FUNC_LDOUBLE_ARG6);
++      assert (inregs->lr_vreg[7].q == FUNC_LDOUBLE_ARG7);
++
++      assert (outregs->lrv_vreg[0].q == FUNC_LDOUBLE_RET);
++    }
++  else
++    abort ();
++
++  assert (inregs->lr_vpcs == 0);
++  assert (outregs->lrv_vpcs == 0);
++
++  /* Clobber the q registers on exit.  */
++  uint8_t v = 0xff;
++  asm volatile ("dup v0.8b, %w0" : : "r" (v) : "v0");
++  asm volatile ("dup v1.8b, %w0" : : "r" (v) : "v1");
++  asm volatile ("dup v2.8b, %w0" : : "r" (v) : "v2");
++  asm volatile ("dup v3.8b, %w0" : : "r" (v) : "v3");
++  asm volatile ("dup v4.8b, %w0" : : "r" (v) : "v4");
++  asm volatile ("dup v5.8b, %w0" : : "r" (v) : "v5");
++  asm volatile ("dup v6.8b, %w0" : : "r" (v) : "v6");
++  asm volatile ("dup v7.8b, %w0" : : "r" (v) : "v7");
++
++  return 0;
++}
+diff --git a/sysdeps/generic/dl-audit-check.h b/sysdeps/generic/dl-audit-check.h
+new file mode 100644
+index 0000000000000000..3ab76532868b5895
+--- /dev/null
++++ b/sysdeps/generic/dl-audit-check.h
+@@ -0,0 +1,23 @@
++/* rtld-audit version check.  Generic version.
++   Copyright (C) 2022 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/>.  */
++
++static inline bool
++_dl_audit_check_version (unsigned int lav)
++{
++  return lav <= LAV_CURRENT;
++}
diff --git a/SOURCES/glibc-rh2047981-35.patch b/SOURCES/glibc-rh2047981-35.patch
new file mode 100644
index 0000000000000000000000000000000000000000..92ecdc64bfa7ccf5a17e9b8d83731991582fd40f
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-35.patch
@@ -0,0 +1,21 @@
+commit 80a08d0faa9b224019f895800c4d97de4e23e1aa
+Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
+Date:   Wed Feb 2 14:03:58 2022 +0000
+
+    Fix elf/tst-audit25a with default bind now toolchains
+    
+    This test relies on lazy binding for the executable so request that
+    explicitly in case the toolchain defaults to bind now.
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 78147ed2dbcaf4c0..4d16ed1637db8582 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -2130,6 +2130,7 @@ $(objpfx)tst-audit25a: $(objpfx)tst-audit25mod1.so \
+ 		       $(objpfx)tst-audit25mod2.so \
+ 		       $(objpfx)tst-audit25mod3.so \
+ 		       $(objpfx)tst-audit25mod4.so
++LDFLAGS-tst-audit25a = -Wl,-z,lazy
+ $(objpfx)tst-audit25mod1.so: $(objpfx)tst-audit25mod3.so
+ LDFLAGS-tst-audit25mod1.so = -Wl,-z,now
+ $(objpfx)tst-audit25mod2.so: $(objpfx)tst-audit25mod4.so
diff --git a/SOURCES/glibc-rh2047981-36.patch b/SOURCES/glibc-rh2047981-36.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ceaec7242d8fe4ef289e5531fe0bffdffe8de373
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-36.patch
@@ -0,0 +1,28 @@
+commit fa7ad1df1915c8a62f50e3a5b7e10f9c7118cd7f
+Author: H.J. Lu <hjl.tools@gmail.com>
+Date:   Sun Feb 6 11:12:24 2022 -0800
+
+    elf: Replace tst-audit24bmod2.so with tst-audit24bmod2
+    
+    Replace tst-audit24bmod2.so with tst-audit24bmod2 to silence:
+    
+    make[2]: Entering directory '/export/gnu/import/git/gitlab/x86-glibc/elf'
+    Makefile:2201: warning: overriding recipe for target '/export/build/gnu/tools-build/glibc-gitlab/build-x86_64-linux/elf/tst-audit24bmod2.so'
+    ../Makerules:765: warning: ignoring old recipe for target '/export/build/gnu/tools-build/glibc-gitlab/build-x86_64-linux/elf/tst-audit24bmod2.so'
+
+Conflicts:
+	elf/Makefile
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 4d16ed1637db8582..73d347339762fc9e 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -855,7 +855,7 @@ extra-test-objs += $(addsuffix .os,$(strip $(modules-names)))
+ 
+ # filtmod1.so has a special rule
+ modules-names-nobuild := filtmod1 \
+-			 tst-audit24bmod1 tst-audit24bmod2.so
++			 tst-audit24bmod1 tst-audit24bmod2
+ 
+ tests += $(tests-static)
+ 
diff --git a/SOURCES/glibc-rh2047981-37.patch b/SOURCES/glibc-rh2047981-37.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8591468ef0360953261733a84c1d28304c61b1f2
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-37.patch
@@ -0,0 +1,112 @@
+commit 9e94f57484a2aba0fe67ea2059b5843f651887c2
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Fri Feb 4 15:54:59 2022 -0300
+
+    hppa: Fix bind-now audit (BZ #28857)
+    
+    On hppa, a function pointer returned by la_symbind is actually a function
+    descriptor has the plabel bit set (bit 30).  This must be cleared to get
+    the actual address of the descriptor.  If the descriptor has been bound,
+    the first word of the descriptor is the physical address of theA function,
+    otherwise, the first word of the descriptor points to a trampoline in the
+    PLT.
+    
+    This patch also adds a workaround on tests because on hppa (and it seems
+    to be the only ABI I have see it), some shared library adds a dynamic PLT
+    relocation to am empty symbol name:
+    
+    $ readelf -r elf/tst-audit25mod1.so
+    [...]
+    Relocation section '.rela.plt' at offset 0x464 contains 6 entries:
+     Offset     Info    Type            Sym.Value  Sym. Name + Addend
+    00002008  00000081 R_PARISC_IPLT                508
+    [...]
+    
+    It breaks some assumptions on the test, where a symbol with an empty
+    name ("") is passed on la_symbind.
+    
+    Checked on x86_64-linux-gnu and hppa-linux-gnu.
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 73d347339762fc9e..6d39b400060a73f3 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -2113,7 +2113,7 @@ $(objpfx)tst-audit24c.out: $(objpfx)tst-auditmod24c.so
+ $(objpfx)tst-audit24c: $(objpfx)tst-audit24amod1.so \
+ 		       $(objpfx)tst-audit24amod2.so
+ tst-audit24c-ENV = LD_BIND_NOW=1 LD_AUDIT=$(objpfx)tst-auditmod24c.so
+-LDFLAGS-tst-audit24b = -Wl,-z,lazy
++LDFLAGS-tst-audit24c = -Wl,-z,lazy
+ 
+ $(objpfx)tst-audit24d.out: $(objpfx)tst-auditmod24d.so
+ $(objpfx)tst-audit24d: $(objpfx)tst-audit24dmod1.so \
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+index 72a50717ef60a357..ec9b032eae37c103 100644
+--- a/elf/dl-audit.c
++++ b/elf/dl-audit.c
+@@ -257,7 +257,8 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
+       reloc_result->flags = flags;
+     }
+ 
+-  DL_FIXUP_BINDNOW_RELOC (value, new_value, sym.st_value);
++  if (flags & LA_SYMB_ALTVALUE)
++    DL_FIXUP_BINDNOW_RELOC (value, new_value, sym.st_value);
+ }
+ 
+ void
+diff --git a/elf/tst-auditmod24a.c b/elf/tst-auditmod24a.c
+index d8e88f3984af1707..3075dfae2fd3d288 100644
+--- a/elf/tst-auditmod24a.c
++++ b/elf/tst-auditmod24a.c
+@@ -110,5 +110,7 @@ la_symbind32 (Elf32_Sym *sym, unsigned int ndx,
+       return sym->st_value;
+     }
+ 
+-  abort ();
++  if (symname[0] != '\0')
++    abort ();
++  return sym->st_value;
+ }
+diff --git a/elf/tst-auditmod24d.c b/elf/tst-auditmod24d.c
+index 8c803ecc0a48f21b..badc6be451ee0357 100644
+--- a/elf/tst-auditmod24d.c
++++ b/elf/tst-auditmod24d.c
+@@ -116,5 +116,7 @@ la_symbind32 (Elf32_Sym *sym, unsigned int ndx,
+ 	}
+     }
+ 
+-  abort ();
++  if (symname[0] != '\0')
++    abort ();
++  return sym->st_value;
+ }
+diff --git a/elf/tst-auditmod25.c b/elf/tst-auditmod25.c
+index 526f5c54bc2c3b8c..20640a8daf346b5f 100644
+--- a/elf/tst-auditmod25.c
++++ b/elf/tst-auditmod25.c
+@@ -72,7 +72,7 @@ la_symbind32 (Elf32_Sym *sym, unsigned int ndx,
+ 	      unsigned int *flags, const char *symname)
+ #endif
+ {
+-  if (*refcook != -1 && *defcook != -1)
++  if (*refcook != -1 && *defcook != -1 && symname[0] != '\0')
+     fprintf (stderr, "la_symbind: %s %u\n", symname,
+ 	     *flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) ? 1 : 0);
+   return sym->st_value;
+diff --git a/sysdeps/hppa/dl-lookupcfg.h b/sysdeps/hppa/dl-lookupcfg.h
+index c3fea1fe5776b17a..86f6a04af46c87ba 100644
+--- a/sysdeps/hppa/dl-lookupcfg.h
++++ b/sysdeps/hppa/dl-lookupcfg.h
+@@ -79,7 +79,9 @@ void attribute_hidden _dl_unmap (struct link_map *map);
+ /* Extract the code address from a fixup value */
+ #define DL_FIXUP_VALUE_CODE_ADDR(value) ((value).ip)
+ #define DL_FIXUP_VALUE_ADDR(value) ((uintptr_t) &(value))
+-#define DL_FIXUP_ADDR_VALUE(addr) (*(struct fdesc *) (addr))
++/* Clear the plabel bit to get the actual address of the descriptor.  */
++#define DL_FIXUP_ADDR_VALUE(addr) \
++  (*(DL_FIXUP_VALUE_TYPE *) ((uintptr_t) (addr) & ~2))
+ #define DL_FIXUP_BINDNOW_ADDR_VALUE(addr) (addr)
+-#define DL_FIXUP_BINDNOW_RELOC(value, new_value, st_value) \
+-  (*value) = *(struct fdesc *) (st_value)
++#define DL_FIXUP_BINDNOW_RELOC(value, new_value, st_value)	\
++  *(value) = *(DL_FIXUP_VALUE_TYPE *) ((uintptr_t) (new_value) & ~2)
diff --git a/SOURCES/glibc-rh2047981-38.patch b/SOURCES/glibc-rh2047981-38.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5e7b79f60b2502c7c14b12e6802f2ddb60622970
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-38.patch
@@ -0,0 +1,44 @@
+commit bc02f1fa2fb302eb8a486794c6b7e4811229b81e
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Fri Mar 25 08:53:42 2022 -0300
+
+    elf: Remove unused functions from tst-audit25(a,b)
+
+diff --git a/elf/tst-audit25a.c b/elf/tst-audit25a.c
+index 49173e862516e876..c2cff8541b3741c3 100644
+--- a/elf/tst-audit25a.c
++++ b/elf/tst-audit25a.c
+@@ -49,14 +49,6 @@ handle_restart (void)
+   return 0;
+ }
+ 
+-static inline bool
+-startswith (const char *str, const char *pre)
+-{
+-  size_t lenpre = strlen (pre);
+-  size_t lenstr = strlen (str);
+-  return lenstr < lenpre ? false : memcmp (pre, str, lenpre) == 0;
+-}
+-
+ static int
+ do_test (int argc, char *argv[])
+ {
+diff --git a/elf/tst-audit25b.c b/elf/tst-audit25b.c
+index a56638d501f9bff5..46391770fdfc1796 100644
+--- a/elf/tst-audit25b.c
++++ b/elf/tst-audit25b.c
+@@ -48,14 +48,6 @@ handle_restart (void)
+   return 0;
+ }
+ 
+-static inline bool
+-startswith (const char *str, const char *pre)
+-{
+-  size_t lenpre = strlen (pre);
+-  size_t lenstr = strlen (str);
+-  return lenstr >= lenpre && memcmp (pre, str, lenpre) == 0;
+-}
+-
+ static int
+ do_test (int argc, char *argv[])
+ {
diff --git a/SOURCES/glibc-rh2047981-39.patch b/SOURCES/glibc-rh2047981-39.patch
new file mode 100644
index 0000000000000000000000000000000000000000..36247b18dfffd1a493e545b95ae007c0ee041bd7
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-39.patch
@@ -0,0 +1,42 @@
+commit 5325233313c66aea13e86f5dd59618e9dd74b510
+Author: Stefan Liebler <stli@linux.ibm.com>
+Date:   Thu Apr 7 13:59:48 2022 +0200
+
+    S390: Fix elf/tst-audit25[ab]
+    
+    If glibc is configured with --disable-default-pie and build on
+    s390 with -O3, the tests elf/tst-audit25a and elf/tst-audit25b are
+    failing as there are additional la_symbind lines for free and malloc.
+    It turns out that those belong to the executable. In fact those are
+    the PLT-stubs. Furthermore la_symbind is also called for calloc and
+    realloc symbols, but those belong to libc.
+    
+    Those functions are not called at all, but dlsym'ed in
+    elf/dl-minimal.c:
+    __rtld_malloc_init_real (struct link_map *main_map)
+    {
+    ...
+      void *new_calloc = lookup_malloc_symbol (main_map, "calloc", &version);
+      void *new_free = lookup_malloc_symbol (main_map, "free", &version);
+      void *new_malloc = lookup_malloc_symbol (main_map, "malloc", &version);
+      void *new_realloc = lookup_malloc_symbol (main_map, "realloc", &version);
+    ...
+    }
+    
+    Therefore, this commit just ignored symbols with LA_SYMB_DLSYM flag.
+    Reviewed-by: Adheemrval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/elf/tst-auditmod25.c b/elf/tst-auditmod25.c
+index 20640a8daf346b5f..0524c5aab17fabba 100644
+--- a/elf/tst-auditmod25.c
++++ b/elf/tst-auditmod25.c
+@@ -72,7 +72,8 @@ la_symbind32 (Elf32_Sym *sym, unsigned int ndx,
+ 	      unsigned int *flags, const char *symname)
+ #endif
+ {
+-  if (*refcook != -1 && *defcook != -1 && symname[0] != '\0')
++  if (*refcook != -1 && *defcook != -1 && symname[0] != '\0'
++      && (*flags & LA_SYMB_DLSYM) == 0)
+     fprintf (stderr, "la_symbind: %s %u\n", symname,
+ 	     *flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT) ? 1 : 0);
+   return sym->st_value;
diff --git a/SOURCES/glibc-rh2047981-4.patch b/SOURCES/glibc-rh2047981-4.patch
new file mode 100644
index 0000000000000000000000000000000000000000..86468bde1f4bbfe275263a280ac18ca8c160a882
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-4.patch
@@ -0,0 +1,34 @@
+commit 3ad5dab476205d6e16156cf0511fa6884b3b0fc4
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Tue Jul 7 09:58:45 2020 +0200
+
+    elf: Do not signal LA_ACT_CONSISTENT for an empty namespace [BZ #26076]
+    
+    The auditing interface identifies namespaces by their first loaded
+    module.  Once the namespace is empty, it is no longer possible to signal
+    LA_ACT_CONSISTENT for it because the first loaded module is already gone
+    at that point.
+    
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+
+diff --git a/elf/dl-close.c b/elf/dl-close.c
+index 7fe91bdd9aaf694e..698bda929c0eab6c 100644
+--- a/elf/dl-close.c
++++ b/elf/dl-close.c
+@@ -795,8 +795,14 @@ _dl_close_worker (struct link_map *map, bool force)
+   if (__glibc_unlikely (do_audit))
+     {
+       struct link_map *head = ns->_ns_loaded;
+-      /* Do not call the functions for any auditing object.  */
+-      if (head->l_auditing == 0)
++      /* If head is NULL, the namespace has become empty, and the
++	 audit interface does not give us a way to signal
++	 LA_ACT_CONSISTENT for it because the first loaded module is
++	 used to identify the namespace.
++
++	 Furthermore, do not notify auditors of the cleanup of a
++	 failed audit module loading attempt.  */
++      if (head != NULL && head->l_auditing == 0)
+ 	{
+ 	  struct audit_ifaces *afct = GLRO(dl_audit);
+ 	  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
diff --git a/SOURCES/glibc-rh2047981-40.patch b/SOURCES/glibc-rh2047981-40.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f1f4e80411a098cb65198266f1a83947239d9c08
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-40.patch
@@ -0,0 +1,170 @@
+commit e4a2fb76efb45210c541ee3f8ef32f317783c3a8
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Wed May 11 20:30:49 2022 +0200
+
+    manual: Document the dlinfo function
+    
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+    Tested-by: Carlos O'Donell <carlos@rehdat.com>
+    (cherry picked from commit 93804a1ee084d4bdc620b2b9f91615c7da0fabe1)
+    
+    Also includes partial backport of commit 5d28a8962dcb6ec056b81d730e
+    (the addition of manual/dynlink.texi).
+
+diff --git a/manual/Makefile b/manual/Makefile
+index c2756640a785afe1..4c835e568f3bab67 100644
+--- a/manual/Makefile
++++ b/manual/Makefile
+@@ -39,7 +39,7 @@ chapters = $(addsuffix .texi, \
+ 		       pipe socket terminal syslog math arith time	\
+ 		       resource setjmp signal startup process ipc job	\
+ 		       nss users sysinfo conf crypt debug threads	\
+-		       probes tunables)
++		       dynlink probes tunables)
+ appendices = lang.texi header.texi install.texi maint.texi platform.texi \
+ 	     contrib.texi
+ licenses = freemanuals.texi lgpl-2.1.texi fdl-1.3.texi
+diff --git a/manual/dynlink.texi b/manual/dynlink.texi
+new file mode 100644
+index 0000000000000000..dbf3de11769d8e57
+--- /dev/null
++++ b/manual/dynlink.texi
+@@ -0,0 +1,100 @@
++@node Dynamic Linker
++@c @node Dynamic Linker, Internal Probes, Threads, Top
++@c %MENU% Loading programs and shared objects.
++@chapter Dynamic Linker
++@cindex dynamic linker
++@cindex dynamic loader
++
++The @dfn{dynamic linker} is responsible for loading dynamically linked
++programs and their dependencies (in the form of shared objects).  The
++dynamic linker in @theglibc{} also supports loading shared objects (such
++as plugins) later at run time.
++
++Dynamic linkers are sometimes called @dfn{dynamic loaders}.
++
++@menu
++* Dynamic Linker Introspection::    Interfaces for querying mapping information.
++@end menu
++
++@node Dynamic Linker Introspection
++@section Dynamic Linker Introspection
++
++@Theglibc{} provides various functions for querying information from the
++dynamic linker.
++
++@deftypefun {int} dlinfo (void *@var{handle}, int @var{request}, void *@var{arg})
++@safety{@mtsafe{}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
++@standards{GNU, dlfcn.h}
++This function returns information about @var{handle} in the memory
++location @var{arg}, based on @var{request}.  The @var{handle} argument
++must be a pointer returned by @code{dlopen} or @code{dlmopen}; it must
++not have been closed by @code{dlclose}.
++
++On success, @code{dlinfo} returns 0.  If there is an error, the function
++returns @math{-1}, and @code{dlerror} can be used to obtain a
++corresponding error message.
++
++The following operations are defined for use with @var{request}:
++
++@vtable @code
++@item RTLD_DI_LINKMAP
++The corresponding @code{struct link_map} pointer for @var{handle} is
++written to @code{*@var{arg}}.  The @var{arg} argument must be the
++address of an object of type @code{struct link_map *}.
++
++@item RTLD_DI_LMID
++The namespace identifier of @var{handle} is written to
++@code{*@var{arg}}.  The @var{arg} argument must be the address of an
++object of type @code{Lmid_t}.
++
++@item RTLD_DI_ORIGIN
++The value of the @code{$ORIGIN} dynamic string token for @var{handle} is
++written to the character array starting at @var{arg} as a
++null-terminated string.
++
++This request type should not be used because it is prone to buffer
++overflows.
++
++@item RTLD_DI_SERINFO
++@itemx RTLD_DI_SERINFOSIZE
++These requests can be used to obtain search path information for
++@var{handle}.  For both requests, @var{arg} must point to a
++@code{Dl_serinfo} object.  The @code{RTLD_DI_SERINFOSIZE} request must
++be made first; it updates the @code{dls_size} and @code{dls_cnt} members
++of the @code{Dl_serinfo} object.  The caller should then allocate memory
++to store at least @code{dls_size} bytes and pass that buffer to a
++@code{RTLD_DI_SERINFO} request.  This second request fills the
++@code{dls_serpath} array.  The number of array elements was returned in
++the @code{dls_cnt} member in the initial @code{RTLD_DI_SERINFOSIZE}
++request.  The caller is responsible for freeing the allocated buffer.
++
++This interface is prone to buffer overflows in multi-threaded processes
++because the required size can change between the
++@code{RTLD_DI_SERINFOSIZE} and @code{RTLD_DI_SERINFO} requests.
++
++@item RTLD_DI_TLS_DATA
++This request writes the address of the TLS block (in the current thread)
++for the shared object identified by @var{handle} to @code{*@var{arg}}.
++The argument @var{arg} must be the address of an object of type
++@code{void *}.  A null pointer is written if the object does not have
++any associated TLS block.
++
++@item RTLD_DI_TLS_MODID
++This request writes the TLS module ID for the shared object @var{handle}
++to @code{*@var{arg}}.  The argument @var{arg} must be the address of an
++object of type @code{size_t}.  The module ID is zero if the object
++does not have an associated TLS block.
++@end vtable
++
++The @code{dlinfo} function is a GNU extension.
++@end deftypefun
++
++@c FIXME these are undocumented:
++@c dladdr
++@c dladdr1
++@c dlclose
++@c dlerror
++@c dlmopen
++@c dlopen
++@c dlsym
++@c dlvsym
+diff --git a/manual/libdl.texi b/manual/libdl.texi
+deleted file mode 100644
+index e3fe0452d9f41d47..0000000000000000
+--- a/manual/libdl.texi
++++ /dev/null
+@@ -1,10 +0,0 @@
+-@c FIXME these are undocumented:
+-@c dladdr
+-@c dladdr1
+-@c dlclose
+-@c dlerror
+-@c dlinfo
+-@c dlmopen
+-@c dlopen
+-@c dlsym
+-@c dlvsym
+diff --git a/manual/probes.texi b/manual/probes.texi
+index 0ea560ed78bcfd7e..892d2451938eb379 100644
+--- a/manual/probes.texi
++++ b/manual/probes.texi
+@@ -1,5 +1,5 @@
+ @node Internal Probes
+-@c @node Internal Probes, Tunables, Threads, Top
++@c @node Internal Probes, Tunables, Dynamic Linker, Top
+ @c %MENU% Probes to monitor libc internal behavior
+ @chapter Internal probes
+ 
+diff --git a/manual/threads.texi b/manual/threads.texi
+index 87fda7d8e716e08c..1c26c57540746e3b 100644
+--- a/manual/threads.texi
++++ b/manual/threads.texi
+@@ -1,5 +1,5 @@
+ @node Threads
+-@c @node Threads, Internal Probes, Debugging Support, Top
++@c @node Threads, Dynamic Linker, Debugging Support, Top
+ @c %MENU% Functions, constants, and data types for working with threads
+ @chapter Threads
+ @cindex threads
diff --git a/SOURCES/glibc-rh2047981-41.patch b/SOURCES/glibc-rh2047981-41.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a92e82dccc3a5556f9e42edad4c2c241288aa89f
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-41.patch
@@ -0,0 +1,268 @@
+Added $(objpfx)tst-dlinfo-phdr: $(libdl) to dlfcn/Makefile since
+we still need $(libdl) in RHEL8.
+
+commit d056c212130280c0a54d9a4f72170ec621b70ce5
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Fri Apr 29 17:00:53 2022 +0200
+
+    dlfcn: Implement the RTLD_DI_PHDR request type for dlinfo
+    
+    The information is theoretically available via dl_iterate_phdr as
+    well, but that approach is very slow if there are many shared
+    objects.
+    
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+    Tested-by: Carlos O'Donell <carlos@rehdat.com>
+
+Conflicts:
+	dlfcn/dlinfo.c
+	  (missing move into libc)
+
+diff --git a/dlfcn/Makefile b/dlfcn/Makefile
+index 0b213b7d9fefcdc9..65cee5b54d891a24 100644
+--- a/dlfcn/Makefile
++++ b/dlfcn/Makefile
+@@ -59,6 +59,10 @@ tststatic3-ENV = $(tststatic-ENV)
+ tststatic4-ENV = $(tststatic-ENV)
+ tststatic5-ENV = $(tststatic-ENV)
+ 
++tests-internal += \
++  tst-dlinfo-phdr \
++  # tests-internal
++
+ ifneq (,$(CXX))
+ modules-names += bug-atexit3-lib
+ else
+@@ -152,3 +156,5 @@ $(objpfx)bug-dl-leaf-lib-cb.so: $(objpfx)bug-dl-leaf-lib.so
+ 
+ $(objpfx)tst-rec-dlopen: $(libdl)
+ $(objpfx)tst-rec-dlopen.out: $(objpfx)moddummy1.so $(objpfx)moddummy2.so
++
++$(objpfx)tst-dlinfo-phdr: $(libdl)
+diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h
+index 0921fd724cf7b785..61c4f59bea4eb7ac 100644
+--- a/dlfcn/dlfcn.h
++++ b/dlfcn/dlfcn.h
+@@ -162,7 +162,12 @@ enum
+        segment, or if the calling thread has not allocated a block for it.  */
+     RTLD_DI_TLS_DATA = 10,
+ 
+-    RTLD_DI_MAX = 10
++    /* Treat ARG as const ElfW(Phdr) **, and store the address of the
++       program header array at that location.  The dlinfo call returns
++       the number of program headers in the array.  */
++    RTLD_DI_PHDR = 11,
++
++    RTLD_DI_MAX = 11
+   };
+ 
+ 
+diff --git a/dlfcn/dlinfo.c b/dlfcn/dlinfo.c
+index 23ef3f57ca41afdf..50cd9af17a56f990 100644
+--- a/dlfcn/dlinfo.c
++++ b/dlfcn/dlinfo.c
+@@ -38,6 +38,10 @@ struct dlinfo_args
+   void *handle;
+   int request;
+   void *arg;
++
++  /* This is the value that is returned from dlinfo if no error is
++     signaled.  */
++  int result;
+ };
+ 
+ static void
+@@ -50,6 +54,7 @@ dlinfo_doit (void *argsblock)
+     {
+     case RTLD_DI_CONFIGADDR:
+     default:
++      args->result = -1;
+       _dl_signal_error (0, NULL, NULL, N_("unsupported dlinfo request"));
+       break;
+ 
+@@ -85,6 +90,11 @@ dlinfo_doit (void *argsblock)
+ 	*(void **) args->arg = data;
+ 	break;
+       }
++
++    case RTLD_DI_PHDR:
++      *(const ElfW(Phdr) **) args->arg = l->l_phdr;
++      args->result = l->l_phnum;
++      break;
+     }
+ }
+ 
+@@ -97,7 +107,8 @@ __dlinfo (void *handle, int request, void *arg)
+ # endif
+ 
+   struct dlinfo_args args = { handle, request, arg };
+-  return _dlerror_run (&dlinfo_doit, &args) ? -1 : 0;
++  _dlerror_run (&dlinfo_doit, &args);
++  return args.result;
+ }
+ # ifdef SHARED
+ strong_alias (__dlinfo, dlinfo)
+diff --git a/dlfcn/tst-dlinfo-phdr.c b/dlfcn/tst-dlinfo-phdr.c
+new file mode 100644
+index 0000000000000000..a15a7d48ebd3b976
+--- /dev/null
++++ b/dlfcn/tst-dlinfo-phdr.c
+@@ -0,0 +1,125 @@
++/* Test for dlinfo (RTLD_DI_PHDR).
++   Copyright (C) 2022 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/>.  */
++
++#include <dlfcn.h>
++#include <link.h>
++#include <stdbool.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/auxv.h>
++
++#include <support/check.h>
++#include <support/xdlfcn.h>
++
++/* Used to verify that the program header array appears as expected
++   among the dl_iterate_phdr callback invocations.  */
++
++struct dlip_callback_args
++{
++  struct link_map *l;           /* l->l_addr is used to find the object.  */
++  const ElfW(Phdr) *phdr;       /* Expected program header pointed.  */
++  int phnum;                    /* Expected program header count.  */
++  bool found;                   /* True if l->l_addr has been found.  */
++};
++
++static int
++dlip_callback (struct dl_phdr_info *dlpi, size_t size, void *closure)
++{
++  TEST_COMPARE (sizeof (*dlpi), size);
++  struct dlip_callback_args *args = closure;
++
++  if (dlpi->dlpi_addr == args->l->l_addr)
++    {
++      TEST_VERIFY (!args->found);
++      args->found = true;
++      TEST_VERIFY (args->phdr == dlpi->dlpi_phdr);
++      TEST_COMPARE (args->phnum, dlpi->dlpi_phnum);
++    }
++
++  return 0;
++}
++
++static int
++do_test (void)
++{
++  /* Avoid a copy relocation.  */
++  struct r_debug *debug = xdlsym (RTLD_DEFAULT, "_r_debug");
++  struct link_map *l = (struct link_map *) debug->r_map;
++  TEST_VERIFY_EXIT (l != NULL);
++
++  do
++    {
++      printf ("info: checking link map %p (%p) for \"%s\"\n",
++              l, l->l_phdr, l->l_name);
++
++      /* Cause dlerror () to return an error message.  */
++      dlsym (RTLD_DEFAULT, "does-not-exist");
++
++      /* Use the extension that link maps are valid dlopen handles.  */
++      const ElfW(Phdr) *phdr;
++      int phnum = dlinfo (l, RTLD_DI_PHDR, &phdr);
++      TEST_VERIFY (phnum >= 0);
++      /* Verify that the error message has been cleared.  */
++      TEST_COMPARE_STRING (dlerror (), NULL);
++
++      TEST_VERIFY (phdr == l->l_phdr);
++      TEST_COMPARE (phnum, l->l_phnum);
++
++      /* Check that we can find PT_DYNAMIC among the array.  */
++      {
++        bool dynamic_found = false;
++        for (int i = 0; i < phnum; ++i)
++          if (phdr[i].p_type == PT_DYNAMIC)
++            {
++              dynamic_found = true;
++              TEST_COMPARE ((ElfW(Addr)) l->l_ld, l->l_addr + phdr[i].p_vaddr);
++            }
++        TEST_VERIFY (dynamic_found);
++      }
++
++      /* Check that dl_iterate_phdr finds the link map with the same
++         program headers.  */
++      {
++        struct dlip_callback_args args =
++          {
++            .l =  l,
++            .phdr = phdr,
++            .phnum = phnum,
++            .found = false,
++          };
++        TEST_COMPARE (dl_iterate_phdr (dlip_callback, &args), 0);
++        TEST_VERIFY (args.found);
++      }
++
++      if (l->l_prev == NULL)
++        {
++          /* This is the executable, so the information is also
++             available via getauxval.  */
++          TEST_COMPARE_STRING (l->l_name, "");
++          TEST_VERIFY (phdr == (const ElfW(Phdr) *) getauxval (AT_PHDR));
++          TEST_COMPARE (phnum, getauxval (AT_PHNUM));
++        }
++
++      l = l->l_next;
++    }
++  while (l != NULL);
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/manual/dynlink.texi b/manual/dynlink.texi
+index dbf3de11769d8e57..7dcac64889e389fd 100644
+--- a/manual/dynlink.texi
++++ b/manual/dynlink.texi
+@@ -30,9 +30,9 @@ location @var{arg}, based on @var{request}.  The @var{handle} argument
+ must be a pointer returned by @code{dlopen} or @code{dlmopen}; it must
+ not have been closed by @code{dlclose}.
+ 
+-On success, @code{dlinfo} returns 0.  If there is an error, the function
+-returns @math{-1}, and @code{dlerror} can be used to obtain a
+-corresponding error message.
++On success, @code{dlinfo} returns 0 for most request types; exceptions
++are noted below.  If there is an error, the function returns @math{-1},
++and @code{dlerror} can be used to obtain a corresponding error message.
+ 
+ The following operations are defined for use with @var{request}:
+ 
+@@ -84,6 +84,15 @@ This request writes the TLS module ID for the shared object @var{handle}
+ to @code{*@var{arg}}.  The argument @var{arg} must be the address of an
+ object of type @code{size_t}.  The module ID is zero if the object
+ does not have an associated TLS block.
++
++@item RTLD_DI_PHDR
++This request writes the address of the program header array to
++@code{*@var{arg}}.  The argument @var{arg} must be the address of an
++object of type @code{const ElfW(Phdr) *} (that is,
++@code{const Elf32_Phdr *} or @code{const Elf64_Phdr *}, as appropriate
++for the current architecture).  For this request, the value returned by
++@code{dlinfo} is the number of program headers in the program header
++array.
+ @end vtable
+ 
+ The @code{dlinfo} function is a GNU extension.
diff --git a/SOURCES/glibc-rh2047981-42.patch b/SOURCES/glibc-rh2047981-42.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d280e1a928cb01153618cec18a4437eff9f64433
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-42.patch
@@ -0,0 +1,296 @@
+commit ad43cac44a6860eaefcadadfb2acb349921e96bf
+Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
+Date:   Fri Jun 15 16:14:58 2018 +0100
+
+    rtld: Use generic argv adjustment in ld.so [BZ #23293]
+    
+    When an executable is invoked as
+    
+      ./ld.so [ld.so-args] ./exe [exe-args]
+    
+    then the argv is adujusted in ld.so before calling the entry point of
+    the executable so ld.so args are not visible to it.  On most targets
+    this requires moving argv, env and auxv on the stack to ensure correct
+    stack alignment at the entry point.  This had several issues:
+    
+    - The code for this adjustment on the stack is written in asm as part
+      of the target specific ld.so _start code which is hard to maintain.
+    
+    - The adjustment is done after _dl_start returns, where it's too late
+      to update GLRO(dl_auxv), as it is already readonly, so it points to
+      memory that was clobbered by the adjustment. This is bug 23293.
+    
+    - _environ is also wrong in ld.so after the adjustment, but it is
+      likely not used after _dl_start returns so this is not user visible.
+    
+    - _dl_argv was updated, but for this it was moved out of relro, which
+      changes security properties across targets unnecessarily.
+    
+    This patch introduces a generic _dl_start_args_adjust function that
+    handles the argument adjustments after ld.so processed its own args
+    and before relro protection is applied.
+    
+    The same algorithm is used on all targets, _dl_skip_args is now 0, so
+    existing target specific adjustment code is no longer used.  The bug
+    affects aarch64, alpha, arc, arm, csky, ia64, nios2, s390-32 and sparc,
+    other targets don't need the change in principle, only for consistency.
+    
+    The GNU Hurd start code relied on _dl_skip_args after dl_main returned,
+    now it checks directly if args were adjusted and fixes the Hurd startup
+    data accordingly.
+    
+    Follow up patches can remove _dl_skip_args and DL_ARGV_NOT_RELRO.
+    
+    Tested on aarch64-linux-gnu and cross tested on i686-gnu.
+    
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/elf/rtld.c b/elf/rtld.c
+index aee5ca357f66121e..22cceeab40319582 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -1127,6 +1127,62 @@ rtld_chain_load (struct link_map *main_map, char *argv0)
+ 		   rtld_soname, pathname, errcode);
+ }
+ 
++/* Adjusts the contents of the stack and related globals for the user
++   entry point.  The ld.so processed skip_args arguments and bumped
++   _dl_argv and _dl_argc accordingly.  Those arguments are removed from
++   argv here.  */
++static void
++_dl_start_args_adjust (int skip_args)
++{
++  void **sp = (void **) (_dl_argv - skip_args - 1);
++  void **p = sp + skip_args;
++
++  if (skip_args == 0)
++    return;
++
++  /* Sanity check.  */
++  intptr_t argc = (intptr_t) sp[0] - skip_args;
++  assert (argc == _dl_argc);
++
++  /* Adjust argc on stack.  */
++  sp[0] = (void *) (intptr_t) _dl_argc;
++
++  /* Update globals in rtld.  */
++  _dl_argv -= skip_args;
++  _environ -= skip_args;
++
++  /* Shuffle argv down.  */
++  do
++    *++sp = *++p;
++  while (*p != NULL);
++
++  assert (_environ == (char **) (sp + 1));
++
++  /* Shuffle envp down.  */
++  do
++    *++sp = *++p;
++  while (*p != NULL);
++
++#ifdef HAVE_AUX_VECTOR
++  void **auxv = (void **) GLRO(dl_auxv) - skip_args;
++  GLRO(dl_auxv) = (ElfW(auxv_t) *) auxv; /* Aliasing violation.  */
++  assert (auxv == sp + 1);
++
++  /* Shuffle auxv down. */
++  ElfW(auxv_t) ax;
++  char *oldp = (char *) (p + 1);
++  char *newp = (char *) (sp + 1);
++  do
++    {
++      memcpy (&ax, oldp, sizeof (ax));
++      memcpy (newp, &ax, sizeof (ax));
++      oldp += sizeof (ax);
++      newp += sizeof (ax);
++    }
++  while (ax.a_type != AT_NULL);
++#endif
++}
++
+ static void
+ dl_main (const ElfW(Phdr) *phdr,
+ 	 ElfW(Word) phnum,
+@@ -1185,6 +1241,7 @@ dl_main (const ElfW(Phdr) *phdr,
+       rtld_is_main = true;
+ 
+       char *argv0 = NULL;
++      char **orig_argv = _dl_argv;
+ 
+       /* Note the place where the dynamic linker actually came from.  */
+       GL(dl_rtld_map).l_name = rtld_progname;
+@@ -1199,7 +1256,6 @@ dl_main (const ElfW(Phdr) *phdr,
+ 		GLRO(dl_lazy) = -1;
+ 	      }
+ 
+-	    ++_dl_skip_args;
+ 	    --_dl_argc;
+ 	    ++_dl_argv;
+ 	  }
+@@ -1208,14 +1264,12 @@ dl_main (const ElfW(Phdr) *phdr,
+ 	    if (state.mode != rtld_mode_help)
+ 	      state.mode = rtld_mode_verify;
+ 
+-	    ++_dl_skip_args;
+ 	    --_dl_argc;
+ 	    ++_dl_argv;
+ 	  }
+ 	else if (! strcmp (_dl_argv[1], "--inhibit-cache"))
+ 	  {
+ 	    GLRO(dl_inhibit_cache) = 1;
+-	    ++_dl_skip_args;
+ 	    --_dl_argc;
+ 	    ++_dl_argv;
+ 	  }
+@@ -1225,7 +1279,6 @@ dl_main (const ElfW(Phdr) *phdr,
+ 	    state.library_path = _dl_argv[2];
+ 	    state.library_path_source = "--library-path";
+ 
+-	    _dl_skip_args += 2;
+ 	    _dl_argc -= 2;
+ 	    _dl_argv += 2;
+ 	  }
+@@ -1234,7 +1287,6 @@ dl_main (const ElfW(Phdr) *phdr,
+ 	  {
+ 	    GLRO(dl_inhibit_rpath) = _dl_argv[2];
+ 
+-	    _dl_skip_args += 2;
+ 	    _dl_argc -= 2;
+ 	    _dl_argv += 2;
+ 	  }
+@@ -1242,14 +1294,12 @@ dl_main (const ElfW(Phdr) *phdr,
+ 	  {
+ 	    audit_list_add_string (&state.audit_list, _dl_argv[2]);
+ 
+-	    _dl_skip_args += 2;
+ 	    _dl_argc -= 2;
+ 	    _dl_argv += 2;
+ 	  }
+ 	else if (! strcmp (_dl_argv[1], "--preload") && _dl_argc > 2)
+ 	  {
+ 	    state.preloadarg = _dl_argv[2];
+-	    _dl_skip_args += 2;
+ 	    _dl_argc -= 2;
+ 	    _dl_argv += 2;
+ 	  }
+@@ -1257,7 +1307,6 @@ dl_main (const ElfW(Phdr) *phdr,
+ 	  {
+ 	    argv0 = _dl_argv[2];
+ 
+-	    _dl_skip_args += 2;
+ 	    _dl_argc -= 2;
+ 	    _dl_argv += 2;
+ 	  }
+@@ -1265,7 +1314,6 @@ dl_main (const ElfW(Phdr) *phdr,
+ 		 && _dl_argc > 2)
+ 	  {
+ 	    state.glibc_hwcaps_prepend = _dl_argv[2];
+-	    _dl_skip_args += 2;
+ 	    _dl_argc -= 2;
+ 	    _dl_argv += 2;
+ 	  }
+@@ -1273,7 +1321,6 @@ dl_main (const ElfW(Phdr) *phdr,
+ 		 && _dl_argc > 2)
+ 	  {
+ 	    state.glibc_hwcaps_mask = _dl_argv[2];
+-	    _dl_skip_args += 2;
+ 	    _dl_argc -= 2;
+ 	    _dl_argv += 2;
+ 	  }
+@@ -1282,7 +1329,6 @@ dl_main (const ElfW(Phdr) *phdr,
+ 	  {
+ 	    state.mode = rtld_mode_list_tunables;
+ 
+-	    ++_dl_skip_args;
+ 	    --_dl_argc;
+ 	    ++_dl_argv;
+ 	  }
+@@ -1291,7 +1337,6 @@ dl_main (const ElfW(Phdr) *phdr,
+ 	  {
+ 	    state.mode = rtld_mode_list_diagnostics;
+ 
+-	    ++_dl_skip_args;
+ 	    --_dl_argc;
+ 	    ++_dl_argv;
+ 	  }
+@@ -1337,7 +1382,6 @@ dl_main (const ElfW(Phdr) *phdr,
+ 	    _dl_usage (ld_so_name, NULL);
+ 	}
+ 
+-      ++_dl_skip_args;
+       --_dl_argc;
+       ++_dl_argv;
+ 
+@@ -1433,6 +1477,9 @@ dl_main (const ElfW(Phdr) *phdr,
+       /* Set the argv[0] string now that we've processed the executable.  */
+       if (argv0 != NULL)
+         _dl_argv[0] = argv0;
++
++      /* Adjust arguments for the application entry point.  */
++      _dl_start_args_adjust (_dl_argv - orig_argv);
+     }
+   else
+     {
+diff --git a/sysdeps/mach/hurd/dl-sysdep.c b/sysdeps/mach/hurd/dl-sysdep.c
+index 7bd1d70c96c229e0..8aab46bf6396c8d4 100644
+--- a/sysdeps/mach/hurd/dl-sysdep.c
++++ b/sysdeps/mach/hurd/dl-sysdep.c
+@@ -107,6 +107,7 @@ _dl_sysdep_start (void **start_argptr,
+ {
+   void go (intptr_t *argdata)
+     {
++      char *orig_argv0;
+       char **p;
+ 
+       /* Cache the information in various global variables.  */
+@@ -115,6 +116,8 @@ _dl_sysdep_start (void **start_argptr,
+       _environ = &_dl_argv[_dl_argc + 1];
+       for (p = _environ; *p++;); /* Skip environ pointers and terminator.  */
+ 
++      orig_argv0 = _dl_argv[0];
++
+       if ((void *) p == _dl_argv[0])
+ 	{
+ 	  static struct hurd_startup_data nodata;
+@@ -189,30 +192,23 @@ unfmh();			/* XXX */
+ 
+       /* The call above might screw a few things up.
+ 
+-	 First of all, if _dl_skip_args is nonzero, we are ignoring
+-	 the first few arguments.  However, if we have no Hurd startup
+-	 data, it is the magical convention that ARGV[0] == P.  The
++	 P is the location after the terminating NULL of the list of
++	 environment variables.  It has to point to the Hurd startup
++	 data or if that's missing then P == ARGV[0] must hold. The
+ 	 startup code in init-first.c will get confused if this is not
+ 	 the case, so we must rearrange things to make it so.  We'll
+-	 overwrite the origional ARGV[0] at P with ARGV[_dl_skip_args].
++	 recompute P and move the Hurd data or the new ARGV[0] there.
+ 
+-	 Secondly, if we need to be secure, it removes some dangerous
+-	 environment variables.  If we have no Hurd startup date this
+-	 changes P (since that's the location after the terminating
+-	 NULL in the list of environment variables).  We do the same
+-	 thing as in the first case but make sure we recalculate P.
+-	 If we do have Hurd startup data, we have to move the data
+-	 such that it starts just after the terminating NULL in the
+-	 environment list.
++	 Note: directly invoked ld.so can move arguments and env vars.
+ 
+ 	 We use memmove, since the locations might overlap.  */
+-      if (__libc_enable_secure || _dl_skip_args)
+-	{
+-	  char **newp;
+ 
+-	  for (newp = _environ; *newp++;);
++      char **newp;
++      for (newp = _environ; *newp++;);
+ 
+-	  if (_dl_argv[-_dl_skip_args] == (char *) p)
++      if (newp != p || _dl_argv[0] != orig_argv0)
++	{
++	  if (orig_argv0 == (char *) p)
+ 	    {
+ 	      if ((char *) newp != _dl_argv[0])
+ 		{
diff --git a/SOURCES/glibc-rh2047981-43.patch b/SOURCES/glibc-rh2047981-43.patch
new file mode 100644
index 0000000000000000000000000000000000000000..13691c9bf69ea41b4ae8bb62d084d6c9119f03db
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-43.patch
@@ -0,0 +1,22 @@
+commit 62c888b3375f82a659a55ec66b1315efa2ed026a
+Author: Carlos O'Donell <carlos@redhat.com>
+Date:   Thu Jun 2 10:59:14 2022 -0400
+
+    elf: Add #include <sys/param.h> for MAX usage.
+    
+    In _dl_audit_pltenter we use MAX and so need to include param.h.
+    
+    Tested on x86_64 and i686 without regression.
+
+diff --git a/elf/dl-audit.c b/elf/dl-audit.c
+index ec9b032eae37c103..e20b7b40e08d79e7 100644
+--- a/elf/dl-audit.c
++++ b/elf/dl-audit.c
+@@ -22,6 +22,7 @@
+ #include <dl-machine.h>
+ #include <dl-runtime.h>
+ #include <dl-fixup-attribute.h>
++#include <sys/param.h>
+ 
+ void
+ _dl_audit_activity_map (struct link_map *l, int action)
diff --git a/SOURCES/glibc-rh2047981-44.patch b/SOURCES/glibc-rh2047981-44.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e5f9389cffc346222310c9b24c7047ff6828dbca
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-44.patch
@@ -0,0 +1,98 @@
+Downstream-only patch to change rtld_active () to return true during
+early audit operations.  GLRO (_dl_profile_output) is initialized much
+earlier than GLRO (dl_init_all_dirs), before auditors run, so it is a
+good replacement.
+
+This is addressed downstream very differently, in this commit:
+
+commit 8dcb6d0af07fda3607b541857e4f3970a74ed55b
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Tue Apr 26 14:23:02 2022 +0200
+
+    dlfcn: Do not use rtld_active () to determine ld.so state (bug 29078)
+
+    When audit modules are loaded, ld.so initialization is not yet
+    complete, and rtld_active () returns false even though ld.so is
+    mostly working.  Instead, the static dlopen hook is used, but that
+    does not work at all because this is not a static dlopen situation.
+
+    Commit 466c1ea15f461edb8e3ffaf5d86d708876343bbf ("dlfcn: Rework
+    static dlopen hooks") moved the hook pointer into _rtld_global_ro,
+    which means that separate protection is not needed anymore and the
+    hook pointer can be checked directly.
+
+    The guard for disabling libio vtable hardening in _IO_vtable_check
+    should stay for now.
+
+    Fixes commit 8e1472d2c1e25e6eabc2059170731365f6d5b3d1 ("ld.so:
+    Examine GLRO to detect inactive loader [BZ #20204]").
+
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/elf/rtld.c b/elf/rtld.c
+index 22cceeab40319582..b47e84ca2fb6f03c 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -2352,9 +2352,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
+   GLRO(dl_initial_searchlist) = *GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist;
+ 
+   /* Remember the last search directory added at startup, now that
+-     malloc will no longer be the one from dl-minimal.c.  As a side
+-     effect, this marks ld.so as initialized, so that the rtld_active
+-     function returns true from now on.  */
++     malloc will no longer be the one from dl-minimal.c.  */
+   GLRO(dl_init_all_dirs) = GL(dl_all_dirs);
+ 
+   /* Print scope information.  */
+@@ -2675,7 +2673,9 @@ process_envvars (struct dl_main_state *state)
+   char *envline;
+   char *debug_output = NULL;
+ 
+-  /* This is the default place for profiling data file.  */
++  /* This is the default place for profiling data file.  As a side
++     effect, this marks ld.so as initialized, so that the rtld_active
++     function returns true from now on.  */
+   GLRO(dl_profile_output)
+     = &"/var/tmp\0/var/profile"[__libc_enable_secure ? 9 : 0];
+ 
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index d4f70211c34d1c59..9dec9e3d3b6d6aa2 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -600,18 +600,18 @@ struct rtld_global_ro
+ 
+   /* Name of the shared object to be profiled (if any).  */
+   EXTERN const char *_dl_profile;
+-  /* Filename of the output file.  */
++  /* Filename of the output file.  This is assigned a
++     non-NULL pointer by the ld.so startup code (after initialization
++     to NULL), so this can also serve as an indicator whether a copy
++     of ld.so is initialized and active.  See the rtld_active function
++     below.  */
+   EXTERN const char *_dl_profile_output;
+   /* Name of the object we want to trace the prelinking.  */
+   EXTERN const char *_dl_trace_prelink;
+   /* Map of shared object to be prelink traced.  */
+   EXTERN struct link_map *_dl_trace_prelink_map;
+ 
+-  /* All search directories defined at startup.  This is assigned a
+-     non-NULL pointer by the ld.so startup code (after initialization
+-     to NULL), so this can also serve as an indicator whether a copy
+-     of ld.so is initialized and active.  See the rtld_active function
+-     below.  */
++  /* All search directories defined at startup.  */
+   EXTERN struct r_search_path_elem *_dl_init_all_dirs;
+ 
+ #ifdef NEED_DL_SYSINFO
+@@ -1259,9 +1259,9 @@ static inline bool
+ rtld_active (void)
+ {
+   /* The default-initialized variable does not have a non-zero
+-     dl_init_all_dirs member, so this allows us to recognize an
++     dl_profile_output member, so this allows us to recognize an
+      initialized and active ld.so copy.  */
+-  return GLRO(dl_init_all_dirs) != NULL;
++  return GLRO(dl_profile_output) != NULL;
+ }
+ 
+ static inline struct auditstate *
diff --git a/SOURCES/glibc-rh2047981-45.patch b/SOURCES/glibc-rh2047981-45.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0111ab94be1e0bd3d4c5716b2a3b6cf690bdbe2a
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-45.patch
@@ -0,0 +1,74 @@
+commit a64af8c9b6598f6d2685227f64f5ccb9b48c663c
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Mon May 10 10:31:41 2021 +0200
+
+    scripts/versions.awk: Add strings and hashes to <first-versions.h>
+    
+    This generates new macros of this from:
+    
+    They are useful for symbol lookups using _dl_lookup_direct.
+    
+    Tested-by: Carlos O'Donell <carlos@redhat.com>
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+
+diff --git a/scripts/versions.awk b/scripts/versions.awk
+index a3df316c703ea98b..0c900b83347ce8f9 100644
+--- a/scripts/versions.awk
++++ b/scripts/versions.awk
+@@ -32,6 +32,29 @@ BEGIN {
+   sort = "sort -t. -k 1,1 -k 2n,2n -k 3 > " tmpfile;
+ }
+ 
++# GNU awk does not implement the ord and chr functions.
++# <https://www.gnu.org/software/gawk/manual/html_node/Ordinal-Functions.html>
++# says that they are "written very nicely", using code similar to what
++# is included here.
++function chr(c) {
++    return sprintf("%c", c)
++}
++
++BEGIN {
++    for (c = 1; c < 127; c++) {
++	ord_table[chr(c)] = c;
++    }
++}
++
++function ord(c) {
++    if (ord_table[c]) {
++	return ord_table[c];
++    } else {
++	printf("Invalid character reference: '%c'\n", c) > "/dev/stderr";
++	++lossage;
++    }
++}
++
+ # Remove comment lines.
+ /^ *#/ {
+   next;
+@@ -90,6 +113,17 @@ function close_and_move(name, real_name) {
+   system(move_if_change " " name " " real_name " >&2");
+ }
+ 
++# ELF hash, for use with symbol versions.
++function elf_hash(s, i, acc) {
++  acc = 0;
++  for (i = 1; i <= length(s); ++i) {
++      acc = and(lshift(acc, 4) + ord(substr(s, i, 1)), 0xffffffff);
++      top = and(acc, 0xf0000000);
++      acc = and(xor(acc, rshift(top, 24)), compl(top));
++  }
++  return acc;
++}
++
+ # Now print the accumulated information.
+ END {
+   close(sort);
+@@ -145,6 +179,8 @@ END {
+ 	  && oldver ~ "^GLIBC_[0-9]" \
+ 	  && sym ~ "^[A-Za-z0-9_]*$") {
+ 	ver_val = oldver;
++	printf("#define %s_STRING \"%s\"\n", first_ver_macro, ver_val) > first_ver_header;
++	printf("#define %s_HASH 0x%x\n", first_ver_macro, elf_hash(ver_val)) > first_ver_header;
+ 	gsub("\\.", "_", ver_val);
+ 	printf("#define %s %s\n", first_ver_macro, ver_val) > first_ver_header;
+ 	first_ver_seen[first_ver_macro] = 1;
diff --git a/SOURCES/glibc-rh2047981-46.patch b/SOURCES/glibc-rh2047981-46.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3bbd90a0ad4c6aca9376b295e33c3cb9998fc9a8
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-46.patch
@@ -0,0 +1,227 @@
+Backport of the new test from this upstream commit:
+
+commit 8dcb6d0af07fda3607b541857e4f3970a74ed55b
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Tue Apr 26 14:23:02 2022 +0200
+
+    dlfcn: Do not use rtld_active () to determine ld.so state (bug 29078)
+
+    When audit modules are loaded, ld.so initialization is not yet
+    complete, and rtld_active () returns false even though ld.so is
+    mostly working.  Instead, the static dlopen hook is used, but that
+    does not work at all because this is not a static dlopen situation.
+
+    Commit 466c1ea15f461edb8e3ffaf5d86d708876343bbf ("dlfcn: Rework
+    static dlopen hooks") moved the hook pointer into _rtld_global_ro,
+    which means that separate protection is not needed anymore and the
+    hook pointer can be checked directly.
+
+    The guard for disabling libio vtable hardening in _IO_vtable_check
+    should stay for now.
+
+    Fixes commit 8e1472d2c1e25e6eabc2059170731365f6d5b3d1 ("ld.so:
+    Examine GLRO to detect inactive loader [BZ #20204]").
+
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+Conflicts:
+	dlfcn/dladdr.c
+	dlfcn/dladdr1.c
+	dlfcn/dlclose.c
+	dlfcn/dlerror.c
+	dlfcn/dlinfo.c
+	dlfcn/dlmopen.c
+	dlfcn/dlopen.c
+	dlfcn/dlopenold.c
+	dlfcn/dlsym.c
+	dlfcn/dlvsym.c
+	elf/dl-libc.c
+	  (Code changes not needed.)
+	elf/Makefile
+	  (Usual test list conflicts.  Also added $(libdl).)
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 6d39b400060a73f3..3fae27d32676caf9 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -353,8 +353,7 @@ tests += \
+   tst-audit24d \
+   tst-audit25a \
+   tst-audit25b \
+-  tst-audit8 \
+-  tst-audit9 \
++  tst-audit26 \
+   tst-auditmany \
+   tst-auxobj \
+   tst-auxobj-dlopen \
+@@ -659,6 +658,7 @@ modules-names = \
+   tst-auditmod24c \
+   tst-auditmod24d \
+   tst-auditmod25 \
++  tst-auditmod26 \
+   tst-big-note-lib \
+   tst-deep1mod1 \
+   tst-deep1mod2 \
+@@ -2145,6 +2145,11 @@ $(objpfx)tst-audit25b: $(objpfx)tst-audit25mod1.so \
+ LDFLAGS-tst-audit25b = -Wl,-z,now
+ tst-audit25b-ARGS = -- $(host-test-program-cmd)
+ 
++$(objpfx)tst-audit26: $(libdl)
++$(objpfx)tst-audit26.out: $(objpfx)tst-auditmod26.so
++$(objpfx)tst-auditmod26.so: $(libsupport) $(libdl)
++tst-audit26-ENV = LD_AUDIT=$(objpfx)tst-auditmod26.so
++
+ # tst-sonamemove links against an older implementation of the library.
+ LDFLAGS-tst-sonamemove-linkmod1.so = \
+   -Wl,--version-script=tst-sonamemove-linkmod1.map \
+diff --git a/elf/tst-audit26.c b/elf/tst-audit26.c
+new file mode 100644
+index 0000000000000000..3f920e83bac247a5
+--- /dev/null
++++ b/elf/tst-audit26.c
+@@ -0,0 +1,35 @@
++/* Check the usability of <dlfcn.h> functions in audit modules.
++   Copyright (C) 2022 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/>.  */
++
++#include <gnu/lib-names.h>
++
++#include <support/check.h>
++#include <support/xdlfcn.h>
++
++static int
++do_test (void)
++{
++  /* Check that the audit module has been loaded.  */
++  void *handle = xdlopen ("mapped to libc", RTLD_LOCAL | RTLD_NOW);
++  TEST_VERIFY (handle
++	       == xdlopen (LIBC_SO, RTLD_LOCAL | RTLD_NOW | RTLD_NOLOAD));
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/elf/tst-auditmod26.c b/elf/tst-auditmod26.c
+new file mode 100644
+index 0000000000000000..db7ba95abec20f53
+--- /dev/null
++++ b/elf/tst-auditmod26.c
+@@ -0,0 +1,104 @@
++/* Check the usability of <dlfcn.h> functions in audit modules.  Audit module.
++   Copyright (C) 2022 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/>.  */
++
++#include <dlfcn.h>
++#include <first-versions.h>
++#include <gnu/lib-names.h>
++#include <link.h>
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++
++#include <support/check.h>
++#include <support/xdlfcn.h>
++
++unsigned int
++la_version (unsigned int current)
++{
++  /* Exercise various <dlfcn.h> functions.  */
++
++  /* Check dlopen, dlsym, dlclose.   */
++  void *handle = xdlopen (LIBM_SO, RTLD_LOCAL | RTLD_NOW);
++  void *ptr = xdlsym (handle, "sincos");
++  TEST_VERIFY (ptr != NULL);
++  ptr = dlsym (handle, "SINCOS");
++  TEST_VERIFY (ptr == NULL);
++  const char *message = dlerror ();
++  TEST_VERIFY (strstr (message, ": undefined symbol: SINCOS") != NULL);
++  ptr = dlsym (handle, "SINCOS");
++  TEST_VERIFY (ptr == NULL);
++  xdlclose (handle);
++  TEST_COMPARE_STRING (dlerror (), NULL);
++
++  handle = xdlopen (LIBC_SO, RTLD_LOCAL | RTLD_NOW | RTLD_NOLOAD);
++
++  /* Check dlvsym.  _exit is unlikely to gain another symbol
++     version.  */
++  TEST_VERIFY (xdlsym (handle, "_exit")
++               == xdlvsym (handle, "_exit", FIRST_VERSION_libc__exit_STRING));
++
++  /* Check dlinfo.  */
++  {
++    void *handle2 = NULL;
++    TEST_COMPARE (dlinfo (handle, RTLD_DI_LINKMAP, &handle2), 0);
++    TEST_VERIFY (handle2 == handle);
++  }
++
++  /* Check dladdr and dladdr1.  */
++  Dl_info info = { };
++  TEST_VERIFY (dladdr (&_exit, &info) != 0);
++  if (strcmp (info.dli_sname, "_Exit") != 0) /* _Exit is an alias.  */
++    TEST_COMPARE_STRING (info.dli_sname, "_exit");
++  TEST_VERIFY (info.dli_saddr == &_exit);
++  TEST_VERIFY (strstr (info.dli_fname, LIBC_SO));
++  void *extra_info;
++  memset (&info, 0, sizeof (info));
++  TEST_VERIFY (dladdr1 (&_exit, &info, &extra_info, RTLD_DL_LINKMAP) != 0);
++  TEST_VERIFY (extra_info == handle);
++
++  /* Verify that dlmopen creates a new namespace.  */
++  void *dlmopen_handle = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
++  TEST_VERIFY (dlmopen_handle != handle);
++  memset (&info, 0, sizeof (info));
++  extra_info = NULL;
++  ptr = xdlsym (dlmopen_handle, "_exit");
++  TEST_VERIFY (dladdr1 (ptr, &info, &extra_info, RTLD_DL_LINKMAP) != 0);
++  TEST_VERIFY (extra_info == dlmopen_handle);
++  xdlclose (dlmopen_handle);
++
++  /* Terminate the process with an error state.  This does not happen
++     automatically because the audit module state is not shared with
++     the main program.  */
++  if (support_record_failure_is_failed ())
++    {
++      fflush (stdout);
++      fflush (stderr);
++      _exit (1);
++    }
++
++  return LAV_CURRENT;
++}
++
++char *
++la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag)
++{
++  if (strcmp (name, "mapped to libc") == 0)
++    return (char *) LIBC_SO;
++  else
++    return (char *) name;
++}
diff --git a/SOURCES/glibc-rh2047981-47.patch b/SOURCES/glibc-rh2047981-47.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c5baf0da3ed59d767436866315c5213c201829b7
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-47.patch
@@ -0,0 +1,59 @@
+commit 2a5b4f7a715921a232f67f6810268c6cd6aa0af2
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Fri Jul 8 12:08:48 2022 +0200
+
+    elf: Rename tst-audit26 to tst-audit28
+    
+    tst-audit26 and tst-audit27 are already used by aarch64.
+    
+    Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
+
+Conflicts:
+	elf/Makefile
+	  (Usual test backport differences.)
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 3fae27d32676caf9..9e721d5d4e0a1cd9 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -353,7 +353,7 @@ tests += \
+   tst-audit24d \
+   tst-audit25a \
+   tst-audit25b \
+-  tst-audit26 \
++  tst-audit28 \
+   tst-auditmany \
+   tst-auxobj \
+   tst-auxobj-dlopen \
+@@ -658,7 +658,7 @@ modules-names = \
+   tst-auditmod24c \
+   tst-auditmod24d \
+   tst-auditmod25 \
+-  tst-auditmod26 \
++  tst-auditmod28 \
+   tst-big-note-lib \
+   tst-deep1mod1 \
+   tst-deep1mod2 \
+@@ -2145,10 +2145,10 @@ $(objpfx)tst-audit25b: $(objpfx)tst-audit25mod1.so \
+ LDFLAGS-tst-audit25b = -Wl,-z,now
+ tst-audit25b-ARGS = -- $(host-test-program-cmd)
+ 
+-$(objpfx)tst-audit26: $(libdl)
+-$(objpfx)tst-audit26.out: $(objpfx)tst-auditmod26.so
+-$(objpfx)tst-auditmod26.so: $(libsupport) $(libdl)
+-tst-audit26-ENV = LD_AUDIT=$(objpfx)tst-auditmod26.so
++$(objpfx)tst-audit28: $(libdl)
++$(objpfx)tst-audit28.out: $(objpfx)tst-auditmod28.so
++$(objpfx)tst-auditmod28.so: $(libsupport) $(libdl)
++tst-audit28-ENV = LD_AUDIT=$(objpfx)tst-auditmod28.so
+ 
+ # tst-sonamemove links against an older implementation of the library.
+ LDFLAGS-tst-sonamemove-linkmod1.so = \
+diff --git a/elf/tst-audit26.c b/elf/tst-audit28.c
+similarity index 100%
+rename from elf/tst-audit26.c
+rename to elf/tst-audit28.c
+diff --git a/elf/tst-auditmod26.c b/elf/tst-auditmod28.c
+similarity index 100%
+rename from elf/tst-auditmod26.c
+rename to elf/tst-auditmod28.c
diff --git a/SOURCES/glibc-rh2047981-5.patch b/SOURCES/glibc-rh2047981-5.patch
new file mode 100644
index 0000000000000000000000000000000000000000..43af14e6e78423f6f2fbffed324625ad7244b172
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-5.patch
@@ -0,0 +1,224 @@
+commit b2964eb1d9a6b8ab1250e8a881cf406182da5875
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Wed Apr 21 19:49:51 2021 +0200
+
+    dlfcn: Failures after dlmopen should not terminate process [BZ #24772]
+    
+    Commit 9e78f6f6e7134a5f299cc8de77370218f8019237 ("Implement
+    _dl_catch_error, _dl_signal_error in libc.so [BZ #16628]") has the
+    side effect that distinct namespaces, as created by dlmopen, now have
+    separate implementations of the rtld exception mechanism.  This means
+    that the call to _dl_catch_error from libdl in a secondary namespace
+    does not actually install an exception handler because the
+    thread-local variable catch_hook in the libc.so copy in the secondary
+    namespace is distinct from that of the base namepace.  As a result, a
+    dlsym/dlopen/... failure in a secondary namespace terminates the process
+    with a dynamic linker error because it looks to the exception handler
+    mechanism as if no handler has been installed.
+    
+    This commit restores GLRO (dl_catch_error) and uses it to set the
+    handler in the base namespace.
+    
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+Conflicts:
+	elf/Makefile
+
+diff --git a/dlfcn/dlerror.c b/dlfcn/dlerror.c
+index 06732460ea1512cd..e08ac3afef302817 100644
+--- a/dlfcn/dlerror.c
++++ b/dlfcn/dlerror.c
+@@ -167,8 +167,10 @@ _dlerror_run (void (*operate) (void *), void *args)
+       result->errstring = NULL;
+     }
+ 
+-  result->errcode = _dl_catch_error (&result->objname, &result->errstring,
+-				     &result->malloced, operate, args);
++  result->errcode = GLRO (dl_catch_error) (&result->objname,
++					   &result->errstring,
++					   &result->malloced,
++					   operate, args);
+ 
+   /* If no error we mark that no error string is available.  */
+   result->returned = result->errstring == NULL;
+diff --git a/elf/Makefile b/elf/Makefile
+index a811919ba4568d64..e0919486a14cab1a 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -216,6 +216,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
+ 	 tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask \
+ 	 tst-tls20 tst-tls21 \
+ 	 tst-rtld-run-static \
++	 tst-dlmopen-dlerror \
+ #	 reldep9
+ tests-internal += loadtest unload unload2 circleload1 \
+ 	 neededtest neededtest2 neededtest3 neededtest4 \
+@@ -349,6 +350,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
+ 		libmarkermod4-1 libmarkermod4-2 libmarkermod4-3 libmarkermod4-4 \
+ 		libmarkermod5-1 libmarkermod5-2 libmarkermod5-3 libmarkermod5-4 \
+ 		libmarkermod5-5 tst-tls20mod-bad tst-tls21mod \
++		tst-dlmopen-dlerror-mod \
+ 
+ # Most modules build with _ISOMAC defined, but those filtered out
+ # depend on internal headers.
+@@ -1546,6 +1548,10 @@ $(objpfx)tst-sonamemove-dlopen.out: \
+   $(objpfx)tst-sonamemove-runmod1.so \
+   $(objpfx)tst-sonamemove-runmod2.so
+ 
++$(objpfx)tst-dlmopen-dlerror: $(libdl)
++$(objpfx)tst-dlmopen-dlerror-mod.so: $(libdl) $(libsupport)
++$(objpfx)tst-dlmopen-dlerror.out: $(objpfx)tst-dlmopen-dlerror-mod.so
++
+ # Override -z defs, so that we can reference an undefined symbol.
+ # Force lazy binding for the same reason.
+ LDFLAGS-tst-latepthreadmod.so = \
+diff --git a/elf/dl-error-skeleton.c b/elf/dl-error-skeleton.c
+index 9cb002ccfed2c7b4..7801aa433b12275f 100644
+--- a/elf/dl-error-skeleton.c
++++ b/elf/dl-error-skeleton.c
+@@ -248,4 +248,16 @@ _dl_receive_error (receiver_fct fct, void (*operate) (void *), void *args)
+   catch_hook = old_catch;
+   receiver = old_receiver;
+ }
++
++/* Forwarder used for initializing GLRO (_dl_catch_error).  */
++int
++_rtld_catch_error (const char **objname, const char **errstring,
++		   bool *mallocedp, void (*operate) (void *),
++		   void *args)
++{
++  /* The reference to _dl_catch_error will eventually be relocated to
++     point to the implementation in libc.so.  */
++  return _dl_catch_error (objname, errstring, mallocedp, operate, args);
++}
++
+ #endif /* DL_ERROR_BOOTSTRAP */
+diff --git a/elf/rtld.c b/elf/rtld.c
+index 461d8c114a875a9b..c445b5ca25dea193 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -365,6 +365,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
+     ._dl_lookup_symbol_x = _dl_lookup_symbol_x,
+     ._dl_open = _dl_open,
+     ._dl_close = _dl_close,
++    ._dl_catch_error = _rtld_catch_error,
+     ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
+ #ifdef HAVE_DL_DISCOVER_OSVERSION
+     ._dl_discover_osversion = _dl_discover_osversion
+diff --git a/elf/tst-dlmopen-dlerror-mod.c b/elf/tst-dlmopen-dlerror-mod.c
+new file mode 100644
+index 0000000000000000..7e95dcdeacf005be
+--- /dev/null
++++ b/elf/tst-dlmopen-dlerror-mod.c
+@@ -0,0 +1,41 @@
++/* Check that dlfcn errors are reported properly after dlmopen.  Test module.
++   Copyright (C) 2021 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
++   <http://www.gnu.org/licenses/>.  */
++
++#include <dlfcn.h>
++#include <stddef.h>
++#include <support/check.h>
++
++/* Note: This object is not linked into the main program, so we cannot
++   use delayed test failure reporting via TEST_VERIFY etc., and have
++   to use FAIL_EXIT1 (or something else that calls exit).  */
++
++void
++call_dlsym (void)
++{
++  void *ptr = dlsym (NULL, "does not exist");
++  if (ptr != NULL)
++    FAIL_EXIT1 ("dlsym did not fail as expected");
++}
++
++void
++call_dlopen (void)
++{
++  void *handle = dlopen ("tst-dlmopen-dlerror does not exist", RTLD_NOW);
++  if (handle != NULL)
++    FAIL_EXIT1 ("dlopen did not fail as expected");
++}
+diff --git a/elf/tst-dlmopen-dlerror.c b/elf/tst-dlmopen-dlerror.c
+new file mode 100644
+index 0000000000000000..e864d2fe4c3484ab
+--- /dev/null
++++ b/elf/tst-dlmopen-dlerror.c
+@@ -0,0 +1,37 @@
++/* Check that dlfcn errors are reported properly after dlmopen.
++   Copyright (C) 2021 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
++   <http://www.gnu.org/licenses/>.  */
++
++#include <stddef.h>
++#include <support/check.h>
++#include <support/xdlfcn.h>
++
++static int
++do_test (void)
++{
++  void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-dlerror-mod.so",
++                           RTLD_NOW);
++  void (*call_dlsym) (void) = xdlsym (handle, "call_dlsym");
++  void (*call_dlopen) (void) = xdlsym (handle, "call_dlopen");
++
++  call_dlsym ();
++  call_dlopen ();
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index 7b0a667629ddc06a..d6d02aa3ccffba33 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -647,6 +647,12 @@ struct rtld_global_ro
+   void *(*_dl_open) (const char *file, int mode, const void *caller_dlopen,
+ 		     Lmid_t nsid, int argc, char *argv[], char *env[]);
+   void (*_dl_close) (void *map);
++  /* libdl in a secondary namespace (after dlopen) must use
++     _dl_catch_error from the main namespace, so it has to be
++     exported in some way.  */
++  int (*_dl_catch_error) (const char **objname, const char **errstring,
++			  bool *mallocedp, void (*operate) (void *),
++			  void *args);
+   void *(*_dl_tls_get_addr_soft) (struct link_map *);
+ #ifdef HAVE_DL_DISCOVER_OSVERSION
+   int (*_dl_discover_osversion) (void);
+@@ -889,6 +895,9 @@ extern int _dl_catch_error (const char **objname, const char **errstring,
+ 			    void *args);
+ libc_hidden_proto (_dl_catch_error)
+ 
++/* Used for initializing GLRO (_dl_catch_error).  */
++extern __typeof__ (_dl_catch_error) _rtld_catch_error attribute_hidden;
++
+ /* Call OPERATE (ARGS).  If no error occurs, set *EXCEPTION to zero.
+    Otherwise, store a copy of the raised exception in *EXCEPTION,
+    which has to be freed by _dl_exception_free.  As a special case, if
diff --git a/SOURCES/glibc-rh2047981-6.patch b/SOURCES/glibc-rh2047981-6.patch
new file mode 100644
index 0000000000000000000000000000000000000000..09a5bf8aef6a6a3e38b40bf9b32434c31e8d8206
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-6.patch
@@ -0,0 +1,822 @@
+commit fada9018199c21c469ff0e731ef75c6020074ac9
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Wed Apr 21 19:49:51 2021 +0200
+
+    dlfcn: dlerror needs to call free from the base namespace [BZ #24773]
+    
+    Calling free directly may end up freeing a pointer allocated by the
+    dynamic loader using malloc from libc.so in the base namespace using
+    the allocator from libc.so in a secondary namespace, which results in
+    crashes.
+    
+    This commit redirects the free call through GLRO and the dynamic
+    linker, to reach the correct namespace.  It also cleans up the dlerror
+    handling along the way, so that pthread_setspecific is no longer
+    needed (which avoids triggering bug 24774).
+
+Conflicts:
+	dlfcn/dlfreeres.c - Remove.
+	malloc/set-freeres.c
+		Manual merge against disinct set of resources.
+	malloc/thread-freeres.c
+		Manual merge against disinct set of resources.
+
+diff --git a/dlfcn/Makefile b/dlfcn/Makefile
+index 34f9923334f42edf..0b213b7d9fefcdc9 100644
+--- a/dlfcn/Makefile
++++ b/dlfcn/Makefile
+@@ -22,9 +22,10 @@ include ../Makeconfig
+ headers		:= bits/dlfcn.h dlfcn.h
+ extra-libs	:= libdl
+ libdl-routines	:= dlopen dlclose dlsym dlvsym dlerror dladdr dladdr1 dlinfo \
+-		   dlmopen dlfcn dlfreeres
++		   dlmopen dlfcn
+ routines	:= $(patsubst %,s%,$(filter-out dlfcn,$(libdl-routines)))
+ elide-routines.os := $(routines)
++routines += libc_dlerror_result
+ 
+ extra-libs-others := libdl
+ 
+diff --git a/dlfcn/Versions b/dlfcn/Versions
+index 1df6925a92ff8b36..f07cb929aa13eaf2 100644
+--- a/dlfcn/Versions
++++ b/dlfcn/Versions
+@@ -1,3 +1,8 @@
++libc {
++  GLIBC_PRIVATE {
++    __libc_dlerror_result;
++  }
++}
+ libdl {
+   GLIBC_2.0 {
+     dladdr; dlclose; dlerror; dlopen; dlsym;
+@@ -13,6 +18,5 @@ libdl {
+   }
+   GLIBC_PRIVATE {
+     _dlfcn_hook;
+-    __libdl_freeres;
+   }
+ }
+diff --git a/dlfcn/dlerror.c b/dlfcn/dlerror.c
+index e08ac3afef302817..070eadbf7c1c0b1c 100644
+--- a/dlfcn/dlerror.c
++++ b/dlfcn/dlerror.c
+@@ -25,6 +25,8 @@
+ #include <libc-lock.h>
+ #include <ldsodefs.h>
+ #include <libc-symbols.h>
++#include <assert.h>
++#include <dlerror.h>
+ 
+ #if !defined SHARED && IS_IN (libdl)
+ 
+@@ -36,92 +38,75 @@ dlerror (void)
+ 
+ #else
+ 
+-/* Type for storing results of dynamic loading actions.  */
+-struct dl_action_result
+-  {
+-    int errcode;
+-    int returned;
+-    bool malloced;
+-    const char *objname;
+-    const char *errstring;
+-  };
+-static struct dl_action_result last_result;
+-static struct dl_action_result *static_buf;
+-
+-/* This is the key for the thread specific memory.  */
+-static __libc_key_t key;
+-__libc_once_define (static, once);
+-
+-/* Destructor for the thread-specific data.  */
+-static void init (void);
+-static void free_key_mem (void *mem);
+-
+-
+ char *
+ __dlerror (void)
+ {
+-  char *buf = NULL;
+-  struct dl_action_result *result;
+-
+ # ifdef SHARED
+   if (!rtld_active ())
+     return _dlfcn_hook->dlerror ();
+ # endif
+ 
+-  /* If we have not yet initialized the buffer do it now.  */
+-  __libc_once (once, init);
++  struct dl_action_result *result = __libc_dlerror_result;
+ 
+-  /* Get error string.  */
+-  if (static_buf != NULL)
+-    result = static_buf;
+-  else
++  /* No libdl function has been called.  No error is possible.  */
++  if (result == NULL)
++    return NULL;
++
++  /* For an early malloc failure, clear the error flag and return the
++     error message.  This marks the error as delivered.  */
++  if (result == dl_action_result_malloc_failed)
+     {
+-      /* init () has been run and we don't use the static buffer.
+-	 So we have a valid key.  */
+-      result = (struct dl_action_result *) __libc_getspecific (key);
+-      if (result == NULL)
+-	result = &last_result;
++      __libc_dlerror_result = NULL;
++      return (char *) "out of memory";
+     }
+ 
+-  /* Test whether we already returned the string.  */
+-  if (result->returned != 0)
++  /* Placeholder object.  This can be observed in a recursive call,
++     e.g. from an ELF constructor.  */
++  if (result->errstring == NULL)
++    return NULL;
++
++  /* If we have already reported the error, we can free the result and
++     return NULL.  See __libc_dlerror_result_free.  */
++  if (result->returned)
+     {
+-      /* We can now free the string.  */
+-      if (result->errstring != NULL)
+-	{
+-	  if (strcmp (result->errstring, "out of memory") != 0)
+-	    free ((char *) result->errstring);
+-	  result->errstring = NULL;
+-	}
++      __libc_dlerror_result = NULL;
++      dl_action_result_errstring_free (result);
++      free (result);
++      return NULL;
+     }
+-  else if (result->errstring != NULL)
+-    {
+-      buf = (char *) result->errstring;
+-      int n;
+-      if (result->errcode == 0)
+-	n = __asprintf (&buf, "%s%s%s",
+-			result->objname,
+-			result->objname[0] == '\0' ? "" : ": ",
+-			_(result->errstring));
+-      else
+-	n = __asprintf (&buf, "%s%s%s: %s",
+-			result->objname,
+-			result->objname[0] == '\0' ? "" : ": ",
+-			_(result->errstring),
+-			strerror (result->errcode));
+-      if (n != -1)
+-	{
+-	  /* We don't need the error string anymore.  */
+-	  if (strcmp (result->errstring, "out of memory") != 0)
+-	    free ((char *) result->errstring);
+-	  result->errstring = buf;
+-	}
+ 
+-      /* Mark the error as returned.  */
+-      result->returned = 1;
+-    }
++  assert (result->errstring != NULL);
++
++  /* Create the combined error message.  */
++  char *buf;
++  int n;
++  if (result->errcode == 0)
++    n = __asprintf (&buf, "%s%s%s",
++		    result->objname,
++		    result->objname[0] == '\0' ? "" : ": ",
++		    _(result->errstring));
++  else
++    n = __asprintf (&buf, "%s%s%s: %s",
++		    result->objname,
++		    result->objname[0] == '\0' ? "" : ": ",
++		    _(result->errstring),
++		    strerror (result->errcode));
+ 
+-  return buf;
++  /* Mark the error as delivered.  */
++  result->returned = true;
++
++  if (n >= 0)
++    {
++      /* Replace the error string with the newly allocated one.  */
++      dl_action_result_errstring_free (result);
++      result->errstring = buf;
++      result->errstring_source = dl_action_result_errstring_local;
++      return buf;
++    }
++  else
++    /* We could not create the combined error message, so use the
++       existing string as a fallback.  */
++    return result->errstring;
+ }
+ # ifdef SHARED
+ strong_alias (__dlerror, dlerror)
+@@ -130,130 +115,94 @@ strong_alias (__dlerror, dlerror)
+ int
+ _dlerror_run (void (*operate) (void *), void *args)
+ {
+-  struct dl_action_result *result;
+-
+-  /* If we have not yet initialized the buffer do it now.  */
+-  __libc_once (once, init);
+-
+-  /* Get error string and number.  */
+-  if (static_buf != NULL)
+-    result = static_buf;
+-  else
++  struct dl_action_result *result = __libc_dlerror_result;
++  if (result != NULL)
+     {
+-      /* We don't use the static buffer and so we have a key.  Use it
+-	 to get the thread-specific buffer.  */
+-      result = __libc_getspecific (key);
+-      if (result == NULL)
++      if (result == dl_action_result_malloc_failed)
+ 	{
+-	  result = (struct dl_action_result *) calloc (1, sizeof (*result));
+-	  if (result == NULL)
+-	    /* We are out of memory.  Since this is no really critical
+-	       situation we carry on by using the global variable.
+-	       This might lead to conflicts between the threads but
+-	       they soon all will have memory problems.  */
+-	    result = &last_result;
+-	  else
+-	    /* Set the tsd.  */
+-	    __libc_setspecific (key, result);
++	  /* Clear the previous error.  */
++	  __libc_dlerror_result = NULL;
++	  result = NULL;
++	}
++      else
++	{
++	  /* There is an existing object.  Free its error string, but
++	     keep the object.  */
++	  dl_action_result_errstring_free (result);
++	  /* Mark the object as not containing an error.  This ensures
++	     that call to dlerror from, for example, an ELF
++	     constructor will not notice this result object.  */
++	  result->errstring = NULL;
+ 	}
+     }
+ 
+-  if (result->errstring != NULL)
+-    {
+-      /* Free the error string from the last failed command.  This can
+-	 happen if `dlerror' was not run after an error was found.  */
+-      if (result->malloced)
+-	free ((char *) result->errstring);
+-      result->errstring = NULL;
+-    }
+-
+-  result->errcode = GLRO (dl_catch_error) (&result->objname,
+-					   &result->errstring,
+-					   &result->malloced,
+-					   operate, args);
+-
+-  /* If no error we mark that no error string is available.  */
+-  result->returned = result->errstring == NULL;
++  const char *objname;
++  const char *errstring;
++  bool malloced;
++  int errcode = GLRO (dl_catch_error) (&objname, &errstring, &malloced,
++				       operate, args);
+ 
+-  return result->errstring != NULL;
+-}
++  /* ELF constructors or destructors may have indirectly altered the
++     value of __libc_dlerror_result, therefore reload it.  */
++  result = __libc_dlerror_result;
+ 
+-
+-/* Initialize buffers for results.  */
+-static void
+-init (void)
+-{
+-  if (__libc_key_create (&key, free_key_mem))
+-    /* Creating the key failed.  This means something really went
+-       wrong.  In any case use a static buffer which is better than
+-       nothing.  */
+-    static_buf = &last_result;
+-}
+-
+-
+-static void
+-check_free (struct dl_action_result *rec)
+-{
+-  if (rec->errstring != NULL
+-      && strcmp (rec->errstring, "out of memory") != 0)
++  if (errstring == NULL)
+     {
+-      /* We can free the string only if the allocation happened in the
+-	 C library used by the dynamic linker.  This means, it is
+-	 always the C library in the base namespace.  When we're statically
+-         linked, the dynamic linker is part of the program and so always
+-	 uses the same C library we use here.  */
+-#ifdef SHARED
+-      struct link_map *map = NULL;
+-      Dl_info info;
+-      if (_dl_addr (check_free, &info, &map, NULL) != 0 && map->l_ns == 0)
+-#endif
++      /* There is no error.  We no longer need the result object if it
++	 does not contain an error.  However, a recursive call may
++	 have added an error even if this call did not cause it.  Keep
++	 the other error.  */
++      if (result != NULL && result->errstring == NULL)
+ 	{
+-	  free ((char *) rec->errstring);
+-	  rec->errstring = NULL;
++	  __libc_dlerror_result = NULL;
++	  free (result);
+ 	}
++      return 0;
+     }
+-}
+-
+-
+-static void
+-__attribute__ ((destructor))
+-fini (void)
+-{
+-  check_free (&last_result);
+-}
+-
+-
+-/* Free the thread specific data, this is done if a thread terminates.  */
+-static void
+-free_key_mem (void *mem)
+-{
+-  check_free ((struct dl_action_result *) mem);
++  else
++    {
++      /* A new error occurred.  Check if a result object has to be
++	 allocated.  */
++      if (result == NULL || result == dl_action_result_malloc_failed)
++	{
++	  /* Allocating storage for the error message after the fact
++	     is not ideal.  But this avoids an infinite recursion in
++	     case malloc itself calls libdl functions (without
++	     triggering errors).  */
++	  result = malloc (sizeof (*result));
++	  if (result == NULL)
++	    {
++	      /* Assume that the dlfcn failure was due to a malloc
++		 failure, too.  */
++	      if (malloced)
++		dl_error_free ((char *) errstring);
++	      __libc_dlerror_result = dl_action_result_malloc_failed;
++	      return 1;
++	    }
++	  __libc_dlerror_result = result;
++	}
++      else
++	/* Deallocate the existing error message from a recursive
++	   call, but reuse the result object.  */
++	dl_action_result_errstring_free (result);
++
++      result->errcode = errcode;
++      result->objname = objname;
++      result->errstring = (char *) errstring;
++      result->returned = false;
++      /* In case of an error, the malloced flag indicates whether the
++	 error string is constant or not.  */
++      if (malloced)
++	result->errstring_source = dl_action_result_errstring_rtld;
++      else
++	result->errstring_source = dl_action_result_errstring_constant;
+ 
+-  free (mem);
+-  __libc_setspecific (key, NULL);
++      return 1;
++    }
+ }
+ 
+ # ifdef SHARED
+ 
+-/* Free the dlerror-related resources.  */
+-void
+-__dlerror_main_freeres (void)
+-{
+-  /* Free the global memory if used.  */
+-  check_free (&last_result);
+-
+-  if (__libc_once_get (once) && static_buf == NULL)
+-    {
+-      /* init () has been run and we don't use the static buffer.
+-	 So we have a valid key.  */
+-      void *mem;
+-      /* Free the TSD memory if used.  */
+-      mem = __libc_getspecific (key);
+-      if (mem != NULL)
+-	free_key_mem (mem);
+-    }
+-}
+-
+ struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon));
+ libdl_hidden_data_def (_dlfcn_hook)
+ 
+diff --git a/dlfcn/dlerror.h b/dlfcn/dlerror.h
+new file mode 100644
+index 0000000000000000..cb9a9cea4c009452
+--- /dev/null
++++ b/dlfcn/dlerror.h
+@@ -0,0 +1,92 @@
++/* Memory management for dlerror messages.
++   Copyright (C) 2021 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/>.  */
++
++#ifndef _DLERROR_H
++#define _DLERROR_H
++
++#include <dlfcn.h>
++#include <ldsodefs.h>
++#include <stdbool.h>
++#include <stdint.h>
++#include <stdlib.h>
++
++/* Source of the errstring member in struct dl_action_result, for
++   finding the right deallocation routine.  */
++enum dl_action_result_errstring_source
++  {
++   dl_action_result_errstring_constant, /* String literal, no deallocation.  */
++   dl_action_result_errstring_rtld, /* libc in the primary namespace.  */
++   dl_action_result_errstring_local, /* libc in the current namespace.  */
++  };
++
++struct dl_action_result
++{
++  int errcode;
++  char errstring_source;
++  bool returned;
++  const char *objname;
++  char *errstring;
++};
++
++/* Used to free the errstring member of struct dl_action_result in the
++   dl_action_result_errstring_rtld case.  */
++static inline void
++dl_error_free (void *ptr)
++{
++#ifdef SHARED
++  /* In the shared case, ld.so may use a different malloc than this
++     namespace.  */
++  GLRO (dl_error_free (ptr));
++#else
++  /* Call the implementation directly.  It still has to check for
++     pointers which cannot be freed, so do not call free directly
++     here.  */
++  _dl_error_free (ptr);
++#endif
++}
++
++/* Deallocate RESULT->errstring, leaving *RESULT itself allocated.  */
++static inline void
++dl_action_result_errstring_free (struct dl_action_result *result)
++{
++  switch (result->errstring_source)
++    {
++    case dl_action_result_errstring_constant:
++      break;
++    case dl_action_result_errstring_rtld:
++      dl_error_free (result->errstring);
++      break;
++    case dl_action_result_errstring_local:
++      free (result->errstring);
++      break;
++    }
++}
++
++/* Stand-in for an error result object whose allocation failed.  No
++   precise message can be reported for this, but an error must still
++   be signaled.  */
++static struct dl_action_result *const dl_action_result_malloc_failed
++  __attribute__ ((unused)) = (struct dl_action_result *) (intptr_t) -1;
++
++/* Thread-local variable for storing dlfcn failures for subsequent
++   reporting via dlerror.  */
++extern __thread struct dl_action_result *__libc_dlerror_result
++  attribute_tls_model_ie;
++void __libc_dlerror_result_free (void) attribute_hidden;
++
++#endif /* _DLERROR_H */
+diff --git a/dlfcn/dlfreeres.c b/dlfcn/dlfreeres.c
+deleted file mode 100644
+index 4004db0edbe0c028..0000000000000000
+--- a/dlfcn/dlfreeres.c
++++ /dev/null
+@@ -1,29 +0,0 @@
+-/* Clean up allocated libdl memory on demand.
+-   Copyright (C) 2018 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
+-   <http://www.gnu.org/licenses/>.  */
+-
+-#include <set-hooks.h>
+-#include <libc-symbols.h>
+-#include <dlfcn.h>
+-
+-/* Free libdl.so resources.
+-   Note: Caller ensures we are called only once.  */
+-void
+-__libdl_freeres (void)
+-{
+-  call_function_static_weak (__dlerror_main_freeres);
+-}
+diff --git a/dlfcn/libc_dlerror_result.c b/dlfcn/libc_dlerror_result.c
+new file mode 100644
+index 0000000000000000..99747186b9218680
+--- /dev/null
++++ b/dlfcn/libc_dlerror_result.c
+@@ -0,0 +1,39 @@
++/* Thread-local variable holding the dlerror result.
++   Copyright (C) 2021 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
++   <http://www.gnu.org/licenses/>.  */
++
++#include <dlerror.h>
++
++/* This pointer is either NULL, dl_action_result_malloc_failed (), or
++   has been allocated using malloc by the namespace that also contains
++   this instance of the thread-local variable.  */
++__thread struct dl_action_result *__libc_dlerror_result attribute_tls_model_ie;
++
++/* Called during thread shutdown to free resources.  */
++void
++__libc_dlerror_result_free (void)
++{
++  if (__libc_dlerror_result != NULL)
++    {
++      if (__libc_dlerror_result != dl_action_result_malloc_failed)
++        {
++          dl_action_result_errstring_free (__libc_dlerror_result);
++          free (__libc_dlerror_result);
++        }
++      __libc_dlerror_result = NULL;
++    }
++}
+diff --git a/elf/dl-exception.c b/elf/dl-exception.c
+index d24bf30a5cf39bc2..f474daf97ae76308 100644
+--- a/elf/dl-exception.c
++++ b/elf/dl-exception.c
+@@ -30,6 +30,17 @@
+    a pointer comparison.  See below and in dlfcn/dlerror.c.  */
+ static const char _dl_out_of_memory[] = "out of memory";
+ 
++/* Call free in the main libc.so.  This allows other namespaces to
++   free pointers on the main libc heap, via GLRO (dl_error_free).  It
++   also avoids calling free on the special, pre-allocated
++   out-of-memory error message.  */
++void
++_dl_error_free (void *ptr)
++{
++  if (ptr != _dl_out_of_memory)
++    free (ptr);
++}
++
+ /* Dummy allocation object used if allocating the message buffer
+    fails.  */
+ static void
+diff --git a/elf/rtld.c b/elf/rtld.c
+index c445b5ca25dea193..e107af4014d43777 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -366,6 +366,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
+     ._dl_open = _dl_open,
+     ._dl_close = _dl_close,
+     ._dl_catch_error = _rtld_catch_error,
++    ._dl_error_free = _dl_error_free,
+     ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
+ #ifdef HAVE_DL_DISCOVER_OSVERSION
+     ._dl_discover_osversion = _dl_discover_osversion
+diff --git a/elf/tst-dlmopen-dlerror-mod.c b/elf/tst-dlmopen-dlerror-mod.c
+index 7e95dcdeacf005be..051025d3fa7a4d6a 100644
+--- a/elf/tst-dlmopen-dlerror-mod.c
++++ b/elf/tst-dlmopen-dlerror-mod.c
+@@ -18,6 +18,8 @@
+ 
+ #include <dlfcn.h>
+ #include <stddef.h>
++#include <stdio.h>
++#include <string.h>
+ #include <support/check.h>
+ 
+ /* Note: This object is not linked into the main program, so we cannot
+@@ -25,17 +27,32 @@
+    to use FAIL_EXIT1 (or something else that calls exit).  */
+ 
+ void
+-call_dlsym (void)
++call_dlsym (const char *name)
+ {
+-  void *ptr = dlsym (NULL, "does not exist");
++  void *ptr = dlsym (NULL, name);
+   if (ptr != NULL)
+-    FAIL_EXIT1 ("dlsym did not fail as expected");
++    FAIL_EXIT1 ("dlsym did not fail as expected for: %s", name);
++  const char *message = dlerror ();
++  if (strstr (message, ": undefined symbol: does not exist X") == NULL)
++    FAIL_EXIT1 ("invalid dlsym error message for [[%s]]: %s", name, message);
++  message = dlerror ();
++  if (message != NULL)
++    FAIL_EXIT1 ("second dlsym for [[%s]]: %s", name, message);
+ }
+ 
+ void
+-call_dlopen (void)
++call_dlopen (const char *name)
+ {
+-  void *handle = dlopen ("tst-dlmopen-dlerror does not exist", RTLD_NOW);
++  void *handle = dlopen (name, RTLD_NOW);
+   if (handle != NULL)
+-    FAIL_EXIT1 ("dlopen did not fail as expected");
++    FAIL_EXIT1 ("dlopen did not fail as expected for: %s", name);
++  const char *message = dlerror ();
++  if (strstr (message, "X: cannot open shared object file:"
++              " No such file or directory") == NULL
++      && strstr (message, "X: cannot open shared object file:"
++                 " File name too long") == NULL)
++    FAIL_EXIT1 ("invalid dlopen error message for [[%s]]: %s", name, message);
++  message = dlerror ();
++  if (message != NULL)
++    FAIL_EXIT1 ("second dlopen for [[%s]]: %s", name, message);
+ }
+diff --git a/elf/tst-dlmopen-dlerror.c b/elf/tst-dlmopen-dlerror.c
+index e864d2fe4c3484ab..aa3d6598df119ce0 100644
+--- a/elf/tst-dlmopen-dlerror.c
++++ b/elf/tst-dlmopen-dlerror.c
+@@ -17,6 +17,7 @@
+    <http://www.gnu.org/licenses/>.  */
+ 
+ #include <stddef.h>
++#include <string.h>
+ #include <support/check.h>
+ #include <support/xdlfcn.h>
+ 
+@@ -25,11 +26,22 @@ do_test (void)
+ {
+   void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-dlerror-mod.so",
+                            RTLD_NOW);
+-  void (*call_dlsym) (void) = xdlsym (handle, "call_dlsym");
+-  void (*call_dlopen) (void) = xdlsym (handle, "call_dlopen");
+-
+-  call_dlsym ();
+-  call_dlopen ();
++  void (*call_dlsym) (const char *name) = xdlsym (handle, "call_dlsym");
++  void (*call_dlopen) (const char *name) = xdlsym (handle, "call_dlopen");
++
++  /* Iterate over various name lengths.  This changes the size of
++     error messages allocated by ld.so and has been shown to trigger
++     detectable heap corruption if malloc/free calls in different
++     namespaces are mixed.  */
++  char buffer[2048];
++  char *buffer_end = &buffer[sizeof (buffer) - 2];
++  for (char *p = stpcpy (buffer, "does not exist "); p < buffer_end; ++p)
++    {
++      p[0] = 'X';
++      p[1] = '\0';
++      call_dlsym (buffer);
++      call_dlopen (buffer);
++    }
+ 
+   return 0;
+ }
+diff --git a/include/dlfcn.h b/include/dlfcn.h
+index 0dc57dbe2217cfe7..109586a1d968b630 100644
+--- a/include/dlfcn.h
++++ b/include/dlfcn.h
+@@ -156,7 +156,5 @@ extern void __libc_register_dlfcn_hook (struct link_map *map)
+      attribute_hidden;
+ #endif
+ 
+-extern void __dlerror_main_freeres (void) attribute_hidden;
+-
+ #endif
+ #endif
+diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c
+index cda368479f910149..43b6a2cd9da49aa9 100644
+--- a/malloc/set-freeres.c
++++ b/malloc/set-freeres.c
+@@ -19,6 +19,7 @@
+ #include <stdlib.h>
+ #include <set-hooks.h>
+ #include <libc-internal.h>
++#include <dlfcn/dlerror.h>
+ 
+ #include "../libio/libioP.h"
+ 
+@@ -26,8 +27,6 @@ DEFINE_HOOK (__libc_subfreeres, (void));
+ 
+ symbol_set_define (__libc_freeres_ptrs);
+ 
+-extern __attribute__ ((weak)) void __libdl_freeres (void);
+-
+ extern __attribute__ ((weak)) void __libpthread_freeres (void);
+ 
+ void __libc_freeres_fn_section
+@@ -46,16 +45,13 @@ __libc_freeres (void)
+       /* We run the resource freeing after IO cleanup.  */
+       RUN_HOOK (__libc_subfreeres, ());
+ 
+-      /* Call the libdl list of cleanup functions
+-	 (weak-ref-and-check).  */
+-      if (&__libdl_freeres != NULL)
+-	__libdl_freeres ();
+-
+       /* Call the libpthread list of cleanup functions
+ 	 (weak-ref-and-check).  */
+       if (&__libpthread_freeres != NULL)
+ 	__libpthread_freeres ();
+ 
++      call_function_static_weak (__libc_dlerror_result_free);
++
+       for (p = symbol_set_first_element (__libc_freeres_ptrs);
+            !symbol_set_end_p (__libc_freeres_ptrs, p); ++p)
+         free (*p);
+diff --git a/malloc/thread-freeres.c b/malloc/thread-freeres.c
+index a63b6c93f3114284..1e37a72c1f4a9c43 100644
+--- a/malloc/thread-freeres.c
++++ b/malloc/thread-freeres.c
+@@ -16,6 +16,7 @@
+    License along with the GNU C Library; if not, see
+    <http://www.gnu.org/licenses/>.  */
+ 
++#include <dlfcn/dlerror.h>
+ #include <libc-internal.h>
+ #include <malloc-internal.h>
+ #include <resolv/resolv-internal.h>
+@@ -32,6 +33,7 @@ __libc_thread_freeres (void)
+   call_function_static_weak (__rpc_thread_destroy);
+   call_function_static_weak (__res_thread_freeres);
+   call_function_static_weak (__strerror_thread_freeres);
++  call_function_static_weak (__libc_dlerror_result_free);
+ 
+   /* This should come last because it shuts down malloc for this
+      thread and the other shutdown functions might well call free.  */
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index d6d02aa3ccffba33..2dd6f0c3c4aaaef5 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -653,6 +653,9 @@ struct rtld_global_ro
+   int (*_dl_catch_error) (const char **objname, const char **errstring,
+ 			  bool *mallocedp, void (*operate) (void *),
+ 			  void *args);
++  /* libdl in a secondary namespace must use free from the base
++     namespace.  */
++  void (*_dl_error_free) (void *);
+   void *(*_dl_tls_get_addr_soft) (struct link_map *);
+ #ifdef HAVE_DL_DISCOVER_OSVERSION
+   int (*_dl_discover_osversion) (void);
+@@ -812,6 +815,10 @@ void _dl_exception_create (struct dl_exception *, const char *object,
+   __attribute__ ((nonnull (1, 3)));
+ rtld_hidden_proto (_dl_exception_create)
+ 
++/* Used internally to implement dlerror message freeing.  See
++   include/dlfcn.h and dlfcn/dlerror.c.  */
++void _dl_error_free (void *ptr) attribute_hidden;
++
+ /* Like _dl_exception_create, but create errstring from a format
+    string FMT.  Currently, only "%s" and "%%" are supported as format
+    directives.  */
diff --git a/SOURCES/glibc-rh2047981-7.patch b/SOURCES/glibc-rh2047981-7.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d1640be5714e49ee07c64d91423251b8583186fa
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-7.patch
@@ -0,0 +1,134 @@
+Added $(objpfx)tst-dlmopen-gethostbyname: $(libdl) in elf/Makefile since
+we still have $(libdl) in RHEL8.
+
+commit c2059edce20c124d1a99f1a94cc52e83b77a917a
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Thu Jun 17 15:06:43 2021 +0200
+
+    elf: Use _dl_catch_error from base namespace in dl-libc.c [BZ #27646]
+    
+    dlerrror_run in elf/dl-libc.c needs to call GLRO (dl_catch_error)
+    from the base namespace, just like the exported dlerror
+    implementation.
+    
+    Fixes commit b2964eb1d9a6b8ab1250e8a881cf406182da5875 ("dlfcn:
+    Failures after dlmopen should not terminate process [BZ #24772]").
+    
+    Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+
+Conflicts:
+	elf/Makefile
+
+diff --git a/elf/Makefile b/elf/Makefile
+index e0919486a14cab1a..30417c3ce15abcb4 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -217,6 +217,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
+ 	 tst-tls20 tst-tls21 \
+ 	 tst-rtld-run-static \
+ 	 tst-dlmopen-dlerror \
++	 tst-dlmopen-gethostbyname \
+ #	 reldep9
+ tests-internal += loadtest unload unload2 circleload1 \
+ 	 neededtest neededtest2 neededtest3 neededtest4 \
+@@ -351,6 +352,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
+ 		libmarkermod5-1 libmarkermod5-2 libmarkermod5-3 libmarkermod5-4 \
+ 		libmarkermod5-5 tst-tls20mod-bad tst-tls21mod \
+ 		tst-dlmopen-dlerror-mod \
++		tst-dlmopen-gethostbyname-mod \
+ 
+ # Most modules build with _ISOMAC defined, but those filtered out
+ # depend on internal headers.
+@@ -1935,3 +1937,5 @@ $(objpfx)tst-tls21.out: $(objpfx)tst-tls21mod.so
+ $(objpfx)tst-tls21mod.so: $(tst-tls-many-dynamic-modules:%=$(objpfx)%.so)
+ 
+ $(objpfx)tst-rtld-run-static.out: $(objpfx)/ldconfig
++$(objpfx)tst-dlmopen-gethostbyname: $(libdl)
++$(objpfx)tst-dlmopen-gethostbyname.out: $(objpfx)tst-dlmopen-gethostbyname-mod.so
+diff --git a/elf/dl-libc.c b/elf/dl-libc.c
+index fc01f5514d4f656c..3a242d219756dac6 100644
+--- a/elf/dl-libc.c
++++ b/elf/dl-libc.c
+@@ -43,8 +43,8 @@ dlerror_run (void (*operate) (void *), void *args)
+   const char *last_errstring = NULL;
+   bool malloced;
+ 
+-  int result = (_dl_catch_error (&objname, &last_errstring, &malloced,
+-				operate, args)
++  int result = (GLRO (dl_catch_error) (&objname, &last_errstring, &malloced,
++				       operate, args)
+ 		?: last_errstring != NULL);
+ 
+   if (result && malloced)
+diff --git a/elf/tst-dlmopen-gethostbyname-mod.c b/elf/tst-dlmopen-gethostbyname-mod.c
+new file mode 100644
+index 0000000000000000..9a68ea5050c3060b
+--- /dev/null
++++ b/elf/tst-dlmopen-gethostbyname-mod.c
+@@ -0,0 +1,29 @@
++/* Exercise dlerror_run in elf/dl-libc.c after dlmopen, via NSS.  Helper module.
++   Copyright (C) 2021 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/>.  */
++
++#include <netdb.h>
++#include <nss.h>
++
++void
++call_gethostbyname (void)
++{
++  __nss_configure_lookup ("hosts", "files");
++  /* This should not terminate the process due to a missing
++     _nss_files_getcanonname_r symbol.  */
++  gethostbyname ("localhost");
++}
+diff --git a/elf/tst-dlmopen-gethostbyname.c b/elf/tst-dlmopen-gethostbyname.c
+new file mode 100644
+index 0000000000000000..12deb29900731c20
+--- /dev/null
++++ b/elf/tst-dlmopen-gethostbyname.c
+@@ -0,0 +1,31 @@
++/* Exercise dlerror_run in elf/dl-libc.c after dlmopen, via NSS (bug 27646).
++   Copyright (C) 2021 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/>.  */
++
++#include <support/xdlfcn.h>
++
++static int
++do_test (void)
++{
++  void *handle = xdlmopen (LM_ID_NEWLM, "tst-dlmopen-gethostbyname-mod.so",
++                           RTLD_NOW);
++  void (*call_gethostbyname) (void) = xdlsym (handle, "call_gethostbyname");
++  call_gethostbyname ();
++  return 0;
++}
++
++#include <support/test-driver.c>
diff --git a/SOURCES/glibc-rh2047981-8.patch b/SOURCES/glibc-rh2047981-8.patch
new file mode 100644
index 0000000000000000000000000000000000000000..885aad493ab0bf65c01fddfabe0ea7f66e8fef9b
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-8.patch
@@ -0,0 +1,29 @@
+commit 832f50be6c9c010e46180d14126bbb81f35e808c
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Tue Jul 6 13:22:01 2021 +0200
+
+    elf: Call free from base namespace on error in dl-libc.c [BZ #27646]
+    
+    In dlerror_run, free corresponds to the local malloc in the
+    namespace, but GLRO (dl_catch_error) uses the malloc from the base
+    namespace.  elf/tst-dlmopen-gethostbyname triggers this mismatch,
+    but it does not crash, presumably because of a fastbin deallocation.
+    
+    Fixes commit c2059edce20c124d1a99f1a94cc52e83b77a917a ("elf: Use
+    _dl_catch_error from base namespace in dl-libc.c [BZ #27646]") and
+    commit b2964eb1d9a6b8ab1250e8a881cf406182da5875 ("dlfcn: Failures
+    after dlmopen should not terminate process [BZ #24772]").
+
+diff --git a/elf/dl-libc.c b/elf/dl-libc.c
+index 3a242d219756dac6..bb6e3378d546b234 100644
+--- a/elf/dl-libc.c
++++ b/elf/dl-libc.c
+@@ -48,7 +48,7 @@ dlerror_run (void (*operate) (void *), void *args)
+ 		?: last_errstring != NULL);
+ 
+   if (result && malloced)
+-    free ((char *) last_errstring);
++    GLRO (dl_error_free) ((char *) last_errstring);
+ 
+   return result;
+ }
diff --git a/SOURCES/glibc-rh2047981-9.patch b/SOURCES/glibc-rh2047981-9.patch
new file mode 100644
index 0000000000000000000000000000000000000000..690027f6cb164711a90ec1ed1cc6fe1a780dda13
--- /dev/null
+++ b/SOURCES/glibc-rh2047981-9.patch
@@ -0,0 +1,126 @@
+commit 3908fa933a4354309225af616d9242f595e11ccf
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Wed Jun 30 00:21:18 2021 -0300
+
+    elf: Fix audit regression
+    
+    Commit 03e187a41d9 added a regression when an audit module does not have
+    libc as DT_NEEDED (although unusual it is possible).
+    
+    Checked on x86_64-linux-gnu.
+
+Conflicts:
+	elf/Makefile
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 30417c3ce15abcb4..6262a4a65cfd2148 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -218,6 +218,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
+ 	 tst-rtld-run-static \
+ 	 tst-dlmopen-dlerror \
+ 	 tst-dlmopen-gethostbyname \
++	 tst-audit17 \
+ #	 reldep9
+ tests-internal += loadtest unload unload2 circleload1 \
+ 	 neededtest neededtest2 neededtest3 neededtest4 \
+@@ -1527,6 +1528,16 @@ $(objpfx)tst-auditlogmod-3.so: $(libsupport)
+ $(objpfx)tst-audit16.out: \
+   $(objpfx)tst-auditlogmod-1.so $(objpfx)tst-auditlogmod-2.so \
+   $(objpfx)tst-auditlogmod-3.so
++$(objpfx)tst-audit17.out: $(objpfx)tst-auditmod17.so
++# The test check if a audit library without libc.so on DT_NEEDED works as
++# intended, so it uses an explicit link rule.
++$(objpfx)tst-auditmod17.so: $(objpfx)tst-auditmod17.os
++	$(CC) -nostdlib -nostartfiles -shared -o $@.new \
++	$(filter-out $(map-file),$^)
++	$(call after-link,$@.new)
++	mv -f $@.new $@
++CFLAGS-.os += $(call elide-stack-protector,.os,tst-auditmod17)
++tst-audit17-ENV = LD_AUDIT=$(objpfx)tst-auditmod17.so
+ 
+ # tst-sonamemove links against an older implementation of the library.
+ LDFLAGS-tst-sonamemove-linkmod1.so = \
+diff --git a/elf/dl-open.c b/elf/dl-open.c
+index 736df62ce6e46d34..661a2172d1789b26 100644
+--- a/elf/dl-open.c
++++ b/elf/dl-open.c
+@@ -759,16 +759,9 @@ dl_open_worker_begin (void *a)
+      namespace.  */
+   if (!args->libc_already_loaded)
+     {
++      /* dlopen cannot be used to load an initial libc by design.  */
+       struct link_map *libc_map = GL(dl_ns)[args->nsid].libc_map;
+-#ifdef SHARED
+-      bool initial = libc_map->l_ns == LM_ID_BASE;
+-#else
+-      /* In the static case, there is only one namespace, but it
+-	 contains a secondary libc (the primary libc is statically
+-	 linked).  */
+-      bool initial = false;
+-#endif
+-      _dl_call_libc_early_init (libc_map, initial);
++      _dl_call_libc_early_init (libc_map, false);
+     }
+ 
+ #ifndef SHARED
+diff --git a/elf/tst-audit17.c b/elf/tst-audit17.c
+new file mode 100644
+index 0000000000000000..92986699d497845f
+--- /dev/null
++++ b/elf/tst-audit17.c
+@@ -0,0 +1,25 @@
++/* Check DT_AUDIT with audit not linked against libc.
++   Copyright (C) 2021 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/>.  */
++
++static int
++do_test (void)
++{
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/elf/tst-auditmod17.c b/elf/tst-auditmod17.c
+new file mode 100644
+index 0000000000000000..7a4467f597b56cf4
+--- /dev/null
++++ b/elf/tst-auditmod17.c
+@@ -0,0 +1,23 @@
++/* Check DT_AUDIT with audit not linked against libc.
++   Copyright (C) 2021 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/>.  */
++
++unsigned int
++la_version (unsigned int version)
++{
++  return version;
++}
diff --git a/SOURCES/glibc-rh2054790.patch b/SOURCES/glibc-rh2054790.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6af769ebc4da8d9ef7e0eb8058c36e0fbd52f486
--- /dev/null
+++ b/SOURCES/glibc-rh2054790.patch
@@ -0,0 +1,27 @@
+commit ea89d5bbd9e5e514b606045d909e6ab87d851c88
+Author: Arjun Shankar <arjun@redhat.com>
+Date:   Thu Feb 24 21:43:09 2022 +0100
+
+    localedef: Handle symbolic links when generating locale-archive
+    
+    Whenever locale data for any locale included symbolic links, localedef
+    would throw the error "incomplete set of locale files" and exclude it
+    from the generated locale archive.  This commit fixes that.
+    
+    Co-authored-by: Florian Weimer <fweimer@redhat.com>
+    
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+
+diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c
+index dec73264563bc2a0..990f7eb6830d2e57 100644
+--- a/locale/programs/locarchive.c
++++ b/locale/programs/locarchive.c
+@@ -1391,7 +1391,7 @@ add_locales_to_archive (size_t nlist, char *list[], bool replace)
+ 		    {
+ 		      char fullname[fnamelen + 2 * strlen (d->d_name) + 7];
+ 
+-		      if (d_type == DT_UNKNOWN)
++		      if (d_type == DT_UNKNOWN || d_type == DT_LNK)
+ 			{
+ 			  strcpy (stpcpy (stpcpy (fullname, fname), "/"),
+ 				  d->d_name);
diff --git a/SOURCES/glibc-rh2063042.patch b/SOURCES/glibc-rh2063042.patch
new file mode 100644
index 0000000000000000000000000000000000000000..921add17159de2a592a52fc69d0799bb0cc4be87
--- /dev/null
+++ b/SOURCES/glibc-rh2063042.patch
@@ -0,0 +1,358 @@
+commit fcfc9086815bf0d277ad47a90ee3fda4c37acca8
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Wed Jan 12 23:34:48 2022 +0530
+
+    debug: Synchronize feature guards in fortified functions [BZ #28746]
+    
+    Some functions (e.g. stpcpy, pread64, etc.) had moved to POSIX in the
+    main headers as they got incorporated into the standard, but their
+    fortified variants remained under __USE_GNU.  As a result, these
+    functions did not get fortified when _GNU_SOURCE was not defined.
+    
+    Add test wrappers that check all functions tested in tst-chk0 at all
+    levels with _GNU_SOURCE undefined and then use the failures to (1)
+    exclude checks for _GNU_SOURCE functions in these tests and (2) Fix
+    feature macro guards in the fortified function headers so that they're
+    the same as the ones in the main headers.
+    
+    This fixes BZ #28746.
+    
+    Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+# Conflicts:
+#	debug/tst-fortify.c
+
+diff --git a/debug/Makefile b/debug/Makefile
+index c92fd23dda1a7279..b0f0b7beb6d5cef5 100644
+--- a/debug/Makefile
++++ b/debug/Makefile
+@@ -132,6 +132,12 @@ define cflags-lfs
+ CFLAGS-tst-fortify-$(1)-lfs-$(2).$(1) += -D_FILE_OFFSET_BITS=64
+ endef
+ 
++define cflags-nongnu
++CFLAGS-tst-fortify-$(1)-nongnu-$(2).$(1) += -D_LARGEFILE64_SOURCE=1
++endef
++
++src-chk-nongnu = \#undef _GNU_SOURCE
++
+ # We know these tests have problems with format strings, this is what
+ # we are testing.  Disable that warning.  They are also testing
+ # deprecated functions (notably gets) so disable that warning as well.
+@@ -145,13 +151,13 @@ CFLAGS-tst-fortify-$(1)-$(2)-$(3).$(1) += -D_FORTIFY_SOURCE=$(3) -Wno-format \
+ $(eval $(call cflags-$(2),$(1),$(3)))
+ $(objpfx)tst-fortify-$(1)-$(2)-$(3).$(1): tst-fortify.c Makefile
+ 	( echo "/* Autogenerated from Makefile.  */"; \
+-	  echo ""; \
++	  echo "$(src-chk-$(2))"; \
+ 	  echo "#include \"tst-fortify.c\"" ) > $$@.tmp
+ 	mv $$@.tmp $$@
+ endef
+ 
+ chk-extensions = c cc
+-chk-types = default lfs
++chk-types = default lfs nongnu
+ chk-levels = 1 2 3
+ 
+ $(foreach e,$(chk-extensions), \
+diff --git a/debug/tst-fortify.c b/debug/tst-fortify.c
+index 5e76081255316a93..1668294e48b5c63c 100644
+--- a/debug/tst-fortify.c
++++ b/debug/tst-fortify.c
+@@ -1,4 +1,5 @@
+-/* Copyright (C) 2004-2018 Free Software Foundation, Inc.
++/* Copyright (C) 2004-2022 Free Software Foundation, Inc.
++   Copyright The GNU Toolchain Authors.
+    This file is part of the GNU C Library.
+    Contributed by Jakub Jelinek <jakub@redhat.com>, 2004.
+ 
+@@ -37,6 +38,17 @@
+ #include <sys/socket.h>
+ #include <sys/un.h>
+ 
++#ifndef _GNU_SOURCE
++# define MEMPCPY memcpy
++# define WMEMPCPY wmemcpy
++# define MEMPCPY_RET(x) 0
++# define WMEMPCPY_RET(x) 0
++#else
++# define MEMPCPY mempcpy
++# define WMEMPCPY wmempcpy
++# define MEMPCPY_RET(x) __builtin_strlen (x)
++# define WMEMPCPY_RET(x) wcslen (x)
++#endif
+ 
+ #define obstack_chunk_alloc malloc
+ #define obstack_chunk_free free
+@@ -163,7 +175,7 @@ do_test (void)
+   if (memcmp (buf, "aabcdefghi", 10))
+     FAIL ();
+ 
+-  if (mempcpy (buf + 5, "abcde", 5) != buf + 10
++  if (MEMPCPY (buf + 5, "abcde", 5) != buf + 5 + MEMPCPY_RET ("abcde")
+       || memcmp (buf, "aabcdabcde", 10))
+     FAIL ();
+ 
+@@ -208,7 +220,7 @@ do_test (void)
+   if (memcmp (buf, "aabcdefghi", 10))
+     FAIL ();
+ 
+-  if (mempcpy (buf + 5, "abcde", l0 + 5) != buf + 10
++  if (MEMPCPY (buf + 5, "abcde", l0 + 5) != buf + 5 + MEMPCPY_RET ("abcde")
+       || memcmp (buf, "aabcdabcde", 10))
+     FAIL ();
+ 
+@@ -267,7 +279,8 @@ do_test (void)
+   if (memcmp (a.buf1, "aabcdefghi", 10))
+     FAIL ();
+ 
+-  if (mempcpy (a.buf1 + 5, "abcde", l0 + 5) != a.buf1 + 10
++  if (MEMPCPY (a.buf1 + 5, "abcde", l0 + 5)
++      != a.buf1 + 5 + MEMPCPY_RET ("abcde")
+       || memcmp (a.buf1, "aabcdabcde", 10))
+     FAIL ();
+ 
+@@ -348,6 +361,7 @@ do_test (void)
+   bcopy (buf + 1, buf + 2, l0 + 9);
+   CHK_FAIL_END
+ 
++#ifdef _GNU_SOURCE
+   CHK_FAIL_START
+   p = (char *) mempcpy (buf + 6, "abcde", 5);
+   CHK_FAIL_END
+@@ -355,6 +369,7 @@ do_test (void)
+   CHK_FAIL_START
+   p = (char *) mempcpy (buf + 6, "abcde", l0 + 5);
+   CHK_FAIL_END
++#endif
+ 
+   CHK_FAIL_START
+   memset (buf + 9, 'j', 2);
+@@ -465,6 +480,7 @@ do_test (void)
+   bcopy (a.buf1 + 1, a.buf1 + 2, l0 + 9);
+   CHK_FAIL_END
+ 
++#ifdef _GNU_SOURCE
+   CHK_FAIL_START
+   p = (char *) mempcpy (a.buf1 + 6, "abcde", 5);
+   CHK_FAIL_END
+@@ -472,6 +488,7 @@ do_test (void)
+   CHK_FAIL_START
+   p = (char *) mempcpy (a.buf1 + 6, "abcde", l0 + 5);
+   CHK_FAIL_END
++#endif
+ 
+   CHK_FAIL_START
+   memset (a.buf1 + 9, 'j', 2);
+@@ -551,7 +568,7 @@ do_test (void)
+   if (wmemcmp (wbuf, L"aabcdefghi", 10))
+     FAIL ();
+ 
+-  if (wmempcpy (wbuf + 5, L"abcde", 5) != wbuf + 10
++  if (WMEMPCPY (wbuf + 5, L"abcde", 5) != wbuf + 5 + WMEMPCPY_RET (L"abcde")
+       || wmemcmp (wbuf, L"aabcdabcde", 10))
+     FAIL ();
+ 
+@@ -584,7 +601,8 @@ do_test (void)
+   if (wmemcmp (wbuf, L"aabcdefghi", 10))
+     FAIL ();
+ 
+-  if (wmempcpy (wbuf + 5, L"abcde", l0 + 5) != wbuf + 10
++  if (WMEMPCPY (wbuf + 5, L"abcde", l0 + 5)
++      != wbuf + 5 + WMEMPCPY_RET (L"abcde")
+       || wmemcmp (wbuf, L"aabcdabcde", 10))
+     FAIL ();
+ 
+@@ -626,7 +644,8 @@ do_test (void)
+   if (wmemcmp (wa.buf1, L"aabcdefghi", 10))
+     FAIL ();
+ 
+-  if (wmempcpy (wa.buf1 + 5, L"abcde", l0 + 5) != wa.buf1 + 10
++  if (WMEMPCPY (wa.buf1 + 5, L"abcde", l0 + 5)
++      != wa.buf1 + 5 + WMEMPCPY_RET (L"abcde")
+       || wmemcmp (wa.buf1, L"aabcdabcde", 10))
+     FAIL ();
+ 
+@@ -695,6 +714,7 @@ do_test (void)
+   wmemmove (wbuf + 2, wbuf + 1, l0 + 9);
+   CHK_FAIL_END
+ 
++#ifdef _GNU_SOURCE
+   CHK_FAIL_START
+   wp = wmempcpy (wbuf + 6, L"abcde", 5);
+   CHK_FAIL_END
+@@ -702,6 +722,7 @@ do_test (void)
+   CHK_FAIL_START
+   wp = wmempcpy (wbuf + 6, L"abcde", l0 + 5);
+   CHK_FAIL_END
++#endif
+ 
+   CHK_FAIL_START
+   wmemset (wbuf + 9, L'j', 2);
+@@ -769,6 +790,7 @@ do_test (void)
+   wmemmove (wa.buf1 + 2, wa.buf1 + 1, l0 + 9);
+   CHK_FAIL_END
+ 
++#ifdef _GNU_SOURCE
+   CHK_FAIL_START
+   wp = wmempcpy (wa.buf1 + 6, L"abcde", 5);
+   CHK_FAIL_END
+@@ -776,6 +798,7 @@ do_test (void)
+   CHK_FAIL_START
+   wp = wmempcpy (wa.buf1 + 6, L"abcde", l0 + 5);
+   CHK_FAIL_END
++#endif
+ 
+   CHK_FAIL_START
+   wmemset (wa.buf1 + 9, L'j', 2);
+@@ -907,6 +930,7 @@ do_test (void)
+   if (fprintf (fp, buf2 + 4, str5) != 7)
+     FAIL ();
+ 
++#ifdef _GNU_SOURCE
+   char *my_ptr = NULL;
+   strcpy (buf2 + 2, "%n%s%n");
+   /* When the format string is writable and contains %n,
+@@ -936,6 +960,7 @@ do_test (void)
+   if (obstack_printf (&obs, "%s%n%s%n", str4, &n1, str5, &n1) != 14)
+     FAIL ();
+   obstack_free (&obs, NULL);
++#endif
+ 
+   if (freopen (temp_filename, "r", stdin) == NULL)
+     {
+@@ -983,6 +1008,7 @@ do_test (void)
+ 
+   rewind (stdin);
+ 
++#ifdef _GNU_SOURCE
+   if (fgets_unlocked (buf, buf_size, stdin) != buf
+       || memcmp (buf, "abcdefgh\n", 10))
+     FAIL ();
+@@ -1009,6 +1035,7 @@ do_test (void)
+ #endif
+ 
+   rewind (stdin);
++#endif
+ 
+   if (fread (buf, 1, buf_size, stdin) != buf_size
+       || memcmp (buf, "abcdefgh\nA", 10))
+@@ -1579,7 +1606,10 @@ do_test (void)
+       ret = 1;
+     }
+ 
+-  int fd = posix_openpt (O_RDWR);
++  int fd;
++
++#ifdef _GNU_SOURCE
++  fd = posix_openpt (O_RDWR);
+   if (fd != -1)
+     {
+       char enough[1000];
+@@ -1595,6 +1625,7 @@ do_test (void)
+ #endif
+       close (fd);
+     }
++#endif
+ 
+ #if PATH_MAX > 0
+   confstr (_CS_GNU_LIBC_VERSION, largebuf, sizeof (largebuf));
+@@ -1712,8 +1743,9 @@ do_test (void)
+   poll (fds, l0 + 2, 0);
+   CHK_FAIL_END
+ #endif
++#ifdef _GNU_SOURCE
+   ppoll (fds, 1, NULL, NULL);
+-#if __USE_FORTIFY_LEVEL >= 1
++# if __USE_FORTIFY_LEVEL >= 1
+   CHK_FAIL_START
+   ppoll (fds, 2, NULL, NULL);
+   CHK_FAIL_END
+@@ -1721,6 +1753,7 @@ do_test (void)
+   CHK_FAIL_START
+   ppoll (fds, l0 + 2, NULL, NULL);
+   CHK_FAIL_END
++# endif
+ #endif
+ 
+   return ret;
+diff --git a/posix/bits/unistd.h b/posix/bits/unistd.h
+index a456d1723547db70..ddfaed4dd7574cd2 100644
+--- a/posix/bits/unistd.h
++++ b/posix/bits/unistd.h
+@@ -38,7 +38,7 @@ read (int __fd, void *__buf, size_t __nbytes)
+ 			  __fd, __buf, __nbytes);
+ }
+ 
+-#ifdef __USE_UNIX98
++#if defined __USE_UNIX98 || defined __USE_XOPEN2K8
+ extern ssize_t __pread_chk (int __fd, void *__buf, size_t __nbytes,
+ 			    __off_t __offset, size_t __bufsize) __wur;
+ extern ssize_t __pread64_chk (int __fd, void *__buf, size_t __nbytes,
+diff --git a/string/bits/string_fortified.h b/string/bits/string_fortified.h
+index 27ec273ec41cd81c..3f86629bf8fc51a2 100644
+--- a/string/bits/string_fortified.h
++++ b/string/bits/string_fortified.h
+@@ -94,7 +94,7 @@ __NTH (strcpy (char *__restrict __dest, const char *__restrict __src))
+   return __builtin___strcpy_chk (__dest, __src, __glibc_objsize (__dest));
+ }
+ 
+-#ifdef __USE_GNU
++#ifdef __USE_XOPEN2K8
+ __fortify_function char *
+ __NTH (stpcpy (char *__restrict __dest, const char *__restrict __src))
+ {
+@@ -111,14 +111,15 @@ __NTH (strncpy (char *__restrict __dest, const char *__restrict __src,
+ 				  __glibc_objsize (__dest));
+ }
+ 
+-#if __GNUC_PREREQ (4, 7) || __glibc_clang_prereq (2, 6)
++#ifdef __USE_XOPEN2K8
++# if __GNUC_PREREQ (4, 7) || __glibc_clang_prereq (2, 6)
+ __fortify_function char *
+ __NTH (stpncpy (char *__dest, const char *__src, size_t __n))
+ {
+   return __builtin___stpncpy_chk (__dest, __src, __n,
+ 				  __glibc_objsize (__dest));
+ }
+-#else
++# else
+ extern char *__stpncpy_chk (char *__dest, const char *__src, size_t __n,
+ 			    size_t __destlen) __THROW;
+ extern char *__REDIRECT_NTH (__stpncpy_alias, (char *__dest, const char *__src,
+@@ -132,6 +133,7 @@ __NTH (stpncpy (char *__dest, const char *__src, size_t __n))
+     return __stpncpy_chk (__dest, __src, __n, __bos (__dest));
+   return __stpncpy_alias (__dest, __src, __n);
+ }
++# endif
+ #endif
+ 
+ 
+diff --git a/support/xsignal.h b/support/xsignal.h
+index 9ab8d1bfddf6c598..fae6108a522ae5fe 100644
+--- a/support/xsignal.h
++++ b/support/xsignal.h
+@@ -28,7 +28,9 @@ __BEGIN_DECLS
+    terminate the process on error.  */
+ 
+ void xraise (int sig);
++#ifdef _GNU_SOURCE
+ sighandler_t xsignal (int sig, sighandler_t handler);
++#endif
+ void xsigaction (int sig, const struct sigaction *newact,
+                  struct sigaction *oldact);
+ 
+diff --git a/wcsmbs/bits/wchar2.h b/wcsmbs/bits/wchar2.h
+index f82bba481981e4fb..5c68979e96a504b4 100644
+--- a/wcsmbs/bits/wchar2.h
++++ b/wcsmbs/bits/wchar2.h
+@@ -457,7 +457,7 @@ __NTH (wcsrtombs (char *__restrict __dst, const wchar_t **__restrict __src,
+ }
+ 
+ 
+-#ifdef __USE_GNU
++#ifdef	__USE_XOPEN2K8
+ extern size_t __mbsnrtowcs_chk (wchar_t *__restrict __dst,
+ 				const char **__restrict __src, size_t __nmc,
+ 				size_t __len, mbstate_t *__restrict __ps,
diff --git a/SOURCES/glibc-rh2063712.patch b/SOURCES/glibc-rh2063712.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b66fce47029d0bfcfab713d6ff0f2e3c2e0fd67f
--- /dev/null
+++ b/SOURCES/glibc-rh2063712.patch
@@ -0,0 +1,208 @@
+This is a downstream rework of this upstream patch:
+
+  [PATCH v2 2/2] nss: Protect against errno changes in function lookup (bug 28953)
+  <https://sourceware.org/pipermail/libc-alpha/2022-March/137015.html>
+
+The NSS module loading code has been rewritten upstream, which is why
+only the test can be reused.  NSS_DECLARE_MODULE_FUNCTIONS does not yet
+exist downstream, so this part had to be skipped.
+
+diff --git a/nss/Makefile b/nss/Makefile
+index d5c28a6b5ed3661c..e8a7d9c7b3cefcdf 100644
+--- a/nss/Makefile
++++ b/nss/Makefile
+@@ -59,7 +59,8 @@ tests			= test-netdb test-digits-dots tst-nss-getpwent bug17079 \
+ 			  tst-nss-test2 \
+ 			  tst-nss-test3 \
+ 			  tst-nss-test4 \
+-			  tst-nss-test5
++			  tst-nss-test5 \
++			  tst-nss-test_errno
+ xtests			= bug-erange
+ 
+ tests-container = \
+@@ -130,7 +131,7 @@ routines                += $(libnss_files-routines)
+ static-only-routines    += $(libnss_files-routines)
+ tests-static		+= tst-nss-static
+ endif
+-extra-test-objs		+= nss_test1.os nss_test2.os
++extra-test-objs		+= nss_test1.os nss_test2.os nss_test_errno.os
+ 
+ include ../Rules
+ 
+@@ -166,10 +167,13 @@ rtld-tests-LDFLAGS += -Wl,--dynamic-list=nss_test.ver
+ 
+ libof-nss_test1 = extramodules
+ libof-nss_test2 = extramodules
++libof-nss_test_errno = extramodules
+ $(objpfx)/libnss_test1.so: $(objpfx)nss_test1.os $(link-libc-deps)
+ 	$(build-module)
+ $(objpfx)/libnss_test2.so: $(objpfx)nss_test2.os $(link-libc-deps)
+ 	$(build-module)
++$(objpfx)/libnss_test_errno.so: $(objpfx)nss_test_errno.os $(link-libc-deps)
++	$(build-module)
+ $(objpfx)nss_test2.os : nss_test1.c
+ ifdef libnss_test1.so-version
+ $(objpfx)/libnss_test1.so$(libnss_test1.so-version): $(objpfx)/libnss_test1.so
+@@ -179,9 +183,13 @@ ifdef libnss_test2.so-version
+ $(objpfx)/libnss_test2.so$(libnss_test2.so-version): $(objpfx)/libnss_test2.so
+ 	$(make-link)
+ endif
++$(objpfx)/libnss_test_errno.so$(libnss_files.so-version): \
++  $(objpfx)/libnss_test_errno.so
++	$(make-link)
+ $(patsubst %,$(objpfx)%.out,$(tests)) : \
+ 	$(objpfx)/libnss_test1.so$(libnss_test1.so-version) \
+-	$(objpfx)/libnss_test2.so$(libnss_test2.so-version)
++	$(objpfx)/libnss_test2.so$(libnss_test2.so-version) \
++	$(objpfx)/libnss_test_errno.so$(libnss_files.so-version)
+ 
+ ifeq (yes,$(have-thread-library))
+ $(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library)
+diff --git a/nss/nss_test_errno.c b/nss/nss_test_errno.c
+new file mode 100644
+index 0000000000000000..ca75c890aa057869
+--- /dev/null
++++ b/nss/nss_test_errno.c
+@@ -0,0 +1,53 @@
++/* NSS service provider with errno clobber.
++   Copyright (C) 2022 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/>.  */
++
++#include <errno.h>
++#include <nss.h>
++#include <pwd.h>
++#include <stdlib.h>
++
++static void __attribute__ ((constructor))
++init (void)
++{
++  /* An arbitrary error code which is otherwise not used.  */
++  errno = ELIBBAD;
++}
++
++/* Lookup functions for pwd follow that do not return any data.  */
++
++enum nss_status
++_nss_test_errno_setpwent (int stayopen)
++{
++  setenv ("_nss_test_errno_setpwent", "yes", 1);
++  return NSS_STATUS_SUCCESS;
++}
++
++enum nss_status
++_nss_test_errno_getpwent_r (struct passwd *result,
++                            char *buffer, size_t size, int *errnop)
++{
++  setenv ("_nss_test_errno_getpwent_r", "yes", 1);
++  return NSS_STATUS_NOTFOUND;
++}
++
++enum nss_status
++_nss_test_errno_endpwent (void)
++{
++  setenv ("_nss_test_errno_endpwent", "yes", 1);
++  return NSS_STATUS_SUCCESS;
++}
+diff --git a/nss/nsswitch.c b/nss/nsswitch.c
+index 17adf1ef03f93d60..e59ab674e0426b26 100644
+--- a/nss/nsswitch.c
++++ b/nss/nsswitch.c
+@@ -401,6 +401,7 @@ void *
+ __nss_lookup_function (service_user *ni, const char *fct_name)
+ {
+   void **found, *result;
++  int saved_errno = errno;
+ 
+   /* We now modify global data.  Protect it.  */
+   __libc_lock_lock (lock);
+@@ -523,6 +524,8 @@ __nss_lookup_function (service_user *ni, const char *fct_name)
+   /* Remove the lock.  */
+   __libc_lock_unlock (lock);
+ 
++  __set_errno (saved_errno);
++
+   return result;
+ }
+ libc_hidden_def (__nss_lookup_function)
+diff --git a/nss/tst-nss-test_errno.c b/nss/tst-nss-test_errno.c
+new file mode 100644
+index 0000000000000000..d2c42dd363a38b0e
+--- /dev/null
++++ b/nss/tst-nss-test_errno.c
+@@ -0,0 +1,61 @@
++/* getpwent failure when dlopen clobbers errno (bug 28953).
++   Copyright (C) 2022 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/>.  */
++
++#include <nss.h>
++#include <support/check.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <stdbool.h>
++#include <pwd.h>
++#include <string.h>
++
++static int
++do_test (void)
++{
++  __nss_configure_lookup ("passwd", "files test_errno");
++
++  errno = 0;
++  setpwent ();
++  TEST_COMPARE (errno, 0);
++
++  bool root_seen = false;
++  while (true)
++    {
++      errno = 0;
++      struct passwd *e = getpwent ();
++      if (e == NULL)
++        break;
++      if (strcmp (e->pw_name, "root"))
++        root_seen = true;
++    }
++
++  TEST_COMPARE (errno, 0);
++  TEST_VERIFY (root_seen);
++
++  errno = 0;
++  endpwent ();
++  TEST_COMPARE (errno, 0);
++
++  TEST_COMPARE_STRING (getenv ("_nss_test_errno_setpwent"), "yes");
++  TEST_COMPARE_STRING (getenv ("_nss_test_errno_getpwent_r"), "yes");
++  TEST_COMPARE_STRING (getenv ("_nss_test_errno_endpwent"), "yes");
++
++  return 0;
++}
++
++#include <support/test-driver.c>
diff --git a/SOURCES/glibc-rh2065588-1.patch b/SOURCES/glibc-rh2065588-1.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3b4db3f8f1cbe4be0b48e627ba905dcb914f4e55
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-1.patch
@@ -0,0 +1,253 @@
+commit 2a973ab7f1a6f6cd9be1c7257fd7b5d331515eab
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Wed Sep 12 10:30:46 2018 -0300
+
+    posix: Add internal symbols for posix_spawn interface
+    
+    This patch adds internal hidden definition for mostly of the posix_spawn
+    function so it can be used internally on both popen and system
+    implementations.
+    
+    Checked on x86_64-linux-gnu.
+    
+            * include/spawn.h (__posix_spawn, posix_spawn_file_actions_addclose,
+            __posix_spawn_file_actions_adddup2, __posix_spawn_file_actions_destroy,
+            __posix_spawn_file_actions_init, __posix_spawnattr_init,
+            __posix_spawnattr_destroy, __posix_spawnattr_setflags,
+            __posix_spawnattr_setsigdefault, __posix_spawnattr_setsigmask): New
+            prototype.
+            * posix/spawn.c (__posix_spawn): Add libc_hidden_def.
+            * posix/spawn_faction_addclose.c
+            (__posix_spawn_file_actions_addclose): Add hidden definition.
+            * posix/spawn_faction_adddup2.c
+            (__posix_spawn_file_actions_adddup2): Likewise.
+            * posix/spawn_faction_destroy.c
+            (__posix_spawn_file_actions_destroy): Likewise.
+            * posix/spawn_faction_init.c (__posix_spawn_file_actions_init):
+            Likewise.
+            * posix/spawnattr_destroy.c (__posix_spawnattr_destroy): Likewise.
+            * posix/spawnattr_init.c (__posix_spawnattr_init): Likewise.
+            * posix/spawnattr_setdefault.c (__posix_spawnattr_setsigdefault):
+            Likewise.
+            * posix/spawnattr_setflags.c (__posix_spawnattr_setflags): Likewise.
+            * posix/spawnattr_setsigmask.c (__posix_spawnattr_setsigmask):
+            Likewise.
+
+diff --git a/include/spawn.h b/include/spawn.h
+index a6c7a8adc361927e..7fdd965bd780f8de 100644
+--- a/include/spawn.h
++++ b/include/spawn.h
+@@ -1 +1,36 @@
++#ifndef _SPAWN_H
+ #include <posix/spawn.h>
++
++# ifndef _ISOMAC
++__typeof (posix_spawn) __posix_spawn;
++libc_hidden_proto (__posix_spawn)
++
++__typeof (posix_spawn_file_actions_addclose)
++  __posix_spawn_file_actions_addclose attribute_hidden;
++
++__typeof (posix_spawn_file_actions_adddup2)
++  __posix_spawn_file_actions_adddup2 attribute_hidden;
++
++__typeof (posix_spawn_file_actions_destroy)
++  __posix_spawn_file_actions_destroy attribute_hidden;
++
++__typeof (posix_spawn_file_actions_init) __posix_spawn_file_actions_init
++  attribute_hidden;
++
++__typeof (posix_spawnattr_init) __posix_spawnattr_init
++  attribute_hidden;
++
++__typeof (posix_spawnattr_destroy) __posix_spawnattr_destroy
++  attribute_hidden;
++
++__typeof (posix_spawnattr_setflags) __posix_spawnattr_setflags
++  attribute_hidden;
++
++__typeof (posix_spawnattr_setsigdefault) __posix_spawnattr_setsigdefault
++  attribute_hidden;
++
++__typeof (posix_spawnattr_setsigmask) __posix_spawnattr_setsigmask
++  attribute_hidden;
++
++# endif /* !_ISOMAC  */
++#endif /* spawn.h  */
+diff --git a/posix/spawn.c b/posix/spawn.c
+index 51f67b2755bd4949..a82f1c84e299f018 100644
+--- a/posix/spawn.c
++++ b/posix/spawn.c
+@@ -30,6 +30,7 @@ __posix_spawn (pid_t *pid, const char *path,
+   return __spawni (pid, path, file_actions, attrp, argv, envp, 0);
+ }
+ versioned_symbol (libc, __posix_spawn, posix_spawn, GLIBC_2_15);
++libc_hidden_def (__posix_spawn)
+ 
+ 
+ #if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_15)
+diff --git a/posix/spawn_faction_addclose.c b/posix/spawn_faction_addclose.c
+index 21081e19b55db44c..e1fafe438cf15c91 100644
+--- a/posix/spawn_faction_addclose.c
++++ b/posix/spawn_faction_addclose.c
+@@ -24,8 +24,8 @@
+ /* Add an action to FILE-ACTIONS which tells the implementation to call
+    `close' for the given file descriptor during the `spawn' call.  */
+ int
+-posix_spawn_file_actions_addclose (posix_spawn_file_actions_t *file_actions,
+-				   int fd)
++__posix_spawn_file_actions_addclose (posix_spawn_file_actions_t *file_actions,
++				     int fd)
+ {
+   struct __spawn_action *rec;
+ 
+@@ -48,3 +48,5 @@ posix_spawn_file_actions_addclose (posix_spawn_file_actions_t *file_actions,
+ 
+   return 0;
+ }
++weak_alias (__posix_spawn_file_actions_addclose,
++	    posix_spawn_file_actions_addclose)
+diff --git a/posix/spawn_faction_adddup2.c b/posix/spawn_faction_adddup2.c
+index 363bc29ae502bd60..371b1de3e6f1979a 100644
+--- a/posix/spawn_faction_adddup2.c
++++ b/posix/spawn_faction_adddup2.c
+@@ -24,8 +24,8 @@
+ /* Add an action to FILE-ACTIONS which tells the implementation to call
+    `dup2' for the given file descriptors during the `spawn' call.  */
+ int
+-posix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *file_actions,
+-				  int fd, int newfd)
++__posix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *file_actions,
++				    int fd, int newfd)
+ {
+   struct __spawn_action *rec;
+ 
+@@ -49,3 +49,5 @@ posix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *file_actions,
+ 
+   return 0;
+ }
++weak_alias (__posix_spawn_file_actions_adddup2,
++	    posix_spawn_file_actions_adddup2)
+diff --git a/posix/spawn_faction_destroy.c b/posix/spawn_faction_destroy.c
+index 46061ee3473d4475..2a2de4e41d6bd6d0 100644
+--- a/posix/spawn_faction_destroy.c
++++ b/posix/spawn_faction_destroy.c
+@@ -22,7 +22,7 @@
+ 
+ /* Deallocate the file actions.  */
+ int
+-posix_spawn_file_actions_destroy (posix_spawn_file_actions_t *file_actions)
++__posix_spawn_file_actions_destroy (posix_spawn_file_actions_t *file_actions)
+ {
+   /* Free the paths in the open actions.  */
+   for (int i = 0; i < file_actions->__used; ++i)
+@@ -44,3 +44,5 @@ posix_spawn_file_actions_destroy (posix_spawn_file_actions_t *file_actions)
+   free (file_actions->__actions);
+   return 0;
+ }
++weak_alias (__posix_spawn_file_actions_destroy,
++	    posix_spawn_file_actions_destroy)
+diff --git a/posix/spawn_faction_init.c b/posix/spawn_faction_init.c
+index ddb42e6a77ba41ec..98432067c645021e 100644
+--- a/posix/spawn_faction_init.c
++++ b/posix/spawn_faction_init.c
+@@ -45,9 +45,10 @@ __posix_spawn_file_actions_realloc (posix_spawn_file_actions_t *file_actions)
+ 
+ /* Initialize data structure for file attribute for `spawn' call.  */
+ int
+-posix_spawn_file_actions_init (posix_spawn_file_actions_t *file_actions)
++__posix_spawn_file_actions_init (posix_spawn_file_actions_t *file_actions)
+ {
+   /* Simply clear all the elements.  */
+   memset (file_actions, '\0', sizeof (*file_actions));
+   return 0;
+ }
++weak_alias (__posix_spawn_file_actions_init, posix_spawn_file_actions_init)
+diff --git a/posix/spawnattr_destroy.c b/posix/spawnattr_destroy.c
+index 603e00fffefae2bf..043386778588913a 100644
+--- a/posix/spawnattr_destroy.c
++++ b/posix/spawnattr_destroy.c
+@@ -19,8 +19,9 @@
+ 
+ /* Initialize data structure for file attribute for `spawn' call.  */
+ int
+-posix_spawnattr_destroy (posix_spawnattr_t *attr)
++__posix_spawnattr_destroy (posix_spawnattr_t *attr)
+ {
+   /* Nothing to do in the moment.  */
+   return 0;
+ }
++weak_alias (__posix_spawnattr_destroy, posix_spawnattr_destroy)
+diff --git a/posix/spawnattr_init.c b/posix/spawnattr_init.c
+index bab464e62bdf7889..4e1218ab44e3f779 100644
+--- a/posix/spawnattr_init.c
++++ b/posix/spawnattr_init.c
+@@ -20,7 +20,7 @@
+ 
+ /* Initialize data structure for file attribute for `spawn' call.  */
+ int
+-posix_spawnattr_init (posix_spawnattr_t *attr)
++__posix_spawnattr_init (posix_spawnattr_t *attr)
+ {
+   /* All elements have to be initialized to the default values which
+      is generally zero.  */
+@@ -28,3 +28,4 @@ posix_spawnattr_init (posix_spawnattr_t *attr)
+ 
+   return 0;
+ }
++weak_alias (__posix_spawnattr_init, posix_spawnattr_init)
+diff --git a/posix/spawnattr_setdefault.c b/posix/spawnattr_setdefault.c
+index c77cda59be3dda20..174bcfa423dc5666 100644
+--- a/posix/spawnattr_setdefault.c
++++ b/posix/spawnattr_setdefault.c
+@@ -20,11 +20,12 @@
+ 
+ /* Set signal mask for signals with default handling in ATTR to SIGDEFAULT.  */
+ int
+-posix_spawnattr_setsigdefault (posix_spawnattr_t *attr,
+-			       const sigset_t *sigdefault)
++__posix_spawnattr_setsigdefault (posix_spawnattr_t *attr,
++				 const sigset_t *sigdefault)
+ {
+   /* Copy the sigset_t data to the user buffer.  */
+   memcpy (&attr->__sd, sigdefault, sizeof (sigset_t));
+ 
+   return 0;
+ }
++weak_alias (__posix_spawnattr_setsigdefault, posix_spawnattr_setsigdefault)
+diff --git a/posix/spawnattr_setflags.c b/posix/spawnattr_setflags.c
+index cf9a60181dc91ccd..0a42e94770224a94 100644
+--- a/posix/spawnattr_setflags.c
++++ b/posix/spawnattr_setflags.c
+@@ -30,7 +30,7 @@
+ 
+ /* Store flags in the attribute structure.  */
+ int
+-posix_spawnattr_setflags (posix_spawnattr_t *attr, short int flags)
++__posix_spawnattr_setflags (posix_spawnattr_t *attr, short int flags)
+ {
+   /* Check no invalid bits are set.  */
+   if (flags & ~ALL_FLAGS)
+@@ -41,3 +41,4 @@ posix_spawnattr_setflags (posix_spawnattr_t *attr, short int flags)
+ 
+   return 0;
+ }
++weak_alias (__posix_spawnattr_setflags, posix_spawnattr_setflags)
+diff --git a/posix/spawnattr_setsigmask.c b/posix/spawnattr_setsigmask.c
+index 7ae81ad47025db6f..12c0111af441dd13 100644
+--- a/posix/spawnattr_setsigmask.c
++++ b/posix/spawnattr_setsigmask.c
+@@ -20,7 +20,7 @@
+ 
+ /* Set signal mask for the new process in ATTR to SIGMASK.  */
+ int
+-posix_spawnattr_setsigmask (posix_spawnattr_t *attr,
++__posix_spawnattr_setsigmask (posix_spawnattr_t *attr,
+ 			    const sigset_t *sigmask)
+ {
+   /* Copy the sigset_t data to the user buffer.  */
+@@ -28,3 +28,4 @@ posix_spawnattr_setsigmask (posix_spawnattr_t *attr,
+ 
+   return 0;
+ }
++weak_alias (__posix_spawnattr_setsigmask, posix_spawnattr_setsigmask)
diff --git a/SOURCES/glibc-rh2065588-10.patch b/SOURCES/glibc-rh2065588-10.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3016994e020e9a416af457ae532a3284187af177
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-10.patch
@@ -0,0 +1,21 @@
+commit 5fce0e095bc413f908f472074c2235198cd76bf4
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Tue Mar 24 15:36:23 2020 -0300
+
+    support/shell-container.c: Return 127 if execve fails
+    
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+
+diff --git a/support/shell-container.c b/support/shell-container.c
+index e87ac5cf1baa84e5..e9eea64bca7e949d 100644
+--- a/support/shell-container.c
++++ b/support/shell-container.c
+@@ -238,7 +238,7 @@ run_command_array (char **argv)
+ 
+       fprintf (stderr, "sh: execing %s failed: %s",
+ 	       argv[0], strerror (errno));
+-      exit (1);
++      exit (127);
+     }
+ 
+   waitpid (pid, &status, 0);
diff --git a/SOURCES/glibc-rh2065588-11.patch b/SOURCES/glibc-rh2065588-11.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a1ef4a70c21cad90bfda891bfd647d137ddd8469
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-11.patch
@@ -0,0 +1,39 @@
+commit 5a5a3a3234bc220a5192d620e0cbc5360da46f14
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Tue Mar 24 15:40:36 2020 -0300
+
+    support/shell-container.c: Add builtin exit
+    
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+
+diff --git a/support/shell-container.c b/support/shell-container.c
+index e9eea64bca7e949d..aeaf6d2733abce61 100644
+--- a/support/shell-container.c
++++ b/support/shell-container.c
+@@ -135,6 +135,18 @@ copy_func (char **argv)
+ 
+ }
+ 
++/* Emulate the 'exit' builtin.  The exit value is optional.  */
++static int
++exit_func (char **argv)
++{
++  int exit_val = 0;
++
++  if (argv[0] != 0)
++    exit_val = atoi (argv[0]) & 0xff;
++  exit (exit_val);
++  return 0;
++}
++
+ /* This is a list of all the built-in commands we understand.  */
+ static struct {
+   const char *name;
+@@ -143,6 +155,7 @@ static struct {
+   { "true", true_func },
+   { "echo", echo_func },
+   { "cp", copy_func },
++  { "exit", exit_func },
+   { NULL, NULL }
+ };
+ 
diff --git a/SOURCES/glibc-rh2065588-12.patch b/SOURCES/glibc-rh2065588-12.patch
new file mode 100644
index 0000000000000000000000000000000000000000..7f4223f3e8c1f3afbdb21d864ce5e6f50422b2c8
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-12.patch
@@ -0,0 +1,60 @@
+commit 1c17100c43c0913ec94f3bcc966bf3792236c690
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Tue Mar 24 15:47:13 2020 -0300
+
+    support/shell-container.c: Add builtin kill
+    
+    No options supported.
+    
+    Reviewed-by: DJ Delorie <dj@redhat.com>
+
+diff --git a/support/shell-container.c b/support/shell-container.c
+index aeaf6d2733abce61..3869e14683fb74dd 100644
+--- a/support/shell-container.c
++++ b/support/shell-container.c
+@@ -147,6 +147,25 @@ exit_func (char **argv)
+   return 0;
+ }
+ 
++/* Emulate the "/bin/kill" command.  Options are ignored.  */
++static int
++kill_func (char **argv)
++{
++  int signum = SIGTERM;
++  int i;
++
++  for (i = 0; argv[i]; i++)
++    {
++      pid_t pid;
++      if (strcmp (argv[i], "$$") == 0)
++	pid = getpid ();
++      else
++	pid = atoi (argv[i]);
++      kill (pid, signum);
++    }
++  return 0;
++}
++
+ /* This is a list of all the built-in commands we understand.  */
+ static struct {
+   const char *name;
+@@ -156,6 +175,7 @@ static struct {
+   { "echo", echo_func },
+   { "cp", copy_func },
+   { "exit", exit_func },
++  { "kill", kill_func },
+   { NULL, NULL }
+ };
+ 
+@@ -264,6 +284,11 @@ run_command_array (char **argv)
+       if (rv)
+ 	exit (rv);
+     }
++  else if (WIFSIGNALED (status))
++    {
++      int sig = WTERMSIG (status);
++      raise (sig);
++    }
+   else
+     exit (1);
+ }
diff --git a/SOURCES/glibc-rh2065588-13.patch b/SOURCES/glibc-rh2065588-13.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e4e9d70dd95591f8f127d775b12d968f98e5d0fe
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-13.patch
@@ -0,0 +1,66 @@
+commit 75fe6d1a1620d84e0e487868feba9b2c0f109610
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Wed May 12 10:13:41 2021 +0530
+
+    support: Close fds in copy_func
+    
+    copy_func may leave file descriptors open on error, so close them on
+    function exit.
+
+diff --git a/support/shell-container.c b/support/shell-container.c
+index 3869e14683fb74dd..f0a9814ae230d167 100644
+--- a/support/shell-container.c
++++ b/support/shell-container.c
+@@ -93,8 +93,9 @@ copy_func (char **argv)
+ {
+   char *sname = argv[0];
+   char *dname = argv[1];
+-  int sfd, dfd;
++  int sfd = -1, dfd = -1;
+   struct stat st;
++  int ret = 1;
+ 
+   sfd = open (sname, O_RDONLY);
+   if (sfd < 0)
+@@ -108,7 +109,7 @@ copy_func (char **argv)
+     {
+       fprintf (stderr, "cp: unable to fstat %s: %s\n",
+ 	       sname, strerror (errno));
+-      return 1;
++      goto out;
+     }
+ 
+   dfd = open (dname, O_WRONLY | O_TRUNC | O_CREAT, 0600);
+@@ -116,22 +117,26 @@ copy_func (char **argv)
+     {
+       fprintf (stderr, "cp: unable to open %s for writing: %s\n",
+ 	       dname, strerror (errno));
+-      return 1;
++      goto out;
+     }
+ 
+   if (support_copy_file_range (sfd, 0, dfd, 0, st.st_size, 0) != st.st_size)
+     {
+       fprintf (stderr, "cp: cannot copy file %s to %s: %s\n",
+ 	       sname, dname, strerror (errno));
+-      return 1;
++      goto out;
+     }
+ 
+-  close (sfd);
+-  close (dfd);
+-
++  ret = 0;
+   chmod (dname, st.st_mode & 0777);
+ 
+-  return 0;
++out:
++  if (sfd >= 0)
++    close (sfd);
++  if (dfd >= 0)
++    close (dfd);
++
++  return ret;
+ 
+ }
+ 
diff --git a/SOURCES/glibc-rh2065588-2.patch b/SOURCES/glibc-rh2065588-2.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c671cf9399da1850fe7558085780bfcf371dec9a
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-2.patch
@@ -0,0 +1,231 @@
+commit 14d0e87d9b8caaa2eca7ca81f1189596671fe4fb
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Wed Sep 12 10:32:05 2018 -0300
+
+    posix: Use posix_spawn on popen
+    
+    This patch uses posix_spawn on popen instead of fork and execl.  On Linux
+    this has the advantage of much lower memory consumption (usually 32 Kb
+    minimum for the mmap stack area).
+    
+    Two issues are also fixed with this change:
+    
+      * BZ#17490: although POSIX pthread_atfork description only list 'fork'
+        as the function that should execute the atfork handlers, popen
+        description states that:
+    
+          '[...] shall be *as if* a child process were created within the popen()
+           call using the fork() function [...]'
+    
+        Other libc/system seems to follow the idea atfork handlers should not be
+        executed for popen:
+    
+        libc/system | run atfork handles   | notes
+        ------------|----------------------|---------------------------------------
+        Freebsd     |        no            | uses vfork
+        Solaris 11  |        no            |
+        MacOSX 11   |        no            | implemented through posix_spawn syscall
+        ------------|----------------------|----------------------------------------
+    
+        Similar to posix_spawn and system, popen idea is to spawn a different
+        binary so all the POSIX rationale to run the atfork handlers to avoid
+        internal process inconsistency is not really required and in some cases
+        might be unsafe.
+    
+      * BZ#22834: the described scenario, where the forked process might access
+        invalid memory due an inconsistent state in multithreaded environment,
+        should not happen because posix_spawn does not access the affected
+        data structure (proc_file_chain).
+    
+    Checked on x86_64-linux-gnu and i686-linux-gnu.
+    
+            [BZ #22834]
+            [BZ #17490]
+            * NEWS: Add new semantic for atfork with popen and system.
+            * libio/iopopen.c (_IO_new_proc_open): use posix_spawn instead of
+            fork and execl.
+
+diff --git a/libio/iopopen.c b/libio/iopopen.c
+index 2eff45b4c80b5cd6..c768295180fdf809 100644
+--- a/libio/iopopen.c
++++ b/libio/iopopen.c
+@@ -34,7 +34,8 @@
+ #include <not-cancel.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
+-#include <kernel-features.h>
++#include <spawn.h>
++#include <paths.h>
+ 
+ struct _IO_proc_file
+ {
+@@ -59,13 +60,60 @@ unlock (void *not_used)
+ }
+ #endif
+ 
++/* POSIX states popen shall ensure that any streams from previous popen()
++   calls that remain open in the parent process should be closed in the new
++   child process.
++   To avoid a race-condition between checking which file descriptors need to
++   be close (by transversing the proc_file_chain list) and the insertion of a
++   new one after a successful posix_spawn this function should be called
++   with proc_file_chain_lock acquired.  */
++static bool
++spawn_process (posix_spawn_file_actions_t *fa, FILE *fp, const char *command,
++	       int do_cloexec, int pipe_fds[2], int parent_end, int child_end,
++	       int child_pipe_fd)
++{
++
++  for (struct _IO_proc_file *p = proc_file_chain; p; p = p->next)
++    {
++      int fd = _IO_fileno ((FILE *) p);
++
++      /* If any stream from previous popen() calls has fileno
++	 child_pipe_fd, it has been already closed by the adddup2 action
++	 above.  */
++      if (fd != child_pipe_fd
++	  && __posix_spawn_file_actions_addclose (fa, fd) != 0)
++	return false;
++    }
++
++  if (__posix_spawn (&((_IO_proc_file *) fp)->pid, _PATH_BSHELL, fa, 0,
++		     (char *const[]){ (char*) "sh", (char*) "-c",
++		     (char *) command, NULL }, __environ) != 0)
++    return false;
++
++  __close_nocancel (pipe_fds[child_end]);
++
++  if (!do_cloexec)
++    /* Undo the effects of the pipe2 call which set the
++       close-on-exec flag.  */
++    __fcntl (pipe_fds[parent_end], F_SETFD, 0);
++
++  _IO_fileno (fp) = pipe_fds[parent_end];
++
++  ((_IO_proc_file *) fp)->next = proc_file_chain;
++  proc_file_chain = (_IO_proc_file *) fp;
++
++  return true;
++}
++
+ FILE *
+ _IO_new_proc_open (FILE *fp, const char *command, const char *mode)
+ {
+   int read_or_write;
++  /* These are indexes for pipe_fds.  */
+   int parent_end, child_end;
+   int pipe_fds[2];
+-  pid_t child_pid;
++  int child_pipe_fd;
++  bool spawn_ok;
+ 
+   int do_read = 0;
+   int do_write = 0;
+@@ -108,72 +156,62 @@ _IO_new_proc_open (FILE *fp, const char *command, const char *mode)
+ 
+   if (do_read)
+     {
+-      parent_end = pipe_fds[0];
+-      child_end = pipe_fds[1];
++      parent_end = 0;
++      child_end = 1;
+       read_or_write = _IO_NO_WRITES;
++      child_pipe_fd = 1;
+     }
+   else
+     {
+-      parent_end = pipe_fds[1];
+-      child_end = pipe_fds[0];
++      parent_end = 1;
++      child_end = 0;
+       read_or_write = _IO_NO_READS;
++      child_pipe_fd = 0;
+     }
+ 
+-  ((_IO_proc_file *) fp)->pid = child_pid = __fork ();
+-  if (child_pid == 0)
+-    {
+-      int child_std_end = do_read ? 1 : 0;
+-      struct _IO_proc_file *p;
+-
+-      if (child_end != child_std_end)
+-	__dup2 (child_end, child_std_end);
+-      else
+-	/* The descriptor is already the one we will use.  But it must
+-	   not be marked close-on-exec.  Undo the effects.  */
+-	__fcntl (child_end, F_SETFD, 0);
+-      /* POSIX.2:  "popen() shall ensure that any streams from previous
+-         popen() calls that remain open in the parent process are closed
+-	 in the new child process." */
+-      for (p = proc_file_chain; p; p = p->next)
+-	{
+-	  int fd = _IO_fileno ((FILE *) p);
++  posix_spawn_file_actions_t fa;
++  /* posix_spawn_file_actions_init does not fail.  */
++  __posix_spawn_file_actions_init (&fa);
+ 
+-	  /* If any stream from previous popen() calls has fileno
+-	     child_std_end, it has been already closed by the dup2 syscall
+-	     above.  */
+-	  if (fd != child_std_end)
+-	    __close_nocancel (fd);
+-	}
+-
+-      execl ("/bin/sh", "sh", "-c", command, (char *) 0);
+-      _exit (127);
+-    }
+-  __close_nocancel (child_end);
+-  if (child_pid < 0)
++  /* The descriptor is already the one the child will use.  In this case
++     it must be moved to another one otherwise, there is no safe way to
++     remove the close-on-exec flag in the child without creating a FD leak
++     race in the parent.  */
++  if (pipe_fds[child_end] == child_pipe_fd)
+     {
+-      __close_nocancel (parent_end);
+-      return NULL;
++      int tmp = __fcntl (child_pipe_fd, F_DUPFD_CLOEXEC, 0);
++      if (tmp < 0)
++	goto spawn_failure;
++      __close_nocancel (pipe_fds[child_end]);
++      pipe_fds[child_end] = tmp;
+     }
+ 
+-  if (!do_cloexec)
+-    /* Undo the effects of the pipe2 call which set the
+-       close-on-exec flag.  */
+-    __fcntl (parent_end, F_SETFD, 0);
++  if (__posix_spawn_file_actions_adddup2 (&fa, pipe_fds[child_end],
++      child_pipe_fd) != 0)
++    goto spawn_failure;
+ 
+-  _IO_fileno (fp) = parent_end;
+-
+-  /* Link into proc_file_chain. */
+ #ifdef _IO_MTSAFE_IO
+   _IO_cleanup_region_start_noarg (unlock);
+   _IO_lock_lock (proc_file_chain_lock);
+ #endif
+-  ((_IO_proc_file *) fp)->next = proc_file_chain;
+-  proc_file_chain = (_IO_proc_file *) fp;
++  spawn_ok = spawn_process (&fa, fp, command, do_cloexec, pipe_fds,
++			    parent_end, child_end, child_pipe_fd);
+ #ifdef _IO_MTSAFE_IO
+   _IO_lock_unlock (proc_file_chain_lock);
+   _IO_cleanup_region_end (0);
+ #endif
+ 
++  __posix_spawn_file_actions_destroy (&fa);
++
++  if (!spawn_ok)
++    {
++    spawn_failure:
++      __close_nocancel (pipe_fds[child_end]);
++      __close_nocancel (pipe_fds[parent_end]);
++      __set_errno (ENOMEM);
++      return NULL;
++    }
++
+   _IO_mask_flags (fp, read_or_write, _IO_NO_READS|_IO_NO_WRITES);
+   return fp;
+ }
diff --git a/SOURCES/glibc-rh2065588-3.patch b/SOURCES/glibc-rh2065588-3.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d168d882b6d5210e76a09a74f35d2228c4ddc0b8
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-3.patch
@@ -0,0 +1,527 @@
+commit 5fb7fc96350575c9adb1316833e48ca11553be49
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Wed Oct 24 16:29:38 2018 -0300
+
+    posix: Use posix_spawn on system
+    
+    This patch uses posix_spawn on system implementation.  On Linux this has
+    the advantage of much lower memory consumption (usually 32 Kb minimum for
+    the mmap stack area).
+    
+    Although POSIX does not require, glibc system implementation aims to be
+    thread and cancellation safe.  The cancellation code is moved to generic
+    implementation and enabled iff SIGCANCEL is defined (similar on how the
+    cancellation handler is enabled on nptl-init.c).
+    
+    Checked on x86_64-linux-gnu, i686-linux-gnu, aarch64-linux-gnu,
+    arm-linux-gnueabihf, and powerpc64le-linux-gnu.
+    
+            * sysdeps/unix/sysv/linux/spawni.c (__spawni_child): Use
+            __sigismember instead of sigismember.
+            * sysdeps/posix/system.c [SIGCANCEL] (cancel_handler_args,
+            cancel_handler): New definitions.
+            (CLEANUP_HANDLER, CLEANUP_RESET): Likewise.
+            (DO_LOCK, DO_UNLOCK, INIT_LOCK, ADD_REF, SUB_REF): Remove.
+            (do_system): Use posix_spawn instead of fork and execl and remove
+            reentracy code.
+            * sysdeps/generic/not-errno.h (__kill_noerrno): New prototype.
+            * sysdeps/unix/sysv/linux/not-errno.h (__kill_noerrno): Likewise.
+            * sysdeps/unix/sysv/linux/ia64/system.c: Remove file.
+            * sysdeps/unix/sysv/linux/s390/system.c: Likewise.
+            * sysdeps/unix/sysv/linux/sparc/system.c: Likewise.
+            * sysdeps/unix/sysv/linux/system.c: Likewise.
+
+diff --git a/sysdeps/generic/not-errno.h b/sysdeps/generic/not-errno.h
+index 93617a3266fd4aad..0fd66b5c5ed82315 100644
+--- a/sysdeps/generic/not-errno.h
++++ b/sysdeps/generic/not-errno.h
+@@ -17,3 +17,5 @@
+    <http://www.gnu.org/licenses/>.  */
+ 
+ extern __typeof (__access) __access_noerrno attribute_hidden;
++
++extern __typeof (__kill) __kill_noerrno attribute_hidden;
+diff --git a/sysdeps/posix/system.c b/sysdeps/posix/system.c
+index d7594436ed59906f..8a51a6b9919ec39b 100644
+--- a/sysdeps/posix/system.c
++++ b/sysdeps/posix/system.c
+@@ -17,20 +17,36 @@
+ 
+ #include <errno.h>
+ #include <signal.h>
+-#include <stddef.h>
+ #include <stdlib.h>
+ #include <unistd.h>
++#include <sigsetops.h>
++#include <spawn.h>
++#include <pthread.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
+-#include <libc-lock.h>
+-#include <sysdep-cancel.h>
+-#include <sigsetops.h>
++#include <stdio.h>
+ 
++#include <libc-lock.h>
++#include <not-errno.h>
++#include <not-cancel.h>
++#include <internal-signals.h>
+ 
+ #define	SHELL_PATH	"/bin/sh"	/* Path of the shell.  */
+ #define	SHELL_NAME	"sh"		/* Name to give it.  */
+ 
+ 
++/* This system implementation aims to be thread-safe, which requires to
++   restore the signal dispositions for SIGINT and SIGQUIT correctly and to
++   deal with cancellation by terminating the child process.
++
++   The signal disposition restoration on the single-thread case is
++   straighfoward.  For multithreaded case, a reference-counter with a lock
++   is used, so the first thread will set the SIGINT/SIGQUIT dispositions and
++   last thread will restore them.
++
++   Cancellation handling is done with thread cancellation clean-up handlers
++   on waitpid call.  */
++
+ #ifdef _LIBC_REENTRANT
+ static struct sigaction intr, quit;
+ static int sa_refcntr;
+@@ -50,17 +66,45 @@ __libc_lock_define_initialized (static, lock);
+ #endif
+ 
+ 
++#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
++struct cancel_handler_args
++{
++  struct sigaction *quit;
++  struct sigaction *intr;
++  pid_t pid;
++};
++
++static void
++cancel_handler (void *arg)
++{
++  struct cancel_handler_args *args = (struct cancel_handler_args *) (arg);
++
++  __kill_noerrno (args->pid, SIGKILL);
++
++  TEMP_FAILURE_RETRY (__waitpid_nocancel (args->pid, NULL, 0));
++
++  DO_LOCK ();
++  if (SUB_REF () == 0)
++    {
++      __sigaction (SIGQUIT, args->quit, NULL);
++      __sigaction (SIGINT, args->intr, NULL);
++    }
++  DO_UNLOCK ();
++}
++#endif
++
+ /* Execute LINE as a shell command, returning its status.  */
+ static int
+ do_system (const char *line)
+ {
+-  int status, save;
++  int status;
+   pid_t pid;
+   struct sigaction sa;
+ #ifndef _LIBC_REENTRANT
+   struct sigaction intr, quit;
+ #endif
+   sigset_t omask;
++  sigset_t reset;
+ 
+   sa.sa_handler = SIG_IGN;
+   sa.sa_flags = 0;
+@@ -69,105 +113,72 @@ do_system (const char *line)
+   DO_LOCK ();
+   if (ADD_REF () == 0)
+     {
+-      if (__sigaction (SIGINT, &sa, &intr) < 0)
+-	{
+-	  (void) SUB_REF ();
+-	  goto out;
+-	}
+-      if (__sigaction (SIGQUIT, &sa, &quit) < 0)
+-	{
+-	  save = errno;
+-	  (void) SUB_REF ();
+-	  goto out_restore_sigint;
+-	}
++      /* sigaction can not fail with SIGINT/SIGQUIT used with SIG_IGN.  */
++      __sigaction (SIGINT, &sa, &intr);
++      __sigaction (SIGQUIT, &sa, &quit);
+     }
+   DO_UNLOCK ();
+ 
+-  /* We reuse the bitmap in the 'sa' structure.  */
+   __sigaddset (&sa.sa_mask, SIGCHLD);
+-  save = errno;
+-  if (__sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0)
++  /* sigprocmask can not fail with SIG_BLOCK used with valid input
++     arguments.  */
++  __sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask);
++
++  __sigemptyset (&reset);
++  if (intr.sa_handler != SIG_IGN)
++    __sigaddset(&reset, SIGINT);
++  if (quit.sa_handler != SIG_IGN)
++    __sigaddset(&reset, SIGQUIT);
++
++  posix_spawnattr_t spawn_attr;
++  /* None of the posix_spawnattr_* function returns an error, including
++     posix_spawnattr_setflags for the follow specific usage (using valid
++     flags).  */
++  __posix_spawnattr_init (&spawn_attr);
++  __posix_spawnattr_setsigmask (&spawn_attr, &omask);
++  __posix_spawnattr_setsigdefault (&spawn_attr, &reset);
++  __posix_spawnattr_setflags (&spawn_attr,
++			      POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK);
++
++  status = __posix_spawn (&pid, SHELL_PATH, 0, &spawn_attr,
++			  (char *const[]){ (char*) SHELL_NAME,
++					   (char*) "-c",
++					   (char *) line, NULL },
++			  __environ);
++  __posix_spawnattr_destroy (&spawn_attr);
++
++  if (status == 0)
+     {
+-#ifndef _LIBC
+-      if (errno == ENOSYS)
+-	__set_errno (save);
+-      else
+-#endif
+-	{
+-	  DO_LOCK ();
+-	  if (SUB_REF () == 0)
+-	    {
+-	      save = errno;
+-	      (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
+-	    out_restore_sigint:
+-	      (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
+-	      __set_errno (save);
+-	    }
+-	out:
+-	  DO_UNLOCK ();
+-	  return -1;
+-	}
+-    }
+-
+-#ifdef CLEANUP_HANDLER
+-  CLEANUP_HANDLER;
+-#endif
+-
+-#ifdef FORK
+-  pid = FORK ();
+-#else
+-  pid = __fork ();
++      /* Cancellation results in cleanup handlers running as exceptions in
++	 the block where they were installed, so it is safe to reference
++	 stack variable allocate in the broader scope.  */
++#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
++      struct cancel_handler_args cancel_args =
++      {
++	.quit = &quit,
++	.intr = &intr,
++	.pid = pid
++      };
++      __libc_cleanup_region_start (1, cancel_handler, &cancel_args);
+ #endif
+-  if (pid == (pid_t) 0)
+-    {
+-      /* Child side.  */
+-      const char *new_argv[4];
+-      new_argv[0] = SHELL_NAME;
+-      new_argv[1] = "-c";
+-      new_argv[2] = line;
+-      new_argv[3] = NULL;
+-
+-      /* Restore the signals.  */
+-      (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
+-      (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
+-      (void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);
+-      INIT_LOCK ();
+-
+-      /* Exec the shell.  */
+-      (void) __execve (SHELL_PATH, (char *const *) new_argv, __environ);
+-      _exit (127);
+-    }
+-  else if (pid < (pid_t) 0)
+-    /* The fork failed.  */
+-    status = -1;
+-  else
+-    /* Parent side.  */
+-    {
+       /* Note the system() is a cancellation point.  But since we call
+ 	 waitpid() which itself is a cancellation point we do not
+ 	 have to do anything here.  */
+       if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)
+ 	status = -1;
+-    }
+-
+-#ifdef CLEANUP_HANDLER
+-  CLEANUP_RESET;
++#if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
++      __libc_cleanup_region_end (0);
+ #endif
++    }
+ 
+-  save = errno;
+   DO_LOCK ();
+-  if ((SUB_REF () == 0
+-       && (__sigaction (SIGINT, &intr, (struct sigaction *) NULL)
+-	   | __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0)
+-      || __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0)
++  if (SUB_REF () == 0)
+     {
+-#ifndef _LIBC
+-      /* glibc cannot be used on systems without waitpid.  */
+-      if (errno == ENOSYS)
+-	__set_errno (save);
+-      else
+-#endif
+-	status = -1;
++      /* sigaction can not fail with SIGINT/SIGQUIT used with old
++	 disposition.  Same applies for sigprocmask.  */
++      __sigaction (SIGINT, &intr, NULL);
++      __sigaction (SIGQUIT, &quit, NULL);
++      __sigprocmask (SIG_SETMASK, &omask, NULL);
+     }
+   DO_UNLOCK ();
+ 
+diff --git a/sysdeps/unix/sysv/linux/ia64/system.c b/sysdeps/unix/sysv/linux/ia64/system.c
+deleted file mode 100644
+index d09fefefe64753ab..0000000000000000
+--- a/sysdeps/unix/sysv/linux/ia64/system.c
++++ /dev/null
+@@ -1,30 +0,0 @@
+-/* Copyright (C) 2002-2018 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
+-   <http://www.gnu.org/licenses/>.  */
+-
+-/* We have to and actually can handle cancelable system().  The big
+-   problem: we have to kill the child process if necessary.  To do
+-   this a cleanup handler has to be registered and is has to be able
+-   to find the PID of the child.  The main problem is to reliable have
+-   the PID when needed.  It is not necessary for the parent thread to
+-   return.  It might still be in the kernel when the cancellation
+-   request comes.  Therefore we have to use the clone() calls ability
+-   to have the kernel write the PID into the user-level variable.  */
+-#define FORK() \
+-  INLINE_SYSCALL (clone2, 6, CLONE_PARENT_SETTID | SIGCHLD, NULL, 0, \
+-		  &pid, NULL, NULL)
+-
+-#include <sysdeps/unix/sysv/linux/system.c>
+diff --git a/sysdeps/unix/sysv/linux/not-errno.h b/sysdeps/unix/sysv/linux/not-errno.h
+index 106ba5c72e3d7dda..b2f72cfb3d412c56 100644
+--- a/sysdeps/unix/sysv/linux/not-errno.h
++++ b/sysdeps/unix/sysv/linux/not-errno.h
+@@ -16,6 +16,9 @@
+    License along with the GNU C Library; if not, see
+    <http://www.gnu.org/licenses/>.  */
+ 
++#include <sysdep.h>
++#include <fcntl.h>
++
+ /* This function is used on maybe_enable_malloc_check (elf/dl-tunables.c)
+    and to avoid having to build/use multiple versions if stack protection
+    in enabled it is defined as inline.  */
+@@ -33,3 +36,14 @@ __access_noerrno (const char *pathname, int mode)
+     return INTERNAL_SYSCALL_ERRNO (res, err);
+   return 0;
+ }
++
++static inline int
++__kill_noerrno (pid_t pid, int sig)
++{
++  int res;
++  INTERNAL_SYSCALL_DECL (err);
++  res = INTERNAL_SYSCALL_CALL (kill, err, pid, sig);
++  if (INTERNAL_SYSCALL_ERROR_P (res, err))
++    return INTERNAL_SYSCALL_ERRNO (res, err);
++  return 0;
++}
+diff --git a/sysdeps/unix/sysv/linux/s390/system.c b/sysdeps/unix/sysv/linux/s390/system.c
+deleted file mode 100644
+index d8ef46133419dd89..0000000000000000
+--- a/sysdeps/unix/sysv/linux/s390/system.c
++++ /dev/null
+@@ -1,29 +0,0 @@
+-/* Copyright (C) 2003-2018 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
+-   <http://www.gnu.org/licenses/>.  */
+-
+-/* We have to and actually can handle cancelable system().  The big
+-   problem: we have to kill the child process if necessary.  To do
+-   this a cleanup handler has to be registered and is has to be able
+-   to find the PID of the child.  The main problem is to reliable have
+-   the PID when needed.  It is not necessary for the parent thread to
+-   return.  It might still be in the kernel when the cancellation
+-   request comes.  Therefore we have to use the clone() calls ability
+-   to have the kernel write the PID into the user-level variable.  */
+-#define FORK() \
+-  INLINE_SYSCALL (clone, 3, 0, CLONE_PARENT_SETTID | SIGCHLD, &pid)
+-
+-#include "../system.c"
+diff --git a/sysdeps/unix/sysv/linux/sparc/system.c b/sysdeps/unix/sysv/linux/sparc/system.c
+deleted file mode 100644
+index 1f65c83399f920d6..0000000000000000
+--- a/sysdeps/unix/sysv/linux/sparc/system.c
++++ /dev/null
+@@ -1,29 +0,0 @@
+-/* Copyright (C) 2003-2018 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
+-   <http://www.gnu.org/licenses/>.  */
+-
+-/* We have to and actually can handle cancelable system().  The big
+-   problem: we have to kill the child process if necessary.  To do
+-   this a cleanup handler has to be registered and is has to be able
+-   to find the PID of the child.  The main problem is to reliable have
+-   the PID when needed.  It is not necessary for the parent thread to
+-   return.  It might still be in the kernel when the cancellation
+-   request comes.  Therefore we have to use the clone() calls ability
+-   to have the kernel write the PID into the user-level variable.  */
+-#define FORK() \
+-  INLINE_CLONE_SYSCALL (CLONE_PARENT_SETTID | SIGCHLD, 0, &pid, NULL, NULL)
+-
+-#include "../system.c"
+diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c
+index 85239cedbf2a5ab5..6a8bd2ed2e1c29b7 100644
+--- a/sysdeps/unix/sysv/linux/spawni.c
++++ b/sysdeps/unix/sysv/linux/spawni.c
+@@ -138,11 +138,11 @@ __spawni_child (void *arguments)
+   for (int sig = 1; sig < _NSIG; ++sig)
+     {
+       if ((attr->__flags & POSIX_SPAWN_SETSIGDEF)
+-	  && sigismember (&attr->__sd, sig))
++	  && __sigismember (&attr->__sd, sig))
+ 	{
+ 	  sa.sa_handler = SIG_DFL;
+ 	}
+-      else if (sigismember (&hset, sig))
++      else if (__sigismember (&hset, sig))
+ 	{
+ 	  if (__is_internal_signal (sig))
+ 	    sa.sa_handler = SIG_IGN;
+diff --git a/sysdeps/unix/sysv/linux/system.c b/sysdeps/unix/sysv/linux/system.c
+deleted file mode 100644
+index 7cc68a1528ee8f99..0000000000000000
+--- a/sysdeps/unix/sysv/linux/system.c
++++ /dev/null
+@@ -1,76 +0,0 @@
+-/* Copyright (C) 2002-2018 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
+-   <http://www.gnu.org/licenses/>.  */
+-
+-#include <sched.h>
+-#include <signal.h>
+-#include <string.h>	/* For the real memset prototype.  */
+-#include <sysdep.h>
+-#include <unistd.h>
+-#include <sys/wait.h>
+-#include <libc-lock.h>
+-
+-/* We have to and actually can handle cancelable system().  The big
+-   problem: we have to kill the child process if necessary.  To do
+-   this a cleanup handler has to be registered and is has to be able
+-   to find the PID of the child.  The main problem is to reliable have
+-   the PID when needed.  It is not necessary for the parent thread to
+-   return.  It might still be in the kernel when the cancellation
+-   request comes.  Therefore we have to use the clone() calls ability
+-   to have the kernel write the PID into the user-level variable.  */
+-#ifndef FORK
+-# define FORK() \
+-  INLINE_SYSCALL (clone, 3, CLONE_PARENT_SETTID | SIGCHLD, 0, &pid)
+-#endif
+-
+-#ifdef _LIBC_REENTRANT
+-static void cancel_handler (void *arg);
+-
+-# define CLEANUP_HANDLER \
+-  __libc_cleanup_region_start (1, cancel_handler, &pid)
+-
+-# define CLEANUP_RESET \
+-  __libc_cleanup_region_end (0)
+-#endif
+-
+-
+-/* Linux has waitpid(), so override the generic unix version.  */
+-#include <sysdeps/posix/system.c>
+-
+-
+-#ifdef _LIBC_REENTRANT
+-/* The cancellation handler.  */
+-static void
+-cancel_handler (void *arg)
+-{
+-  pid_t child = *(pid_t *) arg;
+-
+-  INTERNAL_SYSCALL_DECL (err);
+-  INTERNAL_SYSCALL (kill, err, 2, child, SIGKILL);
+-
+-  TEMP_FAILURE_RETRY (__waitpid (child, NULL, 0));
+-
+-  DO_LOCK ();
+-
+-  if (SUB_REF () == 0)
+-    {
+-      (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
+-      (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
+-    }
+-
+-  DO_UNLOCK ();
+-}
+-#endif
diff --git a/SOURCES/glibc-rh2065588-4.patch b/SOURCES/glibc-rh2065588-4.patch
new file mode 100644
index 0000000000000000000000000000000000000000..33a0cce6651276f35ea8aa29440e8d4e5d38f5b3
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-4.patch
@@ -0,0 +1,194 @@
+commit f09542c584b121da0322fde4b55306d512b85d93
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Mon Mar 23 15:23:20 2020 -0300
+
+    posix: Fix system error return value [BZ #25715]
+    
+    It fixes 5fb7fc9635 when posix_spawn fails.
+    
+    Checked on x86_64-linux-gnu and i686-linux-gnu.
+    
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+
+diff --git a/stdlib/tst-system.c b/stdlib/tst-system.c
+index d14839f3ec3a7bad..b61bd347df7ec46a 100644
+--- a/stdlib/tst-system.c
++++ b/stdlib/tst-system.c
+@@ -17,14 +17,128 @@
+    <http://www.gnu.org/licenses/>.  */
+ 
+ #include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <signal.h>
++#include <paths.h>
+ 
++#include <support/capture_subprocess.h>
++#include <support/check.h>
++#include <support/temp_file.h>
++#include <support/support.h>
++
++static char *tmpdir;
++static long int namemax;
++
++static void
++do_prepare (int argc, char *argv[])
++{
++  tmpdir = support_create_temp_directory ("tst-system-");
++  /* Include the last '/0'.  */
++  namemax = pathconf (tmpdir, _PC_NAME_MAX) + 1;
++  TEST_VERIFY_EXIT (namemax != -1);
++}
++#define PREPARE do_prepare
++
++struct args
++{
++  const char *command;
++  int exit_status;
++  int term_sig;
++  const char *path;
++};
++
++static void
++call_system (void *closure)
++{
++  struct args *args = (struct args *) closure;
++  int ret;
++
++  if (args->path != NULL)
++    TEST_COMPARE (setenv ("PATH", args->path, 1), 0);
++  ret = system (args->command);
++  if (args->term_sig == 0)
++    {
++      /* Expect regular termination.  */
++      TEST_VERIFY (WIFEXITED (ret) != 0);
++      TEST_COMPARE (WEXITSTATUS (ret), args->exit_status);
++    }
++  else
++    {
++      /* status_or_signal < 0.  Expect termination by signal.  */
++      TEST_VERIFY (WIFSIGNALED (ret) != 0);
++      TEST_COMPARE (WTERMSIG (ret), args->term_sig);
++    }
++}
+ 
+ static int
+ do_test (void)
+ {
+-  return system (":");
+-}
++  TEST_VERIFY (system (NULL) != 0);
+ 
++  {
++    char cmd[namemax];
++    memset (cmd, 'a', sizeof(cmd));
++    cmd[sizeof(cmd) - 1] = '\0';
++
++    struct support_capture_subprocess result;
++    result = support_capture_subprocess (call_system,
++					 &(struct args) {
++					   cmd, 127, 0, tmpdir
++					 });
++    support_capture_subprocess_check (&result, "system", 0, sc_allow_stderr);
++
++    char *returnerr = xasprintf ("%s: 1: %s: not found\n",
++				 basename(_PATH_BSHELL), cmd);
++    TEST_COMPARE_STRING (result.err.buffer, returnerr);
++    free (returnerr);
++  }
++
++  {
++    char cmd[namemax + 1];
++    memset (cmd, 'a', sizeof(cmd));
++    cmd[sizeof(cmd) - 1] = '\0';
++
++    struct support_capture_subprocess result;
++    result = support_capture_subprocess (call_system,
++					 &(struct args) {
++					   cmd, 127, 0, tmpdir
++					 });
++    support_capture_subprocess_check (&result, "system", 0, sc_allow_stderr);
++
++    char *returnerr = xasprintf ("%s: 1: %s: File name too long\n",
++				 basename(_PATH_BSHELL), cmd);
++    TEST_COMPARE_STRING (result.err.buffer, returnerr);
++    free (returnerr);
++  }
++
++  {
++    struct support_capture_subprocess result;
++    result = support_capture_subprocess (call_system,
++					 &(struct args) {
++					   "kill -USR1 $$", 0, SIGUSR1
++					 });
++    support_capture_subprocess_check (&result, "system", 0, sc_allow_none);
++  }
++
++  {
++    struct support_capture_subprocess result;
++    result = support_capture_subprocess (call_system,
++					 &(struct args) { "echo ...", 0 });
++    support_capture_subprocess_check (&result, "system", 0, sc_allow_stdout);
++    TEST_COMPARE_STRING (result.out.buffer, "...\n");
++  }
++
++  {
++    struct support_capture_subprocess result;
++    result = support_capture_subprocess (call_system,
++					 &(struct args) { "exit 1", 1 });
++    support_capture_subprocess_check (&result, "system", 0, sc_allow_none);
++  }
++
++  TEST_COMPARE (system (":"), 0);
++
++  return 0;
++}
+ 
+-#define TEST_FUNCTION do_test ()
+-#include "../test-skeleton.c"
++#include <support/test-driver.c>
+diff --git a/sysdeps/posix/system.c b/sysdeps/posix/system.c
+index 8a51a6b9919ec39b..7db09a05c3fbca43 100644
+--- a/sysdeps/posix/system.c
++++ b/sysdeps/posix/system.c
+@@ -97,7 +97,8 @@ cancel_handler (void *arg)
+ static int
+ do_system (const char *line)
+ {
+-  int status;
++  int status = -1;
++  int ret;
+   pid_t pid;
+   struct sigaction sa;
+ #ifndef _LIBC_REENTRANT
+@@ -140,14 +141,14 @@ do_system (const char *line)
+   __posix_spawnattr_setflags (&spawn_attr,
+ 			      POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK);
+ 
+-  status = __posix_spawn (&pid, SHELL_PATH, 0, &spawn_attr,
+-			  (char *const[]){ (char*) SHELL_NAME,
+-					   (char*) "-c",
+-					   (char *) line, NULL },
+-			  __environ);
++  ret = __posix_spawn (&pid, SHELL_PATH, 0, &spawn_attr,
++		       (char *const[]){ (char *) SHELL_NAME,
++					(char *) "-c",
++					(char *) line, NULL },
++		       __environ);
+   __posix_spawnattr_destroy (&spawn_attr);
+ 
+-  if (status == 0)
++  if (ret == 0)
+     {
+       /* Cancellation results in cleanup handlers running as exceptions in
+ 	 the block where they were installed, so it is safe to reference
+@@ -182,6 +183,9 @@ do_system (const char *line)
+     }
+   DO_UNLOCK ();
+ 
++  if (ret != 0)
++    __set_errno (ret);
++
+   return status;
+ }
+ 
diff --git a/SOURCES/glibc-rh2065588-5.patch b/SOURCES/glibc-rh2065588-5.patch
new file mode 100644
index 0000000000000000000000000000000000000000..368e7596747171c70c7c4014fdc4a680aac590d8
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-5.patch
@@ -0,0 +1,64 @@
+commit 7a7226543611897103c7483bec160547294dcf0d
+Author: Alexandra Hájková <ahajkova@redhat.com>
+Date:   Sat Dec 26 20:44:34 2020 +0100
+
+     Add xfchmod to libsupport
+
+diff --git a/support/Makefile b/support/Makefile
+index d2b95539403e416c..4875f52495ef292d 100644
+--- a/support/Makefile
++++ b/support/Makefile
+@@ -91,6 +91,7 @@ libsupport-routines = \
+   xdlfcn \
+   xdlmopen \
+   xdup2 \
++  xfchmod \
+   xfclose \
+   xfopen \
+   xfork \
+diff --git a/support/xfchmod.c b/support/xfchmod.c
+new file mode 100644
+index 0000000000000000..4323b9ca8e078c98
+--- /dev/null
++++ b/support/xfchmod.c
+@@ -0,0 +1,28 @@
++/* fchmod with error checking.
++   Copyright (C) 2021 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/>.  */
++
++#include <support/check.h>
++#include <support/xunistd.h>
++#include <sys/stat.h>
++
++void
++xfchmod (int fd, mode_t mode)
++{
++  if (fchmod (fd, mode) != 0)
++    FAIL_EXIT1 ("fchmod (%d, 0%o): %m", fd, mode);
++}
+diff --git a/support/xunistd.h b/support/xunistd.h
+index 74fd2771d12c36fe..ced8cb1dd9ee356c 100644
+--- a/support/xunistd.h
++++ b/support/xunistd.h
+@@ -45,6 +45,7 @@ long long xlseek (int fd, long long offset, int whence);
+ void xftruncate (int fd, long long length);
+ void xsymlink (const char *target, const char *linkpath);
+ void xchdir (const char *path);
++void xfchmod (int fd, mode_t mode);
+ 
+ /* Equivalent of "mkdir -p".  */
+ void xmkdirp (const char *, mode_t);
diff --git a/SOURCES/glibc-rh2065588-6.patch b/SOURCES/glibc-rh2065588-6.patch
new file mode 100644
index 0000000000000000000000000000000000000000..16fdb47559a4f5513d073c423e7fbcbb6dfaea4f
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-6.patch
@@ -0,0 +1,56 @@
+commit 7b9c3260bcca73781dda6bc2ddee84869bedfb8c
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Mon Dec 14 11:42:33 2020 -0300
+
+    support: Add xchmod wrapper
+    
+    Checked on x86_64-linux-gnu.
+
+diff --git a/support/xchmod.c b/support/xchmod.c
+new file mode 100644
+index 0000000000000000..5e403c7cc2705aef
+--- /dev/null
++++ b/support/xchmod.c
+@@ -0,0 +1,30 @@
++/* chmod with error checking.
++   Copyright (C) 2020 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/>.  */
++
++#include <support/xunistd.h>
++#include <support/check.h>
++
++#include <sys/stat.h>
++
++void
++xchmod (const char *pathname, mode_t mode)
++{
++  int r = chmod (pathname, mode);
++  if (r < 0)
++    FAIL_EXIT1 ("chmod (%s, %d): %m", pathname, mode);
++}
+diff --git a/support/xunistd.h b/support/xunistd.h
+index ced8cb1dd9ee356c..e92056c65efe8d6a 100644
+--- a/support/xunistd.h
++++ b/support/xunistd.h
+@@ -46,6 +46,7 @@ void xftruncate (int fd, long long length);
+ void xsymlink (const char *target, const char *linkpath);
+ void xchdir (const char *path);
+ void xfchmod (int fd, mode_t mode);
++void xchmod (const char *pathname, mode_t mode);
+ 
+ /* Equivalent of "mkdir -p".  */
+ void xmkdirp (const char *, mode_t);
diff --git a/SOURCES/glibc-rh2065588-7.patch b/SOURCES/glibc-rh2065588-7.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b16b79e2b869f026203c8479edd9ca0c9b6e3a23
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-7.patch
@@ -0,0 +1,73 @@
+commit 4eda036f5b897fa8bc20ddd2099b5a6ed4239dc9
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Tue Mar 24 15:48:34 2020 -0300
+
+    stdlib: Move tst-system to tests-container
+    
+    Fix some issues with different shell and error messages.
+    
+    Checked on x86_64-linux-gnu and i686-linux-gnu.
+
+diff --git a/stdlib/Makefile b/stdlib/Makefile
+index 01194bbf7cc96851..9d0edcf6a7749b28 100644
+--- a/stdlib/Makefile
++++ b/stdlib/Makefile
+@@ -70,7 +70,7 @@ tests		:= tst-strtol tst-strtod testmb testrand testsort testdiv   \
+ 		   test-canon test-canon2 tst-strtoll tst-environ	    \
+ 		   tst-xpg-basename tst-random tst-random2 tst-bsearch	    \
+ 		   tst-limits tst-rand48 bug-strtod tst-setcontext	    \
+-		   tst-setcontext2 test-a64l tst-qsort tst-system testmb2   \
++		   tst-setcontext2 test-a64l tst-qsort testmb2              \
+ 		   bug-strtod2 tst-atof1 tst-atof2 tst-strtod2		    \
+ 		   tst-rand48-2 tst-makecontext tst-strtod5		    \
+ 		   tst-qsort2 tst-makecontext2 tst-strtod6 tst-unsetenv1    \
+@@ -92,6 +92,7 @@ tests		:= tst-strtol tst-strtod testmb testrand testsort testdiv   \
+ tests-internal	:= tst-strtod1i tst-strtod3 tst-strtod4 tst-strtod5i \
+ 		   tst-tls-atexit tst-tls-atexit-nodelete
+ tests-static	:= tst-secure-getenv
++tests-container := tst-system
+ 
+ ifeq ($(build-hardcoded-path-in-tests),yes)
+ tests += tst-empty-env
+diff --git a/stdlib/tst-system.c b/stdlib/tst-system.c
+index b61bd347df7ec46a..194e09828dd5c206 100644
+--- a/stdlib/tst-system.c
++++ b/stdlib/tst-system.c
+@@ -88,7 +88,8 @@ do_test (void)
+ 					 });
+     support_capture_subprocess_check (&result, "system", 0, sc_allow_stderr);
+ 
+-    char *returnerr = xasprintf ("%s: 1: %s: not found\n",
++    char *returnerr = xasprintf ("%s: execing %s failed: "
++				 "No such file or directory",
+ 				 basename(_PATH_BSHELL), cmd);
+     TEST_COMPARE_STRING (result.err.buffer, returnerr);
+     free (returnerr);
+@@ -106,7 +107,8 @@ do_test (void)
+ 					 });
+     support_capture_subprocess_check (&result, "system", 0, sc_allow_stderr);
+ 
+-    char *returnerr = xasprintf ("%s: 1: %s: File name too long\n",
++    char *returnerr = xasprintf ("%s: execing %s failed: "
++				 "File name too long",
+ 				 basename(_PATH_BSHELL), cmd);
+     TEST_COMPARE_STRING (result.err.buffer, returnerr);
+     free (returnerr);
+@@ -116,7 +118,7 @@ do_test (void)
+     struct support_capture_subprocess result;
+     result = support_capture_subprocess (call_system,
+ 					 &(struct args) {
+-					   "kill -USR1 $$", 0, SIGUSR1
++					   "kill $$", 0, SIGTERM
+ 					 });
+     support_capture_subprocess_check (&result, "system", 0, sc_allow_none);
+   }
+@@ -136,7 +138,7 @@ do_test (void)
+     support_capture_subprocess_check (&result, "system", 0, sc_allow_none);
+   }
+ 
+-  TEST_COMPARE (system (":"), 0);
++  TEST_COMPARE (system (""), 0);
+ 
+   return 0;
+ }
diff --git a/SOURCES/glibc-rh2065588-8.patch b/SOURCES/glibc-rh2065588-8.patch
new file mode 100644
index 0000000000000000000000000000000000000000..102b72ae7c76e9a2ae641a55a3a04c530bc3baa9
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-8.patch
@@ -0,0 +1,74 @@
+commit 42dda89dcb0407f6799dbfd0b9dab1529666ad51
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Fri Dec 11 15:23:05 2020 -0300
+
+    posix: Fix return value of system if shell can not be executed [BZ #27053]
+    
+    POSIX states that system returned code for failure to execute the shell
+    shall be as if the shell had terminated using _exit(127).  This
+    behaviour was removed with 5fb7fc96350575.
+    
+    Checked on x86_64-linux-gnu.
+
+diff --git a/stdlib/tst-system.c b/stdlib/tst-system.c
+index 194e09828dd5c206..8681584f15ef3b47 100644
+--- a/stdlib/tst-system.c
++++ b/stdlib/tst-system.c
+@@ -26,6 +26,7 @@
+ #include <support/check.h>
+ #include <support/temp_file.h>
+ #include <support/support.h>
++#include <support/xunistd.h>
+ 
+ static char *tmpdir;
+ static long int namemax;
+@@ -138,6 +139,22 @@ do_test (void)
+     support_capture_subprocess_check (&result, "system", 0, sc_allow_none);
+   }
+ 
++  {
++    struct stat64 st;
++    xstat (_PATH_BSHELL, &st);
++    mode_t mode = st.st_mode;
++    xchmod (_PATH_BSHELL, mode & ~(S_IXUSR | S_IXGRP | S_IXOTH));
++
++    struct support_capture_subprocess result;
++    result = support_capture_subprocess (call_system,
++					 &(struct args) {
++					   "exit 1", 127, 0
++					 });
++    support_capture_subprocess_check (&result, "system", 0, sc_allow_none);
++
++    xchmod (_PATH_BSHELL, st.st_mode);
++  }
++
+   TEST_COMPARE (system (""), 0);
+ 
+   return 0;
+diff --git a/support/Makefile b/support/Makefile
+index 4875f52495ef292d..09b41b0d57e9239a 100644
+--- a/support/Makefile
++++ b/support/Makefile
+@@ -86,6 +86,7 @@ libsupport-routines = \
+   xchroot \
+   xclone \
+   xclose \
++  xchmod \
+   xconnect \
+   xcopy_file_range \
+   xdlfcn \
+diff --git a/sysdeps/posix/system.c b/sysdeps/posix/system.c
+index 7db09a05c3fbca43..047ded4badfddcab 100644
+--- a/sysdeps/posix/system.c
++++ b/sysdeps/posix/system.c
+@@ -171,6 +171,10 @@ do_system (const char *line)
+       __libc_cleanup_region_end (0);
+ #endif
+     }
++  else
++   /* POSIX states that failure to execute the shell should return
++      as if the shell had terminated using _exit(127).  */
++   status = W_EXITCODE (127, 0);
+ 
+   DO_LOCK ();
+   if (SUB_REF () == 0)
diff --git a/SOURCES/glibc-rh2065588-9.patch b/SOURCES/glibc-rh2065588-9.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6d259d82ac2e7fae22eda91de7c2cc7a08269baa
--- /dev/null
+++ b/SOURCES/glibc-rh2065588-9.patch
@@ -0,0 +1,21 @@
+commit 542160f0b6a7c26758c9575a8876f6624a5dd65f
+Author: Girish Joshi <girish946@gmail.com>
+Date:   Mon Mar 2 15:19:29 2020 -0500
+
+    Fixed typo in run_command_array() in support/shell-container.c
+    
+    https://sourceware.org/bugzilla/show_bug.cgi?id=23991
+
+diff --git a/support/shell-container.c b/support/shell-container.c
+index 9bd90d3f60529079..e87ac5cf1baa84e5 100644
+--- a/support/shell-container.c
++++ b/support/shell-container.c
+@@ -228,7 +228,7 @@ run_command_array (char **argv)
+       if (new_stderr != 2)
+ 	{
+ 	  dup2 (new_stderr, 2);
+-	  close (new_stdout);
++	  close (new_stderr);
+ 	}
+ 
+       if (builtin_func != NULL)
diff --git a/SOURCES/glibc-rh2071745.patch b/SOURCES/glibc-rh2071745.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8fbfffb3390ced9781b6d46c4f31acbe06d9ad9d
--- /dev/null
+++ b/SOURCES/glibc-rh2071745.patch
@@ -0,0 +1,23 @@
+commit 62db87ab24f9ca483f97f5e52ea92445f6a63c6f
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Mon Jan 10 10:17:18 2022 -0300
+
+    timezone: Fix tst-bz28707 Makefile rule
+    
+    The $(testdata)/XT5 rule is ambiguous and it may not be correct
+    evaluated.
+
+diff --git a/timezone/Makefile b/timezone/Makefile
+index ac7f483c130b5b4a..c4a63daadb8d5dc5 100644
+--- a/timezone/Makefile
++++ b/timezone/Makefile
+@@ -123,7 +123,8 @@ $(testdata)/XT%: testdata/XT%
+ 	$(make-target-directory)
+ 	cp $< $@
+ 
+-$(testdata)/XT%: testdata/gen-XT%.sh
++$(testdata)/XT5: testdata/gen-XT5.sh
++	$(make-target-directory)
+ 	$(SHELL) $< > $@.tmp
+ 	mv $@.tmp $@
+ 
diff --git a/SOURCES/glibc-rh2072329.patch b/SOURCES/glibc-rh2072329.patch
new file mode 100644
index 0000000000000000000000000000000000000000..e26331e01328ee14f759a74aa4e84dcdc25f3bf5
--- /dev/null
+++ b/SOURCES/glibc-rh2072329.patch
@@ -0,0 +1,86 @@
+commit 33e03f9cd2be4f2cd62f93fda539cc07d9c8130e
+Author: Joan Bruguera <joanbrugueram@gmail.com>
+Date:   Mon Apr 11 19:49:56 2022 +0200
+
+    misc: Fix rare fortify crash on wchar funcs. [BZ 29030]
+    
+    If `__glibc_objsize (__o) == (size_t) -1` (i.e. `__o` is unknown size), fortify
+    checks should pass, and `__whatever_alias` should be called.
+    
+    Previously, `__glibc_objsize (__o) == (size_t) -1` was explicitly checked, but
+    on commit a643f60c53876b, this was moved into `__glibc_safe_or_unknown_len`.
+    
+    A comment says the -1 case should work as: "The -1 check is redundant because
+    since it implies that __glibc_safe_len_cond is true.". But this fails when:
+    * `__s > 1`
+    * `__osz == -1` (i.e. unknown size at compile time)
+    * `__l` is big enough
+    * `__l * __s <= __osz` can be folded to a constant
+    (I only found this to be true for `mbsrtowcs` and other functions in wchar2.h)
+    
+    In this case `__l * __s <= __osz` is false, and `__whatever_chk_warn` will be
+    called by `__glibc_fortify` or `__glibc_fortify_n` and crash the program.
+    
+    This commit adds the explicit `__osz == -1` check again.
+    moc crashes on startup due to this, see: https://bugs.archlinux.org/task/74041
+    
+    Minimal test case (test.c):
+        #include <wchar.h>
+    
+        int main (void)
+        {
+            const char *hw = "HelloWorld";
+            mbsrtowcs (NULL, &hw, (size_t)-1, NULL);
+            return 0;
+        }
+    
+    Build with:
+        gcc -O2 -Wp,-D_FORTIFY_SOURCE=2 test.c -o test && ./test
+    
+    Output:
+        *** buffer overflow detected ***: terminated
+    
+    Fixes: BZ #29030
+    Signed-off-by: Joan Bruguera <joanbrugueram@gmail.com>
+    Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+
+diff --git a/debug/tst-fortify.c b/debug/tst-fortify.c
+index 1668294e48b5c63c..701bffd1d664f289 100644
+--- a/debug/tst-fortify.c
++++ b/debug/tst-fortify.c
+@@ -1505,6 +1505,11 @@ do_test (void)
+       CHK_FAIL_END
+ #endif
+ 
++      /* Bug 29030 regresion check */
++      cp = "HelloWorld";
++      if (mbsrtowcs (NULL, &cp, (size_t)-1, &s) != 10)
++        FAIL ();
++
+       cp = "A";
+       if (mbstowcs (wenough, cp, 10) != 1
+ 	  || wcscmp (wenough, L"A") != 0)
+diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
+index a17ae0ed87e6163f..404496c7d6da4fb3 100644
+--- a/misc/sys/cdefs.h
++++ b/misc/sys/cdefs.h
+@@ -143,13 +143,13 @@
+    || (__builtin_constant_p (__l) && (__l) > 0))
+ 
+ /* Length is known to be safe at compile time if the __L * __S <= __OBJSZ
+-   condition can be folded to a constant and if it is true.  The -1 check is
+-   redundant because since it implies that __glibc_safe_len_cond is true.  */
++   condition can be folded to a constant and if it is true, or unknown (-1) */
+ #define __glibc_safe_or_unknown_len(__l, __s, __osz) \
+-  (__glibc_unsigned_or_positive (__l)					      \
+-   && __builtin_constant_p (__glibc_safe_len_cond ((__SIZE_TYPE__) (__l),     \
+-						   __s, __osz))		      \
+-   && __glibc_safe_len_cond ((__SIZE_TYPE__) (__l), __s, __osz))
++  ((__osz) == (__SIZE_TYPE__) -1					      \
++   || (__glibc_unsigned_or_positive (__l)				      \
++       && __builtin_constant_p (__glibc_safe_len_cond ((__SIZE_TYPE__) (__l), \
++						       (__s), (__osz)))	      \
++       && __glibc_safe_len_cond ((__SIZE_TYPE__) (__l), (__s), (__osz))))
+ 
+ /* Conversely, we know at compile time that the length is unsafe if the
+    __L * __S <= __OBJSZ condition can be folded to a constant and if it is
diff --git a/SOURCES/glibc-rh2077835.patch b/SOURCES/glibc-rh2077835.patch
new file mode 100644
index 0000000000000000000000000000000000000000..7323d49a14de81000c8059ca25554f37393b6f37
--- /dev/null
+++ b/SOURCES/glibc-rh2077835.patch
@@ -0,0 +1,211 @@
+commit 2376944b9e5c0364b9fb473e4d8dabca31b57167
+Author: Stefan Liebler <stli@linux.ibm.com>
+Date:   Wed Apr 13 14:36:09 2022 +0200
+
+    S390: Add new s390 platform z16.
+
+    The new IBM z16 is added to platform string array.
+    The macro _DL_PLATFORMS_COUNT is incremented.
+
+    _dl_hwcaps_subdir is extended by "z16" if HWCAP_S390_VXRS_PDE2
+    is set. HWCAP_S390_NNPA is not tested in _dl_hwcaps_subdirs_active
+    as those instructions may be replaced or removed in future.
+
+    tst-glibc-hwcaps.c is extended in order to test z16 via new marker5.
+
+    A fatal glibc error is dumped if glibc was build with architecture
+    level set for z16, but run on an older machine. (See dl-hwcap-check.h)
+
+Reworked for RHEL 8.7.0
+
+diff -Nrup a/elf/Makefile b/elf/Makefile
+--- a/elf/Makefile	2022-05-16 21:48:11.267916411 -0400
++++ b/elf/Makefile	2022-05-16 21:48:56.106095151 -0400
+@@ -347,7 +347,8 @@ modules-names = testobj1 testobj2 testob
+ 		libmarkermod2-1 libmarkermod2-2 \
+ 		libmarkermod3-1 libmarkermod3-2 libmarkermod3-3 \
+ 		libmarkermod4-1 libmarkermod4-2 libmarkermod4-3 libmarkermod4-4 \
+-		tst-tls20mod-bad tst-tls21mod \
++		libmarkermod5-1 libmarkermod5-2 libmarkermod5-3 libmarkermod5-4 \
++		libmarkermod5-5 tst-tls20mod-bad tst-tls21mod \
+ 
+ # Most modules build with _ISOMAC defined, but those filtered out
+ # depend on internal headers.
+@@ -1782,6 +1783,7 @@ LDFLAGS-libmarkermod1-1.so += -Wl,-sonam
+ LDFLAGS-libmarkermod2-1.so += -Wl,-soname,libmarkermod2.so
+ LDFLAGS-libmarkermod3-1.so += -Wl,-soname,libmarkermod3.so
+ LDFLAGS-libmarkermod4-1.so += -Wl,-soname,libmarkermod4.so
++LDFLAGS-libmarkermod5-1.so += -Wl,-soname,libmarkermod5.so
+ $(objpfx)libmarkermod%.os : markermodMARKER-VALUE.c
+ 	$(compile-command.c) \
+ 	  -DMARKER=marker$(firstword $(subst -, ,$*)) \
+@@ -1794,6 +1796,8 @@ $(objpfx)libmarkermod3.so: $(objpfx)libm
+ 	cp $< $@
+ $(objpfx)libmarkermod4.so: $(objpfx)libmarkermod4-1.so
+ 	cp $< $@
++$(objpfx)libmarkermod5.so: $(objpfx)libmarkermod5-1.so
++	cp $< $@
+ 
+ # tst-glibc-hwcaps-prepend checks that --glibc-hwcaps-prepend is
+ # preferred over auto-detected subdirectories.
+diff -Nrup a/elf/tst-glibc-hwcaps-cache.script b/elf/tst-glibc-hwcaps-cache.script
+--- a/elf/tst-glibc-hwcaps-cache.script	2022-05-16 21:48:11.053915558 -0400
++++ b/elf/tst-glibc-hwcaps-cache.script	2022-05-16 21:48:56.107095155 -0400
+@@ -4,6 +4,7 @@
+ cp $B/elf/libmarkermod2-1.so $L/libmarkermod2.so
+ cp $B/elf/libmarkermod3-1.so $L/libmarkermod3.so
+ cp $B/elf/libmarkermod4-1.so $L/libmarkermod4.so
++cp $B/elf/libmarkermod5-1.so $L/libmarkermod5.so
+ 
+ mkdirp 0770 $L/glibc-hwcaps/power9
+ cp $B/elf/libmarkermod2-2.so $L/glibc-hwcaps/power9/libmarkermod2.so
+@@ -20,6 +21,11 @@ mkdirp 0770 $L/glibc-hwcaps/z15
+ cp $B/elf/libmarkermod4-2.so $L/glibc-hwcaps/z13/libmarkermod4.so
+ cp $B/elf/libmarkermod4-3.so $L/glibc-hwcaps/z14/libmarkermod4.so
+ cp $B/elf/libmarkermod4-4.so $L/glibc-hwcaps/z15/libmarkermod4.so
++mkdirp 0770 $L/glibc-hwcaps/z16
++cp $B/elf/libmarkermod5-2.so $L/glibc-hwcaps/z13/libmarkermod5.so
++cp $B/elf/libmarkermod5-3.so $L/glibc-hwcaps/z14/libmarkermod5.so
++cp $B/elf/libmarkermod5-4.so $L/glibc-hwcaps/z15/libmarkermod5.so
++cp $B/elf/libmarkermod5-5.so $L/glibc-hwcaps/z16/libmarkermod5.so
+ 
+ mkdirp 0770 $L/glibc-hwcaps/x86-64-v2
+ cp $B/elf/libmarkermod2-2.so $L/glibc-hwcaps/x86-64-v2/libmarkermod2.so
+diff -Nrup a/sysdeps/s390/dl-procinfo.c b/sysdeps/s390/dl-procinfo.c
+--- a/sysdeps/s390/dl-procinfo.c	2022-05-16 21:48:11.250916343 -0400
++++ b/sysdeps/s390/dl-procinfo.c	2022-05-16 21:48:56.107095155 -0400
+@@ -64,11 +64,12 @@ PROCINFO_CLASS const char _dl_s390_cap_f
+ #if !defined PROCINFO_DECL && defined SHARED
+   ._dl_s390_platforms
+ #else
+-PROCINFO_CLASS const char _dl_s390_platforms[10][7]
++PROCINFO_CLASS const char _dl_s390_platforms[11][7]
+ #endif
+ #ifndef PROCINFO_DECL
+ = {
+-    "g5", "z900", "z990", "z9-109", "z10", "z196", "zEC12", "z13", "z14", "z15"
++    "g5", "z900", "z990", "z9-109", "z10", "z196", "zEC12", "z13", "z14", "z15",
++    "z16"
+   }
+ #endif
+ #if !defined SHARED || defined PROCINFO_DECL
+diff -Nrup a/sysdeps/s390/dl-procinfo.h b/sysdeps/s390/dl-procinfo.h
+--- a/sysdeps/s390/dl-procinfo.h	2022-05-16 21:48:11.250916343 -0400
++++ b/sysdeps/s390/dl-procinfo.h	2022-05-16 21:48:56.107095155 -0400
+@@ -23,7 +23,7 @@
+ 
+ #define _DL_HWCAP_COUNT 23
+ 
+-#define _DL_PLATFORMS_COUNT	10
++#define _DL_PLATFORMS_COUNT	11
+ 
+ /* The kernel provides up to 32 capability bits with elf_hwcap.  */
+ #define _DL_FIRST_PLATFORM	32
+diff -Nrup a/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c b/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c
+--- a/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c	2022-05-16 21:48:11.053915558 -0400
++++ b/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c	2022-05-16 21:58:02.840301911 -0400
+@@ -19,8 +19,8 @@
+ #include <dl-hwcaps.h>
+ #include <ldsodefs.h>
+ 
+-const char _dl_hwcaps_subdirs[] = "z15:z14:z13";
+-enum { subdirs_count = 3 }; /* Number of components in _dl_hwcaps_subdirs.  */
++const char _dl_hwcaps_subdirs[] = "z16:z15:z14:z13";
++enum { subdirs_count = 4 }; /* Number of components in _dl_hwcaps_subdirs.  */
+ 
+ uint32_t
+ _dl_hwcaps_subdirs_active (void)
+@@ -50,5 +50,12 @@ _dl_hwcaps_subdirs_active (void)
+     return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active);
+   ++active;
+ 
++  /* z16.
++   Note: We do not list HWCAP_S390_NNPA here as, according to the Principles of
++   Operation, those instructions may be replaced or removed in future.  */
++  if (!(GLRO (dl_hwcap) & HWCAP_S390_VXRS_PDE2))
++    return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active);
++  ++active;
++
+   return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active);
+ }
+diff -Nrup a/sysdeps/s390/s390-64/Makefile b/sysdeps/s390/s390-64/Makefile
+--- a/sysdeps/s390/s390-64/Makefile	2022-05-16 21:48:11.053915558 -0400
++++ b/sysdeps/s390/s390-64/Makefile	2022-05-16 21:54:08.832355745 -0400
+@@ -7,8 +7,11 @@ CFLAGS-rtld.c += -Wno-uninitialized -Wno
+ CFLAGS-dl-load.c += -Wno-unused
+ CFLAGS-dl-reloc.c += -Wno-unused
+ 
+-$(objpfx)tst-glibc-hwcaps: $(objpfx)libmarkermod2-1.so \
+-  $(objpfx)libmarkermod3-1.so $(objpfx)libmarkermod4-1.so
++$(objpfx)tst-glibc-hwcaps: \
++    $(objpfx)libmarkermod2-1.so \
++    $(objpfx)libmarkermod3-1.so \
++    $(objpfx)libmarkermod4-1.so \
++    $(objpfx)libmarkermod5-1.so
+ $(objpfx)tst-glibc-hwcaps.out: \
+   $(objpfx)libmarkermod2.so \
+     $(objpfx)glibc-hwcaps/z13/libmarkermod2.so \
+@@ -19,6 +22,11 @@ $(objpfx)tst-glibc-hwcaps.out: \
+     $(objpfx)glibc-hwcaps/z13/libmarkermod4.so \
+     $(objpfx)glibc-hwcaps/z14/libmarkermod4.so \
+     $(objpfx)glibc-hwcaps/z15/libmarkermod4.so \
++  $(objpfx)libmarkermod5.so \
++    $(objpfx)glibc-hwcaps/z13/libmarkermod5.so \
++    $(objpfx)glibc-hwcaps/z14/libmarkermod5.so \
++    $(objpfx)glibc-hwcaps/z15/libmarkermod5.so \
++    $(objpfx)glibc-hwcaps/z16/libmarkermod5.so
+ 
+ $(objpfx)glibc-hwcaps/z13/libmarkermod2.so: $(objpfx)libmarkermod2-2.so
+ 	$(make-target-directory)
+@@ -38,6 +46,18 @@ $(objpfx)glibc-hwcaps/z14/libmarkermod4.
+ $(objpfx)glibc-hwcaps/z15/libmarkermod4.so: $(objpfx)libmarkermod4-4.so
+ 	$(make-target-directory)
+ 	cp $< $@
++$(objpfx)glibc-hwcaps/z13/libmarkermod5.so: $(objpfx)libmarkermod5-2.so
++	$(make-target-directory)
++	cp $< $@
++$(objpfx)glibc-hwcaps/z14/libmarkermod5.so: $(objpfx)libmarkermod5-3.so
++	$(make-target-directory)
++	cp $< $@
++$(objpfx)glibc-hwcaps/z15/libmarkermod5.so: $(objpfx)libmarkermod5-4.so
++	$(make-target-directory)
++	cp $< $@
++$(objpfx)glibc-hwcaps/z16/libmarkermod5.so: $(objpfx)libmarkermod5-5.so
++	$(make-target-directory)
++	cp $< $@
+ 
+ ifeq (no,$(build-hardcoded-path-in-tests))
+ # This is an ld.so.cache test, and RPATH/RUNPATH in the executable
+diff -Nrup a/sysdeps/s390/s390-64/tst-glibc-hwcaps.c b/sysdeps/s390/s390-64/tst-glibc-hwcaps.c
+--- a/sysdeps/s390/s390-64/tst-glibc-hwcaps.c	2022-05-16 21:48:11.053915558 -0400
++++ b/sysdeps/s390/s390-64/tst-glibc-hwcaps.c	2022-05-16 21:48:56.107095155 -0400
+@@ -25,6 +25,7 @@
+ extern int marker2 (void);
+ extern int marker3 (void);
+ extern int marker4 (void);
++extern int marker5 (void);
+ 
+ /* Return the arch level, 10 for the baseline libmarkermod*.so's.  */
+ static int
+@@ -63,9 +64,13 @@ compute_level (void)
+     return 12;
+   if (strcmp (platform, "z15") == 0)
+     return 13;
++  if (strcmp (platform, "z16") == 0)
++    return 14;
+   printf ("warning: unrecognized AT_PLATFORM value: %s\n", platform);
+   /* Assume that the new platform supports z15.  */
+   return 13;
++  /* Assume that the new platform supports z16.  */
++  return 14;
+ }
+ 
+ static int
+@@ -76,6 +81,7 @@ do_test (void)
+   TEST_COMPARE (marker2 (), MIN (level - 9, 2));
+   TEST_COMPARE (marker3 (), MIN (level - 9, 3));
+   TEST_COMPARE (marker4 (), MIN (level - 9, 4));
++  TEST_COMPARE (marker5 (), MIN (level - 9, 5));
+   return 0;
+ }
+ 
diff --git a/SOURCES/glibc-rh2080349-1.patch b/SOURCES/glibc-rh2080349-1.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0bc7524bad1df2095fdb3c1da125a094e58d3374
--- /dev/null
+++ b/SOURCES/glibc-rh2080349-1.patch
@@ -0,0 +1,37 @@
+commit 4a7c342605bc653f72d60c36abe698986fb5cb47
+Author: Joseph Myers <joseph@codesourcery.com>
+Date:   Wed Apr 28 17:19:24 2021 +0000
+
+    Update syscall lists for Linux 5.12.
+    
+    Linux 5.12 has one new syscall, mount_setattr.  Update
+    syscall-names.list and regenerate the arch-syscall.h headers with
+    build-many-glibcs.py update-syscalls.
+    
+    Tested with build-many-glibcs.py.
+
+Modified to only update syscall-names.list to Linux 5.12.
+
+diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
+index f6cb34089d..8e3cfa0e77 100644
+--- a/sysdeps/unix/sysv/linux/syscall-names.list
++++ b/sysdeps/unix/sysv/linux/syscall-names.list
+@@ -21,8 +21,8 @@
+ # This file can list all potential system calls.  The names are only
+ # used if the installed kernel headers also provide them.
+ 
+-# The list of system calls is current as of Linux 5.11.
+-kernel 5.11
++# The list of system calls is current as of Linux 5.12.
++kernel 5.12
+ 
+ FAST_atomic_update
+ FAST_cmpxchg
+@@ -258,6 +258,7 @@ mmap
+ mmap2
+ modify_ldt
+ mount
++mount_setattr
+ move_mount
+ move_pages
+ mprotect
diff --git a/SOURCES/glibc-rh2080349-2.patch b/SOURCES/glibc-rh2080349-2.patch
new file mode 100644
index 0000000000000000000000000000000000000000..6084110c46802ffce11c15fac953a1da10e55c95
--- /dev/null
+++ b/SOURCES/glibc-rh2080349-2.patch
@@ -0,0 +1,40 @@
+commit b1b4f7209ecaad4bf9a5d0d2ef1338409d364bac
+Author: Joseph Myers <joseph@codesourcery.com>
+Date:   Thu Jul 1 17:37:36 2021 +0000
+
+    Update syscall lists for Linux 5.13
+    
+    Linux 5.13 has three new syscalls (landlock_create_ruleset,
+    landlock_add_rule, landlock_restrict_self).  Update syscall-names.list
+    and regenerate the arch-syscall.h headers with build-many-glibcs.py
+    update-syscalls.
+    
+    Tested with build-many-glibcs.py.
+
+Modified to only update syscall-names.list to Linux 5.13.
+
+diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
+index 8e3cfa0e77..89c5895b9b 100644
+--- a/sysdeps/unix/sysv/linux/syscall-names.list
++++ b/sysdeps/unix/sysv/linux/syscall-names.list
+@@ -21,8 +21,8 @@
+ # This file can list all potential system calls.  The names are only
+ # used if the installed kernel headers also provide them.
+ 
+-# The list of system calls is current as of Linux 5.12.
+-kernel 5.12
++# The list of system calls is current as of Linux 5.13.
++kernel 5.13
+ 
+ FAST_atomic_update
+ FAST_cmpxchg
+@@ -224,6 +224,9 @@ kexec_file_load
+ kexec_load
+ keyctl
+ kill
++landlock_add_rule
++landlock_create_ruleset
++landlock_restrict_self
+ lchown
+ lchown32
+ lgetxattr
diff --git a/SOURCES/glibc-rh2080349-3.patch b/SOURCES/glibc-rh2080349-3.patch
new file mode 100644
index 0000000000000000000000000000000000000000..7293e0390ce6c4fb0627ff9c11fe2aba139d3527
--- /dev/null
+++ b/SOURCES/glibc-rh2080349-3.patch
@@ -0,0 +1,45 @@
+commit 89dc0372a9055e7ef86fe19be6201fa0b16b2f0e
+Author: Joseph Myers <joseph@codesourcery.com>
+Date:   Wed Sep 8 12:42:06 2021 +0000
+
+    Update syscall lists for Linux 5.14
+    
+    Linux 5.14 has two new syscalls, memfd_secret (on some architectures
+    only) and quotactl_fd.  Update syscall-names.list and regenerate the
+    arch-syscall.h headers with build-many-glibcs.py update-syscalls.
+    
+    Tested with build-many-glibcs.py.
+
+Modified to only update syscall-names.list to Linux 5.14.
+
+diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
+index 89c5895b9b..fd98893b0e 100644
+--- a/sysdeps/unix/sysv/linux/syscall-names.list
++++ b/sysdeps/unix/sysv/linux/syscall-names.list
+@@ -21,8 +21,8 @@
+ # This file can list all potential system calls.  The names are only
+ # used if the installed kernel headers also provide them.
+ 
+-# The list of system calls is current as of Linux 5.13.
+-kernel 5.13
++# The list of system calls is current as of Linux 5.14.
++kernel 5.14
+ 
+ FAST_atomic_update
+ FAST_cmpxchg
+@@ -247,6 +247,7 @@ madvise
+ mbind
+ membarrier
+ memfd_create
++memfd_secret
+ memory_ordering
+ migrate_pages
+ mincore
+@@ -452,6 +453,7 @@ pwritev
+ pwritev2
+ query_module
+ quotactl
++quotactl_fd
+ read
+ readahead
+ readdir
diff --git a/SOURCES/glibc-rh2080349-4.patch b/SOURCES/glibc-rh2080349-4.patch
new file mode 100644
index 0000000000000000000000000000000000000000..205b9033d97482a28f548e89be69d3b07ae0ca11
--- /dev/null
+++ b/SOURCES/glibc-rh2080349-4.patch
@@ -0,0 +1,43 @@
+commit 3387c40a8bbad5faf85b1feb56429cb20feaa640
+Author: Joseph Myers <joseph@codesourcery.com>
+Date:   Wed Nov 10 15:21:19 2021 +0000
+
+    Update syscall lists for Linux 5.15
+    
+    Linux 5.15 has one new syscall, process_mrelease (and also enables the
+    clone3 syscall for RV32).  It also has a macro __NR_SYSCALL_MASK for
+    Arm, which is not a syscall but matches the pattern used for syscall
+    macro names.
+    
+    Add __NR_SYSCALL_MASK to the names filtered out in the code dealing
+    with syscall lists, update syscall-names.list for the new syscall and
+    regenerate the arch-syscall.h headers with build-many-glibcs.py
+    update-syscalls.
+    
+    Tested with build-many-glibcs.py.
+
+Modified to only update syscall-names.list to Linux 5.15.
+
+diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
+index fd98893b0e..1a74d090b7 100644
+--- a/sysdeps/unix/sysv/linux/syscall-names.list
++++ b/sysdeps/unix/sysv/linux/syscall-names.list
+@@ -21,8 +21,8 @@
+ # This file can list all potential system calls.  The names are only
+ # used if the installed kernel headers also provide them.
+ 
+-# The list of system calls is current as of Linux 5.14.
+-kernel 5.14
++# The list of system calls is current as of Linux 5.15.
++kernel 5.15
+ 
+ FAST_atomic_update
+ FAST_cmpxchg
+@@ -440,6 +440,7 @@ preadv
+ preadv2
+ prlimit64
+ process_madvise
++process_mrelease
+ process_vm_readv
+ process_vm_writev
+ prof
diff --git a/SOURCES/glibc-rh2080349-5.patch b/SOURCES/glibc-rh2080349-5.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b0429170aa4f6cce387ad11b9d0f628f3e607ba9
--- /dev/null
+++ b/SOURCES/glibc-rh2080349-5.patch
@@ -0,0 +1,37 @@
+commit 4997a533ae4b51ef66a6b68862b7578a7acb82df
+Author: Joseph Myers <joseph@codesourcery.com>
+Date:   Thu Jan 13 22:18:13 2022 +0000
+
+    Update syscall lists for Linux 5.16
+    
+    Linux 5.16 has one new syscall, futex_waitv.  Update
+    syscall-names.list and regenerate the arch-syscall.h headers with
+    build-many-glibcs.py update-syscalls.
+    
+    Tested with build-many-glibcs.py.
+
+Modified to only update syscall-names.list to Linux 5.16.
+
+diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
+index c80a9a59cb..6421806110 100644
+--- a/sysdeps/unix/sysv/linux/syscall-names.list
++++ b/sysdeps/unix/sysv/linux/syscall-names.list
+@@ -21,8 +21,8 @@
+ # This file can list all potential system calls.  The names are only
+ # used if the installed kernel headers also provide them.
+ 
+-# The list of system calls is current as of Linux 5.15.
+-kernel 5.15
++# The list of system calls is current as of Linux 5.16.
++kernel 5.16
+ 
+ FAST_atomic_update
+ FAST_cmpxchg
+@@ -146,6 +146,7 @@ ftruncate
+ ftruncate64
+ futex
+ futex_time64
++futex_waitv
+ futimesat
+ get_kernel_syms
+ get_mempolicy
diff --git a/SOURCES/glibc-rh2080349-6.patch b/SOURCES/glibc-rh2080349-6.patch
new file mode 100644
index 0000000000000000000000000000000000000000..333f3623065bfa3746e5f048b1664751b7725435
--- /dev/null
+++ b/SOURCES/glibc-rh2080349-6.patch
@@ -0,0 +1,37 @@
+commit 8ef9196b26793830515402ea95aca2629f7721ec
+Author: Joseph Myers <joseph@codesourcery.com>
+Date:   Wed Mar 23 17:11:56 2022 +0000
+
+    Update syscall lists for Linux 5.17
+    
+    Linux 5.17 has one new syscall, set_mempolicy_home_node.  Update
+    syscall-names.list and regenerate the arch-syscall.h headers with
+    build-many-glibcs.py update-syscalls.
+    
+    Tested with build-many-glibcs.py.
+
+Modified to only update syscall-names.list to Linux 5.17.
+
+diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
+index 6421806110..b8c0b0c586 100644
+--- a/sysdeps/unix/sysv/linux/syscall-names.list
++++ b/sysdeps/unix/sysv/linux/syscall-names.list
+@@ -21,8 +21,8 @@
+ # This file can list all potential system calls.  The names are only
+ # used if the installed kernel headers also provide them.
+ 
+-# The list of system calls is current as of Linux 5.16.
+-kernel 5.16
++# The list of system calls is current as of Linux 5.17.
++kernel 5.17
+ 
+ FAST_atomic_update
+ FAST_cmpxchg
+@@ -524,6 +524,7 @@ sendmmsg
+ sendmsg
+ sendto
+ set_mempolicy
++set_mempolicy_home_node
+ set_robust_list
+ set_thread_area
+ set_tid_address
diff --git a/SOURCES/glibc-rh2080349-7.patch b/SOURCES/glibc-rh2080349-7.patch
new file mode 100644
index 0000000000000000000000000000000000000000..067f10e6ca6fa793811d95bbe8c1eb9ba9c77eb6
--- /dev/null
+++ b/SOURCES/glibc-rh2080349-7.patch
@@ -0,0 +1,28 @@
+commit 3d9926663cba19f40d26d8a8ab3b2a7cc09ffb13
+Author: Joseph Myers <joseph@codesourcery.com>
+Date:   Wed May 25 14:37:28 2022 +0000
+
+    Update syscall-names.list for Linux 5.18
+    
+    Linux 5.18 has no new syscalls.  Update the version number in
+    syscall-names.list to reflect that it is still current for 5.18.
+    
+    Tested with build-many-glibcs.py.
+
+Modified to only update syscall-names.list to Linux 5.18.
+
+diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
+index b8c0b0c586..6c7b2f7011 100644
+--- a/sysdeps/unix/sysv/linux/syscall-names.list
++++ b/sysdeps/unix/sysv/linux/syscall-names.list
+@@ -21,8 +21,8 @@
+ # This file can list all potential system calls.  The names are only
+ # used if the installed kernel headers also provide them.
+ 
+-# The list of system calls is current as of Linux 5.17.
+-kernel 5.17
++# The list of system calls is current as of Linux 5.18.
++kernel 5.18
+ 
+ FAST_atomic_update
+ FAST_cmpxchg
diff --git a/SOURCES/glibc-rh2080349-8.patch b/SOURCES/glibc-rh2080349-8.patch
new file mode 100644
index 0000000000000000000000000000000000000000..5e6fa9ffc8f3080df8f88cce7314c6498e863dc3
--- /dev/null
+++ b/SOURCES/glibc-rh2080349-8.patch
@@ -0,0 +1,20 @@
+commit 9dde3a24f132090fa8f88d6eaa2bc4c48f2e942f
+Author: Stafford Horne <shorne@gmail.com>
+Date:   Sat May 23 12:41:31 2020 +0900
+
+    linux/syscalls: Add or1k_atomic syscall for OpenRISC
+    
+    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+diff --git a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
+index 42701ce583..c80a9a59cb 100644
+--- a/sysdeps/unix/sysv/linux/syscall-names.list
++++ b/sysdeps/unix/sysv/linux/syscall-names.list
+@@ -304,6 +304,7 @@ open_by_handle_at
+ open_tree
+ openat
+ openat2
++or1k_atomic
+ osf_adjtime
+ osf_afs_syscall
+ osf_alt_plock
diff --git a/SOURCES/glibc-rh2080349-9.patch b/SOURCES/glibc-rh2080349-9.patch
new file mode 100644
index 0000000000000000000000000000000000000000..080811e8278fb21a218aeceddc6280a5234cb536
--- /dev/null
+++ b/SOURCES/glibc-rh2080349-9.patch
@@ -0,0 +1,10 @@
+diff -Nrup a/sysdeps/unix/sysv/linux/syscall-names.list b/sysdeps/unix/sysv/linux/syscall-names.list
+--- a/sysdeps/unix/sysv/linux/syscall-names.list	2022-07-18 10:44:38.791332453 -0400
++++ b/sysdeps/unix/sysv/linux/syscall-names.list	2022-07-18 11:02:51.054663735 -0400
+@@ -1,5 +1,5 @@
+ # List of all known Linux system calls.
+-# Copyright (C) 2017-2021 Free Software Foundation, Inc.
++# Copyright (C) 2017-2022 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
diff --git a/SOURCES/glibc-rh2086853.patch b/SOURCES/glibc-rh2086853.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d11e4cb2fd60fa1483c52c10ae3121669884b0dd
--- /dev/null
+++ b/SOURCES/glibc-rh2086853.patch
@@ -0,0 +1,30 @@
+commit 61a87530108ec9181e1b18a9b727ec3cc3ba7532
+Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
+Date:   Fri May 13 10:01:47 2022 +0530
+
+    fortify: Ensure that __glibc_fortify condition is a constant [BZ #29141]
+    
+    The fix c8ee1c85 introduced a -1 check for object size without also
+    checking that object size is a constant.  Because of this, the tree
+    optimizer passes in gcc fail to fold away one of the branches in
+    __glibc_fortify and trips on a spurious Wstringop-overflow.  The warning
+    itself is incorrect and the branch does go away eventually in DCE in the
+    rtl passes in gcc, but the constant check is a helpful hint to simplify
+    code early, so add it in.
+    
+    Resolves: BZ #29141
+    Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
+
+diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
+index 404496c7d6da4fb3..f3d7efdd2a9320f7 100644
+--- a/misc/sys/cdefs.h
++++ b/misc/sys/cdefs.h
+@@ -145,7 +145,7 @@
+ /* Length is known to be safe at compile time if the __L * __S <= __OBJSZ
+    condition can be folded to a constant and if it is true, or unknown (-1) */
+ #define __glibc_safe_or_unknown_len(__l, __s, __osz) \
+-  ((__osz) == (__SIZE_TYPE__) -1					      \
++  ((__builtin_constant_p (__osz) && (__osz) == (__SIZE_TYPE__) -1)	      \
+    || (__glibc_unsigned_or_positive (__l)				      \
+        && __builtin_constant_p (__glibc_safe_len_cond ((__SIZE_TYPE__) (__l), \
+ 						       (__s), (__osz)))	      \
diff --git a/SOURCES/glibc-rh2089247-1.patch b/SOURCES/glibc-rh2089247-1.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b3e05ef610c9aee1c9c9ff0d866672a661ffc0a7
--- /dev/null
+++ b/SOURCES/glibc-rh2089247-1.patch
@@ -0,0 +1,47 @@
+commit e1df30fbc2e2167a982c0e77a7ebee28f4dd0800
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Thu Jul 25 11:22:17 2019 -0300
+
+    Get new entropy on each attempt __gen_tempname (BZ #15813)
+    
+    This is missing bit for fully fix BZ#15813 (the other two were fixed
+    by 359653aaacad463).
+    
+    Checked on x86_64-linux-gnu.
+    
+            [BZ #15813]
+            sysdeps/posix/tempname.c (__gen_tempname): get entrypy on each
+            attempt.
+
+diff --git a/sysdeps/posix/tempname.c b/sysdeps/posix/tempname.c
+index 3d26f378021680ae..61d7a9f36d37abae 100644
+--- a/sysdeps/posix/tempname.c
++++ b/sysdeps/posix/tempname.c
+@@ -186,7 +186,6 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
+ {
+   int len;
+   char *XXXXXX;
+-  uint64_t value;
+   unsigned int count;
+   int fd = -1;
+   int save_errno = errno;
+@@ -218,13 +217,13 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
+   /* This is where the Xs start.  */
+   XXXXXX = &tmpl[len - 6 - suffixlen];
+ 
+-  /* Get some more or less random data.  */
+-  RANDOM_BITS (value);
+-  value ^= (uint64_t)__getpid () << 32;
+-
+-  for (count = 0; count < attempts; value += 7777, ++count)
++  uint64_t pid = (uint64_t) __getpid () << 32;
++  for (count = 0; count < attempts; ++count)
+     {
+-      uint64_t v = value;
++      uint64_t v;
++      /* Get some more or less random data.  */
++      RANDOM_BITS (v);
++      v ^= pid;
+ 
+       /* Fill in the random bits.  */
+       XXXXXX[0] = letters[v % 62];
diff --git a/SOURCES/glibc-rh2089247-2.patch b/SOURCES/glibc-rh2089247-2.patch
new file mode 100644
index 0000000000000000000000000000000000000000..84c6ac23e5f9728a71006acf7b4436fe703d2cc0
--- /dev/null
+++ b/SOURCES/glibc-rh2089247-2.patch
@@ -0,0 +1,87 @@
+commit 8eaf34eda256ba3647ed6e7ed5c7c9aa19955d17
+Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
+Date:   Fri Dec 13 10:10:59 2019 +0100
+
+    hurd: Fix local PLT
+
+    * include/sys/random.h (__getrandom): Add hidden prototype.
+    * stdlib/getrandom.c (getrandom): Rename to hidden definition __getrandom.
+    Add weak alias.
+    * sysdeps/mach/hurd/getrandom.c (getrandom): Likewise.
+    * sysdeps/unix/sysv/linux/getrandom.c (getrandom): Likewise.
+    * sysdeps/mach/hurd/getentropy.c (getentropy): Use __getrandom instead of
+    getrandom.
+
+Conflicts:
+	include/sys/random.h
+	  (Missing backport of include/ consistency patch,
+	  commit ebd32784ce2029d0461a90a79bc4e37f8d051765 upstream.)
+	sysdeps/mach/hurd/getentropy.c
+	  (Hurd change has been dropped.)
+	sysdeps/unix/sysv/linux/dl-write.c
+	  (Mismerge of sysdeps/mach/hurd/getrandom.c.)
+
+diff --git a/include/sys/random.h b/include/sys/random.h
+new file mode 100644
+index 0000000000000000..6aa313d35dbdce8a
+--- /dev/null
++++ b/include/sys/random.h
+@@ -0,0 +1,11 @@
++#ifndef _SYS_RANDOM_H
++#include <stdlib/sys/random.h>
++
++# ifndef _ISOMAC
++
++extern ssize_t __getrandom (void *__buffer, size_t __length,
++                            unsigned int __flags) __wur;
++libc_hidden_proto (__getrandom)
++
++# endif /* !_ISOMAC */
++#endif
+diff --git a/stdlib/getrandom.c b/stdlib/getrandom.c
+index 45234bea17c5c86c..f8056688e40a0215 100644
+--- a/stdlib/getrandom.c
++++ b/stdlib/getrandom.c
+@@ -22,10 +22,12 @@
+ /* Write up to LENGTH bytes of randomness starting at BUFFER.
+    Return the number of bytes written, or -1 on error.  */
+ ssize_t
+-getrandom (void *buffer, size_t length, unsigned int flags)
++__getrandom (void *buffer, size_t length, unsigned int flags)
+ {
+   __set_errno (ENOSYS);
+   return -1;
+ }
+-
+ stub_warning (getrandom)
++
++libc_hidden_def (__getrandom)
++weak_alias (__getrandom, getrandom)
+diff --git a/sysdeps/unix/sysv/linux/getrandom.c b/sysdeps/unix/sysv/linux/getrandom.c
+index 435b037399665654..e34d7fdcd89d9b06 100644
+--- a/sysdeps/unix/sysv/linux/getrandom.c
++++ b/sysdeps/unix/sysv/linux/getrandom.c
+@@ -25,7 +25,7 @@
+ /* Write up to LENGTH bytes of randomness starting at BUFFER.
+    Return the number of bytes written, or -1 on error.  */
+ ssize_t
+-getrandom (void *buffer, size_t length, unsigned int flags)
++__getrandom (void *buffer, size_t length, unsigned int flags)
+ {
+   return SYSCALL_CANCEL (getrandom, buffer, length, flags);
+ }
+@@ -33,7 +33,7 @@ getrandom (void *buffer, size_t length, unsigned int flags)
+ /* Always provide a definition, even if the kernel headers lack the
+    system call number. */
+ ssize_t
+-getrandom (void *buffer, size_t length, unsigned int flags)
++__getrandom (void *buffer, size_t length, unsigned int flags)
+ {
+   /* Ideally, we would add a cancellation point here, but we currently
+      cannot do so inside libc.  */
+@@ -41,3 +41,5 @@ getrandom (void *buffer, size_t length, unsigned int flags)
+   return -1;
+ }
+ #endif
++libc_hidden_def (__getrandom)
++weak_alias (__getrandom, getrandom)
diff --git a/SOURCES/glibc-rh2089247-3.patch b/SOURCES/glibc-rh2089247-3.patch
new file mode 100644
index 0000000000000000000000000000000000000000..b7f72e6b7923cdc8ef3cbdd5a43c0818e7844162
--- /dev/null
+++ b/SOURCES/glibc-rh2089247-3.patch
@@ -0,0 +1,67 @@
+Partial backport of:
+
+commit 04986243d1af37ac0177ed2f9db0a066ebd2b212
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Wed Jul 15 19:35:58 2020 +0000
+
+    Remove internal usage of extensible stat functions
+
+    It replaces the internal usage of __{f,l}xstat{at}{64} with the
+    __{f,l}stat{at}{64}.  It should not change the generate code since
+    sys/stat.h explicit defines redirections to internal calls back to
+    xstat* symbols.
+
+    Checked with a build for all affected ABIs.  I also check on
+    x86_64-linux-gnu and i686-linux-gnu.
+
+    Reviewed-by: Lukasz Majewski <lukma@denx.de>
+
+Only the changes to include/sys/stat.h and sysdeps/posix/tempname.c
+are included here.
+
+diff --git a/include/sys/stat.h b/include/sys/stat.h
+index b82d4527801d4797..c5b1938b87c9c5c3 100644
+--- a/include/sys/stat.h
++++ b/include/sys/stat.h
+@@ -52,6 +52,7 @@ extern __typeof (__fxstatat64) __fxstatat64 attribute_hidden;
+ #define lstat64(fname, buf)  __lxstat64 (_STAT_VER, fname, buf)
+ #define __lstat64(fname, buf)  __lxstat64 (_STAT_VER, fname, buf)
+ #define stat64(fname, buf) __xstat64 (_STAT_VER, fname, buf)
++#define __stat64(fname, buf) __xstat64 (_STAT_VER, fname, buf)
+ #define fstat64(fd, buf) __fxstat64 (_STAT_VER, fd, buf)
+ #define __fstat64(fd, buf) __fxstat64 (_STAT_VER, fd, buf)
+ #define fstat(fd, buf) __fxstat (_STAT_VER, fd, buf)
+diff --git a/sysdeps/posix/tempname.c b/sysdeps/posix/tempname.c
+index 61d7a9f36d37abae..a7b404cf4410cb00 100644
+--- a/sysdeps/posix/tempname.c
++++ b/sysdeps/posix/tempname.c
+@@ -66,7 +66,6 @@
+ # define __gettimeofday gettimeofday
+ # define __mkdir mkdir
+ # define __open open
+-# define __lxstat64(version, file, buf) lstat (file, buf)
+ # define __secure_getenv secure_getenv
+ #endif
+ 
+@@ -97,7 +96,7 @@ static int
+ direxists (const char *dir)
+ {
+   struct_stat64 buf;
+-  return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode);
++  return __stat64 (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
+ }
+ 
+ /* Path search algorithm, for tmpnam, tmpfile, etc.  If DIR is
+@@ -252,10 +251,10 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
+ 
+ 	case __GT_NOCREATE:
+ 	  /* This case is backward from the other three.  __gen_tempname
+-	     succeeds if __xstat fails because the name does not exist.
++	     succeeds if lstat fails because the name does not exist.
+ 	     Note the continue to bypass the common logic at the bottom
+ 	     of the loop.  */
+-	  if (__lxstat64 (_STAT_VER, tmpl, &st) < 0)
++	  if (__lstat64 (tmpl, &st) < 0)
+ 	    {
+ 	      if (errno == ENOENT)
+ 		{
diff --git a/SOURCES/glibc-rh2089247-4.patch b/SOURCES/glibc-rh2089247-4.patch
new file mode 100644
index 0000000000000000000000000000000000000000..878739aaab8562c66e1965b26f3e920867da2aa0
--- /dev/null
+++ b/SOURCES/glibc-rh2089247-4.patch
@@ -0,0 +1,440 @@
+commit 4dddd7e9cbecad4aa03ee5a9b9edb596e3d4e909
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Tue Sep 29 08:56:07 2020 -0300
+
+    posix: Sync tempname with gnulib [BZ #26648]
+
+    It syncs with gnulib commit b1268f22f443e8e4b9e.  The try_tempname_len
+    now uses getrandom on each iteration to get entropy and only uses the
+    clock plus ASLR as source of entropy if getrandom fails.
+
+    Checked on x86_64-linux-gnu and i686-linux-gnu.
+
+Conflicts:
+	sysdeps/posix/tempname.c
+	  (Missing tree-wide __gettimeofday to clock_gettime change,
+	  commit 4a39c34c4f85de57fb4e648cfa1e774437d69680 upstream.
+	  File was rebased to the upstream version.)
+
+diff --git a/sysdeps/posix/tempname.c b/sysdeps/posix/tempname.c
+index a7b404cf4410cb00..f199b25a7a227751 100644
+--- a/sysdeps/posix/tempname.c
++++ b/sysdeps/posix/tempname.c
+@@ -1,4 +1,4 @@
+-/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
++/* Copyright (C) 1991-2021 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
+@@ -13,10 +13,10 @@
+ 
+    You should have received a copy of the GNU Lesser General Public
+    License along with the GNU C Library; if not, see
+-   <http://www.gnu.org/licenses/>.  */
++   <https://www.gnu.org/licenses/>.  */
+ 
+ #if !_LIBC
+-# include <config.h>
++# include <libc-config.h>
+ # include "tempname.h"
+ #endif
+ 
+@@ -24,9 +24,6 @@
+ #include <assert.h>
+ 
+ #include <errno.h>
+-#ifndef __set_errno
+-# define __set_errno(Val) errno = (Val)
+-#endif
+ 
+ #include <stdio.h>
+ #ifndef P_tmpdir
+@@ -36,12 +33,12 @@
+ # define TMP_MAX 238328
+ #endif
+ #ifndef __GT_FILE
+-# define __GT_FILE	0
+-# define __GT_DIR	1
+-# define __GT_NOCREATE	2
++# define __GT_FILE      0
++# define __GT_DIR       1
++# define __GT_NOCREATE  2
+ #endif
+-#if !_LIBC && (GT_FILE != __GT_FILE || GT_DIR != __GT_DIR	\
+-	       || GT_NOCREATE != __GT_NOCREATE)
++#if !_LIBC && (GT_FILE != __GT_FILE || GT_DIR != __GT_DIR       \
++               || GT_NOCREATE != __GT_NOCREATE)
+ # error report this to bug-gnulib@gnu.org
+ #endif
+ 
+@@ -50,11 +47,11 @@
+ #include <string.h>
+ 
+ #include <fcntl.h>
+-#include <sys/time.h>
++#include <stdalign.h>
+ #include <stdint.h>
+-#include <unistd.h>
+-
++#include <sys/random.h>
+ #include <sys/stat.h>
++#include <time.h>
+ 
+ #if _LIBC
+ # define struct_stat64 struct stat64
+@@ -62,33 +59,38 @@
+ #else
+ # define struct_stat64 struct stat
+ # define __gen_tempname gen_tempname
+-# define __getpid getpid
+-# define __gettimeofday gettimeofday
+ # define __mkdir mkdir
+ # define __open open
+-# define __secure_getenv secure_getenv
++# define __lstat64(file, buf) lstat (file, buf)
++# define __stat64(file, buf) stat (file, buf)
++# define __getrandom getrandom
++# define __clock_gettime64 clock_gettime
++# define __timespec64 timespec
+ #endif
+ 
+-#ifdef _LIBC
+-# include <random-bits.h>
+-# define RANDOM_BITS(Var) ((Var) = random_bits ())
+-# else
+-# define RANDOM_BITS(Var) \
+-    {                                                                         \
+-      struct timeval tv;                                                      \
+-      __gettimeofday (&tv, NULL);                                             \
+-      (Var) = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;                      \
+-    }
+-#endif
++/* Use getrandom if it works, falling back on a 64-bit linear
++   congruential generator that starts with Var's value
++   mixed in with a clock's low-order bits if available.  */
++typedef uint_fast64_t random_value;
++#define RANDOM_VALUE_MAX UINT_FAST64_MAX
++#define BASE_62_DIGITS 10 /* 62**10 < UINT_FAST64_MAX */
++#define BASE_62_POWER (62LL * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62)
+ 
+-/* Use the widest available unsigned type if uint64_t is not
+-   available.  The algorithm below extracts a number less than 62**6
+-   (approximately 2**35.725) from uint64_t, so ancient hosts where
+-   uintmax_t is only 32 bits lose about 3.725 bits of randomness,
+-   which is better than not having mkstemp at all.  */
+-#if !defined UINT64_MAX && !defined uint64_t
+-# define uint64_t uintmax_t
++static random_value
++random_bits (random_value var)
++{
++  random_value r;
++  /* Without GRND_NONBLOCK it can be blocked for minutes on some systems.  */
++  if (__getrandom (&r, sizeof r, GRND_NONBLOCK) == sizeof r)
++    return r;
++#if _LIBC || (defined CLOCK_MONOTONIC && HAVE_CLOCK_GETTIME)
++  /* Add entropy if getrandom did not work.  */
++  struct __timespec64 tv;
++  __clock_gettime64 (CLOCK_MONOTONIC, &tv);
++  var ^= tv.tv_nsec;
+ #endif
++  return 2862933555777941757 * var + 3037000493;
++}
+ 
+ #if _LIBC
+ /* Return nonzero if DIR is an existent directory.  */
+@@ -107,7 +109,7 @@ direxists (const char *dir)
+    enough space in TMPL. */
+ int
+ __path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
+-	       int try_tmpdir)
++               int try_tmpdir)
+ {
+   const char *d;
+   size_t dlen, plen;
+@@ -121,35 +123,35 @@ __path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
+     {
+       plen = strlen (pfx);
+       if (plen > 5)
+-	plen = 5;
++        plen = 5;
+     }
+ 
+   if (try_tmpdir)
+     {
+       d = __secure_getenv ("TMPDIR");
+       if (d != NULL && direxists (d))
+-	dir = d;
++        dir = d;
+       else if (dir != NULL && direxists (dir))
+-	/* nothing */ ;
++        /* nothing */ ;
+       else
+-	dir = NULL;
++        dir = NULL;
+     }
+   if (dir == NULL)
+     {
+       if (direxists (P_tmpdir))
+-	dir = P_tmpdir;
++        dir = P_tmpdir;
+       else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp"))
+-	dir = "/tmp";
++        dir = "/tmp";
+       else
+-	{
+-	  __set_errno (ENOENT);
+-	  return -1;
+-	}
++        {
++          __set_errno (ENOENT);
++          return -1;
++        }
+     }
+ 
+   dlen = strlen (dir);
+   while (dlen > 1 && dir[dlen - 1] == '/')
+-    dlen--;			/* remove trailing slashes */
++    dlen--;                     /* remove trailing slashes */
+ 
+   /* check we have room for "${dir}/${pfx}XXXXXX\0" */
+   if (tmpl_len < dlen + 1 + plen + 6 + 1)
+@@ -163,39 +165,91 @@ __path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
+ }
+ #endif /* _LIBC */
+ 
++#if _LIBC
++static int try_tempname_len (char *, int, void *, int (*) (char *, void *),
++                             size_t);
++#endif
++
++static int
++try_file (char *tmpl, void *flags)
++{
++  int *openflags = flags;
++  return __open (tmpl,
++                 (*openflags & ~O_ACCMODE)
++                 | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
++}
++
++static int
++try_dir (char *tmpl, void *flags _GL_UNUSED)
++{
++  return __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
++}
++
++static int
++try_nocreate (char *tmpl, void *flags _GL_UNUSED)
++{
++  struct_stat64 st;
++
++  if (__lstat64 (tmpl, &st) == 0 || errno == EOVERFLOW)
++    __set_errno (EEXIST);
++  return errno == ENOENT ? 0 : -1;
++}
++
+ /* These are the characters used in temporary file names.  */
+ static const char letters[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ 
+ /* Generate a temporary file name based on TMPL.  TMPL must match the
+-   rules for mk[s]temp (i.e. end in "XXXXXX", possibly with a suffix).
++   rules for mk[s]temp (i.e., end in at least X_SUFFIX_LEN "X"s,
++   possibly with a suffix).
+    The name constructed does not exist at the time of the call to
+-   __gen_tempname.  TMPL is overwritten with the result.
++   this function.  TMPL is overwritten with the result.
+ 
+    KIND may be one of:
+-   __GT_NOCREATE:	simply verify that the name does not exist
+-			at the time of the call.
+-   __GT_FILE:		create the file using open(O_CREAT|O_EXCL)
+-			and return a read-write fd.  The file is mode 0600.
+-   __GT_DIR:		create a directory, which will be mode 0700.
++   __GT_NOCREATE:       simply verify that the name does not exist
++                        at the time of the call.
++   __GT_FILE:           create the file using open(O_CREAT|O_EXCL)
++                        and return a read-write fd.  The file is mode 0600.
++   __GT_DIR:            create a directory, which will be mode 0700.
+ 
+    We use a clever algorithm to get hard-to-predict names. */
++#ifdef _LIBC
++static
++#endif
+ int
+-__gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
++gen_tempname_len (char *tmpl, int suffixlen, int flags, int kind,
++                  size_t x_suffix_len)
+ {
+-  int len;
++  static int (*const tryfunc[]) (char *, void *) =
++    {
++      [__GT_FILE] = try_file,
++      [__GT_DIR] = try_dir,
++      [__GT_NOCREATE] = try_nocreate
++    };
++  return try_tempname_len (tmpl, suffixlen, &flags, tryfunc[kind],
++                           x_suffix_len);
++}
++
++#ifdef _LIBC
++static
++#endif
++int
++try_tempname_len (char *tmpl, int suffixlen, void *args,
++                  int (*tryfunc) (char *, void *), size_t x_suffix_len)
++{
++  size_t len;
+   char *XXXXXX;
+   unsigned int count;
+   int fd = -1;
+   int save_errno = errno;
+-  struct_stat64 st;
+ 
+   /* A lower bound on the number of temporary files to attempt to
+      generate.  The maximum total number of temporary file names that
+      can exist for a given template is 62**6.  It should never be
+      necessary to try all of these combinations.  Instead if a reasonable
+      number of names is tried (we define reasonable as 62**3) fail to
+-     give the system administrator the chance to remove the problems.  */
++     give the system administrator the chance to remove the problems.
++     This value requires that X_SUFFIX_LEN be at least 3.  */
+ #define ATTEMPTS_MIN (62 * 62 * 62)
+ 
+   /* The number of times to attempt to generate a temporary file.  To
+@@ -206,82 +260,75 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
+   unsigned int attempts = ATTEMPTS_MIN;
+ #endif
+ 
++  /* A random variable.  The initial value is used only the for fallback path
++     on 'random_bits' on 'getrandom' failure.  Its initial value tries to use
++     some entropy from the ASLR and ignore possible bits from the stack
++     alignment.  */
++  random_value v = ((uintptr_t) &v) / alignof (max_align_t);
++
++  /* How many random base-62 digits can currently be extracted from V.  */
++  int vdigits = 0;
++
++  /* Least unfair value for V.  If V is less than this, V can generate
++     BASE_62_DIGITS digits fairly.  Otherwise it might be biased.  */
++  random_value const unfair_min
++    = RANDOM_VALUE_MAX - RANDOM_VALUE_MAX % BASE_62_POWER;
++
+   len = strlen (tmpl);
+-  if (len < 6 + suffixlen || memcmp (&tmpl[len - 6 - suffixlen], "XXXXXX", 6))
++  if (len < x_suffix_len + suffixlen
++      || strspn (&tmpl[len - x_suffix_len - suffixlen], "X") < x_suffix_len)
+     {
+       __set_errno (EINVAL);
+       return -1;
+     }
+ 
+   /* This is where the Xs start.  */
+-  XXXXXX = &tmpl[len - 6 - suffixlen];
++  XXXXXX = &tmpl[len - x_suffix_len - suffixlen];
+ 
+-  uint64_t pid = (uint64_t) __getpid () << 32;
+   for (count = 0; count < attempts; ++count)
+     {
+-      uint64_t v;
+-      /* Get some more or less random data.  */
+-      RANDOM_BITS (v);
+-      v ^= pid;
+-
+-      /* Fill in the random bits.  */
+-      XXXXXX[0] = letters[v % 62];
+-      v /= 62;
+-      XXXXXX[1] = letters[v % 62];
+-      v /= 62;
+-      XXXXXX[2] = letters[v % 62];
+-      v /= 62;
+-      XXXXXX[3] = letters[v % 62];
+-      v /= 62;
+-      XXXXXX[4] = letters[v % 62];
+-      v /= 62;
+-      XXXXXX[5] = letters[v % 62];
+-
+-      switch (kind)
+-	{
+-	case __GT_FILE:
+-	  fd = __open (tmpl,
+-		       (flags & ~O_ACCMODE)
+-		       | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+-	  break;
+-
+-	case __GT_DIR:
+-	  fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
+-	  break;
+-
+-	case __GT_NOCREATE:
+-	  /* This case is backward from the other three.  __gen_tempname
+-	     succeeds if lstat fails because the name does not exist.
+-	     Note the continue to bypass the common logic at the bottom
+-	     of the loop.  */
+-	  if (__lstat64 (tmpl, &st) < 0)
+-	    {
+-	      if (errno == ENOENT)
+-		{
+-		  __set_errno (save_errno);
+-		  return 0;
+-		}
+-	      else
+-		/* Give up now. */
+-		return -1;
+-	    }
+-	  continue;
+-
+-	default:
+-	  assert (! "invalid KIND in __gen_tempname");
+-	  abort ();
+-	}
+-
++      for (size_t i = 0; i < x_suffix_len; i++)
++        {
++          if (vdigits == 0)
++            {
++              do
++                v = random_bits (v);
++              while (unfair_min <= v);
++
++              vdigits = BASE_62_DIGITS;
++            }
++
++          XXXXXX[i] = letters[v % 62];
++          v /= 62;
++          vdigits--;
++        }
++
++      fd = tryfunc (tmpl, args);
+       if (fd >= 0)
+-	{
+-	  __set_errno (save_errno);
+-	  return fd;
+-	}
++        {
++          __set_errno (save_errno);
++          return fd;
++        }
+       else if (errno != EEXIST)
+-	return -1;
++        return -1;
+     }
+ 
+   /* We got out of the loop because we ran out of combinations to try.  */
+   __set_errno (EEXIST);
+   return -1;
+ }
++
++int
++__gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
++{
++  return gen_tempname_len (tmpl, suffixlen, flags, kind, 6);
++}
++
++#if !_LIBC
++int
++try_tempname (char *tmpl, int suffixlen, void *args,
++              int (*tryfunc) (char *, void *))
++{
++  return try_tempname_len (tmpl, suffixlen, args, tryfunc, 6);
++}
++#endif
diff --git a/SOURCES/glibc-rh2089247-5.patch b/SOURCES/glibc-rh2089247-5.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ba26b89470687a973437f0369ebe3e8a9acaea50
--- /dev/null
+++ b/SOURCES/glibc-rh2089247-5.patch
@@ -0,0 +1,17 @@
+Downstream-only patch to use non-time64 identifiers in
+sysdeps/posix/tempname.c.  Upstream has switched to the time64
+symbols.
+
+diff --git a/sysdeps/posix/tempname.c b/sysdeps/posix/tempname.c
+index f199b25a7a227751..fcab9b26364021e4 100644
+--- a/sysdeps/posix/tempname.c
++++ b/sysdeps/posix/tempname.c
+@@ -56,6 +56,8 @@
+ #if _LIBC
+ # define struct_stat64 struct stat64
+ # define __secure_getenv __libc_secure_getenv
++# define __clock_gettime64 __clock_gettime
++# define __timespec64 timespec
+ #else
+ # define struct_stat64 struct stat
+ # define __gen_tempname gen_tempname
diff --git a/SOURCES/glibc-rh2089247-6.patch b/SOURCES/glibc-rh2089247-6.patch
new file mode 100644
index 0000000000000000000000000000000000000000..713b4575406b0a8fe525b634dec87ec4f9d68d61
--- /dev/null
+++ b/SOURCES/glibc-rh2089247-6.patch
@@ -0,0 +1,66 @@
+commit f430293d842031f2afc3013f156e1018065e480e
+Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+Date:   Tue Jan 12 09:17:09 2021 -0300
+
+    posix: consume less entropy on tempname
+    
+    The first getrandom is used only for __GT_NOCREATE, which is inherently
+    insecure and can use the entropy as a small improvement.  On the
+    second and later attempts it might help against DoS attacks.
+    
+    It sync with gnulib commit 854fbb81d91f7a0f2b463e7ace2499dee2f380f2.
+    
+    Checked on x86_64-linux-gnu.
+
+diff --git a/sysdeps/posix/tempname.c b/sysdeps/posix/tempname.c
+index fcab9b26364021e4..3435c4bf75a01f42 100644
+--- a/sysdeps/posix/tempname.c
++++ b/sysdeps/posix/tempname.c
+@@ -22,6 +22,7 @@
+ 
+ #include <sys/types.h>
+ #include <assert.h>
++#include <stdbool.h>
+ 
+ #include <errno.h>
+ 
+@@ -79,11 +80,11 @@ typedef uint_fast64_t random_value;
+ #define BASE_62_POWER (62LL * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62)
+ 
+ static random_value
+-random_bits (random_value var)
++random_bits (random_value var, bool use_getrandom)
+ {
+   random_value r;
+   /* Without GRND_NONBLOCK it can be blocked for minutes on some systems.  */
+-  if (__getrandom (&r, sizeof r, GRND_NONBLOCK) == sizeof r)
++  if (use_getrandom && __getrandom (&r, sizeof r, GRND_NONBLOCK) == sizeof r)
+     return r;
+ #if _LIBC || (defined CLOCK_MONOTONIC && HAVE_CLOCK_GETTIME)
+   /* Add entropy if getrandom did not work.  */
+@@ -271,6 +272,13 @@ try_tempname_len (char *tmpl, int suffixlen, void *args,
+   /* How many random base-62 digits can currently be extracted from V.  */
+   int vdigits = 0;
+ 
++  /* Whether to consume entropy when acquiring random bits.  On the
++     first try it's worth the entropy cost with __GT_NOCREATE, which
++     is inherently insecure and can use the entropy to make it a bit
++     less secure.  On the (rare) second and later attempts it might
++     help against DoS attacks.  */
++  bool use_getrandom = tryfunc == try_nocreate;
++
+   /* Least unfair value for V.  If V is less than this, V can generate
+      BASE_62_DIGITS digits fairly.  Otherwise it might be biased.  */
+   random_value const unfair_min
+@@ -294,7 +302,10 @@ try_tempname_len (char *tmpl, int suffixlen, void *args,
+           if (vdigits == 0)
+             {
+               do
+-                v = random_bits (v);
++                {
++                  v = random_bits (v, use_getrandom);
++                  use_getrandom = true;
++                }
+               while (unfair_min <= v);
+ 
+               vdigits = BASE_62_DIGITS;
diff --git a/SOURCES/glibc-rh2091553.patch b/SOURCES/glibc-rh2091553.patch
new file mode 100644
index 0000000000000000000000000000000000000000..376eb480045050a2b2d9e8137a77d369efcb1851
--- /dev/null
+++ b/SOURCES/glibc-rh2091553.patch
@@ -0,0 +1,41 @@
+From 82c7441f04e3c2a653ee29672731e040a1799c6b Mon Sep 17 00:00:00 2001
+From: Matheus Castanho <msc@linux.ibm.com>
+Date: Tue, 7 Jun 2022 10:27:26 -0300
+Subject: powerpc: Fix VSX register number on __strncpy_power9 [BZ #29197]
+
+__strncpy_power9 initializes VR 18 with zeroes to be used throughout the
+code, including when zero-padding the destination string. However, the
+v18 reference was mistakenly being used for stxv and stxvl, which take a
+VSX vector as operand. The code ended up using the uninitialized VSR 18
+register by mistake.
+
+Both occurrences have been changed to use the proper VSX number for VR 18
+(i.e. VSR 50).
+
+Tested on powerpc, powerpc64 and powerpc64le.
+
+Signed-off-by: Kewen Lin <linkw@gcc.gnu.org>
+(cherry picked from commit 0218463dd8265ed937622f88ac68c7d984fe0cfc)
+
+diff --git a/sysdeps/powerpc/powerpc64/le/power9/strncpy.S b/sysdeps/powerpc/powerpc64/le/power9/strncpy.S
+index 291941c1e5..5421525ace 100644
+--- a/sysdeps/powerpc/powerpc64/le/power9/strncpy.S
++++ b/sysdeps/powerpc/powerpc64/le/power9/strncpy.S
+@@ -352,7 +352,7 @@ L(zero_padding_loop):
+ 	cmpldi	cr6,r5,16	/* Check if length was reached.  */
+ 	ble	cr6,L(zero_padding_end)
+ 
+-	stxv	v18,0(r11)
++	stxv	32+v18,0(r11)
+ 	addi	r11,r11,16
+ 	addi	r5,r5,-16
+ 
+@@ -360,7 +360,7 @@ L(zero_padding_loop):
+ 
+ L(zero_padding_end):
+ 	sldi	r10,r5,56	/* stxvl wants size in top 8 bits  */
+-	stxvl	v18,r11,r10	/* Partial store  */
++	stxvl	32+v18,r11,r10	/* Partial store  */
+ 	blr
+ 
+ 	.align	4
diff --git a/SOURCES/glibc-rh2096189-1.patch b/SOURCES/glibc-rh2096189-1.patch
new file mode 100644
index 0000000000000000000000000000000000000000..33cc31002aa1d4fa2d8d5dcdd7c77b40ea33b408
--- /dev/null
+++ b/SOURCES/glibc-rh2096189-1.patch
@@ -0,0 +1,67 @@
+commit 62a321b12d0e397af88fa422db65079332f971dc
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Fri Jun 24 18:16:41 2022 +0200
+
+    support: Change non-address output format of support_format_dns_packet
+    
+    It makes sense to include the owner name (LHS) and record type in the
+    output, so that they can be checked for correctness.
+    
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+
+diff --git a/support/support_format_dns_packet.c b/support/support_format_dns_packet.c
+index 1170eafb0f008fee..ef862bc4c8d14af0 100644
+--- a/support/support_format_dns_packet.c
++++ b/support/support_format_dns_packet.c
+@@ -101,6 +101,17 @@ extract_name (struct in_buffer full, struct in_buffer *in, struct dname *value)
+   return true;
+ }
+ 
++static void
++extract_name_data (struct in_buffer full, struct in_buffer *rdata,
++                   const struct dname *owner, const char *typename, FILE *out)
++{
++  struct dname name;
++  if (extract_name (full, rdata, &name))
++    fprintf (out, "data: %s %s %s\n", owner->name, typename, name.name);
++  else
++    fprintf (out, "error: malformed CNAME/PTR record\n");
++}
++
+ char *
+ support_format_dns_packet (const unsigned char *buffer, size_t length)
+ {
+@@ -206,14 +217,11 @@ support_format_dns_packet (const unsigned char *buffer, size_t length)
+           }
+           break;
+         case T_CNAME:
++          extract_name_data (full, &rdata, &rname, "CNAME", mem.out);
++          break;
+         case T_PTR:
+-          {
+-            struct dname name;
+-            if (extract_name (full, &rdata, &name))
+-              fprintf (mem.out, "name: %s\n", name.name);
+-            else
+-              fprintf (mem.out, "error: malformed CNAME/PTR record\n");
+-          }
++          extract_name_data (full, &rdata, &rname, "PTR", mem.out);
++          break;
+         }
+     }
+ 
+diff --git a/support/tst-support_format_dns_packet.c b/support/tst-support_format_dns_packet.c
+index b1135eebc6c02d55..35f475fe86177772 100644
+--- a/support/tst-support_format_dns_packet.c
++++ b/support/tst-support_format_dns_packet.c
+@@ -85,8 +85,8 @@ test_multiple_cnames (void)
+     "\xc0\x00\x02\x01";
+   check_packet (packet, sizeof (packet) - 1, __func__,
+                 "name: www.example\n"
+-                "name: www1.example\n"
+-                "name: www2.example\n"
++                "data: www.example CNAME www1.example\n"
++                "data: www1.example CNAME www2.example\n"
+                 "address: 192.0.2.1\n");
+ }
+ 
diff --git a/SOURCES/glibc-rh2096189-2.patch b/SOURCES/glibc-rh2096189-2.patch
new file mode 100644
index 0000000000000000000000000000000000000000..201f0898920088bf03ed06592c1109f2ce6cc18c
--- /dev/null
+++ b/SOURCES/glibc-rh2096189-2.patch
@@ -0,0 +1,957 @@
+commit f282cdbe7f436c75864e5640a409a10485e9abb2
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Fri Jun 24 18:16:41 2022 +0200
+
+    resolv: Implement no-aaaa stub resolver option
+
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+
+Conflicts:
+	resolv/Makefile
+	  (missing partial libresolv integration downstream)
+	resolv/res-noaaaa.c
+	  (call ns_name_skip instead of __ns_name_skip (not available
+	  downstream) and ns_name_unpack instead of __ns_name_unpack
+	  (avoid PLT))
+	resolv/res_debug.c
+	resolv/res_init.c
+	resolv/resolv.h
+	resolv/tst-resolv-res_init-skeleton.c
+	  (missing trust-ad support downstream)
+
+diff --git a/resolv/Makefile b/resolv/Makefile
+index cee5225f8933f245..ab8ad49b5318ad41 100644
+--- a/resolv/Makefile
++++ b/resolv/Makefile
+@@ -57,6 +57,7 @@ tests += \
+   tst-resolv-binary \
+   tst-resolv-edns \
+   tst-resolv-network \
++  tst-resolv-noaaaa \
+   tst-resolv-nondecimal \
+   tst-resolv-res_init-multi \
+   tst-resolv-search \
+@@ -110,7 +111,7 @@ libresolv-routines := res_comp res_debug \
+ 		      res_data res_mkquery res_query res_send		\
+ 		      inet_net_ntop inet_net_pton inet_neta base64	\
+ 		      ns_parse ns_name ns_netint ns_ttl ns_print	\
+-		      ns_samedomain ns_date res_enable_icmp \
++		      ns_samedomain ns_date res_enable_icmp res-noaaaa  \
+ 		      compat-hooks compat-gethnamaddr
+ 
+ libanl-routines := gai_cancel gai_error gai_misc gai_notify gai_suspend \
+@@ -200,6 +201,7 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \
+   $(shared-thread-library)
+ $(objpfx)tst-resolv-res_init-thread: $(libdl) $(objpfx)libresolv.so \
+   $(shared-thread-library)
++$(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library)
+ $(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library)
+ $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
+ $(objpfx)tst-resolv-rotate: $(objpfx)libresolv.so $(shared-thread-library)
+diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
+index 99c3b61e1cee4d42..ff0a0b6f7f1f4703 100644
+--- a/resolv/nss_dns/dns-host.c
++++ b/resolv/nss_dns/dns-host.c
+@@ -123,6 +123,14 @@ static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1,
+ 				       char *buffer, size_t buflen,
+ 				       int *errnop, int *h_errnop,
+ 				       int32_t *ttlp);
++static enum nss_status gaih_getanswer_noaaaa (const querybuf *answer1,
++					      int anslen1,
++					      const char *qname,
++					      struct gaih_addrtuple **pat,
++					      char *buffer, size_t buflen,
++					      int *errnop, int *h_errnop,
++					      int32_t *ttlp);
++
+ 
+ static enum nss_status gethostbyname3_context (struct resolv_context *ctx,
+ 					       const char *name, int af,
+@@ -367,17 +375,31 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
+   int resplen2 = 0;
+   int ans2p_malloced = 0;
+ 
++
+   int olderr = errno;
+-  int n = __res_context_search (ctx, name, C_IN, T_QUERY_A_AND_AAAA,
++  int n;
++
++  if ((ctx->resp->options & RES_NOAAAA) == 0)
++    {
++      n = __res_context_search (ctx, name, C_IN, T_QUERY_A_AND_AAAA,
+ 				host_buffer.buf->buf, 2048, &host_buffer.ptr,
+ 				&ans2p, &nans2p, &resplen2, &ans2p_malloced);
+-  if (n >= 0)
+-    {
+-      status = gaih_getanswer (host_buffer.buf, n, (const querybuf *) ans2p,
+-			       resplen2, name, pat, buffer, buflen,
+-			       errnop, herrnop, ttlp);
++      if (n >= 0)
++	status = gaih_getanswer (host_buffer.buf, n, (const querybuf *) ans2p,
++				 resplen2, name, pat, buffer, buflen,
++				 errnop, herrnop, ttlp);
+     }
+   else
++    {
++      n = __res_context_search (ctx, name, C_IN, T_A,
++				host_buffer.buf->buf, 2048, NULL,
++				NULL, NULL, NULL, NULL);
++      if (n >= 0)
++	status = gaih_getanswer_noaaaa (host_buffer.buf, n,
++					name, pat, buffer, buflen,
++					errnop, herrnop, ttlp);
++    }
++  if (n < 0)
+     {
+       switch (errno)
+ 	{
+@@ -1386,3 +1408,21 @@ gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
+ 
+   return status;
+ }
++
++/* Variant of gaih_getanswer without a second (AAAA) response.  */
++static enum nss_status
++gaih_getanswer_noaaaa (const querybuf *answer1, int anslen1, const char *qname,
++		       struct gaih_addrtuple **pat,
++		       char *buffer, size_t buflen,
++		       int *errnop, int *h_errnop, int32_t *ttlp)
++{
++  int first = 1;
++
++  enum nss_status status = NSS_STATUS_NOTFOUND;
++  if (anslen1 > 0)
++    status = gaih_getanswer_slice (answer1, anslen1, qname,
++				   &pat, &buffer, &buflen,
++				   errnop, h_errnop, ttlp,
++				   &first);
++  return status;
++}
+diff --git a/resolv/res-noaaaa.c b/resolv/res-noaaaa.c
+new file mode 100644
+index 0000000000000000..e2a13cf38a74c160
+--- /dev/null
++++ b/resolv/res-noaaaa.c
+@@ -0,0 +1,143 @@
++/* Implement suppression of AAAA queries.
++   Copyright (C) 2022 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/>.  */
++
++#include <resolv.h>
++#include <string.h>
++#include <resolv-internal.h>
++#include <resolv_context.h>
++#include <arpa/nameser.h>
++
++/* Returns true if the question type at P matches EXPECTED, and the
++   class is IN.  */
++static bool
++qtype_matches (const unsigned char *p, int expected)
++{
++  /* This assumes that T_A/C_IN constants are less than 256, which
++     they are.  */
++  return p[0] == 0 && p[1] == expected && p[2] == 0 && p[3] == C_IN;
++}
++
++/* Handle RES_NOAAAA translation of AAAA queries.  To produce a Name
++   Error (NXDOMAIN) repsonse for domain names that do not exist, it is
++   still necessary to send a query.  Using question type A is a
++   conservative choice.  In the returned answer, it is necessary to
++   switch back the question type to AAAA.  */
++bool
++__res_handle_no_aaaa (struct resolv_context *ctx,
++                      const unsigned char *buf, int buflen,
++                      unsigned char *ans, int anssiz, int *result)
++{
++  /* AAAA mode is not active, or the query looks invalid (will not be
++     able to be parsed).  */
++  if ((ctx->resp->options & RES_NOAAAA) == 0
++      || buflen <= sizeof (HEADER))
++    return false;
++
++  /* The replacement A query is produced here.  */
++  struct
++  {
++    HEADER header;
++    unsigned char question[NS_MAXCDNAME + 4];
++  } replacement;
++  memcpy (&replacement.header, buf, sizeof (replacement.header));
++
++  if (replacement.header.qr
++      || replacement.header.opcode != 0
++      || replacement.header.rcode != 0
++      || ntohs (replacement.header.qdcount) != 1
++      || ntohs (replacement.header.ancount) != 0
++      || ntohs (replacement.header.nscount) != 0)
++    /* Not a well-formed question.  Let the core resolver code produce
++       the proper error.  */
++    return false;
++
++  /* Disable EDNS0.  */
++  replacement.header.arcount = htons (0);
++
++  /* Extract the QNAME.  */
++  int ret = ns_name_unpack (buf, buf + buflen, buf + sizeof (HEADER),
++                              replacement.question, NS_MAXCDNAME);
++  if (ret < 0)
++    /* Format error.  */
++    return false;
++
++  /* Compute the end of the question name.  */
++  const unsigned char *after_question = buf + sizeof (HEADER) + ret;
++
++  /* Check that we are dealing with an AAAA query.  */
++  if (buf + buflen - after_question < 4
++      || !qtype_matches (after_question, T_AAAA))
++    return false;
++
++  /* Find the place to store the type/class data in the replacement
++     query.  */
++  after_question = replacement.question;
++  /* This cannot fail because ns_name_unpack above produced a valid
++     domain name.  */
++  (void) ns_name_skip (&after_question, &replacement.question[NS_MAXCDNAME]);
++  unsigned char *start_of_query = (unsigned char *) &replacement;
++  const unsigned char *end_of_query = after_question + 4;
++
++  /* Produce an A/IN query.  */
++  {
++    unsigned char *p = (unsigned char *) after_question;
++    p[0] = 0;
++    p[1] = T_A;
++    p[2] = 0;
++    p[3] = C_IN;
++  }
++
++  /* Clear the output buffer, to avoid reading undefined data when
++     rewriting the result from A to AAAA.  */
++  memset (ans, 0, anssiz);
++
++  /* Always perform the message translation, independent of the error
++     code.  */
++  ret = __res_context_send (ctx,
++                            start_of_query, end_of_query - start_of_query,
++                            NULL, 0, ans, anssiz,
++                            NULL, NULL, NULL, NULL, NULL);
++
++  /* Patch in the AAAA question type if there is room and the A query
++     type was received.  */
++  after_question = ans + sizeof (HEADER);
++  if (ns_name_skip (&after_question, ans + anssiz) == 0
++      && ans + anssiz - after_question >= 4
++      && qtype_matches (after_question, T_A))
++    {
++      ((unsigned char *) after_question)[1] = T_AAAA;
++
++      /* Create an aligned copy of the header.  Hide all data except
++         the question from the response.  Put back the header.  There is
++         no need to change the response code.  The zero answer count turns
++         a positive response with data into a no-data response.  */
++      memcpy (&replacement.header, ans, sizeof (replacement.header));
++      replacement.header.ancount = htons (0);
++      replacement.header.nscount = htons (0);
++      replacement.header.arcount = htons (0);
++      memcpy (ans, &replacement.header, sizeof (replacement.header));
++
++      /* Truncate the reply.  */
++      if (ret <= 0)
++        *result = ret;
++      else
++        *result = after_question - ans + 4;
++    }
++
++  return true;
++}
+diff --git a/resolv/res_debug.c b/resolv/res_debug.c
+index 7681ad4639d8a7bc..43b3b1bfe4afdcaf 100644
+--- a/resolv/res_debug.c
++++ b/resolv/res_debug.c
+@@ -615,6 +615,7 @@ p_option(u_long option) {
+ 	case RES_USE_DNSSEC:	return "dnssec";
+ 	case RES_NOTLDQUERY:	return "no-tld-query";
+ 	case RES_NORELOAD:	return "no-reload";
++	case RES_NOAAAA:	return "no-aaaa";
+ 				/* XXX nonreentrant */
+ 	default:		sprintf(nbuf, "?0x%lx?", (u_long)option);
+ 				return (nbuf);
+diff --git a/resolv/res_init.c b/resolv/res_init.c
+index bb99ddeec4d6d47f..20434bfe147a3fb5 100644
+--- a/resolv/res_init.c
++++ b/resolv/res_init.c
+@@ -694,7 +694,8 @@ res_setoptions (struct resolv_conf_parser *parser, const char *options)
+             { STRnLEN ("no_tld_query"), 0, RES_NOTLDQUERY },
+             { STRnLEN ("no-tld-query"), 0, RES_NOTLDQUERY },
+             { STRnLEN ("no-reload"), 0, RES_NORELOAD },
+-            { STRnLEN ("use-vc"), 0, RES_USEVC }
++            { STRnLEN ("use-vc"), 0, RES_USEVC },
++            { STRnLEN ("no-aaaa"), 0, RES_NOAAAA },
+           };
+ #define noptions (sizeof (options) / sizeof (options[0]))
+           for (int i = 0; i < noptions; ++i)
+diff --git a/resolv/res_query.c b/resolv/res_query.c
+index ebbe5a6a4ed86abe..d94966a47c3dac90 100644
+--- a/resolv/res_query.c
++++ b/resolv/res_query.c
+@@ -204,10 +204,26 @@ __res_context_query (struct resolv_context *ctx, const char *name,
+ 			free (buf);
+ 		return (n);
+ 	}
+-	assert (answerp == NULL || (void *) *answerp == (void *) answer);
+-	n = __res_context_send (ctx, query1, nquery1, query2, nquery2, answer,
+-				anslen, answerp, answerp2, nanswerp2, resplen2,
+-				answerp2_malloced);
++
++	/* Suppress AAAA lookups if required.  __res_handle_no_aaaa
++	   checks RES_NOAAAA first, so avoids parsing the
++	   just-generated query packet in most cases.  nss_dns avoids
++	   using T_QUERY_A_AND_AAAA in RES_NOAAAA mode, so there is no
++	   need to handle it here.  */
++	if (type == T_AAAA && __res_handle_no_aaaa (ctx, query1, nquery1,
++						    answer, anslen, &n))
++	  /* There must be no second query for AAAA queries.  The code
++	     below is still needed to translate NODATA responses.  */
++	  assert (query2 == NULL);
++	else
++	  {
++	    assert (answerp == NULL || (void *) *answerp == (void *) answer);
++	    n = __res_context_send (ctx, query1, nquery1, query2, nquery2,
++				    answer, anslen,
++				    answerp, answerp2, nanswerp2, resplen2,
++				    answerp2_malloced);
++	  }
++
+ 	if (use_malloc)
+ 		free (buf);
+ 	if (n < 0) {
+diff --git a/resolv/res_send.c b/resolv/res_send.c
+index 55e7fa438e7baac1..2e676bff0edf0cdc 100644
+--- a/resolv/res_send.c
++++ b/resolv/res_send.c
+@@ -550,8 +550,13 @@ context_send_common (struct resolv_context *ctx,
+       RES_SET_H_ERRNO (&_res, NETDB_INTERNAL);
+       return -1;
+     }
+-  int result = __res_context_send (ctx, buf, buflen, NULL, 0, ans, anssiz,
+-				   NULL, NULL, NULL, NULL, NULL);
++
++  int result;
++  if (__res_handle_no_aaaa (ctx, buf, buflen, ans, anssiz, &result))
++    return result;
++
++  result = __res_context_send (ctx, buf, buflen, NULL, 0, ans, anssiz,
++			       NULL, NULL, NULL, NULL, NULL);
+   __resolv_context_put (ctx);
+   return result;
+ }
+diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h
+index 0878f6830f2a08ff..4564f6ba2f7202f5 100644
+--- a/resolv/resolv-internal.h
++++ b/resolv/resolv-internal.h
+@@ -79,6 +79,14 @@ int __res_context_send (struct resolv_context *, const unsigned char *, int,
+                         int, unsigned char **, unsigned char **,
+                         int *, int *, int *) attribute_hidden;
+ 
++/* Return true if the query has been handled in RES_NOAAAA mode.  For
++   that, RES_NOAAAA must be active, and the question type must be AAAA.
++   The caller is expected to return *RESULT as the return value.  */
++bool __res_handle_no_aaaa (struct resolv_context *ctx,
++                           const unsigned char *buf, int buflen,
++                           unsigned char *ans, int anssiz, int *result)
++  attribute_hidden;
++
+ /* Internal function similar to res_hostalias.  */
+ const char *__res_context_hostalias (struct resolv_context *,
+                                      const char *, char *, size_t);
+diff --git a/resolv/resolv.h b/resolv/resolv.h
+index 80a523e5e40982ad..0f7298f395a829d3 100644
+--- a/resolv/resolv.h
++++ b/resolv/resolv.h
+@@ -135,6 +135,7 @@ struct res_sym {
+ #define RES_NOTLDQUERY	0x01000000	/* Do not look up unqualified name
+ 					   as a TLD.  */
+ #define RES_NORELOAD    0x02000000 /* No automatic configuration reload.  */
++#define RES_NOAAAA      0x08000000 /* Suppress AAAA queries.  */
+ 
+ #define RES_DEFAULT	(RES_RECURSE|RES_DEFNAMES|RES_DNSRCH)
+ 
+diff --git a/resolv/tst-resolv-noaaaa.c b/resolv/tst-resolv-noaaaa.c
+new file mode 100644
+index 0000000000000000..56b25f88a58ad286
+--- /dev/null
++++ b/resolv/tst-resolv-noaaaa.c
+@@ -0,0 +1,533 @@
++/* Test the RES_NOAAAA resolver option.
++   Copyright (C) 2022 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/>.  */
++
++#include <errno.h>
++#include <netdb.h>
++#include <resolv.h>
++#include <stdlib.h>
++#include <support/check.h>
++#include <support/check_nss.h>
++#include <support/resolv_test.h>
++#include <support/support.h>
++
++/* Used to keep track of the number of queries.  */
++static volatile unsigned int queries;
++
++static void
++response (const struct resolv_response_context *ctx,
++          struct resolv_response_builder *b,
++          const char *qname, uint16_t qclass, uint16_t qtype)
++{
++  /* Each test should only send one query.  */
++  ++queries;
++  TEST_COMPARE (queries, 1);
++
++  /* AAAA queries are supposed to be disabled.  */
++  TEST_VERIFY (qtype != T_AAAA);
++  TEST_COMPARE (qclass, C_IN);
++
++  /* The only other query type besides A is PTR.  */
++  if (qtype != T_A)
++    TEST_COMPARE (qtype, T_PTR);
++
++  int an, ns, ar;
++  char *tail;
++  if (sscanf (qname, "an%d.ns%d.ar%d.%ms", &an, &ns, &ar, &tail) != 4)
++    FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
++  TEST_COMPARE_STRING (tail, "example");
++  free (tail);
++
++  if (an < 0 || ns < 0 || ar < 0)
++    {
++      struct resolv_response_flags flags = { .rcode = NXDOMAIN, };
++      resolv_response_init (b, flags);
++      resolv_response_add_question (b, qname, qclass, qtype);
++      return;
++    }
++
++  struct resolv_response_flags flags = {};
++  resolv_response_init (b, flags);
++  resolv_response_add_question (b, qname, qclass, qtype);
++
++  resolv_response_section (b, ns_s_an);
++  for (int i = 0; i < an; ++i)
++    {
++      resolv_response_open_record (b, qname, qclass, qtype, 60);
++      switch (qtype)
++        {
++        case T_A:
++          char ipv4[4] = {192, 0, 2, i + 1};
++          resolv_response_add_data (b, &ipv4, sizeof (ipv4));
++          break;
++
++        case T_PTR:
++          char *name = xasprintf ("ptr-%d", i);
++          resolv_response_add_name (b, name);
++          free (name);
++          break;
++        }
++      resolv_response_close_record (b);
++    }
++
++  resolv_response_section (b, ns_s_ns);
++  for (int i = 0; i < ns; ++i)
++    {
++      resolv_response_open_record (b, qname, qclass, T_NS, 60);
++      char *name = xasprintf ("ns%d.example.net", i);
++      resolv_response_add_name (b, name);
++      free (name);
++      resolv_response_close_record (b);
++    }
++
++  resolv_response_section (b, ns_s_ar);
++  int addr = 1;
++  for (int i = 0; i < ns; ++i)
++    {
++      char *name = xasprintf ("ns%d.example.net", i);
++      for (int j = 0; j < ar; ++j)
++        {
++          resolv_response_open_record (b, name, qclass, T_A, 60);
++          char ipv4[4] = {192, 0, 2, addr};
++          resolv_response_add_data (b, &ipv4, sizeof (ipv4));
++          resolv_response_close_record (b);
++
++          resolv_response_open_record (b, name, qclass, T_AAAA, 60);
++          char ipv6[16]
++            = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, addr};
++          resolv_response_add_data (b, &ipv6, sizeof (ipv6));
++          resolv_response_close_record (b);
++
++          ++addr;
++        }
++      free (name);
++    }
++}
++
++/* Number of modes.  Lowest bit encodes *n* function vs implicit _res
++   argument.  The mode numbers themselves are arbitrary.  */
++enum { mode_count = 8 };
++
++/* res_send-like modes do not perform error translation.  */
++enum { first_send_mode = 6 };
++
++static int
++libresolv_query (unsigned int mode, const char *qname, uint16_t qtype,
++                 unsigned char *buf, size_t buflen)
++{
++  int saved_errno = errno;
++
++  TEST_VERIFY_EXIT (mode < mode_count);
++
++  switch (mode)
++    {
++    case 0:
++      return res_query (qname, C_IN, qtype, buf, buflen);
++    case 1:
++      return res_nquery (&_res, qname, C_IN, qtype, buf, buflen);
++    case 2:
++      return res_search (qname, C_IN, qtype, buf, buflen);
++    case 3:
++      return res_nsearch (&_res, qname, C_IN, qtype, buf, buflen);
++    case 4:
++      return res_querydomain (qname, "", C_IN, qtype, buf, buflen);
++    case 5:
++      return res_nquerydomain (&_res, qname, "", C_IN, qtype, buf, buflen);
++    case 6:
++      {
++        unsigned char querybuf[512];
++        int ret = res_mkquery (QUERY, qname, C_IN, qtype,
++                               NULL, 0, NULL, querybuf, sizeof (querybuf));
++        TEST_VERIFY_EXIT (ret > 0);
++        errno = saved_errno;
++        return res_send (querybuf, ret, buf, buflen);
++      }
++    case 7:
++      {
++        unsigned char querybuf[512];
++        int ret = res_nmkquery (&_res, QUERY, qname, C_IN, qtype,
++                                NULL, 0, NULL, querybuf, sizeof (querybuf));
++        TEST_VERIFY_EXIT (ret > 0);
++        errno = saved_errno;
++        return res_nsend (&_res, querybuf, ret, buf, buflen);
++      }
++    }
++  __builtin_unreachable ();
++}
++
++static int
++do_test (void)
++{
++  struct resolv_test *obj = resolv_test_start
++    ((struct resolv_redirect_config)
++     {
++       .response_callback = response
++     });
++
++  _res.options |= RES_NOAAAA;
++
++  check_hostent ("an1.ns2.ar1.example",
++                 gethostbyname ("an1.ns2.ar1.example"),
++                 "name: an1.ns2.ar1.example\n"
++                 "address: 192.0.2.1\n");
++  queries = 0;
++  check_hostent ("an0.ns2.ar1.example",
++                 gethostbyname ("an0.ns2.ar1.example"),
++                 "error: NO_ADDRESS\n");
++  queries = 0;
++  check_hostent ("an-1.ns2.ar1.example",
++                 gethostbyname ("an-1.ns2.ar1.example"),
++                 "error: HOST_NOT_FOUND\n");
++  queries = 0;
++
++  check_hostent ("an1.ns2.ar1.example AF_INET",
++                 gethostbyname2 ("an1.ns2.ar1.example", AF_INET),
++                 "name: an1.ns2.ar1.example\n"
++                 "address: 192.0.2.1\n");
++  queries = 0;
++  check_hostent ("an0.ns2.ar1.example AF_INET",
++                 gethostbyname2 ("an0.ns2.ar1.example", AF_INET),
++                 "error: NO_ADDRESS\n");
++  queries = 0;
++  check_hostent ("an-1.ns2.ar1.example AF_INET",
++                 gethostbyname2 ("an-1.ns2.ar1.example", AF_INET),
++                 "error: HOST_NOT_FOUND\n");
++  queries = 0;
++
++  check_hostent ("an1.ns2.ar1.example AF_INET6",
++                 gethostbyname2 ("an1.ns2.ar1.example", AF_INET6),
++                 "error: NO_ADDRESS\n");
++  queries = 0;
++  check_hostent ("an0.ns2.ar1.example AF_INET6",
++                 gethostbyname2 ("an0.ns2.ar1.example", AF_INET6),
++                 "error: NO_ADDRESS\n");
++  queries = 0;
++  check_hostent ("an-1.ns2.ar1.example AF_INET6",
++                 gethostbyname2 ("an-1.ns2.ar1.example", AF_INET6),
++                 "error: HOST_NOT_FOUND\n");
++  queries = 0;
++
++  /* Multiple addresses.  */
++  check_hostent ("an2.ns0.ar0.example",
++                 gethostbyname ("an2.ns0.ar0.example"),
++                 "name: an2.ns0.ar0.example\n"
++                 "address: 192.0.2.1\n"
++                 "address: 192.0.2.2\n");
++  queries = 0;
++  check_hostent ("an2.ns0.ar0.example AF_INET6",
++                 gethostbyname2 ("an2.ns0.ar0.example", AF_INET6),
++                 "error: NO_ADDRESS\n");
++  queries = 0;
++
++  /* getaddrinfo checks with one address.  */
++  struct addrinfo *ai;
++  int ret;
++  ret = getaddrinfo ("an1.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_INET,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an1.ns2.ar1.example (AF_INET)", ai, ret,
++                  "address: STREAM/TCP 192.0.2.1 80\n");
++  freeaddrinfo (ai);
++  queries = 0;
++  ret = getaddrinfo ("an1.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_INET6,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an1.ns2.ar1.example (AF_INET6)", ai, ret,
++                  "error: No address associated with hostname\n");
++  queries = 0;
++  ret = getaddrinfo ("an1.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_UNSPEC,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an1.ns2.ar1.example (AF_UNSPEC)", ai, ret,
++                  "address: STREAM/TCP 192.0.2.1 80\n");
++  freeaddrinfo (ai);
++  queries = 0;
++
++  /* getaddrinfo checks with three addresses.  */
++  ret = getaddrinfo ("an3.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_INET,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an3.ns2.ar1.example (AF_INET)", ai, ret,
++                  "address: STREAM/TCP 192.0.2.1 80\n"
++                  "address: STREAM/TCP 192.0.2.2 80\n"
++                  "address: STREAM/TCP 192.0.2.3 80\n");
++  freeaddrinfo (ai);
++  queries = 0;
++  ret = getaddrinfo ("an3.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_INET6,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an3.ns2.ar1.example (AF_INET6)", ai, ret,
++                  "error: No address associated with hostname\n");
++  queries = 0;
++  ret = getaddrinfo ("an3.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_UNSPEC,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an3.ns2.ar1.example (AF_UNSPEC)", ai, ret,
++                  "address: STREAM/TCP 192.0.2.1 80\n"
++                  "address: STREAM/TCP 192.0.2.2 80\n"
++                  "address: STREAM/TCP 192.0.2.3 80\n");
++  freeaddrinfo (ai);
++  queries = 0;
++
++  /* getaddrinfo checks with no address.  */
++  ret = getaddrinfo ("an0.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_INET,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an0.ns2.ar1.example (AF_INET)", ai, ret,
++                  "error: No address associated with hostname\n");
++  queries = 0;
++  ret = getaddrinfo ("an0.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_INET6,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an0.ns2.ar1.example (AF_INET6)", ai, ret,
++                  "error: No address associated with hostname\n");
++  queries = 0;
++  ret = getaddrinfo ("an0.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_UNSPEC,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an-1.ns2.ar1.example (AF_UNSPEC)", ai, ret,
++                  "error: No address associated with hostname\n");
++  queries = 0;
++
++  /* getaddrinfo checks with NXDOMAIN.  */
++  ret = getaddrinfo ("an-1.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_INET,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an-1.ns2.ar1.example (AF_INET)", ai, ret,
++                  "error: Name or service not known\n");
++  queries = 0;
++  ret = getaddrinfo ("an-1.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_INET6,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an-1.ns2.ar1.example (AF_INET6)", ai, ret,
++                  "error: Name or service not known\n");
++  queries = 0;
++  ret = getaddrinfo ("an-1.ns2.ar1.example", "80",
++                     &(struct addrinfo)
++                     {
++                       .ai_family = AF_UNSPEC,
++                       .ai_socktype = SOCK_STREAM,
++                     }, &ai);
++  check_addrinfo ("an-1.ns2.ar1.example (AF_UNSPEC)", ai, ret,
++                  "error: Name or service not known\n");
++  queries = 0;
++
++  for (unsigned int mode = 0; mode < mode_count; ++mode)
++    {
++      unsigned char *buf;
++      int ret;
++
++      /* Response for A.  */
++      buf = malloc (512);
++      ret = libresolv_query (mode, "an1.ns2.ar1.example", T_A, buf, 512);
++      TEST_VERIFY_EXIT (ret > 0);
++      check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
++                        "name: an1.ns2.ar1.example\n"
++                        "address: 192.0.2.1\n");
++      free (buf);
++      queries = 0;
++
++      /* NODATA response for A.  */
++      buf = malloc (512);
++      errno = 0;
++      ret = libresolv_query (mode, "an0.ns2.ar1.example", T_A, buf, 512);
++      if (mode < first_send_mode)
++        {
++          TEST_COMPARE (ret, -1);
++          TEST_COMPARE (errno, 0);
++          TEST_COMPARE (h_errno, NO_ADDRESS);
++        }
++      else
++        {
++          TEST_VERIFY_EXIT (ret > 0);
++          TEST_COMPARE (((HEADER *)buf)->rcode, 0);
++          check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
++                            "name: an0.ns2.ar1.example\n");
++        }
++      free (buf);
++      queries = 0;
++
++      /* NXDOMAIN response for A.  */
++      buf = malloc (512);
++      errno = 0;
++      ret = libresolv_query (mode, "an-1.ns2.ar1.example", T_A, buf, 512);
++      if (mode < first_send_mode)
++        {
++          TEST_COMPARE (ret, -1);
++          TEST_COMPARE (errno, 0);
++          TEST_COMPARE (h_errno, HOST_NOT_FOUND);
++        }
++      else
++        {
++          TEST_VERIFY_EXIT (ret > 0);
++          TEST_COMPARE (((HEADER *)buf)->rcode, NXDOMAIN);
++          check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
++                            "name: an-1.ns2.ar1.example\n");
++        }
++      free (buf);
++      queries = 0;
++
++      /* Response for PTR.  */
++      buf = malloc (512);
++      ret = libresolv_query (mode, "an1.ns2.ar1.example", T_PTR, buf, 512);
++      TEST_VERIFY_EXIT (ret > 0);
++      check_dns_packet ("an1.ns2.ar1.example PTR", buf, ret,
++                        "name: an1.ns2.ar1.example\n"
++                        "data: an1.ns2.ar1.example PTR ptr-0\n");
++      free (buf);
++      queries = 0;
++
++      /* NODATA response for PTR.  */
++      buf = malloc (512);
++      errno = 0;
++      ret = libresolv_query (mode, "an0.ns2.ar1.example", T_PTR, buf, 512);
++      if (mode < first_send_mode)
++        {
++          TEST_COMPARE (ret, -1);
++          TEST_COMPARE (errno, 0);
++          TEST_COMPARE (h_errno, NO_ADDRESS);
++        }
++      else
++        {
++          TEST_VERIFY_EXIT (ret > 0);
++          TEST_COMPARE (((HEADER *)buf)->rcode, 0);
++          check_dns_packet ("an1.ns2.ar1.example PTR", buf, ret,
++                            "name: an0.ns2.ar1.example\n");
++        }
++      free (buf);
++      queries = 0;
++
++      /* NXDOMAIN response for PTR.  */
++      buf = malloc (512);
++      errno = 0;
++      ret = libresolv_query (mode, "an-1.ns2.ar1.example", T_PTR, buf, 512);
++      if (mode < first_send_mode)
++        {
++          TEST_COMPARE (ret, -1);
++          TEST_COMPARE (errno, 0);
++          TEST_COMPARE (h_errno, HOST_NOT_FOUND);
++        }
++      else
++        {
++          TEST_VERIFY_EXIT (ret > 0);
++          TEST_COMPARE (((HEADER *)buf)->rcode, NXDOMAIN);
++          check_dns_packet ("an1.ns2.ar1.example PTR", buf, ret,
++                            "name: an-1.ns2.ar1.example\n");
++        }
++      free (buf);
++      queries = 0;
++
++      /* NODATA response for AAAA.  */
++      buf = malloc (512);
++      errno = 0;
++      ret = libresolv_query (mode, "an1.ns2.ar1.example", T_AAAA, buf, 512);
++      if (mode < first_send_mode)
++        {
++          TEST_COMPARE (ret, -1);
++          TEST_COMPARE (errno, 0);
++          TEST_COMPARE (h_errno, NO_ADDRESS);
++        }
++      else
++        {
++          TEST_VERIFY_EXIT (ret > 0);
++          TEST_COMPARE (((HEADER *)buf)->rcode, 0);
++          check_dns_packet ("an1.ns2.ar1.example A", buf, ret,
++                            "name: an1.ns2.ar1.example\n");
++        }
++      free (buf);
++      queries = 0;
++
++      /* NODATA response for AAAA (original is already NODATA).  */
++      buf = malloc (512);
++      errno = 0;
++      ret = libresolv_query (mode, "an0.ns2.ar1.example", T_AAAA, buf, 512);
++      if (mode < first_send_mode)
++        {
++          TEST_COMPARE (ret, -1);
++          TEST_COMPARE (errno, 0);
++          TEST_COMPARE (h_errno, NO_ADDRESS);
++        }
++      else
++        {
++          TEST_VERIFY_EXIT (ret > 0);
++          TEST_COMPARE (((HEADER *)buf)->rcode, 0);
++          check_dns_packet ("an0.ns2.ar1.example A", buf, ret,
++                            "name: an0.ns2.ar1.example\n");
++        }
++      free (buf);
++      queries = 0;
++
++      /* NXDOMAIN response.  */
++      buf = malloc (512);
++      errno = 0;
++      ret = libresolv_query (mode, "an-1.ns2.ar1.example", T_AAAA, buf, 512);
++      if (mode < first_send_mode)
++        {
++          TEST_COMPARE (ret, -1);
++          TEST_COMPARE (errno, 0);
++          TEST_COMPARE (h_errno, HOST_NOT_FOUND);
++        }
++      else
++        {
++          TEST_VERIFY_EXIT (ret > 0);
++          TEST_COMPARE (((HEADER *)buf)->rcode, NXDOMAIN);
++          check_dns_packet ("an-1.ns2.ar1.example A", buf, ret,
++                            "name: an-1.ns2.ar1.example\n");
++        }
++      free (buf);
++      queries = 0;
++    }
++
++  resolv_test_end (obj);
++
++  return 0;
++}
++
++#include <support/test-driver.c>
+diff --git a/resolv/tst-resolv-res_init-skeleton.c b/resolv/tst-resolv-res_init-skeleton.c
+index a5061e6d4fb98311..7d8758a99e180d97 100644
+--- a/resolv/tst-resolv-res_init-skeleton.c
++++ b/resolv/tst-resolv-res_init-skeleton.c
+@@ -129,6 +129,7 @@ print_resp (FILE *fp, res_state resp)
+                            "single-request-reopen");
+         print_option_flag (fp, &options, RES_NOTLDQUERY, "no-tld-query");
+         print_option_flag (fp, &options, RES_NORELOAD, "no-reload");
++        print_option_flag (fp, &options, RES_NOAAAA, "no-aaaa");
+         fputc ('\n', fp);
+         if (options != 0)
+           fprintf (fp, "; error: unresolved option bits: 0x%x\n", options);
+@@ -713,6 +714,15 @@ struct test_case test_cases[] =
+      "nameserver 192.0.2.1\n"
+      "; nameserver[0]: [192.0.2.1]:53\n"
+     },
++    {.name = "no-aaaa flag",
++     .conf = "options no-aaaa\n"
++     "nameserver 192.0.2.1\n",
++     .expected = "options no-aaaa\n"
++     "search example.com\n"
++     "; search[0]: example.com\n"
++     "nameserver 192.0.2.1\n"
++     "; nameserver[0]: [192.0.2.1]:53\n"
++    },
+     { NULL }
+   };
+ 
diff --git a/SOURCES/glibc-rh2096189-3.patch b/SOURCES/glibc-rh2096189-3.patch
new file mode 100644
index 0000000000000000000000000000000000000000..604afa4916c91b4e44941ae6f9fcc08a65044f75
--- /dev/null
+++ b/SOURCES/glibc-rh2096189-3.patch
@@ -0,0 +1,44 @@
+commit 77536da3dea5af4d1859e4e754f07f47cf8d7d4c
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Fri Jun 24 19:38:14 2022 +0200
+
+    resolv/tst-resolv-noaaaa: Support building for older C standards
+    
+    This avoids a compilation error:
+    
+    tst-resolv-noaaaa.c: In function 'response':
+    tst-resolv-noaaaa.c:74:11: error: a label can only be part of a statement and a declaration is not a statement
+               char ipv4[4] = {192, 0, 2, i + 1};
+               ^~~~
+    tst-resolv-noaaaa.c:79:11: error: a label can only be part of a statement and a declaration is not a statement
+               char *name = xasprintf ("ptr-%d", i);
+               ^~~~
+
+diff --git a/resolv/tst-resolv-noaaaa.c b/resolv/tst-resolv-noaaaa.c
+index 56b25f88a58ad286..6e0c6b6fb809e245 100644
+--- a/resolv/tst-resolv-noaaaa.c
++++ b/resolv/tst-resolv-noaaaa.c
+@@ -71,14 +71,18 @@ response (const struct resolv_response_context *ctx,
+       switch (qtype)
+         {
+         case T_A:
+-          char ipv4[4] = {192, 0, 2, i + 1};
+-          resolv_response_add_data (b, &ipv4, sizeof (ipv4));
++          {
++            char ipv4[4] = {192, 0, 2, i + 1};
++            resolv_response_add_data (b, &ipv4, sizeof (ipv4));
++          }
+           break;
+ 
+         case T_PTR:
+-          char *name = xasprintf ("ptr-%d", i);
+-          resolv_response_add_name (b, name);
+-          free (name);
++          {
++            char *name = xasprintf ("ptr-%d", i);
++            resolv_response_add_name (b, name);
++            free (name);
++          }
+           break;
+         }
+       resolv_response_close_record (b);
diff --git a/SOURCES/glibc-rh2104907.patch b/SOURCES/glibc-rh2104907.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3a65f182256b4a5da68b40c0e55721cbe09bc933
--- /dev/null
+++ b/SOURCES/glibc-rh2104907.patch
@@ -0,0 +1,14 @@
+diff --git a/localedata/SUPPORTED b/localedata/SUPPORTED
+index a4bf79c6a6e6401b..fdf15fddf5178319 100644
+--- a/localedata/SUPPORTED
++++ b/localedata/SUPPORTED
+@@ -159,7 +159,8 @@ en_SG/ISO-8859-1 \
+ en_US.UTF-8/UTF-8 \
+ en_US/ISO-8859-1 \
+ en_US.ISO-8859-15/ISO-8859-15 \
+-en_US@ampm.UTF-8/UTF-8 \
++en_US@ampm/UTF-8 \
++en_US.UTF-8@ampm/UTF-8 \
+ en_ZA.UTF-8/UTF-8 \
+ en_ZA/ISO-8859-1 \
+ en_ZM/UTF-8 \
diff --git a/SOURCES/glibc-rh2119304-1.patch b/SOURCES/glibc-rh2119304-1.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a76e1bfec5bd0e8116657787b94a33eea9cbfa93
--- /dev/null
+++ b/SOURCES/glibc-rh2119304-1.patch
@@ -0,0 +1,49 @@
+Downstream-only patch to move the recently added members (from
+glibc-rh2047981-5.patch and glibc-rh2047981-6.patch) to the end
+of _rtld_global_ro.  This avoids changing the offset of
+GLRO (dl_naudit).
+
+Without this change, the audit invocation loop in the old
+__libc_start_main function in a not-yet-updated version of libc.so.6
+reads a non-zero garbage value for GLRO (dl_naudit), assumes that
+auditing is active, and reads further garbage pointers, leading to
+to a crash.  Preserving the old offset of GLRO (dl_naudit) avoids
+that.  This works because RPM updates /lib64/ld-* before
+/lib64/libc.so.6 because it sorts earlier (except on POWER9 due
+to the glibc-hwcaps/power9 multilib).
+
+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
+index 9dec9e3d3b6d6aa2..5e56550a4d556fa7 100644
+--- a/sysdeps/generic/ldsodefs.h
++++ b/sysdeps/generic/ldsodefs.h
+@@ -648,6 +648,15 @@ struct rtld_global_ro
+   void *(*_dl_open) (const char *file, int mode, const void *caller_dlopen,
+ 		     Lmid_t nsid, int argc, char *argv[], char *env[]);
+   void (*_dl_close) (void *map);
++  void *(*_dl_tls_get_addr_soft) (struct link_map *);
++#ifdef HAVE_DL_DISCOVER_OSVERSION
++  int (*_dl_discover_osversion) (void);
++#endif
++
++  /* List of auditing interfaces.  */
++  struct audit_ifaces *_dl_audit;
++  unsigned int _dl_naudit;
++
+   /* libdl in a secondary namespace (after dlopen) must use
+      _dl_catch_error from the main namespace, so it has to be
+      exported in some way.  */
+@@ -657,14 +666,6 @@ struct rtld_global_ro
+   /* libdl in a secondary namespace must use free from the base
+      namespace.  */
+   void (*_dl_error_free) (void *);
+-  void *(*_dl_tls_get_addr_soft) (struct link_map *);
+-#ifdef HAVE_DL_DISCOVER_OSVERSION
+-  int (*_dl_discover_osversion) (void);
+-#endif
+-
+-  /* List of auditing interfaces.  */
+-  struct audit_ifaces *_dl_audit;
+-  unsigned int _dl_naudit;
+ };
+ # define __rtld_global_attribute__
+ # if IS_IN (rtld)
diff --git a/SOURCES/glibc-rh2119304-2.patch b/SOURCES/glibc-rh2119304-2.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a1e121b696d52a374bad66719bc6c079925177cc
--- /dev/null
+++ b/SOURCES/glibc-rh2119304-2.patch
@@ -0,0 +1,202 @@
+commit 5ecc98241229d494aaad23a4a3fe106fe11e1f40
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Thu Aug 25 16:34:20 2022 +0200
+
+    s390: Move hwcaps/platform names out of _rtld_global_ro
+    
+    Changes to these arrays are often backported to stable releases,
+    but additions to these arrays shift the offsets of the following
+    _rltd_global_ro members, thus breaking the GLIBC_PRIVATE ABI.
+    
+    Obviously, this change is itself an internal ABI break, but at least
+    it will avoid further ABI breaks going forward.
+    
+    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
+
+Conflicts:
+	sysdeps/s390/Makefile
+	  (missing lazy binding test downstream)
+
+diff --git a/sysdeps/s390/Makefile b/sysdeps/s390/Makefile
+index 5c8e1170b4d799ba..ea453ba87646c95a 100644
+--- a/sysdeps/s390/Makefile
++++ b/sysdeps/s390/Makefile
+@@ -42,6 +42,10 @@ $(modpfx)gconv-modules-s390.conf: ../sysdeps/s390/gconv-modules-s390.conf \
+ 	cp $< $@
+ endif
+ 
++ifeq ($(subdir),elf)
++sysdep-dl-routines += dl-procinfo-s390
++endif
++
+ ifeq ($(subdir),string)
+ sysdep_routines += bzero memset memset-z900 \
+ 		   memcmp memcmp-z900 \
+diff --git a/sysdeps/s390/dl-procinfo-s390.c b/sysdeps/s390/dl-procinfo-s390.c
+new file mode 100644
+index 0000000000000000..559f3827936cd017
+--- /dev/null
++++ b/sysdeps/s390/dl-procinfo-s390.c
+@@ -0,0 +1,32 @@
++/* Data for s390 version of processor capability information.
++   Copyright (C) 2006-2022 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/>.  */
++
++#include <dl-procinfo.h>
++
++const char _dl_s390_cap_flags[_DL_HWCAP_COUNT][9] =
++  {
++    "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", "edat", "etf3eh",
++    "highgprs", "te", "vx", "vxd", "vxe", "gs", "vxe2", "vxp", "sort", "dflt",
++    "vxp2", "nnpa", "pcimio", "sie"
++  };
++
++const char _dl_s390_platforms[_DL_PLATFORMS_COUNT][7] =
++  {
++    "g5", "z900", "z990", "z9-109", "z10", "z196", "zEC12", "z13", "z14", "z15",
++    "z16"
++  };
+diff --git a/sysdeps/s390/dl-procinfo.c b/sysdeps/s390/dl-procinfo.c
+index 85108943d0e79f29..f928b485609a3b8a 100644
+--- a/sysdeps/s390/dl-procinfo.c
++++ b/sysdeps/s390/dl-procinfo.c
+@@ -17,66 +17,10 @@
+    License along with the GNU C Library; if not, see
+    <http://www.gnu.org/licenses/>.  */
+ 
+-/* This information must be kept in sync with the _DL_HWCAP_COUNT and
+-   _DL_PLATFORM_COUNT definitions in procinfo.h.
+-
+-   If anything should be added here check whether the size of each string
+-   is still ok with the given array size.
+-
+-   All the #ifdefs in the definitions are quite irritating but
+-   necessary if we want to avoid duplicating the information.  There
+-   are three different modes:
+-
+-   - PROCINFO_DECL is defined.  This means we are only interested in
+-     declarations.
+-
+-   - PROCINFO_DECL is not defined:
+-
+-     + if SHARED is defined the file is included in an array
+-       initializer.  The .element = { ... } syntax is needed.
+-
+-     + if SHARED is not defined a normal array initialization is
+-       needed.
+-  */
+-
+-#ifndef PROCINFO_CLASS
+-# define PROCINFO_CLASS
+-#endif
+-
+-#if !defined PROCINFO_DECL && defined SHARED
+-  ._dl_s390_cap_flags
+-#else
+-PROCINFO_CLASS const char _dl_s390_cap_flags[23][9]
+-#endif
+-#ifndef PROCINFO_DECL
+-= {
+-     "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", "edat", "etf3eh",
+-     "highgprs", "te", "vx", "vxd", "vxe", "gs", "vxe2", "vxp", "sort", "dflt",
+-     "vxp2", "nnpa", "pcimio", "sie"
+-  }
+-#endif
+-#if !defined SHARED || defined PROCINFO_DECL
+-;
+-#else
+-,
+-#endif
+-
+-#if !defined PROCINFO_DECL && defined SHARED
+-  ._dl_s390_platforms
+-#else
+-PROCINFO_CLASS const char _dl_s390_platforms[11][7]
+-#endif
+-#ifndef PROCINFO_DECL
+-= {
+-    "g5", "z900", "z990", "z9-109", "z10", "z196", "zEC12", "z13", "z14", "z15",
+-    "z16"
+-  }
+-#endif
+-#if !defined SHARED || defined PROCINFO_DECL
+-;
+-#else
+-,
+-#endif
++/* The hwcap and platform strings are now in
++   sysdeps/s390/dl-procinfo-s390.c.  */
+ 
++/* Needed by sysdeps/unix/sysv/linux/dl-vdso-setup.c (as included from
++   sysdeps/generic/ldsodefs.h).  */
+ #undef PROCINFO_DECL
+ #undef PROCINFO_CLASS
+diff --git a/sysdeps/s390/dl-procinfo.h b/sysdeps/s390/dl-procinfo.h
+index f2b2c9ac1bb7239b..5eb2c0a39fcff520 100644
+--- a/sysdeps/s390/dl-procinfo.h
++++ b/sysdeps/s390/dl-procinfo.h
+@@ -22,8 +22,10 @@
+ #include <ldsodefs.h>
+ 
+ #define _DL_HWCAP_COUNT 23
++extern const char _dl_s390_cap_flags[_DL_HWCAP_COUNT][9] attribute_hidden;
+ 
+ #define _DL_PLATFORMS_COUNT	11
++extern const char _dl_s390_platforms[_DL_PLATFORMS_COUNT][7] attribute_hidden;
+ 
+ /* The kernel provides up to 32 capability bits with elf_hwcap.  */
+ #define _DL_FIRST_PLATFORM	32
+@@ -79,7 +81,7 @@ static inline const char *
+ __attribute__ ((unused))
+ _dl_hwcap_string (int idx)
+ {
+-  return GLRO(dl_s390_cap_flags)[idx];
++  return _dl_s390_cap_flags[idx];
+ };
+ 
+ static inline int
+@@ -90,7 +92,7 @@ _dl_string_hwcap (const char *str)
+ 
+   for (i = 0; i < _DL_HWCAP_COUNT; i++)
+     {
+-      if (strcmp (str, GLRO(dl_s390_cap_flags)[i]) == 0)
++      if (strcmp (str, _dl_s390_cap_flags[i]) == 0)
+ 	return i;
+     }
+   return -1;
+@@ -105,7 +107,7 @@ _dl_string_platform (const char *str)
+   if (str != NULL)
+     for (i = 0; i < _DL_PLATFORMS_COUNT; ++i)
+       {
+-	if (strcmp (str, GLRO(dl_s390_platforms)[i]) == 0)
++	if (strcmp (str, _dl_s390_platforms[i]) == 0)
+ 	  return _DL_FIRST_PLATFORM + i;
+       }
+   return -1;
+diff --git a/sysdeps/unix/sysv/linux/s390/dl-procinfo.h b/sysdeps/unix/sysv/linux/s390/dl-procinfo.h
+index d1516a05e3042163..4aefd7eef14eaf52 100644
+--- a/sysdeps/unix/sysv/linux/s390/dl-procinfo.h
++++ b/sysdeps/unix/sysv/linux/s390/dl-procinfo.h
+@@ -40,7 +40,7 @@ _dl_procinfo (unsigned int type, unsigned long int word)
+ 
+   for (i = 0; i < _DL_HWCAP_COUNT; ++i)
+     if (word & (1UL << i))
+-      _dl_printf (" %s", GLRO(dl_s390_cap_flags)[i]);
++      _dl_printf (" %s", _dl_s390_cap_flags[i]);
+ 
+   _dl_printf ("\n");
+ 
diff --git a/SOURCES/glibc-rh2119304-3.patch b/SOURCES/glibc-rh2119304-3.patch
new file mode 100644
index 0000000000000000000000000000000000000000..faaea99ee8fe553a49cd37657858c0eeed5e4943
--- /dev/null
+++ b/SOURCES/glibc-rh2119304-3.patch
@@ -0,0 +1,19 @@
+Downstream-only patch to preserve the 8.6.0 _rtld_global_ro ABI on s390x.
+
+diff --git a/sysdeps/s390/dl-procinfo.c b/sysdeps/s390/dl-procinfo.c
+index f928b485609a3b8a..3f46b2785fafe51e 100644
+--- a/sysdeps/s390/dl-procinfo.c
++++ b/sysdeps/s390/dl-procinfo.c
+@@ -20,6 +20,12 @@
+ /* The hwcap and platform strings are now in
+    sysdeps/s390/dl-procinfo-s390.c.  */
+ 
++/* Dummy entries to preserve ABI. */
++#if defined SHARED && defined PROCINFO_DECL
++const char _dl_s390_cap_flags_unused[23][9];
++const char _dl_s390_platforms_unused[10][7];
++#endif
++
+ /* Needed by sysdeps/unix/sysv/linux/dl-vdso-setup.c (as included from
+    sysdeps/generic/ldsodefs.h).  */
+ #undef PROCINFO_DECL
diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec
index 7788fb5c2cf491767387b14505a422a546a257f5..848f4d13ee557d70a1ba9c0c45c65d65262a2a91 100644
--- a/SPECS/glibc.spec
+++ b/SPECS/glibc.spec
@@ -1,6 +1,6 @@
 %define glibcsrcdir glibc-2.28
 %define glibcversion 2.28
-%define glibcrelease 189.5%{?dist}
+%define glibcrelease 211%{?dist}
 # Pre-release tarballs are pulled in from git using a command that is
 # effectively:
 #
@@ -855,16 +855,119 @@ Patch660: glibc-rh2045063-2.patch
 Patch661: glibc-rh2045063-3.patch
 Patch662: glibc-rh2045063-4.patch
 Patch663: glibc-rh2045063-5.patch
-Patch664: glibc-rh2061727.patch
-Patch665: glibc-rh2073432.patch
-Patch666: glibc-rh2084564.patch
-Patch667: glibc-rh2094540.patch
-Patch668: glibc-rh2093457-1.patch
-Patch669: glibc-rh2093457-2.patch
-Patch670: glibc-rh2093457-3.patch
-Patch671: glibc-rh2093457-4.patch
-Patch672: glibc-rh2093457-5.patch
-Patch673: glibc-rh2093457-6.patch
+Patch664: glibc-rh2054790.patch
+Patch665: glibc-rh2037416-1.patch
+Patch666: glibc-rh2037416-2.patch
+Patch667: glibc-rh2037416-3.patch
+Patch668: glibc-rh2037416-4.patch
+Patch669: glibc-rh2037416-5.patch
+Patch670: glibc-rh2037416-6.patch
+Patch671: glibc-rh2037416-7.patch
+Patch672: glibc-rh2037416-8.patch
+Patch673: glibc-rh2033684-1.patch
+Patch674: glibc-rh2033684-2.patch
+Patch675: glibc-rh2033684-3.patch
+Patch676: glibc-rh2033684-4.patch
+Patch677: glibc-rh2033684-5.patch
+Patch678: glibc-rh2033684-6.patch
+Patch679: glibc-rh2033684-7.patch
+Patch680: glibc-rh2033684-8.patch
+Patch681: glibc-rh2033684-9.patch
+Patch682: glibc-rh2033684-10.patch
+Patch683: glibc-rh2033684-11.patch
+Patch684: glibc-rh2033684-12.patch
+Patch685: glibc-rh2063712.patch
+Patch686: glibc-rh2063042.patch
+Patch687: glibc-rh2071745.patch
+Patch688: glibc-rh2065588-1.patch
+Patch689: glibc-rh2065588-2.patch
+Patch690: glibc-rh2065588-3.patch
+Patch691: glibc-rh2065588-4.patch
+Patch692: glibc-rh2065588-5.patch
+Patch693: glibc-rh2065588-6.patch
+Patch694: glibc-rh2065588-7.patch
+Patch695: glibc-rh2065588-8.patch
+Patch696: glibc-rh2065588-9.patch
+Patch697: glibc-rh2065588-10.patch
+Patch698: glibc-rh2065588-11.patch
+Patch699: glibc-rh2065588-12.patch
+Patch700: glibc-rh2065588-13.patch
+Patch701: glibc-rh2072329.patch
+Patch702: glibc-rh1982608.patch
+Patch703: glibc-rh1961109.patch
+Patch704: glibc-rh2086853.patch
+Patch705: glibc-rh2077835.patch
+Patch706: glibc-rh2089247-1.patch
+Patch707: glibc-rh2089247-2.patch
+Patch708: glibc-rh2089247-3.patch
+Patch709: glibc-rh2089247-4.patch
+Patch710: glibc-rh2089247-5.patch
+Patch711: glibc-rh2089247-6.patch
+Patch712: glibc-rh2091553.patch
+Patch713: glibc-rh1888660.patch
+Patch714: glibc-rh2096189-1.patch
+Patch715: glibc-rh2096189-2.patch
+Patch716: glibc-rh2096189-3.patch
+Patch717: glibc-rh2080349-1.patch
+Patch718: glibc-rh2080349-2.patch
+Patch719: glibc-rh2080349-3.patch
+Patch720: glibc-rh2080349-4.patch
+Patch721: glibc-rh2080349-5.patch
+Patch722: glibc-rh2080349-6.patch
+Patch723: glibc-rh2080349-7.patch
+Patch724: glibc-rh2080349-8.patch
+Patch725: glibc-rh2080349-9.patch
+Patch727: glibc-rh2047981-1.patch
+Patch728: glibc-rh2047981-2.patch
+Patch729: glibc-rh2047981-3.patch
+Patch730: glibc-rh2047981-4.patch
+Patch731: glibc-rh2047981-5.patch
+Patch732: glibc-rh2047981-6.patch
+Patch733: glibc-rh2047981-7.patch
+Patch734: glibc-rh2047981-8.patch
+Patch735: glibc-rh2047981-9.patch
+Patch736: glibc-rh2047981-10.patch
+Patch737: glibc-rh2047981-11.patch
+Patch738: glibc-rh2047981-12.patch
+Patch739: glibc-rh2047981-13.patch
+Patch740: glibc-rh2047981-14.patch
+Patch741: glibc-rh2047981-15.patch
+Patch742: glibc-rh2047981-16.patch
+Patch743: glibc-rh2047981-17.patch
+Patch744: glibc-rh2047981-18.patch
+Patch745: glibc-rh2047981-19.patch
+Patch746: glibc-rh2047981-20.patch
+Patch747: glibc-rh2047981-21.patch
+Patch748: glibc-rh2047981-22.patch
+Patch749: glibc-rh2047981-23.patch
+Patch750: glibc-rh2047981-24.patch
+Patch751: glibc-rh2047981-25.patch
+Patch752: glibc-rh2047981-26.patch
+Patch753: glibc-rh2047981-27.patch
+Patch754: glibc-rh2047981-28.patch
+Patch755: glibc-rh2047981-29.patch
+Patch756: glibc-rh2047981-30.patch
+Patch757: glibc-rh2047981-31.patch
+Patch758: glibc-rh2047981-32.patch
+Patch759: glibc-rh2047981-33.patch
+Patch760: glibc-rh2047981-34.patch
+Patch761: glibc-rh2047981-35.patch
+Patch762: glibc-rh2047981-36.patch
+Patch763: glibc-rh2047981-37.patch
+Patch764: glibc-rh2047981-38.patch
+Patch766: glibc-rh2047981-39.patch
+Patch767: glibc-rh2047981-40.patch
+Patch768: glibc-rh2047981-41.patch
+Patch769: glibc-rh2047981-42.patch
+Patch770: glibc-rh2047981-43.patch
+Patch771: glibc-rh2047981-44.patch
+Patch772: glibc-rh2047981-45.patch
+Patch773: glibc-rh2047981-46.patch
+Patch774: glibc-rh2047981-47.patch
+Patch775: glibc-rh2104907.patch
+Patch776: glibc-rh2119304-1.patch
+Patch777: glibc-rh2119304-2.patch
+Patch778: glibc-rh2119304-3.patch
 
 ##############################################################################
 # Continued list of core "glibc" package information:
@@ -1756,6 +1859,20 @@ $olddir/build-%{target}/testrun.sh \
 # default locale-archive without modification, and leaving compiled
 # locales as they are (without inclusion into the archive).
 cp locale-archive{,.tmpl}
+
+# Almost half the LC_CTYPE files in langpacks are identical to the C.utf8
+# variant which is installed by default.  When we keep them as hardlinks,
+# each langpack ends up retaining a copy.  If we convert these to symbolic
+# links instead, we save ~350K each when they get installed that way.
+#
+# LC_MEASUREMENT and LC_PAPER also have several duplicates but we don't
+# bother with these because they are only ~30 bytes each.
+pushd %{glibc_sysroot}/usr/lib/locale
+for f in $(find eo *_* -samefile C.utf8/LC_CTYPE); do
+  rm $f && ln -s '../C.utf8/LC_CTYPE' $f
+done
+popd
+
 # Create the file lists for the language specific sub-packages:
 for i in eo *_*
 do
@@ -2064,7 +2181,7 @@ chmod 0444 master.filelist
 # - All the libnss files (we add back the ones we want later).
 # - All bench test binaries.
 # - The aux-cache, since it's handled specially in the files section.
-# - The build-locale-archive binary since it's in the common package.
+# - The build-locale-archive binary since it's in the all-langpacks package.
 # - Extra gconv modules.  We add the required modules later.
 cat master.filelist \
 	| grep -v \
@@ -2189,13 +2306,15 @@ grep '%{_libdir}/lib.*\.a' < master.filelist \
 ###############################################################################
 
 # All of the bin and certain sbin files go into the common package except
-# iconvconfig which needs to go in glibc. Likewise nscd is excluded because
+# iconvconfig which needs to go in glibc, and build-locale-archive which
+# needs to go into glibc-all-langpacks. Likewise nscd is excluded because
 # it goes in nscd. The iconvconfig binary is kept in the main glibc package
 # because we use it in the post-install scriptlet to rebuild the
 # gconv-modules.cache.
 grep '%{_prefix}/bin' master.filelist >> common.filelist
 grep '%{_prefix}/sbin' master.filelist \
 	| grep -v '%{_prefix}/sbin/iconvconfig' \
+	| grep -v '%{_prefix}/sbin/build-locale-archive' \
 	| grep -v 'nscd' >> common.filelist
 # All of the files under share go into the common package since they should be
 # multilib-independent.
@@ -2211,9 +2330,6 @@ grep '%{_prefix}/share' master.filelist \
 	-e '%{_docdir}' \
 	>> common.filelist
 
-# Add the binary to build locales to the common subpackage.
-echo '%{_prefix}/sbin/build-locale-archive' >> common.filelist
-
 ###############################################################################
 # nscd
 ###############################################################################
@@ -2623,6 +2739,8 @@ fi
 %files all-langpacks
 %attr(0644,root,root) %verify(not md5 size mtime) %{_prefix}/lib/locale/locale-archive.tmpl
 %attr(0644,root,root) %verify(not md5 size mtime mode) %ghost %{_prefix}/lib/locale/locale-archive
+# build-locale-archive re-generates locale-archive during install/upgrade/downgrade
+%attr(0700,root,root) %{_prefix}/sbin/build-locale-archive
 
 %files locale-source
 %dir %{_prefix}/share/i18n/locales
@@ -2680,20 +2798,74 @@ fi
 %files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared
 
 %changelog
-* Wed Jun  8 2022 Florian Weimer <fweimer@redhat.com> - 2.28-189.5
-- Increase tempnam randomness (#2093457)
+* Thu Aug 25 2022 Florian Weimer <fweimer@redhat.com> - 2.28-211
+- Preserve GLRO (dl_naudit) internal ABI (#2119304)
+- Avoid s390x ABI change due to z16 recognition on s390x (#2119304)
+
+* Tue Aug 23 2022 Arjun Shankar <arjun@redhat.com> - 2.28-210
+- Fix locale en_US@ampm (#2104907)
+
+* Fri Jul 22 2022 Carlos O'Donell <carlos@redhat.com> - 2.28-209
+- Improve dynamic loader auditing interface (LD_AUDIT) (#2047981)
+- Add dlinfo() API support for RTLD_DI_PHDR (#2097898)
+
+* Fri Jul 15 2022 Patsy Griffin <patsy@redhat.com> - 2.28-208
+- Update syscall-names.list to Linux 5.18. (#2080349)
+
+* Fri Jun 24 2022 Florian Weimer <fweimer@redhat.com> - 2.28-207
+- Add the no-aaaa DNS stub resolver option (#2096189)
+
+* Thu Jun  9 2022 Arjun Shankar <arjun@redhat.com> - 2.28-206
+- Fix deadlocks in pthread_atfork handlers (#1888660)
+
+* Tue Jun 07 2022 DJ Delorie <dj@redhat.com) - 2.28-205
+- Fix incorrect strncpy results on POWER9 (#2091553)
+
+* Mon May 23 2022 Florian Weimer <fweimer@redhat.com> - 2.28-204
+- Increase tempnam randomness (#2089247)
+
+* Tue May 17 2022 Patsy Griffin <patsy@redhat.com> - 2.28-203
+- 390x: Add support for IBM z16. (#2077835)
+
+* Mon May 16 2022 Siddhesh Poyarekar <siddhesh@redhat.com> - 2.28-202
+- Ensure that condition in __glibc_fortify is a constant (#2086853)
+
+* Tue May 10 2022 Arjun Shankar <arjun@redhat.com> - 2.28-201
+- Add missing MACRON to EBCDIC character sets (#1961109)
+
+* Wed May  4 2022 DJ Delorie <dj@redhat.com> - 2.28-200
+- Fix glob defects on certain XFS filesystems (#1982608)
+
+* Tue Apr 26 2022 Siddhesh Poyarekar <siddhesh@redhat.com> - 2.28-199
+- Fix fortify false positive with mbsrtowcs and mbsnrtowcs (#2072329).
+
+* Fri Apr 22 2022 Carlos O'Donell <carlos@redhat.com> - 2.28-198
+- Fix multi-threaded popen defect leading to segfault (#2065588)
+
+* Tue Apr 05 2022 Arjun Shankar <arjun@redhat.com> - 2.28-197
+- timezone: Fix a test that causes occasional build failure (#2071745)
+
+* Tue Mar 15 2022 Siddhesh Poyarekar <siddhesh@redhat.com> 2.28-196
+- Synchronize feature guards in fortified functions (#2063042)
+
+* Mon Mar 14 2022 Florian Weimer <fweimer@redhat.com> - 2.28-195
+- nss: Avoid clobbering errno in get*ent via dlopen (#2063712)
+
+* Fri Mar 11 2022 Siddhesh Poyarekar <siddhesh@redhat.com> 2.28-194
+- Enable support for _FORTIFY_SOURCE=3 for gcc 12 and later (#2033684)
 
-* Tue Jun 07 2022 DJ Delorie <dj@redhat.com> - 2.28-189.4
-- Fix incorrect strncpy results on POWER9 (#2094540)
+* Wed Mar 9 2022 DJ Delorie <dj@redhat.com> - 2.28-193
+- memory operation A64FX SVE performance improvement (#2037416)
 
-* Fri May 13 2022 Arjun Shankar <arjun@redhat.com> - 2.28-189.3
-- Add missing MACRON to EBCDIC character sets (#2084564)
+* Mon Mar 07 2022 Arjun Shankar <arjun@redhat.com> - 2.28-192
+- Move build-locale-archive to glibc-all-langpacks (#2057513)
 
-* Fri May 13 2022 Arjun Shankar <arjun@redhat.com> - 2.28-189.2
-- timezone: Fix a test that causes occasional build failure (#2073432)
+* Mon Mar 07 2022 Arjun Shankar <arjun@redhat.com> - 2.28-191
+- Fix build-locale-archive to handle symbolic links (#2054790)
 
-* Thu Mar 10 2022 Florian Weimer <fweimer@redhat.com> - 2.28-189.1
-- nss: Avoid clobbering errno in get*ent via dlopen (#2061727)
+* Fri Mar 04 2022 Arjun Shankar <arjun@redhat.com> - 2.28-190
+- Reduce installed size of some langpacks by de-duplicating LC_CTYPE (#2054790)
+- Fix localedef so it can handle symbolic links when generating locale-archive.
 
 * Thu Jan 27 2022 Siddhesh Poyarekar <siddhesh@redhat.com> - 2.28-189
 - CVE-2021-3999: getcwd: align stack on clone in aarch64 and fix a memory leak