diff --git a/SOURCES/glibc-RHEL-21997.patch b/SOURCES/glibc-RHEL-21997.patch
new file mode 100644
index 0000000000000000000000000000000000000000..865c50821336e46ef91eba067f9515eb7c27326c
--- /dev/null
+++ b/SOURCES/glibc-RHEL-21997.patch
@@ -0,0 +1,112 @@
+This downstream-only patch compensates for the missing backport of
+commit 2d651eb9265d1366d7b9e881bfddd46db9c1ecc4 ("x86: Move
+x86 processor cache info to cpu_features").  Without it,
+ld.so --list-diagnostics prints values that have not been properly
+initalized from CPUID data.
+
+diff --git a/sysdeps/x86/cacheinfo.h b/sysdeps/x86/cacheinfo.h
+index 10ebadd819d9efff..d8421fab83ab08ac 100644
+--- a/sysdeps/x86/cacheinfo.h
++++ b/sysdeps/x86/cacheinfo.h
+@@ -19,31 +19,42 @@
+ #include <assert.h>
+ #include <unistd.h>
+ 
++/* When building ld.so, do not export any of the variables.  They are
++   only used for diagnostics and are not initialized during regular
++   operation.  */
++#if IS_IN (rtld)
++# define CACHEINFO_VARIABLE(name, initializer) \
++  static long int name = initializer
++#else
++# define CACHEINFO_VARIABLE(name, initializer) \
++  long int name attribute_hidden = initializer
++#endif
++
+ /* Data cache size for use in memory and string routines, typically
+    L1 size, rounded to multiple of 256 bytes.  */
+-long int __x86_data_cache_size_half attribute_hidden = 32 * 1024 / 2;
+-long int __x86_data_cache_size attribute_hidden = 32 * 1024;
++CACHEINFO_VARIABLE (__x86_data_cache_size_half, 32 * 1024 / 2);
++CACHEINFO_VARIABLE (__x86_data_cache_size, 32 * 1024);
+ /* Similar to __x86_data_cache_size_half, but not rounded.  */
+-long int __x86_raw_data_cache_size_half attribute_hidden = 32 * 1024 / 2;
++CACHEINFO_VARIABLE (__x86_raw_data_cache_size_half, 32 * 1024 / 2);
+ /* Similar to __x86_data_cache_size, but not rounded.  */
+-long int __x86_raw_data_cache_size attribute_hidden = 32 * 1024;
++CACHEINFO_VARIABLE (__x86_raw_data_cache_size, 32 * 1024);
+ /* Shared cache size for use in memory and string routines, typically
+    L2 or L3 size, rounded to multiple of 256 bytes.  */
+-long int __x86_shared_cache_size_half attribute_hidden = 1024 * 1024 / 2;
+-long int __x86_shared_cache_size attribute_hidden = 1024 * 1024;
++CACHEINFO_VARIABLE (__x86_shared_cache_size_half, 1024 * 1024 / 2);
++CACHEINFO_VARIABLE (__x86_shared_cache_size, 1024 * 1024);
+ /* Similar to __x86_shared_cache_size_half, but not rounded.  */
+-long int __x86_raw_shared_cache_size_half attribute_hidden = 1024 * 1024 / 2;
++CACHEINFO_VARIABLE (__x86_raw_shared_cache_size_half, 1024 * 1024 / 2);
+ /* Similar to __x86_shared_cache_size, but not rounded.  */
+-long int __x86_raw_shared_cache_size attribute_hidden = 1024 * 1024;
++CACHEINFO_VARIABLE (__x86_raw_shared_cache_size, 1024 * 1024);
+ 
+ /* Threshold to use non temporal store.  */
+-long int __x86_shared_non_temporal_threshold attribute_hidden;
++CACHEINFO_VARIABLE (__x86_shared_non_temporal_threshold, 0);
+ 
+ /* Threshold to use Enhanced REP MOVSB.  */
+-long int __x86_rep_movsb_threshold attribute_hidden = 2048;
++CACHEINFO_VARIABLE (__x86_rep_movsb_threshold, 2048);
+ 
+ /* Threshold to use Enhanced REP STOSB.  */
+-long int __x86_rep_stosb_threshold attribute_hidden = 2048;
++CACHEINFO_VARIABLE (__x86_rep_stosb_threshold, 2048);
+ 
+ static void
+ get_common_cache_info (long int *shared_ptr, long int * shared_per_thread_ptr, unsigned int *threads_ptr,
+diff --git a/sysdeps/x86/dl-diagnostics-cpu.c b/sysdeps/x86/dl-diagnostics-cpu.c
+index 0ba286a828b69937..9215604ecf22344c 100644
+--- a/sysdeps/x86/dl-diagnostics-cpu.c
++++ b/sysdeps/x86/dl-diagnostics-cpu.c
+@@ -19,6 +19,13 @@
+ #include <dl-diagnostics.h>
+ #include <ldsodefs.h>
+ 
++#include <assert.h>
++#include <unistd.h>
++#include <cpu-features.h>
++#include <cpuid.h>
++#include <dl-cacheinfo.h>
++#include <cacheinfo.h>
++
+ static void
+ print_cpu_features_value (const char *label, uint64_t value)
+ {
+@@ -81,19 +88,21 @@ _dl_diagnostics_cpu (void)
+ #include "cpu-features-preferred_feature_index_1.def"
+ #undef BIT
+ 
++  /* The cache information variables are only used for diagnostics and
++     are not initialized during startup.  The values used at run time
++     are only in libc.so.6.  */
++  init_cacheinfo ();
++
+   print_cpu_features_value ("xsave_state_size",
+                             cpu_features->xsave_state_size);
+   print_cpu_features_value ("xsave_state_full_size",
+                             cpu_features->xsave_state_full_size);
+-  print_cpu_features_value ("data_cache_size", cpu_features->data_cache_size);
+-  print_cpu_features_value ("shared_cache_size",
+-                            cpu_features->shared_cache_size);
++  print_cpu_features_value ("data_cache_size", __x86_data_cache_size);
++  print_cpu_features_value ("shared_cache_size",  __x86_shared_cache_size);
+   print_cpu_features_value ("non_temporal_threshold",
+-                            cpu_features->non_temporal_threshold);
+-  print_cpu_features_value ("rep_movsb_threshold",
+-                            cpu_features->rep_movsb_threshold);
+-  print_cpu_features_value ("rep_stosb_threshold",
+-                            cpu_features->rep_stosb_threshold);
++                            __x86_shared_non_temporal_threshold);
++  print_cpu_features_value ("rep_movsb_threshold", __x86_rep_movsb_threshold);
++  print_cpu_features_value ("rep_stosb_threshold", __x86_rep_stosb_threshold);
+   _Static_assert (offsetof (struct cpu_features, rep_stosb_threshold)
+                   + sizeof (cpu_features->rep_stosb_threshold)
+                   == sizeof (*cpu_features),
diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec
index c5be956deb0ab7f6eda0aef5007d9be31d3403f1..9a2f9dd8357e27695012c5a6e5a867312f2f283a 100644
--- a/SPECS/glibc.spec
+++ b/SPECS/glibc.spec
@@ -1,6 +1,6 @@
 %define glibcsrcdir glibc-2.28
 %define glibcversion 2.28
-%define glibcrelease 250%{?dist}
+%define glibcrelease 251%{?dist}
 # Pre-release tarballs are pulled in from git using a command that is
 # effectively:
 #
@@ -1182,6 +1182,7 @@ Patch994: glibc-RHEL-3010-1.patch
 Patch995: glibc-RHEL-3010-2.patch
 Patch996: glibc-RHEL-3010-3.patch
 Patch997: glibc-RHEL-19445.patch
+Patch998: glibc-RHEL-21997.patch
 
 ##############################################################################
 # Continued list of core "glibc" package information:
@@ -3013,6 +3014,9 @@ fi
 %files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared
 
 %changelog
+* Thu Jan 18 2024 Florian Weimer <fweimer@redhat.com> - 2.28-251
+- Cache information in x86_64 ld.so --list-diagnostics output (RHEL-21997)
+
 * Wed Jan 10 2024 Arjun Shankar <arjun@redhat.com> - 2.28-250
 - getaddrinfo: Return correct error EAI_MEMORY when out-of-memory (RHEL-19445)