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