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