This is the mail archive of the libc-alpha@sources.redhat.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] ppc64 LD_PRELOAD tolerance


On PowerPC64 systems applications may contain a mix of 32- and 64-bit
programs and libraries. For example a RDBMS may have both 64-bit RDB
server, both 32- and 64-bit CLI libraries and collection of 32-bit
utility programs.  The set up for a RDB client may update LD_LIBRARY_PATH,
LD_PRELOAD, or both with a mixture 32- and 64-bit libraries.

Supporting mixed libraries LD_LIBRARY_PATH (and ld-cache) was relatively
simple to support but LD_PRELOAD was problematic. The comment in dl-load.c
(open_verify):

	    /* This is not a fatal error.  On architectures where
	       32-bit and 64-bit binaries can be run this might
	       happen.  */

gives one hope that mixed libraries in LD_PRELOAD should work (other
libraries, that don't match the word size of the current program, should
be silently ignored).  But in reality loader reports an error:

	"cannot open shared object file xyz"

and exits.  This can stop a user dead because any command that
requires a new program load while LD_PRELOAD is set may fail.  Specifically
exporting LD_PRELOAD=libany64bit.so to a bash script immediately disables
any command not built-in to bash.  By default the bash shell and all the
usual UNIX commands are 32-bit programs so adding any 64-bit library to
LD_PRELOAD is a bad idea.  Fortunately export is a build-in command and
the wise user can undo the damage with out a reboot.

So I would really like to fix this.

I traced the problem back to open_verify and _dl_map_object (in dl-load.c).
After checking of the requested library is already loaded, _dl_map_object
tests if the "name" is a path qualified or unqualified name with the
statement:

  if (strchr (name, '/') == NULL)

In the else leg of this if statement, which applies to libraries specified
by LD_PRELOAD, _dl_map_object expands any dynamic string tokens and passes
that string to open_verify.  Open_verify reads the elf header and checkes
the e_ident field (magic, class, data-endian), etc).  If a 32-bit loader
finds a 64-bit library (and vice versa) this test will fail because the
CLASS (ELFCLASS32/ELFCLASS64) will be different.  In this case open_verify
closes the file and return -1 to _dl_map_object.

If open_verify returns -1 and trace_mode is false _dl_map_object calls:

	_dl_signal_error) (errno, name, NULL,
			N_("cannot open shared object file"));

which spits out the message and exits. If trace_mode is true _dl_map_object
builds a "fake" map entry and returns a pointer to that. Neither is the
appropriate response.

I toyed with the idea just changing _dl_map_object to ignore the -1 return
from open_verify, but that seemed inappropriate:  1) A -1 return is ambiguous
with multiple conditions, some of which should not be tolerated.  2) Systems
that are not BIARCH should report the error.

I think it is best to: 1) Explicitly test for conditions that should be
tolerated. 2) A tolerated condition should have a separate unique return
code. 3) These tests should be conditionally enabled from architecture
specific includes and the tests should be calls back to architecture
specific code.

This set the design direction:

    1) powerpc64/dl-machine.h and powerpc32/dl-machine.h will:

        1a) Define the macro symbol ELF_MACHINE_BIARCH.

        1b) Define the static inline functions elf_tolerates_class and
        elf_tolerates_machine.  These functions return true if this
        loader can't load an ELF binary of this e_machine/e_ident[EI_CLASS]
        but another loader on this platform can.

    2) In dl-load.c(open_verify). If the e_ident comparison fails and
       ELF_MACHINE_BIARCH is defined:

        2a) If (elf_tolerates_class (ehdr) && (elf_tolerates_machine (ehdr)
        are turn; close the file, set errstring and errno, and return -2.

    3) In dl-load.c(_dl_map_object). When processing a path qualified name
       and open_verify returns -2 and ELF_MACHINE_BIARCH is defined;
       return NULL;

    4) In rtld.c(dl_main). When processed LD_PRELOAD or /etc/ld.so.preload
    and _dl_map_object returns NULL and ELF_MACHINE_BIARCH is defined;
    ignore it and continue processing.

This design has some minor side effects.

    5) In dl-load.c(open_path). Calls to open_verify may now return a -2
    in addition to -1.  If ELF_MACHINE_BIARCH is defined and open_verify
    returns -2 then mark this directory as existing and keep this
    directory in the path.


I believe this change is necessary for the sucessful deployment of
powerpc64 systems supporting a mixture of 32- and 64-bit applications.
It also minimal risk as this code is only enabled for those platforms
that define ELF_MACHINE_BIARCH.


2002-09-11  Steven Munroe  <sjmunroe@us.ibm.com>

	* elf/dl-load.c (open_verify) [ELF_MACHINE_BIARCH] <e_ident missmatch>:
	If elf_tolerates_class(ehdr) and elf_tolerates_machine (ehdr) are true
	then return -2.
	* elf/dl-load.c (open_path) [ELF_MACHINE_BIARCH]: When
	open_verify returns -2 then skip this library and continue
	search.
	* elf/dl-load.c (_dl_map_object) [ELF_MACHINE_BIARCH]: For
	qualified library path case and open_verify returns -2
	skip _dl_map_object_from_fd and return NULL.
	* elf/rtld.c (dl_main)[ELF_MACHINE_BIARCH] <LD_PRELOAD>: If
	_dl_map_object returns NULL skip this library and continue
	processing.
	(dl_main)[ELF_MACHINE_BIARCH] </etc/ld.so.preload>: If
	_dl_map_object returns NULL skip this library and continue
	processing.
	* sysdeps/powerpc/powerpc32/dl-machine.h: Define ELF_MACHINE_BIARCH.
	(elf_tolerates_class): New function.
	(elf_tolerates_machine): New function.
	* sysdeps/powerpc/powerpc64/dl-machine.h [ELF_MULT_MACHINES_SUPPORTED]:
	Changed to ELF_MACHINE_BIARCH.
	(elf_host_tolerates_class): Changed name to elf_tolerates_class.
	(elf_host_tolerates_machine): Changed name to elf_tolerates_machine.

>>>>>>> ppc64-LD_PRELOAD.patch
diff -rupPN libc23-cvstip-20020918/elf/dl-load.c libc23/elf/dl-load.c
--- libc23-cvstip-20020918/elf/dl-load.c	Thu Sep 12 00:15:30 2002
+++ libc23/elf/dl-load.c	Thu Sep 19 16:35:44 2002
@@ -1376,7 +1376,29 @@ open_verify (const char *name, struct fi
 	    /* This is not a fatal error.  On architectures where
 	       32-bit and 64-bit binaries can be run this might
 	       happen.  */
-	    goto close_and_out;
+#ifdef ELF_MACHINE_BIARCH
+      {
+        if (__builtin_expect (elf_tolerates_class (ehdr), 1)
+            &&  __builtin_expect (elf_tolerates_machine (ehdr), 1))
+          {
+            __close (fd);
+            errstring = N_("ELF header for other machine mode");
+            __set_errno (ENOENT);
+            fd = -2; /* return "tolerate" value */
+
+            /* Print that this file is tolerated but ignored,
+	             if this is wanted.  */
+            if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_LIBS, 0))
+              _dl_debug_printf ("  file=%s is incompatible with this loader"
+                                " but is tolerated\n", name);
+            return fd;
+          }
+        else
+          goto close_and_out;
+      }
+#else
+	    goto close_and_out;
+#endif
 	  else if (ehdr->e_ident[EI_DATA] != byteorder)
 	    {
 	      if (BYTE_ORDER == BIG_ENDIAN)
@@ -1530,8 +1552,22 @@ open_path (const char *name, size_t name
 	  fd = open_verify (buf, fbp);
 	  if (this_dir->status[cnt] == unknown)
 	    {
+#ifdef ELF_MACHINE_BIARCH
+      /* If open_verify returned -2 then this library is for another
+	     supported machine mode but is not supported by the current loader.
+	     For example a PowerPC64 loader may encounter a PowerPC32 ".so".
+	     In this case we need to silently ignore the 32-bit ".so" but keep
+	     the directory in the path. */
+        if (fd != -1)
+        {
+          this_dir->status[cnt] = existing;
+        	if (fd == -2)
+            fd = -1;
+        }
+#else
 	      if (fd != -1)
 		this_dir->status[cnt] = existing;
+#endif
 	      else
 		{
 		  /* We failed to open machine dependent library.  Let's
@@ -1837,10 +1873,15 @@ _dl_map_object (struct link_map *loader,
 	    free (realname);
 	}
     }
-
+#ifdef ELF_MACHINE_BIARCH
+  if (__builtin_expect (fd, 0) < 0)
+    {
+      if (trace_mode && (fd == -1)
+#else
   if (__builtin_expect (fd, 0) == -1)
     {
       if (trace_mode
+#endif
 	  && __builtin_expect (GL(dl_debug_mask) & DL_DEBUG_PRELINK, 0) == 0)
 	{
 	  /* We haven't found an appropriate library.  But since we
@@ -1866,6 +1907,17 @@ cannot create shared object descriptor")
 	  return l;
 	}
       else
+#ifdef ELF_MACHINE_BIARCH
+      /* If open_verify returned -2 then this library is for another
+	     supported machine mode but not supported by the current loader.
+	     For example a PowerPC64 loader may encounter a PowerPC32 ".so"
+	     in LD_PRELOAD.  In this case we need to silently ignore the
+	     32-bit ".so" by skipping the call to _dl_map_object_from_fd and
+	     returning NULL.  */
+        if (fd == -2)
+          return NULL;
+        else
+#endif
 	INTUSE(_dl_signal_error) (errno, name, NULL,
 				  N_("cannot open shared object file"));
     }
diff -rupPN libc23-cvstip-20020918/elf/rtld.c libc23/elf/rtld.c
--- libc23-cvstip-20020918/elf/rtld.c	Tue Sep 17 01:56:09 2002
+++ libc23/elf/rtld.c	Thu Sep 19 16:39:39 2002
@@ -948,9 +948,23 @@ of this helper program; chances are you
 							       p, 1,
 							       lt_library,
 							       0, 0);
+#ifdef ELF_MACHINE_BIARCH
+      /* if _dl_map_object returned NULL then this library is for
+         another supported machine mode but is not supported by the current
+         loader.  For example a PowerPC64 loader may encounter a PowerPC32
+         ".so" in LD_Preload.  In this case we need to silently ignore the
+         32-bit ".so".  */
+      if (new_map != NULL)
+      {
+        if (++new_map->l_opencount == 1)
+        /* It is no duplicate.  */
+        ++npreloads;
+      }
+#else
 	    if (++new_map->l_opencount == 1)
 	      /* It is no duplicate.  */
 	      ++npreloads;
+#endif
 	  }

       HP_TIMING_NOW (stop);
@@ -1018,9 +1032,23 @@ of this helper program; chances are you
 								   p, 1,
 								   lt_library,
 								   0, 0);
-		if (++new_map->l_opencount == 1)
-		  /* It is no duplicate.  */
-		  ++npreloads;
+#ifdef ELF_MACHINE_BIARCH
+      /* if _dl_map_object returned NULL then this library is for
+         another supported machine mode but is not supported by the
+         current loader.  For example a PowerPC64 loader may encounter
+         a PowerPC32 ".so" in "/etc/ld.so.preload".  In this case we
+         need to silently ignore the 32-bit ".so".  */
+      if (new_map != NULL)
+      {
+        if (++new_map->l_opencount == 1)
+        /* It is no duplicate.  */
+          ++npreloads;
+      }
+#else
+      if (++new_map->l_opencount == 1)
+      /* It is no duplicate.  */
+        ++npreloads;
+#endif
 	      }
 	}

@@ -1030,9 +1058,23 @@ of this helper program; chances are you
 	  struct link_map *new_map = INTUSE(_dl_map_object) (GL(dl_loaded), p,
 							     1, lt_library,
 							     0, 0);
+#ifdef ELF_MACHINE_BIARCH
+    /* if _dl_map_object returned NULL then this library is for
+       another supported machine mode but does not work with this
+       loader and the machine mode it runs in.  For example a
+       PowerPC64 loader may encounter a PowerPC32 ".so" in LD_PRELOAD.
+       In this case we need to silently ignore the 32-bit ".so".  */
+    if (new_map != NULL)
+      {
+        if (++new_map->l_opencount == 1)
+        /* It is no duplicate.  */
+          ++npreloads;
+      }
+#else
 	  if (++new_map->l_opencount == 1)
 	    /* It is no duplicate.  */
 	    ++npreloads;
+#endif
 	}

       HP_TIMING_NOW (stop);
@@ -2013,3 +2055,4 @@ print_statistics (void)
     }
 #endif
 }
+
diff -rupPN libc23-cvstip-20020918/sysdeps/powerpc/powerpc32/dl-machine.h libc23/sysdeps/powerpc/powerpc32/dl-machine.h
--- libc23-cvstip-20020918/sysdeps/powerpc/powerpc32/dl-machine.h	Thu Sep  5 03:24:49 2002
+++ libc23/sysdeps/powerpc/powerpc32/dl-machine.h	Thu Sep 19 16:30:26 2002
@@ -24,6 +24,28 @@

 #include <assert.h>

+/* Define this machine as BIARCH so we can tolerate libraries built
+   for other machine modes.  In this case the powerpc32 loader should
+   tolerate powerpc64 libraries it may find in LD_PRELOAD or
+   /etc/ld.so.preload.  */
+#define ELF_MACHINE_BIARCH
+
+/* Return true if the ELF binary associated with this header is
+   incompatible with this loader but is a valid machine type for this host.  */
+static inline int
+elf_tolerates_machine (const Elf32_Ehdr *ehdr)
+{
+  return ehdr->e_machine == EM_PPC64;
+}
+
+/* Return true if the ELF binary associated with this header is
+   incompatible with this loader but is a valid ELF_CLASS for this host.  */
+static inline int
+elf_tolerates_class (const Elf32_Ehdr *ehdr)
+{
+  return ehdr->e_ident[EI_CLASS] == ELFCLASS64;
+}
+
 /* Return nonzero iff ELF header is compatible with the running host.  */
 static inline int
 elf_machine_matches_host (const Elf32_Ehdr *ehdr)
diff -rupPN libc23-cvstip-20020918/sysdeps/powerpc/powerpc64/dl-machine.h libc23/sysdeps/powerpc/powerpc64/dl-machine.h
--- libc23-cvstip-20020918/sysdeps/powerpc/powerpc64/dl-machine.h	Tue Sep 17 18:50:02 2002
+++ libc23/sysdeps/powerpc/powerpc64/dl-machine.h	Thu Sep 19 16:27:36 2002
@@ -41,7 +41,11 @@ typedef struct
   Elf64_Addr fd_aux;
 } Elf64_FuncDesc;

-#define ELF_MULT_MACHINES_SUPPORTED
+/* Define this machine as BIARCH so we can tolerate libraries built
+   for other machine modes.  In this case the powerpc64 loader should
+   tolerate powerpc32 libraries it may find in LD_PRELOAD or
+   /etc/ld.so.preload.  */
+#define ELF_MACHINE_BIARCH

 /* Return nonzero iff ELF header is compatible with the running host.  */
 static inline int
@@ -50,18 +54,18 @@ elf_machine_matches_host (const Elf64_Eh
   return ehdr->e_machine == EM_PPC64;
 }

-/* Return nonzero iff ELF header is compatible with the running host,
-   but not this loader.  */
+/* Return true if the ELF binary associated with this header is
+   incompatible with this loader but is a valid machine type for this host.  */
 static inline int
-elf_host_tolerates_machine (const Elf64_Ehdr *ehdr)
+elf_tolerates_machine (const Elf64_Ehdr *ehdr)
 {
   return ehdr->e_machine == EM_PPC;
 }

-/* Return nonzero iff ELF header is compatible with the running host,
-   but not this loader.  */
+/* Return true if the ELF binary associated with this header is
+   incompatible with this loader but is a valid machine type for this host.  */
 static inline int
-elf_host_tolerates_class (const Elf64_Ehdr *ehdr)
+elf_tolerates_class (const Elf64_Ehdr *ehdr)
 {
   return ehdr->e_ident[EI_CLASS] == ELFCLASS32;
 }
<<<<<<<	ppc64-LD_PRELOAD.patch


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]