jdk/src/macosx/bin/java_md_macosx.c
changeset 12047 320a714614e9
child 12527 22abaf748b5b
equal deleted inserted replaced
12046:378aa3362868 12047:320a714614e9
       
     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         snprintf(envVar, sizeof(envVar), "APP_NAME_%d", getpid());
       
   910         setenv(envVar, (arg + 12), 1);
       
   911     }
       
   912 
       
   913     if (strstr(arg, "-Xdock:icon=") == arg) {
       
   914         snprintf(envVar, sizeof(envVar), "APP_ICON_%d", getpid());
       
   915         setenv(envVar, (arg + 12), 1);
       
   916     }
       
   917 }
       
   918 
       
   919 static void
       
   920 SetMainClassForAWT(JNIEnv *env, jclass mainClass) {
       
   921     jclass classClass = NULL;
       
   922     NULL_CHECK(classClass = FindBootStrapClass(env, "java/lang/Class"));
       
   923 
       
   924     jmethodID getCanonicalNameMID = NULL;
       
   925     NULL_CHECK(getCanonicalNameMID = (*env)->GetMethodID(env, classClass, "getCanonicalName", "()Ljava/lang/String;"));
       
   926 
       
   927     jstring mainClassString = NULL;
       
   928     NULL_CHECK(mainClassString = (*env)->CallObjectMethod(env, mainClass, getCanonicalNameMID));
       
   929 
       
   930     const char *mainClassName = NULL;
       
   931     NULL_CHECK(mainClassName = (*env)->GetStringUTFChars(env, mainClassString, NULL));
       
   932 
       
   933     char envVar[80];
       
   934     snprintf(envVar, sizeof(envVar), "JAVA_MAIN_CLASS_%d", getpid());
       
   935     setenv(envVar, mainClassName, 1);
       
   936 
       
   937     (*env)->ReleaseStringUTFChars(env, mainClassString, mainClassName);
       
   938 }
       
   939 
       
   940 void
       
   941 SetXStartOnFirstThreadArg()
       
   942 {
       
   943     // XXX: BEGIN HACK
       
   944     // short circuit hack for <https://bugs.eclipse.org/bugs/show_bug.cgi?id=211625>
       
   945     // need a way to get AWT/Swing apps launched when spawned from Eclipse,
       
   946     // which currently has no UI to not pass the -XstartOnFirstThread option
       
   947     if (getenv("HACK_IGNORE_START_ON_FIRST_THREAD") != NULL) return;
       
   948     // XXX: END HACK
       
   949 
       
   950     sameThread = JNI_TRUE;
       
   951     // Set a variable that tells us we started on the main thread.
       
   952     // This is used by the AWT during startup. (See awt.m)
       
   953     char envVar[80];
       
   954     snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
       
   955     setenv(envVar, "1", 1);
       
   956 }
       
   957 
       
   958 // MacOSX we may continue in the same thread
       
   959 int
       
   960 JVMInit(InvocationFunctions* ifn, jlong threadStackSize,
       
   961                  int argc, char **argv,
       
   962                  int mode, char *what, int ret) {
       
   963     if (sameThread) {
       
   964         JLI_TraceLauncher("In same thread\n");
       
   965         // need to block this thread against the main thread
       
   966         // so signals get caught correctly
       
   967         __block int rslt;
       
   968         dispatch_sync(dispatch_get_main_queue(), ^(void) {
       
   969             JavaMainArgs args;
       
   970             args.argc = argc;
       
   971             args.argv = argv;
       
   972             args.mode = mode;
       
   973             args.what = what;
       
   974             args.ifn  = *ifn;
       
   975             rslt = JavaMain((void*)&args);
       
   976         });
       
   977         return rslt;
       
   978     } else {
       
   979         return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
       
   980     }
       
   981 }
       
   982 
       
   983 /*
       
   984  * Note the jvmInstance must be initialized first before entering into
       
   985  * ShowSplashScreen, as there is a callback into the JLI_GetJavaVMInstance.
       
   986  */
       
   987 void PostJVMInit(JNIEnv *env, jstring mainClass, JavaVM *vm) {
       
   988     jvmInstance = vm;
       
   989     SetMainClassForAWT(env, mainClass);
       
   990     ShowSplashScreen();
       
   991 }
       
   992 
       
   993 jboolean
       
   994 ProcessPlatformOption(const char* arg)
       
   995 {
       
   996     if (JLI_StrCmp(arg, "-XstartOnFirstThread") == 0) {
       
   997        SetXStartOnFirstThreadArg();
       
   998        return JNI_TRUE;
       
   999     } else if (JLI_StrCCmp(arg, "-Xdock:") == 0) {
       
  1000        SetXDockArgForAWT(arg);
       
  1001        return JNI_TRUE;
       
  1002     }
       
  1003     // arguments we know not
       
  1004     return JNI_FALSE;
       
  1005 }