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