about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile4
-rw-r--r--elf/dl-error.c4
-rw-r--r--elf/dl-load.c34
-rw-r--r--elf/dl-lookup.c159
-rw-r--r--elf/dl-object.c9
-rw-r--r--elf/dl-open.c4
-rw-r--r--elf/dl-reloc.c11
-rw-r--r--elf/dl-runtime.c35
-rw-r--r--elf/dl-version.c337
-rw-r--r--elf/dlerror.c4
-rw-r--r--elf/dlfcn.h13
-rw-r--r--elf/dlopen.c4
-rw-r--r--elf/dlsym.c4
-rw-r--r--elf/dlvsym.c72
-rw-r--r--elf/do-rel.h36
-rw-r--r--elf/elf.h48
-rw-r--r--elf/ldd.bash.in14
-rw-r--r--elf/ldd.sh.in14
-rw-r--r--elf/link.h66
-rw-r--r--elf/rtld.c77
20 files changed, 851 insertions, 98 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 4666919478..b69b8a0754 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -22,7 +22,7 @@ subdir		:= elf
 
 headers		= elf.h elfclass.h link.h dlfcn.h
 routines	= $(dl-routines) dl-open dl-close dl-symbol dl-support \
-		  enbl-secure
+		  dl-version enbl-secure
 
 # The core dynamic linking functions are in libc for the static and
 # profiled libraries.
@@ -39,7 +39,7 @@ distribute	= $(rtld-routines:=.c) dynamic-link.h do-rel.h dl-machine.h \
 
 extra-libs	 = libdl
 extra-libs-others = $(extra-libs)
-libdl-routines	:= dlopen dlclose dlsym dlerror dladdr
+libdl-routines	:= dlopen dlclose dlsym dlvsym dlerror dladdr
 
 before-compile = $(objpfx)trusted-dirs.h
 
diff --git a/elf/dl-error.c b/elf/dl-error.c
index a19ccff626..263bd65eb0 100644
--- a/elf/dl-error.c
+++ b/elf/dl-error.c
@@ -66,9 +66,9 @@ _dl_signal_error (int errcode,
   else if (receiver)
     {
       /* We are inside _dl_receive_error.  Call the user supplied
-	 handler and resume the work.  The receiver will still
+	 handler and resume the work.  The receiver will still be
 	 installed.  */
-      (*receiver) (errstring, objname);
+      (*receiver) (errcode, objname, errstring);
     }
   else
     {
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 7368858f64..e3de0764ec 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -154,11 +154,34 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
   for (l = _dl_loaded; l; l = l->l_next)
     if (! strcmp (realname, l->l_name))
       {
+	struct libname_list *lnp, *lastp;
 	/* The object is already loaded.
 	   Just bump its reference count and return it.  */
 	__close (fd);
-	free (name);
+
+	/* If the name is not in the list of names for this object add
+	   it.  */
 	free (realname);
+	lastp = NULL;
+	for (lnp = l->l_libname; lnp != NULL; lastp = lnp, lnp = lnp->next)
+	  if (strcmp (name, lnp->name) == 0)
+	    {
+	      free (name);
+	      break;
+	    }
+	if (lnp == NULL)
+	  {
+	    struct libname_list *newname = malloc (sizeof *newname);
+	    if (newname == NULL)
+	      /* No more memory.  */
+	      lose (ENOMEM, "cannot allocate name record");
+	    /* The object should have a libname set.  */
+	    assert (lastp != NULL);
+
+	    newname->name = name;
+	    newname->next = NULL;
+	    lastp->next = newname;
+	  }
 	++l->l_opencount;
 	return l;
       }
@@ -517,8 +540,7 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
 
   /* Look for this name among those already loaded.  */
   for (l = _dl_loaded; l; l = l->l_next)
-    if (! strcmp (name, l->l_libname) || /* NAME was requested before.  */
-	! strcmp (name, l->l_name) || /* NAME was found before.  */
+    if (_dl_does_name_match_p (name, l) ||
 	/* If the requested name matches the soname of a loaded object,
 	   use that object.  */
 	(l->l_info[DT_SONAME] &&
@@ -559,15 +581,17 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
 				 l->l_info[DT_STRTAB]->d_un.d_ptr +
 				 l->l_info[DT_RPATH]->d_un.d_val), NULL);
       /* Try an environment variable (unless setuid).  */
-      if (fd == -1 && ! __libc_enable_secure)
+      if (fd == -1)
 	{
 	  static const char *trusted_dirs[] =
 	  {
 #include "trusted-dirs.h"
 	    NULL
 	  };
+	  const char *ld_library_path = getenv ("LD_LIBRARY_PATH");
 
-	  trypath (getenv ("LD_LIBRARY_PATH"), trusted_dirs);
+	  if (ld_library_path != NULL && *ld_library_path != '\0')
+	    trypath (ld_library_path, trusted_dirs);
 	}
       if (fd == -1)
 	{
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 1000a52e0d..06d1d09b90 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -1,5 +1,5 @@
 /* Look up a symbol in the loaded objects.
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997 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
@@ -22,6 +22,9 @@
 #include <assert.h>
 #include <string.h>
 
+#include "../stdio-common/_itoa.h"
+
+#define VERSTAG(tag)	(DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
 
 struct sym_val
   {
@@ -30,22 +33,46 @@ struct sym_val
   };
 
 
-/* This is the hashing function specified by the ELF ABI.  */
+/* This is the hashing function specified by the ELF ABI.  In the
+   first five operations now overflow is possible so we optimized it a
+   bit.  */
 static inline unsigned
 _dl_elf_hash (const char *name)
 {
   unsigned long int hash = 0;
-  while (*name != '\0')
+  if (*name != '\0')
     {
-      unsigned long int hi;
       hash = (hash << 4) + *name++;
-      hi = hash & 0xf0000000;
-      if (hi != 0)
+      if (*name != '\0')
 	{
-	  hash ^= hi >> 24;
-	  /* The ELF ABI says `hash &= ~hi', but this is equivalent
-	     in this case and on some machines one insn instead of two.  */
-	  hash ^= hi;
+	  hash = (hash << 4) + *name++;
+	  if (*name != '\0')
+	    {
+	      hash = (hash << 4) + *name++;
+	      if (*name != '\0')
+		{
+		  hash = (hash << 4) + *name++;
+		  if (*name != '\0')
+		    {
+		      hash = (hash << 4) + *name++;
+		      while (*name != '\0')
+			{
+			  unsigned long int hi;
+			  hash = (hash << 4) + *name++;
+			  hi = hash & 0xf0000000;
+			  if (hi != 0)
+			    {
+			      hash ^= hi >> 24;
+			      /* The ELF ABI says `hash &= ~hi', but
+				 this is equivalent in this case and
+				 on some machines one insn instead of
+				 two.  */
+			      hash ^= hi;
+			    }
+			}
+		    }
+		}
+	    }
 	}
     }
   return hash;
@@ -57,7 +84,8 @@ static inline ElfW(Addr)
 do_lookup (const char *undef_name, unsigned long int hash,
 	   const ElfW(Sym) **ref, struct sym_val *result,
 	   struct link_map *list[], size_t i, size_t n,
-	   const char *reference_name, struct link_map *skip, int flags)
+	   const char *reference_name, const hash_name_pair *version,
+	   struct link_map *skip, int flags)
 {
   struct link_map *map;
 
@@ -65,6 +93,7 @@ do_lookup (const char *undef_name, unsigned long int hash,
     {
       const ElfW(Sym) *symtab;
       const char *strtab;
+      const ElfW(Half) *verstab;
       ElfW(Symndx) symidx;
 
       map = list[i];
@@ -79,6 +108,12 @@ do_lookup (const char *undef_name, unsigned long int hash,
 
       symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
       strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr);
+      if (version != NULL
+	  && map->l_nversions > 0 && map->l_info[VERSTAG (DT_VERSYM)] != NULL)
+	verstab = ((void *) map->l_addr
+		   + map->l_info[VERSTAG (DT_VERSYM)]->d_un.d_ptr);
+      else
+	verstab = NULL;
 
       /* Search the appropriate hash bucket in this object's symbol table
 	 for a definition for the same symbol name.  */
@@ -108,6 +143,15 @@ do_lookup (const char *undef_name, unsigned long int hash,
 	    /* Not the symbol we are looking for.  */
 	    continue;
 
+	  if (verstab != NULL)
+	    {
+	      /* We can match the version information.  */
+	      ElfW(Half) ndx = verstab[symidx] & 0x7fff;
+	      if (map->l_versions[ndx].hash != version->hash
+		  || strcmp (map->l_versions[ndx].name, version->name))
+	      continue;
+	    }
+
 	  switch (ELFW(ST_BIND) (sym->st_info))
 	    {
 	    case STB_GLOBAL:
@@ -155,14 +199,14 @@ _dl_lookup_symbol (const char *undef_name, const ElfW(Sym) **ref,
   for (scope = symbol_scope; *scope; ++scope)
     if (do_lookup (undef_name, hash, ref, &current_value,
 		   (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
-		   reference_name, NULL, flags))
+		   reference_name, NULL, NULL, flags))
       break;
 
   if (current_value.s == NULL &&
       (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
     {
       /* We could find no value for a strong reference.  */
-      const char msg[] = "undefined symbol: ";
+      static const char msg[] = "undefined symbol: ";
       const size_t len = strlen (undef_name);
       char buf[sizeof msg + len];
       memcpy (buf, msg, sizeof msg - 1);
@@ -199,11 +243,96 @@ _dl_lookup_symbol_skip (const char *undef_name, const ElfW(Sym) **ref,
 
   if (! do_lookup (undef_name, hash, ref, &current_value,
 		   (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
-		   reference_name, skip_map, flags))
+		   reference_name, NULL, skip_map, flags))
+    while (*++scope)
+      if (do_lookup (undef_name, hash, ref, &current_value,
+		     (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
+		     reference_name, NULL, skip_map, flags))
+	break;
+
+  *ref = current_value.s;
+  return current_value.a;
+}
+
+
+/* This function works like _dl_lookup_symbol but it takes an
+   additional arguement with the version number of the requested
+   symbol.
+
+   XXX We'll see whether we need this separate function.  */
+ElfW(Addr)
+_dl_lookup_versioned_symbol (const char *undef_name, const ElfW(Sym) **ref,
+			     struct link_map *symbol_scope[],
+			     const char *reference_name,
+			     const hash_name_pair *version, int flags)
+{
+  const unsigned long int hash = _dl_elf_hash (undef_name);
+  struct sym_val current_value = { 0, NULL };
+  struct link_map **scope;
+
+  /* Search the relevant loaded objects for a definition.  */
+  for (scope = symbol_scope; *scope; ++scope)
+    if (do_lookup (undef_name, hash, ref, &current_value,
+		   (*scope)->l_searchlist, 0, (*scope)->l_nsearchlist,
+		   reference_name, version, NULL, flags))
+      break;
+
+  if (current_value.s == NULL &&
+      (*ref == NULL || ELFW(ST_BIND) ((*ref)->st_info) != STB_WEAK))
+    {
+      /* We could find no value for a strong reference.  */
+      static const char msg1[] = "undefined symbol: ";
+      const size_t len = strlen (undef_name);
+      static const char msg2[] = ", version ";
+      const size_t verslen = strlen (version->name ?: "") + 1;
+      char buf[sizeof msg1 - 1 + len + sizeof msg2 - 1 + verslen];
+
+      memcpy (buf, msg1, sizeof msg1 - 1);
+      memcpy (&buf[sizeof msg1 - 1], undef_name, len + 1);
+      memcpy (&buf[sizeof msg1 - 1 + len], msg2, sizeof msg2 - 1);
+      memcpy (&buf[sizeof msg1 - 1 + len + sizeof msg2 - 1], version->name,
+	      verslen);
+      _dl_signal_error (0, reference_name, buf);
+    }
+
+  *ref = current_value.s;
+  return current_value.a;
+}
+
+
+/* Similar to _dl_lookup_symbol_skip but takes an additional argument
+   with the version we are looking for.  */
+ElfW(Addr)
+_dl_lookup_versioned_symbol_skip (const char *undef_name,
+				  const ElfW(Sym) **ref,
+				  struct link_map *symbol_scope[],
+				  const char *reference_name,
+				  const char *version_name,
+				  struct link_map *skip_map,
+				  int flags)
+{
+  const unsigned long int hash = _dl_elf_hash (undef_name);
+  struct sym_val current_value = { 0, NULL };
+  struct link_map **scope;
+  hash_name_pair version;
+  size_t i;
+
+  /* First convert the VERSION_NAME into a `hash_name_pair' value.  */
+  version.hash = _dl_elf_hash (version_name);
+  version.name = version_name;
+
+  /* Search the relevant loaded objects for a definition.  */
+  scope = symbol_scope;
+  for (i = 0; (*scope)->l_dupsearchlist[i] != skip_map; ++i)
+    assert (i < (*scope)->l_ndupsearchlist);
+
+  if (! do_lookup (undef_name, hash, ref, &current_value,
+		   (*scope)->l_dupsearchlist, i, (*scope)->l_ndupsearchlist,
+		   reference_name, &version, skip_map, flags))
     while (*++scope)
       if (do_lookup (undef_name, hash, ref, &current_value,
 		     (*scope)->l_dupsearchlist, 0, (*scope)->l_ndupsearchlist,
-		     reference_name, skip_map, flags))
+		     reference_name, &version, skip_map, flags))
 	break;
 
   *ref = current_value.s;
diff --git a/elf/dl-object.c b/elf/dl-object.c
index 6986dc34d3..941bfa34cc 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -1,5 +1,5 @@
 /* Storage management for the chain of loaded shared objects.
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997 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
@@ -34,12 +34,15 @@ struct link_map *
 _dl_new_object (char *realname, const char *libname, int type)
 {
   struct link_map *new = malloc (sizeof *new);
-  if (! new)
+  struct libname_list *newname = malloc (sizeof *newname);
+  if (! new || ! newname)
     return NULL;
 
   memset (new, 0, sizeof *new);
   new->l_name = realname;
-  new->l_libname = libname;
+  newname->name = libname;
+  newname->next = NULL;
+  new->l_libname = newname;
   new->l_type = type;
 
   if (_dl_loaded == NULL)
diff --git a/elf/dl-open.c b/elf/dl-open.c
index fe9f8ff017..5915b7f2a2 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -1,5 +1,5 @@
 /* Load a shared object at runtime, relocate it, and run its initializer.
-   Copyright (C) 1996 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997 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
@@ -54,6 +54,8 @@ _dl_open (const char *file, int mode)
   /* Load that object's dependencies.  */
   _dl_map_object_deps (new, NULL, 0, 0);
 
+  /* So far, so good.  Now check the versions.  */
+  (void) _dl_check_map_versions (new, 0);
 
   /* Relocate the objects loaded.  We do this in reverse order so that copy
      relocs of earlier objects overwrite the data written by later objects.  */
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index e299e523ee..f1c43ea174 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -1,5 +1,5 @@
 /* Relocate a shared object and resolve its references to other loaded objects.
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997 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
@@ -57,9 +57,12 @@ _dl_relocate_object (struct link_map *l, struct link_map *scope[], int lazy)
       = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);
 
     /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code.  */
-#define RESOLVE(ref, flags) \
-    (_dl_lookup_symbol (strtab + (*ref)->st_name, ref, scope, \
-			l->l_name, flags))
+#define RESOLVE(ref, version, flags) \
+    ((version) != NULL && (version)->hash != 0				      \
+     ? _dl_lookup_versioned_symbol (strtab + (*ref)->st_name, (ref), scope,   \
+				    l->l_name, (version), (flags))	      \
+     : _dl_lookup_symbol (strtab + (*ref)->st_name, (ref), scope,	      \
+			  l->l_name, (flags)))
 
 #include "dynamic-link.h"
     ELF_DYNAMIC_RELOCATE (l, lazy);
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index 088ff64c6e..08c605cd0f 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -1,5 +1,5 @@
 /* On-demand PLT fixup for shared objects.
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997 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
@@ -74,15 +74,19 @@ _dl_object_relocation_scope (struct link_map *l)
 #define elf_machine_rel 1
 #define elf_machine_rela 2
 #if elf_machine_relplt == elf_machine_rel
-#define PLTREL ElfW(Rel)
+# define PLTREL ElfW(Rel)
 #elif elf_machine_relplt == elf_machine_rela
-#define PLTREL ElfW(Rela)
+# define PLTREL ElfW(Rela)
 #else
-#error "dl-machine.h bug: elf_machine_relplt not rel or rela"
+# error "dl-machine.h bug: elf_machine_relplt not rel or rela"
 #endif
 #undef elf_machine_rel
 #undef elf_machine_rela
 
+#ifndef VERSYMIDX
+# define VERSYMIDX(sym)	(DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (sym))
+#endif
+
 /* We need to define the function as a local symbol so that the reference
    in the trampoline code will be a local PC-relative call.  Tell the
    compiler not to worry that the function appears not to be called.  */
@@ -122,13 +126,28 @@ fixup (
 
   {
     /* This macro is used as a callback from the elf_machine_relplt code.  */
-#define RESOLVE(ref, flags) \
-  (_dl_lookup_symbol (strtab + (*ref)->st_name, ref, scope, \
-		      l->l_name, flags))
+#define RESOLVE(ref, version, flags) \
+  ((version) != NULL && (version)->hash != 0				      \
+   ? _dl_lookup_versioned_symbol (strtab + (*ref)->st_name, (ref), scope,     \
+				  l->l_name, (version), (flags))	      \
+   : _dl_lookup_symbol (strtab + (*ref)->st_name, (ref), scope,		      \
+			l->l_name, (flags)))
 #include "dynamic-link.h"
 
     /* Perform the specified relocation.  */
-    elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)]);
+    if (l->l_info[VERSYMIDX (DT_VERNEEDNUM)])
+      {
+	const ElfW(Half) * version =
+	  (const ElfW(Half) *) (l->l_addr +
+				l->l_info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr);
+	ElfW(Half) ndx = version[ELFW(R_SYM) (reloc->r_info)];
+
+	elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)],
+			    &l->l_versions[ndx]);
+      }
+    else
+      elf_machine_relplt (l, reloc, &symtab[ELFW(R_SYM) (reloc->r_info)],
+			  NULL);
   }
 
   *_dl_global_scope_end = NULL;
diff --git a/elf/dl-version.c b/elf/dl-version.c
new file mode 100644
index 0000000000..4382a4c0ae
--- /dev/null
+++ b/elf/dl-version.c
@@ -0,0 +1,337 @@
+/* Handle symbol and library versioning.
+   Copyright (C) 1997 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <elf.h>
+#include <errno.h>
+#include <link.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../stdio-common/_itoa.h"
+
+
+/* Set in rtld.c at startup.  */
+extern char **_dl_argv;
+
+#define VERSTAG(tag)	(DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
+
+
+#define make_string(string, rest...) \
+  ({									      \
+    const char *all[] = { string, ## rest };				      \
+    size_t len, cnt;							      \
+    char *result, *cp;							      \
+									      \
+    len = 1;								      \
+    for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
+      len += strlen (all[cnt]);						      \
+									      \
+    cp = result = alloca (len);						      \
+    for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)		      \
+      cp = stpcpy (cp, all[cnt]);					      \
+									      \
+    result;								      \
+  })
+
+
+static inline struct link_map *
+find_needed (struct link_map *map, const char *name)
+{
+  unsigned int n;
+
+  for (n = 0; n < map->l_nsearchlist; ++n)
+    if (_dl_does_name_match_p (name, map->l_searchlist[n]))
+      return map->l_searchlist[n];
+
+  /* Should never happen.  */
+  return NULL;
+}
+
+
+static int
+match_symbol (const char *name, ElfW(Word) hash, const char *string,
+	      struct link_map *map, int verbose, int weak)
+{
+  const char *strtab = (const char *) (map->l_addr
+				       + map->l_info[DT_STRTAB]->d_un.d_ptr);
+  ElfW(Addr) def_offset = map->l_info[VERSTAG (DT_VERDEF)]->d_un.d_ptr;
+  ElfW(Verdef) *def;
+
+  if (def_offset == 0)
+    {
+      /* The file has no symbol versioning.  I.e., the dependent
+	 object was linked against another version of this file.  We
+	 only print a message if verbose output is requested.  */
+      if (verbose)
+	_dl_signal_error (0, map->l_name, make_string ("\
+no version information available (required by ",
+						       name, ")"));
+      return 0;
+    }
+
+  def = (ElfW(Verdef) *) (map->l_addr + def_offset);
+  while (1)
+    {
+      /* Currently the version number of the definition entry is 1.
+	 Make sure all we see is this version.  */
+      if (def->vd_version  != 1)
+	{
+	  char buf[20];
+	  buf[sizeof (buf) - 1] = '\0';
+	  _dl_signal_error (0, map->l_name,
+			    make_string ("unsupported version ",
+					 _itoa_word (def->vd_version,
+						     &buf[sizeof (buf) - 1],
+						     10, 0),
+					 " of Verdef record"));
+	  return 1;
+	}
+
+      /* Compare the hash values.  */
+      if (hash == def->vd_hash)
+	{
+	  ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux);
+
+	  /* To be safe, compare the string as well.  */
+	  if (strcmp (string, strtab + aux->vda_name) == 0)
+	    /* Bingo!  */
+	    return 0;
+	}
+
+      /* If no more definitions we failed to find what we want.  */
+      if (def->vd_next == 0)
+	break;
+
+      /* Next definition.  */
+      def = (ElfW(Verdef) *) ((char *) def + def->vd_next);
+    }
+
+  /* Symbol not found.  If it was a weak reference it is not fatal.  */
+  if (weak)
+    {
+      if (verbose)
+	_dl_signal_error (0, map->l_name,
+			  make_string ("weak version `", string,
+				       "' not found (required by ", name,
+				       ")"));
+      return 0;
+    }
+
+  _dl_signal_error (0, map->l_name,
+		    make_string ("version `", string,
+				 "' not found (required by ", name, ")"));
+  return 1;
+}
+
+
+int
+_dl_check_map_versions (struct link_map *map, int verbose)
+{
+  int result = 0;
+  const char *strtab = (const char *) (map->l_addr
+				       + map->l_info[DT_STRTAB]->d_un.d_ptr);
+  /* Pointer to section with needed versions.  */
+  ElfW(Dyn) *dyn = map->l_info[VERSTAG (DT_VERNEED)];
+  /* Pointer to dynamic section with definitions.  */
+  ElfW(Dyn) *def = map->l_info[VERSTAG (DT_VERDEF)];
+  /* We need to find out which is the highest version index used
+    in a dependecy.  */
+  unsigned int ndx_high = 0;
+
+  if (dyn != NULL)
+    {
+      /* This file requires special versions from its dependencies.  */
+      ElfW(Verneed) *ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
+
+      /* Currently the version number of the needed entry is 1.
+	 Make sure all we see is this version.  */
+      if (ent->vn_version  != 1)
+	{
+	  char buf[20];
+	  buf[sizeof (buf) - 1] = '\0';
+	  _dl_signal_error (0, (*map->l_name ? map->l_name : _dl_argv[0]),
+			    make_string ("unsupported version ",
+					 _itoa_word (ent->vn_version,
+						     &buf[sizeof (buf) - 1],
+						     10, 0),
+					 " of Verneed record\n"));
+	  return 1;
+	}
+
+      while (1)
+	{
+	  ElfW(Vernaux) *aux;
+	  struct link_map *needed = find_needed (map, strtab + ent->vn_file);
+
+	  /* If NEEDED is NULL this means a dependency was not found
+	     and no stub entry was created.  This should never happen.  */
+	  assert (needed != NULL);
+
+	  /* NEEDED is the map for the file we need.  Now look for the
+	     dependency symbols.  */
+	  aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
+	  while (1)
+	    {
+	      /* Match the symbol.  */
+	      result |= match_symbol ((*map->l_name
+				       ? map->l_name : _dl_argv[0]),
+				      aux->vna_hash,
+				      strtab + aux->vna_name,
+				      needed, verbose,
+				      aux->vna_flags & VER_FLG_WEAK);
+
+	      /* Compare the version index.  */
+	      if ((aux->vna_other & 0x7fff) > ndx_high)
+		ndx_high = aux->vna_other & 0x7fff;
+
+	      if (aux->vna_next == 0)
+		/* No more symbols.  */
+		break;
+
+	      /* Next symbol.  */
+	      aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
+	    }
+
+	  if (ent->vn_next == 0)
+	    /* No more dependencies.  */
+	    break;
+
+	  /* Next dependency.  */
+	  ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
+	}
+    }
+
+  /* We also must store the names of the defined versions.  Determine
+     the maximum index here as well.
+
+     XXX We could avoid the loop by just taking the number of definitions
+     as an upper bound of new indeces.  */
+  if (def != NULL)
+    {
+      ElfW(Verdef) *ent;
+      ent = (ElfW(Verdef)  *) (map->l_addr + def->d_un.d_ptr);
+      while (1)
+	{
+	  if ((ent->vd_ndx & 0x7fff) > ndx_high)
+	    ndx_high = ent->vd_ndx & 0x7fff;
+
+	  if (ent->vd_next == 0)
+	    /* No more definitions.  */
+	    break;
+
+	  ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
+	}
+    }
+
+  if (ndx_high > 0)
+    {
+      /* Now we are ready to build the array with the version names
+	 which can be indexed by the version index in the VERSYM
+	 section.  */
+      map->l_versions = (hash_name_pair*) malloc ((ndx_high + 1)
+						  * sizeof (hash_name_pair));
+      memset (map->l_versions, '\0', (ndx_high + 1) * sizeof (hash_name_pair));
+      if (map->l_versions == NULL)
+	{
+	  _dl_signal_error (ENOMEM, (*map->l_name ? map->l_name : _dl_argv[0]),
+			    "cannot allocate version name table");
+	  result = 1;
+	}
+      else
+	{
+	  /* Store the number of available symbols.  */
+	  map->l_nversions = ndx_high + 1;
+
+	  if (dyn != NULL)
+	    {
+	      ElfW(Verneed) *ent;
+	      ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
+	      while (1)
+		{
+		  ElfW(Vernaux) *aux;
+		  aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
+		  while (1)
+		    {
+		      ElfW(Half) ndx = aux->vna_other & 0x7fff;
+		      map->l_versions[ndx].hash = aux->vna_hash;
+		      map->l_versions[ndx].name = &strtab[aux->vna_name];
+
+		      if (aux->vna_next == 0)
+			/* No more symbols.  */
+			break;
+
+		      /* Advance to next symbol.  */
+		      aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
+		    }
+
+		  if (ent->vn_next == 0)
+		    /* No more dependencies.  */
+		    break;
+
+		  /* Advance to next dependency.  */
+		  ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
+		}
+	    }
+
+	  /* And insert the defined versions.  */
+	  if (def != NULL)
+	    {
+	      ElfW(Verdef) *ent;
+	      ent = (ElfW(Verdef)  *) (map->l_addr + def->d_un.d_ptr);
+	      while (1)
+		{
+		  ElfW(Verdaux) *aux;
+		  aux = (ElfW(Verdaux) *) ((char *) ent + ent->vd_aux);
+
+		  if ((ent->vd_flags & VER_FLG_BASE) == 0)
+		    {
+		      /* The name of the base version should not be
+			 available for matching a versioned symbol.  */
+		      ElfW(Half) ndx = ent->vd_ndx & 0x7fff;
+		      map->l_versions[ndx].hash = ent->vd_hash;
+		      map->l_versions[ndx].name = &strtab[aux->vda_name];
+		    }
+
+		  if (ent->vd_next == 0)
+		    /* No more definitions.  */
+		    break;
+
+		  ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
+		}
+	    }
+	}
+    }
+
+  return result;
+}
+
+
+int
+_dl_check_all_versions (struct link_map *map, int verbose)
+{
+  struct link_map *l;
+  int result = 0;
+
+  for (l = map; l != NULL; l = l->l_next)
+    result |= _dl_check_map_versions (l, verbose);
+
+  return result;
+}
diff --git a/elf/dlerror.c b/elf/dlerror.c
index 466aa2811e..9e55bc7c76 100644
--- a/elf/dlerror.c
+++ b/elf/dlerror.c
@@ -1,5 +1,5 @@
-/* dlerror -- Return error detail for failing <dlfcn.h> functions.
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+/* Return error detail for failing <dlfcn.h> functions.
+   Copyright (C) 1995, 1996, 1997 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
diff --git a/elf/dlfcn.h b/elf/dlfcn.h
index 973c7e1cc8..94e1fae8a4 100644
--- a/elf/dlfcn.h
+++ b/elf/dlfcn.h
@@ -1,5 +1,5 @@
-/* dlfcn.h -- User functions for run-time dynamic loading.
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+/* User functions for run-time dynamic loading.
+   Copyright (C) 1995, 1996, 1997 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
@@ -52,6 +52,15 @@ extern int dlclose __P ((void *__handle));
    of the symbol called NAME.  */
 extern void *dlsym __P ((void *__handle, __const char *__name));
 
+#ifdef __USE_GNU
+/* Find the run-time address in the shared object HANDLE refers to
+   of the symbol called NAME with VERSION.  */
+extern void *__dlvsym __P ((void *__handle, __const char *__name,
+			    __const char *__version));
+extern void *dlvsym __P ((void *__handle, __const char *__name,
+			  __const char *__version));
+#endif
+
 /* When any of the above functions fails, call this function
    to return a string describing the error.  Each call resets
    the error string so that a following call returns null.  */
diff --git a/elf/dlopen.c b/elf/dlopen.c
index 7ad3429164..c2cf8cd58d 100644
--- a/elf/dlopen.c
+++ b/elf/dlopen.c
@@ -1,5 +1,5 @@
-/* dlopen -- Load a shared object at run time.
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+/* Load a shared object at run time.
+   Copyright (C) 1995, 1996, 1997 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
diff --git a/elf/dlsym.c b/elf/dlsym.c
index 12a29e4b4c..73f6e86475 100644
--- a/elf/dlsym.c
+++ b/elf/dlsym.c
@@ -1,5 +1,5 @@
-/* dlsym -- Look up a symbol in a shared object loaded by `dlopen'.
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+/* Look up a symbol in a shared object loaded by `dlopen'.
+   Copyright (C) 1995, 1996, 1997 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
diff --git a/elf/dlvsym.c b/elf/dlvsym.c
new file mode 100644
index 0000000000..5063b3ba0d
--- /dev/null
+++ b/elf/dlvsym.c
@@ -0,0 +1,72 @@
+/* Look up a symbol in a shared object loaded by `dlopen'.
+   Copyright (C) 1995, 1996, 1997 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include <stddef.h>
+#include <link.h>
+#include <dlfcn.h>
+#include <setjmp.h>
+
+
+void *
+__dlvsym (void *handle, const char *name, const char *version)
+{
+  ElfW(Addr) caller = (ElfW(Addr)) __builtin_return_address (0);
+  ElfW(Addr) loadbase;
+  const ElfW(Sym) *ref = NULL;
+  void doit (void)
+    {
+      if (handle == NULL)
+	/* Search the global scope.  */
+	loadbase = _dl_lookup_versioned_symbol
+	  (name, &ref, &(_dl_global_scope ?: _dl_default_scope)[2], NULL,
+	   version, 0);
+      else if (handle == RTLD_NEXT)
+	{
+	  struct link_map *l, *match;
+
+	  /* Find the highest-addressed object that CALLER is not below.  */
+	  match = NULL;
+	  for (l = _dl_loaded; l; l = l->l_next)
+	    if (caller >= l->l_addr && (!match || match->l_addr < l->l_addr))
+	      match = l;
+
+	  if (! match)
+	    _dl_signal_error (0, NULL, _("\
+RTLD_NEXT used in code not dynamically loaded"));
+
+	  l = match;
+	  while (l->l_loader)
+	    l = l->l_loader;
+
+	  loadbase = _dl_lookup_versioned_symbol_skip
+	    (name, &ref, &_dl_loaded, NULL, version, l, 0);
+	}
+      else
+	{
+	  /* Search the scope of the given object.  */
+	  struct link_map *map = handle;
+	  struct link_map *mapscope[2] = { map, NULL };
+	  loadbase = _dl_lookup_versioned_symbol
+	    (name, &ref, mapscope, map->l_name, version, 0);
+	}
+    }
+
+  return _dlerror_run (doit) ? NULL : (void *) (loadbase + ref->st_value);
+}
+weak_alias (__dlvsym, dlvsym)
diff --git a/elf/do-rel.h b/elf/do-rel.h
index a7b7a60c66..66f135916c 100644
--- a/elf/do-rel.h
+++ b/elf/do-rel.h
@@ -1,5 +1,5 @@
 /* Do relocations for ELF dynamic linking.
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997 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
@@ -21,11 +21,14 @@
    `elf_dynamic_do_rel' and `elf_dynamic_do_rela'.  */
 
 #ifdef DO_RELA
-#define elf_dynamic_do_rel	elf_dynamic_do_rela
-#define	Rel			Rela
-#define elf_machine_rel		elf_machine_rela
+# define elf_dynamic_do_rel	elf_dynamic_do_rela
+# define Rel			Rela
+# define elf_machine_rel	elf_machine_rela
 #endif
 
+#ifndef VERSYMIDX
+# define VERSYMIDX(sym)	(DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (sym))
+#endif
 
 /* Perform the relocations in MAP on the running program image as specified
    by RELTAG, SZTAG.  If LAZY is nonzero, this is the first pass on PLT
@@ -37,8 +40,6 @@ elf_dynamic_do_rel (struct link_map *map,
 		    int reltag, int sztag,
 		    int lazy)
 {
-  const ElfW(Sym) *const symtab
-    = (const ElfW(Sym) *) (map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
   const ElfW(Rel) *r
     = (const ElfW(Rel) *) (map->l_addr + map->l_info[reltag]->d_un.d_ptr);
   const ElfW(Rel) *end = &r[map->l_info[sztag]->d_un.d_val / sizeof *r];
@@ -48,8 +49,27 @@ elf_dynamic_do_rel (struct link_map *map,
     for (; r < end; ++r)
       elf_machine_lazy_rel (map, r);
   else
-    for (; r < end; ++r)
-      elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)]);
+    {
+      const ElfW(Sym) *const symtab =
+	(const ElfW(Sym) *) (map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr);
+
+      if (map->l_info[VERSYMIDX (DT_VERNEEDNUM)])
+	{
+	  const ElfW(Half) *const version =
+	    (const ElfW(Half) *) (map->l_addr
+				  + map->l_info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr);
+
+	  for (; r < end; ++r)
+	    {
+	      ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)];
+	      elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)],
+			       &map->l_versions[ndx]);
+	    }
+	}
+      else
+	for (; r < end; ++r)
+	  elf_machine_rel (map, r, &symtab[ELFW(R_SYM) (r->r_info)], NULL);
+    }
 }
 
 #undef elf_dynamic_do_rel
diff --git a/elf/elf.h b/elf/elf.h
index 05eeb3664f..f6779ba16a 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -504,6 +504,20 @@ typedef struct
 					   entry */
 } Elf32_Verdef;
 
+/* XXX We have no information what types should be used for 64 bit
+   architectures.  What is following is only an intelligent guess.  */
+typedef struct
+{
+  Elf64_Half	vd_version;		/* Version revision */
+  Elf64_Half	vd_flags;		/* Version information */
+  Elf64_Half	vd_ndx;			/* Version Index */
+  Elf64_Half	vd_cnt;			/* Number of associated aux entries */
+  Elf64_Word	vd_hash;		/* Version name hash value */
+  Elf64_Word	vd_aux;			/* Offset in bytes to verdaux array */
+  Elf64_Word	vd_next;		/* Offset in bytes to next verdef
+					   entry */
+} Elf64_Verdef;
+
 /* Legal values for vd_version (version revision).  */
 #define VER_DEF_NONE	0		/* No version */
 #define VER_DEF_CURRENT	1		/* Current version */
@@ -522,6 +536,15 @@ typedef struct
 					   entry */
 } Elf32_Verdaux;
 
+/* XXX We have no information what types should be used for 64 bit
+   architectures.  What is following is only an intelligent guess.  */
+typedef struct
+{
+  Elf64_Addr	vda_name;		/* Version or dependency names */
+  Elf64_Word	vda_next;		/* Offset in bytes to next verdaux
+					   entry */
+} Elf64_Verdaux;
+
 /* Version dependency section.  */
 
 typedef struct
@@ -535,6 +558,19 @@ typedef struct
 					   entry */
 } Elf32_Verneed;
 
+/* XXX We have no information what types should be used for 64 bit
+   architectures.  What is following is only an intelligent guess.  */
+typedef struct
+{
+  Elf64_Half	vn_version;		/* Version of structure */
+  Elf64_Half	vn_cnt;			/* Number of associated aux entries */
+  Elf64_Addr	vn_file;		/* Offset of filename for this
+					   dependency */
+  Elf64_Word	vn_aux;			/* Offset in bytes to vernaux array */
+  Elf64_Word	vn_next;		/* Offset in bytes to next verneed
+					   entry */
+} Elf64_Verneed;
+
 /* Legal values for vn_version (version revision).  */
 #define VER_NEED_NONE	 0		/* No version */
 #define VER_NEED_CURRENT 1		/* Current version */
@@ -552,6 +588,18 @@ typedef struct
 					   entry */
 } Elf32_Vernaux;
 
+/* XXX We have no information what types should be used for 64 bit
+   architectures.  What is following is only an intelligent guess.  */
+typedef struct
+{
+  Elf64_Word	vna_hash;		/* Hash value of dependency name */
+  Elf64_Half	vna_flags;		/* Dependency specific information */
+  Elf64_Half	vna_other;		/* Unused */
+  Elf64_Addr	vna_name;		/* Dependency name string offset */
+  Elf64_Word	vna_next;		/* Offset in bytes to next vernaux
+					   entry */
+} Elf64_Vernaux;
+
 /* Legal values for vna_flags.  */
 #define VER_FLG_WEAK	0x2		/* Weak verison identifier */
 
diff --git a/elf/ldd.bash.in b/elf/ldd.bash.in
index d4ea3310fd..dd4fc6ab96 100644
--- a/elf/ldd.bash.in
+++ b/elf/ldd.bash.in
@@ -29,7 +29,8 @@ TEXTDOMAIN=libc
 TEXTDOMAINDIR=@TEXTDOMAINDIR@
 
 RTLD=@RTLD@
-RELOCS=
+warn=
+bind_now=
 
 while test $# -gt 0; do
   case "$1" in
@@ -49,12 +50,13 @@ Report bugs using the \`glibcbug' script to <bugs@gnu.ai.mit.edu>."
     exit 0 ;;
   -d | --d | --da | --dat | --data | --data- | --data-r | --data-re | \
   --data-rel | --data-relo | --data-reloc | --data-relocs)
-    RELOCS='--data-relocs'
+    warn=yes
     shift ;;
   -r | --f | --fu | --fun | --func | --funct | --functi | --functio | \
   --function | --function- | --function-r | --function-re | --function-rel | \
   --function-relo | --function-reloc | --function-relocs)
-    RELOCS='--function-relocs'
+    warn=yes
+    bind_now=yes
     shift ;;
   --)		# Stop option processing.
     shift; break ;;
@@ -85,7 +87,8 @@ case $# in
     test -x "$file" ||
     echo $"ldd: warning: you do not have execution permission for" "\`$file'"
       if ${RTLD} --verify "$file"; then
-	LD_TRACE_LOADED_OBJECTS=1 exec ${RTLD} ${RELOCS} "$file" || exit 1
+	LD_TRACE_LOADED_OBJECTS=1 LD_WARN=$warn LD_BIND_NOW=$bind_now \
+	 exec ${RTLD} ${RELOCS} "$file" || exit 1
       else
 	echo $"	not a dynamic executable"
 	exit 1
@@ -111,7 +114,8 @@ case $# in
       test -x "$file" || echo $"\
 ldd: warning: you do not have execution permission for" "\`$file'"
       if ${RTLD} --verify "$file"; then
-	LD_TRACE_LOADED_OBJECTS=1 ${RTLD} ${RELOCS} "$file" || result=1
+	LD_TRACE_LOADED_OBJECTS=1 LD_WARN=$warn LD_BIND_NOW=$bind_now \
+	 ${RTLD} ${RELOCS} "$file" || result=1
       else
 	echo $"	not a dynamic executable"
 	result=1
diff --git a/elf/ldd.sh.in b/elf/ldd.sh.in
index f7c1163890..0f3ed2e729 100644
--- a/elf/ldd.sh.in
+++ b/elf/ldd.sh.in
@@ -25,7 +25,8 @@
 # variable LD_TRACE_LOADED_OBJECTS to a non-empty value.
 
 RTLD=@RTLD@
-RELOCS=
+warn=
+bind_now=
 
 while test $# -gt 0; do
   case "$1" in
@@ -45,12 +46,13 @@ Report bugs using the \`glibcbug' script to <bugs@gnu.ai.mit.edu>."
     exit 0 ;;
   -d | --d | --da | --dat | --data | --data- | --data-r | --data-re | \
   --data-rel | --data-relo | --data-reloc | --data-relocs)
-    RELOCS='--data-relocs'
+    warn=yes
     shift ;;
   -r | --f | --fu | --fun | --func | --funct | --functi | --functio | \
   --function | --function- | --function-r | --function-re | --function-rel | \
   --function-relo | --function-reloc | --function-relocs)
-    RELOCS='--function-relocs'
+    warn=yes
+    bind_now=yes
     shift ;;
   --)		# Stop option processing.
     shift; break ;;
@@ -84,7 +86,8 @@ Try \`ldd --help' for more information."
       test -x "$file" ||
 	echo "ldd: warning: you do not have execution permission for \`$file'"
       if ${RTLD} --verify "$file"; then
-	LD_TRACE_LOADED_OBJECTS=1 exec ${RTLD} ${RELOCS} "$file" || exit 1
+	LD_TRACE_LOADED_OBJECTS=1 LD_WARN=$warn LD_BIND_NOW=$bind_now \
+	 exec ${RTLD} "$file" || exit 1
       else
 	echo '	not a dynamic executable'
 	exit 1
@@ -112,7 +115,8 @@ Try \`ldd --help' for more information."
 	test -x "$file" || echo "\
 ldd: warning: you do not have execution permission for \`$file'"
 	if ${RTLD} --verify "$file"; then
-	  LD_TRACE_LOADED_OBJECTS=1 ${RTLD} ${RELOCS} "$file" || result=1
+	  LD_TRACE_LOADED_OBJECTS=1 LD_WARN=$warn LD_BIND_NOW=$bind_now \
+	   ${RTLD} "$file" || result=1
 	else
 	  echo '	not a dynamic executable'
 	  result=1
diff --git a/elf/link.h b/elf/link.h
index b05ecca199..58041332e9 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -74,6 +74,13 @@ extern struct r_debug _r_debug;
 
 extern ElfW(Dyn) _DYNAMIC[];
 
+/* For the version handling we need an array with only names and their
+   hash values.  */
+typedef struct
+{
+  const char *name;
+  ElfW(Word) hash;
+} hash_name_pair;
 
 /* Structure describing a loaded shared object.  The `l_next' and `l_prev'
    members form a chain of all the shared objects loaded at startup.
@@ -94,7 +101,11 @@ struct link_map
     /* All following members are internal to the dynamic linker.
        They may change without notice.  */
 
-    const char *l_libname;	/* Name requested (before search).  */
+    struct libname_list
+      {
+	const char *name;	/* Name requested (before search).  */
+	struct libname_list *next; /* Link to next name for this object.  */
+      } *l_libname;
     /* Indexed pointers to dynamic section.
        [0,DT_NUM) are indexed by the processor-independent tags.
        [DT_NUM,DT_NUM+DT_PROCNUM) are indexed by the tag minus DT_LOPROC.
@@ -139,13 +150,34 @@ struct link_map
     unsigned int l_init_running:1; /* Nonzero while DT_INIT function runs.  */
     unsigned int l_global:1;	/* Nonzero if object in _dl_global_scope.  */
     unsigned int l_reserved:2;	/* Reserved for internal use.  */
+
+    /* Array with version names.  */
+    unsigned int l_nversions;
+    hash_name_pair *l_versions;
   };
 
 
+/* Test whether given NAME matches any of the names of the given object.  */
+static inline int
+__attribute__ ((unused))
+_dl_does_name_match_p (const char *__name, struct link_map *__map)
+{
+  int __found = strcmp (__name, __map->l_name) == 0;
+  struct libname_list *__runp = __map->l_libname;
+
+  while (! __found && __runp != NULL)
+    if (strcmp (__name, __runp->name) == 0)
+      __found = 1;
+    else
+      __runp = __runp->next;
+
+  return __found;
+}
+
 /* Function used as argument for `_dl_receive_error' function.  The
-   arguments are the error string and the objname the error occurred
-   in.  */
-typedef void (*receiver_fct) (const char *, const char *);
+   arguments are the error code, error string, and the objname the
+   error occurred in.  */
+typedef void (*receiver_fct) (int, const char *, const char *);
 
 /* Internal functions of the run-time dynamic linker.
    These can be accessed if you link again the dynamic linker
@@ -264,6 +296,14 @@ extern ElfW(Addr) _dl_lookup_symbol (const char *undef,
 				     const char *reference_name,
 				     int flags);
 
+/* Lookup versioned symbol.  */
+extern ElfW(Addr) _dl_lookup_versioned_symbol (const char *undef,
+					       const ElfW(Sym) **sym,
+					       struct link_map *symbol_scope[],
+					       const char *reference_name,
+					       const hash_name_pair *version,
+					       int flags);
+
 /* For handling RTLD_NEXT we must be able to skip shared objects.  */
 extern ElfW(Addr) _dl_lookup_symbol_skip (const char *undef,
 					  const ElfW(Sym) **sym,
@@ -272,6 +312,16 @@ extern ElfW(Addr) _dl_lookup_symbol_skip (const char *undef,
 					  struct link_map *skip_this,
 					  int flags);
 
+/* For handling RTLD_NEXT with versioned symbols we must be able to
+   skip shared objects.  */
+extern ElfW(Addr) _dl_lookup_versioned_symbol_skip (const char *undef,
+						    const ElfW(Sym) **sym,
+						    struct link_map *symbol_scope[],
+						    const char *reference_name,
+						    const char *version_name,
+						    struct link_map *skip_this,
+						    int flags);
+
 /* Look up symbol NAME in MAP's scope and return its run-time address.  */
 extern ElfW(Addr) _dl_symbol_value (struct link_map *map, const char *name);
 
@@ -319,6 +369,14 @@ extern void _dl_relocate_object (struct link_map *map,
 				 struct link_map *scope[],
 				 int lazy);
 
+/* Check the version dependencies of all objects available through
+   MAP.  If VERBOSE print some more diagnostics.  */
+extern int _dl_check_all_versions (struct link_map *map, int verbose);
+
+/* Check the version dependencies for MAP.  If VERBOSE print some more
+   diagnostics.  */
+extern int _dl_check_map_versions (struct link_map *map, int verbose);
+
 /* Return the address of the next initializer function for MAP or one of
    its dependencies that has not yet been run.  When there are no more
    initializers to be run, this returns zero.  The functions are returned
diff --git a/elf/rtld.c b/elf/rtld.c
index f9a2cd3d03..26cacef894 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -45,7 +45,12 @@ extern void *_dl_sysdep_read_whole_file (const char *filename,
 					 int mmap_prot);
 
 /* Helper function to handle errors while resolving symbols.  */
-static void print_unresolved (const char *errstring, const char *objname);
+static void print_unresolved (int errcode, const char *objname,
+			      const char *errsting);
+
+/* Helper function to handle errors when a version is missing.  */
+static void print_missing_version (int errcode, const char *objname,
+				   const char *errsting);
 
 
 int _dl_argc;
@@ -66,6 +71,7 @@ static void dl_main (const ElfW(Phdr) *phdr,
 		     ElfW(Addr) *user_entry);
 
 struct link_map _dl_rtld_map;
+struct libname_list _dl_rtld_libname;
 
 #ifdef RTLD_START
 RTLD_START
@@ -81,7 +87,7 @@ _dl_start (void *arg)
   /* This #define produces dynamic linking inline functions for
      bootstrap relocation instead of general-purpose relocation.  */
 #define RTLD_BOOTSTRAP
-#define RESOLVE(sym, flags) bootstrap_map.l_addr
+#define RESOLVE(sym, version, flags) bootstrap_map.l_addr
 #include "dynamic-link.h"
 
   /* Figure out the run-time load address of the dynamic linker itself.  */
@@ -155,7 +161,8 @@ dl_main (const ElfW(Phdr) *phdr,
   /* LAZY is determined by the parameters --datadeps and --function-deps
      if we trace the binary.  */
   if (mode == trace)
-    lazy = -1;
+    lazy = (*(getenv ("LD_WARN") ?: "") == '\0' ? -1
+	    : (*(getenv ("LD_BIND_NOW") ?: "") == '\0' ? 1 : 0));
   else
     lazy = !__libc_enable_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0';
 
@@ -216,24 +223,6 @@ of this helper program; chances are you did not intend to run this program.\n",
 	    --_dl_argc;
 	    ++_dl_argv;
 	  }
-	else if (! strcmp (_dl_argv[1], "--data-relocs"))
-	  {
-	    mode = trace;
-	    lazy = 1;	/* This means do only data relocation analysis.  */
-
-	    ++_dl_skip_args;
-	    --_dl_argc;
-	    ++_dl_argv;
-	  }
-	else if (! strcmp (_dl_argv[1], "--function-relocs"))
-	  {
-	    mode = trace;
-	    lazy = 0;	/* This means do also function relocation analysis.  */
-
-	    ++_dl_skip_args;
-	    --_dl_argc;
-	    ++_dl_argv;
-	  }
 	else
 	  break;
 
@@ -307,12 +296,19 @@ of this helper program; chances are you did not intend to run this program.\n",
 	   dlopen call or DT_NEEDED entry, for something that wants to link
 	   against the dynamic linker as a shared library, will know that
 	   the shared object is already loaded.  */
-	_dl_rtld_map.l_libname = (const char *) l->l_addr + ph->p_vaddr;
+	_dl_rtld_libname.name = (const char *) l->l_addr + ph->p_vaddr;
+	_dl_rtld_libname.next = NULL;
+	_dl_rtld_map.l_libname = &_dl_rtld_libname;
 	break;
       }
   if (! _dl_rtld_map.l_libname && _dl_rtld_map.l_name)
-    /* We were invoked directly, so the program might not have a PT_INTERP.  */
-    _dl_rtld_map.l_libname = _dl_rtld_map.l_name;
+    {
+      /* We were invoked directly, so the program might not have a
+	 PT_INTERP.  */
+      _dl_rtld_libname.name = _dl_rtld_map.l_name;
+      _dl_rtld_libname.next = NULL;
+      _dl_rtld_map.l_libname =  &_dl_rtld_libname;
+    }
   else
     assert (_dl_rtld_map.l_libname); /* How else did we get here?  */
 
@@ -332,7 +328,7 @@ of this helper program; chances are you did not intend to run this program.\n",
   if (! _dl_rtld_map.l_name)
     /* If not invoked directly, the dynamic linker shared object file was
        found by the PT_INTERP name.  */
-    _dl_rtld_map.l_name = (char *) _dl_rtld_map.l_libname;
+    _dl_rtld_map.l_name = (char *) _dl_rtld_map.l_libname->name;
   _dl_rtld_map.l_type = lt_library;
   while (l->l_next)
     l = l->l_next;
@@ -481,6 +477,19 @@ of this helper program; chances are you did not intend to run this program.\n",
 	}
     }
 
+  /* Now let us see whether all libraries are available in the
+     versions we need.  */
+  {
+    void doit (void)
+      {
+	if (_dl_check_all_versions (l, 1) && mode == normal)
+	  /* We cannot start the application.  Abort now.  */
+	  _exit (1);
+      }
+
+    _dl_receive_error (print_missing_version, doit);
+  }
+
   if (mode != normal)
     {
       /* We were run just to list the shared libraries.  It is
@@ -496,7 +505,8 @@ of this helper program; chances are you did not intend to run this program.\n",
 	for (l = _dl_loaded->l_next; l; l = l->l_next)
 	  if (l->l_opencount == 0)
 	    /* The library was not found.  */
-	    _dl_sysdep_message ("\t", l->l_libname, " => not found\n", NULL);
+	    _dl_sysdep_message ("\t", l->l_libname->name, " => not found\n",
+				NULL);
 	  else
 	    {
 	      char buf[20], *bp;
@@ -505,7 +515,7 @@ of this helper program; chances are you did not intend to run this program.\n",
 	      while ((size_t) (&buf[sizeof buf - 1] - bp)
 		     < sizeof l->l_addr * 2)
 		*--bp = '0';
-	      _dl_sysdep_message ("\t", l->l_libname, " => ", l->l_name,
+	      _dl_sysdep_message ("\t", l->l_libname->name, " => ", l->l_name,
 				  " (0x", bp, ")\n", NULL);
 	    }
 
@@ -629,7 +639,18 @@ of this helper program; chances are you did not intend to run this program.\n",
 /* This is a little helper function for resolving symbols while
    tracing the binary.  */
 static void
-print_unresolved (const char *errstring, const char *objname)
+print_unresolved (int errcode __attribute__ ((unused)), const char *objname,
+		  const char *errstring)
 {
   _dl_sysdep_error (errstring, "	(", objname, ")\n", NULL);
 }
+
+/* This is a little helper function for resolving symbols while
+   tracing the binary.  */
+static void
+print_missing_version (int errcode __attribute__ ((unused)),
+		       const char *objname, const char *errstring)
+{
+  _dl_sysdep_error (_dl_argv[0] ?: "<program name unknown>", ": ",
+		    objname, ": ", errstring, "\n", NULL);
+}