changeset 3986:d8dfd1a0bd8d

7029048: (launcher) fence the launcher against LD_LIBRARY_PATH Reviewed-by: mchung, ohair
author ksrini
date Thu, 07 Apr 2011 12:06:32 -0700
parents 5137806a3e34
children 587e968b03ee
files src/share/bin/jli_util.h src/solaris/bin/java_md.c test/tools/launcher/ExecutionEnvironment.java test/tools/launcher/Test7029048.java test/tools/launcher/TestHelper.java
diffstat 5 files changed, 845 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/bin/jli_util.h	Thu Apr 07 11:25:09 2011 -0400
+++ b/src/share/bin/jli_util.h	Thu Apr 07 12:06:32 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -35,7 +35,6 @@
 void  JLI_MemFree(void *ptr);
 int   JLI_StrCCmp(const char *s1, const char* s2);
 
-
 #define JLI_StrLen(p1)          strlen((p1))
 #define JLI_StrChr(p1, p2)      strchr((p1), (p2))
 #define JLI_StrRChr(p1, p2)     strrchr((p1), (p2))
@@ -48,6 +47,7 @@
 #define JLI_StrSpn(p1, p2)      strspn((p1), (p2))
 #define JLI_StrCSpn(p1, p2)     strcspn((p1), (p2))
 #define JLI_StrPBrk(p1, p2)     strpbrk((p1), (p2))
+#define JLI_StrTok(p1, p2)      strtok((p1), (p2))
 
 /* On Windows lseek() is in io.h rather than the location dictated by POSIX. */
 #ifdef _WIN32
--- a/src/solaris/bin/java_md.c	Thu Apr 07 11:25:09 2011 -0400
+++ b/src/solaris/bin/java_md.c	Thu Apr 07 12:06:32 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -46,6 +46,10 @@
 #define JVM_DLL "libjvm.so"
 #define JAVA_DLL "libjava.so"
 
+/* help jettison the LD_LIBRARY_PATH settings in the future */
+#ifndef SETENV_REQUIRED
+#define SETENV_REQUIRED
+#endif
 /*
  * If a processor / os combination has the ability to run binaries of
  * two data models and cohabitation of jre/jdk bits with both data
@@ -106,10 +110,22 @@
  * Previously the launcher modified the LD_LIBRARY_PATH appropriately for the
  * desired data model path, regardless if data models matched or not. The
  * launcher subsequently exec'ed the desired executable, in order to make the
- * LD_LIBRARY_PATH path available for the runtime linker. This is no longer the
- * case, the launcher dlopens the target libjvm.so. All other required
- * libraries are loaded by the runtime linker, by virtue of the $ORIGIN paths
- * baked into the shared libraries, by the build infrastructure at compile time.
+ * LD_LIBRARY_PATH path available, for the runtime linker.
+ *
+ * Now, in most cases,the launcher will dlopen the target libjvm.so. All
+ * required libraries are loaded by the runtime linker, using the
+ * $RPATH/$ORIGIN baked into the shared libraries at compile time. Therefore,
+ * in most cases, the launcher will only exec, if the data models are
+ * mismatched, and will not set any environment variables, regardless of the
+ * data models.
+ *
+ * However, if the environment contains a LD_LIBRARY_PATH, this will cause the
+ * launcher to inspect the LD_LIBRARY_PATH. The launcher will check
+ *  a. if the LD_LIBRARY_PATH's first component is the the path to the desired
+ *     libjvm.so
+ *  b. if any other libjvm.so is found in any of the paths.
+ * If case b is true, then the launcher will set the LD_LIBRARY_PATH to the
+ * desired JRE and reexec, in order to propagate the environment.
  *
  *  Main
  *  (incoming argv)
@@ -137,11 +153,11 @@
  *  |                                          |
  *  |                                          |
  * \|/                                        \|/
- * YES                              (find the desired executable and exec child)
+ * YES                             Find the desired executable/library
  *  |                                          |
  *  |                                          |
  * \|/                                        \|/
- * CheckJvmType                                Main
+ * CheckJvmType                          RequiresSetenv
  * (removes -client, -server, etc.)
  *  |
  *  |
@@ -156,7 +172,42 @@
  *  processes version options,
  *  creates argument list for vm,
  *  etc.)
- *
+ *   |
+ *   |
+ *  \|/
+ * RequiresSetenv
+ * Is LD_LIBRARY_PATH
+ * and friends set ? --> NO --> Have Desired Model ? NO --> Re-exec --> Main
+ *  YES                              YES --> Continue
+ *   |
+ *   |
+ *  \|/
+ * Path is desired JRE ? YES --> Have Desired Model ? NO --> Re-exec --> Main
+ *  NO                               YES --> Continue
+ *   |
+ *   |
+ *  \|/
+ * Paths have well known
+ * jvm paths ?       --> NO --> Have Desired Model ? NO --> Re-exec --> Main
+ *  YES                              YES --> Continue
+ *   |
+ *   |
+ *  \|/
+ *  Does libjvm.so exit
+ *  in any of them ? --> NO --> Have Desired Model ? NO --> Re-exec --> Main
+ *   YES                             YES --> Continue
+ *   |
+ *   |
+ *  \|/
+ *  Set the LD_LIBRARY_PATH
+ *   |
+ *   |
+ *  \|/
+ * Re-exec
+ *   |
+ *   |
+ *  \|/
+ * Main
  */
 
 static const char * SetExecname(char **argv);
@@ -182,6 +233,130 @@
     }
 }
 
+#ifdef SETENV_REQUIRED
+static jboolean
+JvmExists(const char *path) {
+    char tmp[PATH_MAX + 1];
+    struct stat statbuf;
+    JLI_Snprintf(tmp, PATH_MAX, "%s/%s", path, JVM_DLL);
+    if (stat(tmp, &statbuf) == 0) {
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+/*
+ * contains a lib/$LIBARCH/{server,client}/libjvm.so ?
+ */
+static jboolean
+ContainsLibJVM(int wanted, const char *env) {
+    char clientPattern[PATH_MAX + 1];
+    char serverPattern[PATH_MAX + 1];
+    char *envpath;
+    char *path;
+    jboolean clientPatternFound;
+    jboolean serverPatternFound;
+
+    /* fastest path */
+    if (env == NULL) {
+        return JNI_FALSE;
+    }
+
+    /* the usual suspects */
+    JLI_Snprintf(clientPattern, PATH_MAX, "lib/%s/client", GetArchPath(wanted));
+    JLI_Snprintf(serverPattern, PATH_MAX, "lib/%s/server", GetArchPath(wanted));
+
+    /* to optimize for time, test if any of our usual suspects are present. */
+    clientPatternFound = JLI_StrStr(env, clientPattern) != NULL;
+    serverPatternFound = JLI_StrStr(env, serverPattern) != NULL;
+    if (clientPatternFound == JNI_FALSE && serverPatternFound == JNI_FALSE) {
+        return JNI_FALSE;
+    }
+
+    /*
+     * we have a suspicious path component, check if it contains a libjvm.so
+     */
+    envpath = JLI_StringDup(env);
+    for (path = JLI_StrTok(envpath, ":"); path != NULL; path = JLI_StrTok(NULL, ":")) {
+        if (clientPatternFound && JLI_StrStr(path, clientPattern) != NULL) {
+            if (JvmExists(path)) {
+                JLI_MemFree(envpath);
+                return JNI_TRUE;
+            }
+        }
+        if (serverPatternFound && JLI_StrStr(path, serverPattern)  != NULL) {
+            if (JvmExists(path)) {
+                JLI_MemFree(envpath);
+                return JNI_TRUE;
+            }
+        }
+    }
+    JLI_MemFree(envpath);
+    return JNI_FALSE;
+}
+
+/*
+ * Test whether the environment variable needs to be set, see flowchart.
+ */
+static jboolean
+RequiresSetenv(int wanted, const char *jvmpath) {
+    char jpath[PATH_MAX + 1];
+    char *llp;
+    char *dmllp = NULL;
+    char *p; /* a utility pointer */
+
+    llp = getenv("LD_LIBRARY_PATH");
+#ifdef __solaris__
+    dmllp = (CURRENT_DATA_MODEL == 32)
+            ? getenv("LD_LIBRARY_PATH_32")
+            : getenv("LD_LIBRARY_PATH_64");
+#endif /* __solaris__ */
+    /* no environment variable is a good environment variable */
+    if (llp == NULL && dmllp == NULL) {
+        return JNI_FALSE;
+    }
+#ifdef __linux
+    /*
+     * On linux, if a binary is running as sgid or suid, glibc sets
+     * LD_LIBRARY_PATH to the empty string for security purposes. (In contrast,
+     * on Solaris the LD_LIBRARY_PATH variable for a privileged binary does not
+     * lose its settings; but the dynamic linker does apply more scrutiny to the
+     * path.) The launcher uses the value of LD_LIBRARY_PATH to prevent an exec
+     * loop, here and further downstream. Therefore, if we are running sgid or
+     * suid, this function's setting of LD_LIBRARY_PATH will be ineffective and
+     * we should case a return from the calling function.  Getting the right
+     * libraries will be handled by the RPATH. In reality, this check is
+     * redundant, as the previous check for a non-null LD_LIBRARY_PATH will
+     * return back to the calling function forthwith, it is left here to safe
+     * guard against any changes, in the glibc's existing security policy.
+     */
+    if ((getgid() != getegid()) || (getuid() != geteuid())) {
+        return JNI_FALSE;
+    }
+#endif /* __linux */
+
+    /*
+     * Prevent recursions. Since LD_LIBRARY_PATH is the one which will be set by
+     * previous versions of the JRE, thus it is the only path that matters here.
+     * So we check to see if the desired JRE is set.
+     */
+    JLI_StrNCpy(jpath, jvmpath, PATH_MAX);
+    p = JLI_StrRChr(jpath, '/');
+    *p = '\0';
+    if (llp != NULL && JLI_StrNCmp(llp, jpath, JLI_StrLen(jpath)) == 0) {
+        return JNI_FALSE;
+    }
+
+    /* scrutinize all the paths further */
+    if (llp != NULL &&  ContainsLibJVM(wanted, llp)) {
+        return JNI_TRUE;
+    }
+    if (dmllp != NULL && ContainsLibJVM(wanted, dmllp)) {
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+#endif /* SETENV_REQUIRED */
+
 void
 CreateExecutionEnvironment(int *pargc, char ***pargv,
                            char jrepath[], jint so_jrepath,
@@ -195,7 +370,6 @@
    * informative to issue an error message based on whether or not the
    * os/processor combination has dual mode capabilities.
    */
-
     jboolean jvmpathExists;
 
     /* Compute/set the name of the executable */
@@ -207,13 +381,24 @@
       char * jvmtype    = NULL;
       int  argc         = *pargc;
       char **argv       = *pargv;
-
       int running       = CURRENT_DATA_MODEL;
 
       int wanted        = running;      /* What data mode is being
                                            asked for? Current model is
                                            fine unless another model
                                            is asked for */
+#ifdef SETENV_REQUIRED
+      jboolean mustsetenv = JNI_FALSE;
+      char *runpath     = NULL; /* existing effective LD_LIBRARY_PATH setting */
+      char* new_runpath = NULL; /* desired new LD_LIBRARY_PATH string */
+      char* newpath     = NULL; /* path on new LD_LIBRARY_PATH */
+      char* lastslash   = NULL;
+      char** newenvp    = NULL; /* current environment */
+#ifdef __solaris__
+      char*  dmpath     = NULL;  /* data model specific LD_LIBRARY_PATH,
+                                    Solaris only */
+#endif /* __solaris__ */
+#endif  /* SETENV_REQUIRED */
 
       char** newargv    = NULL;
       int    newargc    = 0;
@@ -300,9 +485,18 @@
         }
         /*
          * we seem to have everything we need, so without further ado
-         * we return back.
+         * we return back, otherwise proceed to set the environment.
          */
+#ifdef SETENV_REQUIRED
+        mustsetenv = RequiresSetenv(wanted, jvmpath);
+        JLI_TraceLauncher("mustsetenv: %s\n", mustsetenv ? "TRUE" : "FALSE");
+
+        if (mustsetenv == JNI_FALSE) {
+            return;
+        }
+#else
         return;
+#endif /* SETENV_REQUIRED */
       } else {  /* do the same speculatively or exit */
 #ifdef DUAL_MODE
         if (running != wanted) {
@@ -331,67 +525,240 @@
 
           /* exec child can do error checking on the existence of the path */
           jvmpathExists = GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath, GetArchPath(wanted));
-
+#ifdef SETENV_REQUIRED
+          mustsetenv = RequiresSetenv(wanted, jvmpath);
+#endif /* SETENV_REQUIRED */
         }
 #else
         JLI_ReportErrorMessage(JRE_ERROR2, wanted);
         exit(1);
 #endif
-      }
+        }
+#ifdef SETENV_REQUIRED
+        if (mustsetenv) {
+            /*
+             * We will set the LD_LIBRARY_PATH as follows:
+             *
+             *     o          $JVMPATH (directory portion only)
+             *     o          $JRE/lib/$LIBARCHNAME
+             *     o          $JRE/../lib/$LIBARCHNAME
+             *
+             * followed by the user's previous effective LD_LIBRARY_PATH, if
+             * any.
+             */
 
-      {
-        char *newexec = execname;
+#ifdef __solaris__
+            /*
+             * Starting in Solaris 7, ld.so.1 supports three LD_LIBRARY_PATH
+             * variables:
+             *
+             * 1. LD_LIBRARY_PATH -- used for 32 and 64 bit searches if
+             * data-model specific variables are not set.
+             *
+             * 2. LD_LIBRARY_PATH_64 -- overrides and replaces LD_LIBRARY_PATH
+             * for 64-bit binaries.
+             *
+             * 3. LD_LIBRARY_PATH_32 -- overrides and replaces LD_LIBRARY_PATH
+             * for 32-bit binaries.
+             *
+             * The vm uses LD_LIBRARY_PATH to set the java.library.path system
+             * property.  To shield the vm from the complication of multiple
+             * LD_LIBRARY_PATH variables, if the appropriate data model
+             * specific variable is set, we will act as if LD_LIBRARY_PATH had
+             * the value of the data model specific variant and the data model
+             * specific variant will be unset.  Note that the variable for the
+             * *wanted* data model must be used (if it is set), not simply the
+             * current running data model.
+             */
+
+            switch (wanted) {
+                case 0:
+                    if (running == 32) {
+                        dmpath = getenv("LD_LIBRARY_PATH_32");
+                        wanted = 32;
+                    } else {
+                        dmpath = getenv("LD_LIBRARY_PATH_64");
+                        wanted = 64;
+                    }
+                    break;
+
+                case 32:
+                    dmpath = getenv("LD_LIBRARY_PATH_32");
+                    break;
+
+                case 64:
+                    dmpath = getenv("LD_LIBRARY_PATH_64");
+                    break;
+
+                default:
+                    JLI_ReportErrorMessage(JRE_ERROR3, __LINE__);
+                    exit(1); /* unknown value in wanted */
+                    break;
+            }
+
+            /*
+             * If dmpath is NULL, the relevant data model specific variable is
+             * not set and normal LD_LIBRARY_PATH should be used.
+             */
+            if (dmpath == NULL) {
+                runpath = getenv("LD_LIBRARY_PATH");
+            } else {
+                runpath = dmpath;
+            }
+#else
+            /*
+             * If not on Solaris, assume only a single LD_LIBRARY_PATH
+             * variable.
+             */
+            runpath = getenv("LD_LIBRARY_PATH");
+#endif /* __solaris__ */
+
+            /* runpath contains current effective LD_LIBRARY_PATH setting */
+
+            jvmpath = JLI_StringDup(jvmpath);
+            new_runpath = JLI_MemAlloc(((runpath != NULL) ? JLI_StrLen(runpath) : 0) +
+                    2 * JLI_StrLen(jrepath) + 2 * JLI_StrLen(arch) +
+                    JLI_StrLen(jvmpath) + 52);
+            newpath = new_runpath + JLI_StrLen("LD_LIBRARY_PATH=");
+
+
+            /*
+             * Create desired LD_LIBRARY_PATH value for target data model.
+             */
+            {
+                /* remove the name of the .so from the JVM path */
+                lastslash = JLI_StrRChr(jvmpath, '/');
+                if (lastslash)
+                    *lastslash = '\0';
+
+                sprintf(new_runpath, "LD_LIBRARY_PATH="
+                        "%s:"
+                        "%s/lib/%s:"
+                        "%s/../lib/%s",
+                        jvmpath,
 #ifdef DUAL_MODE
-        /*
-         * If the data model is being changed, the path to the
-         * executable must be updated accordingly; the executable name
-         * and directory the executable resides in are separate.  In the
-         * case of 32 => 64, the new bits are assumed to reside in, e.g.
-         * "olddir/LIBARCH64NAME/execname"; in the case of 64 => 32,
-         * the bits are assumed to be in "olddir/../execname".  For example,
-         *
-         * olddir/sparcv9/execname
-         * olddir/amd64/execname
-         *
-         * for Solaris SPARC and Linux amd64, respectively.
-         */
+                        jrepath, GetArchPath(wanted),
+                        jrepath, GetArchPath(wanted)
+#else
+                        jrepath, arch,
+                        jrepath, arch
+#endif
+                        );
 
-        if (running != wanted) {
-          char *oldexec = JLI_StrCpy(JLI_MemAlloc(JLI_StrLen(execname) + 1), execname);
-          char *olddir = oldexec;
-          char *oldbase = JLI_StrRChr(oldexec, '/');
 
+                /*
+                 * Check to make sure that the prefix of the current path is the
+                 * desired environment variable setting, though the RequiresSetenv
+                 * checks if the desired runpath exists, this logic does a more
+                 * comprehensive check.
+                 */
+                if (runpath != NULL &&
+                        JLI_StrNCmp(newpath, runpath, JLI_StrLen(newpath)) == 0 &&
+                        (runpath[JLI_StrLen(newpath)] == 0 || runpath[JLI_StrLen(newpath)] == ':') &&
+                        (running == wanted) /* data model does not have to be changed */
+#ifdef __solaris__
+                        && (dmpath == NULL) /* data model specific variables not set  */
+#endif
+                        ) {
 
-          newexec = JLI_MemAlloc(JLI_StrLen(execname) + 20);
-          *oldbase++ = 0;
-          sprintf(newexec, "%s/%s/%s", olddir,
-                  ((wanted==64) ? LIBARCH64NAME : ".."), oldbase);
-          argv[0] = newexec;
+                    return;
+
+                }
+            }
+
+            /*
+             * Place the desired environment setting onto the prefix of
+             * LD_LIBRARY_PATH.  Note that this prevents any possible infinite
+             * loop of execv() because we test for the prefix, above.
+             */
+            if (runpath != 0) {
+                JLI_StrCat(new_runpath, ":");
+                JLI_StrCat(new_runpath, runpath);
+            }
+
+            if (putenv(new_runpath) != 0) {
+                exit(1); /* problem allocating memory; LD_LIBRARY_PATH not set
+                    properly */
+            }
+
+            /*
+             * Unix systems document that they look at LD_LIBRARY_PATH only
+             * once at startup, so we have to re-exec the current executable
+             * to get the changed environment variable to have an effect.
+             */
+
+#ifdef __solaris__
+            /*
+             * If dmpath is not NULL, remove the data model specific string
+             * in the environment for the exec'ed child.
+             */
+            if (dmpath != NULL)
+                (void)UnsetEnv((wanted == 32) ? "LD_LIBRARY_PATH_32" : "LD_LIBRARY_PATH_64");
+#endif
+
+            newenvp = environ;
         }
-#endif
-        JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n");
-        (void)fflush(stdout);
-        (void)fflush(stderr);
-        execv(newexec, argv);
-        JLI_ReportErrorMessageSys(JRE_ERROR4, newexec);
+#endif /* SETENV_REQUIRED */
+        {
+            char *newexec = execname;
+#ifdef DUAL_MODE
+            /*
+             * If the data model is being changed, the path to the
+             * executable must be updated accordingly; the executable name
+             * and directory the executable resides in are separate.  In the
+             * case of 32 => 64, the new bits are assumed to reside in, e.g.
+             * "olddir/LIBARCH64NAME/execname"; in the case of 64 => 32,
+             * the bits are assumed to be in "olddir/../execname".  For example,
+             *
+             * olddir/sparcv9/execname
+             * olddir/amd64/execname
+             *
+             * for Solaris SPARC and Linux amd64, respectively.
+             */
+
+            if (running != wanted) {
+                char *oldexec = JLI_StrCpy(JLI_MemAlloc(JLI_StrLen(execname) + 1), execname);
+                char *olddir = oldexec;
+                char *oldbase = JLI_StrRChr(oldexec, '/');
+
+
+                newexec = JLI_MemAlloc(JLI_StrLen(execname) + 20);
+                *oldbase++ = 0;
+                sprintf(newexec, "%s/%s/%s", olddir,
+                        ((wanted == 64) ? LIBARCH64NAME : ".."), oldbase);
+                argv[0] = newexec;
+            }
+#endif /* DUAL_MODE */
+            JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n");
+            (void) fflush(stdout);
+            (void) fflush(stderr);
+#ifdef SETENV_REQUIRED
+            if (mustsetenv) {
+                execve(newexec, argv, newenvp);
+            } else {
+                execv(newexec, argv);
+            }
+#else
+            execv(newexec, argv);
+#endif /* SETENV_REQUIRED */
+            JLI_ReportErrorMessageSys(JRE_ERROR4, newexec);
 
 #ifdef DUAL_MODE
-        if (running != wanted) {
-          JLI_ReportErrorMessage(JRE_ERROR5, wanted, running);
-#  ifdef __solaris__
-#    ifdef __sparc
-          JLI_ReportErrorMessage(JRE_ERROR6);
-#    else
-          JLI_ReportErrorMessage(JRE_ERROR7);
-#    endif
+            if (running != wanted) {
+                JLI_ReportErrorMessage(JRE_ERROR5, wanted, running);
+#ifdef __solaris__
+#ifdef __sparc
+                JLI_ReportErrorMessage(JRE_ERROR6);
+#else
+                JLI_ReportErrorMessage(JRE_ERROR7);
+#endif  /* __sparc */
+            }
+#endif /* __solaris__ */
+#endif /* DUAL_MODE */
+
         }
-#  endif
-#endif
-
-      }
-      exit(1);
+        exit(1);
     }
-
 }
 
 /*
--- a/test/tools/launcher/ExecutionEnvironment.java	Thu Apr 07 11:25:09 2011 -0400
+++ b/test/tools/launcher/ExecutionEnvironment.java	Thu Apr 07 12:06:32 2011 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,7 @@
  */
 
 /*
- * This test tests for various things as follows:
+ * This tests for various things as follows:
  * Ensures that:
  *   1. uneccessary execs do not occur
  *   2. the environment is pristine,  users environment variable wrt.
@@ -84,7 +84,9 @@
     static int errors = 0;
     static int passes = 0;
 
-    private static void createTestJar() {
+    static final String LIBJVM = TestHelper.isWindows ? "jvm.dll" : "libjvm.so";
+
+    static void createTestJar() {
         try {
             List<String> codeList = new ArrayList<String>();
             codeList.add("static void printValue(String name, boolean property) {\n");
@@ -127,6 +129,7 @@
                 testJarFile.getAbsolutePath());
 
         if (!tr.isNotZeroOutput()) {
+            System.out.println(tr);
             throw new RuntimeException("Error: No output at all. Did the test execute ?");
         }
 
@@ -177,7 +180,6 @@
 
         Map<String, String> env = new HashMap<String, String>();
 
-
         if (TestHelper.isLinux) {
             for (String x : LD_PATH_STRINGS) {
                 String pairs[] = x.split("=");
@@ -209,7 +211,7 @@
             verifyJavaLibraryPathOverride(tr, true);
 
             // try changing the model from 32 to 64 bit
-            if (TestHelper.java64Cmd != null && TestHelper.is32Bit) {
+            if (TestHelper.dualModePresent() && TestHelper.is32Bit) {
                 // verify the override occurs
                 env.clear();
                 for (String x : LD_PATH_STRINGS) {
@@ -326,7 +328,7 @@
         File symLink = null;
         String libPathPrefix = TestHelper.isSDK ? "jre/lib" : "/lib";
         symLink = new File(TestHelper.JAVAHOME, libPathPrefix +
-                TestHelper.getJreArch() + "/libjvm.so");
+                TestHelper.getJreArch() + "/" + LIBJVM);
         if (symLink.exists()) {
             System.out.println("FAIL: The symlink exists " +
                     symLink.getAbsolutePath());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/Test7029048.java	Thu Apr 07 12:06:32 2011 -0700
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7029048
+ * @summary Checks for LD_LIBRARY_PATH on *nixes
+ * @compile -XDignore.symbol.file ExecutionEnvironment.java TestHelper.java Test7029048.java
+ * @run main Test7029048
+ */
+
+/*
+ * 7029048: test for LD_LIBRARY_PATH set to different paths pointing which may
+ * contain a libjvm.so and may not, but we test to ensure that the launcher
+ * behaves correctly in all cases.
+ */
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Test7029048 {
+
+    static int passes = 0;
+    static int errors = 0;
+
+    private static final String LIBJVM = ExecutionEnvironment.LIBJVM;
+    private static final String LD_LIBRARY_PATH =
+            ExecutionEnvironment.LD_LIBRARY_PATH;
+    private static final String LD_LIBRARY_PATH_32 =
+            ExecutionEnvironment.LD_LIBRARY_PATH_32;
+    private static final String LD_LIBRARY_PATH_64 =
+            ExecutionEnvironment.LD_LIBRARY_PATH_64;
+
+    private static final File libDir =
+            new File(System.getProperty("sun.boot.library.path"));
+    private static final File srcServerDir = new File(libDir, "server");
+    private static final File srcLibjvmSo = new File(srcServerDir, LIBJVM);
+
+    private static final File dstLibDir = new File("lib");
+    private static final File dstLibArchDir =
+            new File(dstLibDir, TestHelper.getJreArch());
+
+    private static final File dstServerDir = new File(dstLibArchDir, "server");
+    private static final File dstServerLibjvm = new File(dstServerDir, LIBJVM);
+
+    private static final File dstClientDir = new File(dstLibArchDir, "client");
+    private static final File dstClientLibjvm = new File(dstClientDir, LIBJVM);
+
+    // used primarily to test the solaris variants in dual mode
+    private static final File dstOtherArchDir;
+    private static final File dstOtherServerDir;
+    private static final File dstOtherServerLibjvm;
+
+    private static final Map<String, String> env = new HashMap<>();
+
+    static {
+        if (TestHelper.isDualMode) {
+            dstOtherArchDir = new File(dstLibDir, TestHelper.getComplementaryJreArch());
+            dstOtherServerDir = new File(dstOtherArchDir, "server");
+            dstOtherServerLibjvm = new File(dstOtherServerDir, LIBJVM);
+        } else {
+            dstOtherArchDir = null;
+            dstOtherServerDir = null;
+            dstOtherServerLibjvm = null;
+        }
+    }
+
+    static String getValue(String name, List<String> in) {
+        for (String x : in) {
+            String[] s = x.split("=");
+            if (name.equals(s[0].trim())) {
+                return s[1].trim();
+            }
+        }
+        return null;
+    }
+
+    static void run(boolean want32, String dflag, Map<String, String> env,
+            int nLLPComponents, String caseID) {
+        final boolean want64 = want32 == false;
+        env.put(ExecutionEnvironment.JLDEBUG_KEY, "true");
+        List<String> cmdsList = new ArrayList<>();
+
+        // only for a dual-mode system
+        if (want64 && TestHelper.isDualMode) {
+            cmdsList.add(TestHelper.java64Cmd);
+        } else {
+            cmdsList.add(TestHelper.javaCmd); // a 32-bit java command for all
+        }
+
+        /*
+         * empty or null strings can confuse the ProcessBuilder. A null flag
+         * indicates that the appropriate data model is enforced on the chosen
+         * launcher variant.
+         */
+
+        if (dflag != null) {
+            cmdsList.add(dflag);
+        } else {
+            cmdsList.add(want32 ? "-d32" : "-d64");
+        }
+        cmdsList.add("-server");
+        cmdsList.add("-jar");
+        cmdsList.add(ExecutionEnvironment.testJarFile.getAbsolutePath());
+        String[] cmds = new String[cmdsList.size()];
+        TestHelper.TestResult tr = TestHelper.doExec(env, cmdsList.toArray(cmds));
+        analyze(tr, nLLPComponents, caseID);
+    }
+
+    // no cross launch, ie. no change to the data model.
+    static void run(Map<String, String> env, int nLLPComponents, String caseID)
+            throws IOException {
+        boolean want32 = TestHelper.is32Bit;
+        run(want32, null, env, nLLPComponents, caseID);
+    }
+
+    static void analyze(TestHelper.TestResult tr, int nLLPComponents, String caseID) {
+        String envValue = getValue(LD_LIBRARY_PATH, tr.testOutput);
+       /*
+        * the envValue can never be null, since the test code should always
+        * print a "null" string.
+        */
+        if (envValue == null) {
+            System.out.println(tr);
+            throw new RuntimeException("NPE, likely a program crash ??");
+        }
+        String values[] = envValue.split(File.pathSeparator);
+        if (values.length == nLLPComponents) {
+            System.out.println(caseID + " :OK");
+            passes++;
+        } else {
+            System.out.println("FAIL: test7029048, " + caseID);
+            System.out.println(" expected " + nLLPComponents
+                    + " but got " + values.length);
+            System.out.println(envValue);
+            System.out.println(tr);
+            errors++;
+        }
+    }
+
+    /*
+     * A crucial piece, specifies what we should expect, given the conditions.
+     * That is for a given enum type, the value indicates how many absolute
+     * environment variables that can be expected. This value is used to base
+     * the actual expected values by adding the set environment variable usually
+     * it is 1, but it could be more if the test wishes to set more paths in
+     * the future.
+     */
+    private static enum LLP_VAR {
+        LLP_SET_NON_EXISTENT_PATH(0),   // env set, but the path does not exist
+        LLP_SET_EMPTY_PATH(0),          // env set, with a path but no libjvm.so
+        LLP_SET_WITH_JVM(3);            // env set, with a libjvm.so
+        private final int value;
+        LLP_VAR(int i) {
+            this.value = i;
+        }
+    }
+
+    /*
+     * test for 7029048
+     */
+    static void test7029048() throws IOException {
+        String desc = null;
+        for (LLP_VAR v : LLP_VAR.values()) {
+            switch (v) {
+                case LLP_SET_WITH_JVM:
+                    // copy the files into the directory structures
+                    TestHelper.copyFile(srcLibjvmSo, dstServerLibjvm);
+                    // does not matter if it is client or a server
+                    TestHelper.copyFile(srcLibjvmSo, dstClientLibjvm);
+                    // does not matter if the arch do not match either
+                    if (TestHelper.isDualMode) {
+                        TestHelper.copyFile(srcLibjvmSo, dstOtherServerLibjvm);
+                    }
+                    desc = "LD_LIBRARY_PATH should be set";
+                    break;
+                case LLP_SET_EMPTY_PATH:
+                    if (!dstClientDir.exists()) {
+                        Files.createDirectories(dstClientDir.toPath());
+                    } else {
+                        Files.deleteIfExists(dstClientLibjvm.toPath());
+                    }
+
+                    if (!dstServerDir.exists()) {
+                        Files.createDirectories(dstServerDir.toPath());
+                    } else {
+                        Files.deleteIfExists(dstServerLibjvm.toPath());
+                    }
+
+                    if (TestHelper.isDualMode) {
+                        if (!dstOtherServerDir.exists()) {
+                            Files.createDirectories(dstOtherServerDir.toPath());
+                        } else {
+                            Files.deleteIfExists(dstOtherServerLibjvm.toPath());
+                        }
+                    }
+
+                    desc = "LD_LIBRARY_PATH should not be set";
+                    break;
+                case LLP_SET_NON_EXISTENT_PATH:
+                    if (dstLibDir.exists()) {
+                        TestHelper.recursiveDelete(dstLibDir);
+                    }
+                    desc = "LD_LIBRARY_PATH should not be set";
+                    break;
+                default:
+                    throw new RuntimeException("unknown case");
+            }
+
+            /*
+             * Case 1: set the server path
+             */
+            env.clear();
+            env.put(LD_LIBRARY_PATH, dstServerDir.getAbsolutePath());
+            run(env, v.value + 1, "Case 1: " + desc);
+
+            /*
+             * Case 2: repeat with client path
+             */
+            env.clear();
+            env.put(LD_LIBRARY_PATH, dstClientDir.getAbsolutePath());
+            run(env, v.value + 1, "Case 2: " + desc);
+
+            if (!TestHelper.isDualMode) {
+                continue; // nothing more to do for Linux
+            }
+
+            // Tests applicable only to solaris.
+
+            // initialize test variables for dual mode operations
+            final File dst32ServerDir = TestHelper.is32Bit
+                    ? dstServerDir
+                    : dstOtherServerDir;
+
+            final File dst64ServerDir = TestHelper.is64Bit
+                    ? dstServerDir
+                    : dstOtherServerDir;
+
+            /*
+             * Case 3: set the appropriate LLP_XX flag,
+             * java32 -d32, LLP_32 is relevant, LLP_64 is ignored
+             * java64 -d64, LLP_64 is relevant, LLP_32 is ignored
+             */
+            env.clear();
+            env.put(LD_LIBRARY_PATH_32, dst32ServerDir.getAbsolutePath());
+            env.put(LD_LIBRARY_PATH_64, dst64ServerDir.getAbsolutePath());
+            run(TestHelper.is32Bit, null, env, v.value + 1, "Case 3: " + desc);
+
+            /*
+             * Case 4: we are in dual mode environment, running 64-bit then
+             * we have the following scenarios:
+             * java32 -d64, LLP_64 is relevant, LLP_32 is ignored
+             * java64 -d32, LLP_32 is relevant, LLP_64 is ignored
+             */
+            if (TestHelper.dualModePresent()) {
+                run(true, "-d64", env, v.value + 1, "Case 4A: " + desc);
+                run(false,"-d32", env, v.value + 1, "Case 4B: " + desc);
+            }
+        }
+        return;
+    }
+
+    public static void main(String... args) throws Exception {
+        if (TestHelper.isWindows) {
+            System.out.println("Warning: noop on windows");
+            return;
+        }
+        // create our test jar first
+        ExecutionEnvironment.createTestJar();
+
+        // run the tests
+        test7029048();
+        if (errors > 0) {
+            throw new Exception("Test7029048: FAIL: with "
+                    + errors + " errors and passes " + passes);
+        } else if (TestHelper.dualModePresent() && passes < 15) {
+            throw new Exception("Test7029048: FAIL: " +
+                    "all tests did not run, expected " + 15 + " got " + passes);
+        } else if (TestHelper.isSolaris && passes < 9) {
+            throw new Exception("Test7029048: FAIL: " +
+                    "all tests did not run, expected " + 9 + " got " + passes);
+        } else if (TestHelper.isLinux && passes < 6) {
+             throw new Exception("Test7029048: FAIL: " +
+                    "all tests did not run, expected " + 6 + " got " + passes);
+        } else {
+            System.out.println("Test7029048: PASS " + passes);
+        }
+    }
+}
--- a/test/tools/launcher/TestHelper.java	Thu Apr 07 11:25:09 2011 -0400
+++ b/test/tools/launcher/TestHelper.java	Thu Apr 07 12:06:32 2011 -0700
@@ -1,6 +1,5 @@
-
 /*
- * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011 Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,20 +21,28 @@
  * questions.
  */
 
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.FileVisitResult;
+import java.nio.file.SimpleFileVisitor;
 import javax.tools.ToolProvider;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import javax.tools.JavaCompiler;
 
+import static java.nio.file.StandardCopyOption.*;
+
 /**
- * This class provides some common utilites for the launcher tests.
+ * This class provides some common utilities for the launcher tests.
  */
 public enum TestHelper {
     INSTANCE;
@@ -101,6 +108,13 @@
     }
 
     /*
+     * is a dual mode available in the test jdk
+     */
+    static boolean dualModePresent() {
+        return isDualMode && java64Cmd != null;
+    }
+
+    /*
      * usually the jre/lib/arch-name is the same as os.arch, except for x86.
      */
     static String getJreArch() {
@@ -109,6 +123,27 @@
     }
 
     /*
+     * get the complementary jre arch ie. if sparc then return sparcv9 and
+     * vice-versa.
+     */
+    static String getComplementaryJreArch() {
+        String arch = System.getProperty("os.arch");
+        if (arch != null) {
+            switch (arch) {
+                case "sparc":
+                    return "sparcv9";
+                case "sparcv9":
+                    return "sparc";
+                case "x86":
+                    return "amd64";
+                case "amd64":
+                    return "i386";
+            }
+        }
+        return null;
+    }
+
+    /*
      * A convenience method to create a jar with jar file name and defs
      */
     static void createJar(File jarName, String... mainDefs)
@@ -168,6 +203,44 @@
         }
     }
 
+   static void copyFile(File src, File dst) throws IOException {
+        Path parent = dst.toPath().getParent();
+        if (parent != null) {
+            Files.createDirectories(parent);
+        }
+        Files.copy(src.toPath(), dst.toPath(), COPY_ATTRIBUTES, REPLACE_EXISTING);
+    }
+
+    static void recursiveDelete(File target) throws IOException {
+        if (!target.exists()) {
+            return;
+        }
+        Files.walkFileTree(target.toPath(), new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
+                try {
+                    Files.deleteIfExists(dir);
+                } catch (IOException ex) {
+                    System.out.println("Error: could not delete: " + dir.toString());
+                    System.out.println(ex.getMessage());
+                    return FileVisitResult.TERMINATE;
+                }
+                return FileVisitResult.CONTINUE;
+            }
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+                try {
+                    Files.deleteIfExists(file);
+                } catch (IOException ex) {
+                    System.out.println("Error: could not delete: " + file.toString());
+                    System.out.println(ex.getMessage());
+                    return FileVisitResult.TERMINATE;
+                }
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
     static TestResult doExec(String...cmds) {
         return doExec(null, cmds);
     }
@@ -187,7 +260,7 @@
         }
         BufferedReader rdr = null;
         try {
-            List<String> outputList = new ArrayList<String>();
+            List<String> outputList = new ArrayList<>();
             pb.redirectErrorStream(true);
             Process p = pb.start();
             rdr = new BufferedReader(new InputStreamReader(p.getInputStream()));
@@ -198,7 +271,9 @@
             }
             p.waitFor();
             p.destroy();
-            return new TestHelper.TestResult(cmdStr, p.exitValue(), outputList);
+
+            return new TestHelper.TestResult(cmdStr, p.exitValue(), outputList,
+                    env, new Throwable("current stack of the test"));
         } catch (Exception ex) {
             ex.printStackTrace();
             throw new RuntimeException(ex.getMessage());
@@ -213,11 +288,16 @@
         StringBuilder status;
         int exitValue;
         List<String> testOutput;
+        Map<String, String> env;
+        Throwable t;
 
-        public TestResult(String str, int rv, List<String> oList) {
+        public TestResult(String str, int rv, List<String> oList,
+                Map<String, String> env, Throwable t) {
             status = new StringBuilder("Executed command: " + str + "\n");
             exitValue = rv;
             testOutput = oList;
+            this.env = env;
+            this.t = t;
         }
 
         void appendStatus(String x) {
@@ -262,11 +342,21 @@
 
         @Override
         public String toString() {
-            status = status.append("++++Test Output Begin++++\n");
+            status.append("++++Begin Test Info++++\n");
+            status.append("++++Test Environment++++\n");
+            for (String x : env.keySet()) {
+                status.append(x).append("=").append(env.get(x)).append("\n");
+            }
+            status.append("++++Test Output++++\n");
             for (String x : testOutput) {
                 appendStatus(x);
             }
-            status = status.append("++++Test Output End++++\n");
+            status.append("++++Test Stack Trace++++\n");
+            status.append(t.toString());
+            for (StackTraceElement e : t.getStackTrace()) {
+                status.append(e.toString());
+            }
+            status.append("++++End of Test Info++++\n");
             return status.toString();
         }