summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>1997-01-21 06:10:42 +0000
committerUlrich Drepper <drepper@redhat.com>1997-01-21 06:10:42 +0000
commitfd26970f3324277683be531ad2c31f42e19e4b48 (patch)
tree5d9802aa7e77cc5c0f4b87ac661fe264a93bbe2e /elf
parentf9c53d1159ff05ac533d3351c70df1ea32c2119d (diff)
downloadglibc-fd26970f3324277683be531ad2c31f42e19e4b48.tar.gz
glibc-fd26970f3324277683be531ad2c31f42e19e4b48.tar.xz
glibc-fd26970f3324277683be531ad2c31f42e19e4b48.zip
update from main archive 970120 cvs/libc-970121
Tue Jan 21 04:05:20 1997  Ulrich Drepper  <drepper@cygnus.com>

	* version.h (VERSION): Bump to 1.101.

	Implement -d and -r option to `ldd' to check relocations.
	* elf/dl-error.c: Add another method to intercept errors.
	(_dl_receive_error): New function.  Install user defined handler.
	(receiver): New variable.  Contains pointer to user provided handler.
	(_dl_signal_error): If user provided handler is installed call this.
	* elf/dl-load.c (_dl_map_object): When shared object is not found in
	trace mode initialize a few more fields so that lookup can actually
	happen but always fails.
	* elf/ldd.sh.in: Rewrite argument handling.  Recognize new arguments
	to trigger reloation test.  Return with appropriate error code if
	a file wasn't found.  Print warning if object is not executable.
	* elf/ldd.bash.in: Likewise.
	* elf/link.h (receiver_fct): New type.  Used in _dl_receive_error.
	(_dl_sysdep_error): New prototype.
	(_dl_receive_error): New prototype.
	(_dl_signal_error): Remove  __attribute__ ((__noreturn__)).
	* elf/rtld.c (dl_main): Rewrite argument handling.  More than
	one argument allowed.  Recognize --data-relocs and --function-relocs
	arguments.
	Don't determine `lazy' mode from LD_BIND_NOW environment variable
	when in trace mode.
	If in trace mode and either --data-relocs or --function-relocs is
	given perform relocation.  Report errors using print_unresolved
	function.
	(print_unresolved): New function.  Print information about missing
	symbol on stderr.
	* sysdeps/generic/dl-sysdep.c (_dl_sysdep_error): New function.
	Like _dl_sysdep_message but print to stderr.
	* sysdeps/mach/hurd/dl-sysdep.c: Likewise.

	* sysdeps/generic/sockaddrcom.h: Add definition of sa_family_t.
	Reported by Andreas Schwab.
	(__SOCKADDR_COMMON): Use sa_family_t for family member.
	* sysdeps/unix/bsd/bsd4.4/sockaddrcom.h: Likewise.

	Linux/Sparc support by Miguel de Icaza.
	* sysdeps/sparc/fpu_control.h: New file.
	* sysdeps/unix/sysv/linux/sparc/__sigtrampoline.S: New file.
	* sysdeps/unix/sysv/linux/sparc/brk.c: New file.
	* sysdeps/unix/sysv/linux/sparc/profil-counter.h: New file.
	* sysdeps/unix/sysv/linux/sparc/sigaction.c: New file.
	* sysdeps/unix/sysv/linux/sparc/socket.S: New file.
	* sysdeps/unix/sysv/linux/sparc/syscall.S: New file.
	* sysdeps/unix/sysv/linux/sparc/sysdep.h: New file.
	* sysdeps/unix/sysv/linux/sparc/Dist: New file.
	* sysdeps/unix/sysv/linux/sparc/Makefile: New file.

	* sysdeps/unix/sysv/linux/net/if_arp.h: Don't use kernel header.
	Provide own definition based on 4.4BSD and Linux.
	* sysdeps/unix/sysv/linux/net/ppp_defs.h: Define __u32 before
	including <linux/ppp_defs.h>.
	* sysdeps/unix/sysv/linux/sys/msq_buf.h (struct msqid_ds): Don't
	use __pid_t since the kernel might have a different size.
	* sysdeps/unix/sysv/linux/sys/shm_buf.h (struct shmid_ds): Likewise.
	Reported by Andreas Schwab.

	* time/asctime.c: Update copyright.
	* time/dysize.c: Likewise.
	* time/gmtime.c: Likewise.
	* time/timegm.c: Likewise.
	* time/offtime.c: Likewise.  De-ANSI-declfy.

	* time/tzset.c (__tzset_internal): When TZ envvar does not name a
	DST timezone don't default to offset -1.

	* sysdeps/unix/sysv/linux/net/route.h: Don't use kernel header.
	Reported by a sun <asun@zoology.washington.edu>.

	* resolv/Makefile: Correct spelling: subdirs-dirs -> subdir-dirs.

	* sysdeps/stub/sysv_signal.c: New file.  Stub implementation.

	* Makefile (distribute): Add mcheck.h.

	* nis/Makefile (distribute): Add nss-nis.h.

	* libio/Makefile (routines): Change vdprintf to iovdprintf to prevent
	dist problem.

	* nss/Makefile (distribute): Add digits_dots.c.

	* sysdeps/unix/sysv/linux/Dist: Add kernel_sigaction.h.
	* sysdeps/unix/sysv/linux/alpha/Dist: Add sys/procfs.h.
	* sysdeps/unix/sysv/linux/sparc/Dist: Add clone.S.
	* new-malloc/Makefile (distribute): Add mcheck-init.c and mcheck.h.

Mon Jan 20 17:54:28 1997  Sven Verdoolaege  <skimo@breughel.ufsia.ac.be>

	* manual/filesys.texi: Fix little problem (reentrant->readdir).

Fri Jan 17 19:07:07 1997  Andreas Schwab  <schwab@issan.informatik.uni-dortmund.de>

	* configure.in [$elf=yes]: Check for support of .previous and
	.popsection in the assembler.
	* config.h.in: Add HAVE_ASM_PREVIOUS_DIRECTIVE and
	HAVE_ASM_POPSECTION_DIRECTIVE.
	* libc-symbols.h (__make_section_unallocated) [HAVE_ELF]: Define
	appropriate if either .previous or .popsection is supported.
	(libc_warning) [HAVE_ELF]: Use it here.

Sat Jan 18 22:15:26 1997  Richard Henderson  <rth@tamu.edu>

	* Makeconfig (CFLAGS-.so): Add -fno-common to prevent odd sorts of
	errors that can occur when linking libc.so.

Mon Jan 20 05:20:49 1997  Ulrich Drepper  <drepper@cygnus.com>

	* elf/dl-load.c (open_path): When running setuid don't try
	a directory if it is not given with the full name.

	* elf/Makefile (before-compile): New variable.  Mention trusted-dirs.h.
	(trusted-dirs.h): Construct file from $(default-rpath) and
	$(user-defined-trusted-dirs) variables.
	* elf/dl-load.c (_dl_map_object): Pass additional argument to open_path
	which is NULL except for the LD_LIBRARY_PATH pass in which case it
	is a pointer to the list of directories from the trusted-dirs.h
	file.
	(open_path): Accept additional argument with list of trusted dirs.
	When running setuid and a list of trusted dirs is given only use
	those which are mentioned in the list.

	* elf/rtld.c (dl_main): Don't reject whole LD_LIBRARY_PATH when
	running setuid.  Instead accept entries which do not contain a '/'.

	* Makeconfig: Correct comment about +(default_cflags).

Mon Jan 20 05:11:14 1997  Hrvoje Niksic  <hniksic@srce.hr>

	* time/strptime.c (recursive): Use && not || to test for valid
	argument.

Mon Jan 20 05:06:50 1997  Ulrich Drepper  <drepper@cygnus.com>

	* elf/ldd.sh.in: Exit with value 1 if an error occured.
	* elf/ldd.bash.in: Likewise.

	* elf/rtld.c (dl_main): Do not always ignore LD_PRELOAD when the
	binary runs setuid.  It is save to use those entries which do not
	contain a '/'.  This is compatible with Solaris-2.
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile13
-rw-r--r--elf/dl-error.c33
-rw-r--r--elf/dl-load.c70
-rw-r--r--elf/ldd.bash.in58
-rw-r--r--elf/ldd.sh.in54
-rw-r--r--elf/link.h20
-rw-r--r--elf/rtld.c124
7 files changed, 297 insertions, 75 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 8f98cb8406..87f2d0d67d 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -41,6 +41,8 @@ extra-libs	 = libdl
 extra-libs-others = $(extra-libs)
 libdl-routines	:= dlopen dlclose dlsym dlerror dladdr
 
+before-compile = $(objpfx)trusted-dirs.h
+
 
 all: # Make this the default target; it will be defined in Rules.
 
@@ -92,6 +94,17 @@ $(objpfx)$(rtld-installed-name): $(objpfx)ld.so
 	ln -s $(<F) $@
 endif
 
+# Build a file mentioning all trustworthy directories to look for shared
+# libraries when using LD_LIBRARY_PATH in a setuid program.  The user can
+# add directories to the list by defining $(user-defined-trusted-dirs)
+# before starting make.
+$(objpfx)trusted-dirs.h: Makefile
+	(for dir in `echo "$(default-rpath) $(user-defined-trusted-dirs)" |   \
+		     sed 's/:/ /g'`; do					      \
+	   echo "  \"$$dir\",";						      \
+	 done;) > $@T
+	mv -f $@T $@
+CFLAGS-dl-load.c = -I$(objdir)/$(subdir)
 
 # Specify the dependencies of libdl.so; its commands come from the generic
 # rule to build a shared library.
diff --git a/elf/dl-error.c b/elf/dl-error.c
index 52eb577eb0..a19ccff626 100644
--- a/elf/dl-error.c
+++ b/elf/dl-error.c
@@ -37,6 +37,11 @@ struct catch
    this is null.  */
 static struct catch *catch;
 
+/* This points to a function which is called when an error is
+   received.  Unlike the handling of `catch' this function may return.
+   The arguments will be the `errstring' and `objname'.  */
+static receiver_fct receiver;
+
 
 void
 _dl_signal_error (int errcode,
@@ -58,6 +63,13 @@ _dl_signal_error (int errcode,
       catch->objname = objname;
       longjmp (catch->env, errcode ?: -1);
     }
+  else if (receiver)
+    {
+      /* We are inside _dl_receive_error.  Call the user supplied
+	 handler and resume the work.  The receiver will still
+	 installed.  */
+      (*receiver) (errstring, objname);
+    }
   else
     {
       /* Lossage while resolving the program's own symbols is always fatal.  */
@@ -77,6 +89,8 @@ _dl_catch_error (char **errstring,
 {
   int errcode;
   struct catch *old, c = { errstring: NULL, objname: NULL };
+  /* We need not handle `receiver' since setting a `catch' is handle
+     before it.  */
 
   old = catch;
   errcode = setjmp (c.env);
@@ -96,3 +110,22 @@ _dl_catch_error (char **errstring,
   *objname = c.objname;
   return errcode == -1 ? 0 : errcode;
 }
+
+void
+_dl_receive_error (receiver_fct fct, void (*operate) (void))
+{
+  struct catch *old_catch;
+  receiver_fct old_receiver;
+
+  old_catch = catch;
+  old_receiver = receiver;
+
+  /* Set the new values.  */
+  catch = NULL;
+  receiver = fct;
+
+  (*operate) ();
+
+  catch = old_catch;
+  receiver = old_receiver;
+}
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 7dc6d91a02..6a3d919976 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1,5 +1,5 @@
 /* _dl_map_object -- Map in a shared object's segments from the file.
-   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
@@ -409,7 +409,8 @@ _dl_map_object_from_fd (char *name, int fd, char *realname,
 static int
 open_path (const char *name, size_t namelen,
 	   const char *dirpath,
-	   char **realname)
+	   char **realname,
+	   const char *trusted_dirs[])
 {
   char *buf;
   const char *p;
@@ -426,13 +427,42 @@ open_path (const char *name, size_t namelen,
   do
     {
       size_t buflen;
+      size_t this_len;
 
       dirpath = p;
       p = strpbrk (dirpath, ":;");
       if (p == NULL)
 	p = strchr (dirpath, '\0');
 
-      if (p == dirpath)
+      this_len = p - dirpath;
+
+      /* When we run a setuid program we do not accept any directory.  */
+      if (__libc_enable_secure)
+	{
+	  /* All trusted directory must be complete name.  */
+	  if (dirpath[0] != '/')
+	    continue;
+
+	  /* If we got a list of trusted directories only accept one
+	     of these.  */
+	  if (trusted_dirs != NULL)
+	    {
+	      const char **trust = trusted_dirs;
+
+	      while (*trust !=  NULL)
+		if (memcmp (dirpath, *trust, this_len) == 0
+		    && (*trust)[this_len] == '\0')
+		  break;
+		else
+		  ++trust;
+
+	      /* If directory is not trusted, ignore this directory.  */
+	      if (*trust == NULL)
+		continue;
+	    }
+	}
+
+      if (this_len == 0)
 	{
 	  /* Two adjacent colons, or a colon at the beginning or the end of
 	     the path means to search the current directory.  */
@@ -442,10 +472,10 @@ open_path (const char *name, size_t namelen,
       else
 	{
 	  /* Construct the pathname to try.  */
-	  (void) memcpy (buf, dirpath, p - dirpath);
-	  buf[p - dirpath] = '/';
-	  (void) memcpy (&buf[(p - dirpath) + 1], name, namelen);
-	  buflen = p - dirpath + 1 + namelen;
+	  (void) memcpy (buf, dirpath, this_len);
+	  buf[this_len] = '/';
+	  (void) memcpy (&buf[this_len + 1], name, namelen);
+	  buflen = this_len + 1 + namelen;
 	}
 
       fd = __open (buf, O_RDONLY);
@@ -508,9 +538,9 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
 
       size_t namelen = strlen (name) + 1;
 
-      inline void trypath (const char *dirpath)
+      inline void trypath (const char *dirpath, const char *trusted[])
 	{
-	  fd = open_path (name, namelen, dirpath, &realname);
+	  fd = open_path (name, namelen, dirpath, &realname, trusted);
 	}
 
       fd = -1;
@@ -521,16 +551,24 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
 	if (l && l->l_info[DT_RPATH])
 	  trypath ((const char *) (l->l_addr +
 				   l->l_info[DT_STRTAB]->d_un.d_ptr +
-				   l->l_info[DT_RPATH]->d_un.d_val));
+				   l->l_info[DT_RPATH]->d_un.d_val), NULL);
       /* If dynamically linked, try the DT_RPATH of the executable itself.  */
       l = _dl_loaded;
       if (fd == -1 && l && l->l_type != lt_loaded && l->l_info[DT_RPATH])
 	trypath ((const char *) (l->l_addr +
 				 l->l_info[DT_STRTAB]->d_un.d_ptr +
-				 l->l_info[DT_RPATH]->d_un.d_val));
+				 l->l_info[DT_RPATH]->d_un.d_val), NULL);
       /* Try an environment variable (unless setuid).  */
       if (fd == -1 && ! __libc_enable_secure)
-	trypath (getenv ("LD_LIBRARY_PATH"));
+	{
+	  static const char *trusted_dirs[] =
+	  {
+#include "trusted-dirs.h"
+	    NULL
+	  };
+
+	  trypath (getenv ("LD_LIBRARY_PATH"), trusted_dirs);
+	}
       if (fd == -1)
 	{
 	  /* Check the list of libraries in the file /etc/ld.so.cache,
@@ -555,7 +593,7 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
       if (fd == -1)
 	{
 	  extern const char *_dl_rpath;	/* Set in rtld.c. */
-	  trypath (_dl_rpath);
+	  trypath (_dl_rpath, NULL);
 	}
     }
   else
@@ -590,6 +628,7 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
 	     are only interested in the list of libraries this isn't
 	     so severe.  Fake an entry with all the information we
 	     have (in fact only the name).  */
+	  static const ElfW(Symndx) dummy_bucket = STN_UNDEF;
 
 	  /* Enter the new object in the list of loaded objects.  */
 	  if ((name_copy = local_strdup (name)) == NULL
@@ -599,6 +638,11 @@ _dl_map_object (struct link_map *loader, const char *name, int type,
 	  /* We use an opencount of 0 as a sign for the faked entry.  */
 	  l->l_opencount = 0;
 	  l->l_reserved = 0;
+	  l->l_buckets = &dummy_bucket;
+	  l->l_nbuckets = 1;
+	  l->l_relocated = 1;
+
+	  return l;
 	}
       else
 	_dl_signal_error (errno, name, "cannot open shared object file");
diff --git a/elf/ldd.bash.in b/elf/ldd.bash.in
index 6e2b51591a..d174709380 100644
--- a/elf/ldd.bash.in
+++ b/elf/ldd.bash.in
@@ -29,23 +29,39 @@ TEXTDOMAIN=libc
 TEXTDOMAINDIR=@TEXTDOMAINDIR@
 
 RTLD=@RTLD@
+RELOCS=
 
 while test $# -gt 0; do
   case "$1" in
-  --v*)
+  --v | --ve | --ver | --vers | --versi | --versio | --version)
     echo $"ldd (GNU libc) @VERSION@
 Copyright (C) 1996, 1997 Free Software Foundation, Inc.
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
     exit 0 ;;
-  --h*)
+  --h | --he | --hel | --help)
     echo $"ldd [OPTION]... FILE...
-  --help           print this help and exit
-  --version        print version information and exit
+      --help              print this help and exit
+      --version           print version information and exit
+  -d, --data-relocs       process data relocations
+  -r, --function-relocs   process data and function relocations
 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'
+    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'
+    shift ;;
   --)		# Stop option processing.
     shift; break ;;
+  -*)
+    echo >&2 $"ldd: unrecognized option" "\`$1'"
+    echo >&2 $"Try \`ldd --help' for more information."
+    exit 1 ;;
   *)
     break ;;
   esac
@@ -53,9 +69,8 @@ done
 
 case $# in
 0)
-  echo >&2 $"\
-ldd: missing file arguments
-Try \`ldd --help' for more information."
+  echo >&2 $"ldd: missing file arguments"
+  echo >&2 $"Try \`ldd --help' for more information."
   exit 1 ;;
 1)
   # We don't list the file name when there is only one.
@@ -65,14 +80,21 @@ Try \`ldd --help' for more information."
   esac
   if test ! -f "$file"; then
     echo "${file}:" $"no such file"
-  elif ${RTLD} --verify "$file"; then
-    LD_TRACE_LOADED_OBJECTS=1 exec ${RTLD} "$file" && exit 1
+    exit 1
   else
-    echo $"	not a dynamic executable"
+    test -x "$file" ||
+      echo $"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
+    else
+      echo $"	not a dynamic executable"
+      exit 1
+    fi
   fi
   exit ;;
 *)
   set -e	# Bail out immediately if ${RTLD} loses on any argument.
+  result=0
   for file; do
     echo "${file}:"
     case "$file" in
@@ -80,16 +102,22 @@ Try \`ldd --help' for more information."
     *) file="./$file" ;;
     esac
     if test ! -f "$file"; then
-      echo "$file:" $"no such file"
-    elif ${RTLD} --verify "$file"; then
-      LD_TRACE_LOADED_OBJECTS=1 ${RTLD} "$file"
+      echo "${file}:" $"no such file"
+      result=1
     else
-      echo $"	not a dynamic executable"
+      test -x "$file" ||
+	echo $"warning: you do not have execution permission for" "\`$file'"
+      if ${RTLD} --verify "$file"; then
+	LD_TRACE_LOADED_OBJECTS=1 ${RTLD} ${RELOCS} "$file" || result=1
+      else
+	echo $"	not a dynamic executable"
+	result=1
+      fi
     fi
   done
 esac
 
-exit 0
+exit $result
 # Local Variables:
 #  mode:ksh
 # End:
diff --git a/elf/ldd.sh.in b/elf/ldd.sh.in
index 5b6cc3a75d..4351578b6d 100644
--- a/elf/ldd.sh.in
+++ b/elf/ldd.sh.in
@@ -25,23 +25,40 @@
 # variable LD_TRACE_LOADED_OBJECTS to a non-empty value.
 
 RTLD=@RTLD@
+RELOCS=
 
 while test $# -gt 0; do
   case "$1" in
-  --v*)
+  --v | --ve | --ver | --vers | --versi | --versio | --version)
     echo 'ldd (GNU libc) @VERSION@
 Copyright (C) 1996, 1997 Free Software Foundation, Inc.
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.'
     exit 0 ;;
-  --h*)
+  --h | --he | --hel | --help)
     echo "ldd [OPTION]... FILE...
-  --help           print this help and exit
-  --version        print version information and exit
+      --help              print this help and exit
+      --version           print version information and exit
+  -d, --data-relocs       process data relocations
+  -r, --function-relocs   process data and function relocations
 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'
+    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'
+    shift ;;
   --)		# Stop option processing.
     shift; break ;;
+  -*)
+    echo >&2 "\
+ldd: unrecognized option \`$1'
+Try \`ldd --help' for more information."
+    exit 1 ;;
   *)
     break ;;
   esac
@@ -61,14 +78,21 @@ Try \`ldd --help' for more information."
   esac
   if test ! -f "$file"; then
     echo "${file}: no such file"
-  elif ${RTLD} --verify "$file"; then
-    LD_TRACE_LOADED_OBJECTS=1 exec ${RTLD} "$file" && exit 1
+    exit 1
   else
-    echo '	not a dynamic executable'
+    test -x "$file" ||
+      echo "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
+    else
+      echo '	not a dynamic executable'
+      exit 1
+    fi
   fi
   exit ;;
 *)
   set -e	# Bail out immediately if ${RTLD} loses on any argument.
+  result=0
   for file; do
     echo "${file}:"
     case "$file" in
@@ -76,13 +100,19 @@ Try \`ldd --help' for more information."
     *) file="./$file" ;;
     esac
     if test ! -f "$file"; then
-      echo "$file: no such file"
-    elif ${RTLD} --verify "$file"; then
-      LD_TRACE_LOADED_OBJECTS=1 ${RTLD} "$file"
+      echo "${file}: no such file"
+      result=1
     else
-      echo '	not a dynamic executable'
+      test -x "$file" ||
+	echo "warning: you do not have execution permission for \`$file'"
+      if ${RTLD} --verify "$file"; then
+	LD_TRACE_LOADED_OBJECTS=1 ${RTLD} ${RELOCS} "$file" || result=1
+      else
+	echo '	not a dynamic executable'
+	result=1
+      fi
     fi
   done
 esac
 
-exit 0
+exit $result
diff --git a/elf/link.h b/elf/link.h
index 7e0b60793f..95d8f0912d 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -137,6 +137,12 @@ struct link_map
     unsigned int l_global:1;	/* Nonzero if object in _dl_global_scope.  */
     unsigned int l_reserved:2;	/* Reserved for internal use.  */
   };
+
+
+/* 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 *);
 
 /* Internal functions of the run-time dynamic linker.
    These can be accessed if you link again the dynamic linker
@@ -161,6 +167,11 @@ extern int _dl_sysdep_open_zero_fill (void); /* dl-sysdep.c */
    are concatenated to form the message to print.  */
 extern void _dl_sysdep_message (const char *string, ...);
 
+/* OS-dependent function to write a message on the standard error.
+   All arguments are `const char *'; args until a null pointer
+   are concatenated to form the message to print.  */
+extern void _dl_sysdep_error (const char *string, ...);
+
 /* OS-dependent function to give a fatal error message and exit
    when the dynamic linker fails before the program is fully linked.
    All arguments are `const char *'; args until a null pointer
@@ -177,11 +188,9 @@ extern int _dl_secure;
    zero; OBJECT is the name of the problematical shared object, or null if
    it is a general problem; ERRSTRING is a string describing the specific
    problem.  */
-
 extern void _dl_signal_error (int errcode,
 			      const char *object,
-			      const char *errstring)
-     __attribute__ ((__noreturn__));
+			      const char *errstring);
 
 /* Call OPERATE, catching errors from `dl_signal_error'.  If there is no
    error, *ERRSTRING is set to null.  If there is an error, *ERRSTRING and
@@ -192,6 +201,11 @@ extern int _dl_catch_error (char **errstring,
 			    const char **object,
 			    void (*operate) (void));
 
+/* Call OPERATE, receiving errors from `dl_signal_error'.  Unlike
+   `_dl_catch_error' the operation is resumed after the OPERATE
+   function returns.  */
+extern void _dl_receive_error (receiver_fct fct, void (*operate) (void));
+
 
 /* Helper function for <dlfcn.h> functions.  Runs the OPERATE function via
    _dl_catch_error.  Returns zero for success, nonzero for failure; and
diff --git a/elf/rtld.c b/elf/rtld.c
index 39435f8243..f9a2cd3d03 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -44,6 +44,10 @@ extern void *_dl_sysdep_read_whole_file (const char *filename,
 					 size_t *filesize_ptr,
 					 int mmap_prot);
 
+/* Helper function to handle errors while resolving symbols.  */
+static void print_unresolved (const char *errstring, const char *objname);
+
+
 int _dl_argc;
 char **_dl_argv;
 const char *_dl_rpath;
@@ -142,11 +146,19 @@ dl_main (const ElfW(Phdr) *phdr,
   enum { normal, list, verify, trace } mode;
   struct link_map **preloads;
   unsigned int npreloads;
+  const char *preloadlist;
   size_t file_size;
   char *file;
 
   mode = getenv ("LD_TRACE_LOADED_OBJECTS") != NULL ? trace : normal;
 
+  /* LAZY is determined by the parameters --datadeps and --function-deps
+     if we trace the binary.  */
+  if (mode == trace)
+    lazy = -1;
+  else
+    lazy = !__libc_enable_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0';
+
   /* Set up a flag which tells we are just starting.  */
   _dl_starting_up = 1;
 
@@ -186,22 +198,44 @@ of this helper program; chances are you did not intend to run this program.\n",
       /* Note the place where the dynamic linker actually came from.  */
       _dl_rtld_map.l_name = _dl_argv[0];
 
-      if (! strcmp (_dl_argv[1], "--list"))
-	{
-	  mode = list;
+      while (_dl_argc > 1)
+	if (! strcmp (_dl_argv[1], "--list"))
+	  {
+	    mode = list;
+	    lazy = -1;	/* This means do no dependency analysis.  */
 
-	  ++_dl_skip_args;
-	  --_dl_argc;
-	  ++_dl_argv;
-	}
-      else if (! strcmp (_dl_argv[1], "--verify"))
-	{
-	  mode = verify;
+	    ++_dl_skip_args;
+	    --_dl_argc;
+	    ++_dl_argv;
+	  }
+	else if (! strcmp (_dl_argv[1], "--verify"))
+	  {
+	    mode = verify;
 
-	  ++_dl_skip_args;
-	  --_dl_argc;
-	  ++_dl_argv;
-	}
+	    ++_dl_skip_args;
+	    --_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;
 
       ++_dl_skip_args;
       --_dl_argc;
@@ -311,23 +345,22 @@ of this helper program; chances are you did not intend to run this program.\n",
   preloads = NULL;
   npreloads = 0;
 
-  if (! __libc_enable_secure)
+  preloadlist = getenv ("LD_PRELOAD");
+  if (preloadlist)
     {
-      const char *preloadlist = getenv ("LD_PRELOAD");
-      if (preloadlist)
-	{
-	  /* The LD_PRELOAD environment variable gives a white space
-	     separated list of libraries that are loaded before the
-	     executable's dependencies and prepended to the global
-	     scope list.  */
-	  char *list = strdupa (preloadlist);
-	  char *p;
-	  while ((p = strsep (&list, " ")) != NULL)
-	    {
-	      (void) _dl_map_object (NULL, p, lt_library, 0);
-	      ++npreloads;
-	    }
-	}
+      /* The LD_PRELOAD environment variable gives a white space
+	 separated list of libraries that are loaded before the
+	 executable's dependencies and prepended to the global scope
+	 list.  If the binary is running setuid all elements
+	 containing a '/' are ignored since it is insecure.  */
+      char *list = strdupa (preloadlist);
+      char *p;
+      while ((p = strsep (&list, " ")) != NULL)
+	if (! __libc_enable_secure || strchr (p, '/') == NULL)
+	  {
+	    (void) _dl_map_object (NULL, p, lt_library, 0);
+	    ++npreloads;
+	  }
     }
 
   /* Read the contents of the file.  */
@@ -496,12 +529,31 @@ of this helper program; chances are you did not intend to run this program.\n",
 	      *--bp = '0';
 	    _dl_sysdep_message (" in object at 0x", bp, "\n", NULL);
 	  }
+      else if (lazy >= 0)
+	{
+	  /* We have to do symbol dependency testing.  */
+	  void doit (void)
+	    {
+	      _dl_relocate_object (l, _dl_object_relocation_scope (l), lazy);
+	    }
+
+	  l = _dl_loaded;
+	  while (l->l_next)
+	    l = l->l_next;
+	  do
+	    {
+	      if (l != &_dl_rtld_map && l->l_opencount > 0)
+		{
+		  _dl_receive_error (print_unresolved, doit);
+		  *_dl_global_scope_end = NULL;
+		}
+	      l = l->l_prev;
+	    } while (l);
+	}
 
       _exit (0);
     }
 
-  lazy = !__libc_enable_secure && *(getenv ("LD_BIND_NOW") ?: "") == '\0';
-
   {
     /* Now we have all the objects loaded.  Relocate them all except for
        the dynamic linker itself.  We do this in reverse order so that copy
@@ -573,3 +625,11 @@ of this helper program; chances are you did not intend to run this program.\n",
   /* Once we return, _dl_sysdep_start will invoke
      the DT_INIT functions and then *USER_ENTRY.  */
 }
+
+/* This is a little helper function for resolving symbols while
+   tracing the binary.  */
+static void
+print_unresolved (const char *errstring, const char *objname)
+{
+  _dl_sysdep_error (errstring, "	(", objname, ")\n", NULL);
+}