| /* | |
| * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| #include "java.h" | |
| #include "jvm_md.h" | |
| #include <dirent.h> | |
| #include <dlfcn.h> | |
| #include <fcntl.h> | |
| #include <inttypes.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <stdlib.h> | |
| #include <sys/stat.h> | |
| #include <unistd.h> | |
| #include <sys/types.h> | |
| #include "manifest_info.h" | |
| #define JVM_DLL "libjvm.so" | |
| #define JAVA_DLL "libjava.so" | |
| #ifdef AIX | |
| #define LD_LIBRARY_PATH "LIBPATH" | |
| #else | |
| #define LD_LIBRARY_PATH "LD_LIBRARY_PATH" | |
| #endif | |
| /* help jettison the LD_LIBRARY_PATH settings in the future */ | |
| #ifndef SETENV_REQUIRED | |
| #define SETENV_REQUIRED | |
| #endif | |
| /* | |
| * Following is the high level flow of the launcher | |
| * code residing in the common java.c and this | |
| * unix specific java_md file: | |
| * | |
| * - JLI_Launch function, which is the entry point | |
| * to the launcher, calls CreateExecutionEnvironment. | |
| * | |
| * - CreateExecutionEnvironment does the following | |
| * (not necessarily in this order): | |
| * - determines the relevant JVM type that | |
| * needs to be ultimately created | |
| * - determines the path and asserts the presence | |
| * of libjava and relevant libjvm library | |
| * - removes any JVM selection options from the | |
| * arguments that were passed to the launcher | |
| * | |
| * - CreateExecutionEnvironment then determines (by calling | |
| * RequiresSetenv function) if LD_LIBRARY_PATH environment | |
| * variable needs to be set/updated. | |
| * - If LD_LIBRARY_PATH needs to be set/updated, | |
| * then CreateExecutionEnvironment exec()s | |
| * the current process with the appropriate value | |
| * for LD_LIBRARY_PATH. | |
| * - Else if LD_LIBRARY_PATH need not be set or | |
| * updated, then CreateExecutionEnvironment | |
| * returns back. | |
| * | |
| * - If CreateExecutionEnvironment exec()ed the process | |
| * in the previous step, then the code control for the | |
| * process will again start from the process' entry | |
| * point and JLI_Launch is thus re-invoked and the | |
| * same above sequence of code flow repeats again. | |
| * During this "recursive" call into CreateExecutionEnvironment, | |
| * the implementation of the check for LD_LIBRARY_PATH | |
| * will realize that no further exec() is required and | |
| * the control will return back from CreateExecutionEnvironment. | |
| * | |
| * - The control returns back from CreateExecutionEnvironment | |
| * to JLI_Launch. | |
| * | |
| * - JLI_Launch then invokes LoadJavaVM which dlopen()s | |
| * the JVM library and asserts the presence of | |
| * JNI Invocation Functions "JNI_CreateJavaVM", | |
| * "JNI_GetDefaultJavaVMInitArgs" and | |
| * "JNI_GetCreatedJavaVMs" in that library. It then | |
| * sets internal function pointers in the launcher to | |
| * point to those functions. | |
| * | |
| * - JLI_Launch then translates any -J options by | |
| * invoking TranslateApplicationArgs. | |
| * | |
| * - JLI_Launch then invokes ParseArguments to | |
| * parse/process the launcher arguments. | |
| * | |
| * - JLI_Launch then ultimately calls JVMInit. | |
| * | |
| * - JVMInit invokes ShowSplashScreen which displays | |
| * a splash screen for the application, if applicable. | |
| * | |
| * - JVMInit then creates a new thread (T2), in the | |
| * current process, and invokes JavaMain function | |
| * in that new thread. The current thread (T1) then | |
| * waits for the newly launched thread (T2) to complete. | |
| * | |
| * - JavaMain function, in thread T2, before launching | |
| * the application, invokes PostJVMInit. | |
| * | |
| * - PostJVMInit is a no-op and returns back. | |
| * | |
| * - Control then returns back from PostJVMInit into JavaMain, | |
| * which then loads the application's main class and invokes | |
| * the relevant main() Java method. | |
| * | |
| * - JavaMain, in thread T2, then returns back an integer | |
| * result and thread T2 execution ends here. | |
| * | |
| * - The thread T1 in JVMInit, which is waiting on T2 to | |
| * complete, receives the integer result and then propagates | |
| * it as a return value all the way out of the | |
| * JLI_Launch function. | |
| */ | |
| /* Store the name of the executable once computed */ | |
| static char *execname = NULL; | |
| /* | |
| * execname accessor from other parts of platform dependent logic | |
| */ | |
| const char * | |
| GetExecName() { | |
| return execname; | |
| } | |
| #ifdef SETENV_REQUIRED | |
| static jboolean | |
| JvmExists(const char *path) { | |
| char tmp[PATH_MAX + 1]; | |
| struct stat statbuf; | |
| JLI_Snprintf(tmp, PATH_MAX, "%s/%s", path, JVM_DLL); | |
| if (stat(tmp, &statbuf) == 0) { | |
| return JNI_TRUE; | |
| } | |
| return JNI_FALSE; | |
| } | |
| /* | |
| * contains a lib/{server,client}/libjvm.so ? | |
| */ | |
| static jboolean | |
| ContainsLibJVM(const char *env) { | |
| /* the usual suspects */ | |
| char clientPattern[] = "lib/client"; | |
| char serverPattern[] = "lib/server"; | |
| char *envpath; | |
| char *path; | |
| char* save_ptr = NULL; | |
| jboolean clientPatternFound; | |
| jboolean serverPatternFound; | |
| /* fastest path */ | |
| if (env == NULL) { | |
| return JNI_FALSE; | |
| } | |
| /* to optimize for time, test if any of our usual suspects are present. */ | |
| clientPatternFound = JLI_StrStr(env, clientPattern) != NULL; | |
| serverPatternFound = JLI_StrStr(env, serverPattern) != NULL; | |
| if (clientPatternFound == JNI_FALSE && serverPatternFound == JNI_FALSE) { | |
| return JNI_FALSE; | |
| } | |
| /* | |
| * we have a suspicious path component, check if it contains a libjvm.so | |
| */ | |
| envpath = JLI_StringDup(env); | |
| for (path = strtok_r(envpath, ":", &save_ptr); path != NULL; path = strtok_r(NULL, ":", &save_ptr)) { | |
| if (clientPatternFound && JLI_StrStr(path, clientPattern) != NULL) { | |
| if (JvmExists(path)) { | |
| JLI_MemFree(envpath); | |
| return JNI_TRUE; | |
| } | |
| } | |
| if (serverPatternFound && JLI_StrStr(path, serverPattern) != NULL) { | |
| if (JvmExists(path)) { | |
| JLI_MemFree(envpath); | |
| return JNI_TRUE; | |
| } | |
| } | |
| } | |
| JLI_MemFree(envpath); | |
| return JNI_FALSE; | |
| } | |
| /* | |
| * Test whether the LD_LIBRARY_PATH environment variable needs to be set. | |
| */ | |
| static jboolean | |
| RequiresSetenv(const char *jvmpath) { | |
| char jpath[PATH_MAX + 1]; | |
| char *llp; | |
| char *p; /* a utility pointer */ | |
| #ifdef MUSL_LIBC | |
| /* | |
| * The musl library loader requires LD_LIBRARY_PATH to be set in order | |
| * to correctly resolve the dependency libjava.so has on libjvm.so. | |
| */ | |
| return JNI_TRUE; | |
| #endif | |
| #ifdef AIX | |
| /* We always have to set the LIBPATH on AIX because ld doesn't support $ORIGIN. */ | |
| return JNI_TRUE; | |
| #endif | |
| llp = getenv("LD_LIBRARY_PATH"); | |
| /* no environment variable is a good environment variable */ | |
| if (llp == NULL) { | |
| return JNI_FALSE; | |
| } | |
| #ifdef __linux | |
| /* | |
| * On linux, if a binary is running as sgid or suid, glibc sets | |
| * LD_LIBRARY_PATH to the empty string for security purposes. (In contrast, | |
| * on Solaris the LD_LIBRARY_PATH variable for a privileged binary does not | |
| * lose its settings; but the dynamic linker does apply more scrutiny to the | |
| * path.) The launcher uses the value of LD_LIBRARY_PATH to prevent an exec | |
| * loop, here and further downstream. Therefore, if we are running sgid or | |
| * suid, this function's setting of LD_LIBRARY_PATH will be ineffective and | |
| * we should case a return from the calling function. Getting the right | |
| * libraries will be handled by the RPATH. In reality, this check is | |
| * redundant, as the previous check for a non-null LD_LIBRARY_PATH will | |
| * return back to the calling function forthwith, it is left here to safe | |
| * guard against any changes, in the glibc's existing security policy. | |
| */ | |
| if ((getgid() != getegid()) || (getuid() != geteuid())) { | |
| return JNI_FALSE; | |
| } | |
| #endif /* __linux */ | |
| /* | |
| * Prevent recursions. Since LD_LIBRARY_PATH is the one which will be set by | |
| * previous versions of the JDK, thus it is the only path that matters here. | |
| * So we check to see if the desired JDK is set. | |
| */ | |
| JLI_StrNCpy(jpath, jvmpath, PATH_MAX); | |
| p = JLI_StrRChr(jpath, '/'); | |
| *p = '\0'; | |
| if (llp != NULL && JLI_StrNCmp(llp, jpath, JLI_StrLen(jpath)) == 0) { | |
| return JNI_FALSE; | |
| } | |
| /* scrutinize all the paths further */ | |
| if (llp != NULL && ContainsLibJVM(llp)) { | |
| return JNI_TRUE; | |
| } | |
| return JNI_FALSE; | |
| } | |
| #endif /* SETENV_REQUIRED */ | |
| void | |
| CreateExecutionEnvironment(int *pargc, char ***pargv, | |
| char jdkroot[], jint so_jdkroot, | |
| char jvmpath[], jint so_jvmpath, | |
| char jvmcfg[], jint so_jvmcfg) { | |
| if (JLI_IsStaticallyLinked()) { | |
| // With static builds, all JDK and VM natives are statically linked | |
| // with the launcher executable. No need to manipulate LD_LIBRARY_PATH | |
| // by adding <jdk_path>/lib and etc. The 'jrepath', 'jvmpath' and | |
| // 'jvmcfg' are not used by the caller for static builds. Simply return. | |
| return; | |
| } | |
| char * jvmtype = NULL; | |
| char **argv = *pargv; | |
| #ifdef SETENV_REQUIRED | |
| jboolean mustsetenv = JNI_FALSE; | |
| char *runpath = NULL; /* existing effective LD_LIBRARY_PATH setting */ | |
| char* new_runpath = NULL; /* desired new LD_LIBRARY_PATH string */ | |
| char* newpath = NULL; /* path on new LD_LIBRARY_PATH */ | |
| char* lastslash = NULL; | |
| char** newenvp = NULL; /* current environment */ | |
| size_t new_runpath_size; | |
| #endif /* SETENV_REQUIRED */ | |
| /* Compute/set the name of the executable */ | |
| SetExecname(*pargv); | |
| /* Check to see if the jvmpath exists */ | |
| /* Find out where the JDK is that we will be using. */ | |
| if (!GetJDKInstallRoot(jdkroot, so_jdkroot, JNI_FALSE)) { | |
| JLI_ReportErrorMessage(LAUNCHER_ERROR1); | |
| exit(2); | |
| } | |
| JLI_Snprintf(jvmcfg, so_jvmcfg, "%s%slib%sjvm.cfg", | |
| jdkroot, FILESEP, FILESEP); | |
| /* Find the specified JVM type */ | |
| if (ReadKnownVMs(jvmcfg, JNI_FALSE) < 1) { | |
| JLI_ReportErrorMessage(CFG_ERROR7); | |
| exit(1); | |
| } | |
| jvmpath[0] = '\0'; | |
| jvmtype = CheckJvmType(pargc, pargv, JNI_FALSE); | |
| if (JLI_StrCmp(jvmtype, "ERROR") == 0) { | |
| JLI_ReportErrorMessage(CFG_ERROR9); | |
| exit(4); | |
| } | |
| if (!GetJVMPath(jdkroot, jvmtype, jvmpath, so_jvmpath)) { | |
| JLI_ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath); | |
| exit(4); | |
| } | |
| /* | |
| * we seem to have everything we need, so without further ado | |
| * we return back, otherwise proceed to set the environment. | |
| */ | |
| #ifdef SETENV_REQUIRED | |
| mustsetenv = RequiresSetenv(jvmpath); | |
| JLI_TraceLauncher("mustsetenv: %s\n", mustsetenv ? "TRUE" : "FALSE"); | |
| if (mustsetenv == JNI_FALSE) { | |
| return; | |
| } | |
| #else | |
| return; | |
| #endif /* SETENV_REQUIRED */ | |
| #ifdef SETENV_REQUIRED | |
| if (mustsetenv) { | |
| /* | |
| * We will set the LD_LIBRARY_PATH as follows: | |
| * | |
| * o $JVMPATH (directory portion only) | |
| * o $JDK/lib | |
| * | |
| * followed by the user's previous effective LD_LIBRARY_PATH, if | |
| * any. | |
| */ | |
| runpath = getenv(LD_LIBRARY_PATH); | |
| /* runpath contains current effective LD_LIBRARY_PATH setting */ | |
| { /* New scope to declare local variable */ | |
| char *new_jvmpath = JLI_StringDup(jvmpath); | |
| new_runpath_size = ((runpath != NULL) ? JLI_StrLen(runpath) : 0) + | |
| 2 * JLI_StrLen(jdkroot) + | |
| JLI_StrLen(new_jvmpath) + 52; | |
| new_runpath = JLI_MemAlloc(new_runpath_size); | |
| newpath = new_runpath + JLI_StrLen(LD_LIBRARY_PATH "="); | |
| /* | |
| * Create desired LD_LIBRARY_PATH value for target data model. | |
| */ | |
| { | |
| /* remove the name of the .so from the JVM path */ | |
| lastslash = JLI_StrRChr(new_jvmpath, '/'); | |
| if (lastslash) | |
| *lastslash = '\0'; | |
| snprintf(new_runpath, new_runpath_size, LD_LIBRARY_PATH "=" | |
| "%s:" | |
| "%s/lib", | |
| new_jvmpath, | |
| jdkroot | |
| ); | |
| JLI_MemFree(new_jvmpath); | |
| /* | |
| * Check to make sure that the prefix of the current path is the | |
| * desired environment variable setting, though the RequiresSetenv | |
| * checks if the desired runpath exists, this logic does a more | |
| * comprehensive check. | |
| */ | |
| if (runpath != NULL && | |
| JLI_StrNCmp(newpath, runpath, JLI_StrLen(newpath)) == 0 && | |
| (runpath[JLI_StrLen(newpath)] == 0 || | |
| runpath[JLI_StrLen(newpath)] == ':')) { | |
| JLI_MemFree(new_runpath); | |
| return; | |
| } | |
| } | |
| } | |
| /* | |
| * Place the desired environment setting onto the prefix of | |
| * LD_LIBRARY_PATH. Note that this prevents any possible infinite | |
| * loop of execv() because we test for the prefix, above. | |
| */ | |
| if (runpath != 0) { | |
| /* ensure storage for runpath + colon + NULL */ | |
| if ((JLI_StrLen(runpath) + 1 + 1) > new_runpath_size) { | |
| JLI_ReportErrorMessageSys(LAUNCHER_ERROR3); | |
| exit(1); | |
| } | |
| JLI_StrCat(new_runpath, ":"); | |
| JLI_StrCat(new_runpath, runpath); | |
| } | |
| if (putenv(new_runpath) != 0) { | |
| /* problem allocating memory; LD_LIBRARY_PATH not set properly */ | |
| exit(1); | |
| } | |
| /* | |
| * Unix systems document that they look at LD_LIBRARY_PATH only | |
| * once at startup, so we have to re-exec the current executable | |
| * to get the changed environment variable to have an effect. | |
| */ | |
| newenvp = environ; | |
| } | |
| #endif /* SETENV_REQUIRED */ | |
| { | |
| char *newexec = execname; | |
| JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n"); | |
| (void) fflush(stdout); | |
| (void) fflush(stderr); | |
| #ifdef SETENV_REQUIRED | |
| if (mustsetenv) { | |
| execve(newexec, argv, newenvp); | |
| } else { | |
| execv(newexec, argv); | |
| } | |
| #else /* !SETENV_REQUIRED */ | |
| execv(newexec, argv); | |
| #endif /* SETENV_REQUIRED */ | |
| JLI_ReportErrorMessageSys(LAUNCHER_ERROR4, newexec); | |
| } | |
| exit(1); | |
| } | |
| static jboolean | |
| GetJVMPath(const char *jdkroot, const char *jvmtype, | |
| char *jvmpath, jint jvmpathsize) | |
| { | |
| struct stat s; | |
| if (JLI_StrChr(jvmtype, '/')) { | |
| JLI_Snprintf(jvmpath, jvmpathsize, "%s/" JVM_DLL, jvmtype); | |
| } else { | |
| JLI_Snprintf(jvmpath, jvmpathsize, "%s/lib/%s/" JVM_DLL, jdkroot, jvmtype); | |
| } | |
| JLI_TraceLauncher("Does `%s' exist ... ", jvmpath); | |
| if (stat(jvmpath, &s) == 0) { | |
| JLI_TraceLauncher("yes.\n"); | |
| return JNI_TRUE; | |
| } else { | |
| JLI_TraceLauncher("no.\n"); | |
| return JNI_FALSE; | |
| } | |
| } | |
| /* | |
| * Find path to the JDK installation root | |
| */ | |
| static jboolean | |
| GetJDKInstallRoot(char *path, jint pathsize, jboolean speculative) | |
| { | |
| char libjava[MAXPATHLEN]; | |
| struct stat s; | |
| JLI_TraceLauncher("Attempt to get JDK installation root from launcher executable path\n"); | |
| if (GetApplicationHome(path, pathsize)) { | |
| /* Is JDK co-located with the application? */ | |
| JLI_Snprintf(libjava, sizeof(libjava), "%s/lib/" JAVA_DLL, path); | |
| if (access(libjava, F_OK) == 0) { | |
| JLI_TraceLauncher("JDK installation root path is %s\n", path); | |
| return JNI_TRUE; | |
| } | |
| } | |
| JLI_TraceLauncher("Attempt to get JDK installation root path from shared lib of the image\n"); | |
| if (GetApplicationHomeFromDll(path, pathsize)) { | |
| JLI_Snprintf(libjava, sizeof(libjava), "%s/lib/" JAVA_DLL, path); | |
| if (stat(libjava, &s) == 0) { | |
| JLI_TraceLauncher("JDK installation root path is %s\n", path); | |
| return JNI_TRUE; | |
| } | |
| } | |
| #if defined(AIX) | |
| /* at least on AIX try also the LD_LIBRARY_PATH / LIBPATH */ | |
| if (GetApplicationHomeFromLibpath(path, pathsize)) { | |
| JLI_Snprintf(libjava, sizeof(libjava), "%s/lib/" JAVA_DLL, path); | |
| if (stat(libjava, &s) == 0) { | |
| JLI_TraceLauncher("JDK installation root path is %s\n", path); | |
| return JNI_TRUE; | |
| } | |
| } | |
| #endif | |
| if (!speculative) | |
| JLI_ReportErrorMessage(LAUNCHER_ERROR2 JAVA_DLL); | |
| return JNI_FALSE; | |
| } | |
| jboolean | |
| LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) | |
| { | |
| void *libjvm; | |
| JLI_TraceLauncher("JVM path is %s\n", jvmpath); | |
| if (JLI_IsStaticallyLinked()) { | |
| libjvm = dlopen(NULL, RTLD_NOW + RTLD_GLOBAL); | |
| } else { | |
| libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL); | |
| if (libjvm == NULL) { | |
| JLI_ReportErrorMessage(DLL_ERROR1, __LINE__); | |
| JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); | |
| return JNI_FALSE; | |
| } | |
| } | |
| ifn->CreateJavaVM = (CreateJavaVM_t) | |
| dlsym(libjvm, "JNI_CreateJavaVM"); | |
| if (ifn->CreateJavaVM == NULL) { | |
| JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); | |
| return JNI_FALSE; | |
| } | |
| ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t) | |
| dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs"); | |
| if (ifn->GetDefaultJavaVMInitArgs == NULL) { | |
| JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); | |
| return JNI_FALSE; | |
| } | |
| ifn->GetCreatedJavaVMs = (GetCreatedJavaVMs_t) | |
| dlsym(libjvm, "JNI_GetCreatedJavaVMs"); | |
| if (ifn->GetCreatedJavaVMs == NULL) { | |
| JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); | |
| return JNI_FALSE; | |
| } | |
| return JNI_TRUE; | |
| } | |
| /* | |
| * Compute the name of the executable | |
| * | |
| * In order to re-exec securely we need the absolute path of the | |
| * executable. On Solaris getexecname(3c) may not return an absolute | |
| * path so we use dladdr to get the filename of the executable and | |
| * then use realpath to derive an absolute path. From Solaris 9 | |
| * onwards the filename returned in DL_info structure from dladdr is | |
| * an absolute pathname so technically realpath isn't required. | |
| * On Linux we read the executable name from /proc/self/exe. | |
| * As a fallback, and for platforms other than Solaris and Linux, | |
| * we use FindExecName to compute the executable name. | |
| */ | |
| const char* | |
| SetExecname(char **argv) | |
| { | |
| char* exec_path = NULL; | |
| #if defined(__linux__) | |
| { | |
| const char* self = "/proc/self/exe"; | |
| char buf[PATH_MAX+1]; | |
| int len = readlink(self, buf, PATH_MAX); | |
| if (len >= 0) { | |
| buf[len] = '\0'; /* readlink(2) doesn't NUL terminate */ | |
| exec_path = JLI_StringDup(buf); | |
| } | |
| } | |
| #else /* !__linux__ */ | |
| { | |
| /* Not implemented */ | |
| } | |
| #endif | |
| if (exec_path == NULL) { | |
| exec_path = FindExecName(argv[0]); | |
| } | |
| execname = exec_path; | |
| return exec_path; | |
| } | |
| /* --- Splash Screen shared library support --- */ | |
| static const char* SPLASHSCREEN_SO = JNI_LIB_NAME("splashscreen"); | |
| static void* hSplashLib = NULL; | |
| void* SplashProcAddress(const char* name) { | |
| if (!hSplashLib) { | |
| int ret; | |
| char jdkRoot[MAXPATHLEN]; | |
| char splashPath[MAXPATHLEN]; | |
| if (JLI_IsStaticallyLinked()) { | |
| hSplashLib = dlopen(NULL, RTLD_LAZY); | |
| } else { | |
| if (!GetJDKInstallRoot(jdkRoot, sizeof(jdkRoot), JNI_FALSE)) { | |
| JLI_ReportErrorMessage(LAUNCHER_ERROR1); | |
| return NULL; | |
| } | |
| ret = JLI_Snprintf(splashPath, sizeof(splashPath), "%s/lib/%s", | |
| jdkRoot, SPLASHSCREEN_SO); | |
| if (ret >= (int) sizeof(splashPath)) { | |
| JLI_ReportErrorMessage(LAUNCHER_ERROR3); | |
| return NULL; | |
| } | |
| if (ret < 0) { | |
| JLI_ReportErrorMessage(LAUNCHER_ERROR5); | |
| return NULL; | |
| } | |
| hSplashLib = dlopen(splashPath, RTLD_LAZY | RTLD_GLOBAL); | |
| } | |
| JLI_TraceLauncher("Info: loaded %s\n", splashPath); | |
| } | |
| if (hSplashLib) { | |
| void* sym = dlsym(hSplashLib, name); | |
| return sym; | |
| } else { | |
| return NULL; | |
| } | |
| } | |
| /* | |
| * Signature adapter for pthread_create() or thr_create(). | |
| */ | |
| static void* ThreadJavaMain(void* args) { | |
| return (void*)(intptr_t)JavaMain(args); | |
| } | |
| static size_t adjustStackSize(size_t stack_size) { | |
| long page_size = sysconf(_SC_PAGESIZE); | |
| if (stack_size % page_size == 0) { | |
| return stack_size; | |
| } else { | |
| long pages = stack_size / page_size; | |
| // Ensure we don't go over limit | |
| if (stack_size <= SIZE_MAX - page_size) { | |
| pages++; | |
| } | |
| return page_size * pages; | |
| } | |
| } | |
| /* | |
| * Block current thread and continue execution in a new thread. | |
| */ | |
| int | |
| CallJavaMainInNewThread(jlong stack_size, void* args) { | |
| int rslt; | |
| pthread_t tid; | |
| pthread_attr_t attr; | |
| pthread_attr_init(&attr); | |
| pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); | |
| size_t adjusted_stack_size; | |
| if (stack_size > 0) { | |
| if (pthread_attr_setstacksize(&attr, stack_size) == EINVAL) { | |
| // System may require stack size to be multiple of page size | |
| // Retry with adjusted value | |
| adjusted_stack_size = adjustStackSize(stack_size); | |
| if (adjusted_stack_size != (size_t) stack_size) { | |
| pthread_attr_setstacksize(&attr, adjusted_stack_size); | |
| } | |
| } | |
| } | |
| pthread_attr_setguardsize(&attr, 0); // no pthread guard page on java threads | |
| if (pthread_create(&tid, &attr, ThreadJavaMain, args) == 0) { | |
| void* tmp; | |
| pthread_join(tid, &tmp); | |
| rslt = (int)(intptr_t)tmp; | |
| } else { | |
| /* | |
| * Continue execution in current thread if for some reason (e.g. out of | |
| * memory/LWP) a new thread can't be created. This will likely fail | |
| * later in JavaMain as JNI_CreateJavaVM needs to create quite a | |
| * few new threads, anyway, just give it a try.. | |
| */ | |
| rslt = JavaMain(args); | |
| } | |
| pthread_attr_destroy(&attr); | |
| return rslt; | |
| } | |
| /* Coarse estimation of number of digits assuming the worst case is a 64-bit pid. */ | |
| #define MAX_PID_STR_SZ 20 | |
| int | |
| JVMInit(InvocationFunctions* ifn, jlong threadStackSize, | |
| int argc, char **argv, | |
| int mode, char *what, int ret) | |
| { | |
| ShowSplashScreen(); | |
| return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret); | |
| } | |
| void | |
| PostJVMInit(JNIEnv *env, jclass mainClass, JavaVM *vm) | |
| { | |
| // stubbed out for windows and *nixes. | |
| } | |
| void | |
| RegisterThread() | |
| { | |
| // stubbed out for windows and *nixes. | |
| } | |
| /* | |
| * on unix, we return a false to indicate this option is not applicable | |
| */ | |
| jboolean | |
| ProcessPlatformOption(const char *arg) | |
| { | |
| return JNI_FALSE; | |
| } |