about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/dl-brk.c5
-rw-r--r--elf/dl-cache.c311
-rw-r--r--elf/dl-environ.c86
-rw-r--r--elf/dl-execstack.c32
-rw-r--r--elf/dl-fptr.c323
-rw-r--r--elf/dl-origin.c51
-rw-r--r--elf/dl-sbrk.c5
-rw-r--r--elf/dl-symaddr.c33
-rw-r--r--elf/dl-sysdep.c590
-rw-r--r--elf/dl-tls.c799
-rw-r--r--elf/dl-trampoline.c1
11 files changed, 2236 insertions, 0 deletions
diff --git a/elf/dl-brk.c b/elf/dl-brk.c
new file mode 100644
index 0000000000..c37cdfec33
--- /dev/null
+++ b/elf/dl-brk.c
@@ -0,0 +1,5 @@
+/* We can use the normal code but we also know the __curbrk is not exported
+   from ld.so.  */
+extern void *__curbrk attribute_hidden;
+
+#include <brk.c>
diff --git a/elf/dl-cache.c b/elf/dl-cache.c
new file mode 100644
index 0000000000..29886e194a
--- /dev/null
+++ b/elf/dl-cache.c
@@ -0,0 +1,311 @@
+/* Support for reading /etc/ld.so.cache files written by Linux ldconfig.
+   Copyright (C) 1996-2002, 2003, 2004 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <assert.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <sys/mman.h>
+#include <dl-cache.h>
+#include <dl-procinfo.h>
+
+#include <stdio-common/_itoa.h>
+
+#ifndef _DL_PLATFORMS_COUNT
+# define _DL_PLATFORMS_COUNT 0
+#endif
+
+/* This is the starting address and the size of the mmap()ed file.  */
+static struct cache_file *cache;
+static struct cache_file_new *cache_new;
+static size_t cachesize;
+
+/* 1 if cache_data + PTR points into the cache.  */
+#define _dl_cache_verify_ptr(ptr) (ptr < cache_data_size)
+
+#define SEARCH_CACHE(cache) \
+/* We use binary search since the table is sorted in the cache file.	      \
+   The first matching entry in the table is returned.			      \
+   It is important to use the same algorithm as used while generating	      \
+   the cache file.  */							      \
+do									      \
+  {									      \
+    left = 0;								      \
+    right = cache->nlibs - 1;						      \
+									      \
+    while (left <= right)						      \
+      {									      \
+	__typeof__ (cache->libs[0].key) key;				      \
+									      \
+	middle = (left + right) / 2;					      \
+									      \
+	key = cache->libs[middle].key;					      \
+									      \
+	/* Make sure string table indices are not bogus before using	      \
+	   them.  */							      \
+	if (! _dl_cache_verify_ptr (key))				      \
+	  {								      \
+	    cmpres = 1;							      \
+	    break;							      \
+	  }								      \
+									      \
+	/* Actually compare the entry with the key.  */			      \
+	cmpres = _dl_cache_libcmp (name, cache_data + key);		      \
+	if (__builtin_expect (cmpres == 0, 0))				      \
+	  {								      \
+	    /* Found it.  LEFT now marks the last entry for which we	      \
+	       know the name is correct.  */				      \
+	    left = middle;						      \
+									      \
+	    /* There might be entries with this name before the one we	      \
+	       found.  So we have to find the beginning.  */		      \
+	    while (middle > 0)						      \
+	      {								      \
+		__typeof__ (cache->libs[0].key) key;			      \
+									      \
+		key = cache->libs[middle - 1].key;			      \
+		/* Make sure string table indices are not bogus before	      \
+		   using them.  */					      \
+		if (! _dl_cache_verify_ptr (key)			      \
+		    /* Actually compare the entry.  */			      \
+		    || _dl_cache_libcmp (name, cache_data + key) != 0)	      \
+		  break;						      \
+		--middle;						      \
+	      }								      \
+									      \
+	    do								      \
+	      {								      \
+		int flags;						      \
+		__typeof__ (cache->libs[0]) *lib = &cache->libs[middle];      \
+									      \
+		/* Only perform the name test if necessary.  */		      \
+		if (middle > left					      \
+		    /* We haven't seen this string so far.  Test whether the  \
+		       index is ok and whether the name matches.  Otherwise   \
+		       we are done.  */					      \
+		    && (! _dl_cache_verify_ptr (lib->key)		      \
+			|| (_dl_cache_libcmp (name, cache_data + lib->key)    \
+			    != 0)))					      \
+		  break;						      \
+									      \
+		flags = lib->flags;					      \
+		if (_dl_cache_check_flags (flags)			      \
+		    && _dl_cache_verify_ptr (lib->value))		      \
+		  {							      \
+		    if (best == NULL || flags == GLRO(dl_correct_cache_id))   \
+		      {							      \
+			HWCAP_CHECK;					      \
+			best = cache_data + lib->value;			      \
+									      \
+			if (flags == GLRO(dl_correct_cache_id))		      \
+			  /* We've found an exact match for the shared	      \
+			     object and no general `ELF' release.  Stop	      \
+			     searching.  */				      \
+			  break;					      \
+		      }							      \
+		  }							      \
+	      }								      \
+	    while (++middle <= right);					      \
+	    break;							      \
+	}								      \
+									      \
+	if (cmpres < 0)							      \
+	  left = middle + 1;						      \
+	else								      \
+	  right = middle - 1;						      \
+      }									      \
+  }									      \
+while (0)
+
+
+int
+internal_function
+_dl_cache_libcmp (const char *p1, const char *p2)
+{
+  while (*p1 != '\0')
+    {
+      if (*p1 >= '0' && *p1 <= '9')
+        {
+          if (*p2 >= '0' && *p2 <= '9')
+            {
+	      /* Must compare this numerically.  */
+	      int val1;
+	      int val2;
+
+	      val1 = *p1++ - '0';
+	      val2 = *p2++ - '0';
+	      while (*p1 >= '0' && *p1 <= '9')
+	        val1 = val1 * 10 + *p1++ - '0';
+	      while (*p2 >= '0' && *p2 <= '9')
+	        val2 = val2 * 10 + *p2++ - '0';
+	      if (val1 != val2)
+		return val1 - val2;
+	    }
+	  else
+            return 1;
+        }
+      else if (*p2 >= '0' && *p2 <= '9')
+        return -1;
+      else if (*p1 != *p2)
+        return *p1 - *p2;
+      else
+	{
+	  ++p1;
+	  ++p2;
+	}
+    }
+  return *p1 - *p2;
+}
+
+
+/* Look up NAME in ld.so.cache and return the file name stored there,
+   or null if none is found.  */
+
+const char *
+internal_function
+_dl_load_cache_lookup (const char *name)
+{
+  int left, right, middle;
+  int cmpres;
+  const char *cache_data;
+  uint32_t cache_data_size;
+  const char *best;
+
+  /* Print a message if the loading of libs is traced.  */
+  if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+    _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE);
+
+  if (cache == NULL)
+    {
+      /* Read the contents of the file.  */
+      void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
+					       PROT_READ);
+
+      /* We can handle three different cache file formats here:
+	 - the old libc5/glibc2.0/2.1 format
+	 - the old format with the new format in it
+	 - only the new format
+	 The following checks if the cache contains any of these formats.  */
+      if (file != MAP_FAILED && cachesize > sizeof *cache
+	  && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0)
+	{
+	  size_t offset;
+	  /* Looks ok.  */
+	  cache = file;
+
+	  /* Check for new version.  */
+	  offset = ALIGN_CACHE (sizeof (struct cache_file)
+				+ cache->nlibs * sizeof (struct file_entry));
+
+	  cache_new = (struct cache_file_new *) ((void *) cache + offset);
+	  if (cachesize < (offset + sizeof (struct cache_file_new))
+	      || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW,
+			 sizeof CACHEMAGIC_VERSION_NEW - 1) != 0)
+	    cache_new = (void *) -1;
+	}
+      else if (file != MAP_FAILED && cachesize > sizeof *cache_new
+	       && memcmp (file, CACHEMAGIC_VERSION_NEW,
+			  sizeof CACHEMAGIC_VERSION_NEW - 1) == 0)
+	{
+	  cache_new = file;
+	  cache = file;
+	}
+      else
+	{
+	  if (file != MAP_FAILED)
+	    __munmap (file, cachesize);
+	  cache = (void *) -1;
+	}
+
+      assert (cache != NULL);
+    }
+
+  if (cache == (void *) -1)
+    /* Previously looked for the cache file and didn't find it.  */
+    return NULL;
+
+  best = NULL;
+
+  if (cache_new != (void *) -1)
+    {
+      uint64_t platform;
+
+      /* This is where the strings start.  */
+      cache_data = (const char *) cache_new;
+
+      /* Now we can compute how large the string table is.  */
+      cache_data_size = (const char *) cache + cachesize - cache_data;
+
+      platform = _dl_string_platform (GLRO(dl_platform));
+      if (platform != (uint64_t) -1)
+	platform = 1ULL << platform;
+
+      /* Only accept hwcap if it's for the right platform.  */
+#ifdef USE_TLS
+# define _DL_HWCAP_TLS_MASK (1LL << 63)
+#else
+# define _DL_HWCAP_TLS_MASK 0
+#endif
+#define HWCAP_CHECK \
+      if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion))	      \
+	continue;							      \
+      if (_DL_PLATFORMS_COUNT && platform != -1				      \
+	  && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0			      \
+	  && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform)		      \
+	continue;							      \
+      if (lib->hwcap							      \
+	  & ~(GLRO(dl_hwcap) | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK))      \
+	continue
+      SEARCH_CACHE (cache_new);
+    }
+  else
+    {
+      /* This is where the strings start.  */
+      cache_data = (const char *) &cache->libs[cache->nlibs];
+
+      /* Now we can compute how large the string table is.  */
+      cache_data_size = (const char *) cache + cachesize - cache_data;
+
+#undef HWCAP_CHECK
+#define HWCAP_CHECK do {} while (0)
+      SEARCH_CACHE (cache);
+    }
+
+  /* Print our result if wanted.  */
+  if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)
+      && best != NULL)
+    _dl_debug_printf ("  trying file=%s\n", best);
+
+  return best;
+}
+
+#ifndef MAP_COPY
+/* If the system does not support MAP_COPY we cannot leave the file open
+   all the time since this would create problems when the file is replaced.
+   Therefore we provide this function to close the file and open it again
+   once needed.  */
+void
+_dl_unload_cache (void)
+{
+  if (cache != NULL && cache != (struct cache_file *) -1)
+    {
+      __munmap (cache, cachesize);
+      cache = NULL;
+    }
+}
+#endif
diff --git a/elf/dl-environ.c b/elf/dl-environ.c
new file mode 100644
index 0000000000..089e89e6e7
--- /dev/null
+++ b/elf/dl-environ.c
@@ -0,0 +1,86 @@
+/* Environment handling for dynamic loader.
+   Copyright (C) 1995-1998, 2000, 2001, 2002 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+
+/* Walk through the environment of the process and return all entries
+   starting with `LD_'.  */
+char *
+internal_function
+_dl_next_ld_env_entry (char ***position)
+{
+  char **current = *position;
+  char *result = NULL;
+
+  while (*current != NULL)
+    {
+      if (__builtin_expect ((*current)[0] == 'L', 0)
+	  && (*current)[1] == 'D' && (*current)[2] == '_')
+	{
+	  result = &(*current)[3];
+
+	  /* Save current position for next visit.  */
+	  *position = ++current;
+
+	  break;
+	}
+
+      ++current;
+    }
+
+  return result;
+}
+
+
+/* In ld.so __environ is not exported.  */
+extern char **__environ attribute_hidden;
+
+int
+unsetenv (const char *name)
+{
+  char **ep;
+
+  ep = __environ;
+  while (*ep != NULL)
+    {
+      size_t cnt = 0;
+
+      while ((*ep)[cnt] == name[cnt] && name[cnt] != '\0')
+	++cnt;
+
+      if (name[cnt] == '\0' && (*ep)[cnt] == '=')
+	{
+	  /* Found it.  Remove this pointer by moving later ones to
+	     the front.  */
+	  char **dp = ep;
+
+	  do
+	    dp[0] = dp[1];
+	  while (*dp++);
+	  /* Continue the loop in case NAME appears again.  */
+	}
+      else
+	++ep;
+    }
+
+  return 0;
+}
diff --git a/elf/dl-execstack.c b/elf/dl-execstack.c
new file mode 100644
index 0000000000..6dce21e7a1
--- /dev/null
+++ b/elf/dl-execstack.c
@@ -0,0 +1,32 @@
+/* Stack executability handling for GNU dynamic linker.  Stub version.
+   Copyright (C) 2003, 2004 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <ldsodefs.h>
+#include <errno.h>
+
+/* There is no portable way to know the bounds of the initial thread's stack
+   so as to mprotect it.  */
+
+int
+internal_function
+_dl_make_stack_executable (void **stack_endp)
+{
+  return ENOSYS;
+}
+rtld_hidden_def (_dl_make_stack_executable)
diff --git a/elf/dl-fptr.c b/elf/dl-fptr.c
new file mode 100644
index 0000000000..78beecfdcb
--- /dev/null
+++ b/elf/dl-fptr.c
@@ -0,0 +1,323 @@
+/* Manage function descriptors.  Generic version.
+   Copyright (C) 1999,2000,2001,2002,2003,2004 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <libintl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <link.h>
+#include <ldsodefs.h>
+#include <elf/dynamic-link.h>
+#include <dl-fptr.h>
+#include <atomic.h>
+
+#ifndef ELF_MACHINE_BOOT_FPTR_TABLE_LEN
+/* ELF_MACHINE_BOOT_FPTR_TABLE_LEN should be greater than the number of
+   dynamic symbols in ld.so.  */
+# define ELF_MACHINE_BOOT_FPTR_TABLE_LEN 256
+#endif
+
+#ifndef ELF_MACHINE_LOAD_ADDRESS
+# error "ELF_MACHINE_LOAD_ADDRESS is not defined."
+#endif
+
+#ifndef COMPARE_AND_SWAP
+# define COMPARE_AND_SWAP(ptr, old, new) \
+  (atomic_compare_and_exchange_bool_acq (ptr, new, old) == 0)
+#endif
+
+ElfW(Addr) _dl_boot_fptr_table [ELF_MACHINE_BOOT_FPTR_TABLE_LEN];
+
+static struct local
+  {
+    struct fdesc_table *root;
+    struct fdesc *free_list;
+    unsigned int npages;		/* # of pages to allocate */
+    /* the next to members MUST be consecutive! */
+    struct fdesc_table boot_table;
+    struct fdesc boot_fdescs[1024];
+  }
+local =
+  {
+    .root = &local.boot_table,
+    .npages = 2,
+    .boot_table =
+      {
+	.len = sizeof (local.boot_fdescs) / sizeof (local.boot_fdescs[0]),
+	.first_unused = 0
+      }
+  };
+
+/* Create a new fdesc table and return a pointer to the first fdesc
+   entry.  The fdesc lock must have been acquired already.  */
+
+static struct fdesc_table *
+new_fdesc_table (struct local *l, size_t *size)
+{
+  size_t old_npages = l->npages;
+  size_t new_npages = old_npages + old_npages;
+  struct fdesc_table *new_table;
+
+  /* If someone has just created a new table, we return NULL to tell
+     the caller to use the new table.  */
+  if (! COMPARE_AND_SWAP (&l->npages, old_npages, new_npages))
+    return (struct fdesc_table *) NULL;
+
+  *size = old_npages * GLRO(dl_pagesize);
+  new_table = __mmap (NULL, *size,
+		      PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+  if (new_table == MAP_FAILED)
+    _dl_signal_error (errno, NULL, NULL,
+		      N_("cannot map pages for fdesc table"));
+
+  new_table->len
+    = (*size - sizeof (*new_table)) / sizeof (struct fdesc);
+  new_table->first_unused = 1;
+  return new_table;
+}
+
+
+static ElfW(Addr)
+make_fdesc (ElfW(Addr) ip, ElfW(Addr) gp)
+{
+  struct fdesc *fdesc = NULL;
+  struct fdesc_table *root;
+  unsigned int old;
+  struct local *l;
+
+  ELF_MACHINE_LOAD_ADDRESS (l, local);
+
+ retry:
+  root = l->root;
+  while (1)
+    {
+      old = root->first_unused;
+      if (old >= root->len)
+	break;
+      else if (COMPARE_AND_SWAP (&root->first_unused, old, old + 1))
+	{
+	  fdesc = &root->fdesc[old];
+	  goto install;
+	}
+    }
+
+  if (l->free_list)
+    {
+      /* Get it from free-list.  */
+      do
+	{
+	  fdesc = l->free_list;
+	  if (fdesc == NULL)
+	    goto retry;
+	}
+      while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->free_list,
+				 (ElfW(Addr)) fdesc, fdesc->ip));
+    }
+  else
+    {
+      /* Create a new fdesc table.  */
+      size_t size;
+      struct fdesc_table *new_table = new_fdesc_table (l, &size);
+
+      if (new_table == NULL)
+	goto retry;
+
+      new_table->next = root;
+      if (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->root,
+			      (ElfW(Addr)) root,
+			      (ElfW(Addr)) new_table))
+	{
+	  /* Someone has just installed a new table. Return NULL to
+	     tell the caller to use the new table.  */
+	  __munmap (new_table, size);
+	  goto retry;
+	}
+
+      /* Note that the first entry was reserved while allocating the
+	 memory for the new page.  */
+      fdesc = &new_table->fdesc[0];
+    }
+
+ install:
+  fdesc->ip = ip;
+  fdesc->gp = gp;
+
+  return (ElfW(Addr)) fdesc;
+}
+
+
+static inline ElfW(Addr) * __attribute__ ((always_inline))
+make_fptr_table (struct link_map *map)
+{
+  const ElfW(Sym) *symtab
+    = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+  const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+  ElfW(Addr) *fptr_table;
+  size_t size;
+  size_t len;
+
+  /* XXX Apparently the only way to find out the size of the dynamic
+     symbol section is to assume that the string table follows right
+     afterwards...  */
+  len = ((strtab - (char *) symtab)
+	 / map->l_info[DT_SYMENT]->d_un.d_val);
+  size = ((len * sizeof (fptr_table[0]) + GLRO(dl_pagesize) - 1)
+	  & -GLRO(dl_pagesize));
+  /* XXX We don't support here in the moment systems without MAP_ANON.
+     There probably are none for IA-64.  In case this is proven wrong
+     we will have to open /dev/null here and use the file descriptor
+     instead of the hard-coded -1.  */
+  fptr_table = __mmap (NULL, size,
+		       PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
+		       -1, 0);
+  if (fptr_table == MAP_FAILED)
+    _dl_signal_error (errno, NULL, NULL,
+		      N_("cannot map pages for fptr table"));
+
+  if (COMPARE_AND_SWAP ((ElfW(Addr) *) &map->l_mach.fptr_table,
+			(ElfW(Addr)) NULL, (ElfW(Addr)) fptr_table))
+    map->l_mach.fptr_table_len = len;
+  else
+    __munmap (fptr_table, len * sizeof (fptr_table[0]));
+
+  return map->l_mach.fptr_table;
+}
+
+
+ElfW(Addr)
+_dl_make_fptr (struct link_map *map, const ElfW(Sym) *sym,
+	       ElfW(Addr) ip)
+{
+  ElfW(Addr) *ftab = map->l_mach.fptr_table;
+  const ElfW(Sym) *symtab;
+  Elf_Symndx symidx;
+  struct local *l;
+
+  if (__builtin_expect (ftab == NULL, 0))
+    ftab = make_fptr_table (map);
+
+  symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
+  symidx = sym - symtab;
+
+  if (symidx >= map->l_mach.fptr_table_len)
+    _dl_signal_error (0, NULL, NULL,
+		      N_("internal error: symidx out of range of fptr table"));
+
+  while (ftab[symidx] == 0)
+    {
+      /* GOT has already been relocated in elf_get_dynamic_info -
+	 don't try to relocate it again.  */
+      ElfW(Addr) fdesc
+	= make_fdesc (ip, map->l_info[DT_PLTGOT]->d_un.d_ptr);
+
+      if (__builtin_expect (COMPARE_AND_SWAP (&ftab[symidx], (ElfW(Addr)) NULL,
+					      fdesc), 1))
+	{
+	  /* Noone has updated the entry and the new function
+	     descriptor has been installed.  */
+#if 0
+	  const char *strtab
+	    = (const void *) D_PTR (map, l_info[DT_STRTAB]);
+
+	  ELF_MACHINE_LOAD_ADDRESS (l, local);
+	  if (l->root != &l->boot_table
+	      || l->boot_table.first_unused > 20)
+	    _dl_debug_printf ("created fdesc symbol `%s' at %lx\n",
+			      strtab + sym->st_name, ftab[symidx]);
+#endif
+	  break;
+	}
+      else
+	{
+	  /* We created a duplicated function descriptor. We put it on
+	     free-list.  */
+	  struct fdesc *f = (struct fdesc *) fdesc;
+
+	  ELF_MACHINE_LOAD_ADDRESS (l, local);
+
+	  do
+	    f->ip = (ElfW(Addr)) l->free_list;
+	  while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &l->free_list,
+				     f->ip, fdesc));
+	}
+    }
+
+  return ftab[symidx];
+}
+
+
+void
+_dl_unmap (struct link_map *map)
+{
+  ElfW(Addr) *ftab = map->l_mach.fptr_table;
+  struct fdesc *head = NULL, *tail = NULL;
+  size_t i;
+
+  __munmap ((void *) map->l_map_start,
+	    map->l_map_end - map->l_map_start);
+
+  if (ftab == NULL)
+    return;
+
+  /* String together the fdesc structures that are being freed.  */
+  for (i = 0; i < map->l_mach.fptr_table_len; ++i)
+    {
+      if (ftab[i])
+	{
+	  *(struct fdesc **) ftab[i] = head;
+	  head = (struct fdesc *) ftab[i];
+	  if (tail == NULL)
+	    tail = head;
+	}
+    }
+
+  /* Prepend the new list to the free_list: */
+  if (tail)
+    do
+      tail->ip = (ElfW(Addr)) local.free_list;
+    while (! COMPARE_AND_SWAP ((ElfW(Addr) *) &local.free_list,
+			       tail->ip, (ElfW(Addr)) head));
+
+  __munmap (ftab, (map->l_mach.fptr_table_len
+		   * sizeof (map->l_mach.fptr_table[0])));
+
+  map->l_mach.fptr_table = NULL;
+}
+
+
+ElfW(Addr)
+_dl_lookup_address (const void *address)
+{
+  ElfW(Addr) addr = (ElfW(Addr)) address;
+  struct fdesc_table *t;
+  unsigned long int i;
+
+  for (t = local.root; t != NULL; t = t->next)
+    {
+      i = (struct fdesc *) addr - &t->fdesc[0];
+      if (i < t->first_unused && addr == (ElfW(Addr)) &t->fdesc[i])
+	{
+	  addr = t->fdesc[i].ip;
+	  break;
+	}
+    }
+
+  return addr;
+}
diff --git a/elf/dl-origin.c b/elf/dl-origin.c
new file mode 100644
index 0000000000..87619379bc
--- /dev/null
+++ b/elf/dl-origin.c
@@ -0,0 +1,51 @@
+/* Find path of executable.
+   Copyright (C) 1998, 1999, 2000, 2002, 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <ldsodefs.h>
+
+#include <dl-dst.h>
+
+
+const char *
+_dl_get_origin (void)
+{
+  char *result = (char *) -1;
+  /* We use the environment variable LD_ORIGIN_PATH.  If it is set make
+     a copy and strip out trailing slashes.  */
+  if (GLRO(dl_origin_path) != NULL)
+    {
+      size_t len = strlen (GLRO(dl_origin_path));
+      result = (char *) malloc (len + 1);
+      if (result == NULL)
+	result = (char *) -1;
+      else
+	{
+	  char *cp = __mempcpy (result, GLRO(dl_origin_path), len);
+	  while (cp > result + 1 && cp[-1] == '/')
+	    --cp;
+	  *cp = '\0';
+	}
+    }
+
+  return result;
+}
diff --git a/elf/dl-sbrk.c b/elf/dl-sbrk.c
new file mode 100644
index 0000000000..4713a92694
--- /dev/null
+++ b/elf/dl-sbrk.c
@@ -0,0 +1,5 @@
+/* We can use the normal code but we also know the __curbrk is not exported
+   from ld.so.  */
+extern void *__curbrk attribute_hidden;
+
+#include <sbrk.c>
diff --git a/elf/dl-symaddr.c b/elf/dl-symaddr.c
new file mode 100644
index 0000000000..3c850ca835
--- /dev/null
+++ b/elf/dl-symaddr.c
@@ -0,0 +1,33 @@
+/* Get the symbol address.  Generic version.
+   Copyright (C) 1999, 2000, 2001, 2003 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <ldsodefs.h>
+#include <dl-fptr.h>
+
+void *
+_dl_symbol_address (struct link_map *map, const ElfW(Sym) *ref)
+{
+  ElfW(Addr) value = (map ? map->l_addr : 0) + ref->st_value;
+
+  /* Return the pointer to function descriptor. */
+  if (ELFW(ST_TYPE) (ref->st_info) == STT_FUNC)
+    return (void *) _dl_make_fptr (map, ref, value);
+  else
+    return (void *) value;
+}
diff --git a/elf/dl-sysdep.c b/elf/dl-sysdep.c
new file mode 100644
index 0000000000..985e2b8f77
--- /dev/null
+++ b/elf/dl-sysdep.c
@@ -0,0 +1,590 @@
+/* Operating system support for run-time dynamic linker.  Generic Unix version.
+   Copyright (C) 1995-1998, 2000-2003, 2004, 2005 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <assert.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <ldsodefs.h>
+#include <stdio-common/_itoa.h>
+#include <fpu_control.h>
+
+#include <entry.h>
+#include <dl-machine.h>
+#include <dl-procinfo.h>
+#include <dl-osinfo.h>
+#include <hp-timing.h>
+#include <tls.h>
+
+#ifdef _DL_FIRST_PLATFORM
+# define _DL_FIRST_EXTRA (_DL_FIRST_PLATFORM + _DL_PLATFORMS_COUNT)
+#else
+# define _DL_FIRST_EXTRA _DL_HWCAP_COUNT
+#endif
+
+extern char **_environ attribute_hidden;
+extern void _end attribute_hidden;
+
+/* Protect SUID program against misuse of file descriptors.  */
+extern void __libc_check_standard_fds (void);
+
+#ifdef NEED_DL_BASE_ADDR
+ElfW(Addr) _dl_base_addr;
+#endif
+int __libc_enable_secure attribute_relro = 0;
+INTVARDEF(__libc_enable_secure)
+int __libc_multiple_libcs = 0;	/* Defining this here avoids the inclusion
+				   of init-first.  */
+/* This variable contains the lowest stack address ever used.  */
+void *__libc_stack_end attribute_relro = NULL;
+rtld_hidden_data_def(__libc_stack_end)
+static ElfW(auxv_t) *_dl_auxv attribute_relro;
+
+#ifndef DL_FIND_ARG_COMPONENTS
+# define DL_FIND_ARG_COMPONENTS(cookie, argc, argv, envp, auxp)	\
+  do {									      \
+    void **_tmp;							      \
+    (argc) = *(long int *) cookie;					      \
+    (argv) = (char **) ((long int *) cookie + 1);			      \
+    (envp) = (argv) + (argc) + 1;					      \
+    for (_tmp = (void **) (envp); *_tmp; ++_tmp)			      \
+      continue;								      \
+    (auxp) = (void *) ++_tmp;						      \
+  } while (0)
+#endif
+
+#ifndef DL_STACK_END
+# define DL_STACK_END(cookie) ((void *) (cookie))
+#endif
+
+ElfW(Addr)
+_dl_sysdep_start (void **start_argptr,
+		  void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phnum,
+				   ElfW(Addr) *user_entry))
+{
+  const ElfW(Phdr) *phdr = NULL;
+  ElfW(Word) phnum = 0;
+  ElfW(Addr) user_entry;
+  ElfW(auxv_t) *av;
+#ifdef HAVE_AUX_SECURE
+# define set_seen(tag) (tag)	/* Evaluate for the side effects.  */
+# define set_seen_secure() ((void) 0)
+#else
+  uid_t uid = 0;
+  gid_t gid = 0;
+  unsigned int seen = 0;
+# define set_seen_secure() (seen = -1)
+# ifdef HAVE_AUX_XID
+#  define set_seen(tag) (tag)	/* Evaluate for the side effects.  */
+# else
+#  define M(type) (1 << (type))
+#  define set_seen(tag) seen |= M ((tag)->a_type)
+# endif
+#endif
+#ifdef NEED_DL_SYSINFO
+  uintptr_t new_sysinfo = 0;
+#endif
+
+  __libc_stack_end = DL_STACK_END (start_argptr);
+  DL_FIND_ARG_COMPONENTS (start_argptr, _dl_argc, INTUSE(_dl_argv), _environ,
+			  _dl_auxv);
+
+  user_entry = (ElfW(Addr)) ENTRY_POINT;
+  GLRO(dl_platform) = NULL; /* Default to nothing known about the platform.  */
+
+  for (av = _dl_auxv; av->a_type != AT_NULL; set_seen (av++))
+    switch (av->a_type)
+      {
+      case AT_PHDR:
+	phdr = (void *) av->a_un.a_val;
+	break;
+      case AT_PHNUM:
+	phnum = av->a_un.a_val;
+	break;
+      case AT_PAGESZ:
+	GLRO(dl_pagesize) = av->a_un.a_val;
+	break;
+      case AT_ENTRY:
+	user_entry = av->a_un.a_val;
+	break;
+#ifdef NEED_DL_BASE_ADDR
+      case AT_BASE:
+	_dl_base_addr = av->a_un.a_val;
+	break;
+#endif
+#ifndef HAVE_AUX_SECURE
+      case AT_UID:
+      case AT_EUID:
+	uid ^= av->a_un.a_val;
+	break;
+      case AT_GID:
+      case AT_EGID:
+	gid ^= av->a_un.a_val;
+	break;
+#endif
+      case AT_SECURE:
+#ifndef HAVE_AUX_SECURE
+	seen = -1;
+#endif
+	INTUSE(__libc_enable_secure) = av->a_un.a_val;
+	break;
+      case AT_PLATFORM:
+	GLRO(dl_platform) = (void *) av->a_un.a_val;
+	break;
+      case AT_HWCAP:
+	GLRO(dl_hwcap) = (unsigned long int) av->a_un.a_val;
+	break;
+      case AT_CLKTCK:
+	GLRO(dl_clktck) = av->a_un.a_val;
+	break;
+      case AT_FPUCW:
+	GLRO(dl_fpu_control) = av->a_un.a_val;
+	break;
+#ifdef NEED_DL_SYSINFO
+      case AT_SYSINFO:
+	new_sysinfo = av->a_un.a_val;
+	break;
+#endif
+#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO
+      case AT_SYSINFO_EHDR:
+	GLRO(dl_sysinfo_dso) = (void *) av->a_un.a_val;
+	break;
+#endif
+#ifdef DL_PLATFORM_AUXV
+      DL_PLATFORM_AUXV
+#endif
+      }
+
+#ifndef HAVE_AUX_SECURE
+  if (seen != -1)
+    {
+      /* Fill in the values we have not gotten from the kernel through the
+	 auxiliary vector.  */
+# ifndef HAVE_AUX_XID
+#  define SEE(UID, var, uid) \
+   if ((seen & M (AT_##UID)) == 0) var ^= __get##uid ()
+      SEE (UID, uid, uid);
+      SEE (EUID, uid, euid);
+      SEE (GID, gid, gid);
+      SEE (EGID, gid, egid);
+# endif
+
+      /* If one of the two pairs of IDs does not match this is a setuid
+	 or setgid run.  */
+      INTUSE(__libc_enable_secure) = uid | gid;
+    }
+#endif
+
+#ifndef HAVE_AUX_PAGESIZE
+  if (GLRO(dl_pagesize) == 0)
+    GLRO(dl_pagesize) = __getpagesize ();
+#endif
+
+#if defined NEED_DL_SYSINFO
+  /* Only set the sysinfo value if we also have the vsyscall DSO.  */
+  if (GLRO(dl_sysinfo_dso) != 0 && new_sysinfo)
+    GLRO(dl_sysinfo) = new_sysinfo;
+#endif
+
+#ifdef DL_SYSDEP_INIT
+  DL_SYSDEP_INIT;
+#endif
+
+#ifdef DL_PLATFORM_INIT
+  DL_PLATFORM_INIT;
+#endif
+
+  /* Determine the length of the platform name.  */
+  if (GLRO(dl_platform) != NULL)
+    GLRO(dl_platformlen) = strlen (GLRO(dl_platform));
+
+  if (__sbrk (0) == &_end)
+    /* The dynamic linker was run as a program, and so the initial break
+       starts just after our bss, at &_end.  The malloc in dl-minimal.c
+       will consume the rest of this page, so tell the kernel to move the
+       break up that far.  When the user program examines its break, it
+       will see this new value and not clobber our data.  */
+    __sbrk (GLRO(dl_pagesize)
+	    - ((&_end - (void *) 0) & (GLRO(dl_pagesize) - 1)));
+
+  /* If this is a SUID program we make sure that FDs 0, 1, and 2 are
+     allocated.  If necessary we are doing it ourself.  If it is not
+     possible we stop the program.  */
+  if (__builtin_expect (INTUSE(__libc_enable_secure), 0))
+    __libc_check_standard_fds ();
+
+  (*dl_main) (phdr, phnum, &user_entry);
+  return user_entry;
+}
+
+void
+internal_function
+_dl_sysdep_start_cleanup (void)
+{
+}
+
+void
+internal_function
+_dl_show_auxv (void)
+{
+  char buf[64];
+  ElfW(auxv_t) *av;
+
+  /* Terminate string.  */
+  buf[63] = '\0';
+
+  /* The following code assumes that the AT_* values are encoded
+     starting from 0 with AT_NULL, 1 for AT_IGNORE, and all other values
+     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 = _dl_auxv; av->a_type != AT_NULL; ++av)
+    {
+      static const struct
+      {
+	const char label[20];
+	enum { unknown = 0, dec, hex, str, ignore } form;
+      } auxvars[] =
+	{
+	  [AT_EXECFD - 2] =		{ "AT_EXECFD:       ", dec },
+	  [AT_PHDR - 2] =		{ "AT_PHDR:         0x", hex },
+	  [AT_PHENT - 2] =		{ "AT_PHENT:        ", dec },
+	  [AT_PHNUM - 2] =		{ "AT_PHNUM:        ", dec },
+	  [AT_PAGESZ - 2] =		{ "AT_PAGESZ:       ", dec },
+	  [AT_BASE - 2] =		{ "AT_BASE:         0x", hex },
+	  [AT_FLAGS - 2] =		{ "AT_FLAGS:        0x", hex },
+	  [AT_ENTRY - 2] =		{ "AT_ENTRY:        0x", hex },
+	  [AT_NOTELF - 2] =		{ "AT_NOTELF:       ", hex },
+	  [AT_UID - 2] =		{ "AT_UID:          ", dec },
+	  [AT_EUID - 2] =		{ "AT_EUID:         ", dec },
+	  [AT_GID - 2] =		{ "AT_GID:          ", dec },
+	  [AT_EGID - 2] =		{ "AT_EGID:         ", dec },
+	  [AT_PLATFORM - 2] =		{ "AT_PLATFORM:     ", str },
+	  [AT_HWCAP - 2] =		{ "AT_HWCAP:        ", hex },
+	  [AT_CLKTCK - 2] =		{ "AT_CLKTCK:       ", dec },
+	  [AT_FPUCW - 2] =		{ "AT_FPUCW:        ", hex },
+	  [AT_DCACHEBSIZE - 2] =	{ "AT_DCACHEBSIZE:  0x", hex },
+	  [AT_ICACHEBSIZE - 2] =	{ "AT_ICACHEBSIZE:  0x", hex },
+	  [AT_UCACHEBSIZE - 2] =	{ "AT_UCACHEBSIZE:  0x", hex },
+	  [AT_IGNOREPPC - 2] =		{ "AT_IGNOREPPC", ignore },
+	  [AT_SECURE - 2] =		{ "AT_SECURE:       ", dec },
+	  [AT_SYSINFO - 2] =		{ "AT_SYSINFO:      0x", hex },
+	  [AT_SYSINFO_EHDR - 2] =	{ "AT_SYSINFO_EHDR: 0x", hex },
+	};
+      unsigned int idx = (unsigned int) (av->a_type - 2);
+
+      if ((unsigned int) av->a_type < 2u || auxvars[idx].form == ignore)
+	continue;
+
+      assert (AT_NULL == 0);
+      assert (AT_IGNORE == 1);
+
+      if (av->a_type == AT_HWCAP)
+	{
+	  /* This is handled special.  */
+	  if (_dl_procinfo (av->a_un.a_val) == 0)
+	    continue;
+	}
+
+      if (idx < sizeof (auxvars) / sizeof (auxvars[0])
+	  && auxvars[idx].form != unknown)
+	{
+	  const char *val = (char *) av->a_un.a_val;
+
+	  if (__builtin_expect (auxvars[idx].form, dec) == dec)
+	    val = _itoa ((unsigned long int) av->a_un.a_val,
+			 buf + sizeof buf - 1, 10, 0);
+	  else if (__builtin_expect (auxvars[idx].form, hex) == hex)
+	    val = _itoa ((unsigned long int) av->a_un.a_val,
+			 buf + sizeof buf - 1, 16, 0);
+
+	  _dl_printf ("%s%s\n", auxvars[idx].label, val);
+
+	  continue;
+	}
+
+      /* Unknown value: print a generic line.  */
+      char buf2[17];
+      buf[sizeof (buf2) - 1] = '\0';
+      const char *val2 = _itoa ((unsigned long int) av->a_un.a_val,
+				buf2 + sizeof buf2 - 1, 16, 0);
+      const char *val =  _itoa ((unsigned long int) av->a_type,
+				buf + sizeof buf - 1, 16, 0);
+      _dl_printf ("AT_??? (0x%s): 0x%s\n", val, val2);
+    }
+}
+
+
+/* Return an array of useful/necessary hardware capability names.  */
+const struct r_strlenpair *
+internal_function
+_dl_important_hwcaps (const char *platform, size_t platform_len, size_t *sz,
+		      size_t *max_capstrlen)
+{
+  /* Determine how many important bits are set.  */
+  uint64_t masked = GLRO(dl_hwcap) & GLRO(dl_hwcap_mask);
+  size_t cnt = platform != NULL;
+  size_t n, m;
+  size_t total;
+  struct r_strlenpair *temp;
+  struct r_strlenpair *result;
+  struct r_strlenpair *rp;
+  char *cp;
+
+  /* Count the number of bits set in the masked value.  */
+  for (n = 0; (~((1ULL << n) - 1) & masked) != 0; ++n)
+    if ((masked & (1ULL << n)) != 0)
+      ++cnt;
+
+#if (defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO) && defined SHARED
+  /* The system-supplied DSO can contain a note of type 2, vendor "GNU".
+     This gives us a list of names to treat as fake hwcap bits.  */
+
+  const char *dsocaps = NULL;
+  size_t dsocapslen = 0;
+  if (GLRO(dl_sysinfo_map) != NULL)
+    {
+      const ElfW(Phdr) *const phdr = GLRO(dl_sysinfo_map)->l_phdr;
+      const ElfW(Word) phnum = GLRO(dl_sysinfo_map)->l_phnum;
+      for (uint_fast16_t i = 0; i < phnum; ++i)
+	if (phdr[i].p_type == PT_NOTE)
+	  {
+	    const ElfW(Addr) start = (phdr[i].p_vaddr
+				      + GLRO(dl_sysinfo_map)->l_addr);
+	    const struct
+	    {
+	      ElfW(Word) vendorlen;
+	      ElfW(Word) datalen;
+	      ElfW(Word) type;
+	    } *note = (const void *) start;
+	    while ((ElfW(Addr)) (note + 1) - start < phdr[i].p_memsz)
+	      {
+#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word)))
+		if (note->type == 2
+		    && note->vendorlen == sizeof "GNU"
+		    && !memcmp ((note + 1), "GNU", sizeof "GNU")
+		    && note->datalen > 2 * sizeof (ElfW(Word)) + 2)
+		  {
+		    const ElfW(Word) *p = ((const void *) (note + 1)
+					   + ROUND (sizeof "GNU"));
+		    cnt += *p++;
+		    ++p;	/* Skip mask word.  */
+		    dsocaps = (const char *) p;
+		    dsocapslen = note->datalen - sizeof *p * 2;
+		    break;
+		  }
+		note = ((const void *) (note + 1)
+			+ ROUND (note->vendorlen) + ROUND (note->datalen));
+	      }
+	    if (dsocaps != NULL)
+	      break;
+	  }
+    }
+#endif
+
+#ifdef USE_TLS
+  /* For TLS enabled builds always add 'tls'.  */
+  ++cnt;
+#else
+  if (cnt == 0)
+    {
+      /* If we no have platform name and no important capability we only
+	 have the base directory to search.  */
+      result = (struct r_strlenpair *) malloc (sizeof (*result));
+      if (result == NULL)
+	goto no_memory;
+
+      result[0].str = (char *) result;	/* Does not really matter.  */
+      result[0].len = 0;
+
+      *sz = 1;
+      return result;
+    }
+#endif
+
+  /* Create temporary data structure to generate result table.  */
+  temp = (struct r_strlenpair *) alloca (cnt * sizeof (*temp));
+  m = 0;
+#if defined NEED_DL_SYSINFO || defined NEED_DL_SYSINFO_DSO
+  if (dsocaps != NULL)
+    {
+      const ElfW(Word) mask = ((const ElfW(Word) *) dsocaps)[-1];
+      GLRO(dl_hwcap) |= (uint64_t) mask << _DL_FIRST_EXTRA;
+      size_t len;
+      for (const char *p = dsocaps; p < dsocaps + dsocapslen; p += len + 1)
+	{
+	  uint_fast8_t bit = *p++;
+	  len = strlen (p);
+
+	  /* Skip entries that are not enabled in the mask word.  */
+	  if (__builtin_expect (mask & ((ElfW(Word)) 1 << bit), 1))
+	    {
+	      temp[m].str = p;
+	      temp[m].len = len;
+	      ++m;
+	    }
+	  else
+	    --cnt;
+	}
+    }
+#endif
+  for (n = 0; masked != 0; ++n)
+    if ((masked & (1ULL << n)) != 0)
+      {
+	temp[m].str = _dl_hwcap_string (n);
+	temp[m].len = strlen (temp[m].str);
+	masked ^= 1ULL << n;
+	++m;
+      }
+  if (platform != NULL)
+    {
+      temp[m].str = platform;
+      temp[m].len = platform_len;
+      ++m;
+    }
+#ifdef USE_TLS
+  temp[m].str = "tls";
+  temp[m].len = 3;
+  ++m;
+#endif
+  assert (m == cnt);
+
+  /* Determine the total size of all strings together.  */
+  if (cnt == 1)
+    total = temp[0].len + 1;
+  else
+    {
+      total = (1UL << (cnt - 2)) * (temp[0].len + temp[cnt - 1].len + 2);
+      for (n = 1; n + 1 < cnt; ++n)
+	total += (1UL << (cnt - 3)) * (temp[n].len + 1);
+    }
+
+  /* The result structure: we use a very compressed way to store the
+     various combinations of capability names.  */
+  *sz = 1 << cnt;
+  result = (struct r_strlenpair *) malloc (*sz * sizeof (*result) + total);
+  if (result == NULL)
+    {
+#ifndef USE_TLS
+    no_memory:
+#endif
+      _dl_signal_error (ENOMEM, NULL, NULL,
+			N_("cannot create capability list"));
+    }
+
+  if (cnt == 1)
+    {
+      result[0].str = (char *) (result + *sz);
+      result[0].len = temp[0].len + 1;
+      result[1].str = (char *) (result + *sz);
+      result[1].len = 0;
+      cp = __mempcpy ((char *) (result + *sz), temp[0].str, temp[0].len);
+      *cp = '/';
+      *sz = 2;
+      *max_capstrlen = result[0].len;
+
+      return result;
+    }
+
+  /* Fill in the information.  This follows the following scheme
+     (indeces from TEMP for four strings):
+	entry #0: 0, 1, 2, 3	binary: 1111
+	      #1: 0, 1, 3	        1101
+	      #2: 0, 2, 3	        1011
+	      #3: 0, 3		        1001
+     This allows the representation of all possible combinations of
+     capability names in the string.  First generate the strings.  */
+  result[1].str = result[0].str = cp = (char *) (result + *sz);
+#define add(idx) \
+      cp = __mempcpy (__mempcpy (cp, temp[idx].str, temp[idx].len), "/", 1);
+  if (cnt == 2)
+    {
+      add (1);
+      add (0);
+    }
+  else
+    {
+      n = 1 << (cnt - 1);
+      do
+	{
+	  n -= 2;
+
+	  /* We always add the last string.  */
+	  add (cnt - 1);
+
+	  /* Add the strings which have the bit set in N.  */
+	  for (m = cnt - 2; m > 0; --m)
+	    if ((n & (1 << m)) != 0)
+	      add (m);
+
+	  /* Always add the first string.  */
+	  add (0);
+	}
+      while (n != 0);
+    }
+#undef add
+
+  /* Now we are ready to install the string pointers and length.  */
+  for (n = 0; n < (1UL << cnt); ++n)
+    result[n].len = 0;
+  n = cnt;
+  do
+    {
+      size_t mask = 1 << --n;
+
+      rp = result;
+      for (m = 1 << cnt; m > 0; ++rp)
+	if ((--m & mask) != 0)
+	  rp->len += temp[n].len + 1;
+    }
+  while (n != 0);
+
+  /* The first half of the strings all include the first string.  */
+  n = (1 << cnt) - 2;
+  rp = &result[2];
+  while (n != (1UL << (cnt - 1)))
+    {
+      if ((--n & 1) != 0)
+	rp[0].str = rp[-2].str + rp[-2].len;
+      else
+	rp[0].str = rp[-1].str;
+      ++rp;
+    }
+
+  /* The second half starts right after the first part of the string of
+     the corresponding entry in the first half.  */
+  do
+    {
+      rp[0].str = rp[-(1 << (cnt - 1))].str + temp[cnt - 1].len + 1;
+      ++rp;
+    }
+  while (--n != 0);
+
+  /* The maximum string length.  */
+  *max_capstrlen = result[0].len;
+
+  return result;
+}
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
new file mode 100644
index 0000000000..4fed570d5c
--- /dev/null
+++ b/elf/dl-tls.c
@@ -0,0 +1,799 @@
+/* Thread-local storage handling in the ELF dynamic linker.  Generic version.
+   Copyright (C) 2002, 2003, 2004, 2005 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+#include <tls.h>
+
+/* We don't need any of this if TLS is not supported.  */
+#ifdef USE_TLS
+
+# include <dl-tls.h>
+# include <ldsodefs.h>
+
+/* Amount of excess space to allocate in the static TLS area
+   to allow dynamic loading of modules defining IE-model TLS data.  */
+# define TLS_STATIC_SURPLUS	64 + DL_NNS * 100
+
+/* Value used for dtv entries for which the allocation is delayed.  */
+# define TLS_DTV_UNALLOCATED	((void *) -1l)
+
+
+/* Out-of-memory handler.  */
+# ifdef SHARED
+static void
+__attribute__ ((__noreturn__))
+oom (void)
+{
+  _dl_fatal_printf ("cannot allocate memory for thread-local data: ABORT\n");
+}
+# endif
+
+
+size_t
+internal_function
+_dl_next_tls_modid (void)
+{
+  size_t result;
+
+  if (__builtin_expect (GL(dl_tls_dtv_gaps), false))
+    {
+      size_t disp = 0;
+      struct dtv_slotinfo_list *runp = GL(dl_tls_dtv_slotinfo_list);
+
+      /* Note that this branch will never be executed during program
+	 start since there are no gaps at that time.  Therefore it
+	 does not matter that the dl_tls_dtv_slotinfo is not allocated
+	 yet when the function is called for the first times.
+
+	 NB: the offset +1 is due to the fact that DTV[0] is used
+	 for something else.  */
+      result = GL(dl_tls_static_nelem) + 1;
+      if (result <= GL(dl_tls_max_dtv_idx))
+	do
+	  {
+	    while (result - disp < runp->len)
+	      {
+		if (runp->slotinfo[result - disp].map == NULL)
+		  break;
+
+		++result;
+		assert (result <= GL(dl_tls_max_dtv_idx) + 1);
+	      }
+
+	    if (result - disp < runp->len)
+	      break;
+
+	    disp += runp->len;
+	  }
+	while ((runp = runp->next) != NULL);
+
+      if (result > GL(dl_tls_max_dtv_idx))
+	{
+	  /* The new index must indeed be exactly one higher than the
+	     previous high.  */
+	  assert (result == GL(dl_tls_max_dtv_idx) + 1);
+	  /* There is no gap anymore.  */
+	  GL(dl_tls_dtv_gaps) = false;
+
+	  goto nogaps;
+	}
+    }
+  else
+    {
+      /* No gaps, allocate a new entry.  */
+    nogaps:
+
+      result = ++GL(dl_tls_max_dtv_idx);
+    }
+
+  return result;
+}
+
+
+# ifdef SHARED
+void
+internal_function
+_dl_determine_tlsoffset (void)
+{
+  size_t max_align = TLS_TCB_ALIGN;
+  size_t freetop = 0;
+  size_t freebottom = 0;
+
+  /* The first element of the dtv slot info list is allocated.  */
+  assert (GL(dl_tls_dtv_slotinfo_list) != NULL);
+  /* There is at this point only one element in the
+     dl_tls_dtv_slotinfo_list list.  */
+  assert (GL(dl_tls_dtv_slotinfo_list)->next == NULL);
+
+  struct dtv_slotinfo *slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo;
+
+  /* Determining the offset of the various parts of the static TLS
+     block has several dependencies.  In addition we have to work
+     around bugs in some toolchains.
+
+     Each TLS block from the objects available at link time has a size
+     and an alignment requirement.  The GNU ld computes the alignment
+     requirements for the data at the positions *in the file*, though.
+     I.e, it is not simply possible to allocate a block with the size
+     of the TLS program header entry.  The data is layed out assuming
+     that the first byte of the TLS block fulfills
+
+       p_vaddr mod p_align == &TLS_BLOCK mod p_align
+
+     This means we have to add artificial padding at the beginning of
+     the TLS block.  These bytes are never used for the TLS data in
+     this module but the first byte allocated must be aligned
+     according to mod p_align == 0 so that the first byte of the TLS
+     block is aligned according to p_vaddr mod p_align.  This is ugly
+     and the linker can help by computing the offsets in the TLS block
+     assuming the first byte of the TLS block is aligned according to
+     p_align.
+
+     The extra space which might be allocated before the first byte of
+     the TLS block need not go unused.  The code below tries to use
+     that memory for the next TLS block.  This can work if the total
+     memory requirement for the next TLS block is smaller than the
+     gap.  */
+
+# if TLS_TCB_AT_TP
+  /* We simply start with zero.  */
+  size_t offset = 0;
+
+  for (size_t cnt = 0; slotinfo[cnt].map != NULL; ++cnt)
+    {
+      assert (cnt < GL(dl_tls_dtv_slotinfo_list)->len);
+
+      size_t firstbyte = (-slotinfo[cnt].map->l_tls_firstbyte_offset
+			  & (slotinfo[cnt].map->l_tls_align - 1));
+      size_t off;
+      max_align = MAX (max_align, slotinfo[cnt].map->l_tls_align);
+
+      if (freebottom - freetop >= slotinfo[cnt].map->l_tls_blocksize)
+	{
+	  off = roundup (freetop + slotinfo[cnt].map->l_tls_blocksize
+			 - firstbyte, slotinfo[cnt].map->l_tls_align)
+		+ firstbyte;
+	  if (off <= freebottom)
+	    {
+	      freetop = off;
+
+	      /* XXX For some architectures we perhaps should store the
+		 negative offset.  */
+	      slotinfo[cnt].map->l_tls_offset = off;
+	      continue;
+	    }
+	}
+
+      off = roundup (offset + slotinfo[cnt].map->l_tls_blocksize - firstbyte,
+		     slotinfo[cnt].map->l_tls_align) + firstbyte;
+      if (off > offset + slotinfo[cnt].map->l_tls_blocksize
+		+ (freebottom - freetop))
+	{
+	  freetop = offset;
+	  freebottom = off - slotinfo[cnt].map->l_tls_blocksize;
+	}
+      offset = off;
+
+      /* XXX For some architectures we perhaps should store the
+	 negative offset.  */
+      slotinfo[cnt].map->l_tls_offset = off;
+    }
+
+  GL(dl_tls_static_used) = offset;
+  GL(dl_tls_static_size) = (roundup (offset + TLS_STATIC_SURPLUS, max_align)
+			    + TLS_TCB_SIZE);
+# elif TLS_DTV_AT_TP
+  /* The TLS blocks start right after the TCB.  */
+  size_t offset = TLS_TCB_SIZE;
+
+  for (size_t cnt = 0; slotinfo[cnt].map != NULL; ++cnt)
+    {
+      assert (cnt < GL(dl_tls_dtv_slotinfo_list)->len);
+
+      size_t firstbyte = (-slotinfo[cnt].map->l_tls_firstbyte_offset
+			  & (slotinfo[cnt].map->l_tls_align - 1));
+      size_t off;
+      max_align = MAX (max_align, slotinfo[cnt].map->l_tls_align);
+
+      if (slotinfo[cnt].map->l_tls_blocksize <= freetop - freebottom)
+	{
+	  off = roundup (freebottom, slotinfo[cnt].map->l_tls_align);
+	  if (off - freebottom < firstbyte)
+	    off += slotinfo[cnt].map->l_tls_align;
+	  if (off + slotinfo[cnt].map->l_tls_blocksize - firstbyte <= freetop)
+	    {
+	      slotinfo[cnt].map->l_tls_offset = off - firstbyte;
+	      freebottom = (off + slotinfo[cnt].map->l_tls_blocksize
+			    - firstbyte);
+	      continue;
+	    }
+	}
+
+      off = roundup (offset, slotinfo[cnt].map->l_tls_align);
+      if (off - offset < firstbyte)
+	off += slotinfo[cnt].map->l_tls_align;
+
+      slotinfo[cnt].map->l_tls_offset = off - firstbyte;
+      if (off - firstbyte - offset > freetop - freebottom)
+	{
+	  freebottom = offset;
+	  freetop = off - firstbyte;
+	}
+
+      offset = off + slotinfo[cnt].map->l_tls_blocksize - firstbyte;
+    }
+
+  GL(dl_tls_static_used) = offset;
+  GL(dl_tls_static_size) = roundup (offset + TLS_STATIC_SURPLUS,
+				    TLS_TCB_ALIGN);
+# else
+#  error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+
+  /* The alignment requirement for the static TLS block.  */
+  GL(dl_tls_static_align) = max_align;
+}
+
+
+/* This is called only when the data structure setup was skipped at startup,
+   when there was no need for it then.  Now we have dynamically loaded
+   something needing TLS, or libpthread needs it.  */
+int
+internal_function
+_dl_tls_setup (void)
+{
+  assert (GL(dl_tls_dtv_slotinfo_list) == NULL);
+  assert (GL(dl_tls_max_dtv_idx) == 0);
+
+  const size_t nelem = 2 + TLS_SLOTINFO_SURPLUS;
+
+  GL(dl_tls_dtv_slotinfo_list)
+    = calloc (1, (sizeof (struct dtv_slotinfo_list)
+		  + nelem * sizeof (struct dtv_slotinfo)));
+  if (GL(dl_tls_dtv_slotinfo_list) == NULL)
+    return -1;
+
+  GL(dl_tls_dtv_slotinfo_list)->len = nelem;
+
+  /* Number of elements in the static TLS block.  It can't be zero
+     because of various assumptions.  The one element is null.  */
+  GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx) = 1;
+
+  /* This initializes more variables for us.  */
+  _dl_determine_tlsoffset ();
+
+  return 0;
+}
+rtld_hidden_def (_dl_tls_setup)
+# endif
+
+static void *
+internal_function
+allocate_dtv (void *result)
+{
+  dtv_t *dtv;
+  size_t dtv_length;
+
+  /* We allocate a few more elements in the dtv than are needed for the
+     initial set of modules.  This should avoid in most cases expansions
+     of the dtv.  */
+  dtv_length = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS;
+  dtv = calloc (dtv_length + 2, sizeof (dtv_t));
+  if (dtv != NULL)
+    {
+      /* This is the initial length of the dtv.  */
+      dtv[0].counter = dtv_length;
+
+      /* The rest of the dtv (including the generation counter) is
+	 Initialize with zero to indicate nothing there.  */
+
+      /* Add the dtv to the thread data structures.  */
+      INSTALL_DTV (result, dtv);
+    }
+  else
+    result = NULL;
+
+  return result;
+}
+
+
+/* Get size and alignment requirements of the static TLS block.  */
+void
+internal_function
+_dl_get_tls_static_info (size_t *sizep, size_t *alignp)
+{
+  *sizep = GL(dl_tls_static_size);
+  *alignp = GL(dl_tls_static_align);
+}
+
+
+void *
+internal_function
+_dl_allocate_tls_storage (void)
+{
+  void *result;
+  size_t size = GL(dl_tls_static_size);
+
+# if TLS_DTV_AT_TP
+  /* Memory layout is:
+     [ TLS_PRE_TCB_SIZE ] [ TLS_TCB_SIZE ] [ TLS blocks ]
+			  ^ This should be returned.  */
+  size += (TLS_PRE_TCB_SIZE + GL(dl_tls_static_align) - 1)
+	  & ~(GL(dl_tls_static_align) - 1);
+# endif
+
+  /* Allocate a correctly aligned chunk of memory.  */
+  result = __libc_memalign (GL(dl_tls_static_align), size);
+  if (__builtin_expect (result != NULL, 1))
+    {
+      /* Allocate the DTV.  */
+      void *allocated = result;
+
+# if TLS_TCB_AT_TP
+      /* The TCB follows the TLS blocks.  */
+      result = (char *) result + size - TLS_TCB_SIZE;
+
+      /* Clear the TCB data structure.  We can't ask the caller (i.e.
+	 libpthread) to do it, because we will initialize the DTV et al.  */
+      memset (result, '\0', TLS_TCB_SIZE);
+# elif TLS_DTV_AT_TP
+      result = (char *) result + size - GL(dl_tls_static_size);
+
+      /* Clear the TCB data structure and TLS_PRE_TCB_SIZE bytes before it.
+	 We can't ask the caller (i.e. libpthread) to do it, because we will
+	 initialize the DTV et al.  */
+      memset ((char *) result - TLS_PRE_TCB_SIZE, '\0',
+	      TLS_PRE_TCB_SIZE + TLS_TCB_SIZE);
+# endif
+
+      result = allocate_dtv (result);
+      if (result == NULL)
+	free (allocated);
+    }
+
+  return result;
+}
+
+
+void *
+internal_function
+_dl_allocate_tls_init (void *result)
+{
+  if (result == NULL)
+    /* The memory allocation failed.  */
+    return NULL;
+
+  dtv_t *dtv = GET_DTV (result);
+  struct dtv_slotinfo_list *listp;
+  size_t total = 0;
+  size_t maxgen = 0;
+
+  /* We have to prepare the dtv for all currently loaded modules using
+     TLS.  For those which are dynamically loaded we add the values
+     indicating deferred allocation.  */
+  listp = GL(dl_tls_dtv_slotinfo_list);
+  while (1)
+    {
+      size_t cnt;
+
+      for (cnt = total == 0 ? 1 : 0; cnt < listp->len; ++cnt)
+	{
+	  struct link_map *map;
+	  void *dest;
+
+	  /* Check for the total number of used slots.  */
+	  if (total + cnt > GL(dl_tls_max_dtv_idx))
+	    break;
+
+	  map = listp->slotinfo[cnt].map;
+	  if (map == NULL)
+	    /* Unused entry.  */
+	    continue;
+
+	  /* Keep track of the maximum generation number.  This might
+	     not be the generation counter.  */
+	  maxgen = MAX (maxgen, listp->slotinfo[cnt].gen);
+
+	  if (map->l_tls_offset == NO_TLS_OFFSET)
+	    {
+	      /* For dynamically loaded modules we simply store
+		 the value indicating deferred allocation.  */
+	      dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED;
+	      dtv[map->l_tls_modid].pointer.is_static = false;
+	      continue;
+	    }
+
+	  assert (map->l_tls_modid == cnt);
+	  assert (map->l_tls_blocksize >= map->l_tls_initimage_size);
+# if TLS_TCB_AT_TP
+	  assert ((size_t) map->l_tls_offset >= map->l_tls_blocksize);
+	  dest = (char *) result - map->l_tls_offset;
+# elif TLS_DTV_AT_TP
+	  dest = (char *) result + map->l_tls_offset;
+# else
+#  error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+# endif
+
+	  /* Copy the initialization image and clear the BSS part.  */
+	  dtv[map->l_tls_modid].pointer.val = dest;
+	  dtv[map->l_tls_modid].pointer.is_static = true;
+	  memset (__mempcpy (dest, map->l_tls_initimage,
+			     map->l_tls_initimage_size), '\0',
+		  map->l_tls_blocksize - map->l_tls_initimage_size);
+	}
+
+      total += cnt;
+      if (total >= GL(dl_tls_max_dtv_idx))
+	break;
+
+      listp = listp->next;
+      assert (listp != NULL);
+    }
+
+  /* The DTV version is up-to-date now.  */
+  dtv[0].counter = maxgen;
+
+  return result;
+}
+rtld_hidden_def (_dl_allocate_tls_init)
+
+void *
+internal_function
+_dl_allocate_tls (void *mem)
+{
+  return _dl_allocate_tls_init (mem == NULL
+				? _dl_allocate_tls_storage ()
+				: allocate_dtv (mem));
+}
+rtld_hidden_def (_dl_allocate_tls)
+
+
+void
+internal_function
+_dl_deallocate_tls (void *tcb, bool dealloc_tcb)
+{
+  dtv_t *dtv = GET_DTV (tcb);
+
+  /* We need to free the memory allocated for non-static TLS.  */
+  for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt)
+    if (! dtv[1 + cnt].pointer.is_static
+	&& dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED)
+      free (dtv[1 + cnt].pointer.val);
+
+  /* The array starts with dtv[-1].  */
+#ifdef SHARED
+  if (dtv != GL(dl_initial_dtv))
+#endif
+    free (dtv - 1);
+
+  if (dealloc_tcb)
+    {
+# if TLS_TCB_AT_TP
+      /* The TCB follows the TLS blocks.  Back up to free the whole block.  */
+      tcb -= GL(dl_tls_static_size) - TLS_TCB_SIZE;
+# elif TLS_DTV_AT_TP
+      /* Back up the TLS_PRE_TCB_SIZE bytes.  */
+      tcb -= (TLS_PRE_TCB_SIZE + GL(dl_tls_static_align) - 1)
+	     & ~(GL(dl_tls_static_align) - 1);
+# endif
+      free (tcb);
+    }
+}
+rtld_hidden_def (_dl_deallocate_tls)
+
+
+# ifdef SHARED
+/* The __tls_get_addr function has two basic forms which differ in the
+   arguments.  The IA-64 form takes two parameters, the module ID and
+   offset.  The form used, among others, on IA-32 takes a reference to
+   a special structure which contain the same information.  The second
+   form seems to be more often used (in the moment) so we default to
+   it.  Users of the IA-64 form have to provide adequate definitions
+   of the following macros.  */
+#  ifndef GET_ADDR_ARGS
+#   define GET_ADDR_ARGS tls_index *ti
+#  endif
+#  ifndef GET_ADDR_MODULE
+#   define GET_ADDR_MODULE ti->ti_module
+#  endif
+#  ifndef GET_ADDR_OFFSET
+#   define GET_ADDR_OFFSET ti->ti_offset
+#  endif
+
+
+static void *
+allocate_and_init (struct link_map *map)
+{
+  void *newp;
+
+  newp = __libc_memalign (map->l_tls_align, map->l_tls_blocksize);
+  if (newp == NULL)
+    oom ();
+
+  /* Initialize the memory.  */
+  memset (__mempcpy (newp, map->l_tls_initimage, map->l_tls_initimage_size),
+	  '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
+
+  return newp;
+}
+
+
+struct link_map *
+_dl_update_slotinfo (unsigned long int req_modid)
+{
+  struct link_map *the_map = NULL;
+  dtv_t *dtv = THREAD_DTV ();
+
+  /* The global dl_tls_dtv_slotinfo array contains for each module
+     index the generation counter current when the entry was created.
+     This array never shrinks so that all module indices which were
+     valid at some time can be used to access it.  Before the first
+     use of a new module index in this function the array was extended
+     appropriately.  Access also does not have to be guarded against
+     modifications of the array.  It is assumed that pointer-size
+     values can be read atomically even in SMP environments.  It is
+     possible that other threads at the same time dynamically load
+     code and therefore add to the slotinfo list.  This is a problem
+     since we must not pick up any information about incomplete work.
+     The solution to this is to ignore all dtv slots which were
+     created after the one we are currently interested.  We know that
+     dynamic loading for this module is completed and this is the last
+     load operation we know finished.  */
+  unsigned long int idx = req_modid;
+  struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list);
+
+  while (idx >= listp->len)
+    {
+      idx -= listp->len;
+      listp = listp->next;
+    }
+
+  if (dtv[0].counter < listp->slotinfo[idx].gen)
+    {
+      /* The generation counter for the slot is higher than what the
+	 current dtv implements.  We have to update the whole dtv but
+	 only those entries with a generation counter <= the one for
+	 the entry we need.  */
+      size_t new_gen = listp->slotinfo[idx].gen;
+      size_t total = 0;
+
+      /* We have to look through the entire dtv slotinfo list.  */
+      listp =  GL(dl_tls_dtv_slotinfo_list);
+      do
+	{
+	  for (size_t cnt = total == 0 ? 1 : 0; cnt < listp->len; ++cnt)
+	    {
+	      size_t gen = listp->slotinfo[cnt].gen;
+
+	      if (gen > new_gen)
+		/* This is a slot for a generation younger than the
+		   one we are handling now.  It might be incompletely
+		   set up so ignore it.  */
+		continue;
+
+	      /* If the entry is older than the current dtv layout we
+		 know we don't have to handle it.  */
+	      if (gen <= dtv[0].counter)
+		continue;
+
+	      /* If there is no map this means the entry is empty.  */
+	      struct link_map *map = listp->slotinfo[cnt].map;
+	      if (map == NULL)
+		{
+		  /* If this modid was used at some point the memory
+		     might still be allocated.  */
+		  if (! dtv[total + cnt].pointer.is_static
+		      && dtv[total + cnt].pointer.val != TLS_DTV_UNALLOCATED)
+		    {
+		      free (dtv[total + cnt].pointer.val);
+		      dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED;
+		    }
+
+		  continue;
+		}
+
+	      /* Check whether the current dtv array is large enough.  */
+	      size_t modid = map->l_tls_modid;
+	      assert (total + cnt == modid);
+	      if (dtv[-1].counter < modid)
+		{
+		  /* Reallocate the dtv.  */
+		  dtv_t *newp;
+		  size_t newsize = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS;
+		  size_t oldsize = dtv[-1].counter;
+
+		  assert (map->l_tls_modid <= newsize);
+
+		  if (dtv == GL(dl_initial_dtv))
+		    {
+		      /* This is the initial dtv that was allocated
+			 during rtld startup using the dl-minimal.c
+			 malloc instead of the real malloc.  We can't
+			 free it, we have to abandon the old storage.  */
+
+		      newp = malloc ((2 + newsize) * sizeof (dtv_t));
+		      if (newp == NULL)
+			oom ();
+		      memcpy (newp, &dtv[-1], oldsize * sizeof (dtv_t));
+		    }
+		  else
+		    {
+		      newp = realloc (&dtv[-1],
+				      (2 + newsize) * sizeof (dtv_t));
+		      if (newp == NULL)
+			oom ();
+		    }
+
+		  newp[0].counter = newsize;
+
+		  /* Clear the newly allocated part.  */
+		  memset (newp + 2 + oldsize, '\0',
+			  (newsize - oldsize) * sizeof (dtv_t));
+
+		  /* Point dtv to the generation counter.  */
+		  dtv = &newp[1];
+
+		  /* Install this new dtv in the thread data
+		     structures.  */
+		  INSTALL_NEW_DTV (dtv);
+		}
+
+	      /* If there is currently memory allocate for this
+		 dtv entry free it.  */
+	      /* XXX Ideally we will at some point create a memory
+		 pool.  */
+	      if (! dtv[modid].pointer.is_static
+		  && dtv[modid].pointer.val != TLS_DTV_UNALLOCATED)
+		/* Note that free is called for NULL is well.  We
+		   deallocate even if it is this dtv entry we are
+		   supposed to load.  The reason is that we call
+		   memalign and not malloc.  */
+		free (dtv[modid].pointer.val);
+
+	      /* This module is loaded dynamically- We defer memory
+		 allocation.  */
+	      dtv[modid].pointer.is_static = false;
+	      dtv[modid].pointer.val = TLS_DTV_UNALLOCATED;
+
+	      if (modid == req_modid)
+		the_map = map;
+	    }
+
+	  total += listp->len;
+	}
+      while ((listp = listp->next) != NULL);
+
+      /* This will be the new maximum generation counter.  */
+      dtv[0].counter = new_gen;
+    }
+
+  return the_map;
+}
+
+
+/* The generic dynamic and local dynamic model cannot be used in
+   statically linked applications.  */
+void *
+__tls_get_addr (GET_ADDR_ARGS)
+{
+  dtv_t *dtv = THREAD_DTV ();
+  struct link_map *the_map = NULL;
+  void *p;
+
+  if (__builtin_expect (dtv[0].counter != GL(dl_tls_generation), 0))
+    the_map = _dl_update_slotinfo (GET_ADDR_MODULE);
+
+  p = dtv[GET_ADDR_MODULE].pointer.val;
+
+  if (__builtin_expect (p == TLS_DTV_UNALLOCATED, 0))
+    {
+      /* The allocation was deferred.  Do it now.  */
+      if (the_map == NULL)
+	{
+	  /* Find the link map for this module.  */
+	  size_t idx = GET_ADDR_MODULE;
+	  struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list);
+
+	  while (idx >= listp->len)
+	    {
+	      idx -= listp->len;
+	      listp = listp->next;
+	    }
+
+	  the_map = listp->slotinfo[idx].map;
+	}
+
+      p = dtv[GET_ADDR_MODULE].pointer.val = allocate_and_init (the_map);
+      dtv[GET_ADDR_MODULE].pointer.is_static = false;
+    }
+
+  return (char *) p + GET_ADDR_OFFSET;
+}
+# endif
+
+
+
+void
+_dl_add_to_slotinfo (struct link_map  *l)
+{
+  /* Now that we know the object is loaded successfully add
+     modules containing TLS data to the dtv info table.  We
+     might have to increase its size.  */
+  struct dtv_slotinfo_list *listp;
+  struct dtv_slotinfo_list *prevp;
+  size_t idx = l->l_tls_modid;
+
+  /* Find the place in the dtv slotinfo list.  */
+  listp = GL(dl_tls_dtv_slotinfo_list);
+  prevp = NULL;		/* Needed to shut up gcc.  */
+  do
+    {
+      /* Does it fit in the array of this list element?  */
+      if (idx < listp->len)
+	break;
+      idx -= listp->len;
+      prevp = listp;
+      listp = listp->next;
+    }
+  while (listp != NULL);
+
+  if (listp == NULL)
+    {
+      /* When we come here it means we have to add a new element
+	 to the slotinfo list.  And the new module must be in
+	 the first slot.  */
+      assert (idx == 0);
+
+      listp = prevp->next = (struct dtv_slotinfo_list *)
+	malloc (sizeof (struct dtv_slotinfo_list)
+		+ TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo));
+      if (listp == NULL)
+	{
+	  /* We ran out of memory.  We will simply fail this
+	     call but don't undo anything we did so far.  The
+	     application will crash or be terminated anyway very
+	     soon.  */
+
+	  /* We have to do this since some entries in the dtv
+	     slotinfo array might already point to this
+	     generation.  */
+	  ++GL(dl_tls_generation);
+
+	  _dl_signal_error (ENOMEM, "dlopen", NULL, N_("\
+cannot create TLS data structures"));
+	}
+
+      listp->len = TLS_SLOTINFO_SURPLUS;
+      listp->next = NULL;
+      memset (listp->slotinfo, '\0',
+	      TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo));
+    }
+
+  /* Add the information into the slotinfo data structure.  */
+  listp->slotinfo[idx].map = l;
+  listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1;
+}
+#endif	/* use TLS */
diff --git a/elf/dl-trampoline.c b/elf/dl-trampoline.c
new file mode 100644
index 0000000000..3ca89f3879
--- /dev/null
+++ b/elf/dl-trampoline.c
@@ -0,0 +1 @@
+#error "Architecture specific PLT trampolines must be defined."