diff mbox series

[1/2] elf: Support getauxval after static dlopen (bug 20802)

Message ID 87tuscp45g.fsf@oldenburg2.str.redhat.com
State Under Review
Delegated to: Carlos O'Donell
Headers show
Series [1/2] elf: Support getauxval after static dlopen (bug 20802) | expand

Commit Message

Florian Weimer Dec. 23, 2020, 10:07 p.m. UTC
Variables which should be available after static dlopen are stored
in __libc_vars and initialized from the outer libc.  This mechanism
is intended to replace _dl_var_init eventually.  The main differences
are that it is used in both the dynamic and static dlopen cases, and
that the initialization happens before relocation, so that no special
steps are necessary for RELRO compatibility.

---
 elf/Makefile                                | 15 ++++++--
 elf/Versions                                |  2 +
 elf/dl-call-libc-early-init.c               | 23 +++++++++++
 elf/dl-libc_vars.c                          | 19 ++++++++++
 elf/dl-libc_vars_init.c                     | 31 +++++++++++++++
 elf/dl-open.c                               | 10 +++++
 elf/dl-support.c                            |  4 +-
 elf/dl-sysdep.c                             |  9 +++--
 elf/libc_vars.c                             | 22 +++++++++++
 elf/rtld.c                                  |  5 +++
 elf/tst-auxvalmod.c                         | 31 +++++++++++++++
 elf/tst-auxvalmod.h                         | 23 +++++++++++
 elf/tst-getauxval-static.c                  | 54 ++++++++++++++++++++++++++
 misc/getauxval.c                            |  3 +-
 sysdeps/generic/ldsodefs.h                  |  5 ---
 sysdeps/generic/libc-vars-cpu.h             | 28 ++++++++++++++
 sysdeps/generic/libc-vars-init.h            | 32 ++++++++++++++++
 sysdeps/generic/libc-vars.h                 | 59 +++++++++++++++++++++++++++++
 sysdeps/unix/sysv/linux/powerpc/dl-static.c | 21 +++-------
 19 files changed, 365 insertions(+), 31 deletions(-)
diff mbox series

Patch

diff --git a/elf/Makefile b/elf/Makefile
index 0b4d78c874..93b378e461 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -25,7 +25,7 @@  headers		= elf.h bits/elfclass.h link.h bits/link.h
 routines	= $(all-dl-routines) dl-support dl-iteratephdr \
 		  dl-addr dl-addr-obj enbl-secure dl-profstub \
 		  dl-origin dl-libc dl-sym dl-sysdep dl-error \
-		  dl-reloc-static-pie libc_early_init
+		  dl-reloc-static-pie libc_early_init libc_vars
 
 # The core dynamic linking functions are in libc for the static and
 # profiled libraries.
@@ -35,7 +35,7 @@  dl-routines	= $(addprefix dl-,load lookup object reloc deps \
 				  execstack open close trampoline \
 				  exception sort-maps lookup-direct \
 				  call-libc-early-init write \
-				  thread_gscope_wait)
+				  thread_gscope_wait libc_vars libc_vars_init)
 ifeq (yes,$(use-ldconfig))
 dl-routines += dl-cache
 endif
@@ -186,9 +186,14 @@  tests-internal := tst-tls1 tst-tls2 $(tests-static-internal)
 tests-static := $(tests-static-normal) $(tests-static-internal)
 
 ifeq (yes,$(build-shared))
-tests-static += tst-tls9-static tst-single_threaded-static-dlopen
+tests-static += \
+  tst-getauxval-static \
+  tst-single_threaded-static-dlopen \
+  tst-tls9-static \
+
 static-dlopen-environment = \
   LD_LIBRARY_PATH=$(ld-library-path):$(common-objpfx)dlfcn
+tst-getauxval-static-ENV = $(static-dlopen-environment)
 tst-tls9-static-ENV = $(static-dlopen-environment)
 tst-single_threaded-static-dlopen-ENV = $(static-dlopen-environment)
 
@@ -343,6 +348,7 @@  modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		libmarkermod2-1 libmarkermod2-2 \
 		libmarkermod3-1 libmarkermod3-2 libmarkermod3-3 \
 		libmarkermod4-1 libmarkermod4-2 libmarkermod4-3 libmarkermod4-4 \
+		tst-auxvalmod \
 
 # Most modules build with _ISOMAC defined, but those filtered out
 # depend on internal headers.
@@ -1894,3 +1900,6 @@  $(objpfx)tst-glibc-hwcaps-mask.out: \
 # Generic dependency for sysdeps implementation of
 # tst-glibc-hwcaps-cache.
 $(objpfx)tst-glibc-hwcaps-cache.out: $(objpfx)tst-glibc-hwcaps
+
+$(objpfx)tst-getauxval-static: $(common-objpfx)dlfcn/libdl.a
+$(objpfx)tst-getauxval-static.out: $(objpfx)tst-auxvalmod.so
diff --git a/elf/Versions b/elf/Versions
index be88c48e6d..94e6daadf5 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -31,6 +31,8 @@  libc {
     # Internal error handling support.  Interposes the functions in ld.so.
     _dl_signal_exception; _dl_catch_exception;
     _dl_signal_error; _dl_catch_error;
+
+    __libc_vars;
   }
 }
 
diff --git a/elf/dl-call-libc-early-init.c b/elf/dl-call-libc-early-init.c
index 9a84680a1c..135fa57bed 100644
--- a/elf/dl-call-libc-early-init.c
+++ b/elf/dl-call-libc-early-init.c
@@ -19,9 +19,12 @@ 
 #include <assert.h>
 #include <ldsodefs.h>
 #include <libc-early-init.h>
+#include <libc-vars.h>
 #include <link.h>
 #include <stddef.h>
 
+#include <libc-vars-init.h>
+
 void
 _dl_call_libc_early_init (struct link_map *libc_map, _Bool initial)
 {
@@ -39,3 +42,23 @@  _dl_call_libc_early_init (struct link_map *libc_map, _Bool initial)
     = DL_SYMBOL_ADDRESS (libc_map, sym);
   early_init (initial);
 }
+
+void
+_dl_call_libc_vars_init (struct link_map *libc_map)
+{
+  /* There is nothing to do if we did not actually load libc.so.  */
+  if (libc_map == NULL)
+    return;
+
+  const ElfW (Sym) *sym
+    = _dl_lookup_direct (libc_map, "__libc_vars",
+                         0x59f05378, /* dl_new_hash output.  */
+                         "GLIBC_PRIVATE",
+                         0x0963cf85); /* _dl_elf_hash output.  */
+  assert (sym != NULL);
+  assert (sym->st_size == sizeof (struct libc_vars));
+  struct libc_vars *vars = DL_SYMBOL_ADDRESS (libc_map, sym);
+
+  _dl_libc_vars_init (vars);
+  _dl_call_libc_vars_init_cpu (libc_map);
+}
diff --git a/elf/dl-libc_vars.c b/elf/dl-libc_vars.c
new file mode 100644
index 0000000000..6d9d96ec63
--- /dev/null
+++ b/elf/dl-libc_vars.c
@@ -0,0 +1,19 @@ 
+/* Local copy of __libc_vars for ld.so.
+   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 "libc_vars.c"
diff --git a/elf/dl-libc_vars_init.c b/elf/dl-libc_vars_init.c
new file mode 100644
index 0000000000..71cb0446fb
--- /dev/null
+++ b/elf/dl-libc_vars_init.c
@@ -0,0 +1,31 @@ 
+/* Update __libc_vars in libc.so with the data from ld.so.
+   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 <libc-vars.h>
+
+#include <libc-vars-init.h>
+
+void
+_dl_libc_vars_init (struct libc_vars *vars)
+{
+#ifdef HAVE_AUX_VECTOR
+  vars->auxv = __libc_vars.auxv;
+#endif
+
+  _dl_libc_vars_init_cpu (vars);
+}
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 6710ea04cd..d5f9e61858 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -35,6 +35,7 @@ 
 #include <libc-internal.h>
 #include <array_length.h>
 #include <libc-early-init.h>
+#include <libc-vars.h>
 
 #include <dl-dst.h>
 #include <dl-prop.h>
@@ -590,6 +591,15 @@  dl_open_worker (void *a)
       (void) _dl_check_map_versions (new->l_searchlist.r_list[i]->l_real,
 				     0, 0);
 
+  /* Initialize __libc_vars before relocation.  This has to come after
+     the call to _dl_check_map_versions, so that lookups of versioned
+     symbols work.  */
+  if (!args->libc_already_loaded)
+    {
+      struct link_map *libc_map = GL (dl_ns)[args->nsid].libc_map;
+      _dl_call_libc_vars_init (libc_map);
+    }
+
 #ifdef SHARED
   /* Auditing checkpoint: we have added all objects.  */
   if (__glibc_unlikely (GLRO(dl_naudit) > 0))
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 250e4cd092..6f6b20cb4e 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -37,6 +37,7 @@ 
 #include <dl-vdso.h>
 #include <dl-vdso-setup.h>
 #include <dl-auxv.h>
+#include <libc-vars.h>
 
 extern char *__progname;
 char **_dl_argv = &__progname;	/* This is checked for some error messages.  */
@@ -154,7 +155,6 @@  int _dl_debug_fd = STDERR_FILENO;
 
 int _dl_correct_cache_id = _DL_CACHE_DEFAULT_ID;
 
-ElfW(auxv_t) *_dl_auxv;
 const ElfW(Phdr) *_dl_phdr;
 size_t _dl_phnum;
 uint64_t _dl_hwcap __attribute__ ((nocommon));
@@ -232,7 +232,7 @@  _dl_aux_init (ElfW(auxv_t) *av)
   uid_t uid = 0;
   gid_t gid = 0;
 
-  _dl_auxv = av;
+  __libc_vars.auxv = av;
   for (; av->a_type != AT_NULL; ++av)
     switch (av->a_type)
       {
diff --git a/elf/dl-sysdep.c b/elf/dl-sysdep.c
index 6cc4a76560..0969773127 100644
--- a/elf/dl-sysdep.c
+++ b/elf/dl-sysdep.c
@@ -42,6 +42,7 @@ 
 #include <dl-procinfo.h>
 #include <dl-osinfo.h>
 #include <libc-internal.h>
+#include <libc-vars.h>
 #include <tls.h>
 
 #include <dl-tunables.h>
@@ -110,12 +111,12 @@  _dl_sysdep_start (void **start_argptr,
 
   __libc_stack_end = DL_STACK_END (start_argptr);
   DL_FIND_ARG_COMPONENTS (start_argptr, _dl_argc, _dl_argv, _environ,
-			  GLRO(dl_auxv));
+			  __libc_vars.auxv);
 
   user_entry = (ElfW(Addr)) ENTRY_POINT;
   GLRO(dl_platform) = NULL; /* Default to nothing known about the platform.  */
 
-  for (av = GLRO(dl_auxv); av->a_type != AT_NULL; set_seen (av++))
+  for (av = __libc_vars.auxv; av->a_type != AT_NULL; set_seen (av++))
     switch (av->a_type)
       {
       case AT_PHDR:
@@ -247,7 +248,7 @@  _dl_sysdep_start (void **start_argptr,
   if (__builtin_expect (__libc_enable_secure, 0))
     __libc_check_standard_fds ();
 
-  (*dl_main) (phdr, phnum, &user_entry, GLRO(dl_auxv));
+  (*dl_main) (phdr, phnum, &user_entry, __libc_vars.auxv);
   return user_entry;
 }
 
@@ -270,7 +271,7 @@  _dl_show_auxv (void)
      close by (otherwise the array will be too large).  In case we have
      to support a platform where these requirements are not fulfilled
      some alternative implementation has to be used.  */
-  for (av = GLRO(dl_auxv); av->a_type != AT_NULL; ++av)
+  for (av = __libc_vars.auxv; av->a_type != AT_NULL; ++av)
     {
       static const struct
       {
diff --git a/elf/libc_vars.c b/elf/libc_vars.c
new file mode 100644
index 0000000000..538de7fa56
--- /dev/null
+++ b/elf/libc_vars.c
@@ -0,0 +1,22 @@ 
+/* Definition of __libc_vars for libc.
+   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 <libc-vars.h>
+
+struct libc_vars __libc_vars attribute_relro;
+hidden_def (__libc_vars)
diff --git a/elf/rtld.c b/elf/rtld.c
index 38f11f5b73..69b5b114ed 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -50,6 +50,7 @@ 
 #include <dl-main.h>
 #include <list.h>
 #include <gnu/lib-names.h>
+#include <libc-vars.h>
 
 #include <assert.h>
 
@@ -2311,6 +2312,10 @@  dl_main (const ElfW(Phdr) *phdr,
 
   _rtld_main_check (main_map, _dl_argv[0]);
 
+  /* We are about to perform relocation.  Make sure that __libc_vars
+     has been initialized.  */
+  _dl_call_libc_vars_init (GL (dl_ns)[LM_ID_BASE].libc_map);
+
   if (prelinked)
     {
       if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL)
diff --git a/elf/tst-auxvalmod.c b/elf/tst-auxvalmod.c
new file mode 100644
index 0000000000..d695c9df91
--- /dev/null
+++ b/elf/tst-auxvalmod.c
@@ -0,0 +1,31 @@ 
+/* Wrapper for getauxval testing.
+   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 <sys/auxv.h>
+
+#include "tst-auxvalmod.h"
+
+unsigned long int
+getauxval_wrapper (unsigned long int type, int *errnop)
+{
+  errno = *errnop;
+  unsigned long int result = getauxval (type);
+  *errnop = errno;
+  return result;
+}
diff --git a/elf/tst-auxvalmod.h b/elf/tst-auxvalmod.h
new file mode 100644
index 0000000000..f030326394
--- /dev/null
+++ b/elf/tst-auxvalmod.h
@@ -0,0 +1,23 @@ 
+/* Wrapper for getauxval testing.
+   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/>.  */
+
+/* Call getauxval, write errno value after the call to ERRNOP, and
+   return the getauxval result.  ERRNOP is needed because across a
+   static dlopen boundary, errno might have a different address, and
+   the caller would never see the intended value.  */
+unsigned long int getauxval_wrapper (unsigned long int type, int *errnop);
diff --git a/elf/tst-getauxval-static.c b/elf/tst-getauxval-static.c
new file mode 100644
index 0000000000..ee5ce54629
--- /dev/null
+++ b/elf/tst-getauxval-static.c
@@ -0,0 +1,54 @@ 
+/* Test getauxval from a dynamic library after static dlopen.
+   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 <support/check.h>
+#include <support/test-driver.h>
+#include <support/xdlfcn.h>
+#include <sys/auxv.h>
+
+#include "tst-auxvalmod.h"
+
+static int
+do_test (void)
+{
+  unsigned long missing_auxv_type;
+  for (missing_auxv_type = 0; ; ++missing_auxv_type)
+    {
+      errno = 0;
+      if (getauxval (missing_auxv_type) == 0 && errno != 0)
+        break;
+    }
+  TEST_COMPARE (errno, ENOENT);
+
+  void *handle = xdlopen ("tst-auxvalmod.so", RTLD_LAZY);
+
+  __typeof__ (getauxval_wrapper) *wrapper
+    = xdlsym (handle, "getauxval_wrapper");
+  int inner_errno = 0;
+  TEST_COMPARE (getauxval (AT_RANDOM), wrapper (AT_RANDOM, &inner_errno));
+
+  inner_errno = 0;
+  TEST_COMPARE (wrapper (missing_auxv_type, &inner_errno), 0);
+  TEST_COMPARE (inner_errno, ENOENT);
+
+  xdlclose (handle);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/misc/getauxval.c b/misc/getauxval.c
index e96d4dfa20..5820884ad1 100644
--- a/misc/getauxval.c
+++ b/misc/getauxval.c
@@ -19,6 +19,7 @@ 
 #include <errno.h>
 #include <ldsodefs.h>
 #include <stdbool.h>
+#include <libc-vars.h>
 
 bool
 __getauxval2 (unsigned long int type, unsigned long int *result)
@@ -39,7 +40,7 @@  __getauxval2 (unsigned long int type, unsigned long int *result)
     }
 
 #ifdef HAVE_AUX_VECTOR
-  for (p = GLRO(dl_auxv); p->a_type != AT_NULL; p++)
+  for (p = __libc_vars.auxv; p->a_type != AT_NULL; p++)
     if (p->a_type == type)
       {
         *result = p->a_un.a_val;
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 933cda117d..48837434b5 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -575,11 +575,6 @@  struct rtld_global_ro
   EXTERN uint64_t _dl_hwcap_mask;
 #endif
 
-#ifdef HAVE_AUX_VECTOR
-  /* Pointer to the auxv list supplied to the program at startup.  */
-  EXTERN ElfW(auxv_t) *_dl_auxv;
-#endif
-
   /* Get architecture specific definitions.  */
 #include <dl-procinfo.c>
 
diff --git a/sysdeps/generic/libc-vars-cpu.h b/sysdeps/generic/libc-vars-cpu.h
new file mode 100644
index 0000000000..fc9065b195
--- /dev/null
+++ b/sysdeps/generic/libc-vars-cpu.h
@@ -0,0 +1,28 @@ 
+/* CPU-specific extensions for __libc_vars.  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/>.  */
+
+#ifndef _LIBC_VARS_CPU_H
+#define _LIBC_VARS_CPU_H
+
+/* The generic version does not have CPU-specific extensions.  */
+#define HAVE_LIBC_VARS_CPU 0
+
+struct libc_vars;
+struct link_map;
+
+#endif /* _LIBC_VARS_CPU_H */
diff --git a/sysdeps/generic/libc-vars-init.h b/sysdeps/generic/libc-vars-init.h
new file mode 100644
index 0000000000..e5cd6ba659
--- /dev/null
+++ b/sysdeps/generic/libc-vars-init.h
@@ -0,0 +1,32 @@ 
+/* Initialization of __libc_vars.  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/>.  */
+
+/* This file is included from elf/dl-libc_vars_init.c
+   and elf/dl-call-libc-early-init.c.
+
+   The generic version does not perform any extra initialization.  */
+
+static inline void
+_dl_libc_vars_init_cpu (struct libc_vars *vars)
+{
+}
+
+static inline void
+_dl_call_libc_vars_init_cpu (struct link_map *libc_map)
+{
+}
diff --git a/sysdeps/generic/libc-vars.h b/sysdeps/generic/libc-vars.h
new file mode 100644
index 0000000000..378c51cc82
--- /dev/null
+++ b/sysdeps/generic/libc-vars.h
@@ -0,0 +1,59 @@ 
+/* Definition of __libc_vars and related update functionality.
+   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/>.  */
+
+#ifndef _LIBC_VARS_H
+#define _LIBC_VARS_H
+
+#include <ldsodefs.h>
+#include <libc-vars-cpu.h>
+#include <link.h>
+#include <stdint.h>
+
+struct libc_vars
+{
+#ifdef HAVE_AUX_VECTOR
+  /* Pointer to the auxv list supplied to the program at startup.  */
+  ElfW (auxv_t) *auxv;
+#endif
+#if HAVE_LIBC_VARS_CPU
+  struct libc_vars_cpu cpu;
+#endif
+};
+
+/* Initialized by ld.so immediate after loading libc.so.6, prior to
+   relocation.  The variable is duplicated in ld.so, where it is set
+   up by the startup code.  It is only exported (under GLIBC_PRIVATE)
+   in libc.so, and this copy is updated in _dl_libc_vars_init.
+
+   In the static case, the initialization has to be put into the
+   startup code, __libc_vars is part of libc.a.  */
+extern struct libc_vars __libc_vars attribute_relro;
+#if IS_IN (libc) || IS_IN (rtld)
+hidden_proto (__libc_vars)
+#endif
+
+/* Called by the dynamic linker to initialize __libc_vars prior to
+   relocation.  This function resides in ld.so, but updates the data
+   in libc.so.  The similar function __libc_early_init resides in
+   libc.so and performs post-relocation initialization.  */
+void _dl_libc_vars_init (struct libc_vars *vars) attribute_hidden;
+
+/* Invoke _dl_libc_vars_init for __libc_vars in LIBC_MAP.  */
+void _dl_call_libc_vars_init (struct link_map *libc_map) attribute_hidden;
+
+#endif /* _LIBC_VARS_H */
diff --git a/sysdeps/unix/sysv/linux/powerpc/dl-static.c b/sysdeps/unix/sysv/linux/powerpc/dl-static.c
index a77e07b503..cab84e5753 100644
--- a/sysdeps/unix/sysv/linux/powerpc/dl-static.c
+++ b/sysdeps/unix/sysv/linux/powerpc/dl-static.c
@@ -23,21 +23,11 @@ 
 void
 _dl_var_init (void *array[])
 {
-  /* It has to match "variables" below. */
-  enum
-    {
-      DL_PAGESIZE = 0,
-      DL_AUXV = 1,
-      DL_HWCAP = 2,
-      DL_HWCAP2 = 3,
-      DL_CACHE_LINE_SIZE = 4
-    };
-
-  GLRO(dl_pagesize) = *((size_t *) array[DL_PAGESIZE]);
-  GLRO(dl_auxv) = (ElfW(auxv_t) *) *((size_t *) array[DL_AUXV]);
-  GLRO(dl_hwcap)  = *((unsigned long int *) array[DL_HWCAP]);
-  GLRO(dl_hwcap2) = *((unsigned long int *) array[DL_HWCAP2]);
-  GLRO(dl_cache_line_size) = (int) *((int *) array[DL_CACHE_LINE_SIZE]);
+  int index = 0;
+  GLRO(dl_pagesize) = *((size_t *) array[index++]);
+  GLRO(dl_hwcap)  = *((unsigned long int *) array[index++]);
+  GLRO(dl_hwcap2) = *((unsigned long int *) array[index++]);
+  GLRO(dl_cache_line_size) = (int) *((int *) array[index++]);
 }
 
 #else
@@ -45,7 +35,6 @@  _dl_var_init (void *array[])
 static void *variables[] =
 {
   &GLRO(dl_pagesize),
-  &GLRO(dl_auxv),
   &GLRO(dl_hwcap),
   &GLRO(dl_hwcap2),
   &GLRO(dl_cache_line_size)