unofficial mirror of libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 00/28] glibc-hwcaps support
@ 2020-10-01 16:31 Florian Weimer via Libc-alpha
  2020-10-01 16:31 ` [PATCH 01/28] elf: Do not search HWCAP subdirectories in statically linked binaries Florian Weimer via Libc-alpha
                   ` (28 more replies)
  0 siblings, 29 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:31 UTC (permalink / raw)
  To: libc-alpha

This is a rebase of the previous patch series.  I adjusted it for the
--argv0 ld.so change, added POWER10 support, and switched to the real
subdirectory names in the x86-64 psABI.

I think the patches up to “elf: Enhance ld.so --help to print HWCAP
subdirectories” are useful on their own because they add diagnostics for
existing functionality.  The ld.so.cache extension mechanism and the
string table are also fairly independent parts.

What's still missing?

* documentation (including NEWS file updates)
* negative caching of missing glibc-hwcaps subdirectories (in bulk)
* tests

I would like to work on these aspects in parallel to the patch reviews.
(Lack of negative caching makes it obvious what's going on with strace,
so it's perhaps even helpful that it's currently missing.)

Support in GCC has landed earlier this week:

  <https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=324bec558e95584e>

I filed a separate LLVM bug, but do not plan to work on it for now:

  <https://bugs.llvm.org/show_bug.cgi?id=47686>

Thanks,
Florian

Florian Weimer (28):
  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-usage.c                                | 268 ++++++++++
 elf/dl-write.c                                |  56 ++
 elf/ldconfig.c                                | 187 +++++--
 elf/rtld.c                                    | 251 ++++-----
 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                    |  18 +-
 sysdeps/generic/ldsodefs.h                    |  34 +-
 .../powerpc/powerpc64/le/dl-hwcaps-subdirs.c  |  34 ++
 sysdeps/s390/s390-64/dl-hwcaps-subdirs.c      |  54 ++
 sysdeps/unix/sysv/linux/dl-write.c            |  30 ++
 sysdeps/x86_64/dl-hwcaps-subdirs.c            |  69 +++
 32 files changed, 2977 insertions(+), 424 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

-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH 01/28] elf: Do not search HWCAP subdirectories in statically linked binaries
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
@ 2020-10-01 16:31 ` Florian Weimer via Libc-alpha
  2020-10-01 18:22   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:31 ` [PATCH 02/28] elf: Implement __rtld_malloc_is_full Florian Weimer via Libc-alpha
                   ` (27 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:31 UTC (permalink / raw)
  To: libc-alpha

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 ce05d05b16..902fa3a7f8 100644
--- a/NEWS
+++ b/NEWS
@@ -33,6 +33,10 @@ Deprecated and removed features, and other changes affecting compatibility:
 * The mallinfo function is marked deprecated.  Callers should call
   mallinfo2 instead.
 
+* 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:
 
   [Add changes to build and runtime requirements here]
diff --git a/elf/Makefile b/elf/Makefile
index c587e9f06e..e0a8bf2998 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 646c5dca40..5ba117d597 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 **)
@@ -1521,11 +1527,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;
@@ -1886,11 +1896,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))
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 02/28] elf: Implement __rtld_malloc_is_full
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
  2020-10-01 16:31 ` [PATCH 01/28] elf: Do not search HWCAP subdirectories in statically linked binaries Florian Weimer via Libc-alpha
@ 2020-10-01 16:31 ` Florian Weimer via Libc-alpha
  2020-10-01 18:23   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:31 ` [PATCH 03/28] elf: Implement _dl_write Florian Weimer via Libc-alpha
                   ` (26 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:31 UTC (permalink / raw)
  To: libc-alpha

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.  */
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 03/28] elf: Implement _dl_write
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
  2020-10-01 16:31 ` [PATCH 01/28] elf: Do not search HWCAP subdirectories in statically linked binaries Florian Weimer via Libc-alpha
  2020-10-01 16:31 ` [PATCH 02/28] elf: Implement __rtld_malloc_is_full Florian Weimer via Libc-alpha
@ 2020-10-01 16:31 ` Florian Weimer via Libc-alpha
  2020-10-05 19:46   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:31 ` [PATCH 04/28] elf: Extract command-line/environment variables state from rtld.c Florian Weimer via Libc-alpha
                   ` (25 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:31 UTC (permalink / raw)
  To: libc-alpha

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 e0a8bf2998..ab792d45c2 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 ba114ab4b1..7cb1fccc80 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -757,6 +757,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;
+}
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 04/28] elf: Extract command-line/environment variables state from rtld.c
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (2 preceding siblings ...)
  2020-10-01 16:31 ` [PATCH 03/28] elf: Implement _dl_write Florian Weimer via Libc-alpha
@ 2020-10-01 16:31 ` Florian Weimer via Libc-alpha
  2020-10-06 20:45   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:32 ` [PATCH 05/28] elf: Move ld.so error/help output to _dl_usage Florian Weimer via Libc-alpha
                   ` (24 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:31 UTC (permalink / raw)
  To: libc-alpha

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    | 155 +++++++++++++++++++-------------------------------
 2 files changed, 154 insertions(+), 96 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 9918fda05e..c1153cb627 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;
@@ -316,6 +281,18 @@ audit_list_count (struct audit_list *list)
   return naudit;
 }
 
+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
@@ -900,15 +877,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.
@@ -1150,7 +1118,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;
@@ -1160,8 +1127,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;
 
@@ -1176,7 +1143,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.  */
@@ -1210,7 +1177,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;
@@ -1219,7 +1186,7 @@ dl_main (const ElfW(Phdr) *phdr,
 	  }
 	else if (! strcmp (_dl_argv[1], "--verify"))
 	  {
-	    mode = verify;
+	    state.mode = verify;
 
 	    ++_dl_skip_args;
 	    --_dl_argc;
@@ -1235,7 +1202,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;
@@ -1252,7 +1219,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;
@@ -1260,7 +1227,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;
@@ -1328,7 +1295,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;
@@ -1357,7 +1324,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])
@@ -1604,7 +1571,7 @@ of this helper program; chances are you did not intend to run this program.\n\
       _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
@@ -1634,7 +1601,7 @@ of this helper program; chances are you did not intend to run this program.\n\
 
   /* 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,
@@ -1699,14 +1666,14 @@ of this helper program; chances are you did not intend to run this program.\n\
     /* 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)
     {
-      size_t naudit = audit_list_count (&audit_list);
+      size_t naudit = audit_list_count (&state.audit_list);
 
       /* Since we start using the auditing DSOs right away we need to
 	 initialize the data structures now.  */
@@ -1719,7 +1686,7 @@ of this helper program; chances are you did not intend to run this program.\n\
       security_init ();
       need_security_init = false;
 
-      load_audit_modules (main_map, &audit_list);
+      load_audit_modules (main_map, &state.audit_list);
 
       /* The count based on audit strings may overestimate the number
 	 of audit modules that got loaded, but not underestimate.  */
@@ -1774,19 +1741,21 @@ of this helper program; chances are you did not intend to run this program.\n\
   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);
     }
 
@@ -1893,7 +1862,8 @@ of this helper program; chances are you did not intend to run this program.\n\
   {
     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);
   }
 
@@ -1920,7 +1890,7 @@ of this helper program; chances are you did not intend to run this program.\n\
       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]
@@ -1953,8 +1923,8 @@ of this helper program; chances are you did not intend to run this program.\n\
      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);
   }
 
@@ -1974,7 +1944,7 @@ of this helper program; chances are you did not intend to run this program.\n\
        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
@@ -2076,7 +2046,7 @@ of this helper program; chances are you did not intend to run this program.\n\
 			  (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;
@@ -2130,7 +2100,7 @@ of this helper program; chances are you did not intend to run this program.\n\
 		}
 	    }
 #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.  */
@@ -2492,13 +2462,10 @@ print_missing_version (int errcode __attribute__ ((unused)),
 		    objname, errstring);
 }
 \f
-/* 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.  */
@@ -2555,7 +2522,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;
 	      }
 
@@ -2609,11 +2576,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.  */
@@ -2645,25 +2611,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;
 	    }
 
@@ -2712,7 +2678,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;
 	    }
 
@@ -2754,7 +2720,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];
@@ -2764,7 +2730,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
@@ -2777,9 +2743,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))
@@ -2808,13 +2771,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);
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 05/28] elf: Move ld.so error/help output to _dl_usage
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (3 preceding siblings ...)
  2020-10-01 16:31 ` [PATCH 04/28] elf: Extract command-line/environment variables state from rtld.c Florian Weimer via Libc-alpha
@ 2020-10-01 16:32 ` Florian Weimer via Libc-alpha
  2020-10-06 21:06   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:32 ` [PATCH 06/28] elf: Record whether paths come from LD_LIBRARY_PATH or --library-path Florian Weimer via Libc-alpha
                   ` (23 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:32 UTC (permalink / raw)
  To: libc-alpha

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 | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++
 elf/rtld.c     | 26 +------------------------
 4 files changed, 65 insertions(+), 26 deletions(-)
 create mode 100644 elf/dl-usage.c

diff --git a/elf/Makefile b/elf/Makefile
index ab792d45c2..71602d04c1 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
@@ -618,6 +618,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' \
@@ -655,6 +661,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..f3d89d22b7
--- /dev/null
+++ b/elf/dl-usage.c
@@ -0,0 +1,51 @@
+/* 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\
+  --argv0 STRING        set argv[0] to STRING before running\n");
+}
diff --git a/elf/rtld.c b/elf/rtld.c
index c1153cb627..f3e1791e2f 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1246,31 +1246,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\
-  --argv0 STRING        set argv[0] to STRING before running\n");
+	_dl_usage ();
 
       ++_dl_skip_args;
       --_dl_argc;
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 06/28] elf: Record whether paths come from LD_LIBRARY_PATH or --library-path
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (4 preceding siblings ...)
  2020-10-01 16:32 ` [PATCH 05/28] elf: Move ld.so error/help output to _dl_usage Florian Weimer via Libc-alpha
@ 2020-10-01 16:32 ` Florian Weimer via Libc-alpha
  2020-10-07 16:39   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:32 ` [PATCH 07/28] elf: Implement ld.so --help Florian Weimer via Libc-alpha
                   ` (22 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:32 UTC (permalink / raw)
  To: libc-alpha

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 5ba117d597..5fbb8c9ad4 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 f3e1791e2f..d11fe22b83 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -286,6 +286,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;
@@ -1203,6 +1204,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;
@@ -2655,6 +2657,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 7cb1fccc80..510a2f6841 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1046,8 +1046,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.  */
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 07/28] elf: Implement ld.so --help
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (5 preceding siblings ...)
  2020-10-01 16:32 ` [PATCH 06/28] elf: Record whether paths come from LD_LIBRARY_PATH or --library-path Florian Weimer via Libc-alpha
@ 2020-10-01 16:32 ` Florian Weimer via Libc-alpha
  2020-10-07 17:16   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:32 ` [PATCH 08/28] elf: Implement ld.so --version Florian Weimer via Libc-alpha
                   ` (21 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:32 UTC (permalink / raw)
  To: libc-alpha

--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 f3d89d22b7..72ff99e70f 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\
@@ -47,5 +59,9 @@ of this helper program; chances are you did not intend to run this program.\n\
                         in LIST\n\
   --audit LIST          use objects named in LIST as auditors\n\
   --preload LIST        preload objects named in LIST\n\
-  --argv0 STRING        set argv[0] to STRING before running\n");
+  --argv0 STRING        set argv[0] to STRING before running\n\
+  --help                display this help and exit\n\
+",
+              argv0);
+  _exit (0);
 }
diff --git a/elf/rtld.c b/elf/rtld.c
index d11fe22b83..5cdab3c99c 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1151,6 +1151,7 @@ dl_main (const ElfW(Phdr) *phdr,
   _dl_starting_up = 1;
 #endif
 
+  const char *ld_so_name = _dl_argv[0];
   if (*user_entry == (ElfW(Addr)) ENTRY_POINT)
     {
       /* Ho ho.  We are not the program interpreter!  We are the program
@@ -1178,8 +1179,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;
@@ -1187,7 +1192,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;
@@ -1242,13 +1248,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 (ld_so_name, _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 (ld_so_name, &state);
+	  else
+	    _dl_usage (ld_so_name, NULL);
+	}
 
       ++_dl_skip_args;
       --_dl_argc;
@@ -1273,7 +1300,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;
@@ -1286,9 +1313,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 (ld_so_name, &state);
+	      else
+		_exit (EXIT_FAILURE);
+	    }
 	}
       else
 	{
@@ -1647,6 +1681,11 @@ dl_main (const ElfW(Phdr) *phdr,
   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 (ld_so_name, &state);
+
   /* If we have auditing DSOs to load, do it now.  */
   bool need_security_init = true;
   if (state.audit_list.length > 0)
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 08/28] elf: Implement ld.so --version
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (6 preceding siblings ...)
  2020-10-01 16:32 ` [PATCH 07/28] elf: Implement ld.so --help Florian Weimer via Libc-alpha
@ 2020-10-01 16:32 ` Florian Weimer via Libc-alpha
  2020-10-07 18:36   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:32 ` [PATCH 09/28] scripts/update-copyrights: Update csu/version.c, elf/dl-usage.c Florian Weimer via Libc-alpha
                   ` (20 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:32 UTC (permalink / raw)
  To: libc-alpha

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 72ff99e70f..7355b094a5 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)
 {
@@ -61,6 +75,7 @@ of this helper program; chances are you did not intend to run this program.\n\
   --preload LIST        preload objects named in LIST\n\
   --argv0 STRING        set argv[0] to STRING before running\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 5cdab3c99c..e0e8e98c2f 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1254,6 +1254,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')
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 09/28] scripts/update-copyrights: Update csu/version.c, elf/dl-usage.c
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (7 preceding siblings ...)
  2020-10-01 16:32 ` [PATCH 08/28] elf: Implement ld.so --version Florian Weimer via Libc-alpha
@ 2020-10-01 16:32 ` Florian Weimer via Libc-alpha
  2020-10-07 18:41   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:32 ` [PATCH 10/28] elf: Use the term "program interpreter" in the ld.so help message Florian Weimer via Libc-alpha
                   ` (19 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:32 UTC (permalink / raw)
  To: libc-alpha

---
 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"
       ;;
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 10/28] elf: Use the term "program interpreter" in the ld.so help message
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (8 preceding siblings ...)
  2020-10-01 16:32 ` [PATCH 09/28] scripts/update-copyrights: Update csu/version.c, elf/dl-usage.c Florian Weimer via Libc-alpha
@ 2020-10-01 16:32 ` Florian Weimer via Libc-alpha
  2020-10-07 21:08   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:32 ` [PATCH 11/28] elf: Print the full name of the dynamic loader " Florian Weimer via Libc-alpha
                   ` (18 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:32 UTC (permalink / raw)
  To: libc-alpha

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 7355b094a5..35a1c0c455 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\
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 11/28] elf: Print the full name of the dynamic loader in the ld.so help message
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (9 preceding siblings ...)
  2020-10-01 16:32 ` [PATCH 10/28] elf: Use the term "program interpreter" in the ld.so help message Florian Weimer via Libc-alpha
@ 2020-10-01 16:32 ` Florian Weimer via Libc-alpha
  2020-10-08 12:38   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:32 ` [PATCH 12/28] elf: Make __rtld_env_path_list and __rtld_search_dirs global variables Florian Weimer via Libc-alpha
                   ` (17 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:32 UTC (permalink / raw)
  To: libc-alpha

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 71602d04c1..7b97e773a5 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -661,7 +661,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 35a1c0c455..0a62e8f7cf 100644
--- a/elf/dl-usage.c
+++ b/elf/dl-usage.c
@@ -76,6 +76,8 @@ setting environment variables (which would be inherted by subprocesses).\n\
   --argv0 STRING        set argv[0] to STRING before running\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);
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 12/28] elf: Make __rtld_env_path_list and __rtld_search_dirs global variables
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (10 preceding siblings ...)
  2020-10-01 16:32 ` [PATCH 11/28] elf: Print the full name of the dynamic loader " Florian Weimer via Libc-alpha
@ 2020-10-01 16:32 ` Florian Weimer via Libc-alpha
  2020-10-08 13:27   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:32 ` [PATCH 13/28] elf: Add library search path information to ld.so --help Florian Weimer via Libc-alpha
                   ` (16 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:32 UTC (permalink / raw)
  To: libc-alpha

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 5fbb8c9ad4..0c8fa72c4d 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;
 }
 
 
@@ -1996,9 +1997,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;
     }
 
@@ -2146,8 +2147,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);
@@ -2236,8 +2237,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.  */
@@ -2405,7 +2406,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"))
@@ -2417,7 +2418,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.
 
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 13/28] elf: Add library search path information to ld.so --help
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (11 preceding siblings ...)
  2020-10-01 16:32 ` [PATCH 12/28] elf: Make __rtld_env_path_list and __rtld_search_dirs global variables Florian Weimer via Libc-alpha
@ 2020-10-01 16:32 ` Florian Weimer via Libc-alpha
  2020-10-08 16:22   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:33 ` [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories Florian Weimer via Libc-alpha
                   ` (15 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:32 UTC (permalink / raw)
  To: libc-alpha

---
 elf/dl-usage.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index 0a62e8f7cf..20aa715cb1 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)
 {
@@ -80,5 +135,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);
 }
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (12 preceding siblings ...)
  2020-10-01 16:32 ` [PATCH 13/28] elf: Add library search path information to ld.so --help Florian Weimer via Libc-alpha
@ 2020-10-01 16:33 ` Florian Weimer via Libc-alpha
  2020-10-08 16:27   ` Adhemerval Zanella via Libc-alpha
  2020-10-09 13:49   ` Matheus Castanho via Libc-alpha
  2020-10-01 16:33 ` [PATCH 15/28] elf: Do not pass GLRO(dl_platform), GLRO(dl_platformlen) to _dl_important_hwcaps Florian Weimer via Libc-alpha
                   ` (14 subsequent siblings)
  28 siblings, 2 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:33 UTC (permalink / raw)
  To: libc-alpha

---
 elf/dl-usage.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index 20aa715cb1..9765d1b5c1 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)
 {
@@ -136,5 +197,6 @@ This program interpreter self-identifies as: " RTLD "\n\
 ",
               argv0);
   print_search_path_for_help (state);
+  print_legacy_hwcap_directories ();
   _exit (0);
 }
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 15/28] elf: Do not pass GLRO(dl_platform), GLRO(dl_platformlen) to _dl_important_hwcaps
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (13 preceding siblings ...)
  2020-10-01 16:33 ` [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories Florian Weimer via Libc-alpha
@ 2020-10-01 16:33 ` Florian Weimer via Libc-alpha
  2020-10-08 18:04   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:33 ` [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH Florian Weimer via Libc-alpha
                   ` (13 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:33 UTC (permalink / raw)
  To: libc-alpha

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 0c8fa72c4d..f3201e7c14 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 510a2f6841..382eeb9be0 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1071,12 +1071,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.  */
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (14 preceding siblings ...)
  2020-10-01 16:33 ` [PATCH 15/28] elf: Do not pass GLRO(dl_platform), GLRO(dl_platformlen) to _dl_important_hwcaps Florian Weimer via Libc-alpha
@ 2020-10-01 16:33 ` Florian Weimer via Libc-alpha
  2020-10-08 10:13   ` Szabolcs Nagy via Libc-alpha
                     ` (2 more replies)
  2020-10-01 16:33 ` [PATCH 17/28] x86_64: Add glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (12 subsequent siblings)
  28 siblings, 3 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:33 UTC (permalink / raw)
  To: libc-alpha

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 7b97e773a5..2c36b08c73 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
@@ -217,7 +218,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-tls-surplus
+	 tst-create_format1 tst-tls-surplus 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 f3201e7c14..9020f1646f 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 9765d1b5c1..9a3cbb8b91 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\
@@ -197,6 +262,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 e0e8e98c2f..c9d2330364 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -289,6 +289,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;
@@ -1244,6 +1246,22 @@ dl_main (const ElfW(Phdr) *phdr,
 	  {
 	    argv0 = _dl_argv[2];
 
+	    _dl_skip_args += 2;
+	    _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;
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 382eeb9be0..0b2babc70c 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1047,8 +1047,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
@@ -1072,9 +1077,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;
 
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 17/28] x86_64: Add glibc-hwcaps support
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (15 preceding siblings ...)
  2020-10-01 16:33 ` [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH Florian Weimer via Libc-alpha
@ 2020-10-01 16:33 ` Florian Weimer via Libc-alpha
  2020-10-01 16:33 ` [PATCH 18/28] powerpc64le: " Florian Weimer via Libc-alpha
                   ` (11 subsequent siblings)
  28 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:33 UTC (permalink / raw)
  To: libc-alpha

The subdirectories match those in the x86-64 psABI:

https://gitlab.com/x86-psABIs/x86-64-ABI/-/commit/77566eb03bc6a326811cb7e9a6b9396884b67c7c
---
 sysdeps/x86_64/dl-hwcaps-subdirs.c | 69 ++++++++++++++++++++++++++++++
 1 file changed, 69 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..0a732a026b
--- /dev/null
+++ b/sysdeps/x86_64/dl-hwcaps-subdirs.c
@@ -0,0 +1,69 @@
+/* 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-64-v4:x86-64-v3:x86-64-v2";
+
+int32_t
+_dl_hwcaps_subdirs_active (void)
+{
+  int32_t result = 0;
+  int32_t bit = 1 << 2;
+
+  /* Test in reverse preference order.  */
+
+  /* x86-64-v2.  */
+  if (!(CPU_FEATURE_USABLE (CMPXCHG16B)
+        && CPU_FEATURE_USABLE (LAHF64_SAHF64)
+        && CPU_FEATURE_USABLE (POPCNT)
+        && CPU_FEATURE_USABLE (SSE3)
+        && CPU_FEATURE_USABLE (SSE4_1)
+        && CPU_FEATURE_USABLE (SSE4_2)
+        && CPU_FEATURE_USABLE (SSSE3)))
+    return result;
+  result |= bit;
+  bit >>= 1;
+
+  /* x86-64-v3.  */
+  if (!(CPU_FEATURE_USABLE (AVX)
+        && CPU_FEATURE_USABLE (AVX2)
+        && CPU_FEATURE_USABLE (BMI1)
+        && CPU_FEATURE_USABLE (BMI2)
+        && CPU_FEATURE_USABLE (F16C)
+        && CPU_FEATURE_USABLE (FMA)
+        && CPU_FEATURE_USABLE (LZCNT)
+        && CPU_FEATURE_USABLE (MOVBE)
+        && CPU_FEATURE_USABLE (OSXSAVE)))
+    return result;
+  result |= bit;
+  bit >>= 1;
+
+ /* x86-64-v4.  */
+  if (!(CPU_FEATURE_USABLE (AVX512F)
+        && CPU_FEATURE_USABLE (AVX512BW)
+        && CPU_FEATURE_USABLE (AVX512CD)
+        && CPU_FEATURE_USABLE (AVX512DQ)
+        && CPU_FEATURE_USABLE (AVX512VL)))
+    return result;
+  result |= bit;
+  bit >>= 1;
+
+  return result;
+}
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 18/28] powerpc64le: Add glibc-hwcaps support
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (16 preceding siblings ...)
  2020-10-01 16:33 ` [PATCH 17/28] x86_64: Add glibc-hwcaps support Florian Weimer via Libc-alpha
@ 2020-10-01 16:33 ` Florian Weimer via Libc-alpha
  2020-10-01 18:56   ` Paul A. Clarke via Libc-alpha
  2020-10-01 16:33 ` [PATCH 19/28] s390x: Add " Florian Weimer via Libc-alpha
                   ` (10 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:33 UTC (permalink / raw)
  To: libc-alpha

The "power10" and "power9" subdirectories are selected.
---
 .../powerpc/powerpc64/le/dl-hwcaps-subdirs.c  | 34 +++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c

diff --git a/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
new file mode 100644
index 0000000000..496daf0fa0
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
@@ -0,0 +1,34 @@
+/* Architecture-specific glibc-hwcaps subdirectories.  powerpc64le 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 <ldsodefs.h>
+
+const char _dl_hwcaps_subdirs[] = "power10:power9";
+
+int32_t
+_dl_hwcaps_subdirs_active (void)
+{
+  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
+    return 3;
+
+  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
+    return 1;
+
+  return 0;
+}
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 19/28] s390x: Add Add glibc-hwcaps support
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (17 preceding siblings ...)
  2020-10-01 16:33 ` [PATCH 18/28] powerpc64le: " Florian Weimer via Libc-alpha
@ 2020-10-01 16:33 ` Florian Weimer via Libc-alpha
  2020-10-01 16:33 ` [PATCH 20/28] aarch64: " Florian Weimer via Libc-alpha
                   ` (9 subsequent siblings)
  28 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:33 UTC (permalink / raw)
  To: libc-alpha

Subdirectories z13, z14, z15 can be selected, mostly based on the
level of support for vector instructions.
---
 sysdeps/s390/s390-64/dl-hwcaps-subdirs.c | 54 ++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 sysdeps/s390/s390-64/dl-hwcaps-subdirs.c

diff --git a/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c b/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c
new file mode 100644
index 0000000000..255af22f02
--- /dev/null
+++ b/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c
@@ -0,0 +1,54 @@
+/* Architecture-specific glibc-hwcaps subdirectories.  s390x 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 <ldsodefs.h>
+
+const char _dl_hwcaps_subdirs[] = "z15:z14:z13";
+
+int32_t
+_dl_hwcaps_subdirs_active (void)
+{
+  int32_t result = 0;
+  int32_t bit = 1 << 2;
+
+  /* Test in reverse preference order.  */
+
+  /* z13.  */
+  if (!(GLRO (dl_hwcap) & HWCAP_S390_VX))
+    return result;
+  result |= bit;
+  bit >>= 1;
+
+  /* z14.  */
+  if (!((GLRO (dl_hwcap) & HWCAP_S390_VXD)
+        && (GLRO (dl_hwcap) & HWCAP_S390_VXE)
+        && (GLRO (dl_hwcap) & HWCAP_S390_GS)))
+    return result;
+  result |= bit;
+  bit >>= 1;
+
+  /* z15.  */
+  if (!((GLRO (dl_hwcap) & HWCAP_S390_VXRS_EXT2)
+        && (GLRO (dl_hwcap) & HWCAP_S390_VXRS_PDE)))
+    return result;
+  result |= bit;
+  bit >>= 1;
+
+  return result;
+}
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 20/28] aarch64: Add glibc-hwcaps support
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (18 preceding siblings ...)
  2020-10-01 16:33 ` [PATCH 19/28] s390x: Add " Florian Weimer via Libc-alpha
@ 2020-10-01 16:33 ` Florian Weimer via Libc-alpha
  2020-10-14 13:46   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:33 ` [PATCH 21/28] elf: Add endianness markup to ld.so.cache Florian Weimer via Libc-alpha
                   ` (8 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:33 UTC (permalink / raw)
  To: libc-alpha

At this point, only the "atomics" subdirectory is available,
for libraries built using LSE atomics.
---
 sysdeps/aarch64/dl-hwcaps-subdirs.c | 31 +++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 sysdeps/aarch64/dl-hwcaps-subdirs.c

diff --git a/sysdeps/aarch64/dl-hwcaps-subdirs.c b/sysdeps/aarch64/dl-hwcaps-subdirs.c
new file mode 100644
index 0000000000..fd6325024e
--- /dev/null
+++ b/sysdeps/aarch64/dl-hwcaps-subdirs.c
@@ -0,0 +1,31 @@
+/* Architecture-specific glibc-hwcaps subdirectories.  aarch64 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 <ldsodefs.h>
+
+const char _dl_hwcaps_subdirs[] = "atomics";
+
+int32_t
+_dl_hwcaps_subdirs_active (void)
+{
+  if (GLRO (dl_hwcap) & HWCAP_ATOMICS)
+    return 1;
+
+  return 0;
+}
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 21/28] elf: Add endianness markup to ld.so.cache
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (19 preceding siblings ...)
  2020-10-01 16:33 ` [PATCH 20/28] aarch64: " Florian Weimer via Libc-alpha
@ 2020-10-01 16:33 ` Florian Weimer via Libc-alpha
  2020-10-14 14:07   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:33 ` [PATCH 22/28] elf: Add extension mechanism " Florian Weimer via Libc-alpha
                   ` (7 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:33 UTC (permalink / raw)
  To: libc-alpha

Use a reserved byte in the new format cache header to indicate whether
the file is in little endian or big endian format.  Eventually, this
information could be used to provide a unified cache for qemu-user
and similiar scenarios.
---
 elf/cache.c                | 11 ++++++++++
 elf/dl-cache.c             | 20 +++++++++++++++++-
 sysdeps/generic/dl-cache.h | 43 +++++++++++++++++++++++++++++++++++++-
 3 files changed, 72 insertions(+), 2 deletions(-)

diff --git a/elf/cache.c b/elf/cache.c
index 1eb1455883..e0aa616352 100644
--- a/elf/cache.c
+++ b/elf/cache.c
@@ -152,6 +152,14 @@ print_entry (const char *lib, int flag, unsigned int osversion,
   printf (") => %s\n", key);
 }
 
+/* Print an error and exit if the new-file cache is internally
+   inconsistent.  */
+static void
+check_new_cache (struct cache_file_new *cache)
+{
+  if (! cache_file_new_matches_endian (cache))
+    error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n"));
+}
 
 /* Print the whole cache file, if a file contains the new cache format
    hidden in the old one, print the contents of the new format.  */
@@ -193,6 +201,7 @@ print_cache (const char *cache_name)
 	  || memcmp (cache_new->version, CACHE_VERSION,
 		      sizeof CACHE_VERSION - 1))
 	error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
+      check_new_cache (cache_new);
       format = 1;
       /* This is where the strings start.  */
       cache_data = (const char *) cache_new;
@@ -222,6 +231,7 @@ print_cache (const char *cache_name)
 	      && memcmp (cache_new->version, CACHE_VERSION,
 			 sizeof CACHE_VERSION - 1) == 0)
 	    {
+	      check_new_cache (cache_new);
 	      cache_data = (const char *) cache_new;
 	      format = 1;
 	    }
@@ -361,6 +371,7 @@ save_cache (const char *cache_name)
 
       file_entries_new->nlibs = cache_entry_count;
       file_entries_new->len_strings = total_strlen;
+      file_entries_new->flags = cache_file_new_flags_endian;
     }
 
   /* Pad for alignment of cache_file_new.  */
diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index 93d185e788..3aa8ed6c13 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -210,6 +210,11 @@ _dl_load_cache_lookup (const char *name)
 	  && ((cachesize - sizeof *cache_new) / sizeof (struct file_entry_new)
 	      >= ((struct cache_file_new *) file)->nlibs))
 	{
+	  if (! cache_file_new_matches_endian (file))
+	    {
+	      __munmap (file, cachesize);
+	      file = (void *) -1;
+	    }
 	  cache_new = file;
 	  cache = file;
 	}
@@ -231,7 +236,20 @@ _dl_load_cache_lookup (const char *name)
 	  if (cachesize < (offset + sizeof (struct cache_file_new))
 	      || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW,
 			 sizeof CACHEMAGIC_VERSION_NEW - 1) != 0)
-	    cache_new = (void *) -1;
+	      cache_new = (void *) -1;
+	  else
+	    {
+	      if (! cache_file_new_matches_endian (cache_new))
+		{
+		  /* The old-format part of the cache is bogus as well
+		     if the endianness does not match.  (But it is
+		     unclear how the new header can be located if the
+		     endianess does not match.)  */
+		  cache = (void *) -1;
+		  cache_new = (void *) -1;
+		  __munmap (file, cachesize);
+		}
+	    }
 	}
       else
 	{
diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
index 6b310e9e15..1b04211f6b 100644
--- a/sysdeps/generic/dl-cache.h
+++ b/sysdeps/generic/dl-cache.h
@@ -16,6 +16,11 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+#ifndef _DL_CACHE_H
+#define _DL_CACHE_H
+
+#include <endian.h>
+#include <stdbool.h>
 #include <stdint.h>
 
 #ifndef _DL_CACHE_DEFAULT_ID
@@ -83,21 +88,57 @@ struct file_entry_new
   uint64_t hwcap;		/* Hwcap entry.	 */
 };
 
+/* See flags member of struct cache_file_new below.  */
+enum
+  {
+   cache_file_new_flags_endian = (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+				  ? 2 : 3)
+  };
+
 struct cache_file_new
 {
   char magic[sizeof CACHEMAGIC_NEW - 1];
   char version[sizeof CACHE_VERSION - 1];
   uint32_t nlibs;		/* Number of entries.  */
   uint32_t len_strings;		/* Size of string table. */
-  uint32_t unused[5];		/* Leave space for future extensions
+
+  /* flags & 3 is used to indicate the endianness of the cache.
+     0: no endianness information available
+        (An old ldconfig version without endianness support wrote the file.)
+     1: cache is invalid
+     2: little endian
+     3: big endian
+
+     The remaining bits are unused and should be generated as zero and
+     ignored by readers.  */
+  uint8_t flags;
+
+  uint8_t padding_unsed[3];	/* Not used, for future extensions.  */
+
+  uint32_t unused[4];		/* Leave space for future extensions
 				   and align to 8 byte boundary.  */
   struct file_entry_new libs[0]; /* Entries describing libraries.  */
   /* After this the string table of size len_strings is found.	*/
 };
 
+/* Returns false if *CACHE has the wrong endianness for this
+   architecture, and true if the endianness matches (or is
+   unknown).  */
+static inline bool
+cache_file_new_matches_endian (const struct cache_file_new *cache)
+{
+  /* A zero value for cache->flags means that no endianness
+     information is available.  */
+  return cache->flags == 0
+    || (cache->flags & 3) == cache_file_new_flags_endian;
+}
+
+
 /* Used to align cache_file_new.  */
 #define ALIGN_CACHE(addr)				\
 (((addr) + __alignof__ (struct cache_file_new) -1)	\
  & (~(__alignof__ (struct cache_file_new) - 1)))
 
 extern int _dl_cache_libcmp (const char *p1, const char *p2) attribute_hidden;
+
+#endif /* _DL_CACHE_H */
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 22/28] elf: Add extension mechanism to ld.so.cache
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (20 preceding siblings ...)
  2020-10-01 16:33 ` [PATCH 21/28] elf: Add endianness markup to ld.so.cache Florian Weimer via Libc-alpha
@ 2020-10-01 16:33 ` Florian Weimer via Libc-alpha
  2020-10-15 17:52   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:34 ` [PATCH 23/28] elf: Unify old and new format cache handling code in ld.so Florian Weimer via Libc-alpha
                   ` (6 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:33 UTC (permalink / raw)
  To: libc-alpha

A previously unused new-format header field is used to record
the address of an extension directory.

This change adds a demo extension which records the version of
ldconfig which builds a file.
---
 elf/cache.c                |  89 +++++++++++++++++++++++++++
 sysdeps/generic/dl-cache.h | 123 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 211 insertions(+), 1 deletion(-)

diff --git a/elf/cache.c b/elf/cache.c
index e0aa616352..3a02a4070a 100644
--- a/elf/cache.c
+++ b/elf/cache.c
@@ -15,6 +15,7 @@
    You should have received a copy of the GNU General Public License
    along with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
+#include <assert.h>
 #include <errno.h>
 #include <error.h>
 #include <dirent.h>
@@ -33,6 +34,7 @@
 
 #include <ldconfig.h>
 #include <dl-cache.h>
+#include <version.h>
 
 struct cache_entry
 {
@@ -161,6 +163,21 @@ check_new_cache (struct cache_file_new *cache)
     error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n"));
 }
 
+/* Print the extension information at the cache at start address
+   FILE_BASE, of ltength FILE_SIZE bytes.  The new-format cache header
+   is at CACHE, and the file name for diagnostics is CACHE_NAME.  */
+static void
+print_extensions (struct cache_extension_all_loaded *ext)
+{
+  if (ext->sections[cache_extension_tag_generator].base != NULL)
+    {
+      fputs (_("Cache generated by: "), stdout);
+      fwrite (ext->sections[cache_extension_tag_generator].base, 1,
+	      ext->sections[cache_extension_tag_generator].size, stdout);
+      putchar ('\n');
+    }
+}
+
 /* Print the whole cache file, if a file contains the new cache format
    hidden in the old one, print the contents of the new format.  */
 void
@@ -250,6 +267,11 @@ print_cache (const char *cache_name)
     }
   else if (format == 1)
     {
+      struct cache_extension_all_loaded ext;
+      if (!cache_extension_load (cache_new, cache, cache_size, &ext))
+	error (EXIT_FAILURE, 0,
+	       _("Malformed extension data in cache file %s\n"), cache_name);
+
       printf (_("%d libs found in cache `%s'\n"),
 	      cache_new->nlibs, cache_name);
 
@@ -260,6 +282,7 @@ print_cache (const char *cache_name)
 		     cache_new->libs[i].osversion,
 		     cache_new->libs[i].hwcap,
 		     cache_data + cache_new->libs[i].value);
+      print_extensions (&ext);
     }
   /* Cleanup.  */
   munmap (cache, cache_size);
@@ -301,6 +324,45 @@ compare (const struct cache_entry *e1, const struct cache_entry *e2)
   return res;
 }
 
+/* Size of the cache extension directory.  All tags are assumed to be
+   present.  */
+enum
+  {
+   cache_extension_size = (offsetof (struct cache_extension, sections)
+			   + (cache_extension_count
+			      * sizeof (struct cache_extension_section)))
+  };
+
+/* Write the cache extensions to FD.  The extension directory is
+   assumed to be located at CACHE_EXTENSION_OFFSET.  */
+static void
+write_extensions (int fd, uint32_t cache_extension_offset)
+{
+  assert ((cache_extension_offset % 4) == 0);
+
+  struct cache_extension *ext = xmalloc (cache_extension_size);
+  ext->magic = cache_extension_magic;
+  ext->count = cache_extension_count;
+
+  for (int i = 0; i < cache_extension_count; ++i)
+    {
+      ext->sections[i].tag = i;
+      ext->sections[i].flags = 0;
+    }
+
+  const char *generator
+    = "ldconfig " PKGVERSION RELEASE " release version " VERSION;
+  ext->sections[cache_extension_tag_generator].offset
+    = cache_extension_offset + cache_extension_size;
+  ext->sections[cache_extension_tag_generator].size = strlen (generator);
+
+  if (write (fd, ext, cache_extension_size) != cache_extension_size
+      || write (fd, generator, strlen (generator)) != strlen (generator))
+    error (EXIT_FAILURE, errno, _("Writing of cache extension data failed"));
+
+  free (ext);
+}
+
 /* Save the contents of the cache.  */
 void
 save_cache (const char *cache_name)
@@ -435,6 +497,25 @@ save_cache (const char *cache_name)
       && idx_old < cache_entry_old_count)
     file_entries->libs[idx_old] = file_entries->libs[idx_old - 1];
 
+  /* Compute the location of the extension directory.  This
+     implementation puts the directory after the string table.  The
+     size computation matches the write calls below.  The extension
+     directory does not exist with format 0, so the value does not
+     matter.  */
+  uint32_t extension_offset = 0;
+  if (opt_format != 2)
+    extension_offset += file_entries_size;
+  if (opt_format != 0)
+    {
+      if (opt_format != 2)
+	extension_offset += pad;
+      extension_offset += file_entries_new_size;
+    }
+  extension_offset += total_strlen;
+  extension_offset = roundup (extension_offset, 4); /* Provide alignment.  */
+  if (opt_format != 0)
+    file_entries_new->extension_offset = extension_offset;
+
   /* Write out the cache.  */
 
   /* Write cache first to a temporary file and rename it later.  */
@@ -473,6 +554,14 @@ save_cache (const char *cache_name)
   if (write (fd, strings, total_strlen) != (ssize_t) total_strlen)
     error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
 
+  if (opt_format != 0)
+    {
+      /* Align file position to 4.  */
+      off64_t old_offset = lseek64 (fd, extension_offset, SEEK_SET);
+      assert ((unsigned long long int) (extension_offset - old_offset) < 4);
+      write_extensions (fd, extension_offset);
+    }
+
   /* Make sure user can always read cache file */
   if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR))
     error (EXIT_FAILURE, errno,
diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
index 1b04211f6b..b154740da9 100644
--- a/sysdeps/generic/dl-cache.h
+++ b/sysdeps/generic/dl-cache.h
@@ -21,7 +21,9 @@
 
 #include <endian.h>
 #include <stdbool.h>
+#include <stddef.h>
 #include <stdint.h>
+#include <string.h>
 
 #ifndef _DL_CACHE_DEFAULT_ID
 # define _DL_CACHE_DEFAULT_ID	3
@@ -115,7 +117,11 @@ struct cache_file_new
 
   uint8_t padding_unsed[3];	/* Not used, for future extensions.  */
 
-  uint32_t unused[4];		/* Leave space for future extensions
+  /* File offset of the extension directory.  See struct
+     cache_extension below.  Must be a multiple of four.  */
+  uint32_t extension_offset;
+
+  uint32_t unused[3];		/* Leave space for future extensions
 				   and align to 8 byte boundary.  */
   struct file_entry_new libs[0]; /* Entries describing libraries.  */
   /* After this the string table of size len_strings is found.	*/
@@ -134,6 +140,121 @@ cache_file_new_matches_endian (const struct cache_file_new *cache)
 }
 
 
+/* Randomly chosen magic value, which allows for additional
+   consistency verification.  */
+enum { cache_extension_magic = (uint32_t) -358342284 };
+
+/* Tag values for different kinds of extension sections.  Similar to
+   SHT_* constants.  */
+enum cache_extension_tag
+  {
+   /* Array of bytes containing the glibc version that generated this
+      cache file.  */
+   cache_extension_tag_generator,
+
+   /* Total number of known cache extension tags.  */
+   cache_extension_count
+  };
+
+/* Element in the array following struct cache_extension.  Similar to
+   an ELF section header.  */
+struct cache_extension_section
+{
+  /* Type of the extension section.  A enum cache_extension_tag value.  */
+  uint32_t tag;
+
+  /* Extension-specific flags.  Currently generated as zero.  */
+  uint32_t flags;
+
+  /* Offset from the start of the file for the data in this extension
+     section.  Specific extensions can have alignment constraints.  */
+  uint32_t offset;
+
+  /* Length in bytes of the extension data.  Specific extensions may
+     have size requirements.  */
+  uint32_t size;
+};
+
+/* The extension directory in the cache.  An array of struct
+   cache_extension_section entries.  */
+struct cache_extension
+{
+  uint32_t magic;		/* Always cache_extension_magic.  */
+  uint32_t count;		/* Number of following entries.  */
+
+  /* count section descriptors of type struct cache_extension_section
+     follow.  */
+  struct cache_extension_section sections[];
+};
+
+/* A relocated version of struct cache_extension_section.  */
+struct cache_extension_loaded
+{
+  /* Address and size of this extension section.  base is NULL if the
+     section is missing from the file.  */
+  const void *base;
+  size_t size;
+
+  /* Flags from struct cache_extension_section.  */
+  uint32_t flags;
+};
+
+/* All supported extension sections, relocated.  Filled in by
+   cache_extension_load below.  */
+struct cache_extension_all_loaded
+{
+  struct cache_extension_loaded sections[cache_extension_count];
+};
+
+static bool __attribute__ ((unused))
+cache_extension_load (const struct cache_file_new *cache,
+		      const void *file_base, size_t file_size,
+		      struct cache_extension_all_loaded *loaded)
+{
+  memset (loaded, 0, sizeof (*loaded));
+  if (cache->extension_offset == 0)
+    /* No extensions present.  This is not a format error.  */
+    return true;
+  if ((cache->extension_offset % 4) != 0)
+    /* Extension offset is misaligned.  */
+    return false;
+  size_t size_tmp;
+  if (__builtin_add_overflow (cache->extension_offset,
+			      sizeof (struct cache_extension), &size_tmp)
+      || size_tmp > file_size)
+    /* Extension extends beyond the end of the file.  */
+    return false;
+  const struct cache_extension *ext = file_base + cache->extension_offset;
+  if (ext->magic != cache_extension_magic)
+    return false;
+  if (__builtin_mul_overflow (ext->count,
+			      sizeof (struct cache_extension_section),
+			      &size_tmp)
+      || __builtin_add_overflow (cache->extension_offset
+				 + sizeof (struct cache_extension), size_tmp,
+				 &size_tmp)
+      || size_tmp > file_size)
+    /* Extension array extends beyond the end of the file.  */
+    return false;
+  for (uint32_t i = 0; i < ext->count; ++i)
+    {
+      if (__builtin_add_overflow (ext->sections[i].offset,
+				  ext->sections[i].size, &size_tmp)
+	  || size_tmp > file_size)
+	/* Extension data extends beyond the end of the file.  */
+	return false;
+
+      uint32_t tag = ext->sections[i].tag;
+      if (tag >= cache_extension_count)
+	/* Tag is out of range and unrecognized.  */
+	continue;
+      loaded->sections[tag].base = file_base + ext->sections[i].offset;
+      loaded->sections[tag].size = ext->sections[i].size;
+      loaded->sections[tag].flags = ext->sections[i].flags;
+    }
+  return true;
+}
+
 /* Used to align cache_file_new.  */
 #define ALIGN_CACHE(addr)				\
 (((addr) + __alignof__ (struct cache_file_new) -1)	\
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 23/28] elf: Unify old and new format cache handling code in ld.so
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (21 preceding siblings ...)
  2020-10-01 16:33 ` [PATCH 22/28] elf: Add extension mechanism " Florian Weimer via Libc-alpha
@ 2020-10-01 16:34 ` Florian Weimer via Libc-alpha
  2020-10-16 14:37   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:34 ` [PATCH 24/28] elf: Implement a string table for ldconfig, with tail merging Florian Weimer via Libc-alpha
                   ` (5 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:34 UTC (permalink / raw)
  To: libc-alpha

struct file_entry_new starts with the fields of struct file_entry,
so the code can be shared if the size computation is made dynamic.
---
 elf/dl-cache.c             | 287 +++++++++++++++++++------------------
 sysdeps/generic/dl-cache.h |  17 ++-
 2 files changed, 158 insertions(+), 146 deletions(-)

diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index 3aa8ed6c13..02c46ffb0c 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -35,103 +35,141 @@ static struct cache_file *cache;
 static struct cache_file_new *cache_new;
 static size_t cachesize;
 
-/* 1 if cache_data + PTR points into the cache.  */
-#define _dl_cache_verify_ptr(ptr) (ptr < cache_data_size)
-
-#define SEARCH_CACHE(cache) \
-/* We use binary search since the table is sorted in the cache file.	      \
-   The first matching entry in the table is returned.			      \
-   It is important to use the same algorithm as used while generating	      \
-   the cache file.  */							      \
-do									      \
-  {									      \
-    left = 0;								      \
-    right = cache->nlibs - 1;						      \
-									      \
-    while (left <= right)						      \
-      {									      \
-	__typeof__ (cache->libs[0].key) key;				      \
-									      \
-	middle = (left + right) / 2;					      \
-									      \
-	key = cache->libs[middle].key;					      \
-									      \
-	/* Make sure string table indices are not bogus before using	      \
-	   them.  */							      \
-	if (! _dl_cache_verify_ptr (key))				      \
-	  {								      \
-	    cmpres = 1;							      \
-	    break;							      \
-	  }								      \
-									      \
-	/* Actually compare the entry with the key.  */			      \
-	cmpres = _dl_cache_libcmp (name, cache_data + key);		      \
-	if (__glibc_unlikely (cmpres == 0))				      \
-	  {								      \
-	    /* Found it.  LEFT now marks the last entry for which we	      \
-	       know the name is correct.  */				      \
-	    left = middle;						      \
-									      \
-	    /* There might be entries with this name before the one we	      \
-	       found.  So we have to find the beginning.  */		      \
-	    while (middle > 0)						      \
-	      {								      \
-		__typeof__ (cache->libs[0].key) key;			      \
-									      \
-		key = cache->libs[middle - 1].key;			      \
-		/* Make sure string table indices are not bogus before	      \
-		   using them.  */					      \
-		if (! _dl_cache_verify_ptr (key)			      \
-		    /* Actually compare the entry.  */			      \
-		    || _dl_cache_libcmp (name, cache_data + key) != 0)	      \
-		  break;						      \
-		--middle;						      \
-	      }								      \
-									      \
-	    do								      \
-	      {								      \
-		int flags;						      \
-		__typeof__ (cache->libs[0]) *lib = &cache->libs[middle];      \
-									      \
-		/* Only perform the name test if necessary.  */		      \
-		if (middle > left					      \
-		    /* We haven't seen this string so far.  Test whether the  \
-		       index is ok and whether the name matches.  Otherwise   \
-		       we are done.  */					      \
-		    && (! _dl_cache_verify_ptr (lib->key)		      \
-			|| (_dl_cache_libcmp (name, cache_data + lib->key)    \
-			    != 0)))					      \
-		  break;						      \
-									      \
-		flags = lib->flags;					      \
-		if (_dl_cache_check_flags (flags)			      \
-		    && _dl_cache_verify_ptr (lib->value))		      \
-		  {							      \
-		    if (best == NULL || flags == GLRO(dl_correct_cache_id))   \
-		      {							      \
-			HWCAP_CHECK;					      \
-			best = cache_data + lib->value;			      \
-									      \
-			if (flags == GLRO(dl_correct_cache_id))		      \
-			  /* We've found an exact match for the shared	      \
-			     object and no general `ELF' release.  Stop	      \
-			     searching.  */				      \
-			  break;					      \
-		      }							      \
-		  }							      \
-	      }								      \
-	    while (++middle <= right);					      \
-	    break;							      \
-	}								      \
-									      \
-	if (cmpres < 0)							      \
-	  left = middle + 1;						      \
-	else								      \
-	  right = middle - 1;						      \
-      }									      \
-  }									      \
-while (0)
+/* True if PTR is a valid string table index.  */
+static inline bool
+_dl_cache_verify_ptr (uint32_t ptr, size_t string_table_size)
+{
+  return ptr < string_table_size;
+}
+
+/* Compute the address of the element INDEX of the array at LIBS.
+   Conceptually, this is &LIBS[INDEX], but use ENTRY_SIZE for the size
+   of *LIBS.  */
+static inline const struct file_entry *
+_dl_cache_file_entry (const struct file_entry *libs, size_t entry_size,
+		      size_t index)
+{
+  return (const void *) libs + index * entry_size;
+}
+
+/* We use binary search since the table is sorted in the cache file.
+   The first matching entry in the table is returned.  It is important
+   to use the same algorithm as used while generating the cache file.
+   STRING_TABLE_SIZE indicates the maximum offset in STRING_TABLE at
+   which data is mapped; it is not exact.  */
+static const char *
+search_cache (const char *string_table, uint32_t string_table_size,
+	      struct file_entry *libs, uint32_t nlibs, uint32_t entry_size,
+	      const char *name)
+{
+  /* Used by the HWCAP check in the struct file_entry_new case.  */
+  uint64_t platform = _dl_string_platform (GLRO (dl_platform));
+  if (platform != (uint64_t) -1)
+    platform = 1ULL << platform;
+  uint64_t hwcap_mask = GET_HWCAP_MASK ();
+#define _DL_HWCAP_TLS_MASK (1LL << 63)
+  uint64_t hwcap_exclude = ~((GLRO (dl_hwcap) & hwcap_mask)
+			     | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK);
+
+  int left = 0;
+  int right = nlibs - 1;
+  const char *best = NULL;
+
+  while (left <= right)
+    {
+      int middle = (left + right) / 2;
+      uint32_t key = _dl_cache_file_entry (libs, entry_size, middle)->key;
+
+      /* Make sure string table indices are not bogus before using
+	 them.  */
+      if (!_dl_cache_verify_ptr (key, string_table_size))
+	return NULL;
 
+      /* Actually compare the entry with the key.  */
+      int cmpres = _dl_cache_libcmp (name, string_table + key);
+      if (__glibc_unlikely (cmpres == 0))
+	{
+	  /* Found it.  LEFT now marks the last entry for which we
+	     know the name is correct.  */
+	  left = middle;
+
+	  /* There might be entries with this name before the one we
+	     found.  So we have to find the beginning.  */
+	  while (middle > 0)
+	    {
+	      key = _dl_cache_file_entry (libs, entry_size, middle - 1)->key;
+	      /* Make sure string table indices are not bogus before
+		 using them.  */
+	      if (!_dl_cache_verify_ptr (key, string_table_size)
+		  /* Actually compare the entry.  */
+		  || _dl_cache_libcmp (name, string_table + key) != 0)
+		break;
+	      --middle;
+	    }
+
+	  do
+	    {
+	      int flags;
+	      const struct file_entry *lib
+		= _dl_cache_file_entry (libs, entry_size, middle);
+
+	      /* Only perform the name test if necessary.  */
+	      if (middle > left
+		  /* We haven't seen this string so far.  Test whether the
+		     index is ok and whether the name matches.  Otherwise
+		     we are done.  */
+		  && (! _dl_cache_verify_ptr (lib->key, string_table_size)
+		      || (_dl_cache_libcmp (name, string_table + lib->key)
+			  != 0)))
+		break;
+
+	      flags = lib->flags;
+	      if (_dl_cache_check_flags (flags)
+		  && _dl_cache_verify_ptr (lib->value, string_table_size))
+		{
+		  if (best == NULL || flags == GLRO (dl_correct_cache_id))
+		    {
+		      if (entry_size >= sizeof (struct file_entry_new))
+			{
+			  /* The entry is large enough to include
+			     HWCAP data.  Check it.  */
+			  struct file_entry_new *libnew
+			    = (struct file_entry_new *) lib;
+
+			  if (libnew->hwcap & hwcap_exclude)
+			    continue;
+			  if (GLRO (dl_osversion)
+			      && libnew->osversion > GLRO (dl_osversion))
+			    continue;
+			  if (_DL_PLATFORMS_COUNT
+			      && (libnew->hwcap & _DL_HWCAP_PLATFORM) != 0
+			      && ((libnew->hwcap & _DL_HWCAP_PLATFORM)
+				  != platform))
+			    continue;
+			}
+
+		      best = string_table + lib->value;
+
+		      if (flags == GLRO (dl_correct_cache_id))
+			/* We've found an exact match for the shared
+			   object and no general `ELF' release.  Stop
+			   searching.  */
+			break;
+		    }
+		}
+	    }
+	  while (++middle <= right);
+	  break;
+	}
+
+      if (cmpres < 0)
+	left = middle + 1;
+      else
+	right = middle - 1;
+    }
+
+  return best;
+}
 
 int
 _dl_cache_libcmp (const char *p1, const char *p2)
@@ -182,12 +220,6 @@ _dl_cache_libcmp (const char *p1, const char *p2)
 char *
 _dl_load_cache_lookup (const char *name)
 {
-  int left, right, middle;
-  int cmpres;
-  const char *cache_data;
-  uint32_t cache_data_size;
-  const char *best;
-
   /* Print a message if the loading of libs is traced.  */
   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
     _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE);
@@ -265,51 +297,22 @@ _dl_load_cache_lookup (const char *name)
     /* Previously looked for the cache file and didn't find it.  */
     return NULL;
 
-  best = NULL;
-
+  const char *best;
   if (cache_new != (void *) -1)
     {
-      uint64_t platform;
-
-      /* This is where the strings start.  */
-      cache_data = (const char *) cache_new;
-
-      /* Now we can compute how large the string table is.  */
-      cache_data_size = (const char *) cache + cachesize - cache_data;
-
-      platform = _dl_string_platform (GLRO(dl_platform));
-      if (platform != (uint64_t) -1)
-	platform = 1ULL << platform;
-
-      uint64_t hwcap_mask = GET_HWCAP_MASK();
-
-#define _DL_HWCAP_TLS_MASK (1LL << 63)
-      uint64_t hwcap_exclude = ~((GLRO(dl_hwcap) & hwcap_mask)
-				 | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK);
-
-      /* Only accept hwcap if it's for the right platform.  */
-#define HWCAP_CHECK \
-      if (lib->hwcap & hwcap_exclude)					      \
-	continue;							      \
-      if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion))	      \
-	continue;							      \
-      if (_DL_PLATFORMS_COUNT						      \
-	  && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0			      \
-	  && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform)		      \
-	continue
-      SEARCH_CACHE (cache_new);
+      const char *string_table = (const char *) cache_new;
+      best = search_cache (string_table, cachesize,
+			   &cache_new->libs[0].entry, cache_new->nlibs,
+			   sizeof (cache_new->libs[0]), name);
     }
   else
     {
-      /* This is where the strings start.  */
-      cache_data = (const char *) &cache->libs[cache->nlibs];
-
-      /* Now we can compute how large the string table is.  */
-      cache_data_size = (const char *) cache + cachesize - cache_data;
-
-#undef HWCAP_CHECK
-#define HWCAP_CHECK do {} while (0)
-      SEARCH_CACHE (cache);
+      const char *string_table = (const char *) &cache->libs[cache->nlibs];
+      uint32_t string_table_size
+	= (const char *) cache + cachesize - string_table;
+      best = search_cache (string_table, string_table_size,
+			   &cache->libs[0], cache->nlibs,
+			   sizeof (cache->libs[0]), name);
     }
 
   /* Print our result if wanted.  */
diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
index b154740da9..fec209509d 100644
--- a/sysdeps/generic/dl-cache.h
+++ b/sysdeps/generic/dl-cache.h
@@ -66,8 +66,8 @@
 */
 struct file_entry
 {
-  int flags;		/* This is 1 for an ELF library.  */
-  unsigned int key, value; /* String table indices.  */
+  int32_t flags;		/* This is 1 for an ELF library.  */
+  uint32_t key, value;		/* String table indices.  */
 };
 
 struct cache_file
@@ -84,8 +84,17 @@ struct cache_file
 
 struct file_entry_new
 {
-  int32_t flags;		/* This is 1 for an ELF library.  */
-  uint32_t key, value;		/* String table indices.  */
+  union
+  {
+    /* Fields shared with struct file_entry.  */
+    struct file_entry entry;
+    /* Also expose these fields directly.  */
+    struct
+    {
+      int32_t flags;		/* This is 1 for an ELF library.  */
+      uint32_t key, value;	/* String table indices.  */
+    };
+  };
   uint32_t osversion;		/* Required OS version.	 */
   uint64_t hwcap;		/* Hwcap entry.	 */
 };
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 24/28] elf: Implement a string table for ldconfig, with tail merging
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (22 preceding siblings ...)
  2020-10-01 16:34 ` [PATCH 23/28] elf: Unify old and new format cache handling code in ld.so Florian Weimer via Libc-alpha
@ 2020-10-01 16:34 ` Florian Weimer via Libc-alpha
  2020-10-20 14:25   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:34 ` [PATCH 25/28] elf: Implement tail merging of strings in ldconfig Florian Weimer via Libc-alpha
                   ` (4 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:34 UTC (permalink / raw)
  To: libc-alpha

This will be used in ldconfig to reduce the ld.so.cache size slightly.
---
 elf/Makefile           |   2 +-
 elf/stringtable.c      | 201 +++++++++++++++++++++++++++++++++++++++++
 elf/stringtable.h      |  61 +++++++++++++
 elf/stringtable_free.c |  32 +++++++
 elf/tst-stringtable.c  | 140 ++++++++++++++++++++++++++++
 5 files changed, 435 insertions(+), 1 deletion(-)
 create mode 100644 elf/stringtable.c
 create mode 100644 elf/stringtable.h
 create mode 100644 elf/stringtable_free.c
 create mode 100644 elf/tst-stringtable.c

diff --git a/elf/Makefile b/elf/Makefile
index 2c36b08c73..ad50a3e16e 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -172,7 +172,7 @@ tests-container := \
 
 tests := tst-tls9 tst-leaks1 \
 	tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 \
-	tst-auxv
+	tst-auxv tst-stringtable
 tests-internal := tst-tls1 tst-tls2 $(tests-static-internal)
 tests-static := $(tests-static-normal) $(tests-static-internal)
 
diff --git a/elf/stringtable.c b/elf/stringtable.c
new file mode 100644
index 0000000000..f9ade50249
--- /dev/null
+++ b/elf/stringtable.c
@@ -0,0 +1,201 @@
+/* String tables for ld.so.cache construction.  Implementation.
+   This file is part of the GNU C Library.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <error.h>
+#include <ldconfig.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringtable.h>
+
+static void
+stringtable_init (struct stringtable *table)
+{
+  table->count = 0;
+  table->allocated = 16;
+  table->entries = xcalloc (table->allocated, sizeof (table->entries[0]));
+}
+
+/* 32-bit FNV-1a hash function.  */
+static uint32_t
+fnv1a (const char *string, size_t length)
+{
+  const unsigned char *p = (const unsigned char *) string;
+  uint32_t hash = 2166136261U;
+  for (size_t i = 0; i < length; ++i)
+    {
+      hash ^= p[i];
+      hash *= 16777619U;
+    }
+  return hash;
+}
+
+/* Double the capacity of the hash table.  */
+static void
+stringtable_rehash (struct stringtable *table)
+{
+  /* Cannot overflow because the old allocation size (in bytes) is
+     larger.  */
+  uint32_t new_allocated = table->allocated * 2;
+  struct stringtable_entry **new_entries
+    = xcalloc (new_allocated, sizeof (table->entries[0]));
+
+  uint32_t mask = new_allocated - 1;
+  for (uint32_t i = 0; i < table->allocated; ++i)
+    for (struct stringtable_entry *e = table->entries[i]; e != NULL; )
+      {
+        struct stringtable_entry *next = e->next;
+        uint32_t hash = fnv1a (e->string, e->length);
+        uint32_t new_index = hash & mask;
+        e->next = new_entries[new_index];
+        new_entries[new_index] = e;
+        e = next;
+      }
+
+  free (table->entries);
+  table->entries = new_entries;
+  table->allocated = new_allocated;
+}
+
+struct stringtable_entry *
+stringtable_intern (struct stringtable *table, const char *string)
+{
+  if (table->allocated == 0)
+    stringtable_init (table);
+
+  size_t length = strlen (string);
+  if (length > (1U << 30))
+    error (EXIT_FAILURE, 0, _("String table string is too long"));
+  uint32_t hash = fnv1a (string, length);
+
+  /* Return a previously-existing entry.  */
+  for (struct stringtable_entry *e
+         = table->entries[hash & (table->allocated - 1)];
+       e != NULL; e = e->next)
+    if (e->length == length && memcmp (e->string, string, length) == 0)
+      return e;
+
+  /* Increase the size of the table if necessary.  Keep utilization
+     below two thirds.  */
+  if (table->count >= (1U << 30))
+    error (EXIT_FAILURE, 0, _("String table has too many entries"));
+  if (table->count * 3 > table->allocated * 2)
+    stringtable_rehash (table);
+
+  /* Add the new table entry.  */
+  ++table->count;
+  struct stringtable_entry *e
+    = xmalloc (offsetof (struct stringtable_entry, string) + length + 1);
+  uint32_t index = hash & (table->allocated - 1);
+  e->next = table->entries[index];
+  table->entries[index] = e;
+  e->length = length;
+  e->offset = 0;
+  memcpy (e->string, string, length + 1);
+  return e;
+}
+
+/* Sort reversed strings in lexicographic order.  This is used for tail
+   merging.  */
+static int
+finalize_compare (const void *l, const void *r)
+{
+  struct stringtable_entry *left = *(struct stringtable_entry **) l;
+  struct stringtable_entry *right = *(struct stringtable_entry **) r;
+  size_t to_compare;
+  if (left->length < right->length)
+    to_compare = left->length;
+  else
+    to_compare = right->length;
+  for (ssize_t i = to_compare - 1; i >= 0; --i)
+    {
+      unsigned char lch = left->string[i];
+      unsigned char rch = right->string[i];
+      if (lch != rch)
+        return lch - rch;
+    }
+  if (left->length == right->length)
+    return 0;
+  else if (left->length < right->length)
+    /* Longer strings should come first.  */
+    return 1;
+  else
+    return -1;
+}
+
+void
+stringtable_finalize (struct stringtable *table,
+                      struct stringtable_finalized *result)
+{
+  if (table->count == 0)
+    {
+      result->strings = xstrdup ("");
+      result->size = 0;
+      return;
+    }
+
+  /* Optimize the order of the strings.  */
+  struct stringtable_entry **array = xcalloc (table->count, sizeof (*array));
+  {
+    size_t j = 0;
+    for (uint32_t i = 0; i < table->allocated; ++i)
+      for (struct stringtable_entry *e = table->entries[i]; e != NULL;
+           e = e->next)
+        {
+          array[j] = e;
+          ++j;
+        }
+    assert (j == table->count);
+  }
+  qsort (array, table->count, sizeof (*array), finalize_compare);
+
+  /* Assign offsets, using table sharing if possible.  */
+  array[0]->offset = 0;
+  for (uint32_t j = 1; j < table->count; ++j)
+    {
+      struct stringtable_entry *previous = array[j - 1];
+      struct stringtable_entry *current = array[j];
+      if (previous->length >= current->length
+          && memcmp (&previous->string[previous->length - current->length],
+                     current->string, current->length) == 0)
+        current->offset = (previous->offset + previous->length
+                           - current->length);
+      else if (__builtin_add_overflow (previous->offset,
+                                       previous->length + 1,
+                                       &current->offset))
+        error (EXIT_FAILURE, 0, _("String table is too large"));
+    }
+
+  /* Allocate the result string.  */
+  {
+    struct stringtable_entry *last = array[table->count - 1];
+    if (__builtin_add_overflow (last->offset, last->length + 1,
+                                &result->size))
+      error (EXIT_FAILURE, 0, _("String table is too large"));
+  }
+  /* The strings are copied from the hash table, so the array is no
+     longer needed.  */
+  free (array);
+  result->strings = xcalloc (result->size, 1);
+
+  /* Copy the strings.  */
+  for (uint32_t i = 0; i < table->allocated; ++i)
+    for (struct stringtable_entry *e = table->entries[i]; e != NULL;
+         e = e->next)
+      if (result->strings[e->offset] == '\0')
+        memcpy (&result->strings[e->offset], e->string, e->length + 1);
+}
diff --git a/elf/stringtable.h b/elf/stringtable.h
new file mode 100644
index 0000000000..e35b6c67fd
--- /dev/null
+++ b/elf/stringtable.h
@@ -0,0 +1,61 @@
+/* String tables for ld.so.cache construction.
+   This file is part of the GNU C Library.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _STRINGTABLE_H
+#define _STRINGTABLE_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* An entry in the string table.  Only the length and string fields are
+   expected to be used outside the string table code.  */
+struct stringtable_entry
+{
+  struct stringtable_entry *next; /* For collision resolution.  */
+  uint32_t length;                /* Length of then string.  */
+  uint32_t offset;                /* From start of finalized table.  */
+  char string[];                  /* Null-terminated string.  */
+};
+
+/* A string table.  Zero-initialization produces a valid atable.  */
+struct stringtable
+{
+  struct stringtable_entry **entries;
+  uint32_t count;                 /* Number of elements in the table.  */
+  uint32_t allocated;             /* Length of the entries array.  */
+};
+
+/* Adds STRING to TABLE.  May return the address of an existing entry.  */
+struct stringtable_entry *stringtable_intern (struct stringtable *table,
+                                              const char *string);
+
+/* Result of stringtable_finalize.  SIZE bytes at STRINGS should be
+   written to the file.  */
+struct stringtable_finalized
+{
+  char *strings;
+  size_t size;
+};
+
+/* Assigns offsets to string table entries and computes the serialized
+   form of the string table.  */
+void stringtable_finalize (struct stringtable *table,
+                           struct stringtable_finalized *result);
+
+/* Deallocate the string table (but not the TABLE pointer itself).  */
+void stringtable_free (struct stringtable *table);
+
+#endif /* _STRINGTABLE_H */
diff --git a/elf/stringtable_free.c b/elf/stringtable_free.c
new file mode 100644
index 0000000000..0e5296e429
--- /dev/null
+++ b/elf/stringtable_free.c
@@ -0,0 +1,32 @@
+/* String tables for ld.so.cache construction.  Deallocation (for tests only).
+   This file is part of the GNU C Library.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+#include <stringtable.h>
+
+void
+stringtable_free (struct stringtable *table)
+{
+  for (uint32_t i = 0; i < table->allocated; ++i)
+    for (struct stringtable_entry *e = table->entries[i]; e != NULL; )
+      {
+        struct stringtable_entry *next = e->next;
+        free (e);
+        e = next;
+      }
+  free (table->entries);
+  *table = (struct stringtable) { 0, };
+}
diff --git a/elf/tst-stringtable.c b/elf/tst-stringtable.c
new file mode 100644
index 0000000000..78ca5434df
--- /dev/null
+++ b/elf/tst-stringtable.c
@@ -0,0 +1,140 @@
+/* Unit test for ldconfig string tables.
+   This file is part of the GNU C Library.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published
+   by the Free Software Foundation; version 2 of the License, or
+   (at your option) any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stringtable.h>
+#include <support/check.h>
+#include <support/support.h>
+
+static int
+do_test (void)
+{
+  /* Empty string table.  */
+  {
+    struct stringtable s = { 0, };
+    struct stringtable_finalized f;
+    stringtable_finalize (&s, &f);
+    TEST_COMPARE_STRING (f.strings, "");
+    TEST_COMPARE (f.size, 0);
+    free (f.strings);
+    stringtable_free (&s);
+  }
+
+  /* String table with one empty string.  */
+  {
+    struct stringtable s = { 0, };
+    struct stringtable_entry *e = stringtable_intern (&s, "");
+    TEST_COMPARE_STRING (e->string, "");
+    TEST_COMPARE (e->length, 0);
+    TEST_COMPARE (s.count, 1);
+
+    struct stringtable_finalized f;
+    stringtable_finalize (&s, &f);
+    TEST_COMPARE (e->offset, 0);
+    TEST_COMPARE_STRING (f.strings, "");
+    TEST_COMPARE (f.size, 1);
+    free (f.strings);
+    stringtable_free (&s);
+  }
+
+  /* String table with one non-empty string.  */
+  {
+    struct stringtable s = { 0, };
+    struct stringtable_entry *e = stringtable_intern (&s, "name");
+    TEST_COMPARE_STRING (e->string, "name");
+    TEST_COMPARE (e->length, 4);
+    TEST_COMPARE (s.count, 1);
+
+    struct stringtable_finalized f;
+    stringtable_finalize (&s, &f);
+    TEST_COMPARE (e->offset, 0);
+    TEST_COMPARE_STRING (f.strings, "name");
+    TEST_COMPARE (f.size, 5);
+    free (f.strings);
+    stringtable_free (&s);
+  }
+
+  /* Two strings, one is a prefix of the other.  Tail-merging can only
+     happen in one way in this case.  */
+  {
+    struct stringtable s = { 0, };
+    struct stringtable_entry *suffix = stringtable_intern (&s, "suffix");
+    TEST_COMPARE_STRING (suffix->string, "suffix");
+    TEST_COMPARE (suffix->length, 6);
+    TEST_COMPARE (s.count, 1);
+
+    struct stringtable_entry *prefix
+      = stringtable_intern (&s, "prefix-suffix");
+    TEST_COMPARE_STRING (prefix->string, "prefix-suffix");
+    TEST_COMPARE (prefix->length, strlen ("prefix-suffix"));
+    TEST_COMPARE (s.count, 2);
+
+    struct stringtable_finalized f;
+    stringtable_finalize (&s, &f);
+    TEST_COMPARE (prefix->offset, 0);
+    TEST_COMPARE (suffix->offset, strlen ("prefix-"));
+    TEST_COMPARE_STRING (f.strings, "prefix-suffix");
+    TEST_COMPARE (f.size, sizeof ("prefix-suffix"));
+    free (f.strings);
+    stringtable_free (&s);
+  }
+
+  /* String table with various shared prefixes.  Triggers hash
+     resizing.  */
+  {
+    enum { count = 1500 };
+    char *strings[2 * count];
+    struct stringtable_entry *entries[2 * count];
+    struct stringtable s = { 0, };
+    for (int i = 0; i < count; ++i)
+      {
+        strings[i] = xasprintf ("%d", i);
+        entries[i] = stringtable_intern (&s, strings[i]);
+        TEST_COMPARE (entries[i]->length, strlen (strings[i]));
+        TEST_COMPARE_STRING (entries[i]->string, strings[i]);
+        strings[i + count] = xasprintf ("prefix/%d", i);
+        entries[i + count] = stringtable_intern (&s, strings[i + count]);
+        TEST_COMPARE (entries[i + count]->length, strlen (strings[i + count]));
+        TEST_COMPARE_STRING (entries[i + count]->string, strings[i + count]);
+      }
+
+    struct stringtable_finalized f;
+    stringtable_finalize (&s, &f);
+
+    for (int i = 0; i < 2 * count; ++i)
+      {
+        TEST_COMPARE (entries[i]->length, strlen (strings[i]));
+        TEST_COMPARE_STRING (entries[i]->string, strings[i]);
+        TEST_COMPARE_STRING (f.strings + entries[i]->offset, strings[i]);
+        free (strings[i]);
+      }
+
+    free (f.strings);
+    stringtable_free (&s);
+  }
+
+  return 0;
+}
+
+#include <support/test-driver.c>
+
+/* Re-compile the string table implementation here.  It is not
+   possible to link against the actual build because it was built for
+   use in ldconfig.  */
+#define _(arg) arg
+#include "stringtable.c"
+#include "stringtable_free.c"
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 25/28] elf: Implement tail merging of strings in ldconfig
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (23 preceding siblings ...)
  2020-10-01 16:34 ` [PATCH 24/28] elf: Implement a string table for ldconfig, with tail merging Florian Weimer via Libc-alpha
@ 2020-10-01 16:34 ` Florian Weimer via Libc-alpha
  2020-10-22 21:08   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:34 ` [PATCH 26/28] elf: In ldconfig, extract the new_sub_entry function from search_dir Florian Weimer via Libc-alpha
                   ` (3 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:34 UTC (permalink / raw)
  To: libc-alpha

This simplifies the string table construction in elf/cache.c
because there is no more need to keep track of offsets explicitly;
the string table implementation does this internally.

This change slightly reduces the size of the cache on disk.  The
file format does not change as a result.  The strings are
null-terminated, without explicit length, so tail merging is
transparent to readers.
---
 elf/Makefile |  3 +-
 elf/cache.c  | 84 ++++++++++++++++++++++++++++------------------------
 2 files changed, 48 insertions(+), 39 deletions(-)

diff --git a/elf/Makefile b/elf/Makefile
index ad50a3e16e..5ad8df7da3 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -118,7 +118,8 @@ others-static	+= ldconfig
 others		+= ldconfig
 install-rootsbin += ldconfig
 
-ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon static-stubs
+ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon static-stubs \
+  stringtable
 extra-objs	+= $(ldconfig-modules:=.o)
 others-extras   = $(ldconfig-modules)
 endif
diff --git a/elf/cache.c b/elf/cache.c
index 3a02a4070a..eda3da98a7 100644
--- a/elf/cache.c
+++ b/elf/cache.c
@@ -35,11 +35,15 @@
 #include <ldconfig.h>
 #include <dl-cache.h>
 #include <version.h>
+#include <stringtable.h>
+
+/* Used to store library names, paths, and other strings.  */
+struct stringtable strings;
 
 struct cache_entry
 {
-  char *lib;			/* Library name.  */
-  char *path;			/* Path to find library.  */
+  struct stringtable_entry *lib; /* Library name.  */
+  struct stringtable_entry *path; /* Path to find library.  */
   int flags;			/* Flags to indicate kind of library.  */
   unsigned int osversion;	/* Required OS version.  */
   uint64_t hwcap;		/* Important hardware capabilities.  */
@@ -300,7 +304,7 @@ static int
 compare (const struct cache_entry *e1, const struct cache_entry *e2)
 {
   /* We need to swap entries here to get the correct sort order.  */
-  int res = _dl_cache_libcmp (e2->lib, e1->lib);
+  int res = _dl_cache_libcmp (e2->lib->string, e1->lib->string);
   if (res == 0)
     {
       if (e1->flags < e2->flags)
@@ -369,26 +373,24 @@ save_cache (const char *cache_name)
 {
   /* The cache entries are sorted already, save them in this order. */
 
-  /* Count the length of all strings.  */
-  /* The old format doesn't contain hwcap entries and doesn't contain
-     libraries in subdirectories with hwcaps entries.  Count therefore
-     also all entries with hwcap == 0.  */
-  size_t total_strlen = 0;
   struct cache_entry *entry;
   /* Number of cache entries.  */
   int cache_entry_count = 0;
-  /* Number of normal cache entries.  */
+  /* The old format doesn't contain hwcap entries and doesn't contain
+     libraries in subdirectories with hwcaps entries.  Count therefore
+     also all entries with hwcap == 0.  */
   int cache_entry_old_count = 0;
 
   for (entry = entries; entry != NULL; entry = entry->next)
     {
-      /* Account the final NULs.  */
-      total_strlen += strlen (entry->lib) + strlen (entry->path) + 2;
       ++cache_entry_count;
       if (entry->hwcap == 0)
 	++cache_entry_old_count;
     }
 
+  struct stringtable_finalized strings_finalized;
+  stringtable_finalize (&strings, &strings_finalized);
+
   /* Create the on disk cache structure.  */
   struct cache_file *file_entries = NULL;
   size_t file_entries_size = 0;
@@ -432,7 +434,7 @@ save_cache (const char *cache_name)
 	      sizeof CACHE_VERSION - 1);
 
       file_entries_new->nlibs = cache_entry_count;
-      file_entries_new->len_strings = total_strlen;
+      file_entries_new->len_strings = strings_finalized.size;
       file_entries_new->flags = cache_file_new_flags_endian;
     }
 
@@ -449,20 +451,20 @@ save_cache (const char *cache_name)
     str_offset = 0;
 
   /* An array for all strings.  */
-  char *strings = xmalloc (total_strlen);
-  char *str = strings;
   int idx_old;
   int idx_new;
 
   for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL;
        entry = entry->next, ++idx_new)
     {
-      /* First the library.  */
       if (opt_format != 2 && entry->hwcap == 0)
 	{
 	  file_entries->libs[idx_old].flags = entry->flags;
 	  /* XXX: Actually we can optimize here and remove duplicates.  */
 	  file_entries->libs[idx_old].key = str_offset + pad;
+	  file_entries->libs[idx_new].key = str_offset + entry->lib->offset;
+	  file_entries->libs[idx_new].value
+	    = str_offset + entry->path->offset;
 	}
       if (opt_format != 0)
 	{
@@ -473,20 +475,12 @@ save_cache (const char *cache_name)
 	  file_entries_new->libs[idx_new].flags = entry->flags;
 	  file_entries_new->libs[idx_new].osversion = entry->osversion;
 	  file_entries_new->libs[idx_new].hwcap = entry->hwcap;
-	  file_entries_new->libs[idx_new].key = str_offset;
+	  file_entries_new->libs[idx_new].key
+	    = str_offset + entry->lib->offset;
+	  file_entries_new->libs[idx_new].value
+	    = str_offset + entry->path->offset;
 	}
 
-      size_t len = strlen (entry->lib) + 1;
-      str = mempcpy (str, entry->lib, len);
-      str_offset += len;
-      /* Then the path.  */
-      if (opt_format != 2 && entry->hwcap == 0)
-	file_entries->libs[idx_old].value = str_offset + pad;
-      if (opt_format != 0)
-	file_entries_new->libs[idx_new].value = str_offset;
-      len = strlen (entry->path) + 1;
-      str = mempcpy (str, entry->path, len);
-      str_offset += len;
       /* Ignore entries with hwcap for old format.  */
       if (entry->hwcap == 0)
 	++idx_old;
@@ -511,7 +505,7 @@ save_cache (const char *cache_name)
 	extension_offset += pad;
       extension_offset += file_entries_new_size;
     }
-  extension_offset += total_strlen;
+  extension_offset += strings_finalized.size;
   extension_offset = roundup (extension_offset, 4); /* Provide alignment.  */
   if (opt_format != 0)
     file_entries_new->extension_offset = extension_offset;
@@ -551,7 +545,8 @@ save_cache (const char *cache_name)
 	error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
     }
 
-  if (write (fd, strings, total_strlen) != (ssize_t) total_strlen)
+  if (write (fd, strings_finalized.strings, strings_finalized.size)
+      != (ssize_t) strings_finalized.size)
     error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
 
   if (opt_format != 0)
@@ -580,7 +575,7 @@ save_cache (const char *cache_name)
   /* Free all allocated memory.  */
   free (file_entries_new);
   free (file_entries);
-  free (strings);
+  free (strings_finalized.strings);
 
   while (entries)
     {
@@ -596,14 +591,27 @@ void
 add_to_cache (const char *path, const char *lib, int flags,
 	      unsigned int osversion, uint64_t hwcap)
 {
-  size_t liblen = strlen (lib) + 1;
-  size_t len = liblen + strlen (path) + 1;
-  struct cache_entry *new_entry
-    = xmalloc (sizeof (struct cache_entry) + liblen + len);
-
-  new_entry->lib = memcpy ((char *) (new_entry + 1), lib, liblen);
-  new_entry->path = new_entry->lib + liblen;
-  snprintf (new_entry->path, len, "%s/%s", path, lib);
+  struct cache_entry *new_entry = xmalloc (sizeof (*new_entry));
+
+  struct stringtable_entry *path_interned;
+  {
+    /* Use a small, on-stack buffer in most cases.  */
+    char buf[200];
+    int ret = snprintf (buf, sizeof (buf), "%s/%s", path, lib);
+    if (ret < 0 || ret >= sizeof (buf) - 1)
+      {
+	char *p;
+	if (asprintf (&p, "%s/%s", path, lib) < 0)
+	  error (EXIT_FAILURE, errno, _("Could not create library path"));
+	path_interned = stringtable_intern (&strings, p);
+	free (p);
+      }
+    else
+      path_interned = stringtable_intern (&strings, buf);
+  }
+
+  new_entry->lib = stringtable_intern (&strings, lib);
+  new_entry->path = path_interned;
   new_entry->flags = flags;
   new_entry->osversion = osversion;
   new_entry->hwcap = hwcap;
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 26/28] elf: In ldconfig, extract the new_sub_entry function from search_dir
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (24 preceding siblings ...)
  2020-10-01 16:34 ` [PATCH 25/28] elf: Implement tail merging of strings in ldconfig Florian Weimer via Libc-alpha
@ 2020-10-01 16:34 ` Florian Weimer via Libc-alpha
  2020-10-27 13:15   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:34 ` [PATCH 27/28] elf: Process glibc-hwcaps subdirectories in ldconfig Florian Weimer via Libc-alpha
                   ` (2 subsequent siblings)
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:34 UTC (permalink / raw)
  To: libc-alpha

---
 elf/ldconfig.c | 34 +++++++++++++++++++++-------------
 1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/elf/ldconfig.c b/elf/ldconfig.c
index 0c090dca15..3768267bac 100644
--- a/elf/ldconfig.c
+++ b/elf/ldconfig.c
@@ -328,6 +328,23 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
 	   "Andreas Jaeger");
 }
 
+/* Allocate a new subdirectory with full path PATH under ENTRY, using
+   inode data from *ST.  */
+static struct dir_entry *
+new_sub_entry (const struct dir_entry *entry, const char *path,
+	       const struct stat64 *st)
+{
+  struct dir_entry *new_entry = xmalloc (sizeof (struct dir_entry));
+  new_entry->from_file = entry->from_file;
+  new_entry->from_line = entry->from_line;
+  new_entry->path = xstrdup (path);
+  new_entry->flag = entry->flag;
+  new_entry->next = NULL;
+  new_entry->ino = st->st_ino;
+  new_entry->dev = st->st_dev;
+  return new_entry;
+}
+
 /* Add a single directory entry.  */
 static void
 add_single_dir (struct dir_entry *entry, int verbose)
@@ -823,26 +840,17 @@ search_dir (const struct dir_entry *entry)
 
       if (is_dir && is_hwcap_platform (direntry->d_name))
 	{
-	  /* Handle subdirectory later.  */
-	  struct dir_entry *new_entry;
-
-	  new_entry = xmalloc (sizeof (struct dir_entry));
-	  new_entry->from_file = entry->from_file;
-	  new_entry->from_line = entry->from_line;
-	  new_entry->path = xstrdup (file_name);
-	  new_entry->flag = entry->flag;
-	  new_entry->next = NULL;
 	  if (!is_link
 	      && direntry->d_type != DT_UNKNOWN
 	      && __builtin_expect (lstat64 (real_file_name, &lstat_buf), 0))
 	    {
 	      error (0, errno, _("Cannot lstat %s"), file_name);
-	      free (new_entry->path);
-	      free (new_entry);
 	      continue;
 	    }
-	  new_entry->ino = lstat_buf.st_ino;
-	  new_entry->dev = lstat_buf.st_dev;
+
+	  /* Handle subdirectory later.  */
+	  struct dir_entry *new_entry = new_sub_entry (entry, file_name,
+						       &lstat_buf);
 	  add_single_dir (new_entry, 0);
 	  continue;
 	}
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 27/28] elf: Process glibc-hwcaps subdirectories in ldconfig
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (25 preceding siblings ...)
  2020-10-01 16:34 ` [PATCH 26/28] elf: In ldconfig, extract the new_sub_entry function from search_dir Florian Weimer via Libc-alpha
@ 2020-10-01 16:34 ` Florian Weimer via Libc-alpha
  2020-10-27 17:28   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 16:34 ` [PATCH 28/28] elf: Add glibc-hwcaps subdirectory support to ld.so cache processing Florian Weimer via Libc-alpha
  2020-10-01 16:50 ` [PATCH 00/28] glibc-hwcaps support H.J. Lu via Libc-alpha
  28 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:34 UTC (permalink / raw)
  To: libc-alpha

Libraries from these subdirectories are added to the cache
with a special hwcap bit DL_CACHE_HWCAP_EXTENSION, so that
they are ignored by older dynamic loaders.
---
 elf/cache.c                | 258 ++++++++++++++++++++++++++++++++-----
 elf/ldconfig.c             | 153 +++++++++++++++++++---
 sysdeps/generic/dl-cache.h |  51 +++++++-
 sysdeps/generic/ldconfig.h |  18 ++-
 4 files changed, 426 insertions(+), 54 deletions(-)

diff --git a/elf/cache.c b/elf/cache.c
index eda3da98a7..7ce4ca9870 100644
--- a/elf/cache.c
+++ b/elf/cache.c
@@ -40,6 +40,105 @@
 /* Used to store library names, paths, and other strings.  */
 struct stringtable strings;
 
+/* Keeping track of "glibc-hwcaps" subdirectories.  During cache
+   construction, a linear search by name is performed to deduplicate
+   entries.  */
+struct glibc_hwcaps_subdirectory
+{
+  struct glibc_hwcaps_subdirectory *next;
+
+  /* Interned string with the subdirectory name.  */
+  struct stringtable_entry *name;
+
+  /* Array index in the cache_extension_tag_glibc_hwcaps section in
+     the stored cached file.  This is computed after all the
+     subdirectories have been processed, so that subdirectory names in
+     the extension section can be sorted.  */
+  uint32_t section_index;
+
+  /* True if the subdirectory is actually used for anything.  */
+  bool used;
+};
+
+const char *
+glibc_hwcaps_subdirectory_name (struct glibc_hwcaps_subdirectory *dir)
+{
+  return dir->name->string;
+}
+
+/* Linked list of known hwcaps subdirecty names.  */
+static struct glibc_hwcaps_subdirectory *hwcaps;
+
+struct glibc_hwcaps_subdirectory *
+new_glibc_hwcaps_subdirectory (const char *name)
+{
+  struct stringtable_entry *name_interned
+    = stringtable_intern (&strings, name);
+  for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next)
+    if (p->name == name_interned)
+      return p;
+  struct glibc_hwcaps_subdirectory *p = xmalloc (sizeof (p));
+  p->next = hwcaps;
+  p->name = name_interned;
+  p->section_index = 0;
+  p->used = false;
+  hwcaps = p;
+  return p;
+}
+
+/* Helper for sorting struct glibc_hwcaps_subdirectory elements by
+   name.  */
+static int
+assign_glibc_hwcaps_indices_compare (const void *l, const void *r)
+{
+  const struct glibc_hwcaps_subdirectory *left
+    = *(struct glibc_hwcaps_subdirectory **)l;
+  const struct glibc_hwcaps_subdirectory *right
+    = *(struct glibc_hwcaps_subdirectory **)r;
+  return strcmp (left->name->string, right->name->string);
+}
+
+/* Count the number of hwcaps subdirectories which are actually
+   used.  */
+static size_t
+glibc_hwcaps_count (void)
+{
+  size_t count = 0;
+  for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next)
+    if (p->used)
+      ++count;
+  return count;
+}
+
+/* Compute the section_index fields for all   */
+static void
+assign_glibc_hwcaps_indices (void)
+{
+  /* Convert the linked list into an array, so that we can use qsort.
+     Only copy the subdirectories which are actually used.  */
+  size_t count = glibc_hwcaps_count ();
+  struct glibc_hwcaps_subdirectory **array
+    = xmalloc (sizeof (*array) * count);
+  {
+    size_t i = 0;
+    for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next)
+      if (p->used)
+	{
+	  array[i] = p;
+	  ++i;
+	}
+    assert (i == count);
+  }
+
+  qsort (array, count, sizeof (*array), assign_glibc_hwcaps_indices_compare);
+
+  /* Assign the array indices.  */
+  for (size_t i = 0; i < count; ++i)
+    array[i]->section_index = i;
+
+  free (array);
+}
+
 struct cache_entry
 {
   struct stringtable_entry *lib; /* Library name.  */
@@ -48,6 +147,10 @@ struct cache_entry
   unsigned int osversion;	/* Required OS version.  */
   uint64_t hwcap;		/* Important hardware capabilities.  */
   int bits_hwcap;		/* Number of bits set in hwcap.  */
+
+  /* glibc-hwcaps subdirectory.  If not NULL, hwcap must be zero.  */
+  struct glibc_hwcaps_subdirectory *hwcaps;
+
   struct cache_entry *next;	/* Next entry in list.  */
 };
 
@@ -60,7 +163,7 @@ static const char *flag_descr[] =
 /* Print a single entry.  */
 static void
 print_entry (const char *lib, int flag, unsigned int osversion,
-	     uint64_t hwcap, const char *key)
+	     uint64_t hwcap, const char *hwcap_string, const char *key)
 {
   printf ("\t%s (", lib);
   switch (flag & FLAG_TYPE_MASK)
@@ -132,7 +235,9 @@ print_entry (const char *lib, int flag, unsigned int osversion,
       printf (",%d", flag & FLAG_REQUIRED_MASK);
       break;
     }
-  if (hwcap != 0)
+  if (hwcap_string != NULL)
+    printf (", hwcap: \"%s\"", hwcap_string);
+  else if (hwcap != 0)
     printf (", hwcap: %#.16" PRIx64, hwcap);
   if (osversion != 0)
     {
@@ -158,6 +263,29 @@ print_entry (const char *lib, int flag, unsigned int osversion,
   printf (") => %s\n", key);
 }
 
+/* Returns the string with the name of the glibcs-hwcaps subdirectory
+   associated with ENTRY->hwcap.  file_base must be the base address
+   for string table indices.  */
+static const char *
+glibc_hwcaps_string (struct cache_extension_all_loaded *ext,
+		     const void *file_base, size_t file_size,
+		     struct file_entry_new *entry)
+{
+  const uint32_t *hwcaps_array
+    = ext->sections[cache_extension_tag_glibc_hwcaps].base;
+  if (dl_cache_hwcap_extension (entry) && hwcaps_array != NULL)
+    {
+      uint32_t index = (uint32_t) entry->hwcap;
+      if (index < ext->sections[cache_extension_tag_glibc_hwcaps].size / 4)
+	{
+	  uint32_t string_table_index = hwcaps_array[index];
+	  if (string_table_index < file_size)
+	    return file_base + string_table_index;
+	}
+    }
+  return NULL;
+}
+
 /* Print an error and exit if the new-file cache is internally
    inconsistent.  */
 static void
@@ -167,9 +295,7 @@ check_new_cache (struct cache_file_new *cache)
     error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n"));
 }
 
-/* Print the extension information at the cache at start address
-   FILE_BASE, of ltength FILE_SIZE bytes.  The new-format cache header
-   is at CACHE, and the file name for diagnostics is CACHE_NAME.  */
+/* Print the extension information in *EXT.  */
 static void
 print_extensions (struct cache_extension_all_loaded *ext)
 {
@@ -266,7 +392,7 @@ print_cache (const char *cache_name)
       /* Print everything.  */
       for (unsigned int i = 0; i < cache->nlibs; i++)
 	print_entry (cache_data + cache->libs[i].key,
-		     cache->libs[i].flags, 0, 0,
+		     cache->libs[i].flags, 0, 0, NULL,
 		     cache_data + cache->libs[i].value);
     }
   else if (format == 1)
@@ -281,11 +407,16 @@ print_cache (const char *cache_name)
 
       /* Print everything.  */
       for (unsigned int i = 0; i < cache_new->nlibs; i++)
-	print_entry (cache_data + cache_new->libs[i].key,
-		     cache_new->libs[i].flags,
-		     cache_new->libs[i].osversion,
-		     cache_new->libs[i].hwcap,
-		     cache_data + cache_new->libs[i].value);
+	{
+	  const char *hwcaps_string
+	    = glibc_hwcaps_string (&ext, cache, cache_size,
+				   &cache_new->libs[i]);
+	  print_entry (cache_data + cache_new->libs[i].key,
+		       cache_new->libs[i].flags,
+		       cache_new->libs[i].osversion,
+		       cache_new->libs[i].hwcap, hwcaps_string,
+		       cache_data + cache_new->libs[i].value);
+	}
       print_extensions (&ext);
     }
   /* Cleanup.  */
@@ -311,8 +442,22 @@ compare (const struct cache_entry *e1, const struct cache_entry *e2)
 	return 1;
       else if (e1->flags > e2->flags)
 	return -1;
+      /* Keep the glibc-hwcaps extension entries before the regular
+	 entries, and sort them by their names.  search_cache in
+	 dl-cache.c stops searching once the first non-extension entry
+	 is found, so the extension entries need to come first.  */
+      else if (e1->hwcaps != NULL && e2->hwcaps == NULL)
+	return -1;
+      else if (e1->hwcaps == NULL && e2->hwcaps != NULL)
+	return 1;
+      else if (e1->hwcaps != NULL && e2->hwcaps != NULL)
+	{
+	  res = strcmp (e1->hwcaps->name->string, e2->hwcaps->name->string);
+	  if (res != 0)
+	    return res;
+	}
       /* Sort by most specific hwcap.  */
-      else if (e2->bits_hwcap > e1->bits_hwcap)
+      if (e2->bits_hwcap > e1->bits_hwcap)
 	return 1;
       else if (e2->bits_hwcap < e1->bits_hwcap)
 	return -1;
@@ -337,30 +482,65 @@ enum
 			      * sizeof (struct cache_extension_section)))
   };
 
-/* Write the cache extensions to FD.  The extension directory is
-   assumed to be located at CACHE_EXTENSION_OFFSET.  */
+/* Write the cache extensions to FD.  The string table is shifted by
+   STRING_TABLE_OFFSET.  The extension directory is assumed to be
+   located at CACHE_EXTENSION_OFFSET.  assign_glibc_hwcaps_indices
+   must have been called.  */
 static void
-write_extensions (int fd, uint32_t cache_extension_offset)
+write_extensions (int fd, uint32_t str_offset,
+		  uint32_t cache_extension_offset)
 {
   assert ((cache_extension_offset % 4) == 0);
 
+  /* The length and contents of the glibc-hwcaps section.  */
+  uint32_t hwcaps_count = glibc_hwcaps_count ();
+  uint32_t hwcaps_offset = cache_extension_offset + cache_extension_size;
+  uint32_t hwcaps_size = hwcaps_count * sizeof (uint32_t);
+  uint32_t *hwcaps_array = xmalloc (hwcaps_size);
+  for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next)
+    if (p->used)
+      hwcaps_array[p->section_index] = str_offset + p->name->offset;
+
+  /* This is the offset of the generator string.  */
+  uint32_t generator_offset = hwcaps_offset;
+  if (hwcaps_count == 0)
+    /* There is no section for the hwcaps subdirectories.  */
+    generator_offset -= sizeof (struct cache_extension_section);
+  else
+    /* The string table indices for the hwcaps subdirectories shift
+       the generator string backwards.  */
+    generator_offset += hwcaps_count * sizeof (uint32_t);
+
   struct cache_extension *ext = xmalloc (cache_extension_size);
   ext->magic = cache_extension_magic;
-  ext->count = cache_extension_count;
 
-  for (int i = 0; i < cache_extension_count; ++i)
-    {
-      ext->sections[i].tag = i;
-      ext->sections[i].flags = 0;
-    }
+  /* Extension index current being filled.  */
+  size_t xid = 0;
 
   const char *generator
     = "ldconfig " PKGVERSION RELEASE " release version " VERSION;
-  ext->sections[cache_extension_tag_generator].offset
-    = cache_extension_offset + cache_extension_size;
-  ext->sections[cache_extension_tag_generator].size = strlen (generator);
+  ext->sections[xid].tag = cache_extension_tag_generator;
+  ext->sections[xid].flags = 0;
+  ext->sections[xid].offset = generator_offset;
+  ext->sections[xid].size = strlen (generator);
+
+  if (hwcaps_count > 0)
+    {
+      ++xid;
+      ext->sections[xid].tag = cache_extension_tag_glibc_hwcaps;
+      ext->sections[xid].flags = 0;
+      ext->sections[xid].offset = hwcaps_offset;
+      ext->sections[xid].size = hwcaps_size;
+    }
+
+  ++xid;
+  ext->count = xid;
+  assert (xid <= cache_extension_count);
 
-  if (write (fd, ext, cache_extension_size) != cache_extension_size
+  size_t ext_size = (offsetof (struct cache_extension, sections)
+		     + xid * sizeof (struct cache_extension_section));
+  if (write (fd, ext, ext_size) != ext_size
+      || write (fd, hwcaps_array, hwcaps_size) != hwcaps_size
       || write (fd, generator, strlen (generator)) != strlen (generator))
     error (EXIT_FAILURE, errno, _("Writing of cache extension data failed"));
 
@@ -373,6 +553,8 @@ save_cache (const char *cache_name)
 {
   /* The cache entries are sorted already, save them in this order. */
 
+  assign_glibc_hwcaps_indices ();
+
   struct cache_entry *entry;
   /* Number of cache entries.  */
   int cache_entry_count = 0;
@@ -474,7 +656,11 @@ save_cache (const char *cache_name)
 	     struct.  */
 	  file_entries_new->libs[idx_new].flags = entry->flags;
 	  file_entries_new->libs[idx_new].osversion = entry->osversion;
-	  file_entries_new->libs[idx_new].hwcap = entry->hwcap;
+	  if (entry->hwcaps == NULL)
+	    file_entries_new->libs[idx_new].hwcap = entry->hwcap;
+	  else
+	    file_entries_new->libs[idx_new].hwcap
+	      = DL_CACHE_HWCAP_EXTENSION | entry->hwcaps->section_index;
 	  file_entries_new->libs[idx_new].key
 	    = str_offset + entry->lib->offset;
 	  file_entries_new->libs[idx_new].value
@@ -554,7 +740,7 @@ save_cache (const char *cache_name)
       /* Align file position to 4.  */
       off64_t old_offset = lseek64 (fd, extension_offset, SEEK_SET);
       assert ((unsigned long long int) (extension_offset - old_offset) < 4);
-      write_extensions (fd, extension_offset);
+      write_extensions (fd, str_offset, extension_offset);
     }
 
   /* Make sure user can always read cache file */
@@ -588,8 +774,9 @@ save_cache (const char *cache_name)
 
 /* Add one library to the cache.  */
 void
-add_to_cache (const char *path, const char *lib, int flags,
-	      unsigned int osversion, uint64_t hwcap)
+add_to_cache (const char *path, const char *filename, const char *soname,
+	      int flags, unsigned int osversion, uint64_t hwcap,
+	      struct glibc_hwcaps_subdirectory *hwcaps)
 {
   struct cache_entry *new_entry = xmalloc (sizeof (*new_entry));
 
@@ -597,11 +784,11 @@ add_to_cache (const char *path, const char *lib, int flags,
   {
     /* Use a small, on-stack buffer in most cases.  */
     char buf[200];
-    int ret = snprintf (buf, sizeof (buf), "%s/%s", path, lib);
+    int ret = snprintf (buf, sizeof (buf), "%s/%s", path, filename);
     if (ret < 0 || ret >= sizeof (buf) - 1)
       {
 	char *p;
-	if (asprintf (&p, "%s/%s", path, lib) < 0)
+	if (asprintf (&p, "%s/%s", path, filename) < 0)
 	  error (EXIT_FAILURE, errno, _("Could not create library path"));
 	path_interned = stringtable_intern (&strings, p);
 	free (p);
@@ -610,13 +797,20 @@ add_to_cache (const char *path, const char *lib, int flags,
       path_interned = stringtable_intern (&strings, buf);
   }
 
-  new_entry->lib = stringtable_intern (&strings, lib);
+  new_entry->lib = stringtable_intern (&strings, soname);
   new_entry->path = path_interned;
   new_entry->flags = flags;
   new_entry->osversion = osversion;
   new_entry->hwcap = hwcap;
+  new_entry->hwcaps = hwcaps;
   new_entry->bits_hwcap = 0;
 
+  if (hwcaps != NULL)
+    {
+      assert (hwcap == 0);
+      hwcaps->used = true;
+    }
+
   /* Count the number of bits set in the masked value.  */
   for (size_t i = 0;
        (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i)
diff --git a/elf/ldconfig.c b/elf/ldconfig.c
index 3768267bac..3136601de7 100644
--- a/elf/ldconfig.c
+++ b/elf/ldconfig.c
@@ -16,6 +16,7 @@
    along with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
 #define PROCINFO_CLASS static
+#include <assert.h>
 #include <alloca.h>
 #include <argp.h>
 #include <dirent.h>
@@ -41,6 +42,7 @@
 
 #include <ldconfig.h>
 #include <dl-cache.h>
+#include <dl-hwcaps.h>
 
 #include <dl-procinfo.h>
 
@@ -85,6 +87,10 @@ struct dir_entry
   dev_t dev;
   const char *from_file;
   int from_line;
+
+  /* Non-NULL for subdirectories under a glibc-hwcaps subdirectory.  */
+  struct glibc_hwcaps_subdirectory *hwcaps;
+
   struct dir_entry *next;
 };
 
@@ -339,17 +345,20 @@ new_sub_entry (const struct dir_entry *entry, const char *path,
   new_entry->from_line = entry->from_line;
   new_entry->path = xstrdup (path);
   new_entry->flag = entry->flag;
+  new_entry->hwcaps = NULL;
   new_entry->next = NULL;
   new_entry->ino = st->st_ino;
   new_entry->dev = st->st_dev;
   return new_entry;
 }
 
-/* Add a single directory entry.  */
-static void
+/* Add a single directory entry.  Return true if the directory is
+   actually added (because it is not a duplicate).  */
+static bool
 add_single_dir (struct dir_entry *entry, int verbose)
 {
   struct dir_entry *ptr, *prev;
+  bool added = true;
 
   ptr = dir_entries;
   prev = ptr;
@@ -369,6 +378,7 @@ add_single_dir (struct dir_entry *entry, int verbose)
 	  ptr->flag = entry->flag;
 	  free (entry->path);
 	  free (entry);
+	  added = false;
 	  break;
 	}
       prev = ptr;
@@ -379,6 +389,73 @@ add_single_dir (struct dir_entry *entry, int verbose)
     dir_entries = entry;
   else if (ptr == NULL)
     prev->next = entry;
+  return added;
+}
+
+/* Check if PATH contains a "glibc-hwcaps" subdirectory.  If so, queue
+   its subdirectories for glibc-hwcaps processing.  */
+static void
+add_glibc_hwcaps_subdirectories (struct dir_entry *entry, const char *path)
+{
+  /* glibc-hwcaps subdirectories do not nest.  */
+  assert (entry->hwcaps == NULL);
+
+  char *glibc_hwcaps;
+  if (asprintf (&glibc_hwcaps, "%s/" GLIBC_HWCAPS_SUBDIRECTORY, path) < 0)
+    error (EXIT_FAILURE, errno, _("Could not form glibc-hwcaps path"));
+
+  DIR *dir = opendir (glibc_hwcaps);
+  if (dir != NULL)
+    {
+      while (true)
+	{
+	  errno = 0;
+	  struct dirent64 *e = readdir64 (dir);
+	  if (e == NULL)
+	    {
+	      if (errno == 0)
+		break;
+	      else
+		error (EXIT_FAILURE, errno, _("Listing directory %s"), path);
+	    }
+
+	  /* Ignore hidden subdirectories, including "." and "..", and
+	     regular files.  File names containing a ':' cannot be
+	     looked up by the dynamic loader, so skip those as
+	     well.  */
+	  if (e->d_name[0] == '.' || e->d_type == DT_REG
+	      || strchr (e->d_name, ':') != NULL)
+	    continue;
+
+	  /* See if this entry eventually resolves to a directory.  */
+	  struct stat64 st;
+	  if (fstatat64 (dirfd (dir), e->d_name, &st, 0) < 0)
+	    /* Ignore unreadable entries.  */
+	    continue;
+
+	  if (S_ISDIR (st.st_mode))
+	    {
+	      /* This is a directory, so it needs to be scanned for
+		 libraries, associated with the hwcaps implied by the
+		 subdirectory name.  */
+	      char *new_path;
+	      if (asprintf (&new_path, "%s/" GLIBC_HWCAPS_SUBDIRECTORY "/%s",
+			    /* Use non-canonicalized path here.  */
+			    entry->path, e->d_name) < 0)
+		error (EXIT_FAILURE, errno,
+		       _("Could not form glibc-hwcaps path"));
+	      struct dir_entry *new_entry = new_sub_entry (entry, new_path,
+							   &st);
+	      free (new_path);
+	      new_entry->hwcaps = new_glibc_hwcaps_subdirectory (e->d_name);
+	      add_single_dir (new_entry, 0);
+	    }
+	}
+
+      closedir (dir);
+    }
+
+  free (glibc_hwcaps);
 }
 
 /* Add one directory to the list of directories to process.  */
@@ -387,6 +464,7 @@ add_dir_1 (const char *line, const char *from_file, int from_line)
 {
   unsigned int i;
   struct dir_entry *entry = xmalloc (sizeof (struct dir_entry));
+  entry->hwcaps = NULL;
   entry->next = NULL;
 
   entry->from_file = strdup (from_file);
@@ -444,7 +522,9 @@ add_dir_1 (const char *line, const char *from_file, int from_line)
       entry->ino = stat_buf.st_ino;
       entry->dev = stat_buf.st_dev;
 
-      add_single_dir (entry, 1);
+      if (add_single_dir (entry, 1))
+	/* Add glibc-hwcaps subdirectories if present.  */
+	add_glibc_hwcaps_subdirectories (entry, path);
     }
 
   if (opt_chroot)
@@ -696,15 +776,27 @@ struct dlib_entry
 static void
 search_dir (const struct dir_entry *entry)
 {
-  uint64_t hwcap = path_hwcap (entry->path);
-  if (opt_verbose)
+  uint64_t hwcap;
+  if (entry->hwcaps == NULL)
     {
-      if (hwcap != 0)
-	printf ("%s: (hwcap: %#.16" PRIx64 ")", entry->path, hwcap);
-      else
-	printf ("%s:", entry->path);
-      printf (_(" (from %s:%d)\n"), entry->from_file, entry->from_line);
+      hwcap = path_hwcap (entry->path);
+      if (opt_verbose)
+	{
+	  if (hwcap != 0)
+	    printf ("%s: (hwcap: %#.16" PRIx64 ")", entry->path, hwcap);
+	  else
+	    printf ("%s:", entry->path);
+	}
     }
+  else
+    {
+      hwcap = 0;
+      if (opt_verbose)
+	printf ("%s: (hwcap: \"%s\")", entry->path,
+		glibc_hwcaps_subdirectory_name (entry->hwcaps));
+    }
+  if (opt_verbose)
+    printf (_(" (from %s:%d)\n"), entry->from_file, entry->from_line);
 
   char *dir_name;
   char *real_file_name;
@@ -746,13 +838,15 @@ search_dir (const struct dir_entry *entry)
 	  && direntry->d_type != DT_DIR)
 	continue;
       /* Does this file look like a shared library or is it a hwcap
-	 subdirectory?  The dynamic linker is also considered as
+	 subdirectory (if not already processing a glibc-hwcaps
+	 subdirectory)?  The dynamic linker is also considered as
 	 shared library.  */
       if (((strncmp (direntry->d_name, "lib", 3) != 0
 	    && strncmp (direntry->d_name, "ld-", 3) != 0)
 	   || strstr (direntry->d_name, ".so") == NULL)
 	  && (direntry->d_type == DT_REG
-	      || !is_hwcap_platform (direntry->d_name)))
+	      || (entry->hwcaps == NULL
+		  && !is_hwcap_platform (direntry->d_name))))
 	continue;
 
       size_t len = strlen (direntry->d_name);
@@ -838,7 +932,10 @@ search_dir (const struct dir_entry *entry)
       else
 	is_dir = S_ISDIR (lstat_buf.st_mode);
 
-      if (is_dir && is_hwcap_platform (direntry->d_name))
+      /* No descending into subdirectories if this directory is a
+	 glibc-hwcaps subdirectory (which are not recursive).  */
+      if (entry->hwcaps == NULL
+	  && is_dir && is_hwcap_platform (direntry->d_name))
 	{
 	  if (!is_link
 	      && direntry->d_type != DT_UNKNOWN
@@ -1029,13 +1126,31 @@ search_dir (const struct dir_entry *entry)
   struct dlib_entry *dlib_ptr;
   for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
     {
-      /* Don't create links to links.  */
-      if (dlib_ptr->is_link == 0)
-	create_links (dir_name, entry->path, dlib_ptr->name,
-		      dlib_ptr->soname);
+      /* The cached file name is the soname for non-glibc-hwcaps
+	 subdirectories (relying on symbolic links; this helps with
+	 library updates that change the file name), and the actual
+	 file for glibc-hwcaps subdirectories.  */
+      const char *filename;
+      if (entry->hwcaps == NULL)
+	{
+	  /* Don't create links to links.  */
+	  if (dlib_ptr->is_link == 0)
+	    create_links (dir_name, entry->path, dlib_ptr->name,
+			  dlib_ptr->soname);
+	  filename = dlib_ptr->soname;
+	}
+      else
+	{
+	  /* Do not create links in glibc-hwcaps subdirectories, but
+	     still log the cache addition.  */
+	  if (opt_verbose)
+	    printf ("\t%s -> %s\n", dlib_ptr->soname, dlib_ptr->name);
+	  filename = dlib_ptr->name;
+	}
       if (opt_build_cache)
-	add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag,
-		      dlib_ptr->osversion, hwcap);
+	add_to_cache (entry->path, filename, dlib_ptr->soname,
+		      dlib_ptr->flag, dlib_ptr->osversion,
+		      hwcap, entry->hwcaps);
     }
 
   /* Free all resources.  */
diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
index fec209509d..66b0312ac1 100644
--- a/sysdeps/generic/dl-cache.h
+++ b/sysdeps/generic/dl-cache.h
@@ -81,7 +81,6 @@ struct cache_file
 #define CACHE_VERSION "1.1"
 #define CACHEMAGIC_VERSION_NEW CACHEMAGIC_NEW CACHE_VERSION
 
-
 struct file_entry_new
 {
   union
@@ -99,6 +98,23 @@ struct file_entry_new
   uint64_t hwcap;		/* Hwcap entry.	 */
 };
 
+/* This bit in the hwcap field of struct file_entry_new indicates that
+   the lower 32 bits contain an index into the
+   cache_extension_tag_glibc_hwcaps section.  Older glibc versions do
+   not know about this HWCAP bit, so they will ignore these
+   entries.  */
+#define DL_CACHE_HWCAP_EXTENSION (1ULL << 62)
+
+/* Return true if the ENTRY->hwcap value indicates that
+   DL_CACHE_HWCAP_EXTENSION is used.  */
+static inline bool
+dl_cache_hwcap_extension (struct file_entry_new *entry)
+{
+  /* If DL_CACHE_HWCAP_EXTENSION is set, but other bits as well, this
+     is a different kind of extension.  */
+  return (entry->hwcap >> 32) == (DL_CACHE_HWCAP_EXTENSION >> 32);
+}
+
 /* See flags member of struct cache_file_new below.  */
 enum
   {
@@ -161,6 +177,17 @@ enum cache_extension_tag
       cache file.  */
    cache_extension_tag_generator,
 
+   /* glibc-hwcaps subdirectory information.  An array of uint32_t
+      values, which are indices into the string table.  The strings
+      are sorted lexicographically (according to strcmp).  The extra
+      level of indirection (instead of using string table indices
+      directly) allows the dynamic loader to compute the preference
+      order of the hwcaps names more efficiently.
+
+      For this section, 4-byte alignment is required, and the section
+      size must be a multiple of 4.  */
+   cache_extension_tag_glibc_hwcaps,
+
    /* Total number of known cache extension tags.  */
    cache_extension_count
   };
@@ -215,6 +242,27 @@ struct cache_extension_all_loaded
   struct cache_extension_loaded sections[cache_extension_count];
 };
 
+/* Performs basic data validation based on section tag, and removes
+   the sections which are invalid.  */
+static void
+cache_extension_verify (struct cache_extension_all_loaded *loaded)
+{
+  {
+    /* Section must not be empty, it must be aligned at 4 bytes, and
+       the size must be a multiple of 4.  */
+    struct cache_extension_loaded *hwcaps
+      = &loaded->sections[cache_extension_tag_glibc_hwcaps];
+    if (hwcaps->size == 0
+	|| ((uintptr_t) hwcaps->base % 4) != 0
+	|| (hwcaps->size % 4) != 0)
+      {
+	hwcaps->base = NULL;
+	hwcaps->size = 0;
+	hwcaps->flags = 0;
+      }
+  }
+}
+
 static bool __attribute__ ((unused))
 cache_extension_load (const struct cache_file_new *cache,
 		      const void *file_base, size_t file_size,
@@ -261,6 +309,7 @@ cache_extension_load (const struct cache_file_new *cache,
       loaded->sections[tag].size = ext->sections[i].size;
       loaded->sections[tag].flags = ext->sections[i].flags;
     }
+  cache_extension_verify (loaded);
   return true;
 }
 
diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h
index b64aab0064..30a76481aa 100644
--- a/sysdeps/generic/ldconfig.h
+++ b/sysdeps/generic/ldconfig.h
@@ -57,8 +57,22 @@ extern void init_cache (void);
 
 extern void save_cache (const char *cache_name);
 
-extern void add_to_cache (const char *path, const char *lib, int flags,
-			  unsigned int osversion, uint64_t hwcap);
+struct glibc_hwcaps_subdirectory;
+
+/* Return a struct describing the subdirectory for NAME.  Reuse an
+   existing struct if it exists.  */
+struct glibc_hwcaps_subdirectory *new_glibc_hwcaps_subdirectory
+  (const char *name);
+
+/* Returns the name that was specified when
+   add_glibc_hwcaps_subdirectory was called.  */
+const char *glibc_hwcaps_subdirectory_name
+  (struct glibc_hwcaps_subdirectory *);
+
+extern void add_to_cache (const char *path, const char *filename,
+			  const char *soname,
+			  int flags, unsigned int osversion, uint64_t hwcap,
+			  struct glibc_hwcaps_subdirectory *);
 
 extern void init_aux_cache (void);
 
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH 28/28] elf: Add glibc-hwcaps subdirectory support to ld.so cache processing
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (26 preceding siblings ...)
  2020-10-01 16:34 ` [PATCH 27/28] elf: Process glibc-hwcaps subdirectories in ldconfig Florian Weimer via Libc-alpha
@ 2020-10-01 16:34 ` Florian Weimer via Libc-alpha
  2020-10-01 16:50 ` [PATCH 00/28] glibc-hwcaps support H.J. Lu via Libc-alpha
  28 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:34 UTC (permalink / raw)
  To: libc-alpha

This recognizes the DL_CACHE_HWCAP_EXTENSION flag and picks up
the supported cache entry with the highest priority.
---
 elf/dl-cache.c  | 182 +++++++++++++++++++++++++++++++++++++++++++++++-
 elf/dl-hwcaps.c |  78 +++++++++++++++++++++
 elf/dl-hwcaps.h |  19 +++++
 3 files changed, 276 insertions(+), 3 deletions(-)

diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index 02c46ffb0c..4714119974 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -35,6 +35,132 @@ static struct cache_file *cache;
 static struct cache_file_new *cache_new;
 static size_t cachesize;
 
+#ifdef SHARED
+/* This is used to cache the priorities of glibc-hwcaps
+   subdirectories.  The elements of _dl_cache_priorities correspond to
+   the strings in the cache_extension_tag_glibc_hwcaps section.  */
+static uint32_t *glibc_hwcaps_priorities;
+static uint32_t glibc_hwcaps_priorities_length;
+static uint32_t glibc_hwcaps_priorities_allocated;
+
+/* True if the full malloc was used to allocated the array.  */
+static bool glibc_hwcaps_priorities_malloced;
+
+/* Deallocate the glibc_hwcaps_priorities array.  */
+static void
+glibc_hwcaps_priorities_free (void)
+{
+  /* When the minimal malloc is in use, free does not do anything,
+     so it does not make sense to call it.  */
+  if (glibc_hwcaps_priorities_malloced)
+    free (glibc_hwcaps_priorities);
+  glibc_hwcaps_priorities = NULL;
+  glibc_hwcaps_priorities_allocated = 0;
+}
+
+/* Return the priority of the cache_extension_tag_glibc_hwcaps section
+   entry at INDEX.  Zero means do not use.  Otherwise, lower values
+   indicate greater preference.  */
+static uint32_t __attribute__ ((noinline, noclone))
+glibc_hwcaps_priority (uint32_t index)
+{
+  /* Using a zero-length array as an indicator that nothing has been
+     loaded is not a problem: It does not lead to repeated
+     initialization attempts because caches without an extension
+     section are processed without calling this function (unless the
+     file is corrupted).  */
+  if (glibc_hwcaps_priorities_length == 0)
+    {
+      struct cache_extension_all_loaded ext;
+      if (!cache_extension_load (cache_new, cache, cachesize, &ext))
+	return 0;
+
+      uint32_t length
+	= (ext.sections[cache_extension_tag_glibc_hwcaps].size
+	   / sizeof (uint32_t));
+      if (length > glibc_hwcaps_priorities_allocated)
+	{
+	  glibc_hwcaps_priorities_free ();
+
+	  glibc_hwcaps_priorities = malloc (length * sizeof (uint32_t));
+	  if (glibc_hwcaps_priorities == NULL)
+	    /* Disable hwcaps on memory allocation error.  */
+	    return 0;
+
+	  glibc_hwcaps_priorities_allocated = length;
+	  glibc_hwcaps_priorities_malloced = __rtld_malloc_is_full ();
+	}
+
+      /* Compute the priorities for the subdirectories by merging the
+	 array in the cache with the dl_hwcaps_priorities array.  */
+      const uint32_t *left
+	= ext.sections[cache_extension_tag_glibc_hwcaps].base;
+      const uint32_t *left_end = left + length;
+      struct dl_hwcaps_priority *right = _dl_hwcaps_priorities;
+      struct dl_hwcaps_priority *right_end
+	= right + _dl_hwcaps_priorities_length;
+      uint32_t *result = glibc_hwcaps_priorities;
+
+      while (left < left_end && right < right_end)
+	{
+	  uint32_t string_table_index = *left;
+	  if (string_table_index < cachesize)
+	    {
+	      const char *left_name
+		= (const char *) cache + string_table_index;
+	      uint32_t left_name_length = strlen (left_name);
+	      uint32_t to_compare;
+	      if (left_name_length < right->name_length)
+		to_compare = left_name_length;
+	      else
+		to_compare = right->name_length;
+	      int cmp = memcmp (left_name, right->name, to_compare);
+	      if (cmp == 0)
+		{
+		  if (left_name_length < right->name_length)
+		    cmp = -1;
+		  else if (left_name_length > right->name_length)
+		    cmp = 1;
+		}
+	      if (cmp == 0)
+		{
+		  *result = right->priority;
+		  ++result;
+		  ++left;
+		  ++right;
+		}
+	      else if (cmp < 0)
+		{
+		  *result = 0;
+		  ++result;
+		  ++left;
+		}
+	      else
+		++right;
+	    }
+	  else
+	    {
+	      *result = 0;
+	      ++result;
+	    }
+	}
+      while (left < left_end)
+	{
+	  *result = 0;
+	  ++result;
+	  ++left;
+	}
+
+      glibc_hwcaps_priorities_length = length;
+    }
+
+  if (index < glibc_hwcaps_priorities_length)
+    return glibc_hwcaps_priorities[index];
+  else
+    return 0;
+}
+#endif /* SHARED */
+
 /* True if PTR is a valid string table index.  */
 static inline bool
 _dl_cache_verify_ptr (uint32_t ptr, size_t string_table_size)
@@ -74,6 +200,9 @@ search_cache (const char *string_table, uint32_t string_table_size,
   int left = 0;
   int right = nlibs - 1;
   const char *best = NULL;
+#ifdef SHARED
+  uint32_t best_priority = 0;
+#endif
 
   while (left <= right)
     {
@@ -129,6 +258,11 @@ search_cache (const char *string_table, uint32_t string_table_size,
 		{
 		  if (best == NULL || flags == GLRO (dl_correct_cache_id))
 		    {
+		      /* Named/extension hwcaps get slightly different
+			 treatment: We keep searching for a better
+			 match.  */
+		      bool named_hwcap = false;
+
 		      if (entry_size >= sizeof (struct file_entry_new))
 			{
 			  /* The entry is large enough to include
@@ -136,7 +270,18 @@ search_cache (const char *string_table, uint32_t string_table_size,
 			  struct file_entry_new *libnew
 			    = (struct file_entry_new *) lib;
 
-			  if (libnew->hwcap & hwcap_exclude)
+#ifdef SHARED
+			  named_hwcap = dl_cache_hwcap_extension (libnew);
+#endif
+
+			  /* The entries with named/extension hwcaps
+			     have been exhausted.  Return the best
+			     match encountered so far if there is
+			     one.  */
+			  if (!named_hwcap && best != NULL)
+			    break;
+
+			  if ((libnew->hwcap & hwcap_exclude) && !named_hwcap)
 			    continue;
 			  if (GLRO (dl_osversion)
 			      && libnew->osversion > GLRO (dl_osversion))
@@ -146,14 +291,41 @@ search_cache (const char *string_table, uint32_t string_table_size,
 			      && ((libnew->hwcap & _DL_HWCAP_PLATFORM)
 				  != platform))
 			    continue;
+
+#ifdef SHARED
+			  /* For named hwcaps, determine the priority
+			     and see if beats what has been found so
+			     far.  */
+			  if (named_hwcap)
+			    {
+			      uint32_t entry_priority
+				= glibc_hwcaps_priority (libnew->hwcap);
+			      if (entry_priority == 0)
+				/* Not usable at all.  Skip.  */
+				continue;
+			      else if (best == NULL
+				       || entry_priority < best_priority)
+				/* This entry is of higher priority
+				   than the previous one, or it is the
+				   first entry.  */
+				best_priority = entry_priority;
+			      else
+				/* An entry has already been found,
+				   but it is a better match.  */
+				continue;
+			    }
+#endif /* SHARED */
 			}
 
 		      best = string_table + lib->value;
 
-		      if (flags == GLRO (dl_correct_cache_id))
+		      if (flags == GLRO (dl_correct_cache_id)
+			  && !named_hwcap)
 			/* We've found an exact match for the shared
 			   object and no general `ELF' release.  Stop
-			   searching.  */
+			   searching, but not if a named (extension)
+			   hwcap is used.  In this case, an entry with
+			   a higher priority may come up later.  */
 			break;
 		    }
 		}
@@ -346,5 +518,9 @@ _dl_unload_cache (void)
       __munmap (cache, cachesize);
       cache = NULL;
     }
+#ifdef SHARED
+  /* This marks the glibc_hwcaps_priorities array as out-of-date.  */
+  glibc_hwcaps_priorities_length = 0;
+#endif
 }
 #endif
diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c
index 4de94759a2..cb53337025 100644
--- a/elf/dl-hwcaps.c
+++ b/elf/dl-hwcaps.c
@@ -89,6 +89,81 @@ copy_hwcaps (struct copy_hwcaps *target, const char *hwcaps,
     }
 }
 
+struct dl_hwcaps_priority *_dl_hwcaps_priorities;
+uint32_t _dl_hwcaps_priorities_length;
+
+/* Allocate _dl_hwcaps_priorities and fill it with data.  */
+static void
+compute_priorities (size_t total_count, const char *prepend,
+		    int32_t bitmask, const char *mask)
+{
+  _dl_hwcaps_priorities = malloc (total_count
+				  * sizeof (*_dl_hwcaps_priorities));
+  if (_dl_hwcaps_priorities == NULL)
+    _dl_signal_error (ENOMEM, NULL, NULL,
+		      N_("cannot create HWCAP priorities"));
+  _dl_hwcaps_priorities_length = total_count;
+
+  /* First the prepended subdirectories.  */
+  size_t i = 0;
+  {
+    struct dl_hwcaps_split sp;
+    _dl_hwcaps_split_init (&sp, prepend);
+    while (_dl_hwcaps_split (&sp))
+      {
+	_dl_hwcaps_priorities[i].name = sp.segment;
+	_dl_hwcaps_priorities[i].name_length = sp.length;
+	_dl_hwcaps_priorities[i].priority = i + 1;
+	++i;
+      }
+  }
+
+  /* Then the built-in subdirectories that are actually active.  */
+  {
+    struct dl_hwcaps_split_masked sp;
+    _dl_hwcaps_split_masked_init (&sp, _dl_hwcaps_subdirs, bitmask, mask);
+    while (_dl_hwcaps_split_masked (&sp))
+      {
+	_dl_hwcaps_priorities[i].name = sp.split.segment;
+	_dl_hwcaps_priorities[i].name_length = sp.split.length;
+	_dl_hwcaps_priorities[i].priority = i + 1;
+	++i;
+      }
+  }
+  assert (i == total_count);
+}
+
+/* Sort the _dl_hwcaps_priorities array by name.  */
+static void
+sort_priorities_by_name (void)
+{
+  /* Insertion sort.  There is no need to link qsort into the dynamic
+     loader for such a short array.  */
+  for (size_t i = 1; i < _dl_hwcaps_priorities_length; ++i)
+    for (size_t j = i; j > 0; --j)
+      {
+	struct dl_hwcaps_priority *previous = _dl_hwcaps_priorities + j - 1;
+	struct dl_hwcaps_priority *current = _dl_hwcaps_priorities + j;
+
+	/* Bail out if current is greater or equal to the previous
+	   value.  */
+	uint32_t to_compare;
+	if (current->name_length < previous->name_length)
+	  to_compare = current->name_length;
+	else
+	  to_compare = previous->name_length;
+	int cmp = memcmp (current->name, previous->name, to_compare);
+	if (cmp >= 0
+	    || (cmp == 0 && current->name_length >= previous->name_length))
+	  break;
+
+	/* Swap *previous and *current.  */
+	struct dl_hwcaps_priority tmp = *previous;
+	*previous = *current;
+	*current = tmp;
+      }
+}
+
 /* Return an array of useful/necessary hardware capability names.  */
 const struct r_strlenpair *
 _dl_important_hwcaps (const char *glibc_hwcaps_prepend,
@@ -111,6 +186,9 @@ _dl_important_hwcaps (const char *glibc_hwcaps_prepend,
   count_hwcaps (&hwcaps_counts, glibc_hwcaps_prepend, -1, NULL);
   count_hwcaps (&hwcaps_counts, _dl_hwcaps_subdirs, hwcaps_subdirs_active,
 		glibc_hwcaps_mask);
+  compute_priorities (hwcaps_counts.count, glibc_hwcaps_prepend,
+		      hwcaps_subdirs_active, glibc_hwcaps_mask);
+  sort_priorities_by_name ();
 
   /* Each hwcaps subdirectory has a GLIBC_HWCAPS_PREFIX string prefix
      and a "/" suffix once stored in the result.  */
diff --git a/elf/dl-hwcaps.h b/elf/dl-hwcaps.h
index a6453f15f3..4b4c143854 100644
--- a/elf/dl-hwcaps.h
+++ b/elf/dl-hwcaps.h
@@ -110,4 +110,23 @@ extern const char _dl_hwcaps_subdirs[] attribute_hidden;
    bitmask.  */
 int32_t _dl_hwcaps_subdirs_active (void) attribute_hidden;
 
+/* Pre-computed glibc-hwcaps subdirectory priorities.  Used in
+   dl-cache.c to quickly find the proprities for the stored HWCAP
+   names.  */
+struct dl_hwcaps_priority
+{
+  /* The name consists of name_length bytes at name (not necessarily
+     null-terminated).  */
+  const char *name;
+  uint32_t name_length;
+
+  /* Priority of this name.  A positive number.  */
+  uint32_t priority;
+};
+
+/* Pre-computed hwcaps priorities.  Set up by
+   _dl_important_hwcaps.  */
+extern struct dl_hwcaps_priority *_dl_hwcaps_priorities attribute_hidden;
+extern uint32_t _dl_hwcaps_priorities_length attribute_hidden;
+
 #endif /* _DL_HWCAPS_H */
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* Re: [PATCH 00/28] glibc-hwcaps support
  2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
                   ` (27 preceding siblings ...)
  2020-10-01 16:34 ` [PATCH 28/28] elf: Add glibc-hwcaps subdirectory support to ld.so cache processing Florian Weimer via Libc-alpha
@ 2020-10-01 16:50 ` H.J. Lu via Libc-alpha
  2020-10-01 16:54   ` Florian Weimer via Libc-alpha
  28 siblings, 1 reply; 103+ messages in thread
From: H.J. Lu via Libc-alpha @ 2020-10-01 16:50 UTC (permalink / raw)
  To: Florian Weimer; +Cc: GNU C Library

On Thu, Oct 1, 2020 at 9:32 AM Florian Weimer via Libc-alpha
<libc-alpha@sourceware.org> wrote:
>
> This is a rebase of the previous patch series.  I adjusted it for the
> --argv0 ld.so change, added POWER10 support, and switched to the real
> subdirectory names in the x86-64 psABI.
>
> I think the patches up to “elf: Enhance ld.so --help to print HWCAP
> subdirectories” are useful on their own because they add diagnostics for
> existing functionality.  The ld.so.cache extension mechanism and the
> string table are also fairly independent parts.
>
> What's still missing?
>
> * documentation (including NEWS file updates)
> * negative caching of missing glibc-hwcaps subdirectories (in bulk)
> * tests
>
> I would like to work on these aspects in parallel to the patch reviews.
> (Lack of negative caching makes it obvious what's going on with strace,
> so it's perhaps even helpful that it's currently missing.)
>
> Support in GCC has landed earlier this week:
>
>   <https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=324bec558e95584e>
>
> I filed a separate LLVM bug, but do not plan to work on it for now:
>
>   <https://bugs.llvm.org/show_bug.cgi?id=47686>
>
> Thanks,
> Florian

Do you have a public git branch to share?

-- 
H.J.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 00/28] glibc-hwcaps support
  2020-10-01 16:50 ` [PATCH 00/28] glibc-hwcaps support H.J. Lu via Libc-alpha
@ 2020-10-01 16:54   ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-01 16:54 UTC (permalink / raw)
  To: H.J. Lu; +Cc: GNU C Library

* H. J. Lu:

> Do you have a public git branch to share?

Sure, I've pushed the series to fw/glibc-hwcaps on sourceware.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 01/28] elf: Do not search HWCAP subdirectories in statically linked binaries
  2020-10-01 16:31 ` [PATCH 01/28] elf: Do not search HWCAP subdirectories in statically linked binaries Florian Weimer via Libc-alpha
@ 2020-10-01 18:22   ` Adhemerval Zanella via Libc-alpha
  2020-10-01 18:24     ` Carlos O'Donell via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-01 18:22 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:31, Florian Weimer via Libc-alpha wrote:
> 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.

This change looks reasonable, although it makes the semantic of 
statically linked programs slight different than dynamic one regarding
dlopen.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  NEWS          |  4 ++++
>  elf/Makefile  |  4 ++--
>  elf/dl-load.c | 14 ++++++++++++++
>  3 files changed, 20 insertions(+), 2 deletions(-)
> 
> diff --git a/NEWS b/NEWS
> index ce05d05b16..902fa3a7f8 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -33,6 +33,10 @@ Deprecated and removed features, and other changes affecting compatibility:
>  * The mallinfo function is marked deprecated.  Callers should call
>    mallinfo2 instead.
>  
> +* 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:
>  
>    [Add changes to build and runtime requirements here]

Ok.

> diff --git a/elf/Makefile b/elf/Makefile
> index c587e9f06e..e0a8bf2998 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 \

Ok.

> @@ -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

Ok.

> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index 646c5dca40..5ba117d597 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

Ok.

> @@ -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 **)
> @@ -1521,11 +1527,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;
> @@ -1886,11 +1896,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))
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 02/28] elf: Implement __rtld_malloc_is_full
  2020-10-01 16:31 ` [PATCH 02/28] elf: Implement __rtld_malloc_is_full Florian Weimer via Libc-alpha
@ 2020-10-01 18:23   ` Adhemerval Zanella via Libc-alpha
  2020-10-08  9:44     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-01 18:23 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:31, Florian Weimer via Libc-alpha wrote:
> 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.

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  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;
> +}
> +

The 'full' is somewhat confusing, maybe 'complete'?

>  /* 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.  */
> 

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 01/28] elf: Do not search HWCAP subdirectories in statically linked binaries
  2020-10-01 18:22   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-01 18:24     ` Carlos O'Donell via Libc-alpha
  2020-10-01 18:29       ` Adhemerval Zanella via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Carlos O'Donell via Libc-alpha @ 2020-10-01 18:24 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha

On 10/1/20 2:22 PM, Adhemerval Zanella via Libc-alpha wrote:
> 
> 
> On 01/10/2020 13:31, Florian Weimer via Libc-alpha wrote:
>> 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.
> 
> This change looks reasonable, although it makes the semantic of 
> statically linked programs slight different than dynamic one regarding
> dlopen.

I think this is OK.

dlopen from statically linked programs is deprecated, the only uses
should be internal to glibc in NSS And iconv.

We really really want:

* Just a statically linked program that has no external deps.

* Just a dynamically linked program can support dlopen.

This half-way inbetween support we have has serious consequences,
and is the reason I supported us deprecating dlopen from static
binaries. Users should not be allowed to shoot themselves in
the foot.

$0.02.

-- 
Cheers,
Carlos.


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 01/28] elf: Do not search HWCAP subdirectories in statically linked binaries
  2020-10-01 18:24     ` Carlos O'Donell via Libc-alpha
@ 2020-10-01 18:29       ` Adhemerval Zanella via Libc-alpha
  2020-10-01 20:24         ` Carlos O'Donell via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-01 18:29 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha



On 01/10/2020 15:24, Carlos O'Donell wrote:
> On 10/1/20 2:22 PM, Adhemerval Zanella via Libc-alpha wrote:
>>
>>
>> On 01/10/2020 13:31, Florian Weimer via Libc-alpha wrote:
>>> 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.
>>
>> This change looks reasonable, although it makes the semantic of 
>> statically linked programs slight different than dynamic one regarding
>> dlopen.
> 
> I think this is OK.
> 
> dlopen from statically linked programs is deprecated, the only uses
> should be internal to glibc in NSS And iconv.
> 
> We really really want:
> 
> * Just a statically linked program that has no external deps.
> 
> * Just a dynamically linked program can support dlopen.
> 
> This half-way inbetween support we have has serious consequences,
> and is the reason I supported us deprecating dlopen from static
> binaries. Users should not be allowed to shoot themselves in
> the foot.
> 
> $0.02.
> 

I agree, usually on discussion about static linking with glibc users
get perplexed why shared libraries are being loaded under the hood
and usually this is a deal breaker on most usercases (another one
is the minimum kernel requirement).

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 18/28] powerpc64le: Add glibc-hwcaps support
  2020-10-01 16:33 ` [PATCH 18/28] powerpc64le: " Florian Weimer via Libc-alpha
@ 2020-10-01 18:56   ` Paul A. Clarke via Libc-alpha
  2020-10-05  9:47     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Paul A. Clarke via Libc-alpha @ 2020-10-01 18:56 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On Thu, Oct 01, 2020 at 06:33:29PM +0200, Florian Weimer via Libc-alpha wrote:
> The "power10" and "power9" subdirectories are selected.
> ---
>  .../powerpc/powerpc64/le/dl-hwcaps-subdirs.c  | 34 +++++++++++++++++++
>  1 file changed, 34 insertions(+)
>  create mode 100644 sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
> 
> diff --git a/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
> new file mode 100644
> index 0000000000..496daf0fa0
> --- /dev/null
> +++ b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
> @@ -0,0 +1,34 @@
> +/* Architecture-specific glibc-hwcaps subdirectories.  powerpc64le 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 <ldsodefs.h>
> +
> +const char _dl_hwcaps_subdirs[] = "power10:power9";
> +
> +int32_t

Is 32 bits enough?  Any reason not to make this 64 bits?

> +_dl_hwcaps_subdirs_active (void)
> +{
> +  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
> +    return 3;
> +
> +  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
> +    return 1;

Is there some way to tie these magic numbers closer to their meaning?
The bits are also inverse the the relative offset in the string,
which could be confusing (index 0 is the 2nd bit and index 1 is the 1st).

Perhaps something like (not tested):
--
const char * const _dl_hwcaps_subdirs[] = {
#define _DL_HWCAPS_SUBDIR_POWER10_BIT 0x2 /* or 1 to preserve same order.  */
	"power10",
#define _DL_HWCAPS_SUBDIR_POWER9_BIT 0x1 /* or 2.  */
	"power9"
};

int32_t
_dl_hwcaps_subdirs_active (void)
{
  int32_t result = 0;

  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
    result |= _DL_HWCAPS_SUBDIR_POWER10_BIT;

  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
    result |= _DL_HWCAPS_SUBDIR_POWER9_BIT;

  return result;
}
--

Of course, that would require changes to the code that parses
_dl_hwcaps_subdirs.

> +
> +  return 0;
> +}

PC

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 01/28] elf: Do not search HWCAP subdirectories in statically linked binaries
  2020-10-01 18:29       ` Adhemerval Zanella via Libc-alpha
@ 2020-10-01 20:24         ` Carlos O'Donell via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Carlos O'Donell via Libc-alpha @ 2020-10-01 20:24 UTC (permalink / raw)
  To: Adhemerval Zanella, libc-alpha

On 10/1/20 2:29 PM, Adhemerval Zanella wrote:
> 
> 
> On 01/10/2020 15:24, Carlos O'Donell wrote:
>> On 10/1/20 2:22 PM, Adhemerval Zanella via Libc-alpha wrote:
>>>
>>>
>>> On 01/10/2020 13:31, Florian Weimer via Libc-alpha wrote:
>>>> 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.
>>>
>>> This change looks reasonable, although it makes the semantic of 
>>> statically linked programs slight different than dynamic one regarding
>>> dlopen.
>>
>> I think this is OK.
>>
>> dlopen from statically linked programs is deprecated, the only uses
>> should be internal to glibc in NSS And iconv.
>>
>> We really really want:
>>
>> * Just a statically linked program that has no external deps.
>>
>> * Just a dynamically linked program can support dlopen.
>>
>> This half-way inbetween support we have has serious consequences,
>> and is the reason I supported us deprecating dlopen from static
>> binaries. Users should not be allowed to shoot themselves in
>> the foot.
>>
>> $0.02.
>>
> 
> I agree, usually on discussion about static linking with glibc users
> get perplexed why shared libraries are being loaded under the hood
> and usually this is a deal breaker on most usercases (another one
> is the minimum kernel requirement).
 Fixing the dlopen case requires splitting the interface at the NSS
boundary (as Florian suggested?) and calling out from the static
binary to some kind of 'getent'-like binary, perhaps even getent
itself in compat cases.

The minimum kernel requirement is much easier to explain. We have
a minimum requirement. I find users get that when it's explained
clearly.

-- 
Cheers,
Carlos.


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 18/28] powerpc64le: Add glibc-hwcaps support
  2020-10-01 18:56   ` Paul A. Clarke via Libc-alpha
@ 2020-10-05  9:47     ` Florian Weimer via Libc-alpha
  2020-10-05 19:15       ` Paul A. Clarke via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-05  9:47 UTC (permalink / raw)
  To: Paul A. Clarke; +Cc: libc-alpha

* Paul A. Clarke:

>> diff --git a/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
>> new file mode 100644
>> index 0000000000..496daf0fa0
>> --- /dev/null
>> +++ b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
>> @@ -0,0 +1,34 @@
>> +/* Architecture-specific glibc-hwcaps subdirectories.  powerpc64le 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 <ldsodefs.h>
>> +
>> +const char _dl_hwcaps_subdirs[] = "power10:power9";
>> +
>> +int32_t
>
> Is 32 bits enough?  Any reason not to make this 64 bits?

It's enough for now for all the targets that have glibc-hwcaps support
so far.  It's a strictly internal interface, so it's easy to change
later.

We also need to consider the impact on library path processing.  Even
just 20 additional open & stat calls for each LD_LIBRARY_PATH entry
might be quite visible in process startup benchmarks.  So I'm not sure
if we will ever use those 32 bits.

>> +_dl_hwcaps_subdirs_active (void)
>> +{
>> +  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
>> +    return 3;
>> +
>> +  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
>> +    return 1;
>
> Is there some way to tie these magic numbers closer to their meaning?

Would you please have a look at the the x86-64 implementation and check
if it is closer to your liking?

> Perhaps something like (not tested):
> --
> const char * const _dl_hwcaps_subdirs[] = {
> #define _DL_HWCAPS_SUBDIR_POWER10_BIT 0x2 /* or 1 to preserve same order.  */
> 	"power10",
> #define _DL_HWCAPS_SUBDIR_POWER9_BIT 0x1 /* or 2.  */
> 	"power9"
> };
>
> int32_t
> _dl_hwcaps_subdirs_active (void)
> {
>   int32_t result = 0;
>
>   if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
>     result |= _DL_HWCAPS_SUBDIR_POWER10_BIT;
>
>   if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
>     result |= _DL_HWCAPS_SUBDIR_POWER9_BIT;
>
>   return result;
> }
> --
>
> Of course, that would require changes to the code that parses
> _dl_hwcaps_subdirs.

I chose the current approach to avoid relocations and memory allocations
for processing hwcaps settings (e.g. from the ld.so command line).

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* RE: [PATCH 18/28] powerpc64le: Add glibc-hwcaps support
  2020-10-05  9:47     ` Florian Weimer via Libc-alpha
@ 2020-10-05 19:15       ` Paul A. Clarke via Libc-alpha
  2020-10-06 12:20         ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Paul A. Clarke via Libc-alpha @ 2020-10-05 19:15 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On Mon, Oct 05, 2020 at 11:47:32AM +0200, Florian Weimer wrote:
> * Paul A. Clarke:
> >> diff --git a/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
> >> new file mode 100644
> >> index 0000000000..496daf0fa0
> >> --- /dev/null
> >> +++ b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
[...snip...]
> >> +_dl_hwcaps_subdirs_active (void)
> >> +{
> >> +  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
> >> +    return 3;
> >> +
> >> +  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
> >> +    return 1;
> >
> > Is there some way to tie these magic numbers closer to their meaning?
> 
> Would you please have a look at the the x86-64 implementation and check
> if it is closer to your liking?

A bit. I am concerned about the opaqueness of the interface.
The x86 code basically does:

>  int32_t result = 0;
>  int32_t bit = 1 << 2;
>  if (...)
>    return result;
>  result |= bit;
>  bit >>= 1;
>  if (...)
>    return result;
>  result |= bit;
>  bit >>= 1;
>  if (...)
>    return result;
>  result |= bit;
>  bit >>= 1;
>  return result;

...which still creates magic numbers and really doesn't explain what the
result represents.
The entire API is documented in a 2-line comment before count_hwcaps().
For someone to update the implementation for a new hwcap, which will
need to be done fairly often, seems to me to be a bit challenging for
the uninitiated (like me ;-).

> > Perhaps something like (not tested):
> > --
> > const char * const _dl_hwcaps_subdirs[] = {
> > #define _DL_HWCAPS_SUBDIR_POWER10_BIT 0x2 /* or 1 to preserve same order.  */
> > 	"power10",
> > #define _DL_HWCAPS_SUBDIR_POWER9_BIT 0x1 /* or 2.  */
> > 	"power9"
> > };
> >
> > int32_t
> > _dl_hwcaps_subdirs_active (void)
> > {
> >   int32_t result = 0;
> >
> >   if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
> >     result |= _DL_HWCAPS_SUBDIR_POWER10_BIT;
> >
> >   if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
> >     result |= _DL_HWCAPS_SUBDIR_POWER9_BIT;
> >
> >   return result;
> > }
> > --
> >
> > Of course, that would require changes to the code that parses
> > _dl_hwcaps_subdirs.
> 
> I chose the current approach to avoid relocations and memory allocations
> for processing hwcaps settings (e.g. from the ld.so command line).

Does the "array of strings" approach introduce new/additional relocations
and memory allocations?  It would seem to avoid the need for the splitting
code, at least.

PC

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 03/28] elf: Implement _dl_write
  2020-10-01 16:31 ` [PATCH 03/28] elf: Implement _dl_write Florian Weimer via Libc-alpha
@ 2020-10-05 19:46   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-05 19:46 UTC (permalink / raw)
  To: libc-alpha, Florian Weimer



On 01/10/2020 13:31, Florian Weimer via Libc-alpha wrote:
> 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.

I think we can in fact replace the _dl_writev usage by _dl_write
with a bonus of a possible code size reduction (since it will be
replace by a function call).

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  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 e0a8bf2998..ab792d45c2 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

Ok.

> 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;
> +}

Ok.

> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index ba114ab4b1..7cb1fccc80 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -757,6 +757,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, ...)

Ok.

> 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;
> +}
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 18/28] powerpc64le: Add glibc-hwcaps support
  2020-10-05 19:15       ` Paul A. Clarke via Libc-alpha
@ 2020-10-06 12:20         ` Florian Weimer via Libc-alpha
  2020-10-06 17:45           ` Paul A. Clarke via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-06 12:20 UTC (permalink / raw)
  To: Paul A. Clarke; +Cc: libc-alpha

* Paul A. Clarke:

> On Mon, Oct 05, 2020 at 11:47:32AM +0200, Florian Weimer wrote:
>> * Paul A. Clarke:
>> >> diff --git a/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
>> >> new file mode 100644
>> >> index 0000000000..496daf0fa0
>> >> --- /dev/null
>> >> +++ b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
> [...snip...]
>> >> +_dl_hwcaps_subdirs_active (void)
>> >> +{
>> >> +  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
>> >> +    return 3;
>> >> +
>> >> +  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
>> >> +    return 1;

Meh, the second test should do: return 2; 8-(

Maybe we should write it this way:

  if ((GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)) == 0)
    return 0;  /* No subdirectories active.  */

  if ((GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)) == 0)
    return 2;  /* Only the second directory (power9) is active.  */

  return 3;  /* Both directories (power10, power9) are active.  */

> ...which still creates magic numbers and really doesn't explain what the
> result represents.
> The entire API is documented in a 2-line comment before count_hwcaps().
> For someone to update the implementation for a new hwcap, which will
> need to be done fairly often, seems to me to be a bit challenging for
> the uninitiated (like me ;-).

The comments are in elf/dl-hwcaps.h:

/* 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;

>> > Perhaps something like (not tested):
>> > --
>> > const char * const _dl_hwcaps_subdirs[] = {
>> > #define _DL_HWCAPS_SUBDIR_POWER10_BIT 0x2 /* or 1 to preserve same order.  */
>> > 	"power10",
>> > #define _DL_HWCAPS_SUBDIR_POWER9_BIT 0x1 /* or 2.  */
>> > 	"power9"
>> > };
>> >
>> > int32_t
>> > _dl_hwcaps_subdirs_active (void)
>> > {
>> >   int32_t result = 0;
>> >
>> >   if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
>> >     result |= _DL_HWCAPS_SUBDIR_POWER10_BIT;
>> >
>> >   if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
>> >     result |= _DL_HWCAPS_SUBDIR_POWER9_BIT;
>> >
>> >   return result;
>> > }
>> > --
>> >
>> > Of course, that would require changes to the code that parses
>> > _dl_hwcaps_subdirs.
>> 
>> I chose the current approach to avoid relocations and memory allocations
>> for processing hwcaps settings (e.g. from the ld.so command line).
>
> Does the "array of strings" approach introduce new/additional relocations
> and memory allocations?  It would seem to avoid the need for the splitting
> code, at least.

The string pointers need runtime relocations (although they probably do
not matter in the grand scheme of things).  We still need the splitting
code for the ld.so command line argument.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 18/28] powerpc64le: Add glibc-hwcaps support
  2020-10-06 12:20         ` Florian Weimer via Libc-alpha
@ 2020-10-06 17:45           ` Paul A. Clarke via Libc-alpha
  2020-10-09  9:06             ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Paul A. Clarke via Libc-alpha @ 2020-10-06 17:45 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On Tue, Oct 06, 2020 at 02:20:21PM +0200, Florian Weimer via Libc-alpha wrote:
> * Paul A. Clarke:
> > On Mon, Oct 05, 2020 at 11:47:32AM +0200, Florian Weimer wrote:
> >> * Paul A. Clarke:
> >> >> diff --git a/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
> >> >> new file mode 100644
> >> >> index 0000000000..496daf0fa0
> >> >> --- /dev/null
> >> >> +++ b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c
> > [...snip...]
> >> >> +_dl_hwcaps_subdirs_active (void)
> >> >> +{
> >> >> +  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
> >> >> +    return 3;
> >> >> +
> >> >> +  if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
> >> >> +    return 1;
> 
> Meh, the second test should do: return 2; 8-(
> 
> Maybe we should write it this way:
> 
>   if ((GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)) == 0)
>     return 0;  /* No subdirectories active.  */
> 
>   if ((GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)) == 0)
>     return 2;  /* Only the second directory (power9) is active.  */
> 
>   return 3;  /* Both directories (power10, power9) are active.  */
> 
> > ...which still creates magic numbers and really doesn't explain what the
> > result represents.

Even with that rewrite, the "magic numbers" are still there.

> > The entire API is documented in a 2-line comment before count_hwcaps().
> > For someone to update the implementation for a new hwcap, which will
> > need to be done fairly often, seems to me to be a bit challenging for
> > the uninitiated (like me ;-).
> 
> The comments are in elf/dl-hwcaps.h:
> 
> /* 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;
> 
> >> > Perhaps something like (not tested):
> >> > --
> >> > const char * const _dl_hwcaps_subdirs[] = {
> >> > #define _DL_HWCAPS_SUBDIR_POWER10_BIT 0x2 /* or 1 to preserve same order.  */
> >> > 	"power10",
> >> > #define _DL_HWCAPS_SUBDIR_POWER9_BIT 0x1 /* or 2.  */
> >> > 	"power9"
> >> > };
> >> >
> >> > int32_t
> >> > _dl_hwcaps_subdirs_active (void)
> >> > {
> >> >   int32_t result = 0;
> >> >
> >> >   if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1)
> >> >     result |= _DL_HWCAPS_SUBDIR_POWER10_BIT;
> >> >
> >> >   if (GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00)
> >> >     result |= _DL_HWCAPS_SUBDIR_POWER9_BIT;
> >> >
> >> >   return result;
> >> > }
> >> > --
> >> >
> >> > Of course, that would require changes to the code that parses
> >> > _dl_hwcaps_subdirs.
> >> 
> >> I chose the current approach to avoid relocations and memory allocations
> >> for processing hwcaps settings (e.g. from the ld.so command line).
> >
> > Does the "array of strings" approach introduce new/additional relocations
> > and memory allocations?  It would seem to avoid the need for the splitting
> > code, at least.
> 
> The string pointers need runtime relocations (although they probably do
> not matter in the grand scheme of things).  We still need the splitting
> code for the ld.so command line argument.

Ah, so you are trying to model the "hwcaps subdirs" on the command-line
argument.  The latter has to be split to be used, why not allow the
subdirs to be "pre-split", then both could be handled as an array
of strings?  The array for the subdirs could be pre-allocated (if it will
always be less than 32 entries) and filled in directly
during "active" processing, obviating the need for the mysterious
and ephemeral bits indicating "use this"/"don't use this".

PC

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 04/28] elf: Extract command-line/environment variables state from rtld.c
  2020-10-01 16:31 ` [PATCH 04/28] elf: Extract command-line/environment variables state from rtld.c Florian Weimer via Libc-alpha
@ 2020-10-06 20:45   ` Adhemerval Zanella via Libc-alpha
  2020-10-08 11:32     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-06 20:45 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:31, Florian Weimer via Libc-alpha wrote:
> 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.

LGTM, with some nits below.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  elf/dl-main.h |  95 +++++++++++++++++++++++++++++++
>  elf/rtld.c    | 155 +++++++++++++++++++-------------------------------
>  2 files changed, 154 insertions(+), 96 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
> +

Ok.

> +/* 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];
> +};
> +

Ok.

> +/* This is a list of all the modes the dynamic loader can be in.  */
> +enum mode { normal, list, verify, trace };
> +

With this enum now outside rtld.c maybe it would be better to use more
explicit identifiers?

> +/* 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;
> +};

Ok.

> +
> +/* 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 */

Ok.

> diff --git a/elf/rtld.c b/elf/rtld.c
> index 9918fda05e..c1153cb627 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
> -

Ok.

> -/* 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];
> -};
> -

Ok.

>  /* 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;
> @@ -316,6 +281,18 @@ audit_list_count (struct audit_list *list)
>    return naudit;
>  }
>  

Ok.

> +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;
> +}
> +

Ok.

>  #ifndef HAVE_INLINED_SYSCALLS
>  /* Set nonzero during loading and initialization of executable and
>     libraries, cleared before the executable's entry point runs.  This
> @@ -900,15 +877,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;
> -

Ok.

>  /* 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.
> @@ -1150,7 +1118,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;
> @@ -1160,8 +1127,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;
>  

Ok.

> @@ -1176,7 +1143,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.  */

Ok.

> @@ -1210,7 +1177,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;
> @@ -1219,7 +1186,7 @@ dl_main (const ElfW(Phdr) *phdr,
>  	  }
>  	else if (! strcmp (_dl_argv[1], "--verify"))
>  	  {
> -	    mode = verify;
> +	    state.mode = verify;
>  
>  	    ++_dl_skip_args;
>  	    --_dl_argc;
> @@ -1235,7 +1202,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;

Ok.

> @@ -1252,7 +1219,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;
> @@ -1260,7 +1227,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;

Ok.

> @@ -1328,7 +1295,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;

Use __glibc_likely here.

> @@ -1357,7 +1324,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])

Ditto.

> @@ -1604,7 +1571,7 @@ of this helper program; chances are you did not intend to run this program.\n\
>        _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

Ditto.

> @@ -1634,7 +1601,7 @@ of this helper program; chances are you did not intend to run this program.\n\
>  
>    /* 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,

Ok.

> @@ -1699,14 +1666,14 @@ of this helper program; chances are you did not intend to run this program.\n\
>      /* 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)
>      {
> -      size_t naudit = audit_list_count (&audit_list);
> +      size_t naudit = audit_list_count (&state.audit_list);
>  
>        /* Since we start using the auditing DSOs right away we need to
>  	 initialize the data structures now.  */
> @@ -1719,7 +1686,7 @@ of this helper program; chances are you did not intend to run this program.\n\
>        security_init ();
>        need_security_init = false;
>  
> -      load_audit_modules (main_map, &audit_list);
> +      load_audit_modules (main_map, &state.audit_list);
>  
>        /* The count based on audit strings may overestimate the number
>  	 of audit modules that got loaded, but not underestimate.  */

Ok.

> @@ -1774,19 +1741,21 @@ of this helper program; chances are you did not intend to run this program.\n\
>    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);
>      }
>  

Ok.

> @@ -1893,7 +1862,8 @@ of this helper program; chances are you did not intend to run this program.\n\
>    {
>      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);
>    }
>  
> @@ -1920,7 +1890,7 @@ of this helper program; chances are you did not intend to run this program.\n\
>        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]

Ok.

> @@ -1953,8 +1923,8 @@ of this helper program; chances are you did not intend to run this program.\n\
>       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);
>    }
>  

Ok.

> @@ -1974,7 +1944,7 @@ of this helper program; chances are you did not intend to run this program.\n\
>         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

Use __glibc_likely.

> @@ -2076,7 +2046,7 @@ of this helper program; chances are you did not intend to run this program.\n\
>  			  (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;
> @@ -2130,7 +2100,7 @@ of this helper program; chances are you did not intend to run this program.\n\
>  		}
>  	    }
>  #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.  */
> @@ -2492,13 +2462,10 @@ print_missing_version (int errcode __attribute__ ((unused)),
>  		    objname, errstring);
>  }
>  \f
> -/* 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.  */

Ok.

> @@ -2555,7 +2522,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;
>  	      }
>  
> @@ -2609,11 +2576,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.  */
> @@ -2645,25 +2611,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;
>  	    }
>  

Ok.

> @@ -2712,7 +2678,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;
>  	    }
>  
> @@ -2754,7 +2720,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];
> @@ -2764,7 +2730,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
> @@ -2777,9 +2743,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))
> @@ -2808,13 +2771,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);
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 05/28] elf: Move ld.so error/help output to _dl_usage
  2020-10-01 16:32 ` [PATCH 05/28] elf: Move ld.so error/help output to _dl_usage Florian Weimer via Libc-alpha
@ 2020-10-06 21:06   ` Adhemerval Zanella via Libc-alpha
  2020-10-08 12:19     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-06 21:06 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
> Also add a comment to elf/Makefile, explaining why we cannot use
> config.status for autoconf template processing.

LGTM, with just a nit below.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  elf/Makefile   |  9 ++++++++-
>  elf/dl-main.h  |  5 +++++
>  elf/dl-usage.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  elf/rtld.c     | 26 +------------------------
>  4 files changed, 65 insertions(+), 26 deletions(-)
>  create mode 100644 elf/dl-usage.c
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index ab792d45c2..71602d04c1 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

Ok.

> @@ -618,6 +618,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' \
> @@ -655,6 +661,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

Ok.

> 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 */

Why not use C11 _Noreturn?

> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
> new file mode 100644
> index 0000000000..f3d89d22b7
> --- /dev/null
> +++ b/elf/dl-usage.c
> @@ -0,0 +1,51 @@
> +/* 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\
> +  --argv0 STRING        set argv[0] to STRING before running\n");
> +}

Ok.

> diff --git a/elf/rtld.c b/elf/rtld.c
> index c1153cb627..f3e1791e2f 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -1246,31 +1246,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\
> -  --argv0 STRING        set argv[0] to STRING before running\n");
> +	_dl_usage ();
>  
>        ++_dl_skip_args;
>        --_dl_argc;
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 06/28] elf: Record whether paths come from LD_LIBRARY_PATH or --library-path
  2020-10-01 16:32 ` [PATCH 06/28] elf: Record whether paths come from LD_LIBRARY_PATH or --library-path Florian Weimer via Libc-alpha
@ 2020-10-07 16:39   ` Adhemerval Zanella via Libc-alpha
  2020-10-07 16:49     ` Florian Weimer
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-07 16:39 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
> This allows more precise diagnostics.

I haven't check the rest of the patchset to see how this information
would be used, but one option might to define the source with an 
enumeration instead of an string (to fully use a type and avoid wrong
usage).

In either case, patch looks good thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  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 5ba117d597..5fbb8c9ad4 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)
>  	{

Ok.

> 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;
>  

Ok.

> @@ -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.  */

Ok.

> 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);

Ok.

> diff --git a/elf/rtld.c b/elf/rtld.c
> index f3e1791e2f..d11fe22b83 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -286,6 +286,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;
> @@ -1203,6 +1204,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;
> @@ -2655,6 +2657,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;
>  	    }
>  

Ok.

> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 7cb1fccc80..510a2f6841 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -1046,8 +1046,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.  */
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 06/28] elf: Record whether paths come from LD_LIBRARY_PATH or --library-path
  2020-10-07 16:39   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-07 16:49     ` Florian Weimer
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer @ 2020-10-07 16:49 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

> On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
>> This allows more precise diagnostics.
>
> I haven't check the rest of the patchset to see how this information
> would be used, but one option might to define the source with an 
> enumeration instead of an string (to fully use a type and avoid wrong
> usage).

It's immediately used by the existing LD_DEBUG diagnostics (those that
currently show LD_LIBRARY_PATH, RUNPATH, RPATH).

>> +  /* Where library_path comes from.  LD_LIBRARY_PATH or --library.  */

>> +	    state.library_path_source = "--library_path";

I'm going to fix those typos before comitting.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 07/28] elf: Implement ld.so --help
  2020-10-01 16:32 ` [PATCH 07/28] elf: Implement ld.so --help Florian Weimer via Libc-alpha
@ 2020-10-07 17:16   ` Adhemerval Zanella via Libc-alpha
  2020-10-08 13:13     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-07 17:16 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
> --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.

LGTM with some nits below.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  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.  */

Ok.

> @@ -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 */

Maybe use _Noreturn here?

> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
> index f3d89d22b7..72ff99e70f 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);
> +}
> +

Maybe EXIT_FAILURE here?

> +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\
> @@ -47,5 +59,9 @@ of this helper program; chances are you did not intend to run this program.\n\
>                          in LIST\n\
>    --audit LIST          use objects named in LIST as auditors\n\
>    --preload LIST        preload objects named in LIST\n\
> -  --argv0 STRING        set argv[0] to STRING before running\n");
> +  --argv0 STRING        set argv[0] to STRING before running\n\
> +  --help                display this help and exit\n\
> +",
> +              argv0);
> +  _exit (0);
>  }

Maybe EXIT_SUCCESS here?

> diff --git a/elf/rtld.c b/elf/rtld.c
> index d11fe22b83..5cdab3c99c 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -1151,6 +1151,7 @@ dl_main (const ElfW(Phdr) *phdr,
>    _dl_starting_up = 1;
>  #endif
>  
> +  const char *ld_so_name = _dl_argv[0];
>    if (*user_entry == (ElfW(Addr)) ENTRY_POINT)
>      {
>        /* Ho ho.  We are not the program interpreter!  We are the program
> @@ -1178,8 +1179,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;

Ok.

> @@ -1187,7 +1192,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;

Ok.

> @@ -1242,13 +1248,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 (ld_so_name, _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 (ld_so_name, &state);
> +	  else
> +	    _dl_usage (ld_so_name, NULL);
> +	}
>  

Why open brackets here? There is no stack variable to possible escape.

>        ++_dl_skip_args;
>        --_dl_argc;
> @@ -1273,7 +1300,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;

Ok.

> @@ -1286,9 +1313,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 (ld_so_name, &state);
> +	      else
> +		_exit (EXIT_FAILURE);
> +	    }
>  	}
>        else
>  	{

Ok.

> @@ -1647,6 +1681,11 @@ dl_main (const ElfW(Phdr) *phdr,
>    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 (ld_so_name, &state);
> +
>    /* If we have auditing DSOs to load, do it now.  */
>    bool need_security_init = true;
>    if (state.audit_list.length > 0)
> 

Use __glibc_{un}likely or just remove the branch hint (I really don't think
this matter here anyway).

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 08/28] elf: Implement ld.so --version
  2020-10-01 16:32 ` [PATCH 08/28] elf: Implement ld.so --version Florian Weimer via Libc-alpha
@ 2020-10-07 18:36   ` Adhemerval Zanella via Libc-alpha
  2020-10-07 18:38     ` Adhemerval Zanella via Libc-alpha
  2020-10-08 13:37     ` Florian Weimer via Libc-alpha
  0 siblings, 2 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-07 18:36 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
> 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).

Some comments below.

> ---
>  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__));
> +

Maybe _Noreturn here?

>  /* 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 72ff99e70f..7355b094a5 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);
> +}
> +

I think we will need a way to embedded the Copyright year using a macro
to avoid need to manually update it on each year.

>  void
>  _dl_help (const char *argv0, struct dl_main_state *state)
>  {
> @@ -61,6 +75,7 @@ of this helper program; chances are you did not intend to run this program.\n\
>    --preload LIST        preload objects named in LIST\n\
>    --argv0 STRING        set argv[0] to STRING before running\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 5cdab3c99c..e0e8e98c2f 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -1254,6 +1254,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')
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 08/28] elf: Implement ld.so --version
  2020-10-07 18:36   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-07 18:38     ` Adhemerval Zanella via Libc-alpha
  2020-10-08 13:37     ` Florian Weimer via Libc-alpha
  1 sibling, 0 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-07 18:38 UTC (permalink / raw)
  To: libc-alpha



On 07/10/2020 15:36, Adhemerval Zanella wrote:
> 
> 
> On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
>> 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).
> 
> Some comments below.
> 
>> ---
>>  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__));
>> +
> 
> Maybe _Noreturn here?
> 
>>  /* 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 72ff99e70f..7355b094a5 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);
>> +}
>> +
> 
> I think we will need a way to embedded the Copyright year using a macro
> to avoid need to manually update it on each year.

And I just noted you are handling on the scripts/update-copyrights,
so this should be ok.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> 
>>  void
>>  _dl_help (const char *argv0, struct dl_main_state *state)
>>  {
>> @@ -61,6 +75,7 @@ of this helper program; chances are you did not intend to run this program.\n\
>>    --preload LIST        preload objects named in LIST\n\
>>    --argv0 STRING        set argv[0] to STRING before running\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 5cdab3c99c..e0e8e98c2f 100644
>> --- a/elf/rtld.c
>> +++ b/elf/rtld.c
>> @@ -1254,6 +1254,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')
>>
> 
> Ok.
> 

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 09/28] scripts/update-copyrights: Update csu/version.c, elf/dl-usage.c
  2020-10-01 16:32 ` [PATCH 09/28] scripts/update-copyrights: Update csu/version.c, elf/dl-usage.c Florian Weimer via Libc-alpha
@ 2020-10-07 18:41   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-07 18:41 UTC (permalink / raw)
  To: libc-alpha

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
> ---
>  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"
>        ;;
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 10/28] elf: Use the term "program interpreter" in the ld.so help message
  2020-10-01 16:32 ` [PATCH 10/28] elf: Use the term "program interpreter" in the ld.so help message Florian Weimer via Libc-alpha
@ 2020-10-07 21:08   ` Adhemerval Zanella via Libc-alpha
  2020-10-08 14:08     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-07 21:08 UTC (permalink / raw)
  To: libc-alpha

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
> 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 7355b094a5..35a1c0c455 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\

s/inherted/inherited

>  \n\
>    --list                list all dependencies and how they are resolved\n\
>    --verify              verify that given object really is a dynamically linked\n\
> 

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 02/28] elf: Implement __rtld_malloc_is_full
  2020-10-01 18:23   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-08  9:44     ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-08  9:44 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

> On 01/10/2020 13:31, Florian Weimer via Libc-alpha wrote:
>> 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.
>
> LGTM, thanks.
>
> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

Thanks, pushed as:

commit 72d36ffd7db55ae599f4c77feb0eae25a0f3714e
Author: Florian Weimer <fweimer@redhat.com>
Date:   Thu Oct 8 10:57:09 2020 +0200

    elf: Implement __rtld_malloc_is_complete
    
    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.
    
    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  2020-10-01 16:33 ` [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH Florian Weimer via Libc-alpha
@ 2020-10-08 10:13   ` Szabolcs Nagy via Libc-alpha
  2020-10-09  9:08     ` Florian Weimer via Libc-alpha
  2020-10-08 23:16   ` Paul A. Clarke via Libc-alpha
  2020-10-09 13:19   ` Adhemerval Zanella via Libc-alpha
  2 siblings, 1 reply; 103+ messages in thread
From: Szabolcs Nagy via Libc-alpha @ 2020-10-08 10:13 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

The 10/01/2020 18:33, Florian Weimer via Libc-alpha wrote:
> 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.
...
> +/* 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;

if this returns a bitmask i would use uint32_t

to avoid overflowing shifts and have 32bits available.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 04/28] elf: Extract command-line/environment variables state from rtld.c
  2020-10-06 20:45   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-08 11:32     ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-08 11:32 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

>> +/* This is a list of all the modes the dynamic loader can be in.  */
>> +enum mode { normal, list, verify, trace };
>> +
>
> With this enum now outside rtld.c maybe it would be better to use more
> explicit identifiers?

I pushed this with this suggested change as:

commit 2bf9e641fd50ec34b04b70829679abf64fc0ed78
Author: Florian Weimer <fweimer@redhat.com>
Date:   Thu Oct 8 10:57:09 2020 +0200

    elf: Extract command-line/environment variables state from rtld.c
    
    Introduce struct dl_main_state and move it to <dl-main.h>.  Rename
    enum mode to enum rtld_mode and add the rtld_mode_ prefix to the enum
    constants.
    
    This avoids the need for putting state that is only needed during
    startup into the ld.so data segment.

It also includes the __glibc_likely/__glibc_unlikely changes.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 05/28] elf: Move ld.so error/help output to _dl_usage
  2020-10-06 21:06   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-08 12:19     ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-08 12:19 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

> On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
>> Also add a comment to elf/Makefile, explaining why we cannot use
>> config.status for autoconf template processing.
>
> LGTM, with just a nit below.
>
> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

Thanks, pushed with the _Noreturn change.

Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 11/28] elf: Print the full name of the dynamic loader in the ld.so help message
  2020-10-01 16:32 ` [PATCH 11/28] elf: Print the full name of the dynamic loader " Florian Weimer via Libc-alpha
@ 2020-10-08 12:38   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-08 12:38 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
> This requires defining a macro for the full path, matching the
> -Wl,--dynamic-link= arguments used for linking glibc programs,
> and ldd script.

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  elf/Makefile   | 3 ++-
>  elf/dl-usage.c | 2 ++
>  2 files changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index 71602d04c1..7b97e773a5 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -661,7 +661,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

Ok.

> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
> index 35a1c0c455..0a62e8f7cf 100644
> --- a/elf/dl-usage.c
> +++ b/elf/dl-usage.c
> @@ -76,6 +76,8 @@ setting environment variables (which would be inherted by subprocesses).\n\
>    --argv0 STRING        set argv[0] to STRING before running\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);
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 07/28] elf: Implement ld.so --help
  2020-10-07 17:16   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-08 13:13     ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-08 13:13 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

>> @@ -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 */
>
> Maybe use _Noreturn here?

Fixed.

>> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
>> index f3d89d22b7..72ff99e70f 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);
>> +}
>> +
>
> Maybe EXIT_FAILURE here?

Fixed.

>> +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\
>> @@ -47,5 +59,9 @@ of this helper program; chances are you did not intend to run this program.\n\
>>                          in LIST\n\
>>    --audit LIST          use objects named in LIST as auditors\n\
>>    --preload LIST        preload objects named in LIST\n\
>> -  --argv0 STRING        set argv[0] to STRING before running\n");
>> +  --argv0 STRING        set argv[0] to STRING before running\n\
>> +  --help                display this help and exit\n\
>> +",
>> +              argv0);
>> +  _exit (0);
>>  }
>
> Maybe EXIT_SUCCESS here?

Fixed.

>>        /* 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 (ld_so_name, &state);
>> +	  else
>> +	    _dl_usage (ld_so_name, NULL);
>> +	}
>>  
>
> Why open brackets here? There is no stack variable to possible escape.

They are guarded by an if statement:

      /* If we have no further argument the program was called incorrectly.
	 Grant the user some education.  */
      if (_dl_argc < 2)
	{
	  if (state.mode == rtld_mode_help)
	    /* --help without an executable is not an error.  */
	    _dl_help (ld_so_name, &state);
	  else
	    _dl_usage (ld_so_name, NULL);
	}


>> @@ -1647,6 +1681,11 @@ dl_main (const ElfW(Phdr) *phdr,
>>    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 (ld_so_name, &state);
>> +
>>    /* If we have auditing DSOs to load, do it now.  */
>>    bool need_security_init = true;
>>    if (state.audit_list.length > 0)
>> 
>
> Use __glibc_{un}likely or just remove the branch hint (I really don't think
> this matter here anyway).

Fixed.

I've pushed this with the indicated changes (and the adjustments for
renaming the enum constant to rtld_mode_help).

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 12/28] elf: Make __rtld_env_path_list and __rtld_search_dirs global variables
  2020-10-01 16:32 ` [PATCH 12/28] elf: Make __rtld_env_path_list and __rtld_search_dirs global variables Florian Weimer via Libc-alpha
@ 2020-10-08 13:27   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-08 13:27 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
> 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.

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>


> ---
>  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 5fbb8c9ad4..0c8fa72c4d 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

Ok.

> @@ -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;
>  

Ok.

> @@ -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:

Ok.

> @@ -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;
>  

Ok.

> @@ -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);

Maybe remove the the (void)? 

>  
> -      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;
>  }
>  
>  

Ok.

> @@ -1996,9 +1997,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;
>      }
>  

Ok.

> @@ -2146,8 +2147,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);

Ok.

> @@ -2236,8 +2237,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.  */

Ok.

> @@ -2405,7 +2406,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"))

Ok.

> @@ -2417,7 +2418,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

Ok.

> 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.
>  
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 08/28] elf: Implement ld.so --version
  2020-10-07 18:36   ` Adhemerval Zanella via Libc-alpha
  2020-10-07 18:38     ` Adhemerval Zanella via Libc-alpha
@ 2020-10-08 13:37     ` Florian Weimer via Libc-alpha
  1 sibling, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-08 13:37 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

> Maybe _Noreturn here?

Fixed.

>> +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);
>> +}

Also switched to EXIT_SUCCESS here.

Pushed with these changes based on your later comment:

> And I just noted you are handling on the scripts/update-copyrights,
> so this should be ok.
> 
> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 10/28] elf: Use the term "program interpreter" in the ld.so help message
  2020-10-07 21:08   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-08 14:08     ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-08 14:08 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

>> +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\
>
> s/inherted/inherited

Pushed with this fixed, and also cut back a bit on the invoking.  Thanks.

Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 13/28] elf: Add library search path information to ld.so --help
  2020-10-01 16:32 ` [PATCH 13/28] elf: Add library search path information to ld.so --help Florian Weimer via Libc-alpha
@ 2020-10-08 16:22   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-08 16:22 UTC (permalink / raw)
  To: libc-alpha

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

On 01/10/2020 13:32, Florian Weimer via Libc-alpha wrote:
> ---
>  elf/dl-usage.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 56 insertions(+)
> 
> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
> index 0a62e8f7cf..20aa715cb1 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);
> +    }
> +}
> +

Do we need the (void) casts? 

> +/* 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)
>  {
> @@ -80,5 +135,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);
>  }
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories
  2020-10-01 16:33 ` [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories Florian Weimer via Libc-alpha
@ 2020-10-08 16:27   ` Adhemerval Zanella via Libc-alpha
  2020-10-09  8:18     ` Florian Weimer via Libc-alpha
  2020-10-09 13:49   ` Matheus Castanho via Libc-alpha
  1 sibling, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-08 16:27 UTC (permalink / raw)
  To: libc-alpha

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
> ---
>  elf/dl-usage.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 62 insertions(+)
> 
> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
> index 20aa715cb1..9765d1b5c1 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;

UINT64_C(1) maybe?

> +      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);
> +        }
> +    }
> +}
> +

Ok.

>  void
>  _dl_help (const char *argv0, struct dl_main_state *state)
>  {
> @@ -136,5 +197,6 @@ This program interpreter self-identifies as: " RTLD "\n\
>  ",
>                argv0);
>    print_search_path_for_help (state);
> +  print_legacy_hwcap_directories ();
>    _exit (0);
>  }
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 15/28] elf: Do not pass GLRO(dl_platform), GLRO(dl_platformlen) to _dl_important_hwcaps
  2020-10-01 16:33 ` [PATCH 15/28] elf: Do not pass GLRO(dl_platform), GLRO(dl_platformlen) to _dl_important_hwcaps Florian Weimer via Libc-alpha
@ 2020-10-08 18:04   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-08 18:04 UTC (permalink / raw)
  To: libc-alpha

LGTM, thanks.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>

On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
> 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;

Ok.

> @@ -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;
>      }
>  

Ok.

> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index 0c8fa72c4d..f3201e7c14 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.  */

Ok.

> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 510a2f6841..382eeb9be0 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -1071,12 +1071,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.  */
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  2020-10-01 16:33 ` [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH Florian Weimer via Libc-alpha
  2020-10-08 10:13   ` Szabolcs Nagy via Libc-alpha
@ 2020-10-08 23:16   ` Paul A. Clarke via Libc-alpha
  2020-10-09  8:56     ` Florian Weimer via Libc-alpha
  2020-10-09 13:19   ` Adhemerval Zanella via Libc-alpha
  2 siblings, 1 reply; 103+ messages in thread
From: Paul A. Clarke via Libc-alpha @ 2020-10-08 23:16 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

just a couple of nits found while browsing...

On Thu, Oct 01, 2020 at 06:33:16PM +0200, Florian Weimer via Libc-alpha wrote:
> 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

separators?

> +     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.  */

remove one "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)

> 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
> @@ -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

s/ane/name/ ?

> +   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 */

PC

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories
  2020-10-08 16:27   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-09  8:18     ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-09  8:18 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

> LGTM, thanks.
>
> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

Thanks.

>> +  const char *platform = GLRO (dl_platform);
>> +  if (platform != NULL)
>> +    _dl_printf ("  %s (AT_PLATFORM)\n", platform);

I'm going to change this to (AT_PLATFORM; supported, searched) for
consistency with the other lines.

>> +
>> +  _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;
>
> UINT64_C(1) maybe?

unsigned long long needs to be at least 64 bits in our environment, so I
think this should be fine.

Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  2020-10-08 23:16   ` Paul A. Clarke via Libc-alpha
@ 2020-10-09  8:56     ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-09  8:56 UTC (permalink / raw)
  To: Paul A. Clarke; +Cc: libc-alpha

* Paul A. Clarke:

>> +  /* Sum of the individual substring lengths (without separates or
>
> separators?

Fixed.

>> +  /* glibc-hwcaps subdirectories.  These are exempted from the power
>> +     set construction below below.  */
>
> remove one "below" :-)

Done.

>> +/* 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
>
> s/ane/name/ ?

Right, fixed.

I'll try to address the other comments and repost.  Thanks.

Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 18/28] powerpc64le: Add glibc-hwcaps support
  2020-10-06 17:45           ` Paul A. Clarke via Libc-alpha
@ 2020-10-09  9:06             ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-09  9:06 UTC (permalink / raw)
  To: Paul A. Clarke via Libc-alpha; +Cc: Paul A. Clarke

* Paul A. Clarke via Libc-alpha:

> Ah, so you are trying to model the "hwcaps subdirs" on the command-line
> argument.  The latter has to be split to be used, why not allow the
> subdirs to be "pre-split", then both could be handled as an array
> of strings?  The array for the subdirs could be pre-allocated (if it will
> always be less than 32 entries) and filled in directly
> during "active" processing, obviating the need for the mysterious
> and ephemeral bits indicating "use this"/"don't use this".

Splitting the strings will need memory allocation and all that.  It may
look more familiar, but I don't think it's going to be simpler.

For the --help diagnostic, we also need information on all potentially
supported (but inactive) subdirectory strings.  So something that
computes a single array would not be sufficient.

I'll see if I can cut down on the magic numbers by other means.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  2020-10-08 10:13   ` Szabolcs Nagy via Libc-alpha
@ 2020-10-09  9:08     ` Florian Weimer via Libc-alpha
  2020-10-09 10:50       ` Szabolcs Nagy via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-09  9:08 UTC (permalink / raw)
  To: Szabolcs Nagy via Libc-alpha

* Szabolcs Nagy via Libc-alpha:

> The 10/01/2020 18:33, Florian Weimer via Libc-alpha wrote:
>> 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.
> ...
>> +/* 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;
>
> if this returns a bitmask i would use uint32_t
>
> to avoid overflowing shifts and have 32bits available.

I did this for alignment with _dl_hwcaps_split_masked_init, where it
would convenient to shift in one bits (so that -1 always means “all
ones”).  But I guess I can turn this into a typedef and use an unsigned
type.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  2020-10-09  9:08     ` Florian Weimer via Libc-alpha
@ 2020-10-09 10:50       ` Szabolcs Nagy via Libc-alpha
  2020-10-09 10:55         ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Szabolcs Nagy via Libc-alpha @ 2020-10-09 10:50 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Szabolcs Nagy via Libc-alpha

The 10/09/2020 11:08, Florian Weimer wrote:
> * Szabolcs Nagy via Libc-alpha:
> > The 10/01/2020 18:33, Florian Weimer via Libc-alpha wrote:
> >> 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.
> > ...
> >> +/* 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;
> >
> > if this returns a bitmask i would use uint32_t
> >
> > to avoid overflowing shifts and have 32bits available.
> 
> I did this for alignment with _dl_hwcaps_split_masked_init, where it
> would convenient to shift in one bits (so that -1 always means “all
> ones”).  But I guess I can turn this into a typedef and use an unsigned
> type.

-1 converted to uint32_t is "all ones" so i don't see a problem there.

i don't think this should be a typedef (that hides
important details about the type where it is used)


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  2020-10-09 10:50       ` Szabolcs Nagy via Libc-alpha
@ 2020-10-09 10:55         ` Florian Weimer via Libc-alpha
  2020-10-09 11:03           ` Szabolcs Nagy via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-09 10:55 UTC (permalink / raw)
  To: Szabolcs Nagy; +Cc: Szabolcs Nagy via Libc-alpha

* Szabolcs Nagy:

> The 10/09/2020 11:08, Florian Weimer wrote:
>> * Szabolcs Nagy via Libc-alpha:
>> > The 10/01/2020 18:33, Florian Weimer via Libc-alpha wrote:
>> >> 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.
>> > ...
>> >> +/* 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;
>> >
>> > if this returns a bitmask i would use uint32_t
>> >
>> > to avoid overflowing shifts and have 32bits available.
>> 
>> I did this for alignment with _dl_hwcaps_split_masked_init, where it
>> would convenient to shift in one bits (so that -1 always means “all
>> ones”).  But I guess I can turn this into a typedef and use an unsigned
>> type.
>
> -1 converted to uint32_t is "all ones" so i don't see a problem there.
>
> i don't think this should be a typedef (that hides
> important details about the type where it is used)

I don't have a strong opinion on this, but could you please elaborate?
Are you concerned that it would hide the number of available bits?

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  2020-10-09 10:55         ` Florian Weimer via Libc-alpha
@ 2020-10-09 11:03           ` Szabolcs Nagy via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Szabolcs Nagy via Libc-alpha @ 2020-10-09 11:03 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Szabolcs Nagy via Libc-alpha

The 10/09/2020 12:55, Florian Weimer wrote:
> * Szabolcs Nagy:
> > The 10/09/2020 11:08, Florian Weimer wrote:
> >> * Szabolcs Nagy via Libc-alpha:
> >> > The 10/01/2020 18:33, Florian Weimer via Libc-alpha wrote:
> >> >> 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.
> >> > ...
> >> >> +/* 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;
> >> >
> >> > if this returns a bitmask i would use uint32_t
> >> >
> >> > to avoid overflowing shifts and have 32bits available.
> >> 
> >> I did this for alignment with _dl_hwcaps_split_masked_init, where it
> >> would convenient to shift in one bits (so that -1 always means “all
> >> ones”).  But I guess I can turn this into a typedef and use an unsigned
> >> type.
> >
> > -1 converted to uint32_t is "all ones" so i don't see a problem there.
> >
> > i don't think this should be a typedef (that hides
> > important details about the type where it is used)
> 
> I don't have a strong opinion on this, but could you please elaborate?
> Are you concerned that it would hide the number of available bits?

looking at code locally, it is easier
to see what operations are valid if
i know the signedness and bit width of
the integer types involved.

typedef does not help with typesafety,
it sometimes helps documenting the
intent, but in this case i think
uint32_t is appropriate for the internal
bit mask.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  2020-10-01 16:33 ` [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH Florian Weimer via Libc-alpha
  2020-10-08 10:13   ` Szabolcs Nagy via Libc-alpha
  2020-10-08 23:16   ` Paul A. Clarke via Libc-alpha
@ 2020-10-09 13:19   ` Adhemerval Zanella via Libc-alpha
  2020-10-12 11:54     ` Florian Weimer via Libc-alpha
  2 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-09 13:19 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
> 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.

Looks good, some nits below with the main suggestion being the
bitmask type change as Szabolcs. 

> ---
>  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 7b97e773a5..2c36b08c73 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

Ok.

> @@ -217,7 +218,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-tls-surplus
> +	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split
>  tests-container += tst-pldd tst-dlopen-tlsmodid-container \
>    tst-dlopen-self-container
>  test-srcs = tst-pathopt

Ok.

> 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;
> +}

Although using a signed does not really afects how you use the bitmask 
on _dl_hwcaps_split_masked (by shift and checking the LSB bit), I tend
to agree with Szabolzs that an unsigned type here would be better.

> 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;
> +    }
> +}
> +

Ok. Maybe define that this updates the input by rename to
'update_count_hwcaps'?

> +/* 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;
> +    }
> +}
> +

Ok.

>  /* 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, };

Are you trying to outline the first element must be 0 initilized here
with the comma? 

> +  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)

Ok.

> @@ -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;
> +  }
> +
> +

Ok.

> +  /* 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

Ok.

> @@ -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;
>  }

Ok.

> 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;
> +}
> +

Ok.

> +/* 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

s/ane/one

> +   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 */

Ok.

> 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;
> +}

Ok.

> +
> +_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;
> +}

Ok.

> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index f3201e7c14..9020f1646f 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.  */

Ok.

> 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.  */

Ok.

> @@ -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.  */

Ok.

> 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);

Ok.

> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
> index 9765d1b5c1..9a3cbb8b91 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);

Maybe 'strlen ("  ")" here? I usually see to use constants related to
string size erro-prone.

> +  _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;

As before, I am not very found of this shift on a signed interger.

> +    }
> +
> +  if (nothing_printed)
> +    _dl_printf ("\n\
> +No subdirectories of glibc-hwcaps directories are searched.\n");
> +}
> +

Ok.

>  /* 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\
> @@ -197,6 +262,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);
>  }

Ok.

> diff --git a/elf/rtld.c b/elf/rtld.c
> index e0e8e98c2f..c9d2330364 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -289,6 +289,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;

Ok.

> @@ -1244,6 +1246,22 @@ dl_main (const ElfW(Phdr) *phdr,
>  	  {
>  	    argv0 = _dl_argv[2];
>  
> +	    _dl_skip_args += 2;
> +	    _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;

Ok.

> 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);
> +}
> +

Ok.

> +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);
> +}
> +

Ok.

> +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"

Ok.

> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 382eeb9be0..0b2babc70c 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -1047,8 +1047,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;
>  

Ok.

>  /* Gather the information needed to install the profiling tables and start
> @@ -1072,9 +1077,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;
>  
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories
  2020-10-01 16:33 ` [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories Florian Weimer via Libc-alpha
  2020-10-08 16:27   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-09 13:49   ` Matheus Castanho via Libc-alpha
  2020-10-09 17:08     ` Florian Weimer via Libc-alpha
  1 sibling, 1 reply; 103+ messages in thread
From: Matheus Castanho via Libc-alpha @ 2020-10-09 13:49 UTC (permalink / raw)
  To: Florian Weimer, libc-alpha

Hi Florian,

After this patch was merged I started seeing the following error when
building with --disable-tunables:

l-usage.c: In function ‘print_legacy_hwcap_directories’:
dl-usage.c:153:11: error: ‘HWCAP_IMPORTANT’ undeclared (first use in
this function)
       if (HWCAP_IMPORTANT & bit)
           ^~~~~~~~~~~~~~~
dl-usage.c:153:11: note: each undeclared identifier is reported only
once for each function it appears in
dl-usage.c:155:31: error: implicit declaration of function
‘_dl_hwcap_string’; did you mean ‘_dl_fatal_printf’?
[-Werror=implicit-function-declaration]
           _dl_printf ("  %s", _dl_hwcap_string (n));
                               ^~~~~~~~~~~~~~~~
                               _dl_fatal_printf
dl-usage.c:155:27: error: format ‘%s’ expects argument of type ‘char *’,
but argument 2 has type ‘int’ [-Werror=format=]
           _dl_printf ("  %s", _dl_hwcap_string (n));
                          ~^   ~~~~~~~~~~~~~~~~~~~~
                          %d
cc1: all warnings being treated as errors
make[2]: *** [../o-iterator.mk:9:
/home/tcbot/bot-worker/glibc-ppc64le-power8-notunables/vpath/elf/dl-usage.os]
Error 1


I could reproduce this on both ppc64le and x84_64 with
--disable-tunables. Are we missing an `#include <dl-procinfo.h>`?

diff --git a/elf/dl-usage.c b/elf/dl-usage.c
index c07f43835b..796ad38b43 100644
--- a/elf/dl-usage.c
+++ b/elf/dl-usage.c
@@ -22,6 +22,7 @@
 #include <unistd.h>
 #include "version.h"

+#include <dl-procinfo.h>
 #include <dl-hwcaps.h>

 void


--
Matheus Castanho

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* Re: [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories
  2020-10-09 13:49   ` Matheus Castanho via Libc-alpha
@ 2020-10-09 17:08     ` Florian Weimer via Libc-alpha
  2020-10-09 17:12       ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-09 17:08 UTC (permalink / raw)
  To: Matheus Castanho; +Cc: libc-alpha

* Matheus Castanho:

> I could reproduce this on both ppc64le and x84_64 with
> --disable-tunables. Are we missing an `#include <dl-procinfo.h>`?
>
> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
> index c07f43835b..796ad38b43 100644
> --- a/elf/dl-usage.c
> +++ b/elf/dl-usage.c
> @@ -22,6 +22,7 @@
>  #include <unistd.h>
>  #include "version.h"
>
> +#include <dl-procinfo.h>
>  #include <dl-hwcaps.h>

Right, I think that's what's missing.  It's an oversight on my part.  I
tried build-many-glibcs.py on the whole series, but it doesn't have a
configuration without tunables.

Would you please post your patch to the list?  You can also push it at
the same time if you want, I think missing #includes are considered
trivial under the check-in policy.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories
  2020-10-09 17:08     ` Florian Weimer via Libc-alpha
@ 2020-10-09 17:12       ` Florian Weimer via Libc-alpha
  2020-10-09 18:54         ` Matheus Castanho via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-09 17:12 UTC (permalink / raw)
  To: Florian Weimer via Libc-alpha

* Florian Weimer via Libc-alpha:

> * Matheus Castanho:
>
>> I could reproduce this on both ppc64le and x84_64 with
>> --disable-tunables. Are we missing an `#include <dl-procinfo.h>`?
>>
>> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
>> index c07f43835b..796ad38b43 100644
>> --- a/elf/dl-usage.c
>> +++ b/elf/dl-usage.c
>> @@ -22,6 +22,7 @@
>>  #include <unistd.h>
>>  #include "version.h"
>>
>> +#include <dl-procinfo.h>
>>  #include <dl-hwcaps.h>
>
> Right, I think that's what's missing.  It's an oversight on my part.  I
> tried build-many-glibcs.py on the whole series, but it doesn't have a
> configuration without tunables.
>
> Would you please post your patch to the list?  You can also push it at
> the same time if you want, I think missing #includes are considered
> trivial under the check-in policy.

Eh, clearly you have posted this to the list already.  It's been a long
day. 8-/

Please check this in at your convenience.  Thanks.

Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories
  2020-10-09 17:12       ` Florian Weimer via Libc-alpha
@ 2020-10-09 18:54         ` Matheus Castanho via Libc-alpha
  2020-10-12  9:47           ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Matheus Castanho via Libc-alpha @ 2020-10-09 18:54 UTC (permalink / raw)
  To: Florian Weimer, Florian Weimer via Libc-alpha

On 10/9/20 2:12 PM, Florian Weimer wrote:
> * Florian Weimer via Libc-alpha:
> 
>> * Matheus Castanho:
>>
>>> I could reproduce this on both ppc64le and x84_64 with
>>> --disable-tunables. Are we missing an `#include <dl-procinfo.h>`?
>>>
>>> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
>>> index c07f43835b..796ad38b43 100644
>>> --- a/elf/dl-usage.c
>>> +++ b/elf/dl-usage.c
>>> @@ -22,6 +22,7 @@
>>>  #include <unistd.h>
>>>  #include "version.h"
>>>
>>> +#include <dl-procinfo.h>
>>>  #include <dl-hwcaps.h>
>>
>> Right, I think that's what's missing.  It's an oversight on my part.  I
>> tried build-many-glibcs.py on the whole series, but it doesn't have a
>> configuration without tunables.
>>
>> Would you please post your patch to the list?  You can also push it at
>> the same time if you want, I think missing #includes are considered
>> trivial under the check-in policy.
> 
> Eh, clearly you have posted this to the list already.  It's been a long
> day. 8-/
> 
> Please check this in at your convenience.  Thanks.
> 
> Florian
> 

Ok. I'll ask someone to push it for me then. Thanks!

--
Matheus Castanho

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories
  2020-10-09 18:54         ` Matheus Castanho via Libc-alpha
@ 2020-10-12  9:47           ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-12  9:47 UTC (permalink / raw)
  To: Matheus Castanho via Libc-alpha

* Matheus Castanho via Libc-alpha:

> On 10/9/20 2:12 PM, Florian Weimer wrote:
>> * Florian Weimer via Libc-alpha:
>> 
>>> * Matheus Castanho:
>>>
>>>> I could reproduce this on both ppc64le and x84_64 with
>>>> --disable-tunables. Are we missing an `#include <dl-procinfo.h>`?
>>>>
>>>> diff --git a/elf/dl-usage.c b/elf/dl-usage.c
>>>> index c07f43835b..796ad38b43 100644
>>>> --- a/elf/dl-usage.c
>>>> +++ b/elf/dl-usage.c
>>>> @@ -22,6 +22,7 @@
>>>>  #include <unistd.h>
>>>>  #include "version.h"
>>>>
>>>> +#include <dl-procinfo.h>
>>>>  #include <dl-hwcaps.h>
>>>
>>> Right, I think that's what's missing.  It's an oversight on my part.  I
>>> tried build-many-glibcs.py on the whole series, but it doesn't have a
>>> configuration without tunables.
>>>
>>> Would you please post your patch to the list?  You can also push it at
>>> the same time if you want, I think missing #includes are considered
>>> trivial under the check-in policy.
>> 
>> Eh, clearly you have posted this to the list already.  It's been a long
>> day. 8-/
>> 
>> Please check this in at your convenience.  Thanks.
>> 
>> Florian
>> 
>
> Ok. I'll ask someone to push it for me then. Thanks!

I've pushed it for you.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH
  2020-10-09 13:19   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-12 11:54     ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-12 11:54 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

>> +  /* 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, };
>
> Are you trying to outline the first element must be 0 initilized here
> with the comma?

Empty initializers are non-standard.  The , suggests that there is more
than one initializer.  I can drop the 0, if you prefer.
>> +/* Print the HWCAP name itself, indented.  */
>> +static void
>> +print_hwcaps_subdirectories_name (const struct dl_hwcaps_split *split)
>> +{
>> +  _dl_write (STDOUT_FILENO, "  ", 2);
>
> Maybe 'strlen ("  ")" here? I usually see to use constants related to
> string size erro-prone.

I prefer it this way here because it documents that there are exactly
two spaces (not one, not three).

I'm going to send a new patch later today, hopefully addressing Paul
Clarke's comment about magic numbers.  There should also be a new test.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 20/28] aarch64: Add glibc-hwcaps support
  2020-10-01 16:33 ` [PATCH 20/28] aarch64: " Florian Weimer via Libc-alpha
@ 2020-10-14 13:46   ` Adhemerval Zanella via Libc-alpha
  2020-10-14 14:08     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-14 13:46 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
> At this point, only the "atomics" subdirectory is available,
> for libraries built using LSE atomics.

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  sysdeps/aarch64/dl-hwcaps-subdirs.c | 31 +++++++++++++++++++++++++++++
>  1 file changed, 31 insertions(+)
>  create mode 100644 sysdeps/aarch64/dl-hwcaps-subdirs.c
> 
> diff --git a/sysdeps/aarch64/dl-hwcaps-subdirs.c b/sysdeps/aarch64/dl-hwcaps-subdirs.c
> new file mode 100644
> index 0000000000..fd6325024e
> --- /dev/null
> +++ b/sysdeps/aarch64/dl-hwcaps-subdirs.c
> @@ -0,0 +1,31 @@
> +/* Architecture-specific glibc-hwcaps subdirectories.  aarch64 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 <ldsodefs.h>
> +
> +const char _dl_hwcaps_subdirs[] = "atomics";
> +
> +int32_t
> +_dl_hwcaps_subdirs_active (void)
> +{
> +  if (GLRO (dl_hwcap) & HWCAP_ATOMICS)
> +    return 1;
> +
> +  return 0;
> +}
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 21/28] elf: Add endianness markup to ld.so.cache
  2020-10-01 16:33 ` [PATCH 21/28] elf: Add endianness markup to ld.so.cache Florian Weimer via Libc-alpha
@ 2020-10-14 14:07   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-14 14:07 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
> Use a reserved byte in the new format cache header to indicate whether
> the file is in little endian or big endian format.  Eventually, this
> information could be used to provide a unified cache for qemu-user
> and similiar scenarios.

Some comments below.

> ---
>  elf/cache.c                | 11 ++++++++++
>  elf/dl-cache.c             | 20 +++++++++++++++++-
>  sysdeps/generic/dl-cache.h | 43 +++++++++++++++++++++++++++++++++++++-
>  3 files changed, 72 insertions(+), 2 deletions(-)
> 
> diff --git a/elf/cache.c b/elf/cache.c
> index 1eb1455883..e0aa616352 100644
> --- a/elf/cache.c
> +++ b/elf/cache.c
> @@ -152,6 +152,14 @@ print_entry (const char *lib, int flag, unsigned int osversion,
>    printf (") => %s\n", key);
>  }
>  
> +/* Print an error and exit if the new-file cache is internally
> +   inconsistent.  */
> +static void
> +check_new_cache (struct cache_file_new *cache)
> +{
> +  if (! cache_file_new_matches_endian (cache))
> +    error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n"));
> +}
>  
>  /* Print the whole cache file, if a file contains the new cache format
>     hidden in the old one, print the contents of the new format.  */

Ok.

> @@ -193,6 +201,7 @@ print_cache (const char *cache_name)
>  	  || memcmp (cache_new->version, CACHE_VERSION,
>  		      sizeof CACHE_VERSION - 1))
>  	error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
> +      check_new_cache (cache_new);
>        format = 1;
>        /* This is where the strings start.  */
>        cache_data = (const char *) cache_new;

Ok.

> @@ -222,6 +231,7 @@ print_cache (const char *cache_name)
>  	      && memcmp (cache_new->version, CACHE_VERSION,
>  			 sizeof CACHE_VERSION - 1) == 0)
>  	    {
> +	      check_new_cache (cache_new);
>  	      cache_data = (const char *) cache_new;
>  	      format = 1;
>  	    }

Ok.

> @@ -361,6 +371,7 @@ save_cache (const char *cache_name)
>  
>        file_entries_new->nlibs = cache_entry_count;
>        file_entries_new->len_strings = total_strlen;
> +      file_entries_new->flags = cache_file_new_flags_endian;
>      }
>  
>    /* Pad for alignment of cache_file_new.  */

Ok.

> diff --git a/elf/dl-cache.c b/elf/dl-cache.c
> index 93d185e788..3aa8ed6c13 100644
> --- a/elf/dl-cache.c
> +++ b/elf/dl-cache.c
> @@ -210,6 +210,11 @@ _dl_load_cache_lookup (const char *name)
>  	  && ((cachesize - sizeof *cache_new) / sizeof (struct file_entry_new)
>  	      >= ((struct cache_file_new *) file)->nlibs))
>  	{
> +	  if (! cache_file_new_matches_endian (file))
> +	    {
> +	      __munmap (file, cachesize);
> +	      file = (void *) -1;
> +	    }
>  	  cache_new = file;
>  	  cache = file;
>  	}

Ok.

> @@ -231,7 +236,20 @@ _dl_load_cache_lookup (const char *name)
>  	  if (cachesize < (offset + sizeof (struct cache_file_new))
>  	      || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW,
>  			 sizeof CACHEMAGIC_VERSION_NEW - 1) != 0)
> -	    cache_new = (void *) -1;
> +	      cache_new = (void *) -1;
> +	  else
> +	    {
> +	      if (! cache_file_new_matches_endian (cache_new))
> +		{
> +		  /* The old-format part of the cache is bogus as well
> +		     if the endianness does not match.  (But it is
> +		     unclear how the new header can be located if the
> +		     endianess does not match.)  */
> +		  cache = (void *) -1;
> +		  cache_new = (void *) -1;
> +		  __munmap (file, cachesize);
> +		}
> +	    }
>  	}
>        else
>  	{

Ok.

> diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
> index 6b310e9e15..1b04211f6b 100644
> --- a/sysdeps/generic/dl-cache.h
> +++ b/sysdeps/generic/dl-cache.h
> @@ -16,6 +16,11 @@
>     License along with the GNU C Library; if not, see
>     <https://www.gnu.org/licenses/>.  */
>  
> +#ifndef _DL_CACHE_H
> +#define _DL_CACHE_H
> +
> +#include <endian.h>
> +#include <stdbool.h>
>  #include <stdint.h>
>  
>  #ifndef _DL_CACHE_DEFAULT_ID

Ok.

> @@ -83,21 +88,57 @@ struct file_entry_new
>    uint64_t hwcap;		/* Hwcap entry.	 */
>  };
>  
> +/* See flags member of struct cache_file_new below.  */
> +enum
> +  {
> +   cache_file_new_flags_endian = (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
> +				  ? 2 : 3)

Why not enumerate the possible values as described below:

enum cache_file_new_flags
  {
    cache_file_flag_invalid = 1,
    cache_file_flag_endianess = 2
  };
enum
  {
    cache_file_new_flags_endian = (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
				   ? cache_file_flag_endianess : 0)
				  | cache_file_flag_invalid
  };

I think this is slight less confusing than using direct values
and more explicit on how the default flags value should be.

> +  };
> +
>  struct cache_file_new
>  {
>    char magic[sizeof CACHEMAGIC_NEW - 1];
>    char version[sizeof CACHE_VERSION - 1];
>    uint32_t nlibs;		/* Number of entries.  */
>    uint32_t len_strings;		/* Size of string table. */
> -  uint32_t unused[5];		/* Leave space for future extensions
> +
> +  /* flags & 3 is used to indicate the endianness of the cache.
> +     0: no endianness information available
> +        (An old ldconfig version without endianness support wrote the file.)
> +     1: cache is invalid
> +     2: little endian
> +     3: big endian
> +
> +     The remaining bits are unused and should be generated as zero and
> +     ignored by readers.  */
> +  uint8_t flags;
> +
> +  uint8_t padding_unsed[3];	/* Not used, for future extensions.  */
> +
> +  uint32_t unused[4];		/* Leave space for future extensions
>  				   and align to 8 byte boundary.  */
>    struct file_entry_new libs[0]; /* Entries describing libraries.  */
>    /* After this the string table of size len_strings is found.	*/
>  };
>  
> +/* Returns false if *CACHE has the wrong endianness for this
> +   architecture, and true if the endianness matches (or is
> +   unknown).  */
> +static inline bool
> +cache_file_new_matches_endian (const struct cache_file_new *cache)
> +{
> +  /* A zero value for cache->flags means that no endianness
> +     information is available.  */
> +  return cache->flags == 0
> +    || (cache->flags & 3) == cache_file_new_flags_endian;
> +}
> +
> +

Using the suggestion above:

static inline bool
cache_file_new_matches_endian (const struct cache_file_new *cache)
{
  return cache->flags == 0
	 || (cache->flags & (cache_file_flag_endianess | cache_file_flag_invalid))
            == cache_file_flag_endianess;
}

>  /* Used to align cache_file_new.  */
>  #define ALIGN_CACHE(addr)				\
>  (((addr) + __alignof__ (struct cache_file_new) -1)	\
>   & (~(__alignof__ (struct cache_file_new) - 1)))
>  
>  extern int _dl_cache_libcmp (const char *p1, const char *p2) attribute_hidden;
> +
> +#endif /* _DL_CACHE_H */
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 20/28] aarch64: Add glibc-hwcaps support
  2020-10-14 13:46   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-14 14:08     ` Florian Weimer via Libc-alpha
  2020-10-14 14:15       ` Adhemerval Zanella via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-14 14:08 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

> On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
>> At this point, only the "atomics" subdirectory is available,
>> for libraries built using LSE atomics.
>
> LGTM, thanks.
>
> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

Thanks.  I didn't want to skip AArch64, but it's really more of a stub
implementation:

>> +const char _dl_hwcaps_subdirs[] = "atomics";

Does this still make sense with GCC defaulting to -moutline-atomics for
ARMv8a?  That's what I wonder.

Eventually, we need to define some levels for AArch64, and I'm not sure
to what extent they would align with the official 8.X versions.  To me,
they look more like a bouquet you can choose from.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 20/28] aarch64: Add glibc-hwcaps support
  2020-10-14 14:08     ` Florian Weimer via Libc-alpha
@ 2020-10-14 14:15       ` Adhemerval Zanella via Libc-alpha
  2020-10-14 14:37         ` Szabolcs Nagy via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-14 14:15 UTC (permalink / raw)
  To: Florian Weimer, Adhemerval Zanella via Libc-alpha



On 14/10/2020 11:08, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
>>> At this point, only the "atomics" subdirectory is available,
>>> for libraries built using LSE atomics.
>>
>> LGTM, thanks.
>>
>> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
> 
> Thanks.  I didn't want to skip AArch64, but it's really more of a stub
> implementation:
> 
>>> +const char _dl_hwcaps_subdirs[] = "atomics";
> 
> Does this still make sense with GCC defaulting to -moutline-atomics for
> ARMv8a?  That's what I wonder.
> 
> Eventually, we need to define some levels for AArch64, and I'm not sure
> to what extent they would align with the official 8.X versions.  To me,
> they look more like a bouquet you can choose from.

I am not sure how ARM maintainers would like to handle it, either by
defining based on ARMv8.x revisions, a subsets of HWCAP capabilities,
or by not defining anything.

For now I think the atomic makes sense because of -moutline-atomics,
although it is essentially ARMv8.1.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 20/28] aarch64: Add glibc-hwcaps support
  2020-10-14 14:15       ` Adhemerval Zanella via Libc-alpha
@ 2020-10-14 14:37         ` Szabolcs Nagy via Libc-alpha
  2020-10-14 14:43           ` Adhemerval Zanella via Libc-alpha
  2020-10-14 14:44           ` Florian Weimer via Libc-alpha
  0 siblings, 2 replies; 103+ messages in thread
From: Szabolcs Nagy via Libc-alpha @ 2020-10-14 14:37 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: Florian Weimer, Adhemerval Zanella via Libc-alpha

The 10/14/2020 11:15, Adhemerval Zanella via Libc-alpha wrote:
> On 14/10/2020 11:08, Florian Weimer wrote:
> > * Adhemerval Zanella via Libc-alpha:
> >> On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
> >>> At this point, only the "atomics" subdirectory is available,
> >>> for libraries built using LSE atomics.
> >>
> >> LGTM, thanks.
> >>
> >> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
> > 
> > Thanks.  I didn't want to skip AArch64, but it's really more of a stub
> > implementation:
> > 
> >>> +const char _dl_hwcaps_subdirs[] = "atomics";
> > 
> > Does this still make sense with GCC defaulting to -moutline-atomics for
> > ARMv8a?  That's what I wonder.
> > 
> > Eventually, we need to define some levels for AArch64, and I'm not sure
> > to what extent they would align with the official 8.X versions.  To me,
> > they look more like a bouquet you can choose from.
> 
> I am not sure how ARM maintainers would like to handle it, either by
> defining based on ARMv8.x revisions, a subsets of HWCAP capabilities,
> or by not defining anything.
> 
> For now I think the atomic makes sense because of -moutline-atomics,
> although it is essentially ARMv8.1.

the atomics path logic was added in glibc 2.28 in
commit 397c54c1afa531242602fe3ac7bb47eff0e909f9
(see the reasoning in the commit message).

since then this is public api that users may rely
on (although likely there are not many users..)

so i dont want to remove it (and yes it's not
very helpful now that outline-atomics is the
default in gcc).

otoh i did not review this patch when it was
posted because i wanted the return value to
not be a signed int when it is a bit mask.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 20/28] aarch64: Add glibc-hwcaps support
  2020-10-14 14:37         ` Szabolcs Nagy via Libc-alpha
@ 2020-10-14 14:43           ` Adhemerval Zanella via Libc-alpha
  2020-10-14 15:13             ` Florian Weimer via Libc-alpha
  2020-10-14 14:44           ` Florian Weimer via Libc-alpha
  1 sibling, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-14 14:43 UTC (permalink / raw)
  To: Szabolcs Nagy; +Cc: Florian Weimer, Adhemerval Zanella via Libc-alpha



On 14/10/2020 11:37, Szabolcs Nagy wrote:
> The 10/14/2020 11:15, Adhemerval Zanella via Libc-alpha wrote:
>> On 14/10/2020 11:08, Florian Weimer wrote:
>>> * Adhemerval Zanella via Libc-alpha:
>>>> On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
>>>>> At this point, only the "atomics" subdirectory is available,
>>>>> for libraries built using LSE atomics.
>>>>
>>>> LGTM, thanks.
>>>>
>>>> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
>>>
>>> Thanks.  I didn't want to skip AArch64, but it's really more of a stub
>>> implementation:
>>>
>>>>> +const char _dl_hwcaps_subdirs[] = "atomics";
>>>
>>> Does this still make sense with GCC defaulting to -moutline-atomics for
>>> ARMv8a?  That's what I wonder.
>>>
>>> Eventually, we need to define some levels for AArch64, and I'm not sure
>>> to what extent they would align with the official 8.X versions.  To me,
>>> they look more like a bouquet you can choose from.
>>
>> I am not sure how ARM maintainers would like to handle it, either by
>> defining based on ARMv8.x revisions, a subsets of HWCAP capabilities,
>> or by not defining anything.
>>
>> For now I think the atomic makes sense because of -moutline-atomics,
>> although it is essentially ARMv8.1.
> 
> the atomics path logic was added in glibc 2.28 in
> commit 397c54c1afa531242602fe3ac7bb47eff0e909f9
> (see the reasoning in the commit message).
> 
> since then this is public api that users may rely
> on (although likely there are not many users..)
> 
> so i dont want to remove it (and yes it's not
> very helpful now that outline-atomics is the
> default in gcc).
> 
> otoh i did not review this patch when it was
> posted because i wanted the return value to
> not be a signed int when it is a bit mask.
> 

Ok fair enough, do you plan to send an updated version Florian
or the rest of the set is ok as-is to review?

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 20/28] aarch64: Add glibc-hwcaps support
  2020-10-14 14:37         ` Szabolcs Nagy via Libc-alpha
  2020-10-14 14:43           ` Adhemerval Zanella via Libc-alpha
@ 2020-10-14 14:44           ` Florian Weimer via Libc-alpha
  2020-10-14 15:09             ` Szabolcs Nagy via Libc-alpha
  1 sibling, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-14 14:44 UTC (permalink / raw)
  To: Szabolcs Nagy; +Cc: Adhemerval Zanella via Libc-alpha

* Szabolcs Nagy:

> The 10/14/2020 11:15, Adhemerval Zanella via Libc-alpha wrote:
>> On 14/10/2020 11:08, Florian Weimer wrote:
>> > * Adhemerval Zanella via Libc-alpha:
>> >> On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
>> >>> At this point, only the "atomics" subdirectory is available,
>> >>> for libraries built using LSE atomics.
>> >>
>> >> LGTM, thanks.
>> >>
>> >> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
>> > 
>> > Thanks.  I didn't want to skip AArch64, but it's really more of a stub
>> > implementation:
>> > 
>> >>> +const char _dl_hwcaps_subdirs[] = "atomics";
>> > 
>> > Does this still make sense with GCC defaulting to -moutline-atomics for
>> > ARMv8a?  That's what I wonder.
>> > 
>> > Eventually, we need to define some levels for AArch64, and I'm not sure
>> > to what extent they would align with the official 8.X versions.  To me,
>> > they look more like a bouquet you can choose from.
>> 
>> I am not sure how ARM maintainers would like to handle it, either by
>> defining based on ARMv8.x revisions, a subsets of HWCAP capabilities,
>> or by not defining anything.
>> 
>> For now I think the atomic makes sense because of -moutline-atomics,
>> although it is essentially ARMv8.1.
>
> the atomics path logic was added in glibc 2.28 in
> commit 397c54c1afa531242602fe3ac7bb47eff0e909f9
> (see the reasoning in the commit message).
>
> since then this is public api that users may rely
> on (although likely there are not many users..)
>
> so i dont want to remove it (and yes it's not
> very helpful now that outline-atomics is the
> default in gcc).

This patch adds glibc-hwcaps/atomics as a new subdirectory.  The whole
series does not remove the legacy directories.

Based on what you wrote, I should drop this patch, right?

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 20/28] aarch64: Add glibc-hwcaps support
  2020-10-14 14:44           ` Florian Weimer via Libc-alpha
@ 2020-10-14 15:09             ` Szabolcs Nagy via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Szabolcs Nagy via Libc-alpha @ 2020-10-14 15:09 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Adhemerval Zanella via Libc-alpha

The 10/14/2020 16:44, Florian Weimer wrote:
> * Szabolcs Nagy:
> 
> > The 10/14/2020 11:15, Adhemerval Zanella via Libc-alpha wrote:
> >> On 14/10/2020 11:08, Florian Weimer wrote:
> >> > * Adhemerval Zanella via Libc-alpha:
> >> >> On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
> >> >>> At this point, only the "atomics" subdirectory is available,
> >> >>> for libraries built using LSE atomics.
> >> >>
> >> >> LGTM, thanks.
> >> >>
> >> >> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
> >> > 
> >> > Thanks.  I didn't want to skip AArch64, but it's really more of a stub
> >> > implementation:
> >> > 
> >> >>> +const char _dl_hwcaps_subdirs[] = "atomics";
> >> > 
> >> > Does this still make sense with GCC defaulting to -moutline-atomics for
> >> > ARMv8a?  That's what I wonder.
> >> > 
> >> > Eventually, we need to define some levels for AArch64, and I'm not sure
> >> > to what extent they would align with the official 8.X versions.  To me,
> >> > they look more like a bouquet you can choose from.
> >> 
> >> I am not sure how ARM maintainers would like to handle it, either by
> >> defining based on ARMv8.x revisions, a subsets of HWCAP capabilities,
> >> or by not defining anything.
> >> 
> >> For now I think the atomic makes sense because of -moutline-atomics,
> >> although it is essentially ARMv8.1.
> >
> > the atomics path logic was added in glibc 2.28 in
> > commit 397c54c1afa531242602fe3ac7bb47eff0e909f9
> > (see the reasoning in the commit message).
> >
> > since then this is public api that users may rely
> > on (although likely there are not many users..)
> >
> > so i dont want to remove it (and yes it's not
> > very helpful now that outline-atomics is the
> > default in gcc).
> 
> This patch adds glibc-hwcaps/atomics as a new subdirectory.  The whole
> series does not remove the legacy directories.
> 
> Based on what you wrote, I should drop this patch, right?

ah i missed that legacy is handled independently.

in that case, yes please drop this.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 20/28] aarch64: Add glibc-hwcaps support
  2020-10-14 14:43           ` Adhemerval Zanella via Libc-alpha
@ 2020-10-14 15:13             ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-14 15:13 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella:

> Ok fair enough, do you plan to send an updated version Florian
> or the rest of the set is ok as-is to review?

The rest of the set is ready for review.  There were some conflicts with
the adjustments based on the comments so far.  I have rebased the
fw/glibc-hwcaps branch on sourceware to reflect what I currently have
locally.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 22/28] elf: Add extension mechanism to ld.so.cache
  2020-10-01 16:33 ` [PATCH 22/28] elf: Add extension mechanism " Florian Weimer via Libc-alpha
@ 2020-10-15 17:52   ` Adhemerval Zanella via Libc-alpha
  2020-10-30 12:22     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-15 17:52 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:33, Florian Weimer via Libc-alpha wrote:
> A previously unused new-format header field is used to record
> the address of an extension directory.
> 
> This change adds a demo extension which records the version of
> ldconfig which builds a file.

Looks good in general, some comments below.

> ---
>  elf/cache.c                |  89 +++++++++++++++++++++++++++
>  sysdeps/generic/dl-cache.h | 123 ++++++++++++++++++++++++++++++++++++-
>  2 files changed, 211 insertions(+), 1 deletion(-)
> 
> diff --git a/elf/cache.c b/elf/cache.c
> index e0aa616352..3a02a4070a 100644
> --- a/elf/cache.c
> +++ b/elf/cache.c
> @@ -15,6 +15,7 @@
>     You should have received a copy of the GNU General Public License
>     along with this program; if not, see <https://www.gnu.org/licenses/>.  */
>  
> +#include <assert.h>
>  #include <errno.h>
>  #include <error.h>
>  #include <dirent.h>
> @@ -33,6 +34,7 @@
>  
>  #include <ldconfig.h>
>  #include <dl-cache.h>
> +#include <version.h>
>  
>  struct cache_entry
>  {
> @@ -161,6 +163,21 @@ check_new_cache (struct cache_file_new *cache)
>      error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n"));
>  }
>  
> +/* Print the extension information at the cache at start address
> +   FILE_BASE, of ltength FILE_SIZE bytes.  The new-format cache header

s/ltength/length

> +   is at CACHE, and the file name for diagnostics is CACHE_NAME.  */
> +static void
> +print_extensions (struct cache_extension_all_loaded *ext)
> +{
> +  if (ext->sections[cache_extension_tag_generator].base != NULL)
> +    {
> +      fputs (_("Cache generated by: "), stdout);
> +      fwrite (ext->sections[cache_extension_tag_generator].base, 1,
> +	      ext->sections[cache_extension_tag_generator].size, stdout);
> +      putchar ('\n');
> +    }
> +}
> +

Ok.  Will be the extension tag data always comprised of ascii printable
characters? 

>  /* Print the whole cache file, if a file contains the new cache format
>     hidden in the old one, print the contents of the new format.  */
>  void
> @@ -250,6 +267,11 @@ print_cache (const char *cache_name)
>      }
>    else if (format == 1)
>      {
> +      struct cache_extension_all_loaded ext;
> +      if (!cache_extension_load (cache_new, cache, cache_size, &ext))
> +	error (EXIT_FAILURE, 0,
> +	       _("Malformed extension data in cache file %s\n"), cache_name);
> +
>        printf (_("%d libs found in cache `%s'\n"),
>  	      cache_new->nlibs, cache_name);
>  

Ok.

> @@ -260,6 +282,7 @@ print_cache (const char *cache_name)
>  		     cache_new->libs[i].osversion,
>  		     cache_new->libs[i].hwcap,
>  		     cache_data + cache_new->libs[i].value);
> +      print_extensions (&ext);
>      }
>    /* Cleanup.  */
>    munmap (cache, cache_size);

Ok.

> @@ -301,6 +324,45 @@ compare (const struct cache_entry *e1, const struct cache_entry *e2)
>    return res;
>  }
>  
> +/* Size of the cache extension directory.  All tags are assumed to be
> +   present.  */
> +enum
> +  {
> +   cache_extension_size = (offsetof (struct cache_extension, sections)
> +			   + (cache_extension_count
> +			      * sizeof (struct cache_extension_section)))
> +  };
> +
> +/* Write the cache extensions to FD.  The extension directory is
> +   assumed to be located at CACHE_EXTENSION_OFFSET.  */
> +static void
> +write_extensions (int fd, uint32_t cache_extension_offset)
> +{
> +  assert ((cache_extension_offset % 4) == 0);

Maybe a proper error msg instead of an assert here?

> +
> +  struct cache_extension *ext = xmalloc (cache_extension_size);
> +  ext->magic = cache_extension_magic;
> +  ext->count = cache_extension_count;
> +
> +  for (int i = 0; i < cache_extension_count; ++i)
> +    {
> +      ext->sections[i].tag = i;
> +      ext->sections[i].flags = 0;
> +    }
> +
> +  const char *generator
> +    = "ldconfig " PKGVERSION RELEASE " release version " VERSION;
> +  ext->sections[cache_extension_tag_generator].offset
> +    = cache_extension_offset + cache_extension_size;
> +  ext->sections[cache_extension_tag_generator].size = strlen (generator);
> +
> +  if (write (fd, ext, cache_extension_size) != cache_extension_size
> +      || write (fd, generator, strlen (generator)) != strlen (generator))
> +    error (EXIT_FAILURE, errno, _("Writing of cache extension data failed"));
> +
> +  free (ext);
> +}
> +

Ok.

>  /* Save the contents of the cache.  */
>  void
>  save_cache (const char *cache_name)
> @@ -435,6 +497,25 @@ save_cache (const char *cache_name)
>        && idx_old < cache_entry_old_count)
>      file_entries->libs[idx_old] = file_entries->libs[idx_old - 1];
>  
> +  /* Compute the location of the extension directory.  This
> +     implementation puts the directory after the string table.  The
> +     size computation matches the write calls below.  The extension
> +     directory does not exist with format 0, so the value does not
> +     matter.  */
> +  uint32_t extension_offset = 0;
> +  if (opt_format != 2)
> +    extension_offset += file_entries_size;
> +  if (opt_format != 0)
> +    {
> +      if (opt_format != 2)
> +	extension_offset += pad;
> +      extension_offset += file_entries_new_size;
> +    }

Ok, although I think we should be good move the 'opt_format' definition to
a proper enumeration.

> +  extension_offset += total_strlen;
> +  extension_offset = roundup (extension_offset, 4); /* Provide alignment.  */
> +  if (opt_format != 0)
> +    file_entries_new->extension_offset = extension_offset;
> +
>    /* Write out the cache.  */
>  
>    /* Write cache first to a temporary file and rename it later.  */

Ok.

> @@ -473,6 +554,14 @@ save_cache (const char *cache_name)
>    if (write (fd, strings, total_strlen) != (ssize_t) total_strlen)
>      error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
>  
> +  if (opt_format != 0)
> +    {
> +      /* Align file position to 4.  */
> +      off64_t old_offset = lseek64 (fd, extension_offset, SEEK_SET);
> +      assert ((unsigned long long int) (extension_offset - old_offset) < 4);

Same as before, should we add a proper error in this case?

> +      write_extensions (fd, extension_offset);
> +    }
> +
>    /* Make sure user can always read cache file */
>    if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR))
>      error (EXIT_FAILURE, errno,

Ok.

> diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
> index 1b04211f6b..b154740da9 100644
> --- a/sysdeps/generic/dl-cache.h
> +++ b/sysdeps/generic/dl-cache.h
> @@ -21,7 +21,9 @@
>  
>  #include <endian.h>
>  #include <stdbool.h>
> +#include <stddef.h>
>  #include <stdint.h>
> +#include <string.h>
>  
>  #ifndef _DL_CACHE_DEFAULT_ID
>  # define _DL_CACHE_DEFAULT_ID	3
> @@ -115,7 +117,11 @@ struct cache_file_new
>  
>    uint8_t padding_unsed[3];	/* Not used, for future extensions.  */
>  
> -  uint32_t unused[4];		/* Leave space for future extensions
> +  /* File offset of the extension directory.  See struct
> +     cache_extension below.  Must be a multiple of four.  */
> +  uint32_t extension_offset;
> +
> +  uint32_t unused[3];		/* Leave space for future extensions
>  				   and align to 8 byte boundary.  */
>    struct file_entry_new libs[0]; /* Entries describing libraries.  */
>    /* After this the string table of size len_strings is found.	*/
> @@ -134,6 +140,121 @@ cache_file_new_matches_endian (const struct cache_file_new *cache)
>  }
>  
>  

Ok.

> +/* Randomly chosen magic value, which allows for additional
> +   consistency verification.  */
> +enum { cache_extension_magic = (uint32_t) -358342284 };
> +
> +/* Tag values for different kinds of extension sections.  Similar to
> +   SHT_* constants.  */
> +enum cache_extension_tag
> +  {
> +   /* Array of bytes containing the glibc version that generated this
> +      cache file.  */
> +   cache_extension_tag_generator,
> +
> +   /* Total number of known cache extension tags.  */
> +   cache_extension_count
> +  };
> +
> +/* Element in the array following struct cache_extension.  Similar to
> +   an ELF section header.  */
> +struct cache_extension_section
> +{
> +  /* Type of the extension section.  A enum cache_extension_tag value.  */
> +  uint32_t tag;
> +
> +  /* Extension-specific flags.  Currently generated as zero.  */
> +  uint32_t flags;
> +
> +  /* Offset from the start of the file for the data in this extension
> +     section.  Specific extensions can have alignment constraints.  */
> +  uint32_t offset;
> +
> +  /* Length in bytes of the extension data.  Specific extensions may
> +     have size requirements.  */
> +  uint32_t size;
> +};

Ok.

> +
> +/* The extension directory in the cache.  An array of struct
> +   cache_extension_section entries.  */
> +struct cache_extension
> +{
> +  uint32_t magic;		/* Always cache_extension_magic.  */
> +  uint32_t count;		/* Number of following entries.  */
> +
> +  /* count section descriptors of type struct cache_extension_section
> +     follow.  */
> +  struct cache_extension_section sections[];
> +};
> +

Ok.

> +/* A relocated version of struct cache_extension_section.  */
> +struct cache_extension_loaded
> +{
> +  /* Address and size of this extension section.  base is NULL if the
> +     section is missing from the file.  */
> +  const void *base;
> +  size_t size;
> +
> +  /* Flags from struct cache_extension_section.  */
> +  uint32_t flags;
> +};
> +
> +/* All supported extension sections, relocated.  Filled in by
> +   cache_extension_load below.  */
> +struct cache_extension_all_loaded
> +{
> +  struct cache_extension_loaded sections[cache_extension_count];
> +};
> +

Ok.

> +static bool __attribute__ ((unused))

Maybe use inline and let the compiler decide? Or the function is
really duplicate in a lot of places?

> +cache_extension_load (const struct cache_file_new *cache,
> +		      const void *file_base, size_t file_size,
> +		      struct cache_extension_all_loaded *loaded)
> +{
> +  memset (loaded, 0, sizeof (*loaded));
> +  if (cache->extension_offset == 0)
> +    /* No extensions present.  This is not a format error.  */
> +    return true;
> +  if ((cache->extension_offset % 4) != 0)
> +    /* Extension offset is misaligned.  */
> +    return false;
> +  size_t size_tmp;
> +  if (__builtin_add_overflow (cache->extension_offset,
> +			      sizeof (struct cache_extension), &size_tmp)
> +      || size_tmp > file_size)
> +    /* Extension extends beyond the end of the file.  */
> +    return false;
> +  const struct cache_extension *ext = file_base + cache->extension_offset;

Maybe we should add an alignment check for 'file_base' as well (to
avoid unaligned struct member deference)?

> +  if (ext->magic != cache_extension_magic)
> +    return false;
> +  if (__builtin_mul_overflow (ext->count,
> +			      sizeof (struct cache_extension_section),
> +			      &size_tmp)
> +      || __builtin_add_overflow (cache->extension_offset
> +				 + sizeof (struct cache_extension), size_tmp,
> +				 &size_tmp)
> +      || size_tmp > file_size)
> +    /* Extension array extends beyond the end of the file.  */
> +    return false;
> +  for (uint32_t i = 0; i < ext->count; ++i)
> +    {
> +      if (__builtin_add_overflow (ext->sections[i].offset,
> +				  ext->sections[i].size, &size_tmp)
> +	  || size_tmp > file_size)
> +	/* Extension data extends beyond the end of the file.  */
> +	return false;
> +
> +      uint32_t tag = ext->sections[i].tag;
> +      if (tag >= cache_extension_count)
> +	/* Tag is out of range and unrecognized.  */
> +	continue;
> +      loaded->sections[tag].base = file_base + ext->sections[i].offset;
> +      loaded->sections[tag].size = ext->sections[i].size;
> +      loaded->sections[tag].flags = ext->sections[i].flags;
> +    }
> +  return true;
> +}
> +
>  /* Used to align cache_file_new.  */
>  #define ALIGN_CACHE(addr)				\
>  (((addr) + __alignof__ (struct cache_file_new) -1)	\
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 23/28] elf: Unify old and new format cache handling code in ld.so
  2020-10-01 16:34 ` [PATCH 23/28] elf: Unify old and new format cache handling code in ld.so Florian Weimer via Libc-alpha
@ 2020-10-16 14:37   ` Adhemerval Zanella via Libc-alpha
  2020-10-30 13:22     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-16 14:37 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:34, Florian Weimer via Libc-alpha wrote:
> struct file_entry_new starts with the fields of struct file_entry,
> so the code can be shared if the size computation is made dynamic.

LGTM, with some nits below.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>


> ---
>  elf/dl-cache.c             | 287 +++++++++++++++++++------------------
>  sysdeps/generic/dl-cache.h |  17 ++-
>  2 files changed, 158 insertions(+), 146 deletions(-)
> 
> diff --git a/elf/dl-cache.c b/elf/dl-cache.c
> index 3aa8ed6c13..02c46ffb0c 100644
> --- a/elf/dl-cache.c
> +++ b/elf/dl-cache.c
> @@ -35,103 +35,141 @@ static struct cache_file *cache;
>  static struct cache_file_new *cache_new;
>  static size_t cachesize;
>  
> -/* 1 if cache_data + PTR points into the cache.  */
> -#define _dl_cache_verify_ptr(ptr) (ptr < cache_data_size)
> -
> -#define SEARCH_CACHE(cache) \
> -/* We use binary search since the table is sorted in the cache file.	      \
> -   The first matching entry in the table is returned.			      \
> -   It is important to use the same algorithm as used while generating	      \
> -   the cache file.  */							      \
> -do									      \
> -  {									      \
> -    left = 0;								      \
> -    right = cache->nlibs - 1;						      \
> -									      \
> -    while (left <= right)						      \
> -      {									      \
> -	__typeof__ (cache->libs[0].key) key;				      \
> -									      \
> -	middle = (left + right) / 2;					      \
> -									      \
> -	key = cache->libs[middle].key;					      \
> -									      \
> -	/* Make sure string table indices are not bogus before using	      \
> -	   them.  */							      \
> -	if (! _dl_cache_verify_ptr (key))				      \
> -	  {								      \
> -	    cmpres = 1;							      \
> -	    break;							      \
> -	  }								      \
> -									      \
> -	/* Actually compare the entry with the key.  */			      \
> -	cmpres = _dl_cache_libcmp (name, cache_data + key);		      \
> -	if (__glibc_unlikely (cmpres == 0))				      \
> -	  {								      \
> -	    /* Found it.  LEFT now marks the last entry for which we	      \
> -	       know the name is correct.  */				      \
> -	    left = middle;						      \
> -									      \
> -	    /* There might be entries with this name before the one we	      \
> -	       found.  So we have to find the beginning.  */		      \
> -	    while (middle > 0)						      \
> -	      {								      \
> -		__typeof__ (cache->libs[0].key) key;			      \
> -									      \
> -		key = cache->libs[middle - 1].key;			      \
> -		/* Make sure string table indices are not bogus before	      \
> -		   using them.  */					      \
> -		if (! _dl_cache_verify_ptr (key)			      \
> -		    /* Actually compare the entry.  */			      \
> -		    || _dl_cache_libcmp (name, cache_data + key) != 0)	      \
> -		  break;						      \
> -		--middle;						      \
> -	      }								      \
> -									      \
> -	    do								      \
> -	      {								      \
> -		int flags;						      \
> -		__typeof__ (cache->libs[0]) *lib = &cache->libs[middle];      \
> -									      \
> -		/* Only perform the name test if necessary.  */		      \
> -		if (middle > left					      \
> -		    /* We haven't seen this string so far.  Test whether the  \
> -		       index is ok and whether the name matches.  Otherwise   \
> -		       we are done.  */					      \
> -		    && (! _dl_cache_verify_ptr (lib->key)		      \
> -			|| (_dl_cache_libcmp (name, cache_data + lib->key)    \
> -			    != 0)))					      \
> -		  break;						      \
> -									      \
> -		flags = lib->flags;					      \
> -		if (_dl_cache_check_flags (flags)			      \
> -		    && _dl_cache_verify_ptr (lib->value))		      \
> -		  {							      \
> -		    if (best == NULL || flags == GLRO(dl_correct_cache_id))   \
> -		      {							      \
> -			HWCAP_CHECK;					      \
> -			best = cache_data + lib->value;			      \
> -									      \
> -			if (flags == GLRO(dl_correct_cache_id))		      \
> -			  /* We've found an exact match for the shared	      \
> -			     object and no general `ELF' release.  Stop	      \
> -			     searching.  */				      \
> -			  break;					      \
> -		      }							      \
> -		  }							      \
> -	      }								      \
> -	    while (++middle <= right);					      \
> -	    break;							      \
> -	}								      \
> -									      \
> -	if (cmpres < 0)							      \
> -	  left = middle + 1;						      \
> -	else								      \
> -	  right = middle - 1;						      \
> -      }									      \
> -  }									      \
> -while (0)

Ok.

> +/* True if PTR is a valid string table index.  */
> +static inline bool
> +_dl_cache_verify_ptr (uint32_t ptr, size_t string_table_size)
> +{
> +  return ptr < string_table_size;
> +}
> +

Ok.

> +/* Compute the address of the element INDEX of the array at LIBS.
> +   Conceptually, this is &LIBS[INDEX], but use ENTRY_SIZE for the size
> +   of *LIBS.  */
> +static inline const struct file_entry *
> +_dl_cache_file_entry (const struct file_entry *libs, size_t entry_size,
> +		      size_t index)
> +{
> +  return (const void *) libs + index * entry_size;
> +}
> +

Although pointer arithmetic with void *is valid for gcc, I think we should 
use uintptr_t here. 

> +/* We use binary search since the table is sorted in the cache file.
> +   The first matching entry in the table is returned.  It is important
> +   to use the same algorithm as used while generating the cache file.
> +   STRING_TABLE_SIZE indicates the maximum offset in STRING_TABLE at
> +   which data is mapped; it is not exact.  */
> +static const char *
> +search_cache (const char *string_table, uint32_t string_table_size,
> +	      struct file_entry *libs, uint32_t nlibs, uint32_t entry_size,
> +	      const char *name)
> +{
> +  /* Used by the HWCAP check in the struct file_entry_new case.  */
> +  uint64_t platform = _dl_string_platform (GLRO (dl_platform));
> +  if (platform != (uint64_t) -1)
> +    platform = 1ULL << platform;

Maybe UINT64_C(1) << platform?

> +  uint64_t hwcap_mask = GET_HWCAP_MASK ();
> +#define _DL_HWCAP_TLS_MASK (1LL << 63)

Same as before.

> +  uint64_t hwcap_exclude = ~((GLRO (dl_hwcap) & hwcap_mask)
> +			     | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK);
> +
> +  int left = 0;
> +  int right = nlibs - 1;
> +  const char *best = NULL;
> +
> +  while (left <= right)
> +    {
> +      int middle = (left + right) / 2;
> +      uint32_t key = _dl_cache_file_entry (libs, entry_size, middle)->key;
> +
> +      /* Make sure string table indices are not bogus before using
> +	 them.  */
> +      if (!_dl_cache_verify_ptr (key, string_table_size))
> +	return NULL;
>  
> +      /* Actually compare the entry with the key.  */
> +      int cmpres = _dl_cache_libcmp (name, string_table + key);
> +      if (__glibc_unlikely (cmpres == 0))
> +	{
> +	  /* Found it.  LEFT now marks the last entry for which we
> +	     know the name is correct.  */
> +	  left = middle;
> +
> +	  /* There might be entries with this name before the one we
> +	     found.  So we have to find the beginning.  */
> +	  while (middle > 0)
> +	    {
> +	      key = _dl_cache_file_entry (libs, entry_size, middle - 1)->key;
> +	      /* Make sure string table indices are not bogus before
> +		 using them.  */
> +	      if (!_dl_cache_verify_ptr (key, string_table_size)
> +		  /* Actually compare the entry.  */
> +		  || _dl_cache_libcmp (name, string_table + key) != 0)
> +		break;
> +	      --middle;
> +	    }
> +
> +	  do
> +	    {
> +	      int flags;
> +	      const struct file_entry *lib
> +		= _dl_cache_file_entry (libs, entry_size, middle);
> +
> +	      /* Only perform the name test if necessary.  */
> +	      if (middle > left
> +		  /* We haven't seen this string so far.  Test whether the
> +		     index is ok and whether the name matches.  Otherwise
> +		     we are done.  */
> +		  && (! _dl_cache_verify_ptr (lib->key, string_table_size)
> +		      || (_dl_cache_libcmp (name, string_table + lib->key)
> +			  != 0)))
> +		break;
> +
> +	      flags = lib->flags;
> +	      if (_dl_cache_check_flags (flags)
> +		  && _dl_cache_verify_ptr (lib->value, string_table_size))
> +		{
> +		  if (best == NULL || flags == GLRO (dl_correct_cache_id))
> +		    {
> +		      if (entry_size >= sizeof (struct file_entry_new))
> +			{
> +			  /* The entry is large enough to include
> +			     HWCAP data.  Check it.  */
> +			  struct file_entry_new *libnew
> +			    = (struct file_entry_new *) lib;
> +
> +			  if (libnew->hwcap & hwcap_exclude)
> +			    continue;
> +			  if (GLRO (dl_osversion)
> +			      && libnew->osversion > GLRO (dl_osversion))
> +			    continue;
> +			  if (_DL_PLATFORMS_COUNT
> +			      && (libnew->hwcap & _DL_HWCAP_PLATFORM) != 0
> +			      && ((libnew->hwcap & _DL_HWCAP_PLATFORM)
> +				  != platform))
> +			    continue;
> +			}

Ok, this is HWCAP_CHECK expanded.

> +
> +		      best = string_table + lib->value;
> +
> +		      if (flags == GLRO (dl_correct_cache_id))
> +			/* We've found an exact match for the shared
> +			   object and no general `ELF' release.  Stop

Should we replace the grave accent with apostrophe in this case?

> +			   searching.  */
> +			break;
> +		    }
> +		}
> +	    }
> +	  while (++middle <= right);
> +	  break;
> +	}
> +
> +      if (cmpres < 0)
> +	left = middle + 1;
> +      else
> +	right = middle - 1;
> +    }
> +
> +  return best;
> +}
>  
>  int
>  _dl_cache_libcmp (const char *p1, const char *p2)

Ok.

> @@ -182,12 +220,6 @@ _dl_cache_libcmp (const char *p1, const char *p2)
>  char *
>  _dl_load_cache_lookup (const char *name)
>  {
> -  int left, right, middle;
> -  int cmpres;
> -  const char *cache_data;
> -  uint32_t cache_data_size;
> -  const char *best;
> -
>    /* Print a message if the loading of libs is traced.  */
>    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
>      _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE);

Ok.

> @@ -265,51 +297,22 @@ _dl_load_cache_lookup (const char *name)
>      /* Previously looked for the cache file and didn't find it.  */
>      return NULL;
>  
> -  best = NULL;
> -
> +  const char *best;
>    if (cache_new != (void *) -1)
>      {
> -      uint64_t platform;
> -
> -      /* This is where the strings start.  */
> -      cache_data = (const char *) cache_new;
> -
> -      /* Now we can compute how large the string table is.  */
> -      cache_data_size = (const char *) cache + cachesize - cache_data;
> -
> -      platform = _dl_string_platform (GLRO(dl_platform));
> -      if (platform != (uint64_t) -1)
> -	platform = 1ULL << platform;
> -
> -      uint64_t hwcap_mask = GET_HWCAP_MASK();
> -
> -#define _DL_HWCAP_TLS_MASK (1LL << 63)
> -      uint64_t hwcap_exclude = ~((GLRO(dl_hwcap) & hwcap_mask)
> -				 | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK);
> -
> -      /* Only accept hwcap if it's for the right platform.  */
> -#define HWCAP_CHECK \
> -      if (lib->hwcap & hwcap_exclude)					      \
> -	continue;							      \
> -      if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion))	      \
> -	continue;							      \
> -      if (_DL_PLATFORMS_COUNT						      \
> -	  && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0			      \
> -	  && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform)		      \
> -	continue
> -      SEARCH_CACHE (cache_new);
> +      const char *string_table = (const char *) cache_new;
> +      best = search_cache (string_table, cachesize,
> +			   &cache_new->libs[0].entry, cache_new->nlibs,
> +			   sizeof (cache_new->libs[0]), name);
>      }

Ok.

>    else
>      {
> -      /* This is where the strings start.  */
> -      cache_data = (const char *) &cache->libs[cache->nlibs];
> -
> -      /* Now we can compute how large the string table is.  */
> -      cache_data_size = (const char *) cache + cachesize - cache_data;
> -
> -#undef HWCAP_CHECK
> -#define HWCAP_CHECK do {} while (0)
> -      SEARCH_CACHE (cache);
> +      const char *string_table = (const char *) &cache->libs[cache->nlibs];
> +      uint32_t string_table_size
> +	= (const char *) cache + cachesize - string_table;
> +      best = search_cache (string_table, string_table_size,
> +			   &cache->libs[0], cache->nlibs,
> +			   sizeof (cache->libs[0]), name);
>      }
>  
>    /* Print our result if wanted.  */

Ok.

> diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
> index b154740da9..fec209509d 100644
> --- a/sysdeps/generic/dl-cache.h
> +++ b/sysdeps/generic/dl-cache.h
> @@ -66,8 +66,8 @@
>  */
>  struct file_entry
>  {
> -  int flags;		/* This is 1 for an ELF library.  */
> -  unsigned int key, value; /* String table indices.  */
> +  int32_t flags;		/* This is 1 for an ELF library.  */
> +  uint32_t key, value;		/* String table indices.  */
>  };
>  

Ok, although not sure if our code guidelines allows multiple fields in the
same line.

>  struct cache_file
> @@ -84,8 +84,17 @@ struct cache_file
>  
>  struct file_entry_new
>  {
> -  int32_t flags;		/* This is 1 for an ELF library.  */
> -  uint32_t key, value;		/* String table indices.  */
> +  union
> +  {
> +    /* Fields shared with struct file_entry.  */
> +    struct file_entry entry;
> +    /* Also expose these fields directly.  */
> +    struct
> +    {
> +      int32_t flags;		/* This is 1 for an ELF library.  */
> +      uint32_t key, value;	/* String table indices.  */
> +    };
> +  };
>    uint32_t osversion;		/* Required OS version.	 */
>    uint64_t hwcap;		/* Hwcap entry.	 */
>  };
> 


Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 24/28] elf: Implement a string table for ldconfig, with tail merging
  2020-10-01 16:34 ` [PATCH 24/28] elf: Implement a string table for ldconfig, with tail merging Florian Weimer via Libc-alpha
@ 2020-10-20 14:25   ` Adhemerval Zanella via Libc-alpha
  2020-10-30 17:08     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-20 14:25 UTC (permalink / raw)
  To: libc-alpha, Florian Weimer



On 01/10/2020 13:34, Florian Weimer via Libc-alpha wrote:
> This will be used in ldconfig to reduce the ld.so.cache size slightly.

Could you extend the commit message to explain why the 32-bit FNV-1a hash
is used and how the hash table is organized (how collisions are handled,
expected memory usage, strategy used to increase/decrease the bucket size)?

It could help also to explain the usercase a bit more, the 'tail merging'
is not straightforward to understand without dig deep in the code.  Also
why kind of cache size decrease do you expect by using strategy?

> ---
>  elf/Makefile           |   2 +-
>  elf/stringtable.c      | 201 +++++++++++++++++++++++++++++++++++++++++
>  elf/stringtable.h      |  61 +++++++++++++
>  elf/stringtable_free.c |  32 +++++++
>  elf/tst-stringtable.c  | 140 ++++++++++++++++++++++++++++
>  5 files changed, 435 insertions(+), 1 deletion(-)
>  create mode 100644 elf/stringtable.c
>  create mode 100644 elf/stringtable.h
>  create mode 100644 elf/stringtable_free.c
>  create mode 100644 elf/tst-stringtable.c
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index 2c36b08c73..ad50a3e16e 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -172,7 +172,7 @@ tests-container := \
>  
>  tests := tst-tls9 tst-leaks1 \
>  	tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 \
> -	tst-auxv
> +	tst-auxv tst-stringtable
>  tests-internal := tst-tls1 tst-tls2 $(tests-static-internal)
>  tests-static := $(tests-static-normal) $(tests-static-internal)
>  
> diff --git a/elf/stringtable.c b/elf/stringtable.c
> new file mode 100644
> index 0000000000..f9ade50249
> --- /dev/null
> +++ b/elf/stringtable.c
> @@ -0,0 +1,201 @@
> +/* String tables for ld.so.cache construction.  Implementation.

This file misses the Copyright year.

> +   This file is part of the GNU C Library.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published
> +   by the Free Software Foundation; version 2 of the License, or
> +   (at your option) any later version.
> +
> +   This program 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 General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
> +
> +#include <assert.h>
> +#include <error.h>
> +#include <ldconfig.h>
> +#include <libintl.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stringtable.h>
> +
> +static void
> +stringtable_init (struct stringtable *table)
> +{
> +  table->count = 0;
> +  table->allocated = 16;
> +  table->entries = xcalloc (table->allocated, sizeof (table->entries[0]));
> +}
> +

Why 16 elements as initial size? 

> +/* 32-bit FNV-1a hash function.  */
> +static uint32_t
> +fnv1a (const char *string, size_t length)
> +{
> +  const unsigned char *p = (const unsigned char *) string;
> +  uint32_t hash = 2166136261U;
> +  for (size_t i = 0; i < length; ++i)
> +    {
> +      hash ^= p[i];
> +      hash *= 16777619U;
> +    }
> +  return hash;
> +}

Why fnv1a was chosen? 

> +
> +/* Double the capacity of the hash table.  */
> +static void
> +stringtable_rehash (struct stringtable *table)
> +{
> +  /* Cannot overflow because the old allocation size (in bytes) is
> +     larger.  */
> +  uint32_t new_allocated = table->allocated * 2;
> +  struct stringtable_entry **new_entries
> +    = xcalloc (new_allocated, sizeof (table->entries[0]));
> +
> +  uint32_t mask = new_allocated - 1;
> +  for (uint32_t i = 0; i < table->allocated; ++i)
> +    for (struct stringtable_entry *e = table->entries[i]; e != NULL; )
> +      {
> +        struct stringtable_entry *next = e->next;
> +        uint32_t hash = fnv1a (e->string, e->length);
> +        uint32_t new_index = hash & mask;
> +        e->next = new_entries[new_index];
> +        new_entries[new_index] = e;
> +        e = next;
> +      }
> +
> +  free (table->entries);
> +  table->entries = new_entries;
> +  table->allocated = new_allocated;
> +}
> +
> +struct stringtable_entry *
> +stringtable_intern (struct stringtable *table, const char *string)
> +{
> +  if (table->allocated == 0)
> +    stringtable_init (table);

How this could happen? Is it expect the caller to set 'allocated' explicitly?

> +
> +  size_t length = strlen (string);
> +  if (length > (1U << 30))
> +    error (EXIT_FAILURE, 0, _("String table string is too long"));
> +  uint32_t hash = fnv1a (string, length);
> +
> +  /* Return a previously-existing entry.  */
> +  for (struct stringtable_entry *e
> +         = table->entries[hash & (table->allocated - 1)];
> +       e != NULL; e = e->next)
> +    if (e->length == length && memcmp (e->string, string, length) == 0)
> +      return e;
> +
> +  /* Increase the size of the table if necessary.  Keep utilization
> +     below two thirds.  */
> +  if (table->count >= (1U << 30))
> +    error (EXIT_FAILURE, 0, _("String table has too many entries"));
> +  if (table->count * 3 > table->allocated * 2)
> +    stringtable_rehash (table);
> +
> +  /* Add the new table entry.  */
> +  ++table->count;
> +  struct stringtable_entry *e
> +    = xmalloc (offsetof (struct stringtable_entry, string) + length + 1);
> +  uint32_t index = hash & (table->allocated - 1);
> +  e->next = table->entries[index];
> +  table->entries[index] = e;
> +  e->length = length;
> +  e->offset = 0;
> +  memcpy (e->string, string, length + 1);
> +  return e;
> +}
> +
> +/* Sort reversed strings in lexicographic order.  This is used for tail
> +   merging.  */
> +static int
> +finalize_compare (const void *l, const void *r)
> +{
> +  struct stringtable_entry *left = *(struct stringtable_entry **) l;
> +  struct stringtable_entry *right = *(struct stringtable_entry **) r;
> +  size_t to_compare;
> +  if (left->length < right->length)
> +    to_compare = left->length;
> +  else
> +    to_compare = right->length;
> +  for (ssize_t i = to_compare - 1; i >= 0; --i)
> +    {
> +      unsigned char lch = left->string[i];
> +      unsigned char rch = right->string[i];
> +      if (lch != rch)
> +        return lch - rch;
> +    }
> +  if (left->length == right->length)
> +    return 0;
> +  else if (left->length < right->length)
> +    /* Longer strings should come first.  */
> +    return 1;
> +  else
> +    return -1;
> +}

Ok.

> +
> +void
> +stringtable_finalize (struct stringtable *table,
> +                      struct stringtable_finalized *result)
> +{
> +  if (table->count == 0)
> +    {
> +      result->strings = xstrdup ("");
> +      result->size = 0;
> +      return;
> +    }
> +
> +  /* Optimize the order of the strings.  */
> +  struct stringtable_entry **array = xcalloc (table->count, sizeof (*array));
> +  {
> +    size_t j = 0;
> +    for (uint32_t i = 0; i < table->allocated; ++i)
> +      for (struct stringtable_entry *e = table->entries[i]; e != NULL;
> +           e = e->next)
> +        {
> +          array[j] = e;
> +          ++j;
> +        }
> +    assert (j == table->count);
> +  }
> +  qsort (array, table->count, sizeof (*array), finalize_compare);
> +
> +  /* Assign offsets, using table sharing if possible.  */
> +  array[0]->offset = 0;
> +  for (uint32_t j = 1; j < table->count; ++j)
> +    {
> +      struct stringtable_entry *previous = array[j - 1];
> +      struct stringtable_entry *current = array[j];
> +      if (previous->length >= current->length
> +          && memcmp (&previous->string[previous->length - current->length],
> +                     current->string, current->length) == 0)
> +        current->offset = (previous->offset + previous->length
> +                           - current->length);
> +      else if (__builtin_add_overflow (previous->offset,
> +                                       previous->length + 1,
> +                                       &current->offset))
> +        error (EXIT_FAILURE, 0, _("String table is too large"));
> +    }
> +
> +  /* Allocate the result string.  */
> +  {
> +    struct stringtable_entry *last = array[table->count - 1];
> +    if (__builtin_add_overflow (last->offset, last->length + 1,
> +                                &result->size))
> +      error (EXIT_FAILURE, 0, _("String table is too large"));
> +  }
> +  /* The strings are copied from the hash table, so the array is no
> +     longer needed.  */
> +  free (array);
> +  result->strings = xcalloc (result->size, 1);
> +
> +  /* Copy the strings.  */
> +  for (uint32_t i = 0; i < table->allocated; ++i)
> +    for (struct stringtable_entry *e = table->entries[i]; e != NULL;
> +         e = e->next)
> +      if (result->strings[e->offset] == '\0')
> +        memcpy (&result->strings[e->offset], e->string, e->length + 1);
> +}

Ok, I guess allocating a new stringtable_finalized should be simpler than
operating the table itself. 

> diff --git a/elf/stringtable.h b/elf/stringtable.h
> new file mode 100644
> index 0000000000..e35b6c67fd
> --- /dev/null
> +++ b/elf/stringtable.h
> @@ -0,0 +1,61 @@
> +/* String tables for ld.so.cache construction.
> +   This file is part of the GNU C Library.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published
> +   by the Free Software Foundation; version 2 of the License, or
> +   (at your option) any later version.
> +
> +   This program 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 General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
> +
> +#ifndef _STRINGTABLE_H
> +#define _STRINGTABLE_H
> +
> +#include <stddef.h>
> +#include <stdint.h>
> +
> +/* An entry in the string table.  Only the length and string fields are
> +   expected to be used outside the string table code.  */
> +struct stringtable_entry
> +{
> +  struct stringtable_entry *next; /* For collision resolution.  */
> +  uint32_t length;                /* Length of then string.  */
> +  uint32_t offset;                /* From start of finalized table.  */
> +  char string[];                  /* Null-terminated string.  */
> +};
> +
> +/* A string table.  Zero-initialization produces a valid atable.  */
> +struct stringtable
> +{
> +  struct stringtable_entry **entries;
> +  uint32_t count;                 /* Number of elements in the table.  */
> +  uint32_t allocated;             /* Length of the entries array.  */
> +};
> +
> +/* Adds STRING to TABLE.  May return the address of an existing entry.  */
> +struct stringtable_entry *stringtable_intern (struct stringtable *table,
> +                                              const char *string);

I think this name is confusing, why not just 'stringtable_add' or
'stringtable_add_element'?

> +
> +/* Result of stringtable_finalize.  SIZE bytes at STRINGS should be
> +   written to the file.  */
> +struct stringtable_finalized
> +{
> +  char *strings;
> +  size_t size;
> +};
> +
> +/* Assigns offsets to string table entries and computes the serialized
> +   form of the string table.  */
> +void stringtable_finalize (struct stringtable *table,
> +                           struct stringtable_finalized *result);
> +
> +/* Deallocate the string table (but not the TABLE pointer itself).  */
> +void stringtable_free (struct stringtable *table);
> +
> +#endif /* _STRINGTABLE_H */
> diff --git a/elf/stringtable_free.c b/elf/stringtable_free.c
> new file mode 100644
> index 0000000000..0e5296e429
> --- /dev/null
> +++ b/elf/stringtable_free.c
> @@ -0,0 +1,32 @@
> +/* String tables for ld.so.cache construction.  Deallocation (for tests only).
> +   This file is part of the GNU C Library.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published
> +   by the Free Software Foundation; version 2 of the License, or
> +   (at your option) any later version.
> +
> +   This program 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 General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
> +
> +#include <stdlib.h>
> +#include <stringtable.h>
> +
> +void
> +stringtable_free (struct stringtable *table)
> +{
> +  for (uint32_t i = 0; i < table->allocated; ++i)
> +    for (struct stringtable_entry *e = table->entries[i]; e != NULL; )
> +      {
> +        struct stringtable_entry *next = e->next;
> +        free (e);
> +        e = next;
> +      }
> +  free (table->entries);
> +  *table = (struct stringtable) { 0, };
> +}

Ok.

> diff --git a/elf/tst-stringtable.c b/elf/tst-stringtable.c
> new file mode 100644
> index 0000000000..78ca5434df
> --- /dev/null
> +++ b/elf/tst-stringtable.c
> @@ -0,0 +1,140 @@
> +/* Unit test for ldconfig string tables.
> +   This file is part of the GNU C Library.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published
> +   by the Free Software Foundation; version 2 of the License, or
> +   (at your option) any later version.
> +
> +   This program 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 General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stringtable.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +
> +static int
> +do_test (void)
> +{
> +  /* Empty string table.  */
> +  {
> +    struct stringtable s = { 0, };
> +    struct stringtable_finalized f;
> +    stringtable_finalize (&s, &f);
> +    TEST_COMPARE_STRING (f.strings, "");
> +    TEST_COMPARE (f.size, 0);
> +    free (f.strings);
> +    stringtable_free (&s);
> +  }
> +
> +  /* String table with one empty string.  */
> +  {
> +    struct stringtable s = { 0, };
> +    struct stringtable_entry *e = stringtable_intern (&s, "");
> +    TEST_COMPARE_STRING (e->string, "");
> +    TEST_COMPARE (e->length, 0);
> +    TEST_COMPARE (s.count, 1);
> +
> +    struct stringtable_finalized f;
> +    stringtable_finalize (&s, &f);
> +    TEST_COMPARE (e->offset, 0);
> +    TEST_COMPARE_STRING (f.strings, "");
> +    TEST_COMPARE (f.size, 1);
> +    free (f.strings);
> +    stringtable_free (&s);
> +  }
> +
> +  /* String table with one non-empty string.  */
> +  {
> +    struct stringtable s = { 0, };
> +    struct stringtable_entry *e = stringtable_intern (&s, "name");
> +    TEST_COMPARE_STRING (e->string, "name");
> +    TEST_COMPARE (e->length, 4);
> +    TEST_COMPARE (s.count, 1);
> +
> +    struct stringtable_finalized f;
> +    stringtable_finalize (&s, &f);
> +    TEST_COMPARE (e->offset, 0);
> +    TEST_COMPARE_STRING (f.strings, "name");
> +    TEST_COMPARE (f.size, 5);
> +    free (f.strings);
> +    stringtable_free (&s);
> +  }
> +
> +  /* Two strings, one is a prefix of the other.  Tail-merging can only
> +     happen in one way in this case.  */
> +  {
> +    struct stringtable s = { 0, };
> +    struct stringtable_entry *suffix = stringtable_intern (&s, "suffix");
> +    TEST_COMPARE_STRING (suffix->string, "suffix");
> +    TEST_COMPARE (suffix->length, 6);
> +    TEST_COMPARE (s.count, 1);
> +
> +    struct stringtable_entry *prefix
> +      = stringtable_intern (&s, "prefix-suffix");
> +    TEST_COMPARE_STRING (prefix->string, "prefix-suffix");
> +    TEST_COMPARE (prefix->length, strlen ("prefix-suffix"));
> +    TEST_COMPARE (s.count, 2);
> +
> +    struct stringtable_finalized f;
> +    stringtable_finalize (&s, &f);
> +    TEST_COMPARE (prefix->offset, 0);
> +    TEST_COMPARE (suffix->offset, strlen ("prefix-"));
> +    TEST_COMPARE_STRING (f.strings, "prefix-suffix");
> +    TEST_COMPARE (f.size, sizeof ("prefix-suffix"));
> +    free (f.strings);
> +    stringtable_free (&s);
> +  }
> +
> +  /* String table with various shared prefixes.  Triggers hash
> +     resizing.  */
> +  {
> +    enum { count = 1500 };
> +    char *strings[2 * count];
> +    struct stringtable_entry *entries[2 * count];
> +    struct stringtable s = { 0, };
> +    for (int i = 0; i < count; ++i)
> +      {
> +        strings[i] = xasprintf ("%d", i);
> +        entries[i] = stringtable_intern (&s, strings[i]);
> +        TEST_COMPARE (entries[i]->length, strlen (strings[i]));
> +        TEST_COMPARE_STRING (entries[i]->string, strings[i]);
> +        strings[i + count] = xasprintf ("prefix/%d", i);
> +        entries[i + count] = stringtable_intern (&s, strings[i + count]);
> +        TEST_COMPARE (entries[i + count]->length, strlen (strings[i + count]));
> +        TEST_COMPARE_STRING (entries[i + count]->string, strings[i + count]);
> +      }
> +
> +    struct stringtable_finalized f;
> +    stringtable_finalize (&s, &f);
> +
> +    for (int i = 0; i < 2 * count; ++i)
> +      {
> +        TEST_COMPARE (entries[i]->length, strlen (strings[i]));
> +        TEST_COMPARE_STRING (entries[i]->string, strings[i]);
> +        TEST_COMPARE_STRING (f.strings + entries[i]->offset, strings[i]);
> +        free (strings[i]);
> +      }
> +
> +    free (f.strings);
> +    stringtable_free (&s);
> +  }
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> +
> +/* Re-compile the string table implementation here.  It is not
> +   possible to link against the actual build because it was built for
> +   use in ldconfig.  */
> +#define _(arg) arg
> +#include "stringtable.c"
> +#include "stringtable_free.c"
> 

Ok.




^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 25/28] elf: Implement tail merging of strings in ldconfig
  2020-10-01 16:34 ` [PATCH 25/28] elf: Implement tail merging of strings in ldconfig Florian Weimer via Libc-alpha
@ 2020-10-22 21:08   ` Adhemerval Zanella via Libc-alpha
  2020-10-30 17:36     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-22 21:08 UTC (permalink / raw)
  To: libc-alpha



On 01/10/2020 13:34, Florian Weimer via Libc-alpha wrote:
> This simplifies the string table construction in elf/cache.c
> because there is no more need to keep track of offsets explicitly;
> the string table implementation does this internally.
> 
> This change slightly reduces the size of the cache on disk.  The
> file format does not change as a result.  The strings are
> null-terminated, without explicit length, so tail merging is
> transparent to readers.

LGTM, thanks.

> ---
>  elf/Makefile |  3 +-
>  elf/cache.c  | 84 ++++++++++++++++++++++++++++------------------------
>  2 files changed, 48 insertions(+), 39 deletions(-)
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index ad50a3e16e..5ad8df7da3 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -118,7 +118,8 @@ others-static	+= ldconfig
>  others		+= ldconfig
>  install-rootsbin += ldconfig
>  
> -ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon static-stubs
> +ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon static-stubs \
> +  stringtable
>  extra-objs	+= $(ldconfig-modules:=.o)
>  others-extras   = $(ldconfig-modules)
>  endif

Ok.

> diff --git a/elf/cache.c b/elf/cache.c
> index 3a02a4070a..eda3da98a7 100644
> --- a/elf/cache.c
> +++ b/elf/cache.c
> @@ -35,11 +35,15 @@
>  #include <ldconfig.h>
>  #include <dl-cache.h>
>  #include <version.h>
> +#include <stringtable.h>
> +
> +/* Used to store library names, paths, and other strings.  */
> +struct stringtable strings;

Maybe static here?

>  
>  struct cache_entry
>  {
> -  char *lib;			/* Library name.  */
> -  char *path;			/* Path to find library.  */
> +  struct stringtable_entry *lib; /* Library name.  */
> +  struct stringtable_entry *path; /* Path to find library.  */
>    int flags;			/* Flags to indicate kind of library.  */
>    unsigned int osversion;	/* Required OS version.  */
>    uint64_t hwcap;		/* Important hardware capabilities.  */

Ok.

> @@ -300,7 +304,7 @@ static int
>  compare (const struct cache_entry *e1, const struct cache_entry *e2)
>  {
>    /* We need to swap entries here to get the correct sort order.  */
> -  int res = _dl_cache_libcmp (e2->lib, e1->lib);
> +  int res = _dl_cache_libcmp (e2->lib->string, e1->lib->string);
>    if (res == 0)
>      {
>        if (e1->flags < e2->flags)

Ok.

> @@ -369,26 +373,24 @@ save_cache (const char *cache_name)
>  {
>    /* The cache entries are sorted already, save them in this order. */
>  
> -  /* Count the length of all strings.  */
> -  /* The old format doesn't contain hwcap entries and doesn't contain
> -     libraries in subdirectories with hwcaps entries.  Count therefore
> -     also all entries with hwcap == 0.  */
> -  size_t total_strlen = 0;
>    struct cache_entry *entry;
>    /* Number of cache entries.  */
>    int cache_entry_count = 0;
> -  /* Number of normal cache entries.  */
> +  /* The old format doesn't contain hwcap entries and doesn't contain
> +     libraries in subdirectories with hwcaps entries.  Count therefore
> +     also all entries with hwcap == 0.  */
>    int cache_entry_old_count = 0;
>  
>    for (entry = entries; entry != NULL; entry = entry->next)
>      {
> -      /* Account the final NULs.  */
> -      total_strlen += strlen (entry->lib) + strlen (entry->path) + 2;
>        ++cache_entry_count;
>        if (entry->hwcap == 0)
>  	++cache_entry_old_count;
>      }
>  
> +  struct stringtable_finalized strings_finalized;
> +  stringtable_finalize (&strings, &strings_finalized);
> +
>    /* Create the on disk cache structure.  */
>    struct cache_file *file_entries = NULL;
>    size_t file_entries_size = 0;

Ok.

> @@ -432,7 +434,7 @@ save_cache (const char *cache_name)
>  	      sizeof CACHE_VERSION - 1);
>  
>        file_entries_new->nlibs = cache_entry_count;
> -      file_entries_new->len_strings = total_strlen;
> +      file_entries_new->len_strings = strings_finalized.size;
>        file_entries_new->flags = cache_file_new_flags_endian;
>      }
>  

Ok.

> @@ -449,20 +451,20 @@ save_cache (const char *cache_name)
>      str_offset = 0;
>  
>    /* An array for all strings.  */
> -  char *strings = xmalloc (total_strlen);
> -  char *str = strings;
>    int idx_old;
>    int idx_new;
>  
>    for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL;
>         entry = entry->next, ++idx_new)
>      {
> -      /* First the library.  */
>        if (opt_format != 2 && entry->hwcap == 0)
>  	{
>  	  file_entries->libs[idx_old].flags = entry->flags;
>  	  /* XXX: Actually we can optimize here and remove duplicates.  */
>  	  file_entries->libs[idx_old].key = str_offset + pad;
> +	  file_entries->libs[idx_new].key = str_offset + entry->lib->offset;
> +	  file_entries->libs[idx_new].value
> +	    = str_offset + entry->path->offset;
>  	}
>        if (opt_format != 0)
>  	{

Ok.

> @@ -473,20 +475,12 @@ save_cache (const char *cache_name)
>  	  file_entries_new->libs[idx_new].flags = entry->flags;
>  	  file_entries_new->libs[idx_new].osversion = entry->osversion;
>  	  file_entries_new->libs[idx_new].hwcap = entry->hwcap;
> -	  file_entries_new->libs[idx_new].key = str_offset;
> +	  file_entries_new->libs[idx_new].key
> +	    = str_offset + entry->lib->offset;
> +	  file_entries_new->libs[idx_new].value
> +	    = str_offset + entry->path->offset;
>  	}
>  
> -      size_t len = strlen (entry->lib) + 1;
> -      str = mempcpy (str, entry->lib, len);
> -      str_offset += len;
> -      /* Then the path.  */
> -      if (opt_format != 2 && entry->hwcap == 0)
> -	file_entries->libs[idx_old].value = str_offset + pad;
> -      if (opt_format != 0)
> -	file_entries_new->libs[idx_new].value = str_offset;
> -      len = strlen (entry->path) + 1;
> -      str = mempcpy (str, entry->path, len);
> -      str_offset += len;
>        /* Ignore entries with hwcap for old format.  */
>        if (entry->hwcap == 0)
>  	++idx_old;

Ok.

> @@ -511,7 +505,7 @@ save_cache (const char *cache_name)
>  	extension_offset += pad;
>        extension_offset += file_entries_new_size;
>      }
> -  extension_offset += total_strlen;
> +  extension_offset += strings_finalized.size;
>    extension_offset = roundup (extension_offset, 4); /* Provide alignment.  */
>    if (opt_format != 0)
>      file_entries_new->extension_offset = extension_offset;

Ok.

> @@ -551,7 +545,8 @@ save_cache (const char *cache_name)
>  	error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
>      }
>  
> -  if (write (fd, strings, total_strlen) != (ssize_t) total_strlen)
> +  if (write (fd, strings_finalized.strings, strings_finalized.size)
> +      != (ssize_t) strings_finalized.size)
>      error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
>  
>    if (opt_format != 0)

Ok.

> @@ -580,7 +575,7 @@ save_cache (const char *cache_name)
>    /* Free all allocated memory.  */
>    free (file_entries_new);
>    free (file_entries);
> -  free (strings);
> +  free (strings_finalized.strings);
>  
>    while (entries)
>      {

Ok.

> @@ -596,14 +591,27 @@ void
>  add_to_cache (const char *path, const char *lib, int flags,
>  	      unsigned int osversion, uint64_t hwcap)
>  {
> -  size_t liblen = strlen (lib) + 1;
> -  size_t len = liblen + strlen (path) + 1;
> -  struct cache_entry *new_entry
> -    = xmalloc (sizeof (struct cache_entry) + liblen + len);
> -
> -  new_entry->lib = memcpy ((char *) (new_entry + 1), lib, liblen);
> -  new_entry->path = new_entry->lib + liblen;
> -  snprintf (new_entry->path, len, "%s/%s", path, lib);
> +  struct cache_entry *new_entry = xmalloc (sizeof (*new_entry));
> +
> +  struct stringtable_entry *path_interned;
> +  {
> +    /* Use a small, on-stack buffer in most cases.  */
> +    char buf[200];
> +    int ret = snprintf (buf, sizeof (buf), "%s/%s", path, lib);
> +    if (ret < 0 || ret >= sizeof (buf) - 1)
> +      {
> +	char *p;
> +	if (asprintf (&p, "%s/%s", path, lib) < 0)
> +	  error (EXIT_FAILURE, errno, _("Could not create library path"));
> +	path_interned = stringtable_intern (&strings, p);
> +	free (p);
> +      }
> +    else
> +      path_interned = stringtable_intern (&strings, buf);
> +  }
> +
> +  new_entry->lib = stringtable_intern (&strings, lib);
> +  new_entry->path = path_interned;
>    new_entry->flags = flags;
>    new_entry->osversion = osversion;
>    new_entry->hwcap = hwcap;
> 

Ok. Is this small string optimization really worth instead of just using
asprintf?

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 26/28] elf: In ldconfig, extract the new_sub_entry function from search_dir
  2020-10-01 16:34 ` [PATCH 26/28] elf: In ldconfig, extract the new_sub_entry function from search_dir Florian Weimer via Libc-alpha
@ 2020-10-27 13:15   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-27 13:15 UTC (permalink / raw)
  To: libc-alpha, Florian Weimer

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

On 01/10/2020 13:34, Florian Weimer via Libc-alpha wrote:
> ---
>  elf/ldconfig.c | 34 +++++++++++++++++++++-------------
>  1 file changed, 21 insertions(+), 13 deletions(-)
> 
> diff --git a/elf/ldconfig.c b/elf/ldconfig.c
> index 0c090dca15..3768267bac 100644
> --- a/elf/ldconfig.c
> +++ b/elf/ldconfig.c
> @@ -328,6 +328,23 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
>  	   "Andreas Jaeger");
>  }
>  
> +/* Allocate a new subdirectory with full path PATH under ENTRY, using
> +   inode data from *ST.  */
> +static struct dir_entry *
> +new_sub_entry (const struct dir_entry *entry, const char *path,
> +	       const struct stat64 *st)
> +{
> +  struct dir_entry *new_entry = xmalloc (sizeof (struct dir_entry));
> +  new_entry->from_file = entry->from_file;
> +  new_entry->from_line = entry->from_line;
> +  new_entry->path = xstrdup (path);
> +  new_entry->flag = entry->flag;
> +  new_entry->next = NULL;
> +  new_entry->ino = st->st_ino;
> +  new_entry->dev = st->st_dev;
> +  return new_entry;
> +}
> +
>  /* Add a single directory entry.  */
>  static void
>  add_single_dir (struct dir_entry *entry, int verbose)

Ok.

> @@ -823,26 +840,17 @@ search_dir (const struct dir_entry *entry)
>  
>        if (is_dir && is_hwcap_platform (direntry->d_name))
>  	{
> -	  /* Handle subdirectory later.  */
> -	  struct dir_entry *new_entry;
> -
> -	  new_entry = xmalloc (sizeof (struct dir_entry));
> -	  new_entry->from_file = entry->from_file;
> -	  new_entry->from_line = entry->from_line;
> -	  new_entry->path = xstrdup (file_name);
> -	  new_entry->flag = entry->flag;
> -	  new_entry->next = NULL;
>  	  if (!is_link
>  	      && direntry->d_type != DT_UNKNOWN
>  	      && __builtin_expect (lstat64 (real_file_name, &lstat_buf), 0))
>  	    {
>  	      error (0, errno, _("Cannot lstat %s"), file_name);
> -	      free (new_entry->path);
> -	      free (new_entry);
>  	      continue;
>  	    }
> -	  new_entry->ino = lstat_buf.st_ino;
> -	  new_entry->dev = lstat_buf.st_dev;
> +
> +	  /* Handle subdirectory later.  */
> +	  struct dir_entry *new_entry = new_sub_entry (entry, file_name,
> +						       &lstat_buf);
>  	  add_single_dir (new_entry, 0);
>  	  continue;
>  	}
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 27/28] elf: Process glibc-hwcaps subdirectories in ldconfig
  2020-10-01 16:34 ` [PATCH 27/28] elf: Process glibc-hwcaps subdirectories in ldconfig Florian Weimer via Libc-alpha
@ 2020-10-27 17:28   ` Adhemerval Zanella via Libc-alpha
  2020-11-04 11:57     ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-10-27 17:28 UTC (permalink / raw)
  To: libc-alpha, Florian Weimer



On 01/10/2020 13:34, Florian Weimer via Libc-alpha wrote:
> Libraries from these subdirectories are added to the cache
> with a special hwcap bit DL_CACHE_HWCAP_EXTENSION, so that
> they are ignored by older dynamic loaders.

Looks goods in general, some comments below.


> ---
>  elf/cache.c                | 258 ++++++++++++++++++++++++++++++++-----
>  elf/ldconfig.c             | 153 +++++++++++++++++++---
>  sysdeps/generic/dl-cache.h |  51 +++++++-
>  sysdeps/generic/ldconfig.h |  18 ++-
>  4 files changed, 426 insertions(+), 54 deletions(-)
> 
> diff --git a/elf/cache.c b/elf/cache.c
> index eda3da98a7..7ce4ca9870 100644
> --- a/elf/cache.c
> +++ b/elf/cache.c
> @@ -40,6 +40,105 @@
>  /* Used to store library names, paths, and other strings.  */
>  struct stringtable strings;
>  
> +/* Keeping track of "glibc-hwcaps" subdirectories.  During cache
> +   construction, a linear search by name is performed to deduplicate
> +   entries.  */
> +struct glibc_hwcaps_subdirectory
> +{
> +  struct glibc_hwcaps_subdirectory *next;
> +
> +  /* Interned string with the subdirectory name.  */
> +  struct stringtable_entry *name;
> +
> +  /* Array index in the cache_extension_tag_glibc_hwcaps section in
> +     the stored cached file.  This is computed after all the
> +     subdirectories have been processed, so that subdirectory names in
> +     the extension section can be sorted.  */
> +  uint32_t section_index;
> +
> +  /* True if the subdirectory is actually used for anything.  */
> +  bool used;
> +};
> +

Ok.

> +const char *
> +glibc_hwcaps_subdirectory_name (struct glibc_hwcaps_subdirectory *dir)
> +{
> +  return dir->name->string;
> +}
> +
> +/* Linked list of known hwcaps subdirecty names.  */
> +static struct glibc_hwcaps_subdirectory *hwcaps;
> +
> +struct glibc_hwcaps_subdirectory *
> +new_glibc_hwcaps_subdirectory (const char *name)
> +{
> +  struct stringtable_entry *name_interned
> +    = stringtable_intern (&strings, name);
> +  for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next)
> +    if (p->name == name_interned)
> +      return p;
> +  struct glibc_hwcaps_subdirectory *p = xmalloc (sizeof (p));
> +  p->next = hwcaps;
> +  p->name = name_interned;
> +  p->section_index = 0;
> +  p->used = false;
> +  hwcaps = p;
> +  return p;
> +}
> +

Ok.

> +/* Helper for sorting struct glibc_hwcaps_subdirectory elements by
> +   name.  */
> +static int
> +assign_glibc_hwcaps_indices_compare (const void *l, const void *r)
> +{
> +  const struct glibc_hwcaps_subdirectory *left
> +    = *(struct glibc_hwcaps_subdirectory **)l;
> +  const struct glibc_hwcaps_subdirectory *right
> +    = *(struct glibc_hwcaps_subdirectory **)r;
> +  return strcmp (left->name->string, right->name->string);
> +}
> +

Maybe:

  strcmp (glibc_hwcaps_subdirectory_name (left),
          glibc_hwcaps_subdirectory_name (right));

> +/* Count the number of hwcaps subdirectories which are actually
> +   used.  */
> +static size_t
> +glibc_hwcaps_count (void)
> +{
> +  size_t count = 0;
> +  for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next)
> +    if (p->used)
> +      ++count;
> +  return count;
> +}
> +

Ok.

> +/* Compute the section_index fields for all   */
> +static void
> +assign_glibc_hwcaps_indices (void)
> +{
> +  /* Convert the linked list into an array, so that we can use qsort.
> +     Only copy the subdirectories which are actually used.  */
> +  size_t count = glibc_hwcaps_count ();
> +  struct glibc_hwcaps_subdirectory **array
> +    = xmalloc (sizeof (*array) * count);
> +  {
> +    size_t i = 0;
> +    for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next)
> +      if (p->used)
> +	{
> +	  array[i] = p;
> +	  ++i;
> +	}
> +    assert (i == count);

Do we need this assert? I think it would make sense if hwcaps is modified
concurrently, which does not seem the case. 

> +  }
> +
> +  qsort (array, count, sizeof (*array), assign_glibc_hwcaps_indices_compare);
> +
> +  /* Assign the array indices.  */
> +  for (size_t i = 0; i < count; ++i)
> +    array[i]->section_index = i;
> +
> +  free (array);
> +}
> +

Ok.

>  struct cache_entry
>  {
>    struct stringtable_entry *lib; /* Library name.  */
> @@ -48,6 +147,10 @@ struct cache_entry
>    unsigned int osversion;	/* Required OS version.  */
>    uint64_t hwcap;		/* Important hardware capabilities.  */
>    int bits_hwcap;		/* Number of bits set in hwcap.  */
> +
> +  /* glibc-hwcaps subdirectory.  If not NULL, hwcap must be zero.  */
> +  struct glibc_hwcaps_subdirectory *hwcaps;
> +
>    struct cache_entry *next;	/* Next entry in list.  */
>  };
>  

Ok.

> @@ -60,7 +163,7 @@ static const char *flag_descr[] =
>  /* Print a single entry.  */
>  static void
>  print_entry (const char *lib, int flag, unsigned int osversion,
> -	     uint64_t hwcap, const char *key)
> +	     uint64_t hwcap, const char *hwcap_string, const char *key)
>  {
>    printf ("\t%s (", lib);
>    switch (flag & FLAG_TYPE_MASK)
> @@ -132,7 +235,9 @@ print_entry (const char *lib, int flag, unsigned int osversion,
>        printf (",%d", flag & FLAG_REQUIRED_MASK);
>        break;
>      }
> -  if (hwcap != 0)
> +  if (hwcap_string != NULL)
> +    printf (", hwcap: \"%s\"", hwcap_string);
> +  else if (hwcap != 0)
>      printf (", hwcap: %#.16" PRIx64, hwcap);
>    if (osversion != 0)
>      {

Ok.

> @@ -158,6 +263,29 @@ print_entry (const char *lib, int flag, unsigned int osversion,
>    printf (") => %s\n", key);
>  }
>  
> +/* Returns the string with the name of the glibcs-hwcaps subdirectory
> +   associated with ENTRY->hwcap.  file_base must be the base address
> +   for string table indices.  */
> +static const char *
> +glibc_hwcaps_string (struct cache_extension_all_loaded *ext,
> +		     const void *file_base, size_t file_size,
> +		     struct file_entry_new *entry)
> +{
> +  const uint32_t *hwcaps_array
> +    = ext->sections[cache_extension_tag_glibc_hwcaps].base;
> +  if (dl_cache_hwcap_extension (entry) && hwcaps_array != NULL)
> +    {
> +      uint32_t index = (uint32_t) entry->hwcap;
> +      if (index < ext->sections[cache_extension_tag_glibc_hwcaps].size / 4)
> +	{
> +	  uint32_t string_table_index = hwcaps_array[index];
> +	  if (string_table_index < file_size)
> +	    return file_base + string_table_index;
> +	}
> +    }
> +  return NULL;
> +}
> +

Ok.

>  /* Print an error and exit if the new-file cache is internally
>     inconsistent.  */
>  static void
> @@ -167,9 +295,7 @@ check_new_cache (struct cache_file_new *cache)
>      error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n"));
>  }
>  
> -/* Print the extension information at the cache at start address
> -   FILE_BASE, of ltength FILE_SIZE bytes.  The new-format cache header
> -   is at CACHE, and the file name for diagnostics is CACHE_NAME.  */
> +/* Print the extension information in *EXT.  */
>  static void
>  print_extensions (struct cache_extension_all_loaded *ext)
>  {
> @@ -266,7 +392,7 @@ print_cache (const char *cache_name)
>        /* Print everything.  */
>        for (unsigned int i = 0; i < cache->nlibs; i++)
>  	print_entry (cache_data + cache->libs[i].key,
> -		     cache->libs[i].flags, 0, 0,
> +		     cache->libs[i].flags, 0, 0, NULL,
>  		     cache_data + cache->libs[i].value);
>      }
>    else if (format == 1)

Ok.

> @@ -281,11 +407,16 @@ print_cache (const char *cache_name)
>  
>        /* Print everything.  */
>        for (unsigned int i = 0; i < cache_new->nlibs; i++)
> -	print_entry (cache_data + cache_new->libs[i].key,
> -		     cache_new->libs[i].flags,
> -		     cache_new->libs[i].osversion,
> -		     cache_new->libs[i].hwcap,
> -		     cache_data + cache_new->libs[i].value);
> +	{
> +	  const char *hwcaps_string
> +	    = glibc_hwcaps_string (&ext, cache, cache_size,
> +				   &cache_new->libs[i]);
> +	  print_entry (cache_data + cache_new->libs[i].key,
> +		       cache_new->libs[i].flags,
> +		       cache_new->libs[i].osversion,
> +		       cache_new->libs[i].hwcap, hwcaps_string,
> +		       cache_data + cache_new->libs[i].value);
> +	}
>        print_extensions (&ext);
>      }
>    /* Cleanup.  */

Ok.

> @@ -311,8 +442,22 @@ compare (const struct cache_entry *e1, const struct cache_entry *e2)
>  	return 1;
>        else if (e1->flags > e2->flags)
>  	return -1;
> +      /* Keep the glibc-hwcaps extension entries before the regular
> +	 entries, and sort them by their names.  search_cache in
> +	 dl-cache.c stops searching once the first non-extension entry
> +	 is found, so the extension entries need to come first.  */
> +      else if (e1->hwcaps != NULL && e2->hwcaps == NULL)
> +	return -1;
> +      else if (e1->hwcaps == NULL && e2->hwcaps != NULL)
> +	return 1;
> +      else if (e1->hwcaps != NULL && e2->hwcaps != NULL)
> +	{
> +	  res = strcmp (e1->hwcaps->name->string, e2->hwcaps->name->string);

Maybe:

  res = strcmp (glibc_hwcaps_subdirectory_name (e1->hwcaps),
                glibc_hwcaps_subdirectory_name (e2->hwcaps));

> +	  if (res != 0)
> +	    return res;
> +	}
>        /* Sort by most specific hwcap.  */
> -      else if (e2->bits_hwcap > e1->bits_hwcap)
> +      if (e2->bits_hwcap > e1->bits_hwcap)
>  	return 1;
>        else if (e2->bits_hwcap < e1->bits_hwcap)
>  	return -1;

Ok.

> @@ -337,30 +482,65 @@ enum
>  			      * sizeof (struct cache_extension_section)))
>    };
>  
> -/* Write the cache extensions to FD.  The extension directory is
> -   assumed to be located at CACHE_EXTENSION_OFFSET.  */
> +/* Write the cache extensions to FD.  The string table is shifted by
> +   STRING_TABLE_OFFSET.  The extension directory is assumed to be
> +   located at CACHE_EXTENSION_OFFSET.  assign_glibc_hwcaps_indices
> +   must have been called.  */
>  static void
> -write_extensions (int fd, uint32_t cache_extension_offset)
> +write_extensions (int fd, uint32_t str_offset,
> +		  uint32_t cache_extension_offset)
>  {
>    assert ((cache_extension_offset % 4) == 0);
>  
> +  /* The length and contents of the glibc-hwcaps section.  */
> +  uint32_t hwcaps_count = glibc_hwcaps_count ();
> +  uint32_t hwcaps_offset = cache_extension_offset + cache_extension_size;
> +  uint32_t hwcaps_size = hwcaps_count * sizeof (uint32_t);
> +  uint32_t *hwcaps_array = xmalloc (hwcaps_size);
> +  for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next)
> +    if (p->used)
> +      hwcaps_array[p->section_index] = str_offset + p->name->offset;
> +
> +  /* This is the offset of the generator string.  */
> +  uint32_t generator_offset = hwcaps_offset;
> +  if (hwcaps_count == 0)
> +    /* There is no section for the hwcaps subdirectories.  */
> +    generator_offset -= sizeof (struct cache_extension_section);
> +  else
> +    /* The string table indices for the hwcaps subdirectories shift
> +       the generator string backwards.  */
> +    generator_offset += hwcaps_count * sizeof (uint32_t);

Maybe 

  generator_offset += hwcaps_size;

> +
>    struct cache_extension *ext = xmalloc (cache_extension_size);
>    ext->magic = cache_extension_magic;
> -  ext->count = cache_extension_count;
>  
> -  for (int i = 0; i < cache_extension_count; ++i)
> -    {
> -      ext->sections[i].tag = i;
> -      ext->sections[i].flags = 0;
> -    }

Ok, although maybe you could refactor the 'elf: Add extension mechanism to ld.so.cache'
to avoid add such code.

> +  /* Extension index current being filled.  */
> +  size_t xid = 0;
>  
>    const char *generator
>      = "ldconfig " PKGVERSION RELEASE " release version " VERSION;
> -  ext->sections[cache_extension_tag_generator].offset
> -    = cache_extension_offset + cache_extension_size;
> -  ext->sections[cache_extension_tag_generator].size = strlen (generator);
> +  ext->sections[xid].tag = cache_extension_tag_generator;
> +  ext->sections[xid].flags = 0;
> +  ext->sections[xid].offset = generator_offset;
> +  ext->sections[xid].size = strlen (generator);
> +
> +  if (hwcaps_count > 0)
> +    {
> +      ++xid;
> +      ext->sections[xid].tag = cache_extension_tag_glibc_hwcaps;
> +      ext->sections[xid].flags = 0;
> +      ext->sections[xid].offset = hwcaps_offset;
> +      ext->sections[xid].size = hwcaps_size;
> +    }
> +
> +  ++xid;
> +  ext->count = xid;
> +  assert (xid <= cache_extension_count);

Would it make more sense to reference the index directly using the
enumeration instead or add an assert to check if the index is within
the expected size?

>  
> -  if (write (fd, ext, cache_extension_size) != cache_extension_size
> +  size_t ext_size = (offsetof (struct cache_extension, sections)
> +		     + xid * sizeof (struct cache_extension_section));

So here we could just use cache_extension_count instead of 'xid' (with
the advantage that we certify at compile-time that only know
cache_extension_count will be written on the file).

> +  if (write (fd, ext, ext_size) != ext_size
> +      || write (fd, hwcaps_array, hwcaps_size) != hwcaps_size
>        || write (fd, generator, strlen (generator)) != strlen (generator))
>      error (EXIT_FAILURE, errno, _("Writing of cache extension data failed"));
>  
> @@ -373,6 +553,8 @@ save_cache (const char *cache_name)
>  {
>    /* The cache entries are sorted already, save them in this order. */
>  
> +  assign_glibc_hwcaps_indices ();
> +
>    struct cache_entry *entry;
>    /* Number of cache entries.  */
>    int cache_entry_count = 0;

Ok.

> @@ -474,7 +656,11 @@ save_cache (const char *cache_name)
>  	     struct.  */
>  	  file_entries_new->libs[idx_new].flags = entry->flags;
>  	  file_entries_new->libs[idx_new].osversion = entry->osversion;
> -	  file_entries_new->libs[idx_new].hwcap = entry->hwcap;
> +	  if (entry->hwcaps == NULL)
> +	    file_entries_new->libs[idx_new].hwcap = entry->hwcap;
> +	  else
> +	    file_entries_new->libs[idx_new].hwcap
> +	      = DL_CACHE_HWCAP_EXTENSION | entry->hwcaps->section_index;
>  	  file_entries_new->libs[idx_new].key
>  	    = str_offset + entry->lib->offset;
>  	  file_entries_new->libs[idx_new].value

Ok.

> @@ -554,7 +740,7 @@ save_cache (const char *cache_name)
>        /* Align file position to 4.  */
>        off64_t old_offset = lseek64 (fd, extension_offset, SEEK_SET);
>        assert ((unsigned long long int) (extension_offset - old_offset) < 4);
> -      write_extensions (fd, extension_offset);
> +      write_extensions (fd, str_offset, extension_offset);
>      }
>  
>    /* Make sure user can always read cache file */

Ok.

> @@ -588,8 +774,9 @@ save_cache (const char *cache_name)
>  
>  /* Add one library to the cache.  */
>  void
> -add_to_cache (const char *path, const char *lib, int flags,
> -	      unsigned int osversion, uint64_t hwcap)
> +add_to_cache (const char *path, const char *filename, const char *soname,
> +	      int flags, unsigned int osversion, uint64_t hwcap,
> +	      struct glibc_hwcaps_subdirectory *hwcaps)
>  {
>    struct cache_entry *new_entry = xmalloc (sizeof (*new_entry));
>  
> @@ -597,11 +784,11 @@ add_to_cache (const char *path, const char *lib, int flags,
>    {
>      /* Use a small, on-stack buffer in most cases.  */
>      char buf[200];
> -    int ret = snprintf (buf, sizeof (buf), "%s/%s", path, lib);
> +    int ret = snprintf (buf, sizeof (buf), "%s/%s", path, filename);
>      if (ret < 0 || ret >= sizeof (buf) - 1)
>        {
>  	char *p;
> -	if (asprintf (&p, "%s/%s", path, lib) < 0)
> +	if (asprintf (&p, "%s/%s", path, filename) < 0)
>  	  error (EXIT_FAILURE, errno, _("Could not create library path"));
>  	path_interned = stringtable_intern (&strings, p);
>  	free (p);

Ok.

> @@ -610,13 +797,20 @@ add_to_cache (const char *path, const char *lib, int flags,
>        path_interned = stringtable_intern (&strings, buf);
>    }
>  
> -  new_entry->lib = stringtable_intern (&strings, lib);
> +  new_entry->lib = stringtable_intern (&strings, soname);
>    new_entry->path = path_interned;
>    new_entry->flags = flags;
>    new_entry->osversion = osversion;
>    new_entry->hwcap = hwcap;
> +  new_entry->hwcaps = hwcaps;
>    new_entry->bits_hwcap = 0;
>  
> +  if (hwcaps != NULL)
> +    {
> +      assert (hwcap == 0);
> +      hwcaps->used = true;
> +    }
> +
>    /* Count the number of bits set in the masked value.  */
>    for (size_t i = 0;
>         (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i)

Ok.

> diff --git a/elf/ldconfig.c b/elf/ldconfig.c
> index 3768267bac..3136601de7 100644
> --- a/elf/ldconfig.c
> +++ b/elf/ldconfig.c
> @@ -16,6 +16,7 @@
>     along with this program; if not, see <https://www.gnu.org/licenses/>.  */
>  
>  #define PROCINFO_CLASS static
> +#include <assert.h>
>  #include <alloca.h>
>  #include <argp.h>
>  #include <dirent.h>
> @@ -41,6 +42,7 @@
>  
>  #include <ldconfig.h>
>  #include <dl-cache.h>
> +#include <dl-hwcaps.h>
>  
>  #include <dl-procinfo.h>
>  
> @@ -85,6 +87,10 @@ struct dir_entry
>    dev_t dev;
>    const char *from_file;
>    int from_line;
> +
> +  /* Non-NULL for subdirectories under a glibc-hwcaps subdirectory.  */
> +  struct glibc_hwcaps_subdirectory *hwcaps;
> +
>    struct dir_entry *next;
>  };
>  

Ok.

> @@ -339,17 +345,20 @@ new_sub_entry (const struct dir_entry *entry, const char *path,
>    new_entry->from_line = entry->from_line;
>    new_entry->path = xstrdup (path);
>    new_entry->flag = entry->flag;
> +  new_entry->hwcaps = NULL;
>    new_entry->next = NULL;
>    new_entry->ino = st->st_ino;
>    new_entry->dev = st->st_dev;
>    return new_entry;
>  }
>  
> -/* Add a single directory entry.  */
> -static void
> +/* Add a single directory entry.  Return true if the directory is
> +   actually added (because it is not a duplicate).  */
> +static bool
>  add_single_dir (struct dir_entry *entry, int verbose)
>  {
>    struct dir_entry *ptr, *prev;
> +  bool added = true;
>  
>    ptr = dir_entries;
>    prev = ptr;
> @@ -369,6 +378,7 @@ add_single_dir (struct dir_entry *entry, int verbose)
>  	  ptr->flag = entry->flag;
>  	  free (entry->path);
>  	  free (entry);
> +	  added = false;
>  	  break;
>  	}
>        prev = ptr;
> @@ -379,6 +389,73 @@ add_single_dir (struct dir_entry *entry, int verbose)
>      dir_entries = entry;
>    else if (ptr == NULL)
>      prev->next = entry;
> +  return added;
> +}
> +

Ok.

> +/* Check if PATH contains a "glibc-hwcaps" subdirectory.  If so, queue
> +   its subdirectories for glibc-hwcaps processing.  */
> +static void
> +add_glibc_hwcaps_subdirectories (struct dir_entry *entry, const char *path)
> +{
> +  /* glibc-hwcaps subdirectories do not nest.  */
> +  assert (entry->hwcaps == NULL);
> +
> +  char *glibc_hwcaps;
> +  if (asprintf (&glibc_hwcaps, "%s/" GLIBC_HWCAPS_SUBDIRECTORY, path) < 0)
> +    error (EXIT_FAILURE, errno, _("Could not form glibc-hwcaps path"));
> +
> +  DIR *dir = opendir (glibc_hwcaps);
> +  if (dir != NULL)
> +    {
> +      while (true)
> +	{
> +	  errno = 0;
> +	  struct dirent64 *e = readdir64 (dir);
> +	  if (e == NULL)
> +	    {
> +	      if (errno == 0)
> +		break;
> +	      else
> +		error (EXIT_FAILURE, errno, _("Listing directory %s"), path);
> +	    }
> +
> +	  /* Ignore hidden subdirectories, including "." and "..", and
> +	     regular files.  File names containing a ':' cannot be
> +	     looked up by the dynamic loader, so skip those as
> +	     well.  */
> +	  if (e->d_name[0] == '.' || e->d_type == DT_REG
> +	      || strchr (e->d_name, ':') != NULL)
> +	    continue;
> +
> +	  /* See if this entry eventually resolves to a directory.  */
> +	  struct stat64 st;
> +	  if (fstatat64 (dirfd (dir), e->d_name, &st, 0) < 0)
> +	    /* Ignore unreadable entries.  */
> +	    continue;
> +
> +	  if (S_ISDIR (st.st_mode))
> +	    {
> +	      /* This is a directory, so it needs to be scanned for
> +		 libraries, associated with the hwcaps implied by the
> +		 subdirectory name.  */
> +	      char *new_path;
> +	      if (asprintf (&new_path, "%s/" GLIBC_HWCAPS_SUBDIRECTORY "/%s",
> +			    /* Use non-canonicalized path here.  */
> +			    entry->path, e->d_name) < 0)
> +		error (EXIT_FAILURE, errno,
> +		       _("Could not form glibc-hwcaps path"));
> +	      struct dir_entry *new_entry = new_sub_entry (entry, new_path,
> +							   &st);
> +	      free (new_path);
> +	      new_entry->hwcaps = new_glibc_hwcaps_subdirectory (e->d_name);
> +	      add_single_dir (new_entry, 0);
> +	    }
> +	}
> +
> +      closedir (dir);
> +    }
> +
> +  free (glibc_hwcaps);
>  }
>  
>  /* Add one directory to the list of directories to process.  */

Ok.

> @@ -387,6 +464,7 @@ add_dir_1 (const char *line, const char *from_file, int from_line)
>  {
>    unsigned int i;
>    struct dir_entry *entry = xmalloc (sizeof (struct dir_entry));
> +  entry->hwcaps = NULL;
>    entry->next = NULL;
>  
>    entry->from_file = strdup (from_file);
> @@ -444,7 +522,9 @@ add_dir_1 (const char *line, const char *from_file, int from_line)
>        entry->ino = stat_buf.st_ino;
>        entry->dev = stat_buf.st_dev;
>  
> -      add_single_dir (entry, 1);
> +      if (add_single_dir (entry, 1))
> +	/* Add glibc-hwcaps subdirectories if present.  */
> +	add_glibc_hwcaps_subdirectories (entry, path);
>      }
>  
>    if (opt_chroot)

Ok.

> @@ -696,15 +776,27 @@ struct dlib_entry
>  static void
>  search_dir (const struct dir_entry *entry)
>  {
> -  uint64_t hwcap = path_hwcap (entry->path);
> -  if (opt_verbose)
> +  uint64_t hwcap;
> +  if (entry->hwcaps == NULL)
>      {
> -      if (hwcap != 0)
> -	printf ("%s: (hwcap: %#.16" PRIx64 ")", entry->path, hwcap);
> -      else
> -	printf ("%s:", entry->path);
> -      printf (_(" (from %s:%d)\n"), entry->from_file, entry->from_line);
> +      hwcap = path_hwcap (entry->path);
> +      if (opt_verbose)
> +	{
> +	  if (hwcap != 0)
> +	    printf ("%s: (hwcap: %#.16" PRIx64 ")", entry->path, hwcap);
> +	  else
> +	    printf ("%s:", entry->path);
> +	}
>      }
> +  else
> +    {
> +      hwcap = 0;
> +      if (opt_verbose)
> +	printf ("%s: (hwcap: \"%s\")", entry->path,
> +		glibc_hwcaps_subdirectory_name (entry->hwcaps));
> +    }
> +  if (opt_verbose)
> +    printf (_(" (from %s:%d)\n"), entry->from_file, entry->from_line);
>  
>    char *dir_name;
>    char *real_file_name;

Ok.

> @@ -746,13 +838,15 @@ search_dir (const struct dir_entry *entry)
>  	  && direntry->d_type != DT_DIR)
>  	continue;
>        /* Does this file look like a shared library or is it a hwcap
> -	 subdirectory?  The dynamic linker is also considered as
> +	 subdirectory (if not already processing a glibc-hwcaps
> +	 subdirectory)?  The dynamic linker is also considered as
>  	 shared library.  */
>        if (((strncmp (direntry->d_name, "lib", 3) != 0
>  	    && strncmp (direntry->d_name, "ld-", 3) != 0)
>  	   || strstr (direntry->d_name, ".so") == NULL)
>  	  && (direntry->d_type == DT_REG
> -	      || !is_hwcap_platform (direntry->d_name)))
> +	      || (entry->hwcaps == NULL
> +		  && !is_hwcap_platform (direntry->d_name))))
>  	continue;
>  
>        size_t len = strlen (direntry->d_name);

Ok.

> @@ -838,7 +932,10 @@ search_dir (const struct dir_entry *entry)
>        else
>  	is_dir = S_ISDIR (lstat_buf.st_mode);
>  
> -      if (is_dir && is_hwcap_platform (direntry->d_name))
> +      /* No descending into subdirectories if this directory is a
> +	 glibc-hwcaps subdirectory (which are not recursive).  */
> +      if (entry->hwcaps == NULL
> +	  && is_dir && is_hwcap_platform (direntry->d_name))
>  	{
>  	  if (!is_link
>  	      && direntry->d_type != DT_UNKNOWN

is_dir is an 'int', maybe make it a boolean?

> @@ -1029,13 +1126,31 @@ search_dir (const struct dir_entry *entry)
>    struct dlib_entry *dlib_ptr;
>    for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
>      {
> -      /* Don't create links to links.  */
> -      if (dlib_ptr->is_link == 0)
> -	create_links (dir_name, entry->path, dlib_ptr->name,
> -		      dlib_ptr->soname);
> +      /* The cached file name is the soname for non-glibc-hwcaps
> +	 subdirectories (relying on symbolic links; this helps with
> +	 library updates that change the file name), and the actual
> +	 file for glibc-hwcaps subdirectories.  */
> +      const char *filename;
> +      if (entry->hwcaps == NULL)
> +	{
> +	  /* Don't create links to links.  */
> +	  if (dlib_ptr->is_link == 0)
> +	    create_links (dir_name, entry->path, dlib_ptr->name,
> +			  dlib_ptr->soname);
> +	  filename = dlib_ptr->soname;
> +	}
> +      else
> +	{
> +	  /* Do not create links in glibc-hwcaps subdirectories, but
> +	     still log the cache addition.  */
> +	  if (opt_verbose)
> +	    printf ("\t%s -> %s\n", dlib_ptr->soname, dlib_ptr->name);
> +	  filename = dlib_ptr->name;
> +	}
>        if (opt_build_cache)
> -	add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag,
> -		      dlib_ptr->osversion, hwcap);
> +	add_to_cache (entry->path, filename, dlib_ptr->soname,
> +		      dlib_ptr->flag, dlib_ptr->osversion,
> +		      hwcap, entry->hwcaps);
>      }
>  
>    /* Free all resources.  */

Ok.

> diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
> index fec209509d..66b0312ac1 100644
> --- a/sysdeps/generic/dl-cache.h
> +++ b/sysdeps/generic/dl-cache.h
> @@ -81,7 +81,6 @@ struct cache_file
>  #define CACHE_VERSION "1.1"
>  #define CACHEMAGIC_VERSION_NEW CACHEMAGIC_NEW CACHE_VERSION
>  
> -

Spurious line removal.

>  struct file_entry_new
>  {
>    union
> @@ -99,6 +98,23 @@ struct file_entry_new
>    uint64_t hwcap;		/* Hwcap entry.	 */
>  };
>  
> +/* This bit in the hwcap field of struct file_entry_new indicates that
> +   the lower 32 bits contain an index into the
> +   cache_extension_tag_glibc_hwcaps section.  Older glibc versions do
> +   not know about this HWCAP bit, so they will ignore these
> +   entries.  */
> +#define DL_CACHE_HWCAP_EXTENSION (1ULL << 62)
> +
> +/* Return true if the ENTRY->hwcap value indicates that
> +   DL_CACHE_HWCAP_EXTENSION is used.  */
> +static inline bool
> +dl_cache_hwcap_extension (struct file_entry_new *entry)
> +{
> +  /* If DL_CACHE_HWCAP_EXTENSION is set, but other bits as well, this
> +     is a different kind of extension.  */
> +  return (entry->hwcap >> 32) == (DL_CACHE_HWCAP_EXTENSION >> 32);
> +}
> +
>  /* See flags member of struct cache_file_new below.  */
>  enum
>    {

Ok.

> @@ -161,6 +177,17 @@ enum cache_extension_tag
>        cache file.  */
>     cache_extension_tag_generator,
>  
> +   /* glibc-hwcaps subdirectory information.  An array of uint32_t
> +      values, which are indices into the string table.  The strings
> +      are sorted lexicographically (according to strcmp).  The extra
> +      level of indirection (instead of using string table indices
> +      directly) allows the dynamic loader to compute the preference
> +      order of the hwcaps names more efficiently.
> +
> +      For this section, 4-byte alignment is required, and the section
> +      size must be a multiple of 4.  */
> +   cache_extension_tag_glibc_hwcaps,
> +
>     /* Total number of known cache extension tags.  */
>     cache_extension_count
>    };

Ok.

> @@ -215,6 +242,27 @@ struct cache_extension_all_loaded
>    struct cache_extension_loaded sections[cache_extension_count];
>  };
>  
> +/* Performs basic data validation based on section tag, and removes
> +   the sections which are invalid.  */
> +static void
> +cache_extension_verify (struct cache_extension_all_loaded *loaded)
> +{
> +  {
> +    /* Section must not be empty, it must be aligned at 4 bytes, and
> +       the size must be a multiple of 4.  */
> +    struct cache_extension_loaded *hwcaps
> +      = &loaded->sections[cache_extension_tag_glibc_hwcaps];
> +    if (hwcaps->size == 0
> +	|| ((uintptr_t) hwcaps->base % 4) != 0
> +	|| (hwcaps->size % 4) != 0)
> +      {
> +	hwcaps->base = NULL;
> +	hwcaps->size = 0;
> +	hwcaps->flags = 0;
> +      }
> +  }
> +}
> +

Ok.

>  static bool __attribute__ ((unused))
>  cache_extension_load (const struct cache_file_new *cache,
>  		      const void *file_base, size_t file_size,
> @@ -261,6 +309,7 @@ cache_extension_load (const struct cache_file_new *cache,
>        loaded->sections[tag].size = ext->sections[i].size;
>        loaded->sections[tag].flags = ext->sections[i].flags;
>      }
> +  cache_extension_verify (loaded);
>    return true;
>  }
>  

Ok.

> diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h
> index b64aab0064..30a76481aa 100644
> --- a/sysdeps/generic/ldconfig.h
> +++ b/sysdeps/generic/ldconfig.h
> @@ -57,8 +57,22 @@ extern void init_cache (void);
>  
>  extern void save_cache (const char *cache_name);
>  
> -extern void add_to_cache (const char *path, const char *lib, int flags,
> -			  unsigned int osversion, uint64_t hwcap);
> +struct glibc_hwcaps_subdirectory;
> +
> +/* Return a struct describing the subdirectory for NAME.  Reuse an
> +   existing struct if it exists.  */
> +struct glibc_hwcaps_subdirectory *new_glibc_hwcaps_subdirectory
> +  (const char *name);
> +
> +/* Returns the name that was specified when
> +   add_glibc_hwcaps_subdirectory was called.  */
> +const char *glibc_hwcaps_subdirectory_name
> +  (struct glibc_hwcaps_subdirectory *);
> +
> +extern void add_to_cache (const char *path, const char *filename,
> +			  const char *soname,
> +			  int flags, unsigned int osversion, uint64_t hwcap,
> +			  struct glibc_hwcaps_subdirectory *);
>  
>  extern void init_aux_cache (void);
>  
> 

Ok.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 22/28] elf: Add extension mechanism to ld.so.cache
  2020-10-15 17:52   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-30 12:22     ` Florian Weimer via Libc-alpha
  2020-11-03 12:45       ` Adhemerval Zanella via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-30 12:22 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

>> +/* Print the extension information at the cache at start address
>> +   FILE_BASE, of ltength FILE_SIZE bytes.  The new-format cache header
>
> s/ltength/length

Fixed.

>> +   is at CACHE, and the file name for diagnostics is CACHE_NAME.  */
>> +static void
>> +print_extensions (struct cache_extension_all_loaded *ext)
>> +{
>> +  if (ext->sections[cache_extension_tag_generator].base != NULL)
>> +    {
>> +      fputs (_("Cache generated by: "), stdout);
>> +      fwrite (ext->sections[cache_extension_tag_generator].base, 1,
>> +	      ext->sections[cache_extension_tag_generator].size, stdout);
>> +      putchar ('\n');
>> +    }
>> +}
>> +
>
> Ok.  Will be the extension tag data always comprised of ascii printable
> characters?

For the generator extension: Yes.  But not for other extensions.
This code only deals with the generator extension.

>> +/* Size of the cache extension directory.  All tags are assumed to be
>> +   present.  */
>> +enum
>> +  {
>> +   cache_extension_size = (offsetof (struct cache_extension, sections)
>> +			   + (cache_extension_count
>> +			      * sizeof (struct cache_extension_section)))
>> +  };
>> +
>> +/* Write the cache extensions to FD.  The extension directory is
>> +   assumed to be located at CACHE_EXTENSION_OFFSET.  */
>> +static void
>> +write_extensions (int fd, uint32_t cache_extension_offset)
>> +{
>> +  assert ((cache_extension_offset % 4) == 0);
>
> Maybe a proper error msg instead of an assert here?

This is in the cache generator, so it's a code bug if the assert fires,
not corrupted input.

>> @@ -435,6 +497,25 @@ save_cache (const char *cache_name)
>>        && idx_old < cache_entry_old_count)
>>      file_entries->libs[idx_old] = file_entries->libs[idx_old - 1];
>>  
>> +  /* Compute the location of the extension directory.  This
>> +     implementation puts the directory after the string table.  The
>> +     size computation matches the write calls below.  The extension
>> +     directory does not exist with format 0, so the value does not
>> +     matter.  */
>> +  uint32_t extension_offset = 0;
>> +  if (opt_format != 2)
>> +    extension_offset += file_entries_size;
>> +  if (opt_format != 0)
>> +    {
>> +      if (opt_format != 2)
>> +	extension_offset += pad;
>> +      extension_offset += file_entries_new_size;
>> +    }
>
> Ok, although I think we should be good move the 'opt_format' definition to
> a proper enumeration.

Can this wait?  Something for a future patch?

>> +static bool __attribute__ ((unused))
>
> Maybe use inline and let the compiler decide? Or the function is
> really duplicate in a lot of places?

The compiler already decides for static functions and will inline them
if they are only used once.  Given the size of the function, I think
that's the appropriate approach here.

>> +cache_extension_load (const struct cache_file_new *cache,
>> +		      const void *file_base, size_t file_size,
>> +		      struct cache_extension_all_loaded *loaded)
>> +{
>> +  memset (loaded, 0, sizeof (*loaded));
>> +  if (cache->extension_offset == 0)
>> +    /* No extensions present.  This is not a format error.  */
>> +    return true;
>> +  if ((cache->extension_offset % 4) != 0)
>> +    /* Extension offset is misaligned.  */
>> +    return false;
>> +  size_t size_tmp;
>> +  if (__builtin_add_overflow (cache->extension_offset,
>> +			      sizeof (struct cache_extension), &size_tmp)
>> +      || size_tmp > file_size)
>> +    /* Extension extends beyond the end of the file.  */
>> +    return false;
>> +  const struct cache_extension *ext = file_base + cache->extension_offset;
>
> Maybe we should add an alignment check for 'file_base' as well (to
> avoid unaligned struct member deference)?

This pointer comes from mmap, so it's not something that can be wrong as
the result of the file data.  I could add an assert, but I don't think
this is likely to go wrong.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 23/28] elf: Unify old and new format cache handling code in ld.so
  2020-10-16 14:37   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-30 13:22     ` Florian Weimer via Libc-alpha
  2020-11-03 13:02       ` Adhemerval Zanella via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-30 13:22 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

>> +/* Compute the address of the element INDEX of the array at LIBS.
>> +   Conceptually, this is &LIBS[INDEX], but use ENTRY_SIZE for the size
>> +   of *LIBS.  */
>> +static inline const struct file_entry *
>> +_dl_cache_file_entry (const struct file_entry *libs, size_t entry_size,
>> +		      size_t index)
>> +{
>> +  return (const void *) libs + index * entry_size;
>> +}
>> +
>
> Although pointer arithmetic with void *is valid for gcc, I think we should 
> use uintptr_t here.

Do you mean char *?  It will need another cast for the result.  I don't
think it clarifies matters.

>> +{
>> +  /* Used by the HWCAP check in the struct file_entry_new case.  */
>> +  uint64_t platform = _dl_string_platform (GLRO (dl_platform));
>> +  if (platform != (uint64_t) -1)
>> +    platform = 1ULL << platform;
>
> Maybe UINT64_C(1) << platform?

1ULL needs to be at least 64 bits, so this should be fine.

>> +
>> +		      best = string_table + lib->value;
>> +
>> +		      if (flags == GLRO (dl_correct_cache_id))
>> +			/* We've found an exact match for the shared
>> +			   object and no general `ELF' release.  Stop
>
> Should we replace the grave accent with apostrophe in this case?

I want to minimize the changes in this refactoring.

>> diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
>> index b154740da9..fec209509d 100644
>> --- a/sysdeps/generic/dl-cache.h
>> +++ b/sysdeps/generic/dl-cache.h
>> @@ -66,8 +66,8 @@
>>  */
>>  struct file_entry
>>  {
>> -  int flags;		/* This is 1 for an ELF library.  */
>> -  unsigned int key, value; /* String table indices.  */
>> +  int32_t flags;		/* This is 1 for an ELF library.  */
>> +  uint32_t key, value;		/* String table indices.  */
>>  };
>>  
>
> Ok, although not sure if our code guidelines allows multiple fields in the
> same line.

Likewise.

I'll assume your Reviewed-by: stands and push this separately.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 24/28] elf: Implement a string table for ldconfig, with tail merging
  2020-10-20 14:25   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-30 17:08     ` Florian Weimer via Libc-alpha
  2020-11-03 13:05       ` Adhemerval Zanella via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-30 17:08 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

> On 01/10/2020 13:34, Florian Weimer via Libc-alpha wrote:
>> This will be used in ldconfig to reduce the ld.so.cache size slightly.
>
> Could you extend the commit message to explain why the 32-bit FNV-1a hash
> is used and how the hash table is organized (how collisions are handled,
> expected memory usage, strategy used to increase/decrease the bucket size)?
>
> It could help also to explain the usercase a bit more, the 'tail merging'
> is not straightforward to understand without dig deep in the code.  Also
> why kind of cache size decrease do you expect by using strategy?

That depends whether cached DSOs use sonames as file names (so that no
symbolic links are needed).  Typically that's not the case today, so the
savings are really small.  glibc-hwcaps may change that.

The repost will have an expanded commit message:

elf: Implement a string table for ldconfig, with tail merging

This will be used in ldconfig to reduce the ld.so.cache size slightly.

Tail merging is an optimization where a pointer points into another
string if the first string is a suffix of the second string.

The hash function FNV-1a was chosen because it is simple and achieves
good dispersion even for short strings (so that the hash table bucket
count can be a power of two).  It is clearly superior to the hsearch
hash and the ELF hash in this regard.

The hash table uses chaining for collision resolution.

>> diff --git a/elf/stringtable.c b/elf/stringtable.c
>> new file mode 100644
>> index 0000000000..f9ade50249
>> --- /dev/null
>> +++ b/elf/stringtable.c
>> @@ -0,0 +1,201 @@
>> +/* String tables for ld.so.cache construction.  Implementation.
>
> This file misses the Copyright year.

Fixed throughout.

>> +static void
>> +stringtable_init (struct stringtable *table)
>> +{
>> +  table->count = 0;
>> +  table->allocated = 16;
>> +  table->entries = xcalloc (table->allocated, sizeof (table->entries[0]));
>> +}
>> +
>
> Why 16 elements as initial size?

I'm increasing it to 128 with a comment.  128 is based on the number of
DSOs within glibc itself.

>> +struct stringtable_entry *
>> +stringtable_intern (struct stringtable *table, const char *string)
>> +{
>> +  if (table->allocated == 0)
>> +    stringtable_init (table);
>
> How this could happen? Is it expect the caller to set 'allocated'
> explicitly?

Zero-initialization is valid.  stringtable_free also leaves the table
ready for re-use.

>> +  /* Copy the strings.  */
>> +  for (uint32_t i = 0; i < table->allocated; ++i)
>> +    for (struct stringtable_entry *e = table->entries[i]; e != NULL;
>> +         e = e->next)
>> +      if (result->strings[e->offset] == '\0')
>> +        memcpy (&result->strings[e->offset], e->string, e->length + 1);
>> +}
>
> Ok, I guess allocating a new stringtable_finalized should be simpler than
> operating the table itself.

Sorry, I don't understand.  Do you mean reusing the table allocation in
some way?  Yes, that would be fairly complicated.

>> +/* Adds STRING to TABLE.  May return the address of an existing entry.  */
>> +struct stringtable_entry *stringtable_intern (struct stringtable *table,
>> +                                              const char *string);
>
> I think this name is confusing, why not just 'stringtable_add' or
> 'stringtable_add_element'?

I'm changing it to stringtable_add.  Didn't realize that interning is
obscure terminology.

Thanls,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 25/28] elf: Implement tail merging of strings in ldconfig
  2020-10-22 21:08   ` Adhemerval Zanella via Libc-alpha
@ 2020-10-30 17:36     ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-10-30 17:36 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

>> diff --git a/elf/cache.c b/elf/cache.c
>> index 3a02a4070a..eda3da98a7 100644
>> --- a/elf/cache.c
>> +++ b/elf/cache.c
>> @@ -35,11 +35,15 @@
>>  #include <ldconfig.h>
>>  #include <dl-cache.h>
>>  #include <version.h>
>> +#include <stringtable.h>
>> +
>> +/* Used to store library names, paths, and other strings.  */
>> +struct stringtable strings;
>
> Maybe static here?

Right, added.

>> @@ -596,14 +591,27 @@ void
>>  add_to_cache (const char *path, const char *lib, int flags,
>>  	      unsigned int osversion, uint64_t hwcap)
>>  {
>> +  struct cache_entry *new_entry = xmalloc (sizeof (*new_entry));
>> +
>> +  struct stringtable_entry *path_interned;
>> +  {
>> +    /* Use a small, on-stack buffer in most cases.  */
>> +    char buf[200];
>> +    int ret = snprintf (buf, sizeof (buf), "%s/%s", path, lib);
>> +    if (ret < 0 || ret >= sizeof (buf) - 1)
>> +      {
>> +	char *p;
>> +	if (asprintf (&p, "%s/%s", path, lib) < 0)
>> +	  error (EXIT_FAILURE, errno, _("Could not create library path"));
>> +	path_interned = stringtable_intern (&strings, p);
>> +	free (p);
>> +      }
>> +    else
>> +      path_interned = stringtable_intern (&strings, buf);
>> +  }
>> +
>> +  new_entry->lib = stringtable_intern (&strings, lib);
>> +  new_entry->path = path_interned;
>>    new_entry->flags = flags;
>>    new_entry->osversion = osversion;
>>    new_entry->hwcap = hwcap;
>> 
>
> Ok. Is this small string optimization really worth instead of just using
> asprintf?

Probably not, due to the tcache.  I'm going to remove it.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 22/28] elf: Add extension mechanism to ld.so.cache
  2020-10-30 12:22     ` Florian Weimer via Libc-alpha
@ 2020-11-03 12:45       ` Adhemerval Zanella via Libc-alpha
  2020-11-03 15:30         ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-11-03 12:45 UTC (permalink / raw)
  To: Florian Weimer, Adhemerval Zanella via Libc-alpha



On 30/10/2020 09:22, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>>> +/* Print the extension information at the cache at start address
>>> +   FILE_BASE, of ltength FILE_SIZE bytes.  The new-format cache header
>>
>> s/ltength/length
> 
> Fixed.
> 
>>> +   is at CACHE, and the file name for diagnostics is CACHE_NAME.  */
>>> +static void
>>> +print_extensions (struct cache_extension_all_loaded *ext)
>>> +{
>>> +  if (ext->sections[cache_extension_tag_generator].base != NULL)
>>> +    {
>>> +      fputs (_("Cache generated by: "), stdout);
>>> +      fwrite (ext->sections[cache_extension_tag_generator].base, 1,
>>> +	      ext->sections[cache_extension_tag_generator].size, stdout);
>>> +      putchar ('\n');
>>> +    }
>>> +}
>>> +
>>
>> Ok.  Will be the extension tag data always comprised of ascii printable
>> characters?
> 
> For the generator extension: Yes.  But not for other extensions.
> This code only deals with the generator extension.
> 
>>> +/* Size of the cache extension directory.  All tags are assumed to be
>>> +   present.  */
>>> +enum
>>> +  {
>>> +   cache_extension_size = (offsetof (struct cache_extension, sections)
>>> +			   + (cache_extension_count
>>> +			      * sizeof (struct cache_extension_section)))
>>> +  };
>>> +
>>> +/* Write the cache extensions to FD.  The extension directory is
>>> +   assumed to be located at CACHE_EXTENSION_OFFSET.  */
>>> +static void
>>> +write_extensions (int fd, uint32_t cache_extension_offset)
>>> +{
>>> +  assert ((cache_extension_offset % 4) == 0);
>>
>> Maybe a proper error msg instead of an assert here?
> 
> This is in the cache generator, so it's a code bug if the assert fires,
> not corrupted input.

Ack.

> 
>>> @@ -435,6 +497,25 @@ save_cache (const char *cache_name)
>>>        && idx_old < cache_entry_old_count)
>>>      file_entries->libs[idx_old] = file_entries->libs[idx_old - 1];
>>>  
>>> +  /* Compute the location of the extension directory.  This
>>> +     implementation puts the directory after the string table.  The
>>> +     size computation matches the write calls below.  The extension
>>> +     directory does not exist with format 0, so the value does not
>>> +     matter.  */
>>> +  uint32_t extension_offset = 0;
>>> +  if (opt_format != 2)
>>> +    extension_offset += file_entries_size;
>>> +  if (opt_format != 0)
>>> +    {
>>> +      if (opt_format != 2)
>>> +	extension_offset += pad;
>>> +      extension_offset += file_entries_new_size;
>>> +    }
>>
>> Ok, although I think we should be good move the 'opt_format' definition to
>> a proper enumeration.
> 
> Can this wait?  Something for a future patch?

Alright, although it does simplify reading this patchset and the
change should most mechanical. 

> 
>>> +static bool __attribute__ ((unused))
>>
>> Maybe use inline and let the compiler decide? Or the function is
>> really duplicate in a lot of places?
> 
> The compiler already decides for static functions and will inline them
> if they are only used once.  Given the size of the function, I think
> that's the appropriate approach here.

Ack.

> 
>>> +cache_extension_load (const struct cache_file_new *cache,
>>> +		      const void *file_base, size_t file_size,
>>> +		      struct cache_extension_all_loaded *loaded)
>>> +{
>>> +  memset (loaded, 0, sizeof (*loaded));
>>> +  if (cache->extension_offset == 0)
>>> +    /* No extensions present.  This is not a format error.  */
>>> +    return true;
>>> +  if ((cache->extension_offset % 4) != 0)
>>> +    /* Extension offset is misaligned.  */
>>> +    return false;
>>> +  size_t size_tmp;
>>> +  if (__builtin_add_overflow (cache->extension_offset,
>>> +			      sizeof (struct cache_extension), &size_tmp)
>>> +      || size_tmp > file_size)
>>> +    /* Extension extends beyond the end of the file.  */
>>> +    return false;
>>> +  const struct cache_extension *ext = file_base + cache->extension_offset;
>>
>> Maybe we should add an alignment check for 'file_base' as well (to
>> avoid unaligned struct member deference)?
> 
> This pointer comes from mmap, so it's not something that can be wrong as
> the result of the file data.  I could add an assert, but I don't think
> this is likely to go wrong.

Ack.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 23/28] elf: Unify old and new format cache handling code in ld.so
  2020-10-30 13:22     ` Florian Weimer via Libc-alpha
@ 2020-11-03 13:02       ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-11-03 13:02 UTC (permalink / raw)
  To: Florian Weimer, Adhemerval Zanella via Libc-alpha



On 30/10/2020 10:22, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>>> +/* Compute the address of the element INDEX of the array at LIBS.
>>> +   Conceptually, this is &LIBS[INDEX], but use ENTRY_SIZE for the size
>>> +   of *LIBS.  */
>>> +static inline const struct file_entry *
>>> +_dl_cache_file_entry (const struct file_entry *libs, size_t entry_size,
>>> +		      size_t index)
>>> +{
>>> +  return (const void *) libs + index * entry_size;
>>> +}
>>> +
>>
>> Although pointer arithmetic with void *is valid for gcc, I think we should 
>> use uintptr_t here.
> 
> Do you mean char *?  It will need another cast for the result.  I don't
> think it clarifies matters.

I don't have a strong opinion in fact, it just is might not be straightforward
for a reader not used with GNU C extensions.

> 
>>> +{
>>> +  /* Used by the HWCAP check in the struct file_entry_new case.  */
>>> +  uint64_t platform = _dl_string_platform (GLRO (dl_platform));
>>> +  if (platform != (uint64_t) -1)
>>> +    platform = 1ULL << platform;
>>
>> Maybe UINT64_C(1) << platform?
> 
> 1ULL needs to be at least 64 bits, so this should be fine.
> 
>>> +
>>> +		      best = string_table + lib->value;
>>> +
>>> +		      if (flags == GLRO (dl_correct_cache_id))
>>> +			/* We've found an exact match for the shared
>>> +			   object and no general `ELF' release.  Stop
>>
>> Should we replace the grave accent with apostrophe in this case?
> 
> I want to minimize the changes in this refactoring.

But git shows this as new lines anyway, so I see no good reason to
not replace it with an apostrophe.

> 
>>> diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
>>> index b154740da9..fec209509d 100644
>>> --- a/sysdeps/generic/dl-cache.h
>>> +++ b/sysdeps/generic/dl-cache.h
>>> @@ -66,8 +66,8 @@
>>>  */
>>>  struct file_entry
>>>  {
>>> -  int flags;		/* This is 1 for an ELF library.  */
>>> -  unsigned int key, value; /* String table indices.  */
>>> +  int32_t flags;		/* This is 1 for an ELF library.  */
>>> +  uint32_t key, value;		/* String table indices.  */
>>>  };
>>>  
>>
>> Ok, although not sure if our code guidelines allows multiple fields in the
>> same line.
> 
> Likewise.
> 
> I'll assume your Reviewed-by: stands and push this separately.

LGTM.

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 24/28] elf: Implement a string table for ldconfig, with tail merging
  2020-10-30 17:08     ` Florian Weimer via Libc-alpha
@ 2020-11-03 13:05       ` Adhemerval Zanella via Libc-alpha
  2020-11-03 15:29         ` Florian Weimer via Libc-alpha
  0 siblings, 1 reply; 103+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2020-11-03 13:05 UTC (permalink / raw)
  To: Florian Weimer, Adhemerval Zanella via Libc-alpha



On 30/10/2020 14:08, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> On 01/10/2020 13:34, Florian Weimer via Libc-alpha wrote:
>>> This will be used in ldconfig to reduce the ld.so.cache size slightly.
>>
>> Could you extend the commit message to explain why the 32-bit FNV-1a hash
>> is used and how the hash table is organized (how collisions are handled,
>> expected memory usage, strategy used to increase/decrease the bucket size)?
>>
>> It could help also to explain the usercase a bit more, the 'tail merging'
>> is not straightforward to understand without dig deep in the code.  Also
>> why kind of cache size decrease do you expect by using strategy?
> 
> That depends whether cached DSOs use sonames as file names (so that no
> symbolic links are needed).  Typically that's not the case today, so the
> savings are really small.  glibc-hwcaps may change that.
> 
> The repost will have an expanded commit message:
> 
> elf: Implement a string table for ldconfig, with tail merging
> 
> This will be used in ldconfig to reduce the ld.so.cache size slightly.
> 
> Tail merging is an optimization where a pointer points into another
> string if the first string is a suffix of the second string.
> 
> The hash function FNV-1a was chosen because it is simple and achieves
> good dispersion even for short strings (so that the hash table bucket
> count can be a power of two).  It is clearly superior to the hsearch
> hash and the ELF hash in this regard.
> 
> The hash table uses chaining for collision resolution.

Looks better, thanks.

> 
>>> diff --git a/elf/stringtable.c b/elf/stringtable.c
>>> new file mode 100644
>>> index 0000000000..f9ade50249
>>> --- /dev/null
>>> +++ b/elf/stringtable.c
>>> @@ -0,0 +1,201 @@
>>> +/* String tables for ld.so.cache construction.  Implementation.
>>
>> This file misses the Copyright year.
> 
> Fixed throughout.
> 
>>> +static void
>>> +stringtable_init (struct stringtable *table)
>>> +{
>>> +  table->count = 0;
>>> +  table->allocated = 16;
>>> +  table->entries = xcalloc (table->allocated, sizeof (table->entries[0]));
>>> +}
>>> +
>>
>> Why 16 elements as initial size?
> 
> I'm increasing it to 128 with a comment.  128 is based on the number of
> DSOs within glibc itself.
> 
>>> +struct stringtable_entry *
>>> +stringtable_intern (struct stringtable *table, const char *string)
>>> +{
>>> +  if (table->allocated == 0)
>>> +    stringtable_init (table);
>>
>> How this could happen? Is it expect the caller to set 'allocated'
>> explicitly?
> 
> Zero-initialization is valid.  stringtable_free also leaves the table
> ready for re-use.

Wouldn't both usages make the above check unnecessary? 

> 
>>> +  /* Copy the strings.  */
>>> +  for (uint32_t i = 0; i < table->allocated; ++i)
>>> +    for (struct stringtable_entry *e = table->entries[i]; e != NULL;
>>> +         e = e->next)
>>> +      if (result->strings[e->offset] == '\0')
>>> +        memcpy (&result->strings[e->offset], e->string, e->length + 1);
>>> +}
>>
>> Ok, I guess allocating a new stringtable_finalized should be simpler than
>> operating the table itself.
> 
> Sorry, I don't understand.  Do you mean reusing the table allocation in
> some way?  Yes, that would be fairly complicated.

Yes, to avoid allocation memory on both new item insertion and on the
finalize operation itself. But I think such optimization does not really
matter for the ldconfig usage.

> 
>>> +/* Adds STRING to TABLE.  May return the address of an existing entry.  */
>>> +struct stringtable_entry *stringtable_intern (struct stringtable *table,
>>> +                                              const char *string);
>>
>> I think this name is confusing, why not just 'stringtable_add' or
>> 'stringtable_add_element'?
> 
> I'm changing it to stringtable_add.  Didn't realize that interning is
> obscure terminology.
> 
> Thanls,
> Florian
> 

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 24/28] elf: Implement a string table for ldconfig, with tail merging
  2020-11-03 13:05       ` Adhemerval Zanella via Libc-alpha
@ 2020-11-03 15:29         ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-11-03 15:29 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella:

>>>> +struct stringtable_entry *
>>>> +stringtable_intern (struct stringtable *table, const char *string)
>>>> +{
>>>> +  if (table->allocated == 0)
>>>> +    stringtable_init (table);
>>>
>>> How this could happen? Is it expect the caller to set 'allocated'
>>> explicitly?
>> 
>> Zero-initialization is valid.  stringtable_free also leaves the table
>> ready for re-use.
>
> Wouldn't both usages make the above check unnecessary?

Doubling zero is still zero, so the growth code path doesn't work.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 22/28] elf: Add extension mechanism to ld.so.cache
  2020-11-03 12:45       ` Adhemerval Zanella via Libc-alpha
@ 2020-11-03 15:30         ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-11-03 15:30 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella:

>>> Ok, although I think we should be good move the 'opt_format' definition to
>>> a proper enumeration.
>> 
>> Can this wait?  Something for a future patch?
>
> Alright, although it does simplify reading this patchset and the
> change should most mechanical. 

I'll add a separate patch for this to the start of the series.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH 27/28] elf: Process glibc-hwcaps subdirectories in ldconfig
  2020-10-27 17:28   ` Adhemerval Zanella via Libc-alpha
@ 2020-11-04 11:57     ` Florian Weimer via Libc-alpha
  0 siblings, 0 replies; 103+ messages in thread
From: Florian Weimer via Libc-alpha @ 2020-11-04 11:57 UTC (permalink / raw)
  To: Adhemerval Zanella via Libc-alpha

* Adhemerval Zanella via Libc-alpha:

>> +/* Helper for sorting struct glibc_hwcaps_subdirectory elements by
>> +   name.  */
>> +static int
>> +assign_glibc_hwcaps_indices_compare (const void *l, const void *r)
>> +{
>> +  const struct glibc_hwcaps_subdirectory *left
>> +    = *(struct glibc_hwcaps_subdirectory **)l;
>> +  const struct glibc_hwcaps_subdirectory *right
>> +    = *(struct glibc_hwcaps_subdirectory **)r;
>> +  return strcmp (left->name->string, right->name->string);
>> +}
>> +
>
> Maybe:
>
>   strcmp (glibc_hwcaps_subdirectory_name (left),
>           glibc_hwcaps_subdirectory_name (right));

Fixed.

>> +/* Compute the section_index fields for all   */
>> +static void
>> +assign_glibc_hwcaps_indices (void)
>> +{
>> +  /* Convert the linked list into an array, so that we can use qsort.
>> +     Only copy the subdirectories which are actually used.  */
>> +  size_t count = glibc_hwcaps_count ();
>> +  struct glibc_hwcaps_subdirectory **array
>> +    = xmalloc (sizeof (*array) * count);
>> +  {
>> +    size_t i = 0;
>> +    for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next)
>> +      if (p->used)
>> +	{
>> +	  array[i] = p;
>> +	  ++i;
>> +	}
>> +    assert (i == count);
>
> Do we need this assert? I think it would make sense if hwcaps is modified
> concurrently, which does not seem the case.

It documents that the loop processed the entire array, consistent with
glibc_hwcaps_count.  Right now, the function is defined immediately
above, but that could change, and I think it is reasonable to capture
this dependency.

>> @@ -311,8 +442,22 @@ compare (const struct cache_entry *e1, const struct cache_entry *e2)
>>  	return 1;
>>        else if (e1->flags > e2->flags)
>>  	return -1;
>> +      /* Keep the glibc-hwcaps extension entries before the regular
>> +	 entries, and sort them by their names.  search_cache in
>> +	 dl-cache.c stops searching once the first non-extension entry
>> +	 is found, so the extension entries need to come first.  */
>> +      else if (e1->hwcaps != NULL && e2->hwcaps == NULL)
>> +	return -1;
>> +      else if (e1->hwcaps == NULL && e2->hwcaps != NULL)
>> +	return 1;
>> +      else if (e1->hwcaps != NULL && e2->hwcaps != NULL)
>> +	{
>> +	  res = strcmp (e1->hwcaps->name->string, e2->hwcaps->name->string);
>
> Maybe:
>
>   res = strcmp (glibc_hwcaps_subdirectory_name (e1->hwcaps),
>                 glibc_hwcaps_subdirectory_name (e2->hwcaps));

Fixed.

>> -/* Write the cache extensions to FD.  The extension directory is
>> -   assumed to be located at CACHE_EXTENSION_OFFSET.  */
>> +/* Write the cache extensions to FD.  The string table is shifted by
>> +   STRING_TABLE_OFFSET.  The extension directory is assumed to be
>> +   located at CACHE_EXTENSION_OFFSET.  assign_glibc_hwcaps_indices
>> +   must have been called.  */
>>  static void
>> -write_extensions (int fd, uint32_t cache_extension_offset)
>> +write_extensions (int fd, uint32_t str_offset,
>> +		  uint32_t cache_extension_offset)
>>  {
>>    assert ((cache_extension_offset % 4) == 0);
>>  
>> +  /* The length and contents of the glibc-hwcaps section.  */
>> +  uint32_t hwcaps_count = glibc_hwcaps_count ();
>> +  uint32_t hwcaps_offset = cache_extension_offset + cache_extension_size;
>> +  uint32_t hwcaps_size = hwcaps_count * sizeof (uint32_t);
>> +  uint32_t *hwcaps_array = xmalloc (hwcaps_size);
>> +  for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next)
>> +    if (p->used)
>> +      hwcaps_array[p->section_index] = str_offset + p->name->offset;
>> +
>> +  /* This is the offset of the generator string.  */
>> +  uint32_t generator_offset = hwcaps_offset;
>> +  if (hwcaps_count == 0)
>> +    /* There is no section for the hwcaps subdirectories.  */
>> +    generator_offset -= sizeof (struct cache_extension_section);
>> +  else
>> +    /* The string table indices for the hwcaps subdirectories shift
>> +       the generator string backwards.  */
>> +    generator_offset += hwcaps_count * sizeof (uint32_t);
>
> Maybe 
>
>   generator_offset += hwcaps_size;

Fixed.

>>    struct cache_extension *ext = xmalloc (cache_extension_size);
>>    ext->magic = cache_extension_magic;
>> -  ext->count = cache_extension_count;
>>  
>> -  for (int i = 0; i < cache_extension_count; ++i)
>> -    {
>> -      ext->sections[i].tag = i;
>> -      ext->sections[i].flags = 0;
>> -    }
>
> Ok, although maybe you could refactor the 'elf: Add extension
> mechanism to ld.so.cache' to avoid add such code.

This is still quite ad-hoc.  I expect that the code will change again
when we have additional extensions and a common pattern emerges.

>> +  /* Extension index current being filled.  */
>> +  size_t xid = 0;
>>  
>>    const char *generator
>>      = "ldconfig " PKGVERSION RELEASE " release version " VERSION;
>> -  ext->sections[cache_extension_tag_generator].offset
>> -    = cache_extension_offset + cache_extension_size;
>> -  ext->sections[cache_extension_tag_generator].size = strlen (generator);
>> +  ext->sections[xid].tag = cache_extension_tag_generator;
>> +  ext->sections[xid].flags = 0;
>> +  ext->sections[xid].offset = generator_offset;
>> +  ext->sections[xid].size = strlen (generator);
>> +
>> +  if (hwcaps_count > 0)
>> +    {
>> +      ++xid;
>> +      ext->sections[xid].tag = cache_extension_tag_glibc_hwcaps;
>> +      ext->sections[xid].flags = 0;
>> +      ext->sections[xid].offset = hwcaps_offset;
>> +      ext->sections[xid].size = hwcaps_size;
>> +    }
>> +
>> +  ++xid;
>> +  ext->count = xid;
>> +  assert (xid <= cache_extension_count);
>
> Would it make more sense to reference the index directly using the
> enumeration instead or add an assert to check if the index is within
> the expected size?

In the future, the index does not necessarily equal the tag value.  We
don't write the glibc-hwcaps extension if no such subdirectories exist.

>> -  if (write (fd, ext, cache_extension_size) != cache_extension_size
>> +  size_t ext_size = (offsetof (struct cache_extension, sections)
>> +		     + xid * sizeof (struct cache_extension_section));
>
> So here we could just use cache_extension_count instead of 'xid' (with
> the advantage that we certify at compile-time that only know
> cache_extension_count will be written on the file).

I think we shouldn't write extensions that aren't used.  It will help to
make sure that the loader code is tolerant of extensions.

>> @@ -838,7 +932,10 @@ search_dir (const struct dir_entry *entry)
>>        else
>>  	is_dir = S_ISDIR (lstat_buf.st_mode);
>>  
>> -      if (is_dir && is_hwcap_platform (direntry->d_name))
>> +      /* No descending into subdirectories if this directory is a
>> +	 glibc-hwcaps subdirectory (which are not recursive).  */
>> +      if (entry->hwcaps == NULL
>> +	  && is_dir && is_hwcap_platform (direntry->d_name))
>>  	{
>>  	  if (!is_link
>>  	      && direntry->d_type != DT_UNKNOWN
>
> is_dir is an 'int', maybe make it a boolean?

Fixed.

>> diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
>> index fec209509d..66b0312ac1 100644
>> --- a/sysdeps/generic/dl-cache.h
>> +++ b/sysdeps/generic/dl-cache.h
>> @@ -81,7 +81,6 @@ struct cache_file
>>  #define CACHE_VERSION "1.1"
>>  #define CACHEMAGIC_VERSION_NEW CACHEMAGIC_NEW CACHE_VERSION
>>  
>> -
>
> Spurious line removal.

Fixed.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 103+ messages in thread

end of thread, other threads:[~2020-11-04 11:57 UTC | newest]

Thread overview: 103+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-01 16:31 [PATCH 00/28] glibc-hwcaps support Florian Weimer via Libc-alpha
2020-10-01 16:31 ` [PATCH 01/28] elf: Do not search HWCAP subdirectories in statically linked binaries Florian Weimer via Libc-alpha
2020-10-01 18:22   ` Adhemerval Zanella via Libc-alpha
2020-10-01 18:24     ` Carlos O'Donell via Libc-alpha
2020-10-01 18:29       ` Adhemerval Zanella via Libc-alpha
2020-10-01 20:24         ` Carlos O'Donell via Libc-alpha
2020-10-01 16:31 ` [PATCH 02/28] elf: Implement __rtld_malloc_is_full Florian Weimer via Libc-alpha
2020-10-01 18:23   ` Adhemerval Zanella via Libc-alpha
2020-10-08  9:44     ` Florian Weimer via Libc-alpha
2020-10-01 16:31 ` [PATCH 03/28] elf: Implement _dl_write Florian Weimer via Libc-alpha
2020-10-05 19:46   ` Adhemerval Zanella via Libc-alpha
2020-10-01 16:31 ` [PATCH 04/28] elf: Extract command-line/environment variables state from rtld.c Florian Weimer via Libc-alpha
2020-10-06 20:45   ` Adhemerval Zanella via Libc-alpha
2020-10-08 11:32     ` Florian Weimer via Libc-alpha
2020-10-01 16:32 ` [PATCH 05/28] elf: Move ld.so error/help output to _dl_usage Florian Weimer via Libc-alpha
2020-10-06 21:06   ` Adhemerval Zanella via Libc-alpha
2020-10-08 12:19     ` Florian Weimer via Libc-alpha
2020-10-01 16:32 ` [PATCH 06/28] elf: Record whether paths come from LD_LIBRARY_PATH or --library-path Florian Weimer via Libc-alpha
2020-10-07 16:39   ` Adhemerval Zanella via Libc-alpha
2020-10-07 16:49     ` Florian Weimer
2020-10-01 16:32 ` [PATCH 07/28] elf: Implement ld.so --help Florian Weimer via Libc-alpha
2020-10-07 17:16   ` Adhemerval Zanella via Libc-alpha
2020-10-08 13:13     ` Florian Weimer via Libc-alpha
2020-10-01 16:32 ` [PATCH 08/28] elf: Implement ld.so --version Florian Weimer via Libc-alpha
2020-10-07 18:36   ` Adhemerval Zanella via Libc-alpha
2020-10-07 18:38     ` Adhemerval Zanella via Libc-alpha
2020-10-08 13:37     ` Florian Weimer via Libc-alpha
2020-10-01 16:32 ` [PATCH 09/28] scripts/update-copyrights: Update csu/version.c, elf/dl-usage.c Florian Weimer via Libc-alpha
2020-10-07 18:41   ` Adhemerval Zanella via Libc-alpha
2020-10-01 16:32 ` [PATCH 10/28] elf: Use the term "program interpreter" in the ld.so help message Florian Weimer via Libc-alpha
2020-10-07 21:08   ` Adhemerval Zanella via Libc-alpha
2020-10-08 14:08     ` Florian Weimer via Libc-alpha
2020-10-01 16:32 ` [PATCH 11/28] elf: Print the full name of the dynamic loader " Florian Weimer via Libc-alpha
2020-10-08 12:38   ` Adhemerval Zanella via Libc-alpha
2020-10-01 16:32 ` [PATCH 12/28] elf: Make __rtld_env_path_list and __rtld_search_dirs global variables Florian Weimer via Libc-alpha
2020-10-08 13:27   ` Adhemerval Zanella via Libc-alpha
2020-10-01 16:32 ` [PATCH 13/28] elf: Add library search path information to ld.so --help Florian Weimer via Libc-alpha
2020-10-08 16:22   ` Adhemerval Zanella via Libc-alpha
2020-10-01 16:33 ` [PATCH 14/28] elf: Enhance ld.so --help to print HWCAP subdirectories Florian Weimer via Libc-alpha
2020-10-08 16:27   ` Adhemerval Zanella via Libc-alpha
2020-10-09  8:18     ` Florian Weimer via Libc-alpha
2020-10-09 13:49   ` Matheus Castanho via Libc-alpha
2020-10-09 17:08     ` Florian Weimer via Libc-alpha
2020-10-09 17:12       ` Florian Weimer via Libc-alpha
2020-10-09 18:54         ` Matheus Castanho via Libc-alpha
2020-10-12  9:47           ` Florian Weimer via Libc-alpha
2020-10-01 16:33 ` [PATCH 15/28] elf: Do not pass GLRO(dl_platform), GLRO(dl_platformlen) to _dl_important_hwcaps Florian Weimer via Libc-alpha
2020-10-08 18:04   ` Adhemerval Zanella via Libc-alpha
2020-10-01 16:33 ` [PATCH 16/28] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH Florian Weimer via Libc-alpha
2020-10-08 10:13   ` Szabolcs Nagy via Libc-alpha
2020-10-09  9:08     ` Florian Weimer via Libc-alpha
2020-10-09 10:50       ` Szabolcs Nagy via Libc-alpha
2020-10-09 10:55         ` Florian Weimer via Libc-alpha
2020-10-09 11:03           ` Szabolcs Nagy via Libc-alpha
2020-10-08 23:16   ` Paul A. Clarke via Libc-alpha
2020-10-09  8:56     ` Florian Weimer via Libc-alpha
2020-10-09 13:19   ` Adhemerval Zanella via Libc-alpha
2020-10-12 11:54     ` Florian Weimer via Libc-alpha
2020-10-01 16:33 ` [PATCH 17/28] x86_64: Add glibc-hwcaps support Florian Weimer via Libc-alpha
2020-10-01 16:33 ` [PATCH 18/28] powerpc64le: " Florian Weimer via Libc-alpha
2020-10-01 18:56   ` Paul A. Clarke via Libc-alpha
2020-10-05  9:47     ` Florian Weimer via Libc-alpha
2020-10-05 19:15       ` Paul A. Clarke via Libc-alpha
2020-10-06 12:20         ` Florian Weimer via Libc-alpha
2020-10-06 17:45           ` Paul A. Clarke via Libc-alpha
2020-10-09  9:06             ` Florian Weimer via Libc-alpha
2020-10-01 16:33 ` [PATCH 19/28] s390x: Add " Florian Weimer via Libc-alpha
2020-10-01 16:33 ` [PATCH 20/28] aarch64: " Florian Weimer via Libc-alpha
2020-10-14 13:46   ` Adhemerval Zanella via Libc-alpha
2020-10-14 14:08     ` Florian Weimer via Libc-alpha
2020-10-14 14:15       ` Adhemerval Zanella via Libc-alpha
2020-10-14 14:37         ` Szabolcs Nagy via Libc-alpha
2020-10-14 14:43           ` Adhemerval Zanella via Libc-alpha
2020-10-14 15:13             ` Florian Weimer via Libc-alpha
2020-10-14 14:44           ` Florian Weimer via Libc-alpha
2020-10-14 15:09             ` Szabolcs Nagy via Libc-alpha
2020-10-01 16:33 ` [PATCH 21/28] elf: Add endianness markup to ld.so.cache Florian Weimer via Libc-alpha
2020-10-14 14:07   ` Adhemerval Zanella via Libc-alpha
2020-10-01 16:33 ` [PATCH 22/28] elf: Add extension mechanism " Florian Weimer via Libc-alpha
2020-10-15 17:52   ` Adhemerval Zanella via Libc-alpha
2020-10-30 12:22     ` Florian Weimer via Libc-alpha
2020-11-03 12:45       ` Adhemerval Zanella via Libc-alpha
2020-11-03 15:30         ` Florian Weimer via Libc-alpha
2020-10-01 16:34 ` [PATCH 23/28] elf: Unify old and new format cache handling code in ld.so Florian Weimer via Libc-alpha
2020-10-16 14:37   ` Adhemerval Zanella via Libc-alpha
2020-10-30 13:22     ` Florian Weimer via Libc-alpha
2020-11-03 13:02       ` Adhemerval Zanella via Libc-alpha
2020-10-01 16:34 ` [PATCH 24/28] elf: Implement a string table for ldconfig, with tail merging Florian Weimer via Libc-alpha
2020-10-20 14:25   ` Adhemerval Zanella via Libc-alpha
2020-10-30 17:08     ` Florian Weimer via Libc-alpha
2020-11-03 13:05       ` Adhemerval Zanella via Libc-alpha
2020-11-03 15:29         ` Florian Weimer via Libc-alpha
2020-10-01 16:34 ` [PATCH 25/28] elf: Implement tail merging of strings in ldconfig Florian Weimer via Libc-alpha
2020-10-22 21:08   ` Adhemerval Zanella via Libc-alpha
2020-10-30 17:36     ` Florian Weimer via Libc-alpha
2020-10-01 16:34 ` [PATCH 26/28] elf: In ldconfig, extract the new_sub_entry function from search_dir Florian Weimer via Libc-alpha
2020-10-27 13:15   ` Adhemerval Zanella via Libc-alpha
2020-10-01 16:34 ` [PATCH 27/28] elf: Process glibc-hwcaps subdirectories in ldconfig Florian Weimer via Libc-alpha
2020-10-27 17:28   ` Adhemerval Zanella via Libc-alpha
2020-11-04 11:57     ` Florian Weimer via Libc-alpha
2020-10-01 16:34 ` [PATCH 28/28] elf: Add glibc-hwcaps subdirectory support to ld.so cache processing Florian Weimer via Libc-alpha
2020-10-01 16:50 ` [PATCH 00/28] glibc-hwcaps support H.J. Lu via Libc-alpha
2020-10-01 16:54   ` Florian Weimer via Libc-alpha

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).