1 /*
   2  * Copyright (c) 2012, 2017, 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 <sys/time.h>
  39 
  40 #include "manifest_info.h"
  41 
  42 /* Support Cocoa event loop on the main thread */
  43 #include <Cocoa/Cocoa.h>
  44 #include <objc/objc-runtime.h>
  45 #include <objc/objc-auto.h>
  46 
  47 #include <errno.h>
  48 #include <spawn.h>
  49 
  50 struct NSAppArgs {
  51     int argc;
  52     char **argv;
  53 };
  54 
  55 #define JVM_DLL "libjvm.dylib"
  56 #define JAVA_DLL "libjava.dylib"
  57 /* FALLBACK avoids naming conflicts with system libraries
  58  * (eg, ImageIO's libJPEG.dylib) */
  59 #define LD_LIBRARY_PATH "DYLD_FALLBACK_LIBRARY_PATH"
  60 
  61 /*
  62  * If a processor / os combination has the ability to run binaries of
  63  * two data models and cohabitation of jre/jdk bits with both data
  64  * models is supported, then DUAL_MODE is defined. MacOSX is a hybrid
  65  * system in that, the universal library can contain all types of libraries
  66  * 32/64 and client/server, thus the spawn is capable of linking with the
  67  * appropriate library as requested.
  68  *
  69  * Notes:
  70  * 1. VM. DUAL_MODE is disabled, and not supported, however, it is left here in
  71  *    for experimentation and perhaps enable it in the future.
  72  * 2. At the time of this writing, the universal library contains only
  73  *    a server 64-bit server JVM.
  74  * 3. "-client" command line option is supported merely as a command line flag,
  75  *    for, compatibility reasons, however, a server VM will be launched.
  76  */
  77 
  78 /*
  79  * Flowchart of launcher execs and options processing on unix
  80  *
  81  * The selection of the proper vm shared library to open depends on
  82  * several classes of command line options, including vm "flavor"
  83  * options (-client, -server) and the data model options, -d32  and
  84  * -d64, as well as a version specification which may have come from
  85  * the command line or from the manifest of an executable jar file.
  86  * The vm selection options are not passed to the running
  87  * virtual machine; they must be screened out by the launcher.
  88  *
  89  * The version specification (if any) is processed first by the
  90  * platform independent routine SelectVersion.  This may result in
  91  * the exec of the specified launcher version.
  92  *
  93  * Now, in most cases,the launcher will dlopen the target libjvm.so. All
  94  * required libraries are loaded by the runtime linker, using the known paths
  95  * baked into the shared libraries at compile time. Therefore,
  96  * in most cases, the launcher will only exec, if the data models are
  97  * mismatched, and will not set any environment variables, regardless of the
  98  * data models.
  99  *
 100  *
 101  *
 102  *  Main
 103  *  (incoming argv)
 104  *  |
 105  * \|/
 106  * CreateExecutionEnvironment
 107  * (determines desired data model)
 108  *  |
 109  *  |
 110  * \|/
 111  *  Have Desired Model ? --> NO --> Is Dual-Mode ? --> NO --> Exit(with error)
 112  *  |                                          |
 113  *  |                                          |
 114  *  |                                         \|/
 115  *  |                                         YES
 116  *  |                                          |
 117  *  |                                          |
 118  *  |                                         \|/
 119  *  |                                CheckJvmType
 120  *  |                               (removes -client, -server etc.)
 121  *  |                                          |
 122  *  |                                          |
 123  * \|/                                        \|/
 124  * YES                             Find the desired executable/library
 125  *  |                                          |
 126  *  |                                          |
 127  * \|/                                        \|/
 128  * CheckJvmType                             POINT A
 129  * (removes -client, -server, etc.)
 130  *  |
 131  *  |
 132  * \|/
 133  * TranslateDashJArgs...
 134  * (Prepare to pass args to vm)
 135  *  |
 136  *  |
 137  * \|/
 138  * ParseArguments
 139  * (processes version options,
 140  *  creates argument list for vm,
 141  *  etc.)
 142  *   |
 143  *   |
 144  *  \|/
 145  * POINT A
 146  *   |
 147  *   |
 148  *  \|/
 149  * Path is desired JRE ? YES --> Continue
 150  *  NO
 151  *   |
 152  *   |
 153  *  \|/
 154  * Paths have well known
 155  * jvm paths ?       --> NO --> Continue
 156  *  YES
 157  *   |
 158  *   |
 159  *  \|/
 160  *  Does libjvm.so exist
 161  *  in any of them ? --> NO --> Continue
 162  *   YES
 163  *   |
 164  *   |
 165  *  \|/
 166  * Re-exec / Spawn
 167  *   |
 168  *   |
 169  *  \|/
 170  * Main
 171  */
 172 
 173 /* Store the name of the executable once computed */
 174 static char *execname = NULL;
 175 
 176 /*
 177  * execname accessor from other parts of platform dependent logic
 178  */
 179 const char *
 180 GetExecName() {
 181     return execname;
 182 }
 183 
 184 /*
 185  * Exports the JNI interface from libjli
 186  *
 187  * This allows client code to link against the .jre/.jdk bundles,
 188  * and not worry about trying to pick a HotSpot to link against.
 189  *
 190  * Switching architectures is unsupported, since client code has
 191  * made that choice before the JVM was requested.
 192  */
 193 
 194 static InvocationFunctions *sExportedJNIFunctions = NULL;
 195 static char *sPreferredJVMType = NULL;
 196 
 197 static InvocationFunctions *GetExportedJNIFunctions() {
 198     if (sExportedJNIFunctions != NULL) return sExportedJNIFunctions;
 199 
 200     char jrePath[PATH_MAX];
 201     jboolean gotJREPath = GetJREPath(jrePath, sizeof(jrePath), JNI_FALSE);
 202     if (!gotJREPath) {
 203         JLI_ReportErrorMessage("Failed to GetJREPath()");
 204         return NULL;
 205     }
 206 
 207     char *preferredJVM = sPreferredJVMType;
 208     if (preferredJVM == NULL) {
 209 #if defined(__i386__)
 210         preferredJVM = "client";
 211 #elif defined(__x86_64__)
 212         preferredJVM = "server";
 213 #else
 214 #error "Unknown architecture - needs definition"
 215 #endif
 216     }
 217 
 218     char jvmPath[PATH_MAX];
 219     jboolean gotJVMPath = GetJVMPath(jrePath, preferredJVM, jvmPath, sizeof(jvmPath));
 220     if (!gotJVMPath) {
 221         JLI_ReportErrorMessage("Failed to GetJVMPath()");
 222         return NULL;
 223     }
 224 
 225     InvocationFunctions *fxns = malloc(sizeof(InvocationFunctions));
 226     jboolean vmLoaded = LoadJavaVM(jvmPath, fxns);
 227     if (!vmLoaded) {
 228         JLI_ReportErrorMessage("Failed to LoadJavaVM()");
 229         return NULL;
 230     }
 231 
 232     return sExportedJNIFunctions = fxns;
 233 }
 234 
 235 #ifndef STATIC_BUILD
 236 
 237 JNIEXPORT jint JNICALL
 238 JNI_GetDefaultJavaVMInitArgs(void *args) {
 239     InvocationFunctions *ifn = GetExportedJNIFunctions();
 240     if (ifn == NULL) return JNI_ERR;
 241     return ifn->GetDefaultJavaVMInitArgs(args);
 242 }
 243 
 244 JNIEXPORT jint JNICALL
 245 JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args) {
 246     InvocationFunctions *ifn = GetExportedJNIFunctions();
 247     if (ifn == NULL) return JNI_ERR;
 248     return ifn->CreateJavaVM(pvm, penv, args);
 249 }
 250 
 251 JNIEXPORT jint JNICALL
 252 JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs) {
 253     InvocationFunctions *ifn = GetExportedJNIFunctions();
 254     if (ifn == NULL) return JNI_ERR;
 255     return ifn->GetCreatedJavaVMs(vmBuf, bufLen, nVMs);
 256 }
 257 #endif
 258 
 259 /*
 260  * Allow JLI-aware launchers to specify a client/server preference
 261  */
 262 JNIEXPORT void JNICALL
 263 JLI_SetPreferredJVM(const char *prefJVM) {
 264     if (sPreferredJVMType != NULL) {
 265         free(sPreferredJVMType);
 266         sPreferredJVMType = NULL;
 267     }
 268 
 269     if (prefJVM == NULL) return;
 270     sPreferredJVMType = strdup(prefJVM);
 271 }
 272 
 273 static BOOL awtLoaded = NO;
 274 static pthread_mutex_t awtLoaded_mutex = PTHREAD_MUTEX_INITIALIZER;
 275 static pthread_cond_t  awtLoaded_cv = PTHREAD_COND_INITIALIZER;
 276 
 277 JNIEXPORT void JNICALL
 278 JLI_NotifyAWTLoaded()
 279 {
 280     pthread_mutex_lock(&awtLoaded_mutex);
 281     awtLoaded = YES;
 282     pthread_cond_signal(&awtLoaded_cv);
 283     pthread_mutex_unlock(&awtLoaded_mutex);
 284 }
 285 
 286 static int (*main_fptr)(int argc, char **argv) = NULL;
 287 
 288 /*
 289  * Unwrap the arguments and re-run main()
 290  */
 291 static void *apple_main (void *arg)
 292 {
 293     objc_registerThreadWithCollector();
 294 
 295     if (main_fptr == NULL) {
 296 #ifdef STATIC_BUILD
 297         extern int main(int argc, char **argv);
 298         main_fptr = &main;
 299 #else
 300         main_fptr = (int (*)())dlsym(RTLD_DEFAULT, "main");
 301 #endif
 302         if (main_fptr == NULL) {
 303             JLI_ReportErrorMessageSys("error locating main entrypoint\n");
 304             exit(1);
 305         }
 306     }
 307 
 308     struct NSAppArgs *args = (struct NSAppArgs *) arg;
 309     exit(main_fptr(args->argc, args->argv));
 310 }
 311 
 312 static void dummyTimer(CFRunLoopTimerRef timer, void *info) {}
 313 
 314 static void ParkEventLoop() {
 315     // RunLoop needs at least one source, and 1e20 is pretty far into the future
 316     CFRunLoopTimerRef t = CFRunLoopTimerCreate(kCFAllocatorDefault, 1.0e20, 0.0, 0, 0, dummyTimer, NULL);
 317     CFRunLoopAddTimer(CFRunLoopGetCurrent(), t, kCFRunLoopDefaultMode);
 318     CFRelease(t);
 319 
 320     // Park this thread in the main run loop.
 321     int32_t result;
 322     do {
 323         result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e20, false);
 324     } while (result != kCFRunLoopRunFinished);
 325 }
 326 
 327 /*
 328  * Mac OS X mandates that the GUI event loop run on very first thread of
 329  * an application. This requires that we re-call Java's main() on a new
 330  * thread, reserving the 'main' thread for Cocoa.
 331  */
 332 static void MacOSXStartup(int argc, char *argv[]) {
 333     // Thread already started?
 334     static jboolean started = false;
 335     if (started) {
 336         return;
 337     }
 338     started = true;
 339 
 340     // Hand off arguments
 341     struct NSAppArgs args;
 342     args.argc = argc;
 343     args.argv = argv;
 344 
 345     // Fire up the main thread
 346     pthread_t main_thr;
 347     if (pthread_create(&main_thr, NULL, &apple_main, &args) != 0) {
 348         JLI_ReportErrorMessageSys("Could not create main thread: %s\n", strerror(errno));
 349         exit(1);
 350     }
 351     if (pthread_detach(main_thr)) {
 352         JLI_ReportErrorMessageSys("pthread_detach() failed: %s\n", strerror(errno));
 353         exit(1);
 354     }
 355 
 356     ParkEventLoop();
 357 }
 358 
 359 void
 360 CreateExecutionEnvironment(int *pargc, char ***pargv,
 361                            char jrepath[], jint so_jrepath,
 362                            char jvmpath[], jint so_jvmpath,
 363                            char jvmcfg[],  jint so_jvmcfg) {
 364     jboolean jvmpathExists;
 365 
 366     /* Compute/set the name of the executable */
 367     SetExecname(*pargv);
 368 
 369     char * jvmtype    = NULL;
 370     int  argc         = *pargc;
 371     char **argv       = *pargv;
 372 
 373     /* Find out where the JRE is that we will be using. */
 374     if (!GetJREPath(jrepath, so_jrepath, JNI_FALSE) ) {
 375         JLI_ReportErrorMessage(JRE_ERROR1);
 376         exit(2);
 377     }
 378     JLI_Snprintf(jvmcfg, so_jvmcfg, "%s%slib%sjvm.cfg",
 379                  jrepath, FILESEP, FILESEP);
 380     /* Find the specified JVM type */
 381     if (ReadKnownVMs(jvmcfg, JNI_FALSE) < 1) {
 382         JLI_ReportErrorMessage(CFG_ERROR7);
 383         exit(1);
 384     }
 385 
 386     jvmpath[0] = '\0';
 387     jvmtype = CheckJvmType(pargc, pargv, JNI_FALSE);
 388     if (JLI_StrCmp(jvmtype, "ERROR") == 0) {
 389         JLI_ReportErrorMessage(CFG_ERROR9);
 390         exit(4);
 391     }
 392 
 393     if (!GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath)) {
 394         JLI_ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath);
 395         exit(4);
 396     }
 397 
 398     /*
 399      * Mac OS X requires the Cocoa event loop to be run on the "main"
 400      * thread. Spawn off a new thread to run main() and pass
 401      * this thread off to the Cocoa event loop.
 402      */
 403     MacOSXStartup(argc, argv);
 404 
 405     /*
 406      * we seem to have everything we need
 407      */
 408     return;
 409 }
 410 
 411 /*
 412  * VM choosing is done by the launcher (java.c).
 413  */
 414 static jboolean
 415 GetJVMPath(const char *jrepath, const char *jvmtype,
 416            char *jvmpath, jint jvmpathsize)
 417 {
 418     struct stat s;
 419 
 420     if (JLI_StrChr(jvmtype, '/')) {
 421         JLI_Snprintf(jvmpath, jvmpathsize, "%s/" JVM_DLL, jvmtype);
 422     } else {
 423         /*
 424          * macosx client library is built thin, i386 only.
 425          * 64 bit client requests must load server library
 426          */
 427         JLI_Snprintf(jvmpath, jvmpathsize, "%s/lib/%s/" JVM_DLL, jrepath, jvmtype);
 428     }
 429 
 430     JLI_TraceLauncher("Does `%s' exist ... ", jvmpath);
 431 
 432 #ifdef STATIC_BUILD
 433     return JNI_TRUE;
 434 #else
 435     if (stat(jvmpath, &s) == 0) {
 436         JLI_TraceLauncher("yes.\n");
 437         return JNI_TRUE;
 438     } else {
 439         JLI_TraceLauncher("no.\n");
 440         return JNI_FALSE;
 441     }
 442 #endif
 443 }
 444 
 445 /*
 446  * Find path to JRE based on .exe's location or registry settings.
 447  */
 448 static jboolean
 449 GetJREPath(char *path, jint pathsize, jboolean speculative)
 450 {
 451     char libjava[MAXPATHLEN];
 452 
 453     if (GetApplicationHome(path, pathsize)) {
 454         /* Is JRE co-located with the application? */
 455 #ifdef STATIC_BUILD
 456         char jvm_cfg[MAXPATHLEN];
 457         JLI_Snprintf(jvm_cfg, sizeof(jvm_cfg), "%s/lib/jvm.cfg", path);
 458         if (access(jvm_cfg, F_OK) == 0) {
 459             return JNI_TRUE;
 460         }
 461 #else
 462         JLI_Snprintf(libjava, sizeof(libjava), "%s/lib/" JAVA_DLL, path);
 463         if (access(libjava, F_OK) == 0) {
 464             return JNI_TRUE;
 465         }
 466 #endif
 467         /* ensure storage for path + /jre + NULL */
 468         if ((JLI_StrLen(path) + 4 + 1) > (size_t) pathsize) {
 469             JLI_TraceLauncher("Insufficient space to store JRE path\n");
 470             return JNI_FALSE;
 471         }
 472         /* Does the app ship a private JRE in <apphome>/jre directory? */
 473         JLI_Snprintf(libjava, sizeof(libjava), "%s/jre/lib/" JAVA_DLL, path);
 474         if (access(libjava, F_OK) == 0) {
 475             JLI_StrCat(path, "/jre");
 476             JLI_TraceLauncher("JRE path is %s\n", path);
 477             return JNI_TRUE;
 478         }
 479     }
 480 
 481     /* try to find ourselves instead */
 482     Dl_info selfInfo;
 483     dladdr(&GetJREPath, &selfInfo);
 484 
 485 #ifdef STATIC_BUILD
 486     char jvm_cfg[MAXPATHLEN];
 487     char *p = NULL;
 488     strncpy(jvm_cfg, selfInfo.dli_fname, MAXPATHLEN);
 489     p = strrchr(jvm_cfg, '/'); *p = '\0';
 490     p = strrchr(jvm_cfg, '/');
 491     if (strcmp(p, "/.") == 0) {
 492       *p = '\0';
 493       p = strrchr(jvm_cfg, '/'); *p = '\0';
 494     }
 495     else *p = '\0';
 496     strncpy(path, jvm_cfg, pathsize);
 497     strncat(jvm_cfg, "/lib/jvm.cfg", MAXPATHLEN);
 498     if (access(jvm_cfg, F_OK) == 0) {
 499       return JNI_TRUE;
 500     }
 501 #endif
 502 
 503     char *realPathToSelf = realpath(selfInfo.dli_fname, path);
 504     if (realPathToSelf != path) {
 505         return JNI_FALSE;
 506     }
 507 
 508     size_t pathLen = strlen(realPathToSelf);
 509     if (pathLen == 0) {
 510         return JNI_FALSE;
 511     }
 512 
 513     const char lastPathComponent[] = "/lib/jli/libjli.dylib";
 514     size_t sizeOfLastPathComponent = sizeof(lastPathComponent) - 1;
 515     if (pathLen < sizeOfLastPathComponent) {
 516         return JNI_FALSE;
 517     }
 518 
 519     size_t indexOfLastPathComponent = pathLen - sizeOfLastPathComponent;
 520     if (0 == strncmp(realPathToSelf + indexOfLastPathComponent, lastPathComponent, sizeOfLastPathComponent - 1)) {
 521         realPathToSelf[indexOfLastPathComponent + 1] = '\0';
 522         return JNI_TRUE;
 523     }
 524 
 525     if (!speculative)
 526       JLI_ReportErrorMessage(JRE_ERROR8 JAVA_DLL);
 527     return JNI_FALSE;
 528 }
 529 
 530 jboolean
 531 LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn)
 532 {
 533     Dl_info dlinfo;
 534     void *libjvm;
 535 
 536     JLI_TraceLauncher("JVM path is %s\n", jvmpath);
 537 
 538 #ifndef STATIC_BUILD
 539     libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL);
 540 #else
 541     libjvm = dlopen(NULL, RTLD_FIRST);
 542 #endif
 543     if (libjvm == NULL) {
 544         JLI_ReportErrorMessage(DLL_ERROR1, __LINE__);
 545         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
 546         return JNI_FALSE;
 547     }
 548 
 549     ifn->CreateJavaVM = (CreateJavaVM_t)
 550         dlsym(libjvm, "JNI_CreateJavaVM");
 551     if (ifn->CreateJavaVM == NULL) {
 552         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
 553         return JNI_FALSE;
 554     }
 555 
 556     ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t)
 557         dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs");
 558     if (ifn->GetDefaultJavaVMInitArgs == NULL) {
 559         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
 560         return JNI_FALSE;
 561     }
 562 
 563     ifn->GetCreatedJavaVMs = (GetCreatedJavaVMs_t)
 564     dlsym(libjvm, "JNI_GetCreatedJavaVMs");
 565     if (ifn->GetCreatedJavaVMs == NULL) {
 566         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
 567         return JNI_FALSE;
 568     }
 569 
 570     return JNI_TRUE;
 571 }
 572 
 573 /*
 574  * Compute the name of the executable
 575  *
 576  * In order to re-exec securely we need the absolute path of the
 577  * executable. On Solaris getexecname(3c) may not return an absolute
 578  * path so we use dladdr to get the filename of the executable and
 579  * then use realpath to derive an absolute path. From Solaris 9
 580  * onwards the filename returned in DL_info structure from dladdr is
 581  * an absolute pathname so technically realpath isn't required.
 582  * On Linux we read the executable name from /proc/self/exe.
 583  * As a fallback, and for platforms other than Solaris and Linux,
 584  * we use FindExecName to compute the executable name.
 585  */
 586 const char*
 587 SetExecname(char **argv)
 588 {
 589     char* exec_path = NULL;
 590     {
 591         Dl_info dlinfo;
 592 
 593 #ifdef STATIC_BUILD
 594         void *fptr;
 595         fptr = (void *)&SetExecname;
 596 #else
 597         int (*fptr)();
 598         fptr = (int (*)())dlsym(RTLD_DEFAULT, "main");
 599 #endif
 600         if (fptr == NULL) {
 601             JLI_ReportErrorMessage(DLL_ERROR3, dlerror());
 602             return JNI_FALSE;
 603         }
 604 
 605         if (dladdr((void*)fptr, &dlinfo)) {
 606             char *resolved = (char*)JLI_MemAlloc(PATH_MAX+1);
 607             if (resolved != NULL) {
 608                 exec_path = realpath(dlinfo.dli_fname, resolved);
 609                 if (exec_path == NULL) {
 610                     JLI_MemFree(resolved);
 611                 }
 612             }
 613         }
 614     }
 615     if (exec_path == NULL) {
 616         exec_path = FindExecName(argv[0]);
 617     }
 618     execname = exec_path;
 619     return exec_path;
 620 }
 621 
 622 /*
 623  * BSD's implementation of CounterGet()
 624  */
 625 int64_t
 626 CounterGet()
 627 {
 628     struct timeval tv;
 629     gettimeofday(&tv, NULL);
 630     return (tv.tv_sec * 1000) + tv.tv_usec;
 631 }
 632 
 633 
 634 /* --- Splash Screen shared library support --- */
 635 
 636 static JavaVM* SetJavaVMValue()
 637 {
 638     JavaVM * jvm = NULL;
 639 
 640     // The handle is good for both the launcher and the libosxapp.dylib
 641     void * handle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
 642     if (handle) {
 643         typedef JavaVM* (*JLI_GetJavaVMInstance_t)();
 644 
 645         JLI_GetJavaVMInstance_t JLI_GetJavaVMInstance =
 646             (JLI_GetJavaVMInstance_t)dlsym(handle,
 647                     "JLI_GetJavaVMInstance");
 648         if (JLI_GetJavaVMInstance) {
 649             jvm = JLI_GetJavaVMInstance();
 650         }
 651 
 652         if (jvm) {
 653             typedef void (*OSXAPP_SetJavaVM_t)(JavaVM*);
 654 
 655             OSXAPP_SetJavaVM_t OSXAPP_SetJavaVM =
 656                 (OSXAPP_SetJavaVM_t)dlsym(handle, "OSXAPP_SetJavaVM");
 657             if (OSXAPP_SetJavaVM) {
 658                 OSXAPP_SetJavaVM(jvm);
 659             } else {
 660                 jvm = NULL;
 661             }
 662         }
 663 
 664         dlclose(handle);
 665     }
 666 
 667     return jvm;
 668 }
 669 
 670 static const char* SPLASHSCREEN_SO = JNI_LIB_NAME("splashscreen");
 671 
 672 static void* hSplashLib = NULL;
 673 
 674 void* SplashProcAddress(const char* name) {
 675     if (!hSplashLib) {
 676         char jrePath[PATH_MAX];
 677         if (!GetJREPath(jrePath, sizeof(jrePath), JNI_FALSE)) {
 678             JLI_ReportErrorMessage(JRE_ERROR1);
 679             return NULL;
 680         }
 681 
 682         char splashPath[PATH_MAX];
 683         const int ret = JLI_Snprintf(splashPath, sizeof(splashPath),
 684                 "%s/lib/%s", jrePath, SPLASHSCREEN_SO);
 685         if (ret >= (int)sizeof(splashPath)) {
 686             JLI_ReportErrorMessage(JRE_ERROR11);
 687             return NULL;
 688         }
 689         if (ret < 0) {
 690             JLI_ReportErrorMessage(JRE_ERROR13);
 691             return NULL;
 692         }
 693 
 694         hSplashLib = dlopen(splashPath, RTLD_LAZY | RTLD_GLOBAL);
 695         // It's OK if dlopen() fails. The splash screen library binary file
 696         // might have been stripped out from the JRE image to reduce its size
 697         // (e.g. on embedded platforms).
 698 
 699         if (hSplashLib) {
 700             if (!SetJavaVMValue()) {
 701                 dlclose(hSplashLib);
 702                 hSplashLib = NULL;
 703             }
 704         }
 705     }
 706     if (hSplashLib) {
 707         void* sym = dlsym(hSplashLib, name);
 708         return sym;
 709     } else {
 710         return NULL;
 711     }
 712 }
 713 
 714 void SplashFreeLibrary() {
 715     if (hSplashLib) {
 716         dlclose(hSplashLib);
 717         hSplashLib = NULL;
 718     }
 719 }
 720 
 721 /*
 722  * Block current thread and continue execution in a new thread
 723  */
 724 int
 725 ContinueInNewThread0(int (JNICALL *continuation)(void *), jlong stack_size, void * args) {
 726     int rslt;
 727     pthread_t tid;
 728     pthread_attr_t attr;
 729     pthread_attr_init(&attr);
 730     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
 731 
 732     if (stack_size > 0) {
 733       pthread_attr_setstacksize(&attr, stack_size);
 734     }
 735     pthread_attr_setguardsize(&attr, 0); // no pthread guard page on java threads
 736 
 737     if (pthread_create(&tid, &attr, (void *(*)(void*))continuation, (void*)args) == 0) {
 738       void * tmp;
 739       pthread_join(tid, &tmp);
 740       rslt = (int)(intptr_t)tmp;
 741     } else {
 742      /*
 743       * Continue execution in current thread if for some reason (e.g. out of
 744       * memory/LWP)  a new thread can't be created. This will likely fail
 745       * later in continuation as JNI_CreateJavaVM needs to create quite a
 746       * few new threads, anyway, just give it a try..
 747       */
 748       rslt = continuation(args);
 749     }
 750 
 751     pthread_attr_destroy(&attr);
 752     return rslt;
 753 }
 754 
 755 void SetJavaLauncherPlatformProps() {
 756    /* Linux only */
 757 }
 758 
 759 static JavaVM* jvmInstance = NULL;
 760 static jboolean sameThread = JNI_FALSE; /* start VM in current thread */
 761 
 762 /*
 763  * Note there is a callback on this function from the splashscreen logic,
 764  * this as well SetJavaVMValue() needs to be simplified.
 765  */
 766 JavaVM*
 767 JLI_GetJavaVMInstance()
 768 {
 769     return jvmInstance;
 770 }
 771 
 772 void
 773 RegisterThread()
 774 {
 775     objc_registerThreadWithCollector();
 776 }
 777 
 778 static void
 779 SetXDockArgForAWT(const char *arg)
 780 {
 781     char envVar[80];
 782     if (strstr(arg, "-Xdock:name=") == arg) {
 783         /*
 784          * The APP_NAME_<pid> environment variable is used to pass
 785          * an application name as specified with the -Xdock:name command
 786          * line option from Java launcher code to the AWT code in order
 787          * to assign this name to the app's dock tile on the Mac.
 788          * The _<pid> part is added to avoid collisions with child processes.
 789          *
 790          * WARNING: This environment variable is an implementation detail and
 791          * isn't meant for use outside of the core platform. The mechanism for
 792          * passing this information from Java launcher to other modules may
 793          * change drastically between update release, and it may even be
 794          * removed or replaced with another mechanism.
 795          *
 796          * NOTE: It is used by SWT, and JavaFX.
 797          */
 798         snprintf(envVar, sizeof(envVar), "APP_NAME_%d", getpid());
 799         setenv(envVar, (arg + 12), 1);
 800     }
 801 
 802     if (strstr(arg, "-Xdock:icon=") == arg) {
 803         /*
 804          * The APP_ICON_<pid> environment variable is used to pass
 805          * an application icon as specified with the -Xdock:icon command
 806          * line option from Java launcher code to the AWT code in order
 807          * to assign this icon to the app's dock tile on the Mac.
 808          * The _<pid> part is added to avoid collisions with child processes.
 809          *
 810          * WARNING: This environment variable is an implementation detail and
 811          * isn't meant for use outside of the core platform. The mechanism for
 812          * passing this information from Java launcher to other modules may
 813          * change drastically between update release, and it may even be
 814          * removed or replaced with another mechanism.
 815          *
 816          * NOTE: It is used by SWT, and JavaFX.
 817          */
 818         snprintf(envVar, sizeof(envVar), "APP_ICON_%d", getpid());
 819         setenv(envVar, (arg + 12), 1);
 820     }
 821 }
 822 
 823 static void
 824 SetMainClassForAWT(JNIEnv *env, jclass mainClass) {
 825     jclass classClass = NULL;
 826     NULL_CHECK(classClass = FindBootStrapClass(env, "java/lang/Class"));
 827 
 828     jmethodID getCanonicalNameMID = NULL;
 829     NULL_CHECK(getCanonicalNameMID = (*env)->GetMethodID(env, classClass, "getCanonicalName", "()Ljava/lang/String;"));
 830 
 831     jstring mainClassString = (*env)->CallObjectMethod(env, mainClass, getCanonicalNameMID);
 832     if ((*env)->ExceptionCheck(env)) {
 833         /*
 834          * Clears all errors caused by getCanonicalName() on the mainclass and
 835          * leaves the JAVA_MAIN_CLASS__<pid> empty.
 836          */
 837         (*env)->ExceptionClear(env);
 838         return;
 839     }
 840 
 841     const char *mainClassName = NULL;
 842     NULL_CHECK(mainClassName = (*env)->GetStringUTFChars(env, mainClassString, NULL));
 843 
 844     char envVar[80];
 845     /*
 846      * The JAVA_MAIN_CLASS_<pid> environment variable is used to pass
 847      * the name of a Java class whose main() method is invoked by
 848      * the Java launcher code to start the application, to the AWT code
 849      * in order to assign the name to the Apple menu bar when the app
 850      * is active on the Mac.
 851      * The _<pid> part is added to avoid collisions with child processes.
 852      *
 853      * WARNING: This environment variable is an implementation detail and
 854      * isn't meant for use outside of the core platform. The mechanism for
 855      * passing this information from Java launcher to other modules may
 856      * change drastically between update release, and it may even be
 857      * removed or replaced with another mechanism.
 858      *
 859      * NOTE: It is used by SWT, and JavaFX.
 860      */
 861     snprintf(envVar, sizeof(envVar), "JAVA_MAIN_CLASS_%d", getpid());
 862     setenv(envVar, mainClassName, 1);
 863 
 864     (*env)->ReleaseStringUTFChars(env, mainClassString, mainClassName);
 865 }
 866 
 867 void
 868 SetXStartOnFirstThreadArg()
 869 {
 870     // XXX: BEGIN HACK
 871     // short circuit hack for <https://bugs.eclipse.org/bugs/show_bug.cgi?id=211625>
 872     // need a way to get AWT/Swing apps launched when spawned from Eclipse,
 873     // which currently has no UI to not pass the -XstartOnFirstThread option
 874     if (getenv("HACK_IGNORE_START_ON_FIRST_THREAD") != NULL) return;
 875     // XXX: END HACK
 876 
 877     sameThread = JNI_TRUE;
 878     // Set a variable that tells us we started on the main thread.
 879     // This is used by the AWT during startup. (See LWCToolkit.m)
 880     char envVar[80];
 881     snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
 882     setenv(envVar, "1", 1);
 883 }
 884 
 885 // MacOSX we may continue in the same thread
 886 int
 887 JVMInit(InvocationFunctions* ifn, jlong threadStackSize,
 888                  int argc, char **argv,
 889                  int mode, char *what, int ret) {
 890     if (sameThread) {
 891         JLI_TraceLauncher("In same thread\n");
 892         // need to block this thread against the main thread
 893         // so signals get caught correctly
 894         __block int rslt = 0;
 895         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 896         {
 897             NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock: ^{
 898                 JavaMainArgs args;
 899                 args.argc = argc;
 900                 args.argv = argv;
 901                 args.mode = mode;
 902                 args.what = what;
 903                 args.ifn  = *ifn;
 904                 rslt = JavaMain(&args);
 905             }];
 906 
 907             /*
 908              * We cannot use dispatch_sync here, because it blocks the main dispatch queue.
 909              * Using the main NSRunLoop allows the dispatch queue to run properly once
 910              * SWT (or whatever toolkit this is needed for) kicks off it's own NSRunLoop
 911              * and starts running.
 912              */
 913             [op performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:YES];
 914         }
 915         [pool drain];
 916         return rslt;
 917     } else {
 918         return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
 919     }
 920 }
 921 
 922 /*
 923  * Note the jvmInstance must be initialized first before entering into
 924  * ShowSplashScreen, as there is a callback into the JLI_GetJavaVMInstance.
 925  */
 926 void PostJVMInit(JNIEnv *env, jclass mainClass, JavaVM *vm) {
 927     jvmInstance = vm;
 928     SetMainClassForAWT(env, mainClass);
 929     CHECK_EXCEPTION_RETURN();
 930     ShowSplashScreen();
 931 }
 932 
 933 jboolean
 934 ProcessPlatformOption(const char* arg)
 935 {
 936     if (JLI_StrCmp(arg, "-XstartOnFirstThread") == 0) {
 937        SetXStartOnFirstThreadArg();
 938        return JNI_TRUE;
 939     } else if (JLI_StrCCmp(arg, "-Xdock:") == 0) {
 940        SetXDockArgForAWT(arg);
 941        return JNI_TRUE;
 942     }
 943     // arguments we know not
 944     return JNI_FALSE;
 945 }