b1dca6
commit ec935dea6332cb22f9881cd1162bad156173f4b0
b1dca6
Author: Florian Weimer <fweimer@redhat.com>
b1dca6
Date:   Fri Apr 24 22:31:15 2020 +0200
b1dca6
b1dca6
    elf: Implement __libc_early_init
b1dca6
    
b1dca6
    This function is defined in libc.so, and the dynamic loader calls
b1dca6
    right after relocation has been finished, before any ELF constructors
b1dca6
    or the preinit function is invoked.  It is also used in the static
b1dca6
    build for initializing parts of the static libc.
b1dca6
    
b1dca6
    To locate __libc_early_init, a direct symbol lookup function is used,
b1dca6
    _dl_lookup_direct.  It does not search the entire symbol scope and
b1dca6
    consults merely a single link map.  This function could also be used
b1dca6
    to implement lookups in the vDSO (as an optimization).
b1dca6
    
b1dca6
    A per-namespace variable (libc_map) is added for locating libc.so,
b1dca6
    to avoid repeated traversals of the search scope.  It is similar to
b1dca6
    GL(dl_initfirst).  An alternative would have been to thread a context
b1dca6
    argument from _dl_open down to _dl_map_object_from_fd (where libc.so
b1dca6
    is identified).  This could have avoided the global variable, but
b1dca6
    the change would be larger as a result.  It would not have been
b1dca6
    possible to use this to replace GL(dl_initfirst) because that global
b1dca6
    variable is used to pass the function pointer past the stack switch
b1dca6
    from dl_main to the main program.  Replacing that requires adding
b1dca6
    a new argument to _dl_init, which in turn needs changes to the
b1dca6
    architecture-specific libc.so startup code written in assembler.
b1dca6
    
b1dca6
    __libc_early_init should not be used to replace _dl_var_init (as
b1dca6
    it exists today on some architectures).  Instead, _dl_lookup_direct
b1dca6
    should be used to look up a new variable symbol in libc.so, and
b1dca6
    that should then be initialized from the dynamic loader, immediately
b1dca6
    after the object has been loaded in _dl_map_object_from_fd (before
b1dca6
    relocation is run).  This way, more IFUNC resolvers which depend on
b1dca6
    these variables will work.
b1dca6
    
b1dca6
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
b1dca6
b1dca6
diff --git a/csu/init-first.c b/csu/init-first.c
b1dca6
index 289373f9d8bd98f4..544229151ef79c67 100644
b1dca6
--- a/csu/init-first.c
b1dca6
+++ b/csu/init-first.c
b1dca6
@@ -16,7 +16,6 @@
b1dca6
    License along with the GNU C Library; if not, see
b1dca6
    <http://www.gnu.org/licenses/>.  */
b1dca6
 
b1dca6
-#include <ctype.h>
b1dca6
 #include <stdio.h>
b1dca6
 #include <stdlib.h>
b1dca6
 #include <fcntl.h>
b1dca6
@@ -80,9 +79,6 @@ _init (int argc, char **argv, char **envp)
b1dca6
 
b1dca6
   __init_misc (argc, argv, envp);
b1dca6
 
b1dca6
-  /* Initialize ctype data.  */
b1dca6
-  __ctype_init ();
b1dca6
-
b1dca6
 #if defined SHARED && !defined NO_CTORS_DTORS_SECTIONS
b1dca6
   __libc_global_ctors ();
b1dca6
 #endif
b1dca6
diff --git a/csu/libc-start.c b/csu/libc-start.c
b1dca6
index dfbf195328239a17..d9c3248219d8f84f 100644
b1dca6
--- a/csu/libc-start.c
b1dca6
+++ b/csu/libc-start.c
b1dca6
@@ -22,6 +22,7 @@
b1dca6
 #include <ldsodefs.h>
b1dca6
 #include <exit-thread.h>
b1dca6
 #include <libc-internal.h>
b1dca6
+#include <elf/libc-early-init.h>
b1dca6
 
b1dca6
 #include <elf/dl-tunables.h>
b1dca6
 
b1dca6
@@ -238,6 +239,10 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
b1dca6
     __cxa_atexit ((void (*) (void *)) rtld_fini, NULL, NULL);
b1dca6
 
b1dca6
 #ifndef SHARED
b1dca6
+  /* Perform early initialization.  In the shared case, this function
b1dca6
+     is called from the dynamic loader as early as possible.  */
b1dca6
+  __libc_early_init ();
b1dca6
+
b1dca6
   /* Call the initializer of the libc.  This is only needed here if we
b1dca6
      are compiling for the static library in which case we haven't
b1dca6
      run the constructors in `_dl_start_user'.  */
b1dca6
diff --git a/elf/Makefile b/elf/Makefile
b1dca6
index a6601ba84c8f4017..cbced7605ebe2443 100644
b1dca6
--- a/elf/Makefile
b1dca6
+++ b/elf/Makefile
b1dca6
@@ -25,7 +25,7 @@ headers		= elf.h bits/elfclass.h link.h bits/link.h
b1dca6
 routines	= $(all-dl-routines) dl-support dl-iteratephdr \
b1dca6
 		  dl-addr dl-addr-obj enbl-secure dl-profstub \
b1dca6
 		  dl-origin dl-libc dl-sym dl-sysdep dl-error \
b1dca6
-		  dl-reloc-static-pie
b1dca6
+		  dl-reloc-static-pie libc_early_init
b1dca6
 
b1dca6
 # The core dynamic linking functions are in libc for the static and
b1dca6
 # profiled libraries.
b1dca6
@@ -33,7 +33,8 @@ dl-routines	= $(addprefix dl-,load lookup object reloc deps hwcaps \
b1dca6
 				  runtime init fini debug misc \
b1dca6
 				  version profile tls origin scope \
b1dca6
 				  execstack open close trampoline \
b1dca6
-				  exception sort-maps)
b1dca6
+				  exception sort-maps lookup-direct \
b1dca6
+				  call-libc-early-init)
b1dca6
 ifeq (yes,$(use-ldconfig))
b1dca6
 dl-routines += dl-cache
b1dca6
 endif
b1dca6
diff --git a/elf/Versions b/elf/Versions
b1dca6
index 705489fc51f4ac5f..3be879c4adfa74c7 100644
b1dca6
--- a/elf/Versions
b1dca6
+++ b/elf/Versions
b1dca6
@@ -26,6 +26,7 @@ libc {
b1dca6
     _dl_open_hook; _dl_open_hook2;
b1dca6
     _dl_sym; _dl_vsym;
b1dca6
     __libc_dlclose; __libc_dlopen_mode; __libc_dlsym; __libc_dlvsym;
b1dca6
+    __libc_early_init;
b1dca6
 
b1dca6
     # Internal error handling support.  Interposes the functions in ld.so.
b1dca6
     _dl_signal_exception; _dl_catch_exception;
b1dca6
diff --git a/elf/dl-call-libc-early-init.c b/elf/dl-call-libc-early-init.c
b1dca6
new file mode 100644
b1dca6
index 0000000000000000..41e9ad9aad8b5b46
b1dca6
--- /dev/null
b1dca6
+++ b/elf/dl-call-libc-early-init.c
b1dca6
@@ -0,0 +1,41 @@
b1dca6
+/* Invoke the early initialization function in libc.so.
b1dca6
+   Copyright (C) 2020 Free Software Foundation, Inc.
b1dca6
+   This file is part of the GNU C Library.
b1dca6
+
b1dca6
+   The GNU C Library is free software; you can redistribute it and/or
b1dca6
+   modify it under the terms of the GNU Lesser General Public
b1dca6
+   License as published by the Free Software Foundation; either
b1dca6
+   version 2.1 of the License, or (at your option) any later version.
b1dca6
+
b1dca6
+   The GNU C Library is distributed in the hope that it will be useful,
b1dca6
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
b1dca6
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
b1dca6
+   Lesser General Public License for more details.
b1dca6
+
b1dca6
+   You should have received a copy of the GNU Lesser General Public
b1dca6
+   License along with the GNU C Library; if not, see
b1dca6
+   <https://www.gnu.org/licenses/>.  */
b1dca6
+
b1dca6
+#include <assert.h>
b1dca6
+#include <ldsodefs.h>
b1dca6
+#include <libc-early-init.h>
b1dca6
+#include <link.h>
b1dca6
+#include <stddef.h>
b1dca6
+
b1dca6
+void
b1dca6
+_dl_call_libc_early_init (struct link_map *libc_map)
b1dca6
+{
b1dca6
+  /* There is nothing to do if we did not actually load libc.so.  */
b1dca6
+  if (libc_map == NULL)
b1dca6
+    return;
b1dca6
+
b1dca6
+  const ElfW(Sym) *sym
b1dca6
+    = _dl_lookup_direct (libc_map, "__libc_early_init",
b1dca6
+                         0x069682ac, /* dl_new_hash output.  */
b1dca6
+                         "GLIBC_PRIVATE",
b1dca6
+                         0x0963cf85); /* _dl_elf_hash output.  */
b1dca6
+  assert (sym != NULL);
b1dca6
+  __typeof (__libc_early_init) *early_init
b1dca6
+    = DL_SYMBOL_ADDRESS (libc_map, sym);
b1dca6
+  early_init ();
b1dca6
+}
b1dca6
diff --git a/elf/dl-load.c b/elf/dl-load.c
b1dca6
index 8f8869ff524ab9f2..64da5323d0e368c1 100644
b1dca6
--- a/elf/dl-load.c
b1dca6
+++ b/elf/dl-load.c
b1dca6
@@ -30,6 +30,7 @@
b1dca6
 #include <sys/param.h>
b1dca6
 #include <sys/stat.h>
b1dca6
 #include <sys/types.h>
b1dca6
+#include <gnu/lib-names.h>
b1dca6
 
b1dca6
 /* Type for the buffer we put the ELF header and hopefully the program
b1dca6
    header.  This buffer does not really have to be too large.  In most
b1dca6
@@ -1390,6 +1391,14 @@ cannot enable executable stack as shared object requires");
b1dca6
     add_name_to_object (l, ((const char *) D_PTR (l, l_info[DT_STRTAB])
b1dca6
 			    + l->l_info[DT_SONAME]->d_un.d_val));
b1dca6
 
b1dca6
+  /* If we have newly loaded libc.so, update the namespace
b1dca6
+     description.  */
b1dca6
+  if (GL(dl_ns)[nsid].libc_map == NULL
b1dca6
+      && l->l_info[DT_SONAME] != NULL
b1dca6
+      && strcmp (((const char *) D_PTR (l, l_info[DT_STRTAB])
b1dca6
+		  + l->l_info[DT_SONAME]->d_un.d_val), LIBC_SO) == 0)
b1dca6
+    GL(dl_ns)[nsid].libc_map = l;
b1dca6
+
b1dca6
   /* _dl_close can only eventually undo the module ID assignment (via
b1dca6
      remove_slotinfo) if this function returns a pointer to a link
b1dca6
      map.  Therefore, delay this step until all possibilities for
b1dca6
diff --git a/elf/dl-lookup-direct.c b/elf/dl-lookup-direct.c
b1dca6
new file mode 100644
b1dca6
index 0000000000000000..5637ae89de8a9d61
b1dca6
--- /dev/null
b1dca6
+++ b/elf/dl-lookup-direct.c
b1dca6
@@ -0,0 +1,116 @@
b1dca6
+/* Look up a symbol in a single specified object.
b1dca6
+   Copyright (C) 1995-2020 Free Software Foundation, Inc.
b1dca6
+   This file is part of the GNU C Library.
b1dca6
+
b1dca6
+   The GNU C Library is free software; you can redistribute it and/or
b1dca6
+   modify it under the terms of the GNU Lesser General Public
b1dca6
+   License as published by the Free Software Foundation; either
b1dca6
+   version 2.1 of the License, or (at your option) any later version.
b1dca6
+
b1dca6
+   The GNU C Library is distributed in the hope that it will be useful,
b1dca6
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
b1dca6
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
b1dca6
+   Lesser General Public License for more details.
b1dca6
+
b1dca6
+   You should have received a copy of the GNU Lesser General Public
b1dca6
+   License along with the GNU C Library; if not, see
b1dca6
+   <https://www.gnu.org/licenses/>.  */
b1dca6
+
b1dca6
+#include <ldsodefs.h>
b1dca6
+#include <string.h>
b1dca6
+#include <elf_machine_sym_no_match.h>
b1dca6
+#include <dl-hash.h>
b1dca6
+
b1dca6
+/* This function corresponds to do_lookup_x in elf/dl-lookup.c.  The
b1dca6
+   variant here is simplified because it requires symbol
b1dca6
+   versioning.  */
b1dca6
+static const ElfW(Sym) *
b1dca6
+check_match (const struct link_map *const map, const char *const undef_name,
b1dca6
+             const char *version, uint32_t version_hash,
b1dca6
+             const Elf_Symndx symidx)
b1dca6
+{
b1dca6
+  const ElfW(Sym) *symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
b1dca6
+  const ElfW(Sym) *sym = &symtab[symidx];
b1dca6
+
b1dca6
+  unsigned int stt = ELFW(ST_TYPE) (sym->st_info);
b1dca6
+  if (__glibc_unlikely ((sym->st_value == 0 /* No value.  */
b1dca6
+                         && sym->st_shndx != SHN_ABS
b1dca6
+                         && stt != STT_TLS)
b1dca6
+                        || elf_machine_sym_no_match (sym)))
b1dca6
+    return NULL;
b1dca6
+
b1dca6
+  /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC,
b1dca6
+     STT_COMMON, STT_TLS, and STT_GNU_IFUNC since these are no
b1dca6
+     code/data definitions.  */
b1dca6
+#define ALLOWED_STT \
b1dca6
+  ((1 << STT_NOTYPE) | (1 << STT_OBJECT) | (1 << STT_FUNC) \
b1dca6
+   | (1 << STT_COMMON) | (1 << STT_TLS) | (1 << STT_GNU_IFUNC))
b1dca6
+  if (__glibc_unlikely (((1 << stt) & ALLOWED_STT) == 0))
b1dca6
+    return NULL;
b1dca6
+
b1dca6
+  const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
b1dca6
+
b1dca6
+  if (strcmp (strtab + sym->st_name, undef_name) != 0)
b1dca6
+    /* Not the symbol we are looking for.  */
b1dca6
+    return NULL;
b1dca6
+
b1dca6
+  ElfW(Half) ndx = map->l_versyms[symidx] & 0x7fff;
b1dca6
+  if (map->l_versions[ndx].hash != version_hash
b1dca6
+      || strcmp (map->l_versions[ndx].name, version) != 0)
b1dca6
+    /* It's not the version we want.  */
b1dca6
+    return NULL;
b1dca6
+
b1dca6
+  return sym;
b1dca6
+}
b1dca6
+
b1dca6
+
b1dca6
+/* This function corresponds to do_lookup_x in elf/dl-lookup.c.  The
b1dca6
+   variant here is simplified because it does not search object
b1dca6
+   dependencies.  It is optimized for a successful lookup.  */
b1dca6
+const ElfW(Sym) *
b1dca6
+_dl_lookup_direct (struct link_map *map,
b1dca6
+                   const char *undef_name, uint32_t new_hash,
b1dca6
+                   const char *version, uint32_t version_hash)
b1dca6
+{
b1dca6
+  const ElfW(Addr) *bitmask = map->l_gnu_bitmask;
b1dca6
+  if (__glibc_likely (bitmask != NULL))
b1dca6
+    {
b1dca6
+      Elf32_Word bucket = map->l_gnu_buckets[new_hash % map->l_nbuckets];
b1dca6
+      if (bucket != 0)
b1dca6
+        {
b1dca6
+          const Elf32_Word *hasharr = &map->l_gnu_chain_zero[bucket];
b1dca6
+
b1dca6
+          do
b1dca6
+            if (((*hasharr ^ new_hash) >> 1) == 0)
b1dca6
+              {
b1dca6
+                Elf_Symndx symidx = ELF_MACHINE_HASH_SYMIDX (map, hasharr);
b1dca6
+                const ElfW(Sym) *sym = check_match (map, undef_name,
b1dca6
+                                                    version, version_hash,
b1dca6
+                                                    symidx);
b1dca6
+                if (sym != NULL)
b1dca6
+                  return sym;
b1dca6
+              }
b1dca6
+          while ((*hasharr++ & 1u) == 0);
b1dca6
+        }
b1dca6
+    }
b1dca6
+  else
b1dca6
+    {
b1dca6
+      /* Fallback code for lack of GNU_HASH support.  */
b1dca6
+      uint32_t old_hash = _dl_elf_hash (undef_name);
b1dca6
+
b1dca6
+      /* Use the old SysV-style hash table.  Search the appropriate
b1dca6
+         hash bucket in this object's symbol table for a definition
b1dca6
+         for the same symbol name.  */
b1dca6
+      for (Elf_Symndx symidx = map->l_buckets[old_hash % map->l_nbuckets];
b1dca6
+           symidx != STN_UNDEF;
b1dca6
+           symidx = map->l_chain[symidx])
b1dca6
+        {
b1dca6
+          const ElfW(Sym) *sym = check_match (map, undef_name,
b1dca6
+                                              version, version_hash, symidx);
b1dca6
+          if (sym != NULL)
b1dca6
+            return sym;
b1dca6
+        }
b1dca6
+    }
b1dca6
+
b1dca6
+  return NULL;
b1dca6
+}
b1dca6
diff --git a/elf/dl-open.c b/elf/dl-open.c
b1dca6
index 7113c4a04f0fddbc..1a77ec833cad6c55 100644
b1dca6
--- a/elf/dl-open.c
b1dca6
+++ b/elf/dl-open.c
b1dca6
@@ -34,6 +34,7 @@
b1dca6
 #include <atomic.h>
b1dca6
 #include <libc-internal.h>
b1dca6
 #include <array_length.h>
b1dca6
+#include <libc-early-init.h>
b1dca6
 
b1dca6
 #include <dl-dst.h>
b1dca6
 #include <dl-prop.h>
b1dca6
@@ -57,6 +58,13 @@ struct dl_open_args
b1dca6
      (non-negative).  */
b1dca6
   unsigned int original_global_scope_pending_adds;
b1dca6
 
b1dca6
+  /* Set to true by dl_open_worker if libc.so was already loaded into
b1dca6
+     the namespace at the time dl_open_worker was called.  This is
b1dca6
+     used to determine whether libc.so early initialization has
b1dca6
+     already been done before, and whether to roll back the cached
b1dca6
+     libc_map value in the namespace in case of a dlopen failure.  */
b1dca6
+  bool libc_already_loaded;
b1dca6
+
b1dca6
   /* Original parameters to the program and the current environment.  */
b1dca6
   int argc;
b1dca6
   char **argv;
b1dca6
@@ -500,6 +508,11 @@ dl_open_worker (void *a)
b1dca6
 	args->nsid = call_map->l_ns;
b1dca6
     }
b1dca6
 
b1dca6
+  /* The namespace ID is now known.  Keep track of whether libc.so was
b1dca6
+     already loaded, to determine whether it is necessary to call the
b1dca6
+     early initialization routine (or clear libc_map on error).  */
b1dca6
+  args->libc_already_loaded = GL(dl_ns)[args->nsid].libc_map != NULL;
b1dca6
+
b1dca6
   /* Retain the old value, so that it can be restored.  */
b1dca6
   args->original_global_scope_pending_adds
b1dca6
     = GL (dl_ns)[args->nsid]._ns_global_scope_pending_adds;
b1dca6
@@ -734,6 +747,11 @@ dl_open_worker (void *a)
b1dca6
   if (relocation_in_progress)
b1dca6
     LIBC_PROBE (reloc_complete, 3, args->nsid, r, new);
b1dca6
 
b1dca6
+  /* If libc.so was not there before, attempt to call its early
b1dca6
+     initialization routine.  */
b1dca6
+  if (!args->libc_already_loaded)
b1dca6
+    _dl_call_libc_early_init (GL(dl_ns)[args->nsid].libc_map);
b1dca6
+
b1dca6
 #ifndef SHARED
b1dca6
   DL_STATIC_INIT (new);
b1dca6
 #endif
b1dca6
@@ -828,6 +846,8 @@ no more namespaces available for dlmopen()"));
b1dca6
   args.caller_dlopen = caller_dlopen;
b1dca6
   args.map = NULL;
b1dca6
   args.nsid = nsid;
b1dca6
+  /* args.libc_already_loaded is always assigned by dl_open_worker
b1dca6
+     (before any explicit/non-local returns).  */
b1dca6
   args.argc = argc;
b1dca6
   args.argv = argv;
b1dca6
   args.env = env;
b1dca6
@@ -856,6 +876,11 @@ no more namespaces available for dlmopen()"));
b1dca6
   /* See if an error occurred during loading.  */
b1dca6
   if (__glibc_unlikely (exception.errstring != NULL))
b1dca6
     {
b1dca6
+      /* Avoid keeping around a dangling reference to the libc.so link
b1dca6
+	 map in case it has been cached in libc_map.  */
b1dca6
+      if (!args.libc_already_loaded)
b1dca6
+	GL(dl_ns)[nsid].libc_map = NULL;
b1dca6
+
b1dca6
       /* Remove the object from memory.  It may be in an inconsistent
b1dca6
 	 state if relocation failed, for example.  */
b1dca6
       if (args.map)
b1dca6
diff --git a/elf/libc-early-init.h b/elf/libc-early-init.h
b1dca6
new file mode 100644
b1dca6
index 0000000000000000..5185fa8895c0e11a
b1dca6
--- /dev/null
b1dca6
+++ b/elf/libc-early-init.h
b1dca6
@@ -0,0 +1,35 @@
b1dca6
+/* Early initialization of libc.so.
b1dca6
+   Copyright (C) 2020 Free Software Foundation, Inc.
b1dca6
+   This file is part of the GNU C Library.
b1dca6
+
b1dca6
+   The GNU C Library is free software; you can redistribute it and/or
b1dca6
+   modify it under the terms of the GNU Lesser General Public
b1dca6
+   License as published by the Free Software Foundation; either
b1dca6
+   version 2.1 of the License, or (at your option) any later version.
b1dca6
+
b1dca6
+   The GNU C Library is distributed in the hope that it will be useful,
b1dca6
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
b1dca6
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
b1dca6
+   Lesser General Public License for more details.
b1dca6
+
b1dca6
+   You should have received a copy of the GNU Lesser General Public
b1dca6
+   License along with the GNU C Library; if not, see
b1dca6
+   <https://www.gnu.org/licenses/>.  */
b1dca6
+
b1dca6
+#ifndef _LIBC_EARLY_INIT_H
b1dca6
+#define _LIBC_EARLY_INIT_H
b1dca6
+
b1dca6
+struct link_map;
b1dca6
+
b1dca6
+/* If LIBC_MAP is not NULL, look up the __libc_early_init symbol in it
b1dca6
+   and call this function.  */
b1dca6
+void _dl_call_libc_early_init (struct link_map *libc_map) attribute_hidden;
b1dca6
+
b1dca6
+/* In the shared case, this function is defined in libc.so and invoked
b1dca6
+   from ld.so (or on the fist static dlopen) after complete relocation
b1dca6
+   of a new loaded libc.so, but before user-defined ELF constructors
b1dca6
+   run.  In the static case, this function is called directly from the
b1dca6
+   startup code.  */
b1dca6
+void __libc_early_init (void);
b1dca6
+
b1dca6
+#endif /* _LIBC_EARLY_INIT_H */
b1dca6
diff --git a/elf/libc_early_init.c b/elf/libc_early_init.c
b1dca6
new file mode 100644
b1dca6
index 0000000000000000..7f4ca332b805a22c
b1dca6
--- /dev/null
b1dca6
+++ b/elf/libc_early_init.c
b1dca6
@@ -0,0 +1,27 @@
b1dca6
+/* Early initialization of libc.so, libc.so side.
b1dca6
+   Copyright (C) 2020 Free Software Foundation, Inc.
b1dca6
+   This file is part of the GNU C Library.
b1dca6
+
b1dca6
+   The GNU C Library is free software; you can redistribute it and/or
b1dca6
+   modify it under the terms of the GNU Lesser General Public
b1dca6
+   License as published by the Free Software Foundation; either
b1dca6
+   version 2.1 of the License, or (at your option) any later version.
b1dca6
+
b1dca6
+   The GNU C Library is distributed in the hope that it will be useful,
b1dca6
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
b1dca6
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
b1dca6
+   Lesser General Public License for more details.
b1dca6
+
b1dca6
+   You should have received a copy of the GNU Lesser General Public
b1dca6
+   License along with the GNU C Library; if not, see
b1dca6
+   <https://www.gnu.org/licenses/>.  */
b1dca6
+
b1dca6
+#include <ctype.h>
b1dca6
+#include <libc-early-init.h>
b1dca6
+
b1dca6
+void
b1dca6
+__libc_early_init (void)
b1dca6
+{
b1dca6
+  /* Initialize ctype data.  */
b1dca6
+  __ctype_init ();
b1dca6
+}
b1dca6
diff --git a/elf/rtld.c b/elf/rtld.c
b1dca6
index d44facf5343b3301..a40d5f17db0dac8b 100644
b1dca6
--- a/elf/rtld.c
b1dca6
+++ b/elf/rtld.c
b1dca6
@@ -44,6 +44,7 @@
b1dca6
 #include <stackinfo.h>
b1dca6
 #include <not-cancel.h>
b1dca6
 #include <array_length.h>
b1dca6
+#include <libc-early-init.h>
b1dca6
 
b1dca6
 #include <assert.h>
b1dca6
 
b1dca6
@@ -2365,6 +2366,9 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
b1dca6
       rtld_timer_accum (&relocate_time, start);
b1dca6
     }
b1dca6
 
b1dca6
+  /* Relocation is complete.  Perform early libc initialization.  */
b1dca6
+  _dl_call_libc_early_init (GL(dl_ns)[LM_ID_BASE].libc_map);
b1dca6
+
b1dca6
   /* Do any necessary cleanups for the startup OS interface code.
b1dca6
      We do these now so that no calls are made after rtld re-relocation
b1dca6
      which might be resolved to different functions than we expect.
b1dca6
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
b1dca6
index a8fb0d211426e4b1..ccec08929e4ad4e7 100644
b1dca6
--- a/sysdeps/generic/ldsodefs.h
b1dca6
+++ b/sysdeps/generic/ldsodefs.h
b1dca6
@@ -336,6 +336,10 @@ struct rtld_global
b1dca6
        recursive dlopen calls from ELF constructors.  */
b1dca6
     unsigned int _ns_global_scope_pending_adds;
b1dca6
 
b1dca6
+    /* Once libc.so has been loaded into the namespace, this points to
b1dca6
+       its link map.  */
b1dca6
+    struct link_map *libc_map;
b1dca6
+
b1dca6
     /* Search table for unique objects.  */
b1dca6
     struct unique_sym_table
b1dca6
     {
b1dca6
@@ -943,6 +947,19 @@ extern lookup_t _dl_lookup_symbol_x (const char *undef,
b1dca6
      attribute_hidden;
b1dca6
 
b1dca6
 
b1dca6
+/* Restricted version of _dl_lookup_symbol_x.  Searches MAP (and only
b1dca6
+   MAP) for the symbol UNDEF_NAME, with GNU hash NEW_HASH (computed
b1dca6
+   with dl_new_hash), symbol version VERSION, and symbol version hash
b1dca6
+   VERSION_HASH (computed with _dl_elf_hash).  Returns a pointer to
b1dca6
+   the symbol table entry in MAP on success, or NULL on failure.  MAP
b1dca6
+   must have symbol versioning information, or otherwise the result is
b1dca6
+   undefined.  */
b1dca6
+const ElfW(Sym) *_dl_lookup_direct (struct link_map *map,
b1dca6
+				    const char *undef_name,
b1dca6
+				    uint32_t new_hash,
b1dca6
+				    const char *version,
b1dca6
+				    uint32_t version_hash) attribute_hidden;
b1dca6
+
b1dca6
 /* Add the new link_map NEW to the end of the namespace list.  */
b1dca6
 extern void _dl_add_to_namespace_list (struct link_map *new, Lmid_t nsid)
b1dca6
      attribute_hidden;
b1dca6
diff --git a/sysdeps/mach/hurd/i386/init-first.c b/sysdeps/mach/hurd/i386/init-first.c
b1dca6
index f8ad2ceb8e324f92..1636a40ee5d78858 100644
b1dca6
--- a/sysdeps/mach/hurd/i386/init-first.c
b1dca6
+++ b/sysdeps/mach/hurd/i386/init-first.c
b1dca6
@@ -17,7 +17,6 @@
b1dca6
    <http://www.gnu.org/licenses/>.  */
b1dca6
 
b1dca6
 #include <assert.h>
b1dca6
-#include <ctype.h>
b1dca6
 #include <hurd.h>
b1dca6
 #include <stdio.h>
b1dca6
 #include <unistd.h>
b1dca6
@@ -84,9 +83,6 @@ posixland_init (int argc, char **argv, char **envp)
b1dca6
 #endif
b1dca6
   __init_misc (argc, argv, envp);
b1dca6
 
b1dca6
-  /* Initialize ctype data.  */
b1dca6
-  __ctype_init ();
b1dca6
-
b1dca6
 #if defined SHARED && !defined NO_CTORS_DTORS_SECTIONS
b1dca6
   __libc_global_ctors ();
b1dca6
 #endif