[PATCH 00/30] RFC: elf: glibc-hwcaps support

classic Classic list List threaded Threaded
38 messages Options
12
Reply | Threaded
Open this post in threaded view
|

[PATCH 00/30] RFC: elf: glibc-hwcaps support

Sourceware - libc-alpha mailing list
This series implements searching for alternative library implementations
under glibc-hwcaps subdirectories on the library search path.  It's
lacking tests, documentation, and a NEWS file update, but I wanted to
submit it early for discussion (and perhaps review of some of the
cleanup patches).

For background, it is best reference first the discussion about the
current hwcaps subdirectory processing:

  hwcaps subdirectory selection in the dynamic loader
  <https://sourceware.org/pipermail/libc-alpha/2020-May/113757.html>

The new approach uses a special subdirectory, called "glibc-hwcaps",
which contains nested subdirectories with all the alternative library
implementations. The glibc-hwcaps subdirectory can be present in every
subdirectory of the library search path.

ldconfig recognizes the "glibc-hwcaps" name, and it receives special
treatment if it exists: subdirectories of the directory (but the
directory itself) are scanned for libraries.  If a library is found, it
is put into the cache, and associated with the name of the subdirectory
(of glibc-hwcaps).  ldconfig does this for all subdirectories (whose
name does not begin with '.'), so it does not have to be updated if new
subdirectories are added.  There is currently no symbolic link
processing for these subdirectories.  In the future, they might store
multiple implementations with different GNU property notes, and we might
want to select those through the cache as well.  (I'm open to changing
this part to align more with traditional cache processing.)

At run time, the dynamic loader has a built-in list of HWCAP
subdirectory names (which are different from the legacy HWCAP
directories).  The new “ld.so --help” option shows these subdirectory
names.  For LD_LIBRARY_PATH processing, ld.so simply attempts to open
libraries under their soname in the active/supported glibc-hwcaps
subdirectories.  (This part is quite similar to the original HWCAP
subdirectory handling, except that the new subdirectories do not nest.)
For ld.so.cache processing, ld.so sorts the active/supported HWCAP
subdirectory names and merges this list against the list of subdirectory
names as stored in a new ld.so.cache extension section.  Each
glibc-hwcaps library listed in the cache has a HWCAP field that contains
an index into the HWCAP subdirectory array of the cache file.  (A
previously unused HWCAP bit is set so that these entries are ignored by
existing dynamic linkers.)  The dynamic linker loads the library with
has the lowest positive preference value (essentially from the
subdirectory that comes first in its consolidated HWCAP name list).

There are new ld.so options, --glibc-hwcaps-prepend and
--glibc-hwcaps-mask, to change the list of glibc-hwcaps subdirectories
searched.  Due to the way ldconfig has been modified, it is possible to
pick up completely new subdirectories (not known to the implementation)
using --glibc-hwcaps-prepend even if ld.so.cache is used.

With these changes, on a current x86-64 machine (with AVX2-level CPU
features), you can drop a shared object into the directory

  /usr/lib64/glibc-hwcaps/x86-102

and the dynamic linker will load it, either via ld.so.cache or
LD_LIBRARY_PATH processing.

Under strace, it look like this if the cache is used:

| openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
| openat(AT_FDCWD, "/lib64/glibc-hwcaps/x86-102/libz.so.1", O_RDONLY|O_CLOEXEC) = 3

Diagnostics output from “ld.so --help” for this machine is this:

| Shared library search path:
|   (libraries located via /etc/ld.so.cache)
|   /lib64 (system search path)
|   /usr/lib64 (system search path)
|
| Subdirectories of glibc-hwcaps directories, in priority order:
|   x86-103
|   x86-102 (supported, searched)
|   x86-101 (supported, searched)
|   x86-100 (supported, searched)
|
| Legacy HWCAP subdirectories under library search path directories:
|   haswell (AT_PLATFORM)
|   tls (supported, searched)
|   avx512_1
|   x86_64 (supported, searched)

I've also included support for a "power9" subdirectory on powerpc64le,
some subdirectories on s390x, and "atomics" on aarch64.  The exact
definitions of the directories are still subject to change.  They are
there to that there is at least something new to be searched.  For the
x86-64 part, I'm going to start a separate, cross-project discussion
about the desired CPU feature flags for each micro-architecture baseline
level.

The series does not yet check if the glibc-hwcaps directory itself
exists.  If the directory does not exist, we can skip searching all its
subdirectories.  This is another motivation for having the magic
glibc-hwcaps subdirectory.  But I have not implemented this yet because
it tends to hide the search activity from strace.

Patches up to (but excluding) “elf: Add glibc-hwcaps support for
LD_LIBRARY_PATH” are more or less cleanups, and so are these patches:

  elf: Unify old and new format cache handling code in ld.so
  elf: Implement a string table for ldconfig, with tail merging
  elf: Implement tail merging of strings in ldconfig
  elf: In ldconfig, extract the new_sub_entry function from search_dir

These cleanups could be reviewed and installed separately.

Thanks,
Florian

Florian Weimer (30):
  elf: Include <stdbool.h> in <dl-tunables.h> because bool is used
  elf: Include <stddef.h> (for size_t), <sys/stat.h> in <ldconfig.h>
  elf: Do not search HWCAP subdirectories in statically linked binaries
  elf: Implement __rtld_malloc_is_full
  elf: Implement _dl_write
  elf: Extract command-line/environment variables state from rtld.c
  elf: Move ld.so error/help output to _dl_usage
  elf: Record whether paths come from LD_LIBRARY_PATH or --library-path
  elf: Implement ld.so --help
  elf: Implement ld.so --version
  scripts/update-copyrights: Update csu/version.c, elf/dl-usage.c
  elf: Use the term "program interpreter" in the ld.so help message
  elf: Print the full name of the dynamic loader in the ld.so help
    message
  elf: Make __rtld_env_path_list and __rtld_search_dirs global variables
  elf: Add library search path information to ld.so --help
  elf: Enhance ld.so --help to print HWCAP subdirectories
  elf: Do not pass GLRO(dl_platform), GLRO(dl_platformlen) to
    _dl_important_hwcaps
  elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  x86_64: Add glibc-hwcaps support
  powerpc64le: Add glibc-hwcaps support
  s390x: Add Add glibc-hwcaps support
  aarch64: Add glibc-hwcaps support
  elf: Add endianness markup to ld.so.cache
  elf: Add extension mechanism to ld.so.cache
  elf: Unify old and new format cache handling code in ld.so
  elf: Implement a string table for ldconfig, with tail merging
  elf: Implement tail merging of strings in ldconfig
  elf: In ldconfig, extract the new_sub_entry function from search_dir
  elf: Process glibc-hwcaps subdirectories in ldconfig
  elf: Add glibc-hwcaps subdirectory support to ld.so cache processing

 NEWS                                          |   4 +
 elf/Makefile                                  |  22 +-
 elf/cache.c                                   | 400 +++++++++++++--
 elf/dl-cache.c                                | 483 ++++++++++++------
 elf/dl-hwcaps-subdirs.c                       |  29 ++
 elf/dl-hwcaps.c                               | 225 +++++++-
 elf/dl-hwcaps.h                               | 102 ++++
 elf/dl-hwcaps_split.c                         |  77 +++
 elf/dl-load.c                                 |  75 +--
 elf/dl-main.h                                 | 120 +++++
 elf/dl-minimal.c                              |   8 +
 elf/dl-support.c                              |   5 +-
 elf/dl-tunables.h                             |   2 +
 elf/dl-usage.c                                | 267 ++++++++++
 elf/dl-write.c                                |  56 ++
 elf/ldconfig.c                                | 187 +++++--
 elf/rtld.c                                    | 248 ++++-----
 elf/stringtable.c                             | 201 ++++++++
 elf/stringtable.h                             |  61 +++
 elf/stringtable_free.c                        |  32 ++
 elf/tst-dl-hwcaps_split.c                     | 139 +++++
 elf/tst-stringtable.c                         | 140 +++++
 include/link.h                                |   4 +
 include/rtld-malloc.h                         |   4 +
 scripts/update-copyrights                     |   6 +
 sysdeps/aarch64/dl-hwcaps-subdirs.c           |  31 ++
 sysdeps/generic/dl-cache.h                    | 232 ++++++++-
 sysdeps/generic/ldconfig.h                    |  20 +-
 sysdeps/generic/ldsodefs.h                    |  34 +-
 .../powerpc/powerpc64/le/dl-hwcaps-subdirs.c  |  31 ++
 sysdeps/s390/s390-64/dl-hwcaps-subdirs.c      |  54 ++
 sysdeps/unix/sysv/linux/dl-write.c            |  30 ++
 sysdeps/x86_64/dl-hwcaps-subdirs.c            |  73 +++
 33 files changed, 2980 insertions(+), 422 deletions(-)
 create mode 100644 elf/dl-hwcaps-subdirs.c
 create mode 100644 elf/dl-hwcaps_split.c
 create mode 100644 elf/dl-main.h
 create mode 100644 elf/dl-usage.c
 create mode 100644 elf/dl-write.c
 create mode 100644 elf/stringtable.c
 create mode 100644 elf/stringtable.h
 create mode 100644 elf/stringtable_free.c
 create mode 100644 elf/tst-dl-hwcaps_split.c
 create mode 100644 elf/tst-stringtable.c
 create mode 100644 sysdeps/aarch64/dl-hwcaps-subdirs.c
 create mode 100644 sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
 create mode 100644 sysdeps/s390/s390-64/dl-hwcaps-subdirs.c
 create mode 100644 sysdeps/unix/sysv/linux/dl-write.c
 create mode 100644 sysdeps/x86_64/dl-hwcaps-subdirs.c

--
2.25.4

Reply | Threaded
Open this post in threaded view
|

[PATCH 01/30] elf: Include <stdbool.h> in <dl-tunables.h> because bool is used

Sourceware - libc-alpha mailing list
---
 elf/dl-tunables.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
index 969e50327b..f05eb50c2f 100644
--- a/elf/dl-tunables.h
+++ b/elf/dl-tunables.h
@@ -21,6 +21,8 @@
 #ifndef _TUNABLES_H_
 #define _TUNABLES_H_
 
+#include <stdbool.h>
+
 #if !HAVE_TUNABLES
 static inline void
 __always_inline
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 02/30] elf: Include <stddef.h> (for size_t), <sys/stat.h> in <ldconfig.h>

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
---
 sysdeps/generic/ldconfig.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h
index a8a2be7856..b64aab0064 100644
--- a/sysdeps/generic/ldconfig.h
+++ b/sysdeps/generic/ldconfig.h
@@ -19,7 +19,9 @@
 #ifndef _LDCONFIG_H
 #define _LDCONFIG_H
 
+#include <stddef.h>
 #include <stdint.h>
+#include <sys/stat.h>
 
 #define FLAG_ANY -1
 #define FLAG_TYPE_MASK 0x00ff
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 03/30] elf: Do not search HWCAP subdirectories in statically linked binaries

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
This functionality does not seem to be useful since static dlopen
is mostly used for iconv/character set conversion and NSS support.
gconv modules are loaded with full paths anyway, so that the
HWCAP subdirectory logic does not apply.
---
 NEWS          |  4 ++++
 elf/Makefile  |  4 ++--
 elf/dl-load.c | 14 ++++++++++++++
 3 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index a660fc59a8..032255d58e 100644
--- a/NEWS
+++ b/NEWS
@@ -51,6 +51,10 @@ Deprecated and removed features, and other changes affecting compatibility:
 * ldconfig now defaults to the new format for ld.so.cache. glibc has
   already supported this format for almost 20 years.
 
+* When dlopen is used in statically linked programs, alternative library
+  implementations from HWCAP subdirectories are no longer loaded.
+  Instead, the default implementation is used.
+
 Changes to build and runtime requirements:
 
 * powerpc64le requires GCC 7.4 or newer.  This is required for supporting
diff --git a/elf/Makefile b/elf/Makefile
index 6fe1df90bb..1293de5b5e 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -29,7 +29,7 @@ routines = $(all-dl-routines) dl-support dl-iteratephdr \
 
 # The core dynamic linking functions are in libc for the static and
 # profiled libraries.
-dl-routines = $(addprefix dl-,load lookup object reloc deps hwcaps \
+dl-routines = $(addprefix dl-,load lookup object reloc deps \
   runtime init fini debug misc \
   version profile tls origin scope \
   execstack open close trampoline \
@@ -59,7 +59,7 @@ elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
 # 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-error-minimal dl-conflict dl-hwcaps
 all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
 
 CFLAGS-dl-runtime.c += -fexceptions -fasynchronous-unwind-tables
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 06f2ba7264..34d3b02a95 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -101,9 +101,13 @@ int __stack_prot attribute_hidden attribute_relro
 static struct r_search_path_struct env_path_list attribute_relro;
 
 /* List of the hardware capabilities we might end up using.  */
+#ifdef SHARED
 static const struct r_strlenpair *capstr attribute_relro;
 static size_t ncapstr attribute_relro;
 static size_t max_capstrlen attribute_relro;
+#else
+enum { ncapstr = 1, max_capstrlen = 0 };
+#endif
 
 
 /* Get the generated information about the trusted directories.  Use
@@ -691,9 +695,11 @@ _dl_init_paths (const char *llp)
   /* Fill in the information about the application's RPATH and the
      directories addressed by the LD_LIBRARY_PATH environment variable.  */
 
+#ifdef SHARED
   /* Get the capabilities.  */
   capstr = _dl_important_hwcaps (GLRO(dl_platform), GLRO(dl_platformlen),
  &ncapstr, &max_capstrlen);
+#endif
 
   /* First set up the rest of the default search directory entries.  */
   aelem = rtld_search_dirs.dirs = (struct r_search_path_elem **)
@@ -1443,11 +1449,15 @@ print_search_path (struct r_search_path_elem **list,
       for (cnt = 0; cnt < ncapstr; ++cnt)
  if ((*list)->status[cnt] != nonexisting)
   {
+#ifdef SHARED
     char *cp = __mempcpy (endp, capstr[cnt].str, capstr[cnt].len);
     if (cp == buf || (cp == buf + 1 && buf[0] == '/'))
       cp[0] = '\0';
     else
       cp[-1] = '\0';
+#else
+    *endp = '\0';
+#endif
 
     _dl_debug_printf_c (first ? "%s" : ":%s", buf);
     first = 0;
@@ -1808,11 +1818,15 @@ open_path (const char *name, size_t namelen, int mode,
   if (this_dir->status[cnt] == nonexisting)
     continue;
 
+#ifdef SHARED
   buflen =
     ((char *) __mempcpy (__mempcpy (edp, capstr[cnt].str,
     capstr[cnt].len),
  name, namelen)
      - buf);
+#else
+  buflen = (char *) __mempcpy (edp, name, namelen) - buf;
+#endif
 
   /* Print name we try if this is wanted.  */
   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 04/30] elf: Implement __rtld_malloc_is_full

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
In some cases, it is difficult to determine the kind of malloc
based on the execution context, so a function to determine that
is helpful.
---
 elf/dl-minimal.c      | 8 ++++++++
 include/rtld-malloc.h | 4 ++++
 2 files changed, 12 insertions(+)

diff --git a/elf/dl-minimal.c b/elf/dl-minimal.c
index 7c64e24c87..dc79f02458 100644
--- a/elf/dl-minimal.c
+++ b/elf/dl-minimal.c
@@ -59,6 +59,14 @@ __rtld_malloc_init_stubs (void)
   __rtld_realloc = &rtld_realloc;
 }
 
+bool
+__rtld_malloc_is_full (void)
+{
+  /* The caller assumes that there is an active malloc.  */
+  assert (__rtld_malloc != NULL);
+  return __rtld_malloc != &rtld_malloc;
+}
+
 /* Lookup NAME at VERSION in the scope of MATCH.  */
 static void *
 lookup_malloc_symbol (struct link_map *main_map, const char *name,
diff --git a/include/rtld-malloc.h b/include/rtld-malloc.h
index b026a3270c..54f53f7888 100644
--- a/include/rtld-malloc.h
+++ b/include/rtld-malloc.h
@@ -66,6 +66,10 @@ realloc (void *ptr, size_t size)
    implementation.  */
 void __rtld_malloc_init_stubs (void) attribute_hidden;
 
+/* Return false if the active malloc is the ld.so minimal malloc, true
+   if it is the full implementation from libc.so.  */
+_Bool __rtld_malloc_is_full (void) attribute_hidden;
+
 /* Called shortly before the final self-relocation (when RELRO
    variables are still writable) to activate the real malloc
    implementation.  MAIN_MAP is the link map of the executable.  */
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 05/30] elf: Implement _dl_write

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
The generic version is parallel to _dl_writev.  It cannot use
_dl_writev directly because the errno value needs to be obtained
under a lock.
---
 elf/Makefile                       |  2 +-
 elf/dl-write.c                     | 56 ++++++++++++++++++++++++++++++
 sysdeps/generic/ldsodefs.h         |  6 ++++
 sysdeps/unix/sysv/linux/dl-write.c | 30 ++++++++++++++++
 4 files changed, 93 insertions(+), 1 deletion(-)
 create mode 100644 elf/dl-write.c
 create mode 100644 sysdeps/unix/sysv/linux/dl-write.c

diff --git a/elf/Makefile b/elf/Makefile
index 1293de5b5e..b56fe0c0b7 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -34,7 +34,7 @@ dl-routines = $(addprefix dl-,load lookup object reloc deps \
   version profile tls origin scope \
   execstack open close trampoline \
   exception sort-maps lookup-direct \
-  call-libc-early-init)
+  call-libc-early-init write)
 ifeq (yes,$(use-ldconfig))
 dl-routines += dl-cache
 endif
diff --git a/elf/dl-write.c b/elf/dl-write.c
new file mode 100644
index 0000000000..7350aff003
--- /dev/null
+++ b/elf/dl-write.c
@@ -0,0 +1,56 @@
+/* Implementation of the _dl_write function.  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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <ldsodefs.h>
+#include <libc-lock.h>
+#include <sys/uio.h>
+
+ssize_t
+_dl_write (int fd, const void *buffer, size_t length)
+{
+  struct iovec iov = { .iov_base = (void *) buffer, .iov_len = length };
+  ssize_t ret;
+
+#if RTLD_PRIVATE_ERRNO
+  /* We have to take this lock just to be sure we don't clobber the private
+     errno when it's being used by another thread that cares about it.
+     Yet we must be sure not to try calling the lock functions before
+     the thread library is fully initialized.  */
+  if (__glibc_unlikely (_dl_starting_up))
+    {
+      ret = __writev (fd, &iov, 1);
+      if (ret < 0)
+        ret = -errno;
+    }
+  else
+    {
+      __rtld_lock_lock_recursive (GL(dl_load_lock));
+      __writev (fd, &iov, 1);
+      if (ret < 0)
+        ret = -errno;
+      __rtld_lock_unlock_recursive (GL(dl_load_lock));
+    }
+#else
+  ret = __writev (fd, &iov, 1);
+  if (ret < 0)
+    ret = -errno;
+#endif
+
+  return ret;
+}
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index d08b97a5ef..42fe912f47 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -749,6 +749,12 @@ _dl_dprintf (int fd, const char *fmt, ...)
 }
 #endif
 
+/* Write LENGTH bytes at BUFFER to FD, like write.  Returns the number
+   of bytes written on success, or a negative error constant on
+   failure.  */
+ssize_t _dl_write (int fd, const void *buffer, size_t length)
+  attribute_hidden;
+
 /* Write a message on the specified descriptor standard output.  The
    parameters are interpreted as for a `printf' call.  */
 void _dl_printf (const char *fmt, ...)
diff --git a/sysdeps/unix/sysv/linux/dl-write.c b/sysdeps/unix/sysv/linux/dl-write.c
new file mode 100644
index 0000000000..1c6298fb41
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/dl-write.c
@@ -0,0 +1,30 @@
+/* Implementation of the _dl_write function.  Linux 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+
+ssize_t
+_dl_write (int fd, const void *buffer, size_t length)
+{
+  long int r = INTERNAL_SYSCALL_CALL (write, fd, buffer, length);
+  if (INTERNAL_SYSCALL_ERROR_P (r))
+    r = - INTERNAL_SYSCALL_ERRNO (r);
+  return r;
+}
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 06/30] elf: Extract command-line/environment variables state from rtld.c

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
Introduce struct dl_main_state and move it to <dl-main.h>.

This avoids the need for putting state that is only needed during
startup into the ld.so data segment.
---
 elf/dl-main.h |  95 +++++++++++++++++++++++++++++++
 elf/rtld.c    | 153 +++++++++++++++++++-------------------------------
 2 files changed, 153 insertions(+), 95 deletions(-)
 create mode 100644 elf/dl-main.h

diff --git a/elf/dl-main.h b/elf/dl-main.h
new file mode 100644
index 0000000000..ad9250171f
--- /dev/null
+++ b/elf/dl-main.h
@@ -0,0 +1,95 @@
+/* Information collection during ld.so startup.
+   Copyright (C) 1995-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/>.  */
+
+#ifndef _DL_MAIN
+#define _DL_MAIN
+
+#include <limits.h>
+
+/* Length limits for names and paths, to protect the dynamic linker,
+   particularly when __libc_enable_secure is active.  */
+#ifdef NAME_MAX
+# define SECURE_NAME_LIMIT NAME_MAX
+#else
+# define SECURE_NAME_LIMIT 255
+#endif
+#ifdef PATH_MAX
+# define SECURE_PATH_LIMIT PATH_MAX
+#else
+# define SECURE_PATH_LIMIT 1024
+#endif
+
+/* Strings containing colon-separated lists of audit modules.  */
+struct audit_list
+{
+  /* Array of strings containing colon-separated path lists.  Each
+     audit module needs its own namespace, so pre-allocate the largest
+     possible list.  */
+  const char *audit_strings[DL_NNS];
+
+  /* Number of entries added to audit_strings.  */
+  size_t length;
+
+  /* Index into the audit_strings array (for the iteration phase).  */
+  size_t current_index;
+
+  /* Tail of audit_strings[current_index] which still needs
+     processing.  */
+  const char *current_tail;
+
+  /* Scratch buffer for returning a name which is part of the strings
+     in audit_strings.  */
+  char fname[SECURE_NAME_LIMIT];
+};
+
+/* This is a list of all the modes the dynamic loader can be in.  */
+enum mode { normal, list, verify, trace };
+
+/* Aggregated state information extracted from environment variables
+   and the ld.so command line.  */
+struct dl_main_state
+{
+  struct audit_list audit_list;
+
+  /* The library search path.  */
+  const char *library_path;
+
+  /* The list preloaded objects from LD_PRELOAD.  */
+  const char *preloadlist;
+
+  /* The preload list passed as a command argument.  */
+  const char *preloadarg;
+
+  enum mode mode;
+
+  /* True if any of the debugging options is enabled.  */
+  bool any_debug;
+
+  /* True if information about versions has to be printed.  */
+  bool version_info;
+};
+
+/* Helper function to invoke _dl_init_paths with the right arguments
+   from *STATE.  */
+static inline void
+call_init_paths (const struct dl_main_state *state)
+{
+  _dl_init_paths (state->library_path);
+}
+
+#endif /* _DL_MAIN */
diff --git a/elf/rtld.c b/elf/rtld.c
index 882b070cc0..e3556eabc4 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -47,6 +47,7 @@
 #include <not-cancel.h>
 #include <array_length.h>
 #include <libc-early-init.h>
+#include <dl-main.h>
 
 #include <assert.h>
 
@@ -111,42 +112,6 @@ static void print_missing_version (int errcode, const char *objname,
 /* Print the various times we collected.  */
 static void print_statistics (const hp_timing_t *total_timep);
 
-/* Length limits for names and paths, to protect the dynamic linker,
-   particularly when __libc_enable_secure is active.  */
-#ifdef NAME_MAX
-# define SECURE_NAME_LIMIT NAME_MAX
-#else
-# define SECURE_NAME_LIMIT 255
-#endif
-#ifdef PATH_MAX
-# define SECURE_PATH_LIMIT PATH_MAX
-#else
-# define SECURE_PATH_LIMIT 1024
-#endif
-
-/* Strings containing colon-separated lists of audit modules.  */
-struct audit_list
-{
-  /* Array of strings containing colon-separated path lists.  Each
-     audit module needs its own namespace, so pre-allocate the largest
-     possible list.  */
-  const char *audit_strings[DL_NNS];
-
-  /* Number of entries added to audit_strings.  */
-  size_t length;
-
-  /* Index into the audit_strings array (for the iteration phase).  */
-  size_t current_index;
-
-  /* Tail of audit_strings[current_index] which still needs
-     processing.  */
-  const char *current_tail;
-
-  /* Scratch buffer for returning a name which is part of the strings
-     in audit_strings.  */
-  char fname[SECURE_NAME_LIMIT];
-};
-
 /* Creates an empty audit list.  */
 static void audit_list_init (struct audit_list *);
 
@@ -167,13 +132,13 @@ static void audit_list_add_dynamic_tag (struct audit_list *,
    audit_list_add_dynamic_tags calls.  */
 static const char *audit_list_next (struct audit_list *);
 
-/* This is a list of all the modes the dynamic loader can be in.  */
-enum mode { normal, list, verify, trace };
+/* Initialize *STATE with the defaults.  */
+static void dl_main_state_init (struct dl_main_state *state);
 
 /* Process all environments variables the dynamic linker must recognize.
    Since all of them start with `LD_' we are a bit smarter while finding
    all the entries.  */
-static void process_envvars (enum mode *modep, struct audit_list *);
+static void process_envvars (struct dl_main_state *state);
 
 #ifdef DL_ARGV_NOT_RELRO
 int _dl_argc attribute_hidden;
@@ -299,6 +264,18 @@ audit_list_next (struct audit_list *list)
     }
 }
 
+static void
+dl_main_state_init (struct dl_main_state *state)
+{
+  audit_list_init (&state->audit_list);
+  state->library_path = NULL;
+  state->preloadlist = NULL;
+  state->preloadarg = NULL;
+  state->mode = normal;
+  state->any_debug = false;
+  state->version_info = false;
+}
+
 #ifndef HAVE_INLINED_SYSCALLS
 /* Set nonzero during loading and initialization of executable and
    libraries, cleared before the executable's entry point runs.  This
@@ -880,15 +857,6 @@ security_init (void)
 
 #include <setup-vdso.h>
 
-/* The library search path.  */
-static const char *library_path attribute_relro;
-/* The list preloaded objects.  */
-static const char *preloadlist attribute_relro;
-/* Nonzero if information about versions has to be printed.  */
-static int version_info attribute_relro;
-/* The preload list passed as a command argument.  */
-static const char *preloadarg attribute_relro;
-
 /* The LD_PRELOAD environment variable gives list of libraries
    separated by white space or colons that are loaded before the
    executable's dependencies and prepended to the global scope list.
@@ -1130,7 +1098,6 @@ dl_main (const ElfW(Phdr) *phdr,
  ElfW(auxv_t) *auxv)
 {
   const ElfW(Phdr) *ph;
-  enum mode mode;
   struct link_map *main_map;
   size_t file_size;
   char *file;
@@ -1140,8 +1107,8 @@ dl_main (const ElfW(Phdr) *phdr,
   bool rtld_is_main = false;
   void *tcbp = NULL;
 
-  struct audit_list audit_list;
-  audit_list_init (&audit_list);
+  struct dl_main_state state;
+  dl_main_state_init (&state);
 
   GL(dl_init_static_tls) = &_dl_nothread_init_static_tls;
 
@@ -1156,7 +1123,7 @@ dl_main (const ElfW(Phdr) *phdr,
   GL(dl_make_stack_executable_hook) = &_dl_make_stack_executable;
 
   /* Process the environment variable which control the behaviour.  */
-  process_envvars (&mode, &audit_list);
+  process_envvars (&state);
 
 #ifndef HAVE_INLINED_SYSCALLS
   /* Set up a flag which tells we are just starting.  */
@@ -1188,7 +1155,7 @@ dl_main (const ElfW(Phdr) *phdr,
       while (_dl_argc > 1)
  if (! strcmp (_dl_argv[1], "--list"))
   {
-    mode = list;
+    state.mode = list;
     GLRO(dl_lazy) = -1; /* This means do no dependency analysis.  */
 
     ++_dl_skip_args;
@@ -1197,7 +1164,7 @@ dl_main (const ElfW(Phdr) *phdr,
   }
  else if (! strcmp (_dl_argv[1], "--verify"))
   {
-    mode = verify;
+    state.mode = verify;
 
     ++_dl_skip_args;
     --_dl_argc;
@@ -1213,7 +1180,7 @@ dl_main (const ElfW(Phdr) *phdr,
  else if (! strcmp (_dl_argv[1], "--library-path")
  && _dl_argc > 2)
   {
-    library_path = _dl_argv[2];
+    state.library_path = _dl_argv[2];
 
     _dl_skip_args += 2;
     _dl_argc -= 2;
@@ -1230,7 +1197,7 @@ dl_main (const ElfW(Phdr) *phdr,
   }
  else if (! strcmp (_dl_argv[1], "--audit") && _dl_argc > 2)
   {
-    audit_list_add_string (&audit_list, _dl_argv[2]);
+    audit_list_add_string (&state.audit_list, _dl_argv[2]);
 
     _dl_skip_args += 2;
     _dl_argc -= 2;
@@ -1238,7 +1205,7 @@ dl_main (const ElfW(Phdr) *phdr,
   }
  else if (! strcmp (_dl_argv[1], "--preload") && _dl_argc > 2)
   {
-    preloadarg = _dl_argv[2];
+    state.preloadarg = _dl_argv[2];
     _dl_skip_args += 2;
     _dl_argc -= 2;
     _dl_argv += 2;
@@ -1297,7 +1264,7 @@ of this helper program; chances are you did not intend to run this program.\n\
     break;
   }
 
-      if (__builtin_expect (mode, normal) == verify)
+      if (__builtin_expect (state.mode, normal) == verify)
  {
   const char *objname;
   const char *err_str = NULL;
@@ -1326,7 +1293,7 @@ of this helper program; chances are you did not intend to run this program.\n\
       /* Now the map for the main executable is available.  */
       main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
 
-      if (__builtin_expect (mode, normal) == normal
+      if (__builtin_expect (state.mode, normal) == normal
   && GL(dl_rtld_map).l_info[DT_SONAME] != NULL
   && main_map->l_info[DT_SONAME] != NULL
   && strcmp ((const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB])
@@ -1563,7 +1530,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
       _dl_setup_hash (main_map);
     }
 
-  if (__builtin_expect (mode, normal) == verify)
+  if (__builtin_expect (state.mode, normal) == verify)
     {
       /* We were called just to verify that this is a dynamic
  executable using us as the program interpreter.  Exit with an
@@ -1593,7 +1560,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
 
   /* Initialize the data structures for the search paths for shared
      objects.  */
-  _dl_init_paths (library_path);
+  call_init_paths (&state);
 
   /* Initialize _r_debug.  */
   struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr,
@@ -1658,12 +1625,12 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
     /* Assign a module ID.  Do this before loading any audit modules.  */
     GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid ();
 
-  audit_list_add_dynamic_tag (&audit_list, main_map, DT_AUDIT);
-  audit_list_add_dynamic_tag (&audit_list, main_map, DT_DEPAUDIT);
+  audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_AUDIT);
+  audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_DEPAUDIT);
 
   /* If we have auditing DSOs to load, do it now.  */
   bool need_security_init = true;
-  if (audit_list.length > 0)
+  if (state.audit_list.length > 0)
     {
       /* Since we start using the auditing DSOs right away we need to
  initialize the data structures now.  */
@@ -1676,7 +1643,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
       security_init ();
       need_security_init = false;
 
-      load_audit_modules (main_map, &audit_list);
+      load_audit_modules (main_map, &state.audit_list);
     }
 
   /* Keep track of the currently loaded modules to count how many
@@ -1727,19 +1694,21 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
   struct link_map **preloads = NULL;
   unsigned int npreloads = 0;
 
-  if (__glibc_unlikely (preloadlist != NULL))
+  if (__glibc_unlikely (state.preloadlist != NULL))
     {
       RTLD_TIMING_VAR (start);
       rtld_timer_start (&start);
-      npreloads += handle_preload_list (preloadlist, main_map, "LD_PRELOAD");
+      npreloads += handle_preload_list (state.preloadlist, main_map,
+ "LD_PRELOAD");
       rtld_timer_accum (&load_time, start);
     }
 
-  if (__glibc_unlikely (preloadarg != NULL))
+  if (__glibc_unlikely (state.preloadarg != NULL))
     {
       RTLD_TIMING_VAR (start);
       rtld_timer_start (&start);
-      npreloads += handle_preload_list (preloadarg, main_map, "--preload");
+      npreloads += handle_preload_list (state.preloadarg, main_map,
+ "--preload");
       rtld_timer_accum (&load_time, start);
     }
 
@@ -1846,7 +1815,8 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
   {
     RTLD_TIMING_VAR (start);
     rtld_timer_start (&start);
-    _dl_map_object_deps (main_map, preloads, npreloads, mode == trace, 0);
+    _dl_map_object_deps (main_map, preloads, npreloads,
+ state.mode == trace, 0);
     rtld_timer_accum (&load_time, start);
   }
 
@@ -1873,7 +1843,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
       rtld_multiple_ref = true;
 
       GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1];
-      if (__builtin_expect (mode, normal) == normal)
+      if (__builtin_expect (state.mode, normal) == normal)
  {
   GL(dl_rtld_map).l_next = (i + 1 < main_map->l_searchlist.r_nlist
     ? main_map->l_searchlist.r_list[i + 1]
@@ -1906,8 +1876,8 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
      versions we need.  */
   {
     struct version_check_args args;
-    args.doexit = mode == normal;
-    args.dotrace = mode == trace;
+    args.doexit = state.mode == normal;
+    args.dotrace = state.mode == trace;
     _dl_receive_error (print_missing_version, version_check_doit, &args);
   }
 
@@ -1927,7 +1897,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
        earlier.  */
     security_init ();
 
-  if (__builtin_expect (mode, normal) != normal)
+  if (__builtin_expect (state.mode, normal) != normal)
     {
       /* We were run just to list the shared libraries.  It is
  important that we do this before real relocation, because the
@@ -2029,7 +1999,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
   (size_t) l->l_map_start);
  }
 
-      if (__builtin_expect (mode, trace) != trace)
+      if (__builtin_expect (state.mode, trace) != trace)
  for (i = 1; i < (unsigned int) _dl_argc; ++i)
   {
     const ElfW(Sym) *ref = NULL;
@@ -2083,7 +2053,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
  }
     }
 #define VERNEEDTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (DT_VERNEED))
-  if (version_info)
+  if (state.version_info)
     {
       /* Print more information.  This means here, print information
  about the versions needed.  */
@@ -2445,13 +2415,10 @@ print_missing_version (int errcode __attribute__ ((unused)),
     objname, errstring);
 }
 
-/* Nonzero if any of the debugging options is enabled.  */
-static int any_debug attribute_relro;
-
 /* Process the string given as the parameter which explains which debugging
    options are enabled.  */
 static void
-process_dl_debug (const char *dl_debug)
+process_dl_debug (struct dl_main_state *state, const char *dl_debug)
 {
   /* When adding new entries make sure that the maximal length of a name
      is correctly handled in the LD_DEBUG_HELP code below.  */
@@ -2508,7 +2475,7 @@ process_dl_debug (const char *dl_debug)
  && memcmp (dl_debug, debopts[cnt].name, len) == 0)
       {
  GLRO(dl_debug_mask) |= debopts[cnt].mask;
- any_debug = 1;
+ state->any_debug = true;
  break;
       }
 
@@ -2562,11 +2529,10 @@ extern char **_environ attribute_hidden;
 
 
 static void
-process_envvars (enum mode *modep, struct audit_list *audit_list)
+process_envvars (struct dl_main_state *state)
 {
   char **runp = _environ;
   char *envline;
-  enum mode mode = normal;
   char *debug_output = NULL;
 
   /* This is the default place for profiling data file.  */
@@ -2598,25 +2564,25 @@ process_envvars (enum mode *modep, struct audit_list *audit_list)
   /* Debugging of the dynamic linker?  */
   if (memcmp (envline, "DEBUG", 5) == 0)
     {
-      process_dl_debug (&envline[6]);
+      process_dl_debug (state, &envline[6]);
       break;
     }
   if (memcmp (envline, "AUDIT", 5) == 0)
-    audit_list_add_string (audit_list, &envline[6]);
+    audit_list_add_string (&state->audit_list, &envline[6]);
   break;
 
  case 7:
   /* Print information about versions.  */
   if (memcmp (envline, "VERBOSE", 7) == 0)
     {
-      version_info = envline[8] != '\0';
+      state->version_info = envline[8] != '\0';
       break;
     }
 
   /* List of objects to be preloaded.  */
   if (memcmp (envline, "PRELOAD", 7) == 0)
     {
-      preloadlist = &envline[8];
+      state->preloadlist = &envline[8];
       break;
     }
 
@@ -2665,7 +2631,7 @@ process_envvars (enum mode *modep, struct audit_list *audit_list)
   if (!__libc_enable_secure
       && memcmp (envline, "LIBRARY_PATH", 12) == 0)
     {
-      library_path = &envline[13];
+      state->library_path = &envline[13];
       break;
     }
 
@@ -2707,7 +2673,7 @@ process_envvars (enum mode *modep, struct audit_list *audit_list)
   /* The mode of the dynamic linker can be set.  */
   if (memcmp (envline, "TRACE_PRELINKING", 16) == 0)
     {
-      mode = trace;
+      state->mode = trace;
       GLRO(dl_verbose) = 1;
       GLRO(dl_debug_mask) |= DL_DEBUG_PRELINK;
       GLRO(dl_trace_prelink) = &envline[17];
@@ -2717,7 +2683,7 @@ process_envvars (enum mode *modep, struct audit_list *audit_list)
  case 20:
   /* The mode of the dynamic linker can be set.  */
   if (memcmp (envline, "TRACE_LOADED_OBJECTS", 20) == 0)
-    mode = trace;
+    state->mode = trace;
   break;
 
   /* We might have some extra environment variable to handle.  This
@@ -2730,9 +2696,6 @@ process_envvars (enum mode *modep, struct audit_list *audit_list)
  }
     }
 
-  /* The caller wants this information.  */
-  *modep = mode;
-
   /* Extra security for SUID binaries.  Remove all dangerous environment
      variables.  */
   if (__builtin_expect (__libc_enable_secure, 0))
@@ -2761,13 +2724,13 @@ process_envvars (enum mode *modep, struct audit_list *audit_list)
   GLRO(dl_debug_mask) = 0;
  }
 
-      if (mode != normal)
+      if (state->mode != normal)
  _exit (5);
     }
   /* If we have to run the dynamic linker in debugging mode and the
      LD_DEBUG_OUTPUT environment variable is given, we write the debug
      messages to this file.  */
-  else if (any_debug && debug_output != NULL)
+  else if (state->any_debug && debug_output != NULL)
     {
       const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW;
       size_t name_len = strlen (debug_output);
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 07/30] elf: Move ld.so error/help output to _dl_usage

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
Also add a comment to elf/Makefile, explaining why we cannot use
config.status for autoconf template processing.
---
 elf/Makefile   |  9 ++++++++-
 elf/dl-main.h  |  5 +++++
 elf/dl-usage.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 elf/rtld.c     | 25 +------------------------
 4 files changed, 64 insertions(+), 25 deletions(-)
 create mode 100644 elf/dl-usage.c

diff --git a/elf/Makefile b/elf/Makefile
index b56fe0c0b7..85f7e08e00 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -59,7 +59,7 @@ elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
 # 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-error-minimal dl-conflict dl-hwcaps dl-usage
 all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
 
 CFLAGS-dl-runtime.c += -fexceptions -fasynchronous-unwind-tables
@@ -605,6 +605,12 @@ ldso_install: $(inst_rtlddir)/$(rtld-installed-name)
 endif
 
 
+# Workarounds for ${exec_prefix} expansion in configure variables.
+# config.status cannot be used directly for processing ldd.bash.in or
+# expanding variables such as sysconfdir because the expansion
+# contains the literal string ${exec_prefix}, which is not valid in C
+# headers or installed shell scripts.
+
 ldd-rewrite = -e 's%@RTLD@%$(rtlddir)/$(rtld-installed-name)%g' \
       -e 's%@VERSION@%$(version)%g' \
       -e 's|@PKGVERSION@|$(PKGVERSION)|g' \
@@ -642,6 +648,7 @@ libof-ldconfig = ldconfig
 CFLAGS-dl-cache.c += $(SYSCONF-FLAGS)
 CFLAGS-cache.c += $(SYSCONF-FLAGS)
 CFLAGS-rtld.c += $(SYSCONF-FLAGS)
+CFLAGS-dl-usage.c += $(SYSCONF-FLAGS)
 
 cpp-srcs-left := $(all-rtld-routines:=.os)
 lib := rtld
diff --git a/elf/dl-main.h b/elf/dl-main.h
index ad9250171f..681f366871 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -19,7 +19,9 @@
 #ifndef _DL_MAIN
 #define _DL_MAIN
 
+#include <ldsodefs.h>
 #include <limits.h>
+#include <stdlib.h>
 
 /* Length limits for names and paths, to protect the dynamic linker,
    particularly when __libc_enable_secure is active.  */
@@ -92,4 +94,7 @@ call_init_paths (const struct dl_main_state *state)
   _dl_init_paths (state->library_path);
 }
 
+/* Print ld.so usage information and exit.  */
+void _dl_usage (void) attribute_hidden __attribute__ ((__noreturn__));
+
 #endif /* _DL_MAIN */
diff --git a/elf/dl-usage.c b/elf/dl-usage.c
new file mode 100644
index 0000000000..e03f183622
--- /dev/null
+++ b/elf/dl-usage.c
@@ -0,0 +1,50 @@
+/* Print usage information and help for ld.so.
+   Copyright (C) 1995-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 <dl-cache.h>
+#include <dl-main.h>
+#include <ldsodefs.h>
+
+void
+_dl_usage (void)
+{
+  _dl_fatal_printf ("\
+Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
+You have invoked `ld.so', the helper program for shared library executables.\n\
+This program usually lives in the file `/lib/ld.so', and special directives\n\
+in executable files using ELF shared libraries tell the system's program\n\
+loader to load the helper program from this file.  This helper program loads\n\
+the shared libraries needed by the program executable, prepares the program\n\
+to run, and runs it.  You may invoke this helper program directly from the\n\
+command line to load and run an ELF executable file; this is like executing\n\
+that file itself, but always uses this helper program from the file you\n\
+specified, instead of the helper program file specified in the executable\n\
+file you run.  This is mostly of use for maintainers to test new versions\n\
+of this helper program; chances are you did not intend to run this program.\n\
+\n\
+  --list                list all dependencies and how they are resolved\n\
+  --verify              verify that given object really is a dynamically linked\n\
+                        object we can handle\n\
+  --inhibit-cache       Do not use " LD_SO_CACHE "\n\
+  --library-path PATH   use given PATH instead of content of the environment\n\
+                        variable LD_LIBRARY_PATH\n\
+  --inhibit-rpath LIST  ignore RUNPATH and RPATH information in object names\n\
+                        in LIST\n\
+  --audit LIST          use objects named in LIST as auditors\n\
+  --preload LIST        preload objects named in LIST\n");
+}
diff --git a/elf/rtld.c b/elf/rtld.c
index e3556eabc4..5fe6ca969a 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1216,30 +1216,7 @@ dl_main (const ElfW(Phdr) *phdr,
       /* If we have no further argument the program was called incorrectly.
  Grant the user some education.  */
       if (_dl_argc < 2)
- _dl_fatal_printf ("\
-Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
-You have invoked `ld.so', the helper program for shared library executables.\n\
-This program usually lives in the file `/lib/ld.so', and special directives\n\
-in executable files using ELF shared libraries tell the system's program\n\
-loader to load the helper program from this file.  This helper program loads\n\
-the shared libraries needed by the program executable, prepares the program\n\
-to run, and runs it.  You may invoke this helper program directly from the\n\
-command line to load and run an ELF executable file; this is like executing\n\
-that file itself, but always uses this helper program from the file you\n\
-specified, instead of the helper program file specified in the executable\n\
-file you run.  This is mostly of use for maintainers to test new versions\n\
-of this helper program; chances are you did not intend to run this program.\n\
-\n\
-  --list                list all dependencies and how they are resolved\n\
-  --verify              verify that given object really is a dynamically linked\n\
- object we can handle\n\
-  --inhibit-cache       Do not use " LD_SO_CACHE "\n\
-  --library-path PATH   use given PATH instead of content of the environment\n\
- variable LD_LIBRARY_PATH\n\
-  --inhibit-rpath LIST  ignore RUNPATH and RPATH information in object names\n\
- in LIST\n\
-  --audit LIST          use objects named in LIST as auditors\n\
-  --preload LIST        preload objects named in LIST\n");
+ _dl_usage ();
 
       ++_dl_skip_args;
       --_dl_argc;
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 08/30] elf: Record whether paths come from LD_LIBRARY_PATH or --library-path

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
This allows more precise diagnostics.
---
 elf/dl-load.c              | 4 ++--
 elf/dl-main.h              | 5 ++++-
 elf/dl-support.c           | 2 +-
 elf/rtld.c                 | 3 +++
 sysdeps/generic/ldsodefs.h | 6 ++++--
 5 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 34d3b02a95..2339ada485 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -682,7 +682,7 @@ cache_rpath (struct link_map *l,
 
 
 void
-_dl_init_paths (const char *llp)
+_dl_init_paths (const char *llp, const char *source)
 {
   size_t idx;
   const char *strp;
@@ -820,7 +820,7 @@ _dl_init_paths (const char *llp)
  }
 
       (void) fillin_rpath (llp_tmp, env_path_list.dirs, ":;",
-   "LD_LIBRARY_PATH", NULL, l);
+   source, NULL, l);
 
       if (env_path_list.dirs[0] == NULL)
  {
diff --git a/elf/dl-main.h b/elf/dl-main.h
index 681f366871..68dd27d0d7 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -71,6 +71,9 @@ struct dl_main_state
   /* The library search path.  */
   const char *library_path;
 
+  /* Where library_path comes from.  LD_LIBRARY_PATH or --library.  */
+  const char *library_path_source;
+
   /* The list preloaded objects from LD_PRELOAD.  */
   const char *preloadlist;
 
@@ -91,7 +94,7 @@ struct dl_main_state
 static inline void
 call_init_paths (const struct dl_main_state *state)
 {
-  _dl_init_paths (state->library_path);
+  _dl_init_paths (state->library_path, state->library_path_source);
 }
 
 /* Print ld.so usage information and exit.  */
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 7704c101c5..afbc94df54 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -323,7 +323,7 @@ _dl_non_dynamic_init (void)
 
   /* Initialize the data structures for the search paths for shared
      objects.  */
-  _dl_init_paths (getenv ("LD_LIBRARY_PATH"));
+  _dl_init_paths (getenv ("LD_LIBRARY_PATH"), "LD_LIBRARY_PATH");
 
   /* Remember the last search directory added at startup.  */
   _dl_init_all_dirs = GL(dl_all_dirs);
diff --git a/elf/rtld.c b/elf/rtld.c
index 5fe6ca969a..eac9a1e743 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -269,6 +269,7 @@ dl_main_state_init (struct dl_main_state *state)
 {
   audit_list_init (&state->audit_list);
   state->library_path = NULL;
+  state->library_path_source = NULL;
   state->preloadlist = NULL;
   state->preloadarg = NULL;
   state->mode = normal;
@@ -1181,6 +1182,7 @@ dl_main (const ElfW(Phdr) *phdr,
  && _dl_argc > 2)
   {
     state.library_path = _dl_argv[2];
+    state.library_path_source = "--library_path";
 
     _dl_skip_args += 2;
     _dl_argc -= 2;
@@ -2609,6 +2611,7 @@ process_envvars (struct dl_main_state *state)
       && memcmp (envline, "LIBRARY_PATH", 12) == 0)
     {
       state->library_path = &envline[13];
+      state->library_path_source = "LD_LIBRARY_PATH";
       break;
     }
 
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 42fe912f47..0f23352302 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1034,8 +1034,10 @@ rtld_hidden_proto (_dl_debug_state)
 extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
      attribute_hidden;
 
-/* Initialize the basic data structure for the search paths.  */
-extern void _dl_init_paths (const char *library_path) attribute_hidden;
+/* Initialize the basic data structure for the search paths.  SOURCE
+   is either "LD_LIBRARY_PATH" or "--library-path".  */
+extern void _dl_init_paths (const char *library_path, const char *source)
+  attribute_hidden;
 
 /* Gather the information needed to install the profiling tables and start
    the timers.  */
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 09/30] elf: Implement ld.so --help

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
--help processing is deferred to the point where the executable has
been loaded, so that it is possible to eventually include information
from the main executable in the help output.

As suggested in the GNU command-line interface guidelines, the help
message is printed to standard output, and the exit status is
successful.

Handle usage errors closer to the GNU command-line interface
guidelines.
---
 elf/dl-main.h  |  9 +++++++--
 elf/dl-usage.c | 24 ++++++++++++++++++----
 elf/rtld.c     | 55 ++++++++++++++++++++++++++++++++++++++++++--------
 3 files changed, 74 insertions(+), 14 deletions(-)

diff --git a/elf/dl-main.h b/elf/dl-main.h
index 68dd27d0d7..71ca5114de 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -60,7 +60,7 @@ struct audit_list
 };
 
 /* This is a list of all the modes the dynamic loader can be in.  */
-enum mode { normal, list, verify, trace };
+enum mode { normal, list, verify, trace, rtld_help };
 
 /* Aggregated state information extracted from environment variables
    and the ld.so command line.  */
@@ -98,6 +98,11 @@ call_init_paths (const struct dl_main_state *state)
 }
 
 /* Print ld.so usage information and exit.  */
-void _dl_usage (void) attribute_hidden __attribute__ ((__noreturn__));
+void _dl_usage (const char *argv0, const char *wrong_option)
+  attribute_hidden __attribute__ ((__noreturn__));
+
+/* Print ld.so --help output and exit.  */
+void _dl_help (const char *argv0, struct dl_main_state *state)
+  attribute_hidden __attribute__ ((__noreturn__));
 
 #endif /* _DL_MAIN */
diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index e03f183622..e1dc5d33b2 100644
--- a/elf/dl-usage.c
+++ b/elf/dl-usage.c
@@ -19,12 +19,24 @@
 #include <dl-cache.h>
 #include <dl-main.h>
 #include <ldsodefs.h>
+#include <unistd.h>
 
 void
-_dl_usage (void)
+_dl_usage (const char *argv0, const char *wrong_option)
 {
-  _dl_fatal_printf ("\
-Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
+  if (wrong_option != NULL)
+    _dl_error_printf ("%s: unrecognized option '%s'\n", argv0, wrong_option);
+  else
+    _dl_error_printf ("%s: missing program name\n", argv0);
+  _dl_error_printf ("Try '%s --help' for more information.\n", argv0);
+  _exit (1);
+}
+
+void
+_dl_help (const char *argv0, struct dl_main_state *state)
+{
+  _dl_printf ("\
+Usage: %s [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
 You have invoked `ld.so', the helper program for shared library executables.\n\
 This program usually lives in the file `/lib/ld.so', and special directives\n\
 in executable files using ELF shared libraries tell the system's program\n\
@@ -46,5 +58,9 @@ of this helper program; chances are you did not intend to run this program.\n\
   --inhibit-rpath LIST  ignore RUNPATH and RPATH information in object names\n\
                         in LIST\n\
   --audit LIST          use objects named in LIST as auditors\n\
-  --preload LIST        preload objects named in LIST\n");
+  --preload LIST        preload objects named in LIST\n\
+  --help                display this help and exit\n\
+",
+              argv0);
+  _exit (0);
 }
diff --git a/elf/rtld.c b/elf/rtld.c
index eac9a1e743..610203d5d2 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1131,6 +1131,7 @@ dl_main (const ElfW(Phdr) *phdr,
   _dl_starting_up = 1;
 #endif
 
+  const char *argv0 = _dl_argv[0];
   if (*user_entry == (ElfW(Addr)) ENTRY_POINT)
     {
       /* Ho ho.  We are not the program interpreter!  We are the program
@@ -1156,8 +1157,12 @@ dl_main (const ElfW(Phdr) *phdr,
       while (_dl_argc > 1)
  if (! strcmp (_dl_argv[1], "--list"))
   {
-    state.mode = list;
-    GLRO(dl_lazy) = -1; /* This means do no dependency analysis.  */
+    if (state.mode != rtld_help)
+      {
+       state.mode = list;
+ /* This means do no dependency analysis.  */
+ GLRO(dl_lazy) = -1;
+      }
 
     ++_dl_skip_args;
     --_dl_argc;
@@ -1165,7 +1170,8 @@ dl_main (const ElfW(Phdr) *phdr,
   }
  else if (! strcmp (_dl_argv[1], "--verify"))
   {
-    state.mode = verify;
+    if (state.mode != rtld_help)
+      state.mode = verify;
 
     ++_dl_skip_args;
     --_dl_argc;
@@ -1212,13 +1218,34 @@ dl_main (const ElfW(Phdr) *phdr,
     _dl_argc -= 2;
     _dl_argv += 2;
   }
+ else if (strcmp (_dl_argv[1], "--help") == 0)
+  {
+    state.mode = rtld_help;
+    --_dl_argc;
+    ++_dl_argv;
+  }
+ else if (_dl_argv[1][0] == '-' && _dl_argv[1][1] == '-')
+  {
+   if (_dl_argv[1][1] == '\0')
+     /* End of option list.  */
+     break;
+   else
+     /* Unrecognized option.  */
+     _dl_usage (argv0, _dl_argv[1]);
+  }
  else
   break;
 
       /* If we have no further argument the program was called incorrectly.
  Grant the user some education.  */
       if (_dl_argc < 2)
- _dl_usage ();
+ {
+  if (state.mode == rtld_help)
+    /* --help without an executable is not an error.  */
+    _dl_help (argv0, &state);
+  else
+    _dl_usage (argv0, NULL);
+ }
 
       ++_dl_skip_args;
       --_dl_argc;
@@ -1243,7 +1270,7 @@ dl_main (const ElfW(Phdr) *phdr,
     break;
   }
 
-      if (__builtin_expect (state.mode, normal) == verify)
+      if (state.mode == verify || state.mode == rtld_help)
  {
   const char *objname;
   const char *err_str = NULL;
@@ -1256,9 +1283,16 @@ dl_main (const ElfW(Phdr) *phdr,
   (void) _dl_catch_error (&objname, &err_str, &malloced, map_doit,
   &args);
   if (__glibc_unlikely (err_str != NULL))
-    /* We don't free the returned string, the programs stops
-       anyway.  */
-    _exit (EXIT_FAILURE);
+    {
+      /* We don't free the returned string, the programs stops
+ anyway.  */
+      if (state.mode == rtld_help)
+ /* Mask the failure to load the main object.  The help
+   message contains less information in this case.  */
+ _dl_help (argv0, &state);
+      else
+ _exit (EXIT_FAILURE);
+    }
  }
       else
  {
@@ -1607,6 +1641,11 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
   audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_AUDIT);
   audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_DEPAUDIT);
 
+  /* At this point, all data has been obtained that is included in the
+     --help output.  */
+  if (__builtin_expect (state.mode, normal) == rtld_help)
+    _dl_help (argv0, &state);
+
   /* If we have auditing DSOs to load, do it now.  */
   bool need_security_init = true;
   if (state.audit_list.length > 0)
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 10/30] elf: Implement ld.so --version

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
This prints out version information for the dynamic loader and
exits immediately, without further command line processing
(which seems to match what some GNU tools do).
---
 elf/dl-main.h  |  3 +++
 elf/dl-usage.c | 15 +++++++++++++++
 elf/rtld.c     |  2 ++
 3 files changed, 20 insertions(+)

diff --git a/elf/dl-main.h b/elf/dl-main.h
index 71ca5114de..0df849d3cd 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -101,6 +101,9 @@ call_init_paths (const struct dl_main_state *state)
 void _dl_usage (const char *argv0, const char *wrong_option)
   attribute_hidden __attribute__ ((__noreturn__));
 
+/* Print ld.so version information and exit.  */
+void _dl_version (void) attribute_hidden __attribute__ ((__noreturn__));
+
 /* Print ld.so --help output and exit.  */
 void _dl_help (const char *argv0, struct dl_main_state *state)
   attribute_hidden __attribute__ ((__noreturn__));
diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index e1dc5d33b2..c8d182c442 100644
--- a/elf/dl-usage.c
+++ b/elf/dl-usage.c
@@ -20,6 +20,7 @@
 #include <dl-main.h>
 #include <ldsodefs.h>
 #include <unistd.h>
+#include "version.h"
 
 void
 _dl_usage (const char *argv0, const char *wrong_option)
@@ -32,6 +33,19 @@ _dl_usage (const char *argv0, const char *wrong_option)
   _exit (1);
 }
 
+void
+_dl_version (void)
+{
+  _dl_printf ("\
+ld.so " PKGVERSION RELEASE " release version " VERSION ".\n\
+Copyright (C) 2020 Free Software Foundation, Inc.\n\
+This is free software; see the source for copying conditions.\n\
+There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n\
+PARTICULAR PURPOSE.\n\
+");
+  _exit (0);
+}
+
 void
 _dl_help (const char *argv0, struct dl_main_state *state)
 {
@@ -60,6 +74,7 @@ of this helper program; chances are you did not intend to run this program.\n\
   --audit LIST          use objects named in LIST as auditors\n\
   --preload LIST        preload objects named in LIST\n\
   --help                display this help and exit\n\
+  --version             output version information and exit\n\
 ",
               argv0);
   _exit (0);
diff --git a/elf/rtld.c b/elf/rtld.c
index 610203d5d2..06bc8eca9a 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1224,6 +1224,8 @@ dl_main (const ElfW(Phdr) *phdr,
     --_dl_argc;
     ++_dl_argv;
   }
+ else if (strcmp (_dl_argv[1], "--version") == 0)
+  _dl_version ();
  else if (_dl_argv[1][0] == '-' && _dl_argv[1][1] == '-')
   {
    if (_dl_argv[1][1] == '\0')
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 11/30] scripts/update-copyrights: Update csu/version.c, elf/dl-usage.c

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
---
 scripts/update-copyrights | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/scripts/update-copyrights b/scripts/update-copyrights
index 5ab9489511..7cca0f2c3d 100755
--- a/scripts/update-copyrights
+++ b/scripts/update-copyrights
@@ -70,6 +70,12 @@ for f in $files; do
       # Pre-1991 gaps in copyright years, so cannot use a single range.
       UPDATE_COPYRIGHT_USE_INTERVALS=1 "$update_script" "$f"
       ;;
+    csu/version.c | elf/dl-usage.c)
+      # Update the copyright string in the output message.
+      year="$(date +%Y)"
+      sed -i 's/^Copyright (C) [0-9]\{4\} /Copyright (C) '"$year"' /' $f
+      "$update_script" "$f"
+      ;;
     *)
       "$update_script" "$f"
       ;;
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 12/30] elf: Use the term "program interpreter" in the ld.so help message

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
This is the term that the ELF standard itself uses.
---
 elf/dl-usage.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index c8d182c442..0a12c631a6 100644
--- a/elf/dl-usage.c
+++ b/elf/dl-usage.c
@@ -51,17 +51,17 @@ _dl_help (const char *argv0, struct dl_main_state *state)
 {
   _dl_printf ("\
 Usage: %s [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...]\n\
-You have invoked `ld.so', the helper program for shared library executables.\n\
-This program usually lives in the file `/lib/ld.so', and special directives\n\
-in executable files using ELF shared libraries tell the system's program\n\
-loader to load the helper program from this file.  This helper program loads\n\
-the shared libraries needed by the program executable, prepares the program\n\
-to run, and runs it.  You may invoke this helper program directly from the\n\
+You have invoked 'ld.so', the program interpreter for dynamically-linked\n\
+ELF programs.  Usually, the program interpreter is invoked automatically\n\
+when a dynamically-linked executable is started.\n\
+\n\
+You may invoke the program interpreter program directly from the\n\
 command line to load and run an ELF executable file; this is like executing\n\
-that file itself, but always uses this helper program from the file you\n\
-specified, instead of the helper program file specified in the executable\n\
-file you run.  This is mostly of use for maintainers to test new versions\n\
-of this helper program; chances are you did not intend to run this program.\n\
+that file itself, but always uses the invoked program interpreter you\n\
+invoked, instead of the program interpreter specified in the executable\n\
+file you run.  Invoking the program interpreter directly provides access to\n\
+additional diagnostics, and changing the dynamic linker behavior without\n\
+setting environment variables (which would be inherted by subprocesses).\n\
 \n\
   --list                list all dependencies and how they are resolved\n\
   --verify              verify that given object really is a dynamically linked\n\
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 13/30] elf: Print the full name of the dynamic loader in the ld.so help message

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
This requires defining a macro for the full path, matching the
-Wl,--dynamic-link= arguments used for linking glibc programs,
and ldd script.
---
 elf/Makefile   | 3 ++-
 elf/dl-usage.c | 2 ++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/elf/Makefile b/elf/Makefile
index 85f7e08e00..d2f58c8ce6 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -648,7 +648,8 @@ libof-ldconfig = ldconfig
 CFLAGS-dl-cache.c += $(SYSCONF-FLAGS)
 CFLAGS-cache.c += $(SYSCONF-FLAGS)
 CFLAGS-rtld.c += $(SYSCONF-FLAGS)
-CFLAGS-dl-usage.c += $(SYSCONF-FLAGS)
+CFLAGS-dl-usage.c += $(SYSCONF-FLAGS) \
+  -D'RTLD="$(rtlddir)/$(rtld-installed-name)"'
 
 cpp-srcs-left := $(all-rtld-routines:=.os)
 lib := rtld
diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index 0a12c631a6..5caf9794c6 100644
--- a/elf/dl-usage.c
+++ b/elf/dl-usage.c
@@ -75,6 +75,8 @@ setting environment variables (which would be inherted by subprocesses).\n\
   --preload LIST        preload objects named in LIST\n\
   --help                display this help and exit\n\
   --version             output version information and exit\n\
+\n\
+This program interpreter self-identifies as: " RTLD "\n\
 ",
               argv0);
   _exit (0);
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 14/30] elf: Make __rtld_env_path_list and __rtld_search_dirs global variables

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
They have been renamed from env_path_list and rtld_search_dirs to
avoid linknamespace issues.

This change will allow future use these variables in diagnostics.
---
 elf/dl-load.c  | 53 +++++++++++++++++++++++++-------------------------
 include/link.h |  4 ++++
 2 files changed, 31 insertions(+), 26 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 2339ada485..675d5bd48b 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -98,7 +98,7 @@ int __stack_prot attribute_hidden attribute_relro
 
 
 /* This is the decomposed LD_LIBRARY_PATH search path.  */
-static struct r_search_path_struct env_path_list attribute_relro;
+struct r_search_path_struct __rtld_env_path_list attribute_relro;
 
 /* List of the hardware capabilities we might end up using.  */
 #ifdef SHARED
@@ -442,7 +442,7 @@ add_name_to_object (struct link_map *l, const char *name)
 }
 
 /* Standard search directories.  */
-static struct r_search_path_struct rtld_search_dirs attribute_relro;
+struct r_search_path_struct __rtld_search_dirs attribute_relro;
 
 static size_t max_dirnamelen;
 
@@ -702,9 +702,9 @@ _dl_init_paths (const char *llp, const char *source)
 #endif
 
   /* First set up the rest of the default search directory entries.  */
-  aelem = rtld_search_dirs.dirs = (struct r_search_path_elem **)
+  aelem = __rtld_search_dirs.dirs = (struct r_search_path_elem **)
     malloc ((nsystem_dirs_len + 1) * sizeof (struct r_search_path_elem *));
-  if (rtld_search_dirs.dirs == NULL)
+  if (__rtld_search_dirs.dirs == NULL)
     {
       errstring = N_("cannot create search path array");
     signal_error:
@@ -715,16 +715,17 @@ _dl_init_paths (const char *llp, const char *source)
  + ncapstr * sizeof (enum r_dir_status))
  / sizeof (struct r_search_path_elem));
 
-  rtld_search_dirs.dirs[0] = malloc (nsystem_dirs_len * round_size
-     * sizeof (*rtld_search_dirs.dirs[0]));
-  if (rtld_search_dirs.dirs[0] == NULL)
+  __rtld_search_dirs.dirs[0]
+    = malloc (nsystem_dirs_len * round_size
+      * sizeof (*__rtld_search_dirs.dirs[0]));
+  if (__rtld_search_dirs.dirs[0] == NULL)
     {
       errstring = N_("cannot create cache for search path");
       goto signal_error;
     }
 
-  rtld_search_dirs.malloced = 0;
-  pelem = GL(dl_all_dirs) = rtld_search_dirs.dirs[0];
+  __rtld_search_dirs.malloced = 0;
+  pelem = GL(dl_all_dirs) = __rtld_search_dirs.dirs[0];
   strp = system_dirs;
   idx = 0;
 
@@ -811,27 +812,27 @@ _dl_init_paths (const char *llp, const char *source)
  if (*cp == ':' || *cp == ';')
   ++nllp;
 
-      env_path_list.dirs = (struct r_search_path_elem **)
+      __rtld_env_path_list.dirs = (struct r_search_path_elem **)
  malloc ((nllp + 1) * sizeof (struct r_search_path_elem *));
-      if (env_path_list.dirs == NULL)
+      if (__rtld_env_path_list.dirs == NULL)
  {
   errstring = N_("cannot create cache for search path");
   goto signal_error;
  }
 
-      (void) fillin_rpath (llp_tmp, env_path_list.dirs, ":;",
+      (void) fillin_rpath (llp_tmp, __rtld_env_path_list.dirs, ":;",
    source, NULL, l);
 
-      if (env_path_list.dirs[0] == NULL)
+      if (__rtld_env_path_list.dirs[0] == NULL)
  {
-  free (env_path_list.dirs);
-  env_path_list.dirs = (void *) -1;
+  free (__rtld_env_path_list.dirs);
+  __rtld_env_path_list.dirs = (void *) -1;
  }
 
-      env_path_list.malloced = 0;
+      __rtld_env_path_list.malloced = 0;
     }
   else
-    env_path_list.dirs = (void *) -1;
+    __rtld_env_path_list.dirs = (void *) -1;
 }
 
 
@@ -1918,9 +1919,9 @@ open_path (const char *name, size_t namelen, int mode,
       if (sps->malloced)
  free (sps->dirs);
 
-      /* rtld_search_dirs and env_path_list are attribute_relro, therefore
- avoid writing into it.  */
-      if (sps != &rtld_search_dirs && sps != &env_path_list)
+      /* __rtld_search_dirs and __rtld_env_path_list are
+ attribute_relro, therefore avoid writing to them.  */
+      if (sps != &__rtld_search_dirs && sps != &__rtld_env_path_list)
  sps->dirs = (void *) -1;
     }
 
@@ -2068,8 +2069,8 @@ _dl_map_object (struct link_map *loader, const char *name,
  }
 
       /* Try the LD_LIBRARY_PATH environment variable.  */
-      if (fd == -1 && env_path_list.dirs != (void *) -1)
- fd = open_path (name, namelen, mode, &env_path_list,
+      if (fd == -1 && __rtld_env_path_list.dirs != (void *) -1)
+ fd = open_path (name, namelen, mode, &__rtld_env_path_list,
  &realname, &fb,
  loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded,
  LA_SER_LIBPATH, &found_other_class);
@@ -2158,8 +2159,8 @@ _dl_map_object (struct link_map *loader, const char *name,
       if (fd == -1
   && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL
       || __glibc_likely (!(l->l_flags_1 & DF_1_NODEFLIB)))
-  && rtld_search_dirs.dirs != (void *) -1)
- fd = open_path (name, namelen, mode, &rtld_search_dirs,
+  && __rtld_search_dirs.dirs != (void *) -1)
+ fd = open_path (name, namelen, mode, &__rtld_search_dirs,
  &realname, &fb, l, LA_SER_DEFAULT, &found_other_class);
 
       /* Add another newline when we are tracing the library loading.  */
@@ -2327,7 +2328,7 @@ _dl_rtld_di_serinfo (struct link_map *loader, Dl_serinfo *si, bool counting)
     }
 
   /* Try the LD_LIBRARY_PATH environment variable.  */
-  add_path (&p, &env_path_list, XXX_ENV);
+  add_path (&p, &__rtld_env_path_list, XXX_ENV);
 
   /* Look at the RUNPATH information for this binary.  */
   if (cache_rpath (loader, &loader->l_runpath_dirs, DT_RUNPATH, "RUNPATH"))
@@ -2339,7 +2340,7 @@ _dl_rtld_di_serinfo (struct link_map *loader, Dl_serinfo *si, bool counting)
 
   /* Finally, try the default path.  */
   if (!(loader->l_flags_1 & DF_1_NODEFLIB))
-    add_path (&p, &rtld_search_dirs, XXX_default);
+    add_path (&p, &__rtld_search_dirs, XXX_default);
 
   if (counting)
     /* Count the struct size before the string area, which we didn't
diff --git a/include/link.h b/include/link.h
index aea268439c..d4714bc28d 100644
--- a/include/link.h
+++ b/include/link.h
@@ -79,6 +79,10 @@ struct r_search_path_struct
     int malloced;
   };
 
+/* Search path information computed by _dl_init_paths.  */
+extern struct r_search_path_struct __rtld_search_dirs attribute_hidden;
+extern struct r_search_path_struct __rtld_env_path_list attribute_hidden;
+
 /* Structure describing a loaded shared object.  The `l_next' and `l_prev'
    members form a chain of all the shared objects loaded at startup.
 
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 15/30] elf: Add library search path information to ld.so --help

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
---
 elf/dl-usage.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index 5caf9794c6..4712ab1c72 100644
--- a/elf/dl-usage.c
+++ b/elf/dl-usage.c
@@ -46,6 +46,61 @@ PARTICULAR PURPOSE.\n\
   _exit (0);
 }
 
+/* Print part of the library search path (from a single source).  */
+static void
+print_search_path_for_help_1 (struct r_search_path_elem **list)
+{
+  if (list == NULL || list == (void *) -1)
+    /* Path is missing or marked as inactive.  */
+    return;
+
+  for (; *list != NULL; ++list)
+    {
+      (void) _dl_write (STDOUT_FILENO, "  ", 2);
+      const char *name = (*list)->dirname;
+      size_t namelen = (*list)->dirnamelen;
+      if (namelen == 0)
+        {
+          /* The empty string denotes the current directory.  */
+          name = ".";
+          namelen = 1;
+        }
+      else if (namelen > 1)
+        /* Remove the trailing slash.  */
+        --namelen;
+      (void) _dl_write (STDOUT_FILENO, name, namelen);
+      _dl_printf (" (%s)\n", (*list)->what);
+    }
+}
+
+/* Prints the library search path.  See _dl_init_paths in dl-load.c
+   how this information is populated.  */
+static void
+print_search_path_for_help (struct dl_main_state *state)
+{
+  if (__rtld_search_dirs.dirs == NULL)
+    /* The run-time search paths have not yet been initialized.  */
+    _dl_init_paths (state->library_path, state->library_path_source);
+
+  _dl_printf ("\nShared library search path:\n");
+
+  /* The print order should reflect the processing in
+     _dl_map_object.  */
+
+  struct link_map *map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+  if (map != NULL)
+    print_search_path_for_help_1 (map->l_rpath_dirs.dirs);
+
+  print_search_path_for_help_1 (__rtld_env_path_list.dirs);
+
+  if (map != NULL)
+    print_search_path_for_help_1 (map->l_runpath_dirs.dirs);
+
+  _dl_printf ("  (libraries located via %s)\n", LD_SO_CACHE);
+
+  print_search_path_for_help_1 (__rtld_search_dirs.dirs);
+}
+
 void
 _dl_help (const char *argv0, struct dl_main_state *state)
 {
@@ -79,5 +134,6 @@ setting environment variables (which would be inherted by subprocesses).\n\
 This program interpreter self-identifies as: " RTLD "\n\
 ",
               argv0);
+  print_search_path_for_help (state);
   _exit (0);
 }
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 16/30] elf: Enhance ld.so --help to print HWCAP subdirectories

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
---
 elf/dl-usage.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index 4712ab1c72..7ff642349c 100644
--- a/elf/dl-usage.c
+++ b/elf/dl-usage.c
@@ -22,6 +22,8 @@
 #include <unistd.h>
 #include "version.h"
 
+#include <dl-hwcaps.h>
+
 void
 _dl_usage (const char *argv0, const char *wrong_option)
 {
@@ -101,6 +103,65 @@ print_search_path_for_help (struct dl_main_state *state)
   print_search_path_for_help_1 (__rtld_search_dirs.dirs);
 }
 
+/* Helper function for printing flags associated with a HWCAP name.  */
+static void
+print_hwcap_1 (bool *first, bool active, const char *label)
+{
+  if (active)
+    {
+      if (*first)
+        {
+          _dl_printf (" (");
+          *first = false;
+        }
+      else
+        _dl_printf (", ");
+      _dl_printf ("%s", label);
+    }
+}
+
+/* Called after a series of print_hwcap_1 calls to emit the line
+   terminator.  */
+static void
+print_hwcap_1_finish (bool *first)
+{
+  if (*first)
+    _dl_printf ("\n");
+  else
+    _dl_printf (")\n");
+}
+
+/* Write a list of hwcap subdirectories to standard output.  See
+ _dl_important_hwcaps in dl-hwcaps.c.  */
+static void
+print_legacy_hwcap_directories (void)
+{
+  _dl_printf ("\n\
+Legacy HWCAP subdirectories under library search path directories:\n");
+
+  const char *platform = GLRO (dl_platform);
+  if (platform != NULL)
+    _dl_printf ("  %s (AT_PLATFORM)\n", platform);
+
+  _dl_printf ("  tls (supported, searched)\n");
+
+  uint64_t hwcap_mask = GET_HWCAP_MASK();
+  uint64_t searched = GLRO (dl_hwcap) & hwcap_mask;
+  for (int n = 63; n >= 0; --n)
+    {
+      uint64_t bit = 1ULL << n;
+      if (HWCAP_IMPORTANT & bit)
+        {
+          _dl_printf ("  %s", _dl_hwcap_string (n));
+          bool first = true;
+          print_hwcap_1 (&first, GLRO (dl_hwcap) & bit, "supported");
+          print_hwcap_1 (&first, !(hwcap_mask & bit), "masked");
+          print_hwcap_1 (&first, searched & bit, "searched");
+          print_hwcap_1_finish (&first);
+        }
+    }
+}
+
 void
 _dl_help (const char *argv0, struct dl_main_state *state)
 {
@@ -135,5 +196,6 @@ This program interpreter self-identifies as: " RTLD "\n\
 ",
               argv0);
   print_search_path_for_help (state);
+  print_legacy_hwcap_directories ();
   _exit (0);
 }
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 17/30] elf: Do not pass GLRO(dl_platform), GLRO(dl_platformlen) to _dl_important_hwcaps

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
In the current code, the function can easily obtain the information
on its own.
---
 elf/dl-hwcaps.c            | 11 +++++------
 elf/dl-load.c              |  3 +--
 sysdeps/generic/ldsodefs.h | 12 ++++++------
 3 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c
index 6df9efb255..44dbac099f 100644
--- a/elf/dl-hwcaps.c
+++ b/elf/dl-hwcaps.c
@@ -28,13 +28,12 @@
 
 /* Return an array of useful/necessary hardware capability names.  */
 const struct r_strlenpair *
-_dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
-      size_t *max_capstrlen)
+_dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
 {
   uint64_t hwcap_mask = GET_HWCAP_MASK();
   /* Determine how many important bits are set.  */
   uint64_t masked = GLRO(dl_hwcap) & hwcap_mask;
-  size_t cnt = platform != NULL;
+  size_t cnt = GLRO (dl_platform) != NULL;
   size_t n, m;
   size_t total;
   struct r_strlenpair *result;
@@ -60,10 +59,10 @@ _dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
  masked ^= 1ULL << n;
  ++m;
       }
-  if (platform != NULL)
+  if (GLRO (dl_platform) != NULL)
     {
-      temp[m].str = platform;
-      temp[m].len = platform_len;
+      temp[m].str = GLRO (dl_platform);
+      temp[m].len = GLRO (dl_platformlen);
       ++m;
     }
 
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 675d5bd48b..6e5fa2af13 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -697,8 +697,7 @@ _dl_init_paths (const char *llp, const char *source)
 
 #ifdef SHARED
   /* Get the capabilities.  */
-  capstr = _dl_important_hwcaps (GLRO(dl_platform), GLRO(dl_platformlen),
- &ncapstr, &max_capstrlen);
+  capstr = _dl_important_hwcaps (&ncapstr, &max_capstrlen);
 #endif
 
   /* First set up the rest of the default search directory entries.  */
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 0f23352302..2de060848b 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1059,12 +1059,12 @@ extern void _dl_show_auxv (void) attribute_hidden;
    other.  */
 extern char *_dl_next_ld_env_entry (char ***position) attribute_hidden;
 
-/* Return an array with the names of the important hardware capabilities.  */
-extern const struct r_strlenpair *_dl_important_hwcaps (const char *platform,
- size_t paltform_len,
- size_t *sz,
- size_t *max_capstrlen)
-     attribute_hidden;
+/* Return an array with the names of the important hardware
+   capabilities.  The length of the array is written to *SZ, and the
+   maximum of all strings length is written to *MAX_CAPSTRLEN.  */
+const struct r_strlenpair *_dl_important_hwcaps (size_t *sz,
+ size_t *max_capstrlen)
+  attribute_hidden;
 
 /* Look up NAME in ld.so.cache and return the file name stored there,
    or null if none is found.  Caller must free returned string.  */
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 18/30] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
This hacks non-power-set processing into _dl_important_hwcaps.
Once the legacy hwcaps handling goes away, the subdirectory
handling needs to be reworked, but it is premature to do this
while both approaches are still supported.
---
 elf/Makefile               |   5 +-
 elf/dl-hwcaps-subdirs.c    |  29 ++++++++
 elf/dl-hwcaps.c            | 138 +++++++++++++++++++++++++++++++-----
 elf/dl-hwcaps.h            |  83 ++++++++++++++++++++++
 elf/dl-hwcaps_split.c      |  77 ++++++++++++++++++++
 elf/dl-load.c              |   7 +-
 elf/dl-main.h              |  11 ++-
 elf/dl-support.c           |   5 +-
 elf/dl-usage.c             |  68 +++++++++++++++++-
 elf/rtld.c                 |  18 +++++
 elf/tst-dl-hwcaps_split.c  | 139 +++++++++++++++++++++++++++++++++++++
 sysdeps/generic/ldsodefs.h |  20 ++++--
 12 files changed, 570 insertions(+), 30 deletions(-)
 create mode 100644 elf/dl-hwcaps-subdirs.c
 create mode 100644 elf/dl-hwcaps_split.c
 create mode 100644 elf/tst-dl-hwcaps_split.c

diff --git a/elf/Makefile b/elf/Makefile
index d2f58c8ce6..728cb3b734 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -59,7 +59,8 @@ elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \
 # 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-usage
+  dl-error-minimal dl-conflict dl-hwcaps dl-hwcaps_split dl-hwcaps-subdirs \
+  dl-usage
 all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines)
 
 CFLAGS-dl-runtime.c += -fexceptions -fasynchronous-unwind-tables
@@ -210,7 +211,7 @@ 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-create_format1 tst-dl-hwcaps_split
 tests-container += tst-pldd tst-dlopen-tlsmodid-container \
   tst-dlopen-self-container
 test-srcs = tst-pathopt
diff --git a/elf/dl-hwcaps-subdirs.c b/elf/dl-hwcaps-subdirs.c
new file mode 100644
index 0000000000..b142a3b826
--- /dev/null
+++ b/elf/dl-hwcaps-subdirs.c
@@ -0,0 +1,29 @@
+/* Architecture-specific glibc-hwcaps subdirectories.  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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dl-hwcaps.h>
+
+/* In the generic version, there are no subdirectories defined.  */
+
+const char _dl_hwcaps_subdirs[] = "";
+
+int32_t
+_dl_hwcaps_subdirs_active (void)
+{
+  return 0;
+}
diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c
index 44dbac099f..4de94759a2 100644
--- a/elf/dl-hwcaps.c
+++ b/elf/dl-hwcaps.c
@@ -26,20 +26,97 @@
 #include <dl-procinfo.h>
 #include <dl-hwcaps.h>
 
+/* This is the result of counting the substrings in a colon-separated
+   hwcaps string.  */
+struct count_hwcaps
+{
+  /* Number of substrings.  */
+  size_t count;
+
+  /* Sum of the individual substring lengths (without separates or
+     null terminators).  */
+  size_t total_length;
+
+  /* Maximum length of an individual substring.  */
+  size_t maximum_length;
+};
+
+/* Update *COUNTS according to the contents of HWCAPS.  Skip over
+   entries whose bit is not set in MASK.  */
+static void
+count_hwcaps (struct count_hwcaps *counts, const char *hwcaps,
+      int32_t bitmask, const char *mask)
+{
+  struct dl_hwcaps_split_masked sp;
+  _dl_hwcaps_split_masked_init (&sp, hwcaps, bitmask, mask);
+  while (_dl_hwcaps_split_masked (&sp))
+    {
+      ++counts->count;
+      counts->total_length += sp.split.length;
+      if (sp.split.length > counts->maximum_length)
+ counts->maximum_length = sp.split.length;
+    }
+}
+
+/* State for copy_hwcaps.  Must be initialized to point to
+   the storage areas for the array and the strings themselves.  */
+struct copy_hwcaps
+{
+  struct r_strlenpair *next_pair;
+  char *next_string;
+};
+
+/* Copy HWCAPS into the string pairs and strings, advancing *TARGET.
+   Skip over entries whose bit is not set in MASK.  */
+static void
+copy_hwcaps (struct copy_hwcaps *target, const char *hwcaps,
+     int32_t bitmask, const char *mask)
+{
+  struct dl_hwcaps_split_masked sp;
+  _dl_hwcaps_split_masked_init (&sp, hwcaps, bitmask, mask);
+  while (_dl_hwcaps_split_masked (&sp))
+    {
+      target->next_pair->str = target->next_string;
+      char *slash = __mempcpy (__mempcpy (target->next_string,
+  GLIBC_HWCAPS_PREFIX,
+  strlen (GLIBC_HWCAPS_PREFIX)),
+       sp.split.segment, sp.split.length);
+      *slash = '/';
+      target->next_pair->len
+ = strlen (GLIBC_HWCAPS_PREFIX) + sp.split.length + 1;
+      ++target->next_pair;
+      target->next_string = slash + 1;
+    }
+}
+
 /* Return an array of useful/necessary hardware capability names.  */
 const struct r_strlenpair *
-_dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
+_dl_important_hwcaps (const char *glibc_hwcaps_prepend,
+      const char *glibc_hwcaps_mask,
+      size_t *sz, size_t *max_capstrlen)
 {
   uint64_t hwcap_mask = GET_HWCAP_MASK();
   /* Determine how many important bits are set.  */
   uint64_t masked = GLRO(dl_hwcap) & hwcap_mask;
   size_t cnt = GLRO (dl_platform) != NULL;
   size_t n, m;
-  size_t total;
   struct r_strlenpair *result;
   struct r_strlenpair *rp;
   char *cp;
 
+  /* glibc-hwcaps subdirectories.  These are exempted from the power
+     set construction below below.  */
+  int32_t hwcaps_subdirs_active = _dl_hwcaps_subdirs_active ();
+  struct count_hwcaps hwcaps_counts =  { 0, };
+  count_hwcaps (&hwcaps_counts, glibc_hwcaps_prepend, -1, NULL);
+  count_hwcaps (&hwcaps_counts, _dl_hwcaps_subdirs, hwcaps_subdirs_active,
+ glibc_hwcaps_mask);
+
+  /* Each hwcaps subdirectory has a GLIBC_HWCAPS_PREFIX string prefix
+     and a "/" suffix once stored in the result.  */
+  size_t total = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1)
+  + hwcaps_counts.total_length);
+
   /* Count the number of bits set in the masked value.  */
   for (n = 0; (~((1ULL << n) - 1) & masked) != 0; ++n)
     if ((masked & (1ULL << n)) != 0)
@@ -74,10 +151,10 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
 
   /* Determine the total size of all strings together.  */
   if (cnt == 1)
-    total = temp[0].len + 1;
+    total += temp[0].len + 1;
   else
     {
-      total = temp[0].len + temp[cnt - 1].len + 2;
+      total += temp[0].len + temp[cnt - 1].len + 2;
       if (cnt > 2)
  {
   total <<= 1;
@@ -94,26 +171,48 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
  }
     }
 
-  /* The result structure: we use a very compressed way to store the
-     various combinations of capability names.  */
-  *sz = 1 << cnt;
-  result = (struct r_strlenpair *) malloc (*sz * sizeof (*result) + total);
-  if (result == NULL)
+  *sz = hwcaps_counts.count + (1 << cnt);
+
+  /* This is the overall result, including both glibc-hwcaps
+     subdirectories and the legacy hwcaps subdirectories using the
+     power set construction.  */
+  struct r_strlenpair *overall_result
+    = malloc (*sz * sizeof (*result) + total);
+  if (overall_result == NULL)
     _dl_signal_error (ENOMEM, NULL, NULL,
       N_("cannot create capability list"));
 
+  /* Fill in the glibc-hwcaps subdirectories.  */
+  {
+    struct copy_hwcaps target;
+    target.next_pair = overall_result;
+    target.next_string = (char *) (overall_result + *sz);
+    copy_hwcaps (&target, glibc_hwcaps_prepend, -1, NULL);
+    copy_hwcaps (&target, _dl_hwcaps_subdirs,
+ hwcaps_subdirs_active, glibc_hwcaps_mask);
+    /* Set up the write target for the power set construction.  */
+    result = target.next_pair;
+    cp = target.next_string;
+  }
+
+
+  /* Power set construction begins here.  We use a very compressed way
+     to store the various combinations of capability names.  */
+
   if (cnt == 1)
     {
-      result[0].str = (char *) (result + *sz);
+      result[0].str = cp;
       result[0].len = temp[0].len + 1;
-      result[1].str = (char *) (result + *sz);
+      result[1].str = cp;
       result[1].len = 0;
-      cp = __mempcpy ((char *) (result + *sz), temp[0].str, temp[0].len);
+      cp = __mempcpy (cp, temp[0].str, temp[0].len);
       *cp = '/';
-      *sz = 2;
-      *max_capstrlen = result[0].len;
+      if (result[0].len > hwcaps_counts.maximum_length)
+ *max_capstrlen = result[0].len;
+      else
+ *max_capstrlen = hwcaps_counts.maximum_length;
 
-      return result;
+      return overall_result;
     }
 
   /* Fill in the information.  This follows the following scheme
@@ -124,7 +223,7 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
       #3: 0, 3 1001
      This allows the representation of all possible combinations of
      capability names in the string.  First generate the strings.  */
-  result[1].str = result[0].str = cp = (char *) (result + *sz);
+  result[1].str = result[0].str = cp;
 #define add(idx) \
       cp = __mempcpy (__mempcpy (cp, temp[idx].str, temp[idx].len), "/", 1);
   if (cnt == 2)
@@ -191,7 +290,10 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen)
   while (--n != 0);
 
   /* The maximum string length.  */
-  *max_capstrlen = result[0].len;
+  if (result[0].len > hwcaps_counts.maximum_length)
+    *max_capstrlen = result[0].len;
+  else
+    *max_capstrlen = hwcaps_counts.maximum_length;
 
-  return result;
+  return overall_result;
 }
diff --git a/elf/dl-hwcaps.h b/elf/dl-hwcaps.h
index b66da59b89..a6453f15f3 100644
--- a/elf/dl-hwcaps.h
+++ b/elf/dl-hwcaps.h
@@ -16,6 +16,11 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+#ifndef _DL_HWCAPS_H
+#define _DL_HWCAPS_H
+
+#include <stdint.h>
+
 #include <elf/dl-tunables.h>
 
 #if HAVE_TUNABLES
@@ -28,3 +33,81 @@
 #  define GET_HWCAP_MASK() (0)
 # endif
 #endif
+
+#define GLIBC_HWCAPS_SUBDIRECTORY "glibc-hwcaps"
+#define GLIBC_HWCAPS_PREFIX GLIBC_HWCAPS_SUBDIRECTORY "/"
+
+/* Used by _dl_hwcaps_split below, to split strings at ':'
+   separators.  */
+struct dl_hwcaps_split
+{
+  const char *segment;          /* Start of the current segment.  */
+  size_t length;                /* Number of bytes until ':' or NUL.  */
+};
+
+/* Prepare *S to parse SUBJECT, for future _dl_hwcaps_split calls.  If
+   SUBJECT is NULL, it is treated as the empty string.  */
+static inline void
+_dl_hwcaps_split_init (struct dl_hwcaps_split *s, const char *subject)
+{
+  s->segment = subject;
+  /* The initial call to _dl_hwcaps_split will not skip anything.  */
+  s->length = 0;
+}
+
+/* Extract the next non-empty string segment, up to ':' or the null
+   terminator.  Return true if one more segment was found, or false if
+   the end of the string was reached.  On success, S->segment is the
+   start of the segment found, and S->length is its length.
+   (Typically, S->segment[S->length] is not null.)  */
+_Bool _dl_hwcaps_split (struct dl_hwcaps_split *s) attribute_hidden;
+
+/* Similar to dl_hwcaps_split, but with bit-based and name-based
+   masking.  */
+struct dl_hwcaps_split_masked
+{
+  struct dl_hwcaps_split split;
+
+  /* For used by the iterator implementation.  */
+  const char *mask;
+  int32_t bitmask;
+};
+
+/* Prepare *S for iteration with _dl_hwcaps_split_masked.  Only HWCAP
+   names in SUBJECT whose bit is set in BITMASK and whose ane is in
+   MASK will be returned.  SUBJECT must not contain empty HWCAP names.
+   If MASK is NULL, no name-based masking is applied.  Likewise for
+   BITMASK if BITMASK is -1 (infinite number of bits).  */
+static inline void
+_dl_hwcaps_split_masked_init (struct dl_hwcaps_split_masked *s,
+                              const char *subject,
+                              int32_t bitmask, const char *mask)
+{
+  _dl_hwcaps_split_init (&s->split, subject);
+  s->bitmask = bitmask;
+  s->mask = mask;
+}
+
+/* Like _dl_hwcaps_split, but apply masking.  */
+_Bool _dl_hwcaps_split_masked (struct dl_hwcaps_split_masked *s)
+  attribute_hidden;
+
+/* Returns true if the colon-separated HWCAP list HWCAPS contains the
+   capability NAME (with length NAME_LENGTH).  If HWCAPS is NULL, the
+   function returns true.  */
+_Bool _dl_hwcaps_contains (const char *hwcaps, const char *name,
+                           size_t name_length) attribute_hidden;
+
+/* Colon-separated string of glibc-hwcaps subdirectories, without the
+   "glibc-hwcaps/" prefix.  The most preferred subdirectory needs to
+   be listed first.  */
+extern const char _dl_hwcaps_subdirs[] attribute_hidden;
+
+/* Returns a bitmap of active subdirectories in _dl_hwcaps_subdirs.
+   Bit 0 (the LSB) corresponds to the first substring in
+   _dl_hwcaps_subdirs, bit 1 to the second substring, and so on.
+   There is no direct correspondence between HWCAP bitmasks and this
+   bitmask.  */
+int32_t _dl_hwcaps_subdirs_active (void) attribute_hidden;
+
+#endif /* _DL_HWCAPS_H */
diff --git a/elf/dl-hwcaps_split.c b/elf/dl-hwcaps_split.c
new file mode 100644
index 0000000000..95225e9f40
--- /dev/null
+++ b/elf/dl-hwcaps_split.c
@@ -0,0 +1,77 @@
+/* Hardware capability support for run-time dynamic loader.  String splitting.
+   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 <dl-hwcaps.h>
+#include <stdbool.h>
+#include <string.h>
+
+_Bool
+_dl_hwcaps_split (struct dl_hwcaps_split *s)
+{
+  if (s->segment == NULL)
+    return false;
+
+  /* Skip over the previous segment.   */
+  s->segment += s->length;
+
+  /* Consume delimiters.  This also avoids returning an empty
+     segment.  */
+  while (*s->segment == ':')
+    ++s->segment;
+  if (*s->segment == '\0')
+    return false;
+
+  /* This could use strchrnul, but we would have to link the function
+     into ld.so for that.  */
+  const char *colon = strchr (s->segment, ':');
+  if (colon == NULL)
+    s->length = strlen (s->segment);
+  else
+    s->length = colon - s->segment;
+  return true;
+}
+
+_Bool
+_dl_hwcaps_split_masked (struct dl_hwcaps_split_masked *s)
+{
+  while (true)
+    {
+      if (!_dl_hwcaps_split (&s->split))
+        return false;
+      bool active = s->bitmask & 1;
+      s->bitmask >>= 1;
+      if (active && _dl_hwcaps_contains (s->mask,
+                                         s->split.segment, s->split.length))
+        return true;
+    }
+}
+
+_Bool
+_dl_hwcaps_contains (const char *hwcaps, const char *name, size_t name_length)
+{
+  if (hwcaps == NULL)
+    return true;
+
+  struct dl_hwcaps_split split;
+  _dl_hwcaps_split_init (&split, hwcaps);
+  while (_dl_hwcaps_split (&split))
+    if (split.length == name_length
+        && memcmp (split.segment, name, name_length) == 0)
+      return true;
+  return false;
+}
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 6e5fa2af13..f99d791edb 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -682,7 +682,9 @@ cache_rpath (struct link_map *l,
 
 
 void
-_dl_init_paths (const char *llp, const char *source)
+_dl_init_paths (const char *llp, const char *source,
+ const char *glibc_hwcaps_prepend,
+ const char *glibc_hwcaps_mask)
 {
   size_t idx;
   const char *strp;
@@ -697,7 +699,8 @@ _dl_init_paths (const char *llp, const char *source)
 
 #ifdef SHARED
   /* Get the capabilities.  */
-  capstr = _dl_important_hwcaps (&ncapstr, &max_capstrlen);
+  capstr = _dl_important_hwcaps (glibc_hwcaps_prepend, glibc_hwcaps_mask,
+ &ncapstr, &max_capstrlen);
 #endif
 
   /* First set up the rest of the default search directory entries.  */
diff --git a/elf/dl-main.h b/elf/dl-main.h
index 0df849d3cd..710d29685b 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -80,6 +80,14 @@ struct dl_main_state
   /* The preload list passed as a command argument.  */
   const char *preloadarg;
 
+  /* Additional glibc-hwcaps subdirectories to search first.
+     Colon-separated list.  */
+  const char *glibc_hwcaps_prepend;
+
+  /* Mask for the internal glibc-hwcaps subdirectories.
+     Colon-separated list.  */
+  const char *glibc_hwcaps_mask;
+
   enum mode mode;
 
   /* True if any of the debugging options is enabled.  */
@@ -94,7 +102,8 @@ struct dl_main_state
 static inline void
 call_init_paths (const struct dl_main_state *state)
 {
-  _dl_init_paths (state->library_path, state->library_path_source);
+  _dl_init_paths (state->library_path, state->library_path_source,
+                  state->glibc_hwcaps_prepend, state->glibc_hwcaps_mask);
 }
 
 /* Print ld.so usage information and exit.  */
diff --git a/elf/dl-support.c b/elf/dl-support.c
index afbc94df54..3264262f4e 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -323,7 +323,10 @@ _dl_non_dynamic_init (void)
 
   /* Initialize the data structures for the search paths for shared
      objects.  */
-  _dl_init_paths (getenv ("LD_LIBRARY_PATH"), "LD_LIBRARY_PATH");
+  _dl_init_paths (getenv ("LD_LIBRARY_PATH"), "LD_LIBRARY_PATH",
+  /* No glibc-hwcaps selection support in statically
+     linked binaries.  */
+  NULL, NULL);
 
   /* Remember the last search directory added at startup.  */
   _dl_init_all_dirs = GL(dl_all_dirs);
diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index 7ff642349c..e94334e877 100644
--- a/elf/dl-usage.c
+++ b/elf/dl-usage.c
@@ -82,7 +82,7 @@ print_search_path_for_help (struct dl_main_state *state)
 {
   if (__rtld_search_dirs.dirs == NULL)
     /* The run-time search paths have not yet been initialized.  */
-    _dl_init_paths (state->library_path, state->library_path_source);
+    call_init_paths (state);
 
   _dl_printf ("\nShared library search path:\n");
 
@@ -131,6 +131,67 @@ print_hwcap_1_finish (bool *first)
     _dl_printf (")\n");
 }
 
+/* Print the header for print_hwcaps_subdirectories.  */
+static void
+print_hwcaps_subdirectories_header (bool *nothing_printed)
+{
+  if (*nothing_printed)
+    {
+      _dl_printf ("\n\
+Subdirectories of glibc-hwcaps directories, in priority order:\n");
+      *nothing_printed = false;
+    }
+}
+
+/* Print the HWCAP name itself, indented.  */
+static void
+print_hwcaps_subdirectories_name (const struct dl_hwcaps_split *split)
+{
+  _dl_write (STDOUT_FILENO, "  ", 2);
+  _dl_write (STDOUT_FILENO, split->segment, split->length);
+}
+
+/* Print the list of recognized glibc-hwcaps subdirectories.  */
+static void
+print_hwcaps_subdirectories (const struct dl_main_state *state)
+{
+  bool nothing_printed = true;
+  struct dl_hwcaps_split split;
+
+  /* The prepended glibc-hwcaps subdirectories.  */
+  _dl_hwcaps_split_init (&split, state->glibc_hwcaps_prepend);
+  while (_dl_hwcaps_split (&split))
+    {
+      print_hwcaps_subdirectories_header (&nothing_printed);
+      print_hwcaps_subdirectories_name (&split);
+      bool first = true;
+      print_hwcap_1 (&first, true, "searched");
+      print_hwcap_1_finish (&first);
+    }
+
+  /* The built-in glibc-hwcaps subdirectories.  Do the filtering
+     manually, so that more precise diagnostics are possible.  */
+  int32_t mask = _dl_hwcaps_subdirs_active ();
+  _dl_hwcaps_split_init (&split, _dl_hwcaps_subdirs);
+  while (_dl_hwcaps_split (&split))
+    {
+      print_hwcaps_subdirectories_header (&nothing_printed);
+      print_hwcaps_subdirectories_name (&split);
+      bool first = true;
+      print_hwcap_1 (&first, mask & 1, "supported");
+      bool listed = _dl_hwcaps_contains (state->glibc_hwcaps_mask,
+                                         split.segment, split.length);
+      print_hwcap_1 (&first, !listed, "masked");
+      print_hwcap_1 (&first, (mask & 1) && listed, "searched");
+      print_hwcap_1_finish (&first);
+      mask >>= 1;
+    }
+
+  if (nothing_printed)
+    _dl_printf ("\n\
+No subdirectories of glibc-hwcaps directories are searched.\n");
+}
+
 /* Write a list of hwcap subdirectories to standard output.  See
  _dl_important_hwcaps in dl-hwcaps.c.  */
 static void
@@ -185,6 +246,10 @@ setting environment variables (which would be inherted by subprocesses).\n\
   --inhibit-cache       Do not use " LD_SO_CACHE "\n\
   --library-path PATH   use given PATH instead of content of the environment\n\
                         variable LD_LIBRARY_PATH\n\
+  --glibc-hwcaps-prepend LIST\n\
+                        search glibc-hwcaps subdirectories in LIST\n\
+  --glibc-hwcaps-mask LIST\n\
+                        only search built-in subdirectories if in LIST\n\
   --inhibit-rpath LIST  ignore RUNPATH and RPATH information in object names\n\
                         in LIST\n\
   --audit LIST          use objects named in LIST as auditors\n\
@@ -196,6 +261,7 @@ This program interpreter self-identifies as: " RTLD "\n\
 ",
               argv0);
   print_search_path_for_help (state);
+  print_hwcaps_subdirectories (state);
   print_legacy_hwcap_directories ();
   _exit (0);
 }
diff --git a/elf/rtld.c b/elf/rtld.c
index 06bc8eca9a..b31597da3c 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -272,6 +272,8 @@ dl_main_state_init (struct dl_main_state *state)
   state->library_path_source = NULL;
   state->preloadlist = NULL;
   state->preloadarg = NULL;
+  state->glibc_hwcaps_prepend = NULL;
+  state->glibc_hwcaps_mask = NULL;
   state->mode = normal;
   state->any_debug = false;
   state->version_info = false;
@@ -1218,6 +1220,22 @@ dl_main (const ElfW(Phdr) *phdr,
     _dl_argc -= 2;
     _dl_argv += 2;
   }
+ else if (strcmp (_dl_argv[1], "--glibc-hwcaps-prepend") == 0
+ && _dl_argc > 2)
+  {
+    state.glibc_hwcaps_prepend = _dl_argv[2];
+    _dl_skip_args += 2;
+    _dl_argc -= 2;
+    _dl_argv += 2;
+  }
+ else if (strcmp (_dl_argv[1], "--glibc-hwcaps-mask") == 0
+ && _dl_argc > 2)
+  {
+    state.glibc_hwcaps_mask = _dl_argv[2];
+    _dl_skip_args += 2;
+    _dl_argc -= 2;
+    _dl_argv += 2;
+  }
  else if (strcmp (_dl_argv[1], "--help") == 0)
   {
     state.mode = rtld_help;
diff --git a/elf/tst-dl-hwcaps_split.c b/elf/tst-dl-hwcaps_split.c
new file mode 100644
index 0000000000..929c99a23b
--- /dev/null
+++ b/elf/tst-dl-hwcaps_split.c
@@ -0,0 +1,139 @@
+/* Unit tests for dl-hwcaps.c.
+   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 <array_length.h>
+#include <dl-hwcaps.h>
+#include <string.h>
+#include <support/check.h>
+
+static void
+check_split_masked (const char *input, int32_t bitmask, const char *mask,
+                    const char *expected[], size_t expected_length)
+{
+  struct dl_hwcaps_split_masked split;
+  _dl_hwcaps_split_masked_init (&split, input, bitmask, mask);
+  size_t index = 0;
+  while (_dl_hwcaps_split_masked (&split))
+    {
+      TEST_VERIFY_EXIT (index < expected_length);
+      TEST_COMPARE_BLOB (expected[index], strlen (expected[index]),
+                         split.split.segment, split.split.length);
+      ++index;
+    }
+  TEST_COMPARE (index, expected_length);
+}
+
+static void
+check_split (const char *input,
+             const char *expected[], size_t expected_length)
+{
+  struct dl_hwcaps_split split;
+  _dl_hwcaps_split_init (&split, input);
+  size_t index = 0;
+  while (_dl_hwcaps_split (&split))
+    {
+      TEST_VERIFY_EXIT (index < expected_length);
+      TEST_COMPARE_BLOB (expected[index], strlen (expected[index]),
+                         split.segment, split.length);
+      ++index;
+    }
+  TEST_COMPARE (index, expected_length);
+
+  /* Reuse the test cases with masking that does not actually remove
+     anything.  */
+  check_split_masked (input, -1, NULL, expected, expected_length);
+  check_split_masked (input, -1, input, expected, expected_length);
+}
+
+static int
+do_test (void)
+{
+  /* Splitting tests, without masking.  */
+  check_split (NULL, NULL, 0);
+  check_split ("", NULL, 0);
+  check_split (":", NULL, 0);
+  check_split ("::", NULL, 0);
+
+  {
+    const char *expected[] = { "first" };
+    check_split ("first", expected, array_length (expected));
+    check_split (":first", expected, array_length (expected));
+    check_split ("first:", expected, array_length (expected));
+    check_split (":first:", expected, array_length (expected));
+  }
+
+  {
+    const char *expected[] = { "first", "second" };
+    check_split ("first:second", expected, array_length (expected));
+    check_split ("first::second", expected, array_length (expected));
+    check_split (":first:second", expected, array_length (expected));
+    check_split ("first:second:", expected, array_length (expected));
+    check_split (":first:second:", expected, array_length (expected));
+  }
+
+  /* Splitting tests with masking.  */
+  {
+    const char *expected[] = { "first" };
+    check_split_masked ("first", 3, "first:second",
+                        expected, array_length (expected));
+    check_split_masked ("first:second", 3, "first:",
+                        expected, array_length (expected));
+    check_split_masked ("first:second", 1, NULL,
+                        expected, array_length (expected));
+  }
+  {
+    const char *expected[] = { "second" };
+    check_split_masked ("first:second", 3, "second",
+                        expected, array_length (expected));
+    check_split_masked ("first:second:third", -1, "second:",
+                        expected, array_length (expected));
+    check_split_masked ("first:second", 2, NULL,
+                        expected, array_length (expected));
+    check_split_masked ("first:second:third", 2, "first:second",
+                        expected, array_length (expected));
+  }
+
+  /* Tests for _dl_hwcaps_contains.  */
+  TEST_VERIFY (_dl_hwcaps_contains (NULL, "first", strlen ("first")));
+  TEST_VERIFY (_dl_hwcaps_contains (NULL, "", 0));
+  TEST_VERIFY (! _dl_hwcaps_contains ("", "first", strlen ("first")));
+  TEST_VERIFY (! _dl_hwcaps_contains ("firs", "first", strlen ("first")));
+  TEST_VERIFY (_dl_hwcaps_contains ("firs", "first", strlen ("first") - 1));
+  for (int i = 0; i < strlen ("first"); ++i)
+    TEST_VERIFY (! _dl_hwcaps_contains ("first", "first", i));
+  TEST_VERIFY (_dl_hwcaps_contains ("first", "first", strlen ("first")));
+  TEST_VERIFY (_dl_hwcaps_contains ("first:", "first", strlen ("first")));
+  TEST_VERIFY (_dl_hwcaps_contains ("first:second",
+                                    "first", strlen ("first")));
+  TEST_VERIFY (_dl_hwcaps_contains (":first:second", "first",
+                                    strlen ("first")));
+  TEST_VERIFY (_dl_hwcaps_contains ("first:second", "second",
+                                    strlen ("second")));
+  TEST_VERIFY (_dl_hwcaps_contains ("first:second:", "second",
+                                    strlen ("second")));
+  for (int i = 0; i < strlen ("second"); ++i)
+    TEST_VERIFY (!_dl_hwcaps_contains ("first:second:", "sec", i));
+
+  return 0;
+}
+
+#include <support/test-driver.c>
+
+/* Rebuild the sources here because the object file is built for
+   inclusion into the dynamic loader.  */
+#include "dl-hwcaps_split.c"
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 2de060848b..7e14650422 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1035,8 +1035,13 @@ extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
      attribute_hidden;
 
 /* Initialize the basic data structure for the search paths.  SOURCE
-   is either "LD_LIBRARY_PATH" or "--library-path".  */
-extern void _dl_init_paths (const char *library_path, const char *source)
+   is either "LD_LIBRARY_PATH" or "--library-path".
+   GLIBC_HWCAPS_PREPEND adds additional glibc-hwcaps subdirectories to
+   search.  GLIBC_HWCAPS_MASK is used to filter the built-in
+   subdirectories if not NULL.  */
+extern void _dl_init_paths (const char *library_path, const char *source,
+    const char *glibc_hwcaps_prepend,
+    const char *glibc_hwcaps_mask)
   attribute_hidden;
 
 /* Gather the information needed to install the profiling tables and start
@@ -1060,9 +1065,14 @@ extern void _dl_show_auxv (void) attribute_hidden;
 extern char *_dl_next_ld_env_entry (char ***position) attribute_hidden;
 
 /* Return an array with the names of the important hardware
-   capabilities.  The length of the array is written to *SZ, and the
-   maximum of all strings length is written to *MAX_CAPSTRLEN.  */
-const struct r_strlenpair *_dl_important_hwcaps (size_t *sz,
+   capabilities.  PREPEND is a colon-separated list of glibc-hwcaps
+   directories to search first.  MASK is a colon-separated list used
+   to filter the built-in glibc-hwcaps subdirectories.  The length of
+   the array is written to *SZ, and the maximum of all strings length
+   is written to *MAX_CAPSTRLEN.  */
+const struct r_strlenpair *_dl_important_hwcaps (const char *prepend,
+ const char *mask,
+ size_t *sz,
  size_t *max_capstrlen)
   attribute_hidden;
 
--
2.25.4


Reply | Threaded
Open this post in threaded view
|

[PATCH 19/30] x86_64: Add glibc-hwcaps support

Sourceware - libc-alpha mailing list
In reply to this post by Sourceware - libc-alpha mailing list
Details of the supported CPU flags and the names are still subject to
changes.
---
 sysdeps/x86_64/dl-hwcaps-subdirs.c | 73 ++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)
 create mode 100644 sysdeps/x86_64/dl-hwcaps-subdirs.c

diff --git a/sysdeps/x86_64/dl-hwcaps-subdirs.c b/sysdeps/x86_64/dl-hwcaps-subdirs.c
new file mode 100644
index 0000000000..4a8fae976e
--- /dev/null
+++ b/sysdeps/x86_64/dl-hwcaps-subdirs.c
@@ -0,0 +1,73 @@
+/* Architecture-specific glibc-hwcaps subdirectories.  x86 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dl-hwcaps.h>
+#include <cpu-features.h>
+
+const char _dl_hwcaps_subdirs[] = "x86-103:x86-102:x86-101:x86-100";
+
+int32_t
+_dl_hwcaps_subdirs_active (void)
+{
+  const struct cpu_features *cpu_features = __get_cpu_features ();
+  int32_t result = 0;
+  int32_t bit = 1 << 3;
+
+  /* Test in reverse preference order.  */
+
+  /* x86-100.  */
+  if (!(CPU_FEATURES_ARCH_P (cpu_features, CMPXCHG16B_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, POPCNT_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, SSE3_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, SSE4_1_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, SSE4_2_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, SSSE3_Usable)))
+    return result;
+  result |= bit;
+  bit >>= 1;
+
+  /* x86-101.  */
+  if (!(CPU_FEATURES_ARCH_P (cpu_features, AVX_Usable)))
+    return result;
+  result |= bit;
+  bit >>= 1;
+
+  /* x86-102.  */
+  if (!(CPU_FEATURES_ARCH_P (cpu_features, AVX2_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, BMI1_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, BMI2_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, F16C_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, FMA_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, LZCNT_Usable)
+        && CPU_FEATURES_ARCH_P (cpu_features, MOVBE_Usable)))
+    return result;
+  result |= bit;
+  bit >>= 1;
+
+ /* x86-103.  */
+  if (!(CPU_FEATURES_ARCH_P (cpu_features, AVX512F_Usable)
+        && CPU_FEATURES_CPU_P (cpu_features, AVX512BW)
+        && CPU_FEATURES_CPU_P (cpu_features, AVX512CD)
+        && CPU_FEATURES_CPU_P (cpu_features, AVX512DQ)
+        && CPU_FEATURES_CPU_P (cpu_features, AVX512VL)))
+    return result;
+  result |= bit;
+  bit >>= 1;
+
+  return result;
+}
--
2.25.4


12