e354a5
commit dad90d528259b669342757c37dedefa8577e2636
e354a5
Author: Florian Weimer <fweimer@redhat.com>
e354a5
Date:   Fri Dec 4 09:13:43 2020 +0100
e354a5
e354a5
    elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
e354a5
    
e354a5
    This hacks non-power-set processing into _dl_important_hwcaps.
e354a5
    Once the legacy hwcaps handling goes away, the subdirectory
e354a5
    handling needs to be reworked, but it is premature to do this
e354a5
    while both approaches are still supported.
e354a5
    
e354a5
    ld.so supports two new arguments, --glibc-hwcaps-prepend and
e354a5
    --glibc-hwcaps-mask.  Each accepts a colon-separated list of
e354a5
    glibc-hwcaps subdirectory names.  The prepend option adds additional
e354a5
    subdirectories that are searched first, in the specified order.  The
e354a5
    mask option restricts the automatically selected subdirectories to
e354a5
    those listed in the option argument.  For example, on systems where
e354a5
    /usr/lib64 is on the library search path,
e354a5
    --glibc-hwcaps-prepend=valgrind:debug causes the dynamic loader to
e354a5
    search the directories /usr/lib64/glibc-hwcaps/valgrind and
e354a5
    /usr/lib64/glibc-hwcaps/debug just before /usr/lib64 is searched.
e354a5
    
e354a5
    Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
e354a5
e354a5
Conflicts:
e354a5
	elf/Makefile
e354a5
	  (Test backport differences.)
e354a5
e354a5
diff --git a/elf/Makefile b/elf/Makefile
e354a5
index bc96b8fd65e376cc..f795617780b393ec 100644
e354a5
--- a/elf/Makefile
e354a5
+++ b/elf/Makefile
e354a5
@@ -59,7 +59,8 @@ elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
e354a5
 # ld.so uses those routines, plus some special stuff for being the program
e354a5
 # interpreter and operating independent of libc.
e354a5
 rtld-routines	= rtld $(all-dl-routines) dl-sysdep dl-environ dl-minimal \
e354a5
-  dl-error-minimal dl-conflict dl-hwcaps dl-usage
e354a5
+  dl-error-minimal dl-conflict dl-hwcaps dl-hwcaps_split dl-hwcaps-subdirs \
e354a5
+  dl-usage
e354a5
 all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
e354a5
 
e354a5
 CFLAGS-dl-runtime.c += -fexceptions -fasynchronous-unwind-tables
e354a5
@@ -199,13 +200,14 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
e354a5
 	 tst-filterobj tst-filterobj-dlopen tst-auxobj tst-auxobj-dlopen \
e354a5
 	 tst-audit14 tst-audit15 tst-audit16 \
e354a5
 	 tst-tls-ie tst-tls-ie-dlmopen \
e354a5
-	 argv0test
e354a5
+	 argv0test \
e354a5
+	 tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask
e354a5
 #	 reldep9
e354a5
 tests-internal += loadtest unload unload2 circleload1 \
e354a5
 	 neededtest neededtest2 neededtest3 neededtest4 \
e354a5
 	 tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \
e354a5
 	 tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym \
e354a5
-	 tst-create_format1 tst-tls-surplus
e354a5
+	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split
e354a5
 tests-container += tst-pldd
e354a5
 ifeq ($(build-hardcoded-path-in-tests),yes)
e354a5
 tests += tst-dlopen-aout
e354a5
@@ -318,7 +320,10 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
e354a5
 		tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3 \
e354a5
 		tst-tls-ie-mod0 tst-tls-ie-mod1 tst-tls-ie-mod2 \
e354a5
 		tst-tls-ie-mod3 tst-tls-ie-mod4 tst-tls-ie-mod5 \
e354a5
-		tst-tls-ie-mod6
e354a5
+		tst-tls-ie-mod6 libmarkermod1-1 libmarkermod1-2 libmarkermod1-3 \
e354a5
+		libmarkermod2-1 libmarkermod2-2 \
e354a5
+		libmarkermod3-1 libmarkermod3-2 libmarkermod3-3 \
e354a5
+		libmarkermod4-1 libmarkermod4-2 libmarkermod4-3 libmarkermod4-4 \
e354a5
 
e354a5
 # Most modules build with _ISOMAC defined, but those filtered out
e354a5
 # depend on internal headers.
e354a5
@@ -1732,3 +1737,60 @@ $(objpfx)argv0test.out: tst-rtld-argv0.sh $(objpfx)ld.so \
e354a5
             '$(test-wrapper-env)' '$(run_program_env)' \
e354a5
             '$(rpath-link)' 'test-argv0' > $@; \
e354a5
     $(evaluate-test)
e354a5
+
e354a5
+# A list containing the name of the most likely searched subdirectory
e354a5
+# of the glibc-hwcaps directory, for each supported architecture (in
e354a5
+# other words, the oldest hardware level recognized by the
e354a5
+# glibc-hwcaps mechanism for this architecture).  Used to obtain test
e354a5
+# coverage for some glibc-hwcaps tests for the widest possible range
e354a5
+# of systems.
e354a5
+glibc-hwcaps-first-subdirs-for-tests =
e354a5
+
e354a5
+# The test modules are parameterized by preprocessor macros.
e354a5
+LDFLAGS-libmarkermod1-1.so += -Wl,-soname,libmarkermod1.so
e354a5
+LDFLAGS-libmarkermod2-1.so += -Wl,-soname,libmarkermod2.so
e354a5
+LDFLAGS-libmarkermod3-1.so += -Wl,-soname,libmarkermod3.so
e354a5
+LDFLAGS-libmarkermod4-1.so += -Wl,-soname,libmarkermod4.so
e354a5
+$(objpfx)libmarkermod%.os : markermodMARKER-VALUE.c
e354a5
+	$(compile-command.c) \
e354a5
+	  -DMARKER=marker$(firstword $(subst -, ,$*)) \
e354a5
+	  -DVALUE=$(lastword $(subst -, ,$*))
e354a5
+$(objpfx)libmarkermod1.so: $(objpfx)libmarkermod1-1.so
e354a5
+	cp $< $@
e354a5
+$(objpfx)libmarkermod2.so: $(objpfx)libmarkermod2-1.so
e354a5
+	cp $< $@
e354a5
+$(objpfx)libmarkermod3.so: $(objpfx)libmarkermod3-1.so
e354a5
+	cp $< $@
e354a5
+$(objpfx)libmarkermod4.so: $(objpfx)libmarkermod4-1.so
e354a5
+	cp $< $@
e354a5
+
e354a5
+# tst-glibc-hwcaps-prepend checks that --glibc-hwcaps-prepend is
e354a5
+# preferred over auto-detected subdirectories.
e354a5
+$(objpfx)tst-glibc-hwcaps-prepend: $(objpfx)libmarkermod1-1.so
e354a5
+$(objpfx)glibc-hwcaps/prepend-markermod1/libmarkermod1.so: \
e354a5
+  $(objpfx)libmarkermod1-2.so
e354a5
+	$(make-target-directory)
e354a5
+	cp $< $@
e354a5
+$(objpfx)glibc-hwcaps/%/libmarkermod1.so: $(objpfx)libmarkermod1-3.so
e354a5
+	$(make-target-directory)
e354a5
+	cp $< $@
e354a5
+$(objpfx)tst-glibc-hwcaps-prepend.out: \
e354a5
+  $(objpfx)tst-glibc-hwcaps-prepend $(objpfx)libmarkermod1.so \
e354a5
+  $(patsubst %,$(objpfx)glibc-hwcaps/%/libmarkermod1.so,prepend-markermod1 \
e354a5
+    $(glibc-hwcaps-first-subdirs-for-tests))
e354a5
+	$(test-wrapper) $(rtld-prefix) \
e354a5
+	  --glibc-hwcaps-prepend prepend-markermod1 \
e354a5
+	  $< > $@; \
e354a5
+	$(evaluate-test)
e354a5
+
e354a5
+# tst-glibc-hwcaps-mask checks that --glibc-hwcaps-mask can be used to
e354a5
+# suppress all auto-detected subdirectories.
e354a5
+$(objpfx)tst-glibc-hwcaps-mask: $(objpfx)libmarkermod1-1.so
e354a5
+$(objpfx)tst-glibc-hwcaps-mask.out: \
e354a5
+  $(objpfx)tst-glibc-hwcaps-mask $(objpfx)libmarkermod1.so \
e354a5
+  $(patsubst %,$(objpfx)glibc-hwcaps/%/libmarkermod1.so,\
e354a5
+    $(glibc-hwcaps-first-subdirs-for-tests))
e354a5
+	$(test-wrapper) $(rtld-prefix) \
e354a5
+	  --glibc-hwcaps-mask does-not-exist \
e354a5
+	  $< > $@; \
e354a5
+	$(evaluate-test)
e354a5
diff --git a/elf/dl-hwcaps-subdirs.c b/elf/dl-hwcaps-subdirs.c
e354a5
new file mode 100644
e354a5
index 0000000000000000..60c6d59731ee3188
e354a5
--- /dev/null
e354a5
+++ b/elf/dl-hwcaps-subdirs.c
e354a5
@@ -0,0 +1,29 @@
e354a5
+/* Architecture-specific glibc-hwcaps subdirectories.  Generic version.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <dl-hwcaps.h>
e354a5
+
e354a5
+/* In the generic version, there are no subdirectories defined.  */
e354a5
+
e354a5
+const char _dl_hwcaps_subdirs[] = "";
e354a5
+
e354a5
+uint32_t
e354a5
+_dl_hwcaps_subdirs_active (void)
e354a5
+{
e354a5
+  return 0;
e354a5
+}
e354a5
diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c
e354a5
index 82ee89c36a1eb4ab..e57d0d2d41741021 100644
e354a5
--- a/elf/dl-hwcaps.c
e354a5
+++ b/elf/dl-hwcaps.c
e354a5
@@ -26,20 +26,97 @@
e354a5
 #include <dl-procinfo.h>
e354a5
 #include <dl-hwcaps.h>
e354a5
 
e354a5
+/* This is the result of counting the substrings in a colon-separated
e354a5
+   hwcaps string.  */
e354a5
+struct hwcaps_counts
e354a5
+{
e354a5
+  /* Number of substrings.  */
e354a5
+  size_t count;
e354a5
+
e354a5
+  /* Sum of the individual substring lengths (without separators or
e354a5
+     null terminators).  */
e354a5
+  size_t total_length;
e354a5
+
e354a5
+  /* Maximum length of an individual substring.  */
e354a5
+  size_t maximum_length;
e354a5
+};
e354a5
+
e354a5
+/* Update *COUNTS according to the contents of HWCAPS.  Skip over
e354a5
+   entries whose bit is not set in MASK.  */
e354a5
+static void
e354a5
+update_hwcaps_counts (struct hwcaps_counts *counts, const char *hwcaps,
e354a5
+		      uint32_t bitmask, const char *mask)
e354a5
+{
e354a5
+  struct dl_hwcaps_split_masked sp;
e354a5
+  _dl_hwcaps_split_masked_init (&sp, hwcaps, bitmask, mask);
e354a5
+  while (_dl_hwcaps_split_masked (&sp))
e354a5
+    {
e354a5
+      ++counts->count;
e354a5
+      counts->total_length += sp.split.length;
e354a5
+      if (sp.split.length > counts->maximum_length)
e354a5
+	counts->maximum_length = sp.split.length;
e354a5
+    }
e354a5
+}
e354a5
+
e354a5
+/* State for copy_hwcaps.  Must be initialized to point to
e354a5
+   the storage areas for the array and the strings themselves.  */
e354a5
+struct copy_hwcaps
e354a5
+{
e354a5
+  struct r_strlenpair *next_pair;
e354a5
+  char *next_string;
e354a5
+};
e354a5
+
e354a5
+/* Copy HWCAPS into the string pairs and strings, advancing *TARGET.
e354a5
+   Skip over entries whose bit is not set in MASK.  */
e354a5
+static void
e354a5
+copy_hwcaps (struct copy_hwcaps *target, const char *hwcaps,
e354a5
+	     uint32_t bitmask, const char *mask)
e354a5
+{
e354a5
+  struct dl_hwcaps_split_masked sp;
e354a5
+  _dl_hwcaps_split_masked_init (&sp, hwcaps, bitmask, mask);
e354a5
+  while (_dl_hwcaps_split_masked (&sp))
e354a5
+    {
e354a5
+      target->next_pair->str = target->next_string;
e354a5
+      char *slash = __mempcpy (__mempcpy (target->next_string,
e354a5
+					  GLIBC_HWCAPS_PREFIX,
e354a5
+					  strlen (GLIBC_HWCAPS_PREFIX)),
e354a5
+			       sp.split.segment, sp.split.length);
e354a5
+      *slash = '/';
e354a5
+      target->next_pair->len
e354a5
+	= strlen (GLIBC_HWCAPS_PREFIX) + sp.split.length + 1;
e354a5
+      ++target->next_pair;
e354a5
+      target->next_string = slash + 1;
e354a5
+    }
e354a5
+}
e354a5
+
e354a5
 /* Return an array of useful/necessary hardware capability names.  */
e354a5
 const struct r_strlenpair *
e354a5
-_dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
e354a5
+_dl_important_hwcaps (const char *glibc_hwcaps_prepend,
e354a5
+		      const char *glibc_hwcaps_mask,
e354a5
+		      size_t *sz, size_t *max_capstrlen)
e354a5
 {
e354a5
   uint64_t hwcap_mask = GET_HWCAP_MASK();
e354a5
   /* Determine how many important bits are set.  */
e354a5
   uint64_t masked = GLRO(dl_hwcap) & hwcap_mask;
e354a5
   size_t cnt = GLRO (dl_platform) != NULL;
e354a5
   size_t n, m;
e354a5
-  size_t total;
e354a5
   struct r_strlenpair *result;
e354a5
   struct r_strlenpair *rp;
e354a5
   char *cp;
e354a5
 
e354a5
+  /* glibc-hwcaps subdirectories.  These are exempted from the power
e354a5
+     set construction below.  */
e354a5
+  uint32_t hwcaps_subdirs_active = _dl_hwcaps_subdirs_active ();
e354a5
+  struct hwcaps_counts hwcaps_counts =  { 0, };
e354a5
+  update_hwcaps_counts (&hwcaps_counts, glibc_hwcaps_prepend, -1, NULL);
e354a5
+  update_hwcaps_counts (&hwcaps_counts, _dl_hwcaps_subdirs,
e354a5
+			hwcaps_subdirs_active, glibc_hwcaps_mask);
e354a5
+
e354a5
+  /* Each hwcaps subdirectory has a GLIBC_HWCAPS_PREFIX string prefix
e354a5
+     and a "/" suffix once stored in the result.  */
e354a5
+  size_t total = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1)
e354a5
+		  + hwcaps_counts.total_length);
e354a5
+
e354a5
   /* Count the number of bits set in the masked value.  */
e354a5
   for (n = 0; (~((1ULL << n) - 1) & masked) != 0; ++n)
e354a5
     if ((masked & (1ULL << n)) != 0)
e354a5
@@ -74,10 +151,10 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
e354a5
 
e354a5
   /* Determine the total size of all strings together.  */
e354a5
   if (cnt == 1)
e354a5
-    total = temp[0].len + 1;
e354a5
+    total += temp[0].len + 1;
e354a5
   else
e354a5
     {
e354a5
-      total = temp[0].len + temp[cnt - 1].len + 2;
e354a5
+      total += temp[0].len + temp[cnt - 1].len + 2;
e354a5
       if (cnt > 2)
e354a5
 	{
e354a5
 	  total <<= 1;
e354a5
@@ -94,26 +171,48 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
e354a5
 	}
e354a5
     }
e354a5
 
e354a5
-  /* The result structure: we use a very compressed way to store the
e354a5
-     various combinations of capability names.  */
e354a5
-  *sz = 1 << cnt;
e354a5
-  result = (struct r_strlenpair *) malloc (*sz * sizeof (*result) + total);
e354a5
-  if (result == NULL)
e354a5
+  *sz = hwcaps_counts.count + (1 << cnt);
e354a5
+
e354a5
+  /* This is the overall result, including both glibc-hwcaps
e354a5
+     subdirectories and the legacy hwcaps subdirectories using the
e354a5
+     power set construction.  */
e354a5
+  struct r_strlenpair *overall_result
e354a5
+    = malloc (*sz * sizeof (*result) + total);
e354a5
+  if (overall_result == NULL)
e354a5
     _dl_signal_error (ENOMEM, NULL, NULL,
e354a5
 		      N_("cannot create capability list"));
e354a5
 
e354a5
+  /* Fill in the glibc-hwcaps subdirectories.  */
e354a5
+  {
e354a5
+    struct copy_hwcaps target;
e354a5
+    target.next_pair = overall_result;
e354a5
+    target.next_string = (char *) (overall_result + *sz);
e354a5
+    copy_hwcaps (&target, glibc_hwcaps_prepend, -1, NULL);
e354a5
+    copy_hwcaps (&target, _dl_hwcaps_subdirs,
e354a5
+		 hwcaps_subdirs_active, glibc_hwcaps_mask);
e354a5
+    /* Set up the write target for the power set construction.  */
e354a5
+    result = target.next_pair;
e354a5
+    cp = target.next_string;
e354a5
+  }
e354a5
+
e354a5
+
e354a5
+  /* Power set construction begins here.  We use a very compressed way
e354a5
+     to store the various combinations of capability names.  */
e354a5
+
e354a5
   if (cnt == 1)
e354a5
     {
e354a5
-      result[0].str = (char *) (result + *sz);
e354a5
+      result[0].str = cp;
e354a5
       result[0].len = temp[0].len + 1;
e354a5
-      result[1].str = (char *) (result + *sz);
e354a5
+      result[1].str = cp;
e354a5
       result[1].len = 0;
e354a5
-      cp = __mempcpy ((char *) (result + *sz), temp[0].str, temp[0].len);
e354a5
+      cp = __mempcpy (cp, temp[0].str, temp[0].len);
e354a5
       *cp = '/';
e354a5
-      *sz = 2;
e354a5
-      *max_capstrlen = result[0].len;
e354a5
+      if (result[0].len > hwcaps_counts.maximum_length)
e354a5
+	*max_capstrlen = result[0].len;
e354a5
+      else
e354a5
+	*max_capstrlen = hwcaps_counts.maximum_length;
e354a5
 
e354a5
-      return result;
e354a5
+      return overall_result;
e354a5
     }
e354a5
 
e354a5
   /* Fill in the information.  This follows the following scheme
e354a5
@@ -124,7 +223,7 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
e354a5
 	      #3: 0, 3			1001
e354a5
      This allows the representation of all possible combinations of
e354a5
      capability names in the string.  First generate the strings.  */
e354a5
-  result[1].str = result[0].str = cp = (char *) (result + *sz);
e354a5
+  result[1].str = result[0].str = cp;
e354a5
 #define add(idx) \
e354a5
       cp = __mempcpy (__mempcpy (cp, temp[idx].str, temp[idx].len), "/", 1);
e354a5
   if (cnt == 2)
e354a5
@@ -191,7 +290,10 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
e354a5
   while (--n != 0);
e354a5
 
e354a5
   /* The maximum string length.  */
e354a5
-  *max_capstrlen = result[0].len;
e354a5
+  if (result[0].len > hwcaps_counts.maximum_length)
e354a5
+    *max_capstrlen = result[0].len;
e354a5
+  else
e354a5
+    *max_capstrlen = hwcaps_counts.maximum_length;
e354a5
 
e354a5
-  return result;
e354a5
+  return overall_result;
e354a5
 }
e354a5
diff --git a/elf/dl-hwcaps.h b/elf/dl-hwcaps.h
e354a5
index d69ee11dc27bb5e5..3fcfbceb1a8fc1c8 100644
e354a5
--- a/elf/dl-hwcaps.h
e354a5
+++ b/elf/dl-hwcaps.h
e354a5
@@ -16,6 +16,11 @@
e354a5
    License along with the GNU C Library; if not, see
e354a5
    <http://www.gnu.org/licenses/>.  */
e354a5
 
e354a5
+#ifndef _DL_HWCAPS_H
e354a5
+#define _DL_HWCAPS_H
e354a5
+
e354a5
+#include <stdint.h>
e354a5
+
e354a5
 #include <elf/dl-tunables.h>
e354a5
 
e354a5
 #if HAVE_TUNABLES
e354a5
@@ -28,3 +33,103 @@
e354a5
 #  define GET_HWCAP_MASK() (0)
e354a5
 # endif
e354a5
 #endif
e354a5
+
e354a5
+#define GLIBC_HWCAPS_SUBDIRECTORY "glibc-hwcaps"
e354a5
+#define GLIBC_HWCAPS_PREFIX GLIBC_HWCAPS_SUBDIRECTORY "/"
e354a5
+
e354a5
+/* Used by _dl_hwcaps_split below, to split strings at ':'
e354a5
+   separators.  */
e354a5
+struct dl_hwcaps_split
e354a5
+{
e354a5
+  const char *segment;          /* Start of the current segment.  */
e354a5
+  size_t length;                /* Number of bytes until ':' or NUL.  */
e354a5
+};
e354a5
+
e354a5
+/* Prepare *S to parse SUBJECT, for future _dl_hwcaps_split calls.  If
e354a5
+   SUBJECT is NULL, it is treated as the empty string.  */
e354a5
+static inline void
e354a5
+_dl_hwcaps_split_init (struct dl_hwcaps_split *s, const char *subject)
e354a5
+{
e354a5
+  s->segment = subject;
e354a5
+  /* The initial call to _dl_hwcaps_split will not skip anything.  */
e354a5
+  s->length = 0;
e354a5
+}
e354a5
+
e354a5
+/* Extract the next non-empty string segment, up to ':' or the null
e354a5
+   terminator.  Return true if one more segment was found, or false if
e354a5
+   the end of the string was reached.  On success, S->segment is the
e354a5
+   start of the segment found, and S->length is its length.
e354a5
+   (Typically, S->segment[S->length] is not null.)  */
e354a5
+_Bool _dl_hwcaps_split (struct dl_hwcaps_split *s) attribute_hidden;
e354a5
+
e354a5
+/* Similar to dl_hwcaps_split, but with bit-based and name-based
e354a5
+   masking.  */
e354a5
+struct dl_hwcaps_split_masked
e354a5
+{
e354a5
+  struct dl_hwcaps_split split;
e354a5
+
e354a5
+  /* For used by the iterator implementation.  */
e354a5
+  const char *mask;
e354a5
+  uint32_t bitmask;
e354a5
+};
e354a5
+
e354a5
+/* Prepare *S for iteration with _dl_hwcaps_split_masked.  Only HWCAP
e354a5
+   names in SUBJECT whose bit is set in BITMASK and whose name is in
e354a5
+   MASK will be returned.  SUBJECT must not contain empty HWCAP names.
e354a5
+   If MASK is NULL, no name-based masking is applied.  Likewise for
e354a5
+   BITMASK if BITMASK is -1 (infinite number of bits).  */
e354a5
+static inline void
e354a5
+_dl_hwcaps_split_masked_init (struct dl_hwcaps_split_masked *s,
e354a5
+                              const char *subject,
e354a5
+                              uint32_t bitmask, const char *mask)
e354a5
+{
e354a5
+  _dl_hwcaps_split_init (&s->split, subject);
e354a5
+  s->bitmask = bitmask;
e354a5
+  s->mask = mask;
e354a5
+}
e354a5
+
e354a5
+/* Like _dl_hwcaps_split, but apply masking.  */
e354a5
+_Bool _dl_hwcaps_split_masked (struct dl_hwcaps_split_masked *s)
e354a5
+  attribute_hidden;
e354a5
+
e354a5
+/* Returns true if the colon-separated HWCAP list HWCAPS contains the
e354a5
+   capability NAME (with length NAME_LENGTH).  If HWCAPS is NULL, the
e354a5
+   function returns true.  */
e354a5
+_Bool _dl_hwcaps_contains (const char *hwcaps, const char *name,
e354a5
+                           size_t name_length) attribute_hidden;
e354a5
+
e354a5
+/* Colon-separated string of glibc-hwcaps subdirectories, without the
e354a5
+   "glibc-hwcaps/" prefix.  The most preferred subdirectory needs to
e354a5
+   be listed first.  Up to 32 subdirectories are supported, limited by
e354a5
+   the width of the uint32_t mask.  */
e354a5
+extern const char _dl_hwcaps_subdirs[] attribute_hidden;
e354a5
+
e354a5
+/* Returns a bitmap of active subdirectories in _dl_hwcaps_subdirs.
e354a5
+   Bit 0 (the LSB) corresponds to the first substring in
e354a5
+   _dl_hwcaps_subdirs, bit 1 to the second substring, and so on.
e354a5
+   There is no direct correspondence between HWCAP bitmasks and this
e354a5
+   bitmask.  */
e354a5
+uint32_t _dl_hwcaps_subdirs_active (void) attribute_hidden;
e354a5
+
e354a5
+/* Returns a bitmask that marks the last ACTIVE subdirectories in a
e354a5
+   _dl_hwcaps_subdirs_active string (containing SUBDIRS directories in
e354a5
+   total) as active.  Intended for use in _dl_hwcaps_subdirs_active
e354a5
+   implementations (if a contiguous tail of the list in
e354a5
+   _dl_hwcaps_subdirs is selected).  */
e354a5
+static inline uint32_t
e354a5
+_dl_hwcaps_subdirs_build_bitmask (int subdirs, int active)
e354a5
+{
e354a5
+  /* Leading subdirectories that are not active.  */
e354a5
+  int inactive = subdirs - active;
e354a5
+  if (inactive == 32)
e354a5
+    return 0;
e354a5
+
e354a5
+  uint32_t mask;
e354a5
+  if (subdirs != 32)
e354a5
+    mask = (1U << subdirs) - 1;
e354a5
+  else
e354a5
+    mask = -1;
e354a5
+  return mask ^ ((1U << inactive) - 1);
e354a5
+}
e354a5
+
e354a5
+#endif /* _DL_HWCAPS_H */
e354a5
diff --git a/elf/dl-hwcaps_split.c b/elf/dl-hwcaps_split.c
e354a5
new file mode 100644
e354a5
index 0000000000000000..95225e9f409ca229
e354a5
--- /dev/null
e354a5
+++ b/elf/dl-hwcaps_split.c
e354a5
@@ -0,0 +1,77 @@
e354a5
+/* Hardware capability support for run-time dynamic loader.  String splitting.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <dl-hwcaps.h>
e354a5
+#include <stdbool.h>
e354a5
+#include <string.h>
e354a5
+
e354a5
+_Bool
e354a5
+_dl_hwcaps_split (struct dl_hwcaps_split *s)
e354a5
+{
e354a5
+  if (s->segment == NULL)
e354a5
+    return false;
e354a5
+
e354a5
+  /* Skip over the previous segment.   */
e354a5
+  s->segment += s->length;
e354a5
+
e354a5
+  /* Consume delimiters.  This also avoids returning an empty
e354a5
+     segment.  */
e354a5
+  while (*s->segment == ':')
e354a5
+    ++s->segment;
e354a5
+  if (*s->segment == '\0')
e354a5
+    return false;
e354a5
+
e354a5
+  /* This could use strchrnul, but we would have to link the function
e354a5
+     into ld.so for that.  */
e354a5
+  const char *colon = strchr (s->segment, ':');
e354a5
+  if (colon == NULL)
e354a5
+    s->length = strlen (s->segment);
e354a5
+  else
e354a5
+    s->length = colon - s->segment;
e354a5
+  return true;
e354a5
+}
e354a5
+
e354a5
+_Bool
e354a5
+_dl_hwcaps_split_masked (struct dl_hwcaps_split_masked *s)
e354a5
+{
e354a5
+  while (true)
e354a5
+    {
e354a5
+      if (!_dl_hwcaps_split (&s->split))
e354a5
+        return false;
e354a5
+      bool active = s->bitmask & 1;
e354a5
+      s->bitmask >>= 1;
e354a5
+      if (active && _dl_hwcaps_contains (s->mask,
e354a5
+                                         s->split.segment, s->split.length))
e354a5
+        return true;
e354a5
+    }
e354a5
+}
e354a5
+
e354a5
+_Bool
e354a5
+_dl_hwcaps_contains (const char *hwcaps, const char *name, size_t name_length)
e354a5
+{
e354a5
+  if (hwcaps == NULL)
e354a5
+    return true;
e354a5
+
e354a5
+  struct dl_hwcaps_split split;
e354a5
+  _dl_hwcaps_split_init (&split, hwcaps);
e354a5
+  while (_dl_hwcaps_split (&split))
e354a5
+    if (split.length == name_length
e354a5
+        && memcmp (split.segment, name, name_length) == 0)
e354a5
+      return true;
e354a5
+  return false;
e354a5
+}
e354a5
diff --git a/elf/dl-load.c b/elf/dl-load.c
e354a5
index d2be21ea7d1545fe..fee08d7816714178 100644
e354a5
--- a/elf/dl-load.c
e354a5
+++ b/elf/dl-load.c
e354a5
@@ -682,7 +682,9 @@ cache_rpath (struct link_map *l,
e354a5
 
e354a5
 
e354a5
 void
e354a5
-_dl_init_paths (const char *llp, const char *source)
e354a5
+_dl_init_paths (const char *llp, const char *source,
e354a5
+		const char *glibc_hwcaps_prepend,
e354a5
+		const char *glibc_hwcaps_mask)
e354a5
 {
e354a5
   size_t idx;
e354a5
   const char *strp;
e354a5
@@ -697,7 +699,8 @@ _dl_init_paths (const char *llp, const char *source)
e354a5
 
e354a5
 #ifdef SHARED
e354a5
   /* Get the capabilities.  */
e354a5
-  capstr = _dl_important_hwcaps (&ncapstr, &max_capstrlen);
e354a5
+  capstr = _dl_important_hwcaps (glibc_hwcaps_prepend, glibc_hwcaps_mask,
e354a5
+				 &ncapstr, &max_capstrlen);
e354a5
 #endif
e354a5
 
e354a5
   /* First set up the rest of the default search directory entries.  */
e354a5
diff --git a/elf/dl-main.h b/elf/dl-main.h
e354a5
index b51256d3b48230b0..566713a0d10cfdb7 100644
e354a5
--- a/elf/dl-main.h
e354a5
+++ b/elf/dl-main.h
e354a5
@@ -84,6 +84,14 @@ struct dl_main_state
e354a5
   /* The preload list passed as a command argument.  */
e354a5
   const char *preloadarg;
e354a5
 
e354a5
+  /* Additional glibc-hwcaps subdirectories to search first.
e354a5
+     Colon-separated list.  */
e354a5
+  const char *glibc_hwcaps_prepend;
e354a5
+
e354a5
+  /* Mask for the internal glibc-hwcaps subdirectories.
e354a5
+     Colon-separated list.  */
e354a5
+  const char *glibc_hwcaps_mask;
e354a5
+
e354a5
   enum rtld_mode mode;
e354a5
 
e354a5
   /* True if any of the debugging options is enabled.  */
e354a5
@@ -98,7 +106,8 @@ struct dl_main_state
e354a5
 static inline void
e354a5
 call_init_paths (const struct dl_main_state *state)
e354a5
 {
e354a5
-  _dl_init_paths (state->library_path, state->library_path_source);
e354a5
+  _dl_init_paths (state->library_path, state->library_path_source,
e354a5
+                  state->glibc_hwcaps_prepend, state->glibc_hwcaps_mask);
e354a5
 }
e354a5
 
e354a5
 /* Print ld.so usage information and exit.  */
e354a5
diff --git a/elf/dl-support.c b/elf/dl-support.c
e354a5
index fb9672367f8d6abd..34be8e5babfb6af3 100644
e354a5
--- a/elf/dl-support.c
e354a5
+++ b/elf/dl-support.c
e354a5
@@ -315,7 +315,10 @@ _dl_non_dynamic_init (void)
e354a5
 
e354a5
   /* Initialize the data structures for the search paths for shared
e354a5
      objects.  */
e354a5
-  _dl_init_paths (getenv ("LD_LIBRARY_PATH"), "LD_LIBRARY_PATH");
e354a5
+  _dl_init_paths (getenv ("LD_LIBRARY_PATH"), "LD_LIBRARY_PATH",
e354a5
+		  /* No glibc-hwcaps selection support in statically
e354a5
+		     linked binaries.  */
e354a5
+		  NULL, NULL);
e354a5
 
e354a5
   /* Remember the last search directory added at startup.  */
e354a5
   _dl_init_all_dirs = GL(dl_all_dirs);
e354a5
diff --git a/elf/dl-usage.c b/elf/dl-usage.c
e354a5
index 796ad38b43c2211b..e22a9c39427187d1 100644
e354a5
--- a/elf/dl-usage.c
e354a5
+++ b/elf/dl-usage.c
e354a5
@@ -83,7 +83,7 @@ print_search_path_for_help (struct dl_main_state *state)
e354a5
 {
e354a5
   if (__rtld_search_dirs.dirs == NULL)
e354a5
     /* The run-time search paths have not yet been initialized.  */
e354a5
-    _dl_init_paths (state->library_path, state->library_path_source);
e354a5
+    call_init_paths (state);
e354a5
 
e354a5
   _dl_printf ("\nShared library search path:\n");
e354a5
 
e354a5
@@ -132,6 +132,67 @@ print_hwcap_1_finish (bool *first)
e354a5
     _dl_printf (")\n");
e354a5
 }
e354a5
 
e354a5
+/* Print the header for print_hwcaps_subdirectories.  */
e354a5
+static void
e354a5
+print_hwcaps_subdirectories_header (bool *nothing_printed)
e354a5
+{
e354a5
+  if (*nothing_printed)
e354a5
+    {
e354a5
+      _dl_printf ("\n\
e354a5
+Subdirectories of glibc-hwcaps directories, in priority order:\n");
e354a5
+      *nothing_printed = false;
e354a5
+    }
e354a5
+}
e354a5
+
e354a5
+/* Print the HWCAP name itself, indented.  */
e354a5
+static void
e354a5
+print_hwcaps_subdirectories_name (const struct dl_hwcaps_split *split)
e354a5
+{
e354a5
+  _dl_write (STDOUT_FILENO, "  ", 2);
e354a5
+  _dl_write (STDOUT_FILENO, split->segment, split->length);
e354a5
+}
e354a5
+
e354a5
+/* Print the list of recognized glibc-hwcaps subdirectories.  */
e354a5
+static void
e354a5
+print_hwcaps_subdirectories (const struct dl_main_state *state)
e354a5
+{
e354a5
+  bool nothing_printed = true;
e354a5
+  struct dl_hwcaps_split split;
e354a5
+
e354a5
+  /* The prepended glibc-hwcaps subdirectories.  */
e354a5
+  _dl_hwcaps_split_init (&split, state->glibc_hwcaps_prepend);
e354a5
+  while (_dl_hwcaps_split (&split))
e354a5
+    {
e354a5
+      print_hwcaps_subdirectories_header (&nothing_printed);
e354a5
+      print_hwcaps_subdirectories_name (&split);
e354a5
+      bool first = true;
e354a5
+      print_hwcap_1 (&first, true, "searched");
e354a5
+      print_hwcap_1_finish (&first);
e354a5
+    }
e354a5
+
e354a5
+  /* The built-in glibc-hwcaps subdirectories.  Do the filtering
e354a5
+     manually, so that more precise diagnostics are possible.  */
e354a5
+  uint32_t mask = _dl_hwcaps_subdirs_active ();
e354a5
+  _dl_hwcaps_split_init (&split, _dl_hwcaps_subdirs);
e354a5
+  while (_dl_hwcaps_split (&split))
e354a5
+    {
e354a5
+      print_hwcaps_subdirectories_header (&nothing_printed);
e354a5
+      print_hwcaps_subdirectories_name (&split);
e354a5
+      bool first = true;
e354a5
+      print_hwcap_1 (&first, mask & 1, "supported");
e354a5
+      bool listed = _dl_hwcaps_contains (state->glibc_hwcaps_mask,
e354a5
+                                         split.segment, split.length);
e354a5
+      print_hwcap_1 (&first, !listed, "masked");
e354a5
+      print_hwcap_1 (&first, (mask & 1) && listed, "searched");
e354a5
+      print_hwcap_1_finish (&first);
e354a5
+      mask >>= 1;
e354a5
+    }
e354a5
+
e354a5
+  if (nothing_printed)
e354a5
+    _dl_printf ("\n\
e354a5
+No subdirectories of glibc-hwcaps directories are searched.\n");
e354a5
+}
e354a5
+
e354a5
 /* Write a list of hwcap subdirectories to standard output.  See
e354a5
  _dl_important_hwcaps in dl-hwcaps.c.  */
e354a5
 static void
e354a5
@@ -186,6 +247,10 @@ setting environment variables (which would be inherited by subprocesses).\n\
e354a5
   --inhibit-cache       Do not use " LD_SO_CACHE "\n\
e354a5
   --library-path PATH   use given PATH instead of content of the environment\n\
e354a5
                         variable LD_LIBRARY_PATH\n\
e354a5
+  --glibc-hwcaps-prepend LIST\n\
e354a5
+                        search glibc-hwcaps subdirectories in LIST\n\
e354a5
+  --glibc-hwcaps-mask LIST\n\
e354a5
+                        only search built-in subdirectories if in LIST\n\
e354a5
   --inhibit-rpath LIST  ignore RUNPATH and RPATH information in object names\n\
e354a5
                         in LIST\n\
e354a5
   --audit LIST          use objects named in LIST as auditors\n\
e354a5
@@ -198,6 +263,7 @@ This program interpreter self-identifies as: " RTLD "\n\
e354a5
 ",
e354a5
               argv0);
e354a5
   print_search_path_for_help (state);
e354a5
+  print_hwcaps_subdirectories (state);
e354a5
   print_legacy_hwcap_directories ();
e354a5
   _exit (EXIT_SUCCESS);
e354a5
 }
e354a5
diff --git a/elf/markermodMARKER-VALUE.c b/elf/markermodMARKER-VALUE.c
e354a5
new file mode 100644
e354a5
index 0000000000000000..99bdcf71a4e219c6
e354a5
--- /dev/null
e354a5
+++ b/elf/markermodMARKER-VALUE.c
e354a5
@@ -0,0 +1,29 @@
e354a5
+/* Source file template for building shared objects with marker functions.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+/* MARKER and VALUE must be set on the compiler command line.  */
e354a5
+
e354a5
+#ifndef MARKER
e354a5
+# error MARKER not defined
e354a5
+#endif
e354a5
+
e354a5
+int
e354a5
+MARKER (void)
e354a5
+{
e354a5
+  return VALUE;
e354a5
+}
e354a5
diff --git a/elf/rtld.c b/elf/rtld.c
e354a5
index da1eef108508b95f..fde5a6a4a485207e 100644
e354a5
--- a/elf/rtld.c
e354a5
+++ b/elf/rtld.c
e354a5
@@ -287,6 +287,8 @@ dl_main_state_init (struct dl_main_state *state)
e354a5
   state->library_path_source = NULL;
e354a5
   state->preloadlist = NULL;
e354a5
   state->preloadarg = NULL;
e354a5
+  state->glibc_hwcaps_prepend = NULL;
e354a5
+  state->glibc_hwcaps_mask = NULL;
e354a5
   state->mode = rtld_mode_normal;
e354a5
   state->any_debug = false;
e354a5
   state->version_info = false;
e354a5
@@ -1238,6 +1240,22 @@ dl_main (const ElfW(Phdr) *phdr,
e354a5
 	  {
e354a5
 	    argv0 = _dl_argv[2];
e354a5
 
e354a5
+	    _dl_skip_args += 2;
e354a5
+	    _dl_argc -= 2;
e354a5
+	    _dl_argv += 2;
e354a5
+	  }
e354a5
+	else if (strcmp (_dl_argv[1], "--glibc-hwcaps-prepend") == 0
e354a5
+		 && _dl_argc > 2)
e354a5
+	  {
e354a5
+	    state.glibc_hwcaps_prepend = _dl_argv[2];
e354a5
+	    _dl_skip_args += 2;
e354a5
+	    _dl_argc -= 2;
e354a5
+	    _dl_argv += 2;
e354a5
+	  }
e354a5
+	else if (strcmp (_dl_argv[1], "--glibc-hwcaps-mask") == 0
e354a5
+		 && _dl_argc > 2)
e354a5
+	  {
e354a5
+	    state.glibc_hwcaps_mask = _dl_argv[2];
e354a5
 	    _dl_skip_args += 2;
e354a5
 	    _dl_argc -= 2;
e354a5
 	    _dl_argv += 2;
e354a5
diff --git a/elf/tst-dl-hwcaps_split.c b/elf/tst-dl-hwcaps_split.c
e354a5
new file mode 100644
e354a5
index 0000000000000000..364159427074bd1c
e354a5
--- /dev/null
e354a5
+++ b/elf/tst-dl-hwcaps_split.c
e354a5
@@ -0,0 +1,148 @@
e354a5
+/* Unit tests for dl-hwcaps.c.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <array_length.h>
e354a5
+#include <dl-hwcaps.h>
e354a5
+#include <string.h>
e354a5
+#include <support/check.h>
e354a5
+
e354a5
+static void
e354a5
+check_split_masked (const char *input, int32_t bitmask, const char *mask,
e354a5
+                    const char *expected[], size_t expected_length)
e354a5
+{
e354a5
+  struct dl_hwcaps_split_masked split;
e354a5
+  _dl_hwcaps_split_masked_init (&split, input, bitmask, mask);
e354a5
+  size_t index = 0;
e354a5
+  while (_dl_hwcaps_split_masked (&split))
e354a5
+    {
e354a5
+      TEST_VERIFY_EXIT (index < expected_length);
e354a5
+      TEST_COMPARE_BLOB (expected[index], strlen (expected[index]),
e354a5
+                         split.split.segment, split.split.length);
e354a5
+      ++index;
e354a5
+    }
e354a5
+  TEST_COMPARE (index, expected_length);
e354a5
+}
e354a5
+
e354a5
+static void
e354a5
+check_split (const char *input,
e354a5
+             const char *expected[], size_t expected_length)
e354a5
+{
e354a5
+  struct dl_hwcaps_split split;
e354a5
+  _dl_hwcaps_split_init (&split, input);
e354a5
+  size_t index = 0;
e354a5
+  while (_dl_hwcaps_split (&split))
e354a5
+    {
e354a5
+      TEST_VERIFY_EXIT (index < expected_length);
e354a5
+      TEST_COMPARE_BLOB (expected[index], strlen (expected[index]),
e354a5
+                         split.segment, split.length);
e354a5
+      ++index;
e354a5
+    }
e354a5
+  TEST_COMPARE (index, expected_length);
e354a5
+
e354a5
+  /* Reuse the test cases with masking that does not actually remove
e354a5
+     anything.  */
e354a5
+  check_split_masked (input, -1, NULL, expected, expected_length);
e354a5
+  check_split_masked (input, -1, input, expected, expected_length);
e354a5
+}
e354a5
+
e354a5
+static int
e354a5
+do_test (void)
e354a5
+{
e354a5
+  /* Splitting tests, without masking.  */
e354a5
+  check_split (NULL, NULL, 0);
e354a5
+  check_split ("", NULL, 0);
e354a5
+  check_split (":", NULL, 0);
e354a5
+  check_split ("::", NULL, 0);
e354a5
+
e354a5
+  {
e354a5
+    const char *expected[] = { "first" };
e354a5
+    check_split ("first", expected, array_length (expected));
e354a5
+    check_split (":first", expected, array_length (expected));
e354a5
+    check_split ("first:", expected, array_length (expected));
e354a5
+    check_split (":first:", expected, array_length (expected));
e354a5
+  }
e354a5
+
e354a5
+  {
e354a5
+    const char *expected[] = { "first", "second" };
e354a5
+    check_split ("first:second", expected, array_length (expected));
e354a5
+    check_split ("first::second", expected, array_length (expected));
e354a5
+    check_split (":first:second", expected, array_length (expected));
e354a5
+    check_split ("first:second:", expected, array_length (expected));
e354a5
+    check_split (":first:second:", expected, array_length (expected));
e354a5
+  }
e354a5
+
e354a5
+  /* Splitting tests with masking.  */
e354a5
+  {
e354a5
+    const char *expected[] = { "first" };
e354a5
+    check_split_masked ("first", 3, "first:second",
e354a5
+                        expected, array_length (expected));
e354a5
+    check_split_masked ("first:second", 3, "first:",
e354a5
+                        expected, array_length (expected));
e354a5
+    check_split_masked ("first:second", 1, NULL,
e354a5
+                        expected, array_length (expected));
e354a5
+  }
e354a5
+  {
e354a5
+    const char *expected[] = { "second" };
e354a5
+    check_split_masked ("first:second", 3, "second",
e354a5
+                        expected, array_length (expected));
e354a5
+    check_split_masked ("first:second:third", -1, "second:",
e354a5
+                        expected, array_length (expected));
e354a5
+    check_split_masked ("first:second", 2, NULL,
e354a5
+                        expected, array_length (expected));
e354a5
+    check_split_masked ("first:second:third", 2, "first:second",
e354a5
+                        expected, array_length (expected));
e354a5
+  }
e354a5
+
e354a5
+  /* Tests for _dl_hwcaps_contains.  */
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains (NULL, "first", strlen ("first")));
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains (NULL, "", 0));
e354a5
+  TEST_VERIFY (! _dl_hwcaps_contains ("", "first", strlen ("first")));
e354a5
+  TEST_VERIFY (! _dl_hwcaps_contains ("firs", "first", strlen ("first")));
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains ("firs", "first", strlen ("first") - 1));
e354a5
+  for (int i = 0; i < strlen ("first"); ++i)
e354a5
+    TEST_VERIFY (! _dl_hwcaps_contains ("first", "first", i));
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains ("first", "first", strlen ("first")));
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains ("first:", "first", strlen ("first")));
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains ("first:second",
e354a5
+                                    "first", strlen ("first")));
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains (":first:second", "first",
e354a5
+                                    strlen ("first")));
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains ("first:second", "second",
e354a5
+                                    strlen ("second")));
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains ("first:second:", "second",
e354a5
+                                    strlen ("second")));
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains ("first::second:", "second",
e354a5
+                                    strlen ("second")));
e354a5
+  TEST_VERIFY (_dl_hwcaps_contains ("first:second::", "second",
e354a5
+                                    strlen ("second")));
e354a5
+  for (int i = 0; i < strlen ("second"); ++i)
e354a5
+    {
e354a5
+      TEST_VERIFY (!_dl_hwcaps_contains ("first:second", "second", i));
e354a5
+      TEST_VERIFY (!_dl_hwcaps_contains ("first:second:", "second", i));
e354a5
+      TEST_VERIFY (!_dl_hwcaps_contains ("first:second::", "second", i));
e354a5
+      TEST_VERIFY (!_dl_hwcaps_contains ("first::second", "second", i));
e354a5
+    }
e354a5
+
e354a5
+  return 0;
e354a5
+}
e354a5
+
e354a5
+#include <support/test-driver.c>
e354a5
+
e354a5
+/* Rebuild the sources here because the object file is built for
e354a5
+   inclusion into the dynamic loader.  */
e354a5
+#include "dl-hwcaps_split.c"
e354a5
diff --git a/elf/tst-glibc-hwcaps-mask.c b/elf/tst-glibc-hwcaps-mask.c
e354a5
new file mode 100644
e354a5
index 0000000000000000..27b09b358caf7853
e354a5
--- /dev/null
e354a5
+++ b/elf/tst-glibc-hwcaps-mask.c
e354a5
@@ -0,0 +1,31 @@
e354a5
+/* Test that --glibc-hwcaps-mask works.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <support/check.h>
e354a5
+
e354a5
+extern int marker1 (void);
e354a5
+
e354a5
+static int
e354a5
+do_test (void)
e354a5
+{
e354a5
+  /* The marker1 function in elf/markermod1.so returns 1.  */
e354a5
+  TEST_COMPARE (marker1 (), 1);
e354a5
+  return 0;
e354a5
+}
e354a5
+
e354a5
+#include <support/test-driver.c>
e354a5
diff --git a/elf/tst-glibc-hwcaps-prepend.c b/elf/tst-glibc-hwcaps-prepend.c
e354a5
new file mode 100644
e354a5
index 0000000000000000..57d7319f1484ca4b
e354a5
--- /dev/null
e354a5
+++ b/elf/tst-glibc-hwcaps-prepend.c
e354a5
@@ -0,0 +1,32 @@
e354a5
+/* Test that --glibc-hwcaps-prepend works.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <support/check.h>
e354a5
+
e354a5
+extern int marker1 (void);
e354a5
+
e354a5
+static int
e354a5
+do_test (void)
e354a5
+{
e354a5
+  /* The marker1 function in
e354a5
+     glibc-hwcaps/prepend-markermod1/markermod1.so returns 2.  */
e354a5
+  TEST_COMPARE (marker1 (), 2);
e354a5
+  return 0;
e354a5
+}
e354a5
+
e354a5
+#include <support/test-driver.c>
e354a5
diff --git a/elf/tst-glibc-hwcaps.c b/elf/tst-glibc-hwcaps.c
e354a5
new file mode 100644
e354a5
index 0000000000000000..28f47cf8914a1f2a
e354a5
--- /dev/null
e354a5
+++ b/elf/tst-glibc-hwcaps.c
e354a5
@@ -0,0 +1,28 @@
e354a5
+/* Stub test for glibc-hwcaps.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <stdio.h>
e354a5
+
e354a5
+static int
e354a5
+do_test (void)
e354a5
+{
e354a5
+  puts ("info: generic tst-glibc-hwcaps (tests nothing)");
e354a5
+  return 0;
e354a5
+}
e354a5
+
e354a5
+#include <support/test-driver.c>
e354a5
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
e354a5
index 2c9fdeb286bdaadf..77923499d3de4366 100644
e354a5
--- a/sysdeps/generic/ldsodefs.h
e354a5
+++ b/sysdeps/generic/ldsodefs.h
e354a5
@@ -1045,8 +1045,13 @@ extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
e354a5
      attribute_hidden;
e354a5
 
e354a5
 /* Initialize the basic data structure for the search paths.  SOURCE
e354a5
-   is either "LD_LIBRARY_PATH" or "--library-path".  */
e354a5
-extern void _dl_init_paths (const char *library_path, const char *source)
e354a5
+   is either "LD_LIBRARY_PATH" or "--library-path".
e354a5
+   GLIBC_HWCAPS_PREPEND adds additional glibc-hwcaps subdirectories to
e354a5
+   search.  GLIBC_HWCAPS_MASK is used to filter the built-in
e354a5
+   subdirectories if not NULL.  */
e354a5
+extern void _dl_init_paths (const char *library_path, const char *source,
e354a5
+			    const char *glibc_hwcaps_prepend,
e354a5
+			    const char *glibc_hwcaps_mask)
e354a5
   attribute_hidden;
e354a5
 
e354a5
 /* Gather the information needed to install the profiling tables and start
e354a5
@@ -1070,9 +1075,14 @@ extern void _dl_show_auxv (void) attribute_hidden;
e354a5
 extern char *_dl_next_ld_env_entry (char ***position) attribute_hidden;
e354a5
 
e354a5
 /* Return an array with the names of the important hardware
e354a5
-   capabilities.  The length of the array is written to *SZ, and the
e354a5
-   maximum of all strings length is written to *MAX_CAPSTRLEN.  */
e354a5
-const struct r_strlenpair *_dl_important_hwcaps (size_t *sz,
e354a5
+   capabilities.  PREPEND is a colon-separated list of glibc-hwcaps
e354a5
+   directories to search first.  MASK is a colon-separated list used
e354a5
+   to filter the built-in glibc-hwcaps subdirectories.  The length of
e354a5
+   the array is written to *SZ, and the maximum of all strings length
e354a5
+   is written to *MAX_CAPSTRLEN.  */
e354a5
+const struct r_strlenpair *_dl_important_hwcaps (const char *prepend,
e354a5
+						 const char *mask,
e354a5
+						 size_t *sz,
e354a5
 						 size_t *max_capstrlen)
e354a5
   attribute_hidden;
e354a5