1 /*
  2  * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 #include "java.h"
 27 #include "jvm_md.h"
 28 #include <dirent.h>
 29 #include <dlfcn.h>
 30 #include <fcntl.h>
 31 #include <inttypes.h>
 32 #include <stdio.h>
 33 #include <string.h>
 34 #include <stdlib.h>
 35 #include <sys/stat.h>
 36 #include <unistd.h>
 37 #include <sys/types.h>
 38 #include "manifest_info.h"
 39 
 40 
 41 #define JVM_DLL "libjvm.so"
 42 #define JAVA_DLL "libjava.so"
 43 #ifdef AIX
 44 #define LD_LIBRARY_PATH "LIBPATH"
 45 #else
 46 #define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
 47 #endif
 48 
 49 /* help jettison the LD_LIBRARY_PATH settings in the future */
 50 #ifndef SETENV_REQUIRED
 51 #define SETENV_REQUIRED
 52 #endif
 53 
 54 #ifdef __solaris__
 55 #  include <sys/systeminfo.h>
 56 #  include <sys/elf.h>
 57 #  include <stdio.h>
 58 #endif
 59 
 60 /*
 61  * Flowchart of launcher execs and options processing on unix
 62  *
 63  * The selection of the proper vm shared library to open depends on
 64  * several classes of command line options, including vm "flavor"
 65  * options (-client, -server).
 66  * The vm selection options are not passed to the running
 67  * virtual machine; they must be screened out by the launcher.
 68  *
 69  * The version specification (if any) is processed first by the
 70  * platform independent routine SelectVersion.  This may result in
 71  * the exec of the specified launcher version.
 72  *
 73  * Previously the launcher modified the LD_LIBRARY_PATH appropriately for the
 74  * desired data model path, regardless if data models matched or not. The
 75  * launcher subsequently exec'ed the desired executable, in order to make the
 76  * LD_LIBRARY_PATH path available, for the runtime linker.
 77  *
 78  * Now, in most cases,the launcher will dlopen the target libjvm.so. All
 79  * required libraries are loaded by the runtime linker, using the
 80  * $RPATH/$ORIGIN baked into the shared libraries at compile time. Therefore,
 81  * in most cases, the launcher will only exec, if the data models are
 82  * mismatched, and will not set any environment variables, regardless of the
 83  * data models.
 84  *
 85  * However, if the environment contains a LD_LIBRARY_PATH, this will cause the
 86  * launcher to inspect the LD_LIBRARY_PATH. The launcher will check
 87  *  a. if the LD_LIBRARY_PATH's first component is the path to the desired
 88  *     libjvm.so
 89  *  b. if any other libjvm.so is found in any of the paths.
 90  * If case b is true, then the launcher will set the LD_LIBRARY_PATH to the
 91  * desired JRE and reexec, in order to propagate the environment.
 92  *
 93  *  Main
 94  *  (incoming argv)
 95  *  |
 96  * \|/
 97  * CreateExecutionEnvironment
 98  * (determines desired data model)
 99  *  |
100  *  |
101  * \|/
102  *  Have Desired Model ? --> NO --> Exit(with error)
103  *  |
104  *  |
105  * \|/
106  * YES
107  *  |
108  *  |
109  * \|/
110  * CheckJvmType
111  * (removes -client, -server, etc.)
112  *  |
113  *  |
114  * \|/
115  * TranslateDashJArgs...
116  * (Prepare to pass args to vm)
117  *  |
118  *  |
119  * \|/
120  * ParseArguments
121  *   |
122  *   |
123  *  \|/
124  * RequiresSetenv
125  * Is LD_LIBRARY_PATH
126  * and friends set ? --> NO --> Continue
127  *  YES
128  *   |
129  *   |
130  *  \|/
131  * Path is desired JRE ? YES --> Continue
132  *  NO
133  *   |
134  *   |
135  *  \|/
136  * Paths have well known
137  * jvm paths ?       --> NO --> Error/Exit
138  *  YES
139  *   |
140  *   |
141  *  \|/
142  *  Does libjvm.so exist
143  *  in any of them ? --> NO  --> Continue
144  *   YES
145  *   |
146  *   |
147  *  \|/
148  *  Set the LD_LIBRARY_PATH
149  *   |
150  *   |
151  *  \|/
152  * Re-exec
153  *   |
154  *   |
155  *  \|/
156  * Main
157  */
158 
159 /* Store the name of the executable once computed */
160 static char *execname = NULL;
161 
162 /*
163  * execname accessor from other parts of platform dependent logic
164  */
165 const char *
166 GetExecName() {
167     return execname;
168 }
169 
170 #ifdef SETENV_REQUIRED
171 static jboolean
172 JvmExists(const char *path) {
173     char tmp[PATH_MAX + 1];
174     struct stat statbuf;
175     JLI_Snprintf(tmp, PATH_MAX, "%s/%s", path, JVM_DLL);
176     if (stat(tmp, &statbuf) == 0) {
177         return JNI_TRUE;
178     }
179     return JNI_FALSE;
180 }
181 /*
182  * contains a lib/{server,client}/libjvm.so ?
183  */
184 static jboolean
185 ContainsLibJVM(const char *env) {
186     /* the usual suspects */
187     char clientPattern[] = "lib/client";
188     char serverPattern[] = "lib/server";
189     char *envpath;
190     char *path;
191     char* save_ptr = NULL;
192     jboolean clientPatternFound;
193     jboolean serverPatternFound;
194 
195     /* fastest path */
196     if (env == NULL) {
197         return JNI_FALSE;
198     }
199 
200     /* to optimize for time, test if any of our usual suspects are present. */
201     clientPatternFound = JLI_StrStr(env, clientPattern) != NULL;
202     serverPatternFound = JLI_StrStr(env, serverPattern) != NULL;
203     if (clientPatternFound == JNI_FALSE && serverPatternFound == JNI_FALSE) {
204         return JNI_FALSE;
205     }
206 
207     /*
208      * we have a suspicious path component, check if it contains a libjvm.so
209      */
210     envpath = JLI_StringDup(env);
211     for (path = strtok_r(envpath, ":", &save_ptr); path != NULL; path = strtok_r(NULL, ":", &save_ptr)) {
212         if (clientPatternFound && JLI_StrStr(path, clientPattern) != NULL) {
213             if (JvmExists(path)) {
214                 JLI_MemFree(envpath);
215                 return JNI_TRUE;
216             }
217         }
218         if (serverPatternFound && JLI_StrStr(path, serverPattern)  != NULL) {
219             if (JvmExists(path)) {
220                 JLI_MemFree(envpath);
221                 return JNI_TRUE;
222             }
223         }
224     }
225     JLI_MemFree(envpath);
226     return JNI_FALSE;
227 }
228 
229 /*
230  * Test whether the environment variable needs to be set, see flowchart.
231  */
232 static jboolean
233 RequiresSetenv(const char *jvmpath) {
234     char jpath[PATH_MAX + 1];
235     char *llp;
236     char *dmllp = NULL;
237     char *p; /* a utility pointer */
238 
239 #ifdef AIX
240     /* We always have to set the LIBPATH on AIX because ld doesn't support $ORIGIN. */
241     return JNI_TRUE;
242 #endif
243 
244     llp = getenv("LD_LIBRARY_PATH");
245 #ifdef __solaris__
246     dmllp = getenv("LD_LIBRARY_PATH_64");
247 #endif /* __solaris__ */
248     /* no environment variable is a good environment variable */
249     if (llp == NULL && dmllp == NULL) {
250         return JNI_FALSE;
251     }
252 #ifdef __linux
253     /*
254      * On linux, if a binary is running as sgid or suid, glibc sets
255      * LD_LIBRARY_PATH to the empty string for security purposes. (In contrast,
256      * on Solaris the LD_LIBRARY_PATH variable for a privileged binary does not
257      * lose its settings; but the dynamic linker does apply more scrutiny to the
258      * path.) The launcher uses the value of LD_LIBRARY_PATH to prevent an exec
259      * loop, here and further downstream. Therefore, if we are running sgid or
260      * suid, this function's setting of LD_LIBRARY_PATH will be ineffective and
261      * we should case a return from the calling function.  Getting the right
262      * libraries will be handled by the RPATH. In reality, this check is
263      * redundant, as the previous check for a non-null LD_LIBRARY_PATH will
264      * return back to the calling function forthwith, it is left here to safe
265      * guard against any changes, in the glibc's existing security policy.
266      */
267     if ((getgid() != getegid()) || (getuid() != geteuid())) {
268         return JNI_FALSE;
269     }
270 #endif /* __linux */
271 
272     /*
273      * Prevent recursions. Since LD_LIBRARY_PATH is the one which will be set by
274      * previous versions of the JRE, thus it is the only path that matters here.
275      * So we check to see if the desired JRE is set.
276      */
277     JLI_StrNCpy(jpath, jvmpath, PATH_MAX);
278     p = JLI_StrRChr(jpath, '/');
279     *p = '\0';
280     if (llp != NULL && JLI_StrNCmp(llp, jpath, JLI_StrLen(jpath)) == 0) {
281         return JNI_FALSE;
282     }
283 
284     /* scrutinize all the paths further */
285     if (llp != NULL &&  ContainsLibJVM(llp)) {
286         return JNI_TRUE;
287     }
288     if (dmllp != NULL && ContainsLibJVM(dmllp)) {
289         return JNI_TRUE;
290     }
291     return JNI_FALSE;
292 }
293 #endif /* SETENV_REQUIRED */
294 
295 void
296 CreateExecutionEnvironment(int *pargc, char ***pargv,
297                            char jrepath[], jint so_jrepath,
298                            char jvmpath[], jint so_jvmpath,
299                            char jvmcfg[],  jint so_jvmcfg) {
300 
301     char * jvmtype = NULL;
302     int argc = *pargc;
303     char **argv = *pargv;
304 
305 #ifdef SETENV_REQUIRED
306     jboolean mustsetenv = JNI_FALSE;
307 #ifdef __solaris__
308     char *llp64 = NULL; /* existing LD_LIBRARY_PATH_64 setting */
309 #endif // __solaris__
310     char *runpath = NULL; /* existing effective LD_LIBRARY_PATH setting */
311     char* new_runpath = NULL; /* desired new LD_LIBRARY_PATH string */
312     char* newpath = NULL; /* path on new LD_LIBRARY_PATH */
313     char* lastslash = NULL;
314     char** newenvp = NULL; /* current environment */
315     size_t new_runpath_size;
316 #endif  /* SETENV_REQUIRED */
317 
318     /* Compute/set the name of the executable */
319     SetExecname(*pargv);
320 
321     /* Check to see if the jvmpath exists */
322     /* Find out where the JRE is that we will be using. */
323     if (!GetJREPath(jrepath, so_jrepath, JNI_FALSE)) {
324         JLI_ReportErrorMessage(JRE_ERROR1);
325         exit(2);
326     }
327     JLI_Snprintf(jvmcfg, so_jvmcfg, "%s%slib%sjvm.cfg",
328             jrepath, FILESEP, FILESEP);
329     /* Find the specified JVM type */
330     if (ReadKnownVMs(jvmcfg, JNI_FALSE) < 1) {
331         JLI_ReportErrorMessage(CFG_ERROR7);
332         exit(1);
333     }
334 
335     jvmpath[0] = '\0';
336     jvmtype = CheckJvmType(pargc, pargv, JNI_FALSE);
337     if (JLI_StrCmp(jvmtype, "ERROR") == 0) {
338         JLI_ReportErrorMessage(CFG_ERROR9);
339         exit(4);
340     }
341 
342     if (!GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath)) {
343         JLI_ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath);
344         exit(4);
345     }
346     /*
347      * we seem to have everything we need, so without further ado
348      * we return back, otherwise proceed to set the environment.
349      */
350 #ifdef SETENV_REQUIRED
351     mustsetenv = RequiresSetenv(jvmpath);
352     JLI_TraceLauncher("mustsetenv: %s\n", mustsetenv ? "TRUE" : "FALSE");
353 
354     if (mustsetenv == JNI_FALSE) {
355         return;
356     }
357 #else
358     return;
359 #endif /* SETENV_REQUIRED */
360 
361 #ifdef SETENV_REQUIRED
362     if (mustsetenv) {
363         /*
364          * We will set the LD_LIBRARY_PATH as follows:
365          *
366          *     o          $JVMPATH (directory portion only)
367          *     o          $JRE/lib
368          *     o          $JRE/../lib
369          *
370          * followed by the user's previous effective LD_LIBRARY_PATH, if
371          * any.
372          */
373 
374 #ifdef __solaris__
375         llp64 = getenv("LD_LIBRARY_PATH_64");
376         runpath = (llp64 == NULL) ? getenv(LD_LIBRARY_PATH) : llp64;
377 #else
378         runpath = getenv(LD_LIBRARY_PATH);
379 #endif /* __solaris__ */
380 
381         /* runpath contains current effective LD_LIBRARY_PATH setting */
382         { /* New scope to declare local variable */
383             char *new_jvmpath = JLI_StringDup(jvmpath);
384             new_runpath_size = ((runpath != NULL) ? JLI_StrLen(runpath) : 0) +
385                     2 * JLI_StrLen(jrepath) +
386                     JLI_StrLen(new_jvmpath) + 52;
387             new_runpath = JLI_MemAlloc(new_runpath_size);
388             newpath = new_runpath + JLI_StrLen(LD_LIBRARY_PATH "=");
389 
390 
391             /*
392              * Create desired LD_LIBRARY_PATH value for target data model.
393              */
394             {
395                 /* remove the name of the .so from the JVM path */
396                 lastslash = JLI_StrRChr(new_jvmpath, '/');
397                 if (lastslash)
398                     *lastslash = '\0';
399 
400                 sprintf(new_runpath, LD_LIBRARY_PATH "="
401                         "%s:"
402                         "%s/lib:"
403                         "%s/../lib",
404                         new_jvmpath,
405                         jrepath,
406                         jrepath
407                         );
408 
409                 JLI_MemFree(new_jvmpath);
410 
411                 /*
412                  * Check to make sure that the prefix of the current path is the
413                  * desired environment variable setting, though the RequiresSetenv
414                  * checks if the desired runpath exists, this logic does a more
415                  * comprehensive check.
416                  */
417                 if (runpath != NULL &&
418                         JLI_StrNCmp(newpath, runpath, JLI_StrLen(newpath)) == 0 &&
419                         (runpath[JLI_StrLen(newpath)] == 0 ||
420                         runpath[JLI_StrLen(newpath)] == ':')) {
421                     JLI_MemFree(new_runpath);
422                     return;
423                 }
424             }
425         }
426 
427         /*
428          * Place the desired environment setting onto the prefix of
429          * LD_LIBRARY_PATH.  Note that this prevents any possible infinite
430          * loop of execv() because we test for the prefix, above.
431          */
432         if (runpath != 0) {
433             /* ensure storage for runpath + colon + NULL */
434             if ((JLI_StrLen(runpath) + 1 + 1) > new_runpath_size) {
435                 JLI_ReportErrorMessageSys(JRE_ERROR11);
436                 exit(1);
437             }
438             JLI_StrCat(new_runpath, ":");
439             JLI_StrCat(new_runpath, runpath);
440         }
441 
442         if (putenv(new_runpath) != 0) {
443             /* problem allocating memory; LD_LIBRARY_PATH not set properly */
444             exit(1);
445         }
446 
447         /*
448          * Unix systems document that they look at LD_LIBRARY_PATH only
449          * once at startup, so we have to re-exec the current executable
450          * to get the changed environment variable to have an effect.
451          */
452 #ifdef __solaris__
453         /*
454          * new LD_LIBRARY_PATH took over for LD_LIBRARY_PATH_64
455          */
456         if (llp64 != NULL) {
457             UnsetEnv("LD_LIBRARY_PATH_64");
458         }
459 #endif // __solaris__
460 
461         newenvp = environ;
462     }
463 #endif /* SETENV_REQUIRED */
464     {
465         char *newexec = execname;
466         JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n");
467         (void) fflush(stdout);
468         (void) fflush(stderr);
469 #ifdef SETENV_REQUIRED
470         if (mustsetenv) {
471             execve(newexec, argv, newenvp);
472         } else {
473             execv(newexec, argv);
474         }
475 #else /* !SETENV_REQUIRED */
476         execv(newexec, argv);
477 #endif /* SETENV_REQUIRED */
478         JLI_ReportErrorMessageSys(JRE_ERROR4, newexec);
479     }
480     exit(1);
481 }
482 
483 
484 static jboolean
485 GetJVMPath(const char *jrepath, const char *jvmtype,
486            char *jvmpath, jint jvmpathsize)
487 {
488     struct stat s;
489 
490     if (JLI_StrChr(jvmtype, '/')) {
491         JLI_Snprintf(jvmpath, jvmpathsize, "%s/" JVM_DLL, jvmtype);
492     } else {
493         JLI_Snprintf(jvmpath, jvmpathsize, "%s/lib/%s/" JVM_DLL, jrepath, jvmtype);
494     }
495 
496     JLI_TraceLauncher("Does `%s' exist ... ", jvmpath);
497 
498     if (stat(jvmpath, &s) == 0) {
499         JLI_TraceLauncher("yes.\n");
500         return JNI_TRUE;
501     } else {
502         JLI_TraceLauncher("no.\n");
503         return JNI_FALSE;
504     }
505 }
506 
507 /*
508  * Find path to JRE based on .exe's location or registry settings.
509  */
510 static jboolean
511 GetJREPath(char *path, jint pathsize, jboolean speculative)
512 {
513     char libjava[MAXPATHLEN];
514     struct stat s;
515 
516     if (GetApplicationHome(path, pathsize)) {
517         /* Is JRE co-located with the application? */
518         JLI_Snprintf(libjava, sizeof(libjava), "%s/lib/" JAVA_DLL, path);
519         if (access(libjava, F_OK) == 0) {
520             JLI_TraceLauncher("JRE path is %s\n", path);
521             return JNI_TRUE;
522         }
523         /* ensure storage for path + /jre + NULL */
524         if ((JLI_StrLen(path) + 4  + 1) > (size_t) pathsize) {
525             JLI_TraceLauncher("Insufficient space to store JRE path\n");
526             return JNI_FALSE;
527         }
528         /* Does the app ship a private JRE in <apphome>/jre directory? */
529         JLI_Snprintf(libjava, sizeof(libjava), "%s/jre/lib/" JAVA_DLL, path);
530         if (access(libjava, F_OK) == 0) {
531             JLI_StrCat(path, "/jre");
532             JLI_TraceLauncher("JRE path is %s\n", path);
533             return JNI_TRUE;
534         }
535     }
536 
537     if (GetApplicationHomeFromDll(path, pathsize)) {
538         JLI_Snprintf(libjava, sizeof(libjava), "%s/lib/" JAVA_DLL, path);
539         if (stat(libjava, &s) == 0) {
540             JLI_TraceLauncher("JRE path is %s\n", path);
541             return JNI_TRUE;
542         }
543     }
544 
545     if (!speculative)
546       JLI_ReportErrorMessage(JRE_ERROR8 JAVA_DLL);
547     return JNI_FALSE;
548 }
549 
550 jboolean
551 LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn)
552 {
553     void *libjvm;
554 
555     JLI_TraceLauncher("JVM path is %s\n", jvmpath);
556 
557     libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL);
558     if (libjvm == NULL) {
559 #if defined(__solaris__) && defined(__sparc) && !defined(_LP64) /* i.e. 32-bit sparc */
560       FILE * fp;
561       Elf32_Ehdr elf_head;
562       int count;
563       int location;
564 
565       fp = fopen(jvmpath, "r");
566       if (fp == NULL) {
567         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
568         return JNI_FALSE;
569       }
570 
571       /* read in elf header */
572       count = fread((void*)(&elf_head), sizeof(Elf32_Ehdr), 1, fp);
573       fclose(fp);
574       if (count < 1) {
575         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
576         return JNI_FALSE;
577       }
578 
579       /*
580        * Check for running a server vm (compiled with -xarch=v8plus)
581        * on a stock v8 processor.  In this case, the machine type in
582        * the elf header would not be included the architecture list
583        * provided by the isalist command, which is turn is gotten from
584        * sysinfo.  This case cannot occur on 64-bit hardware and thus
585        * does not have to be checked for in binaries with an LP64 data
586        * model.
587        */
588       if (elf_head.e_machine == EM_SPARC32PLUS) {
589         char buf[257];  /* recommended buffer size from sysinfo man
590                            page */
591         long length;
592         char* location;
593 
594         length = sysinfo(SI_ISALIST, buf, 257);
595         if (length > 0) {
596             location = JLI_StrStr(buf, "sparcv8plus ");
597           if (location == NULL) {
598             JLI_ReportErrorMessage(JVM_ERROR3);
599             return JNI_FALSE;
600           }
601         }
602       }
603 #endif
604         JLI_ReportErrorMessage(DLL_ERROR1, __LINE__);
605         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
606         return JNI_FALSE;
607     }
608 
609     ifn->CreateJavaVM = (CreateJavaVM_t)
610         dlsym(libjvm, "JNI_CreateJavaVM");
611     if (ifn->CreateJavaVM == NULL) {
612         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
613         return JNI_FALSE;
614     }
615 
616     ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t)
617         dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs");
618     if (ifn->GetDefaultJavaVMInitArgs == NULL) {
619         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
620         return JNI_FALSE;
621     }
622 
623     ifn->GetCreatedJavaVMs = (GetCreatedJavaVMs_t)
624         dlsym(libjvm, "JNI_GetCreatedJavaVMs");
625     if (ifn->GetCreatedJavaVMs == NULL) {
626         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
627         return JNI_FALSE;
628     }
629 
630     return JNI_TRUE;
631 }
632 
633 /*
634  * Compute the name of the executable
635  *
636  * In order to re-exec securely we need the absolute path of the
637  * executable. On Solaris getexecname(3c) may not return an absolute
638  * path so we use dladdr to get the filename of the executable and
639  * then use realpath to derive an absolute path. From Solaris 9
640  * onwards the filename returned in DL_info structure from dladdr is
641  * an absolute pathname so technically realpath isn't required.
642  * On Linux we read the executable name from /proc/self/exe.
643  * As a fallback, and for platforms other than Solaris and Linux,
644  * we use FindExecName to compute the executable name.
645  */
646 const char*
647 SetExecname(char **argv)
648 {
649     char* exec_path = NULL;
650 #if defined(__solaris__)
651     {
652         Dl_info dlinfo;
653         int (*fptr)();
654 
655         fptr = (int (*)())dlsym(RTLD_DEFAULT, "main");
656         if (fptr == NULL) {
657             JLI_ReportErrorMessage(DLL_ERROR3, dlerror());
658             return JNI_FALSE;
659         }
660 
661         if (dladdr((void*)fptr, &dlinfo)) {
662             char *resolved = (char*)JLI_MemAlloc(PATH_MAX+1);
663             if (resolved != NULL) {
664                 exec_path = realpath(dlinfo.dli_fname, resolved);
665                 if (exec_path == NULL) {
666                     JLI_MemFree(resolved);
667                 }
668             }
669         }
670     }
671 #elif defined(__linux__)
672     {
673         const char* self = "/proc/self/exe";
674         char buf[PATH_MAX+1];
675         int len = readlink(self, buf, PATH_MAX);
676         if (len >= 0) {
677             buf[len] = '\0';            /* readlink(2) doesn't NUL terminate */
678             exec_path = JLI_StringDup(buf);
679         }
680     }
681 #else /* !__solaris__ && !__linux__ */
682     {
683         /* Not implemented */
684     }
685 #endif
686 
687     if (exec_path == NULL) {
688         exec_path = FindExecName(argv[0]);
689     }
690     execname = exec_path;
691     return exec_path;
692 }
693 
694 /* --- Splash Screen shared library support --- */
695 static const char* SPLASHSCREEN_SO = JNI_LIB_NAME("splashscreen");
696 static void* hSplashLib = NULL;
697 
698 void* SplashProcAddress(const char* name) {
699     if (!hSplashLib) {
700         int ret;
701         char jrePath[MAXPATHLEN];
702         char splashPath[MAXPATHLEN];
703 
704         if (!GetJREPath(jrePath, sizeof(jrePath), JNI_FALSE)) {
705             JLI_ReportErrorMessage(JRE_ERROR1);
706             return NULL;
707         }
708         ret = JLI_Snprintf(splashPath, sizeof(splashPath), "%s/lib/%s",
709                      jrePath, SPLASHSCREEN_SO);
710 
711         if (ret >= (int) sizeof(splashPath)) {
712             JLI_ReportErrorMessage(JRE_ERROR11);
713             return NULL;
714         }
715         if (ret < 0) {
716             JLI_ReportErrorMessage(JRE_ERROR13);
717             return NULL;
718         }
719         hSplashLib = dlopen(splashPath, RTLD_LAZY | RTLD_GLOBAL);
720         JLI_TraceLauncher("Info: loaded %s\n", splashPath);
721     }
722     if (hSplashLib) {
723         void* sym = dlsym(hSplashLib, name);
724         return sym;
725     } else {
726         return NULL;
727     }
728 }
729 
730 /*
731  * Signature adapter for pthread_create() or thr_create().
732  */
733 static void* ThreadJavaMain(void* args) {
734     return (void*)(intptr_t)JavaMain(args);
735 }
736 
737 /*
738  * Block current thread and continue execution in a new thread.
739  */
740 int
741 CallJavaMainInNewThread(jlong stack_size, void* args) {
742     int rslt;
743 #ifndef __solaris__
744     pthread_t tid;
745     pthread_attr_t attr;
746     pthread_attr_init(&attr);
747     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
748 
749     if (stack_size > 0) {
750         pthread_attr_setstacksize(&attr, stack_size);
751     }
752     pthread_attr_setguardsize(&attr, 0); // no pthread guard page on java threads
753 
754     if (pthread_create(&tid, &attr, ThreadJavaMain, args) == 0) {
755         void* tmp;
756         pthread_join(tid, &tmp);
757         rslt = (int)(intptr_t)tmp;
758     } else {
759        /*
760         * Continue execution in current thread if for some reason (e.g. out of
761         * memory/LWP)  a new thread can't be created. This will likely fail
762         * later in JavaMain as JNI_CreateJavaVM needs to create quite a
763         * few new threads, anyway, just give it a try..
764         */
765         rslt = JavaMain(args);
766     }
767 
768     pthread_attr_destroy(&attr);
769 #else /* __solaris__ */
770     thread_t tid;
771     long flags = 0;
772     if (thr_create(NULL, stack_size, ThreadJavaMain, args, flags, &tid) == 0) {
773         void* tmp;
774         thr_join(tid, NULL, &tmp);
775         rslt = (int)(intptr_t)tmp;
776     } else {
777         /* See above. Continue in current thread if thr_create() failed */
778         rslt = JavaMain(args);
779     }
780 #endif /* !__solaris__ */
781     return rslt;
782 }
783 
784 /* Coarse estimation of number of digits assuming the worst case is a 64-bit pid. */
785 #define MAX_PID_STR_SZ   20
786 
787 int
788 JVMInit(InvocationFunctions* ifn, jlong threadStackSize,
789         int argc, char **argv,
790         int mode, char *what, int ret)
791 {
792     ShowSplashScreen();
793     return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
794 }
795 
796 void
797 PostJVMInit(JNIEnv *env, jclass mainClass, JavaVM *vm)
798 {
799     // stubbed out for windows and *nixes.
800 }
801 
802 void
803 RegisterThread()
804 {
805     // stubbed out for windows and *nixes.
806 }
807 
808 /*
809  * on unix, we return a false to indicate this option is not applicable
810  */
811 jboolean
812 ProcessPlatformOption(const char *arg)
813 {
814     return JNI_FALSE;
815 }