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