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