|
1 /* |
|
2 * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 #include "java.h" |
|
27 #include "jvm_md.h" |
|
28 #include <dirent.h> |
|
29 #include <dlfcn.h> |
|
30 #include <fcntl.h> |
|
31 #include <inttypes.h> |
|
32 #include <stdio.h> |
|
33 #include <string.h> |
|
34 #include <stdlib.h> |
|
35 #include <sys/stat.h> |
|
36 #include <unistd.h> |
|
37 #include <sys/types.h> |
|
38 #include "manifest_info.h" |
|
39 |
|
40 |
|
41 #define JVM_DLL "libjvm.so" |
|
42 #define JAVA_DLL "libjava.so" |
|
43 #ifdef AIX |
|
44 #define LD_LIBRARY_PATH "LIBPATH" |
|
45 #else |
|
46 #define LD_LIBRARY_PATH "LD_LIBRARY_PATH" |
|
47 #endif |
|
48 |
|
49 /* help jettison the LD_LIBRARY_PATH settings in the future */ |
|
50 #ifndef SETENV_REQUIRED |
|
51 #define SETENV_REQUIRED |
|
52 #endif |
|
53 |
|
54 #ifdef __solaris__ |
|
55 # include <sys/systeminfo.h> |
|
56 # include <sys/elf.h> |
|
57 # include <stdio.h> |
|
58 #endif |
|
59 |
|
60 /* |
|
61 * Flowchart of launcher execs and options processing on unix |
|
62 * |
|
63 * The selection of the proper vm shared library to open depends on |
|
64 * several classes of command line options, including vm "flavor" |
|
65 * options (-client, -server). |
|
66 * The vm selection options are not passed to the running |
|
67 * virtual machine; they must be screened out by the launcher. |
|
68 * |
|
69 * The version specification (if any) is processed first by the |
|
70 * platform independent routine SelectVersion. This may result in |
|
71 * the exec of the specified launcher version. |
|
72 * |
|
73 * Previously the launcher modified the LD_LIBRARY_PATH appropriately for the |
|
74 * desired data model path, regardless if data models matched or not. The |
|
75 * launcher subsequently exec'ed the desired executable, in order to make the |
|
76 * LD_LIBRARY_PATH path available, for the runtime linker. |
|
77 * |
|
78 * Now, in most cases,the launcher will dlopen the target libjvm.so. All |
|
79 * required libraries are loaded by the runtime linker, using the |
|
80 * $RPATH/$ORIGIN baked into the shared libraries at compile time. Therefore, |
|
81 * in most cases, the launcher will only exec, if the data models are |
|
82 * mismatched, and will not set any environment variables, regardless of the |
|
83 * data models. |
|
84 * |
|
85 * However, if the environment contains a LD_LIBRARY_PATH, this will cause the |
|
86 * launcher to inspect the LD_LIBRARY_PATH. The launcher will check |
|
87 * a. if the LD_LIBRARY_PATH's first component is the path to the desired |
|
88 * libjvm.so |
|
89 * b. if any other libjvm.so is found in any of the paths. |
|
90 * If case b is true, then the launcher will set the LD_LIBRARY_PATH to the |
|
91 * desired JRE and reexec, in order to propagate the environment. |
|
92 * |
|
93 * Main |
|
94 * (incoming argv) |
|
95 * | |
|
96 * \|/ |
|
97 * CreateExecutionEnvironment |
|
98 * (determines desired data model) |
|
99 * | |
|
100 * | |
|
101 * \|/ |
|
102 * Have Desired Model ? --> NO --> Exit(with error) |
|
103 * | |
|
104 * | |
|
105 * \|/ |
|
106 * YES |
|
107 * | |
|
108 * | |
|
109 * \|/ |
|
110 * CheckJvmType |
|
111 * (removes -client, -server, etc.) |
|
112 * | |
|
113 * | |
|
114 * \|/ |
|
115 * TranslateDashJArgs... |
|
116 * (Prepare to pass args to vm) |
|
117 * | |
|
118 * | |
|
119 * \|/ |
|
120 * ParseArguments |
|
121 * | |
|
122 * | |
|
123 * \|/ |
|
124 * RequiresSetenv |
|
125 * Is LD_LIBRARY_PATH |
|
126 * and friends set ? --> NO --> Continue |
|
127 * YES |
|
128 * | |
|
129 * | |
|
130 * \|/ |
|
131 * Path is desired JRE ? YES --> Continue |
|
132 * NO |
|
133 * | |
|
134 * | |
|
135 * \|/ |
|
136 * Paths have well known |
|
137 * jvm paths ? --> NO --> Error/Exit |
|
138 * YES |
|
139 * | |
|
140 * | |
|
141 * \|/ |
|
142 * Does libjvm.so exist |
|
143 * in any of them ? --> NO --> Continue |
|
144 * YES |
|
145 * | |
|
146 * | |
|
147 * \|/ |
|
148 * Set the LD_LIBRARY_PATH |
|
149 * | |
|
150 * | |
|
151 * \|/ |
|
152 * Re-exec |
|
153 * | |
|
154 * | |
|
155 * \|/ |
|
156 * Main |
|
157 */ |
|
158 |
|
159 /* Store the name of the executable once computed */ |
|
160 static char *execname = NULL; |
|
161 |
|
162 /* |
|
163 * execname accessor from other parts of platform dependent logic |
|
164 */ |
|
165 const char * |
|
166 GetExecName() { |
|
167 return execname; |
|
168 } |
|
169 |
|
170 #ifdef SETENV_REQUIRED |
|
171 static jboolean |
|
172 JvmExists(const char *path) { |
|
173 char tmp[PATH_MAX + 1]; |
|
174 struct stat statbuf; |
|
175 JLI_Snprintf(tmp, PATH_MAX, "%s/%s", path, JVM_DLL); |
|
176 if (stat(tmp, &statbuf) == 0) { |
|
177 return JNI_TRUE; |
|
178 } |
|
179 return JNI_FALSE; |
|
180 } |
|
181 /* |
|
182 * contains a lib/{server,client}/libjvm.so ? |
|
183 */ |
|
184 static jboolean |
|
185 ContainsLibJVM(const char *env) { |
|
186 /* the usual suspects */ |
|
187 char clientPattern[] = "lib/client"; |
|
188 char serverPattern[] = "lib/server"; |
|
189 char *envpath; |
|
190 char *path; |
|
191 jboolean clientPatternFound; |
|
192 jboolean serverPatternFound; |
|
193 |
|
194 /* fastest path */ |
|
195 if (env == NULL) { |
|
196 return JNI_FALSE; |
|
197 } |
|
198 |
|
199 /* to optimize for time, test if any of our usual suspects are present. */ |
|
200 clientPatternFound = JLI_StrStr(env, clientPattern) != NULL; |
|
201 serverPatternFound = JLI_StrStr(env, serverPattern) != NULL; |
|
202 if (clientPatternFound == JNI_FALSE && serverPatternFound == JNI_FALSE) { |
|
203 return JNI_FALSE; |
|
204 } |
|
205 |
|
206 /* |
|
207 * we have a suspicious path component, check if it contains a libjvm.so |
|
208 */ |
|
209 envpath = JLI_StringDup(env); |
|
210 for (path = JLI_StrTok(envpath, ":"); path != NULL; path = JLI_StrTok(NULL, ":")) { |
|
211 if (clientPatternFound && JLI_StrStr(path, clientPattern) != NULL) { |
|
212 if (JvmExists(path)) { |
|
213 JLI_MemFree(envpath); |
|
214 return JNI_TRUE; |
|
215 } |
|
216 } |
|
217 if (serverPatternFound && JLI_StrStr(path, serverPattern) != NULL) { |
|
218 if (JvmExists(path)) { |
|
219 JLI_MemFree(envpath); |
|
220 return JNI_TRUE; |
|
221 } |
|
222 } |
|
223 } |
|
224 JLI_MemFree(envpath); |
|
225 return JNI_FALSE; |
|
226 } |
|
227 |
|
228 /* |
|
229 * Test whether the environment variable needs to be set, see flowchart. |
|
230 */ |
|
231 static jboolean |
|
232 RequiresSetenv(const char *jvmpath) { |
|
233 char jpath[PATH_MAX + 1]; |
|
234 char *llp; |
|
235 char *dmllp = NULL; |
|
236 char *p; /* a utility pointer */ |
|
237 |
|
238 #ifdef AIX |
|
239 /* We always have to set the LIBPATH on AIX because ld doesn't support $ORIGIN. */ |
|
240 return JNI_TRUE; |
|
241 #endif |
|
242 |
|
243 llp = getenv("LD_LIBRARY_PATH"); |
|
244 #ifdef __solaris__ |
|
245 dmllp = getenv("LD_LIBRARY_PATH_64"); |
|
246 #endif /* __solaris__ */ |
|
247 /* no environment variable is a good environment variable */ |
|
248 if (llp == NULL && dmllp == NULL) { |
|
249 return JNI_FALSE; |
|
250 } |
|
251 #ifdef __linux |
|
252 /* |
|
253 * On linux, if a binary is running as sgid or suid, glibc sets |
|
254 * LD_LIBRARY_PATH to the empty string for security purposes. (In contrast, |
|
255 * on Solaris the LD_LIBRARY_PATH variable for a privileged binary does not |
|
256 * lose its settings; but the dynamic linker does apply more scrutiny to the |
|
257 * path.) The launcher uses the value of LD_LIBRARY_PATH to prevent an exec |
|
258 * loop, here and further downstream. Therefore, if we are running sgid or |
|
259 * suid, this function's setting of LD_LIBRARY_PATH will be ineffective and |
|
260 * we should case a return from the calling function. Getting the right |
|
261 * libraries will be handled by the RPATH. In reality, this check is |
|
262 * redundant, as the previous check for a non-null LD_LIBRARY_PATH will |
|
263 * return back to the calling function forthwith, it is left here to safe |
|
264 * guard against any changes, in the glibc's existing security policy. |
|
265 */ |
|
266 if ((getgid() != getegid()) || (getuid() != geteuid())) { |
|
267 return JNI_FALSE; |
|
268 } |
|
269 #endif /* __linux */ |
|
270 |
|
271 /* |
|
272 * Prevent recursions. Since LD_LIBRARY_PATH is the one which will be set by |
|
273 * previous versions of the JRE, thus it is the only path that matters here. |
|
274 * So we check to see if the desired JRE is set. |
|
275 */ |
|
276 JLI_StrNCpy(jpath, jvmpath, PATH_MAX); |
|
277 p = JLI_StrRChr(jpath, '/'); |
|
278 *p = '\0'; |
|
279 if (llp != NULL && JLI_StrNCmp(llp, jpath, JLI_StrLen(jpath)) == 0) { |
|
280 return JNI_FALSE; |
|
281 } |
|
282 |
|
283 /* scrutinize all the paths further */ |
|
284 if (llp != NULL && ContainsLibJVM(llp)) { |
|
285 return JNI_TRUE; |
|
286 } |
|
287 if (dmllp != NULL && ContainsLibJVM(dmllp)) { |
|
288 return JNI_TRUE; |
|
289 } |
|
290 return JNI_FALSE; |
|
291 } |
|
292 #endif /* SETENV_REQUIRED */ |
|
293 |
|
294 void |
|
295 CreateExecutionEnvironment(int *pargc, char ***pargv, |
|
296 char jrepath[], jint so_jrepath, |
|
297 char jvmpath[], jint so_jvmpath, |
|
298 char jvmcfg[], jint so_jvmcfg) { |
|
299 |
|
300 char * jvmtype = NULL; |
|
301 int argc = *pargc; |
|
302 char **argv = *pargv; |
|
303 |
|
304 #ifdef SETENV_REQUIRED |
|
305 jboolean mustsetenv = JNI_FALSE; |
|
306 char *runpath = NULL; /* existing effective LD_LIBRARY_PATH setting */ |
|
307 char* new_runpath = NULL; /* desired new LD_LIBRARY_PATH string */ |
|
308 char* newpath = NULL; /* path on new LD_LIBRARY_PATH */ |
|
309 char* lastslash = NULL; |
|
310 char** newenvp = NULL; /* current environment */ |
|
311 size_t new_runpath_size; |
|
312 #endif /* SETENV_REQUIRED */ |
|
313 |
|
314 /* Compute/set the name of the executable */ |
|
315 SetExecname(*pargv); |
|
316 |
|
317 /* Check to see if the jvmpath exists */ |
|
318 /* Find out where the JRE is that we will be using. */ |
|
319 if (!GetJREPath(jrepath, so_jrepath, JNI_FALSE)) { |
|
320 JLI_ReportErrorMessage(JRE_ERROR1); |
|
321 exit(2); |
|
322 } |
|
323 JLI_Snprintf(jvmcfg, so_jvmcfg, "%s%slib%sjvm.cfg", |
|
324 jrepath, FILESEP, FILESEP); |
|
325 /* Find the specified JVM type */ |
|
326 if (ReadKnownVMs(jvmcfg, JNI_FALSE) < 1) { |
|
327 JLI_ReportErrorMessage(CFG_ERROR7); |
|
328 exit(1); |
|
329 } |
|
330 |
|
331 jvmpath[0] = '\0'; |
|
332 jvmtype = CheckJvmType(pargc, pargv, JNI_FALSE); |
|
333 if (JLI_StrCmp(jvmtype, "ERROR") == 0) { |
|
334 JLI_ReportErrorMessage(CFG_ERROR9); |
|
335 exit(4); |
|
336 } |
|
337 |
|
338 if (!GetJVMPath(jrepath, jvmtype, jvmpath, so_jvmpath)) { |
|
339 JLI_ReportErrorMessage(CFG_ERROR8, jvmtype, jvmpath); |
|
340 exit(4); |
|
341 } |
|
342 /* |
|
343 * we seem to have everything we need, so without further ado |
|
344 * we return back, otherwise proceed to set the environment. |
|
345 */ |
|
346 #ifdef SETENV_REQUIRED |
|
347 mustsetenv = RequiresSetenv(jvmpath); |
|
348 JLI_TraceLauncher("mustsetenv: %s\n", mustsetenv ? "TRUE" : "FALSE"); |
|
349 |
|
350 if (mustsetenv == JNI_FALSE) { |
|
351 return; |
|
352 } |
|
353 #else |
|
354 return; |
|
355 #endif /* SETENV_REQUIRED */ |
|
356 |
|
357 #ifdef SETENV_REQUIRED |
|
358 if (mustsetenv) { |
|
359 /* |
|
360 * We will set the LD_LIBRARY_PATH as follows: |
|
361 * |
|
362 * o $JVMPATH (directory portion only) |
|
363 * o $JRE/lib |
|
364 * o $JRE/../lib |
|
365 * |
|
366 * followed by the user's previous effective LD_LIBRARY_PATH, if |
|
367 * any. |
|
368 */ |
|
369 |
|
370 runpath = getenv(LD_LIBRARY_PATH); |
|
371 |
|
372 /* runpath contains current effective LD_LIBRARY_PATH setting */ |
|
373 { /* New scope to declare local variable */ |
|
374 char *new_jvmpath = JLI_StringDup(jvmpath); |
|
375 new_runpath_size = ((runpath != NULL) ? JLI_StrLen(runpath) : 0) + |
|
376 2 * JLI_StrLen(jrepath) + |
|
377 #ifdef AIX |
|
378 /* On AIX we additionally need 'jli' in the path because ld doesn't support $ORIGIN. */ |
|
379 JLI_StrLen(jrepath) + JLI_StrLen("/lib//jli:") + |
|
380 #endif |
|
381 JLI_StrLen(new_jvmpath) + 52; |
|
382 new_runpath = JLI_MemAlloc(new_runpath_size); |
|
383 newpath = new_runpath + JLI_StrLen(LD_LIBRARY_PATH "="); |
|
384 |
|
385 |
|
386 /* |
|
387 * Create desired LD_LIBRARY_PATH value for target data model. |
|
388 */ |
|
389 { |
|
390 /* remove the name of the .so from the JVM path */ |
|
391 lastslash = JLI_StrRChr(new_jvmpath, '/'); |
|
392 if (lastslash) |
|
393 *lastslash = '\0'; |
|
394 |
|
395 sprintf(new_runpath, LD_LIBRARY_PATH "=" |
|
396 "%s:" |
|
397 "%s/lib:" |
|
398 #ifdef AIX |
|
399 "%s/lib/jli:" /* Needed on AIX because ld doesn't support $ORIGIN. */ |
|
400 #endif |
|
401 "%s/../lib", |
|
402 new_jvmpath, |
|
403 jrepath, |
|
404 #ifdef AIX |
|
405 jrepath, |
|
406 #endif |
|
407 jrepath |
|
408 ); |
|
409 |
|
410 JLI_MemFree(new_jvmpath); |
|
411 |
|
412 /* |
|
413 * Check to make sure that the prefix of the current path is the |
|
414 * desired environment variable setting, though the RequiresSetenv |
|
415 * checks if the desired runpath exists, this logic does a more |
|
416 * comprehensive check. |
|
417 */ |
|
418 if (runpath != NULL && |
|
419 JLI_StrNCmp(newpath, runpath, JLI_StrLen(newpath)) == 0 && |
|
420 (runpath[JLI_StrLen(newpath)] == 0 || |
|
421 runpath[JLI_StrLen(newpath)] == ':')) { |
|
422 JLI_MemFree(new_runpath); |
|
423 return; |
|
424 } |
|
425 } |
|
426 } |
|
427 |
|
428 /* |
|
429 * Place the desired environment setting onto the prefix of |
|
430 * LD_LIBRARY_PATH. Note that this prevents any possible infinite |
|
431 * loop of execv() because we test for the prefix, above. |
|
432 */ |
|
433 if (runpath != 0) { |
|
434 /* ensure storage for runpath + colon + NULL */ |
|
435 if ((JLI_StrLen(runpath) + 1 + 1) > new_runpath_size) { |
|
436 JLI_ReportErrorMessageSys(JRE_ERROR11); |
|
437 exit(1); |
|
438 } |
|
439 JLI_StrCat(new_runpath, ":"); |
|
440 JLI_StrCat(new_runpath, runpath); |
|
441 } |
|
442 |
|
443 if (putenv(new_runpath) != 0) { |
|
444 /* problem allocating memory; LD_LIBRARY_PATH not set properly */ |
|
445 exit(1); |
|
446 } |
|
447 |
|
448 /* |
|
449 * Unix systems document that they look at LD_LIBRARY_PATH only |
|
450 * once at startup, so we have to re-exec the current executable |
|
451 * to get the changed environment variable to have an effect. |
|
452 */ |
|
453 |
|
454 newenvp = environ; |
|
455 } |
|
456 #endif /* SETENV_REQUIRED */ |
|
457 { |
|
458 char *newexec = execname; |
|
459 JLI_TraceLauncher("TRACER_MARKER:About to EXEC\n"); |
|
460 (void) fflush(stdout); |
|
461 (void) fflush(stderr); |
|
462 #ifdef SETENV_REQUIRED |
|
463 if (mustsetenv) { |
|
464 execve(newexec, argv, newenvp); |
|
465 } else { |
|
466 execv(newexec, argv); |
|
467 } |
|
468 #else /* !SETENV_REQUIRED */ |
|
469 execv(newexec, argv); |
|
470 #endif /* SETENV_REQUIRED */ |
|
471 JLI_ReportErrorMessageSys(JRE_ERROR4, newexec); |
|
472 } |
|
473 exit(1); |
|
474 } |
|
475 |
|
476 |
|
477 static jboolean |
|
478 GetJVMPath(const char *jrepath, const char *jvmtype, |
|
479 char *jvmpath, jint jvmpathsize) |
|
480 { |
|
481 struct stat s; |
|
482 |
|
483 if (JLI_StrChr(jvmtype, '/')) { |
|
484 JLI_Snprintf(jvmpath, jvmpathsize, "%s/" JVM_DLL, jvmtype); |
|
485 } else { |
|
486 JLI_Snprintf(jvmpath, jvmpathsize, "%s/lib/%s/" JVM_DLL, jrepath, jvmtype); |
|
487 } |
|
488 |
|
489 JLI_TraceLauncher("Does `%s' exist ... ", jvmpath); |
|
490 |
|
491 if (stat(jvmpath, &s) == 0) { |
|
492 JLI_TraceLauncher("yes.\n"); |
|
493 return JNI_TRUE; |
|
494 } else { |
|
495 JLI_TraceLauncher("no.\n"); |
|
496 return JNI_FALSE; |
|
497 } |
|
498 } |
|
499 |
|
500 /* |
|
501 * Find path to JRE based on .exe's location or registry settings. |
|
502 */ |
|
503 static jboolean |
|
504 GetJREPath(char *path, jint pathsize, jboolean speculative) |
|
505 { |
|
506 char libjava[MAXPATHLEN]; |
|
507 struct stat s; |
|
508 |
|
509 if (GetApplicationHome(path, pathsize)) { |
|
510 /* Is JRE co-located with the application? */ |
|
511 JLI_Snprintf(libjava, sizeof(libjava), "%s/lib/" JAVA_DLL, path); |
|
512 if (access(libjava, F_OK) == 0) { |
|
513 JLI_TraceLauncher("JRE path is %s\n", path); |
|
514 return JNI_TRUE; |
|
515 } |
|
516 /* ensure storage for path + /jre + NULL */ |
|
517 if ((JLI_StrLen(path) + 4 + 1) > (size_t) pathsize) { |
|
518 JLI_TraceLauncher("Insufficient space to store JRE path\n"); |
|
519 return JNI_FALSE; |
|
520 } |
|
521 /* Does the app ship a private JRE in <apphome>/jre directory? */ |
|
522 JLI_Snprintf(libjava, sizeof(libjava), "%s/jre/lib/" JAVA_DLL, path); |
|
523 if (access(libjava, F_OK) == 0) { |
|
524 JLI_StrCat(path, "/jre"); |
|
525 JLI_TraceLauncher("JRE path is %s\n", path); |
|
526 return JNI_TRUE; |
|
527 } |
|
528 } |
|
529 |
|
530 if (GetApplicationHomeFromDll(path, pathsize)) { |
|
531 JLI_Snprintf(libjava, sizeof(libjava), "%s/lib/" JAVA_DLL, path); |
|
532 if (stat(libjava, &s) == 0) { |
|
533 JLI_TraceLauncher("JRE path is %s\n", path); |
|
534 return JNI_TRUE; |
|
535 } |
|
536 } |
|
537 |
|
538 if (!speculative) |
|
539 JLI_ReportErrorMessage(JRE_ERROR8 JAVA_DLL); |
|
540 return JNI_FALSE; |
|
541 } |
|
542 |
|
543 jboolean |
|
544 LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) |
|
545 { |
|
546 void *libjvm; |
|
547 |
|
548 JLI_TraceLauncher("JVM path is %s\n", jvmpath); |
|
549 |
|
550 libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL); |
|
551 if (libjvm == NULL) { |
|
552 #if defined(__solaris__) && defined(__sparc) && !defined(_LP64) /* i.e. 32-bit sparc */ |
|
553 FILE * fp; |
|
554 Elf32_Ehdr elf_head; |
|
555 int count; |
|
556 int location; |
|
557 |
|
558 fp = fopen(jvmpath, "r"); |
|
559 if (fp == NULL) { |
|
560 JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); |
|
561 return JNI_FALSE; |
|
562 } |
|
563 |
|
564 /* read in elf header */ |
|
565 count = fread((void*)(&elf_head), sizeof(Elf32_Ehdr), 1, fp); |
|
566 fclose(fp); |
|
567 if (count < 1) { |
|
568 JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); |
|
569 return JNI_FALSE; |
|
570 } |
|
571 |
|
572 /* |
|
573 * Check for running a server vm (compiled with -xarch=v8plus) |
|
574 * on a stock v8 processor. In this case, the machine type in |
|
575 * the elf header would not be included the architecture list |
|
576 * provided by the isalist command, which is turn is gotten from |
|
577 * sysinfo. This case cannot occur on 64-bit hardware and thus |
|
578 * does not have to be checked for in binaries with an LP64 data |
|
579 * model. |
|
580 */ |
|
581 if (elf_head.e_machine == EM_SPARC32PLUS) { |
|
582 char buf[257]; /* recommended buffer size from sysinfo man |
|
583 page */ |
|
584 long length; |
|
585 char* location; |
|
586 |
|
587 length = sysinfo(SI_ISALIST, buf, 257); |
|
588 if (length > 0) { |
|
589 location = JLI_StrStr(buf, "sparcv8plus "); |
|
590 if (location == NULL) { |
|
591 JLI_ReportErrorMessage(JVM_ERROR3); |
|
592 return JNI_FALSE; |
|
593 } |
|
594 } |
|
595 } |
|
596 #endif |
|
597 JLI_ReportErrorMessage(DLL_ERROR1, __LINE__); |
|
598 JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); |
|
599 return JNI_FALSE; |
|
600 } |
|
601 |
|
602 ifn->CreateJavaVM = (CreateJavaVM_t) |
|
603 dlsym(libjvm, "JNI_CreateJavaVM"); |
|
604 if (ifn->CreateJavaVM == NULL) { |
|
605 JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); |
|
606 return JNI_FALSE; |
|
607 } |
|
608 |
|
609 ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t) |
|
610 dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs"); |
|
611 if (ifn->GetDefaultJavaVMInitArgs == NULL) { |
|
612 JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); |
|
613 return JNI_FALSE; |
|
614 } |
|
615 |
|
616 ifn->GetCreatedJavaVMs = (GetCreatedJavaVMs_t) |
|
617 dlsym(libjvm, "JNI_GetCreatedJavaVMs"); |
|
618 if (ifn->GetCreatedJavaVMs == NULL) { |
|
619 JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror()); |
|
620 return JNI_FALSE; |
|
621 } |
|
622 |
|
623 return JNI_TRUE; |
|
624 } |
|
625 |
|
626 /* |
|
627 * Compute the name of the executable |
|
628 * |
|
629 * In order to re-exec securely we need the absolute path of the |
|
630 * executable. On Solaris getexecname(3c) may not return an absolute |
|
631 * path so we use dladdr to get the filename of the executable and |
|
632 * then use realpath to derive an absolute path. From Solaris 9 |
|
633 * onwards the filename returned in DL_info structure from dladdr is |
|
634 * an absolute pathname so technically realpath isn't required. |
|
635 * On Linux we read the executable name from /proc/self/exe. |
|
636 * As a fallback, and for platforms other than Solaris and Linux, |
|
637 * we use FindExecName to compute the executable name. |
|
638 */ |
|
639 const char* |
|
640 SetExecname(char **argv) |
|
641 { |
|
642 char* exec_path = NULL; |
|
643 #if defined(__solaris__) |
|
644 { |
|
645 Dl_info dlinfo; |
|
646 int (*fptr)(); |
|
647 |
|
648 fptr = (int (*)())dlsym(RTLD_DEFAULT, "main"); |
|
649 if (fptr == NULL) { |
|
650 JLI_ReportErrorMessage(DLL_ERROR3, dlerror()); |
|
651 return JNI_FALSE; |
|
652 } |
|
653 |
|
654 if (dladdr((void*)fptr, &dlinfo)) { |
|
655 char *resolved = (char*)JLI_MemAlloc(PATH_MAX+1); |
|
656 if (resolved != NULL) { |
|
657 exec_path = realpath(dlinfo.dli_fname, resolved); |
|
658 if (exec_path == NULL) { |
|
659 JLI_MemFree(resolved); |
|
660 } |
|
661 } |
|
662 } |
|
663 } |
|
664 #elif defined(__linux__) |
|
665 { |
|
666 const char* self = "/proc/self/exe"; |
|
667 char buf[PATH_MAX+1]; |
|
668 int len = readlink(self, buf, PATH_MAX); |
|
669 if (len >= 0) { |
|
670 buf[len] = '\0'; /* readlink(2) doesn't NUL terminate */ |
|
671 exec_path = JLI_StringDup(buf); |
|
672 } |
|
673 } |
|
674 #else /* !__solaris__ && !__linux__ */ |
|
675 { |
|
676 /* Not implemented */ |
|
677 } |
|
678 #endif |
|
679 |
|
680 if (exec_path == NULL) { |
|
681 exec_path = FindExecName(argv[0]); |
|
682 } |
|
683 execname = exec_path; |
|
684 return exec_path; |
|
685 } |
|
686 |
|
687 /* --- Splash Screen shared library support --- */ |
|
688 static const char* SPLASHSCREEN_SO = JNI_LIB_NAME("splashscreen"); |
|
689 static void* hSplashLib = NULL; |
|
690 |
|
691 void* SplashProcAddress(const char* name) { |
|
692 if (!hSplashLib) { |
|
693 int ret; |
|
694 char jrePath[MAXPATHLEN]; |
|
695 char splashPath[MAXPATHLEN]; |
|
696 |
|
697 if (!GetJREPath(jrePath, sizeof(jrePath), JNI_FALSE)) { |
|
698 JLI_ReportErrorMessage(JRE_ERROR1); |
|
699 return NULL; |
|
700 } |
|
701 ret = JLI_Snprintf(splashPath, sizeof(splashPath), "%s/lib/%s", |
|
702 jrePath, SPLASHSCREEN_SO); |
|
703 |
|
704 if (ret >= (int) sizeof(splashPath)) { |
|
705 JLI_ReportErrorMessage(JRE_ERROR11); |
|
706 return NULL; |
|
707 } |
|
708 if (ret < 0) { |
|
709 JLI_ReportErrorMessage(JRE_ERROR13); |
|
710 return NULL; |
|
711 } |
|
712 hSplashLib = dlopen(splashPath, RTLD_LAZY | RTLD_GLOBAL); |
|
713 JLI_TraceLauncher("Info: loaded %s\n", splashPath); |
|
714 } |
|
715 if (hSplashLib) { |
|
716 void* sym = dlsym(hSplashLib, name); |
|
717 return sym; |
|
718 } else { |
|
719 return NULL; |
|
720 } |
|
721 } |
|
722 |
|
723 void SplashFreeLibrary() { |
|
724 if (hSplashLib) { |
|
725 dlclose(hSplashLib); |
|
726 hSplashLib = NULL; |
|
727 } |
|
728 } |
|
729 |
|
730 /* |
|
731 * Block current thread and continue execution in a new thread |
|
732 */ |
|
733 int |
|
734 ContinueInNewThread0(int (JNICALL *continuation)(void *), jlong stack_size, void * args) { |
|
735 int rslt; |
|
736 #ifndef __solaris__ |
|
737 pthread_t tid; |
|
738 pthread_attr_t attr; |
|
739 pthread_attr_init(&attr); |
|
740 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); |
|
741 |
|
742 if (stack_size > 0) { |
|
743 pthread_attr_setstacksize(&attr, stack_size); |
|
744 } |
|
745 pthread_attr_setguardsize(&attr, 0); // no pthread guard page on java threads |
|
746 |
|
747 if (pthread_create(&tid, &attr, (void *(*)(void*))continuation, (void*)args) == 0) { |
|
748 void * tmp; |
|
749 pthread_join(tid, &tmp); |
|
750 rslt = (int)(intptr_t)tmp; |
|
751 } else { |
|
752 /* |
|
753 * Continue execution in current thread if for some reason (e.g. out of |
|
754 * memory/LWP) a new thread can't be created. This will likely fail |
|
755 * later in continuation as JNI_CreateJavaVM needs to create quite a |
|
756 * few new threads, anyway, just give it a try.. |
|
757 */ |
|
758 rslt = continuation(args); |
|
759 } |
|
760 |
|
761 pthread_attr_destroy(&attr); |
|
762 #else /* __solaris__ */ |
|
763 thread_t tid; |
|
764 long flags = 0; |
|
765 if (thr_create(NULL, stack_size, (void *(*)(void *))continuation, args, flags, &tid) == 0) { |
|
766 void * tmp; |
|
767 thr_join(tid, NULL, &tmp); |
|
768 rslt = (int)(intptr_t)tmp; |
|
769 } else { |
|
770 /* See above. Continue in current thread if thr_create() failed */ |
|
771 rslt = continuation(args); |
|
772 } |
|
773 #endif /* !__solaris__ */ |
|
774 return rslt; |
|
775 } |
|
776 |
|
777 /* Coarse estimation of number of digits assuming the worst case is a 64-bit pid. */ |
|
778 #define MAX_PID_STR_SZ 20 |
|
779 |
|
780 void SetJavaLauncherPlatformProps() { |
|
781 /* Linux only */ |
|
782 #ifdef __linux__ |
|
783 const char *substr = "-Dsun.java.launcher.pid="; |
|
784 char *pid_prop_str = (char *)JLI_MemAlloc(JLI_StrLen(substr) + MAX_PID_STR_SZ + 1); |
|
785 sprintf(pid_prop_str, "%s%d", substr, getpid()); |
|
786 AddOption(pid_prop_str, NULL); |
|
787 #endif /* __linux__ */ |
|
788 } |
|
789 |
|
790 int |
|
791 JVMInit(InvocationFunctions* ifn, jlong threadStackSize, |
|
792 int argc, char **argv, |
|
793 int mode, char *what, int ret) |
|
794 { |
|
795 ShowSplashScreen(); |
|
796 return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret); |
|
797 } |
|
798 |
|
799 void |
|
800 PostJVMInit(JNIEnv *env, jclass mainClass, JavaVM *vm) |
|
801 { |
|
802 // stubbed out for windows and *nixes. |
|
803 } |
|
804 |
|
805 void |
|
806 RegisterThread() |
|
807 { |
|
808 // stubbed out for windows and *nixes. |
|
809 } |
|
810 |
|
811 /* |
|
812 * on unix, we return a false to indicate this option is not applicable |
|
813 */ |
|
814 jboolean |
|
815 ProcessPlatformOption(const char *arg) |
|
816 { |
|
817 return JNI_FALSE; |
|
818 } |