GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
NativeLibrary.java
Go to the documentation of this file.
1/**
2 * Copyright 2011-2023 JogAmp Community. All rights reserved.
3 * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification, are
6 * permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
21 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 * The views and conclusions contained in the software and documentation are those of the
26 * authors and should not be interpreted as representing official policies, either expressed
27 * or implied, of JogAmp Community.
28 */
29
30package com.jogamp.common.os;
31
32import java.io.File;
33import java.io.IOException;
34import java.lang.reflect.Method;
35import java.net.URISyntaxException;
36import java.security.PrivilegedAction;
37import java.util.Arrays;
38import java.util.Iterator;
39import java.util.List;
40import java.util.StringTokenizer;
41
42import jogamp.common.os.BionicDynamicLinker32bitImpl;
43import jogamp.common.os.BionicDynamicLinker64BitImpl;
44import jogamp.common.os.MacOSXDynamicLinkerImpl;
45import jogamp.common.os.PlatformPropsImpl;
46import jogamp.common.os.PosixDynamicLinkerImpl;
47import jogamp.common.os.WindowsDynamicLinkerImpl;
48
49import com.jogamp.common.ExceptionUtils;
50import com.jogamp.common.util.ArrayHashSet;
51import com.jogamp.common.util.IOUtil;
52import com.jogamp.common.util.SecurityUtil;
53import com.jogamp.common.util.cache.TempJarCache;
54
55/**
56 * Provides low-level, relatively platform-independent access to
57 * shared ("native") libraries.
58 *
59 * The core library routines `System.load()` and `System.loadLibrary()`
60 * in general provide suitable functionality for applications using
61 * native code, but are not flexible enough to support certain kinds
62 * of glue code generation and deployment strategies.
63 *
64 * This class supports direct linking of native libraries to other shared
65 * objects not necessarily installed on the system (in particular,
66 * via the use of `dlopen(RTLD_GLOBAL)` on Unix platforms) as well as
67 * manual lookup of function names to support e.g. GlueGen's
68 * ProcAddressTable glue code generation style without additional
69 * supporting code needed in the generated library.
70 *
71 * ## System Search Resolution
72 * System search's behavior depends on `searchOSSystemPath` and `searchSystemPathFirst`.
73 *
74 * ### OS System Search
75 * - System search path direct lookup (absolute path)
76 * - Windows: `PATH`
77 * - MacOS: `DYLD_LIBRARY_PATH`
78 * - Unix: `LD_LIBRARY_PATH`
79 * - System search path implicit lookup (relative path)
80 * - OSX System search path direct lookup (absolute path)
81 * - `/Library/Frameworks/`
82 * - `/System/Library/Frameworks/`
83 *
84 * ### System Search
85 * - if `searchSystemPathFirst`
86 * - If `searchOSSystemPath`
87 * - Perform described `OS System Search` above
88 * - Java's ClassLoader `findLibrary` mechanism
89 * - Java's Java system library path property
90 * - `sun.boot.library.path`
91 * - if `!searchSystemPathFirst`
92 * - Java's Java system library path property
93 * - `sun.boot.library.path`
94 * - Java's ClassLoader `findLibrary` mechanism
95 * - If `searchOSSystemPath`
96 * - Perform described `OS System Search` above
97 *
98 * ## Native Library Search Resolution
99 * - Absolute path only, if given
100 * - JogAmp's optional primary search path from Java property `jogamp.primary.library.path`
101 * - path is separated via `File.pathseparator`
102 * - if `searchSystemPathFirst`
103 * - Perform described `System Search Resolution` above
104 * - Java's Java user library path property
105 * - `java.library.path`
106 * - Java's Java user current working directory
107 * - user: `user.dir`
108 * - user+fat: `user.dir` + File.separator + `natives` + File.separator + `PlatformPropsImpl.os_and_arch`
109 * - if `!searchSystemPathFirst`
110 * - Perform described `System Search Resolution` above
111 */
112public final class NativeLibrary implements DynamicLookupHelper {
113 private static final String[] prefixes;
114 private static final String[] suffixes;
115 private static final boolean isOSX;
116 private static String sys_env_lib_path_varname;
117
118 static {
119 // Instantiate dynamic linker implementation
120 switch (PlatformPropsImpl.OS_TYPE) {
121 case WINDOWS:
122 prefixes = new String[] { "" };
123 suffixes = new String[] { ".dll" };
124 sys_env_lib_path_varname = "PATH";
125 isOSX = false;
126 break;
127
128 case MACOS:
129 case IOS:
130 prefixes = new String[] { "lib" };
131 suffixes = new String[] { ".dylib" };
132 sys_env_lib_path_varname = "DYLD_LIBRARY_PATH";
133 isOSX = true;
134 break;
135
136 /*
137 case ANDROID:
138 case FREEBSD:
139 case SUNOS:
140 case HPUX:
141 case OPENKODE:
142 case LINUX: */
143 default:
144 prefixes = new String[] { "lib" };
145 suffixes = new String[] { ".so" };
146 sys_env_lib_path_varname = "LD_LIBRARY_PATH";
147 isOSX = false;
148 break;
149 }
150 }
151
152 /** Native Library Path Specification */
153 public static class LibPath {
154 /** Relative or absolute library path. */
155 public final String path;
156
157 /** True if path is an absolute path */
158 public final boolean isAbsolute;
159
160 /**
161 * True if directory of absolute library path shall be added to the linker search path.
162 *
163 * May not be supported on all systems.
164 *
165 * Supported OS: Windows.
166 *
167 * @see #searchPathPrepend
168 */
169 public final boolean addToSearchPath;
170
171 /**
172 * Search path prepend directories, separated by OS {@link File#pathSeparator}.
173 *
174 * @see #addToSearchPath
175 */
176 public final String searchPathPrepend;
177
178 /** Returns new instance with relative path in system linker search path */
179 public static LibPath createRelative(final String p) { return new LibPath(p, false, false, null); }
180
181 /** Returns new instance with absolute path in system linker search path */
182 public static LibPath createAbsolute(final String p) { return new LibPath(p, true, false, null); }
183
184 /**
185 * Returns new instance with absolute path not in system linker search path.
186 * @see #addToSearchPath
187 */
188 public static LibPath createExtra(final String p, final String searchPathPrepend) { return new LibPath(p, true, true, searchPathPrepend); }
189
190 private LibPath(final String _path, final boolean _isAbsolute, final boolean _addToSearchPath, final String _searchPathPrepend) {
191 path = _path;
192 isAbsolute = _isAbsolute;
193 addToSearchPath = _addToSearchPath;
194 searchPathPrepend = _searchPathPrepend;
195 }
196
197 @Override
198 public int hashCode() {
199 // 31 * x == (x << 5) - x
200 int hash = path.hashCode();
201 if(null != searchPathPrepend) {
202 hash = ((hash << 5) - hash) + searchPathPrepend.hashCode();
203 }
204 hash = ((hash << 5) - hash) + (isAbsolute? 1 : 0);
205 hash = ((hash << 5) - hash) + (addToSearchPath? 1 : 0);
206 return hash;
207 }
208
209 @Override
210 public boolean equals(final Object o) {
211 if(!(o instanceof LibPath)) {
212 return false;
213 }
214 final LibPath o2 = (LibPath)o;
215 return path.equals(o2.path) &&
216 isAbsolute == o2.isAbsolute &&
217 addToSearchPath == o2.addToSearchPath &&
218 ( ( searchPathPrepend == null && o2.searchPathPrepend == null ) ||
219 ( searchPathPrepend != null && o2.searchPathPrepend != null && searchPathPrepend.equals(o2.searchPathPrepend) ) );
220 }
221
222 @Override
223 public String toString() {
224 return "LibPath['"+path+"', "+(isAbsolute?"abs":"rel")+", "+(addToSearchPath?"xsp "+searchPathPrepend:"sys")+"]";
225 }
226 }
227
228 private final DynamicLinker dynLink;
229
230 // Platform-specific representation for the handle to the open
231 // library. This is an HMODULE on Windows and a void* (the result of
232 // a dlopen() call) on Unix and Mac OS X platforms.
233 private long libraryHandle;
234
235 // May as well keep around the path to the library we opened
236 private final LibPath libraryPath;
237
238 // Native library path of the opened native libraryHandle, maybe null
239 private final String nativeLibraryPath;
240
241 private final boolean global;
242
243 // Private constructor to prevent arbitrary instances from floating around
244 private NativeLibrary(final DynamicLinker dynLink, final long libraryHandle, final LibPath libraryPath, final boolean global, final String symbolName) {
245 this.dynLink = dynLink;
246 this.libraryHandle = libraryHandle;
247 this.libraryPath = libraryPath;
248 this.nativeLibraryPath = dynLink.lookupLibraryPathname(libraryHandle, symbolName);
249 this.global = global;
250 if (DEBUG) {
251 System.err.println("NativeLibrary.open(): Successfully loaded: " + this);
252 }
253 }
254
255 @Override
256 public final String toString() {
257 final String nlp_s = null != nativeLibraryPath ? ", native '"+nativeLibraryPath+"'" : "";
258 return "NativeLibrary[" + dynLink.getClass().getSimpleName() + ", path[given '" + libraryPath + "'"+nlp_s+"], 0x" +
259 Long.toHexString(libraryHandle) + ", global " + global + "]";
260 }
261
262 /**
263 * Returns the system's environment variable name used for the dynamic linker to resolve library locations, e.g.
264 * - Windows: PATH
265 * - MacOS: DYLD_LIBRARY_PATH
266 * - Unix: LD_LIBRARY_PATH
267 */
268 public static final String getSystemEnvLibraryPathVarname() { return sys_env_lib_path_varname; }
269
270 /**
271 * Returns a system paths separated with {@link File#pathSeparator}, from the {@link #getSystemEnvLibraryPathVarname()} variable.
272 */
273 public static final String getSystemEnvLibraryPaths() {
274 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
275 @Override
276 public String run() {
277 return System.getenv(getSystemEnvLibraryPathVarname());
278 }
279 });
280 }
281
282 /** Returns JogAmp's primary library path `jogamp.primary.library.path` separated with {@link File#pathSeparator}. */
283 public static final String getJogAmpPrimaryLibraryPaths() {
284 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
285 @Override
286 public String run() {
287 return System.getProperty("jogamp.primary.library.path");
288 }
289 });
290 }
291
292 /** Returns Java library system path `sun.boot.library.path` separated with {@link File#pathSeparator}. */
293 private static final String getJavaLibrarySystemPaths() {
294 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
295 @Override
296 public String run() {
297 final String p = System.getProperty("sun.boot.library.path");
298 if(null != p && !p.isEmpty()) {
299 return p;
300 }
301 return null;
302 }
303 });
304 }
305 /** Returns Java library user path `java.library.path` separated with {@link File#pathSeparator}. */
306 private static final String getJavaLibraryUserPaths() {
307 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
308 @Override
309 public String run() {
310 final String p = System.getProperty("java.library.path");
311 if(null != p && !p.isEmpty()) {
312 return p;
313 }
314 return null;
315 }
316 });
317 }
318
319 /** Returns Java library user path `user.dir` */
320 private static final String getJavaCurrentWorkingDir() {
321 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
322 @Override
323 public String run() {
324 return System.getProperty("user.dir");
325 }
326 });
327 }
328
329 /** Opens the given native library, assuming it has the same base
330 name on all platforms.
331 <p>
332 The {@code searchOSSystemPath} argument changes the behavior to
333 either use the default system path or not at all.
334 </p>
335 <p>
336 Assuming {@code searchOSSystemPath} is {@code true},
337 the {@code searchSystemPathFirst} argument changes the behavior to first
338 search the default system path rather than searching it last.
339 </p>
340 * @param libName library name, with or without prefix and suffix
341 * @param searchOSSystemPath if {@code true} library shall be searched in the system path <i>(default)</i>, otherwise {@code false}.
342 * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
343 * if {@code searchOSSystemPath} is {@code true} this includes the order of the OS system path as well.
344 * @param loader {@link ClassLoader} to locate the library
345 * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process.
346 * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded.
347 * @throws SecurityException if user is not granted access for the named library.
348 * @since 2.4.0
349 */
350 public static final NativeLibrary open(final String libName,
351 final boolean searchOSSystemPath,
352 final boolean searchSystemPathFirst,
353 final ClassLoader loader, final boolean global) throws SecurityException {
354 return open(libName, libName, libName, searchOSSystemPath, searchSystemPathFirst, loader, global, null);
355 }
356
357 /** Opens the given native library, assuming it has the same base
358 name on all platforms.
359 <p>
360 The {@code searchOSSystemPath} argument changes the behavior to
361 either use the default system path or not at all.
362 </p>
363 <p>
364 Assuming {@code searchOSSystemPath} is {@code true},
365 the {@code searchSystemPathFirst} argument changes the behavior to first
366 search the default system path rather than searching it last.
367 </p>
368 * @param libName library name, with or without prefix and suffix
369 * @param searchOSSystemPath if {@code true} library shall be searched in the OS system path <i>(default)</i>, otherwise {@code false}.
370 * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
371 * if {@code searchOSSystemPath} is {@code true} this includes the order of the OS system path as well.
372 * @param loader {@link ClassLoader} to locate the library
373 * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process.
374 * @param symbolName optional symbol name for an OS which requires the symbol's address to retrieve the path of the containing library
375 * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded.
376 * @throws SecurityException if user is not granted access for the named library.
377 * @since 2.4.0
378 */
379 public static final NativeLibrary open(final String libName,
380 final boolean searchOSSystemPath,
381 final boolean searchSystemPathFirst,
382 final ClassLoader loader, final boolean global, final String symbolName) throws SecurityException {
383 return open(libName, libName, libName, searchOSSystemPath, searchSystemPathFirst, loader, global, symbolName);
384 }
385
386 /** Opens the given native library, assuming it has the given base
387 names (no "lib" prefix or ".dll/.so/.dylib" suffix) on the
388 Windows, Unix and Mac OS X platforms, respectively, and in the
389 context of the specified ClassLoader, which is used to help find
390 the library in the case of e.g. Java Web Start.
391 <p>
392 The {@code searchOSSystemPath} argument changes the behavior to
393 either use the default OS system path or not at all.
394 </p>
395 <p>
396 Assuming {@code searchOSSystemPath} is {@code true},
397 the {@code searchSystemPathFirst} argument changes the behavior to first
398 search the default system path rather than searching it last.
399 </p>
400 Note that we do not currently handle DSO versioning on Unix.
401 Experience with JOAL and OpenAL has shown that it is extremely
402 problematic to rely on a specific .so version (for one thing,
403 ClassLoader.findLibrary on Unix doesn't work with files not
404 ending in .so, for example .so.0), and in general if this
405 dynamic loading facility is used correctly the version number
406 will be irrelevant.
407 * @param windowsLibName windows library name, with or without prefix and suffix
408 * @param unixLibName unix library name, with or without prefix and suffix
409 * @param macOSXLibName mac-osx library name, with or without prefix and suffix
410 * @param searchOSSystemPath if {@code true} library shall be searched in the OS system path <i>(default)</i>, otherwise {@code false}.
411 * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
412 * if {@code searchOSSystemPath} is {@code true} this includes the order of the OS system path as well.
413 * @param loader {@link ClassLoader} to locate the library
414 * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process.
415 * @param symbolName optional symbol name for an OS which requires the symbol's address to retrieve the path of the containing library
416 * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded.
417 * @throws SecurityException if user is not granted access for the named library.
418 */
419 public static final NativeLibrary open(final String windowsLibName,
420 final String unixLibName,
421 final String macOSXLibName,
422 final boolean searchOSSystemPath,
423 final boolean searchSystemPathFirst,
424 final ClassLoader loader, final boolean global, final String symbolName) throws SecurityException {
425 final List<LibPath> possiblePaths = enumerateLibraryPaths(windowsLibName,
426 unixLibName,
427 macOSXLibName,
428 searchOSSystemPath, searchSystemPathFirst,
429 loader);
430 Platform.initSingleton(); // loads native gluegen_rt library
431
432 final DynamicLinker dynLink = getDynamicLinker();
433
434 // Iterate down these and see which one if any we can actually find.
435 for (final Iterator<LibPath> iter = possiblePaths.iterator(); iter.hasNext(); ) {
436 final LibPath path = iter.next();
437 if (DEBUG) {
438 System.err.println("NativeLibrary.open(global "+global+"): Trying to load " + path);
439 }
440 long res;
441 Throwable t = null;
442 try {
443 if(global) {
444 res = dynLink.openLibraryGlobal(path, DEBUG);
445 } else {
446 res = dynLink.openLibraryLocal(path, DEBUG);
447 }
448 } catch (final Throwable t1) {
449 t = t1;
450 res = 0;
451 }
452 if ( 0 != res ) {
453 return new NativeLibrary(dynLink, res, path, global, symbolName);
454 } else if( DEBUG ) {
455 if( null != t ) {
456 System.err.println("NativeLibrary.open: Caught "+t.getClass().getSimpleName()+": "+t.getMessage());
457 }
458 String errstr;
459 try {
460 errstr = dynLink.getLastError();
461 } catch (final Throwable t2) { errstr=null; }
462 System.err.println("NativeLibrary.open: Last error "+errstr);
463 if( null != t ) {
464 t.printStackTrace();
465 }
466 }
467 }
468
469 if (DEBUG) {
470 System.err.println("NativeLibrary.open(global "+global+"): Did not succeed in loading (" + windowsLibName + ", " + unixLibName + ", " + macOSXLibName + ")");
471 }
472
473 // For now, just return null to indicate the open operation didn't
474 // succeed (could also throw an exception if we could tell which
475 // of the openLibrary operations actually failed)
476 return null;
477 }
478
479 @Override
480 public final void claimAllLinkPermission() throws SecurityException {
481 dynLink.claimAllLinkPermission();
482 }
483 @Override
484 public final void releaseAllLinkPermission() throws SecurityException {
485 dynLink.releaseAllLinkPermission();
486 }
487
488 @Override
489 public final long dynamicLookupFunction(final String funcName) throws SecurityException {
490 if ( 0 == libraryHandle ) {
491 throw new RuntimeException("Library is not open");
492 }
493 return dynLink.lookupSymbol(libraryHandle, funcName);
494 }
495
496 @Override
497 public final boolean isFunctionAvailable(final String funcName) throws SecurityException {
498 if ( 0 == libraryHandle ) {
499 throw new RuntimeException("Library is not open");
500 }
501 return 0 != dynLink.lookupSymbol(libraryHandle, funcName);
502 }
503
504 /** Looks up the given function name in all loaded libraries.
505 * @throws SecurityException if user is not granted access for the named library.
506 */
507 public final long dynamicLookupFunctionGlobal(final String funcName) throws SecurityException {
508 return dynLink.lookupSymbolGlobal(funcName);
509 }
510
511 /* pp */ final DynamicLinker dynamicLinker() { return dynLink; }
512
513 /* pp */ static DynamicLinker getDynamicLinker() {
514 final DynamicLinker dynLink;
515 switch (PlatformPropsImpl.OS_TYPE) {
516 case WINDOWS:
517 dynLink = new WindowsDynamicLinkerImpl();
518 break;
519
520 case MACOS:
521 case IOS:
522 dynLink = new MacOSXDynamicLinkerImpl();
523 break;
524
525 case ANDROID:
526 if( PlatformPropsImpl.CPU_ARCH.is32Bit ) {
527 dynLink = new BionicDynamicLinker32bitImpl();
528 } else {
529 dynLink = new BionicDynamicLinker64BitImpl();
530 }
531 break;
532
533 default:
534 dynLink = new PosixDynamicLinkerImpl();
535 break;
536 }
537 return dynLink;
538 }
539
540 /** Retrieves the low-level library handle from this NativeLibrary
541 object. On the Windows platform this is an HMODULE, and on Unix
542 and Mac OS X platforms the void* result of calling dlopen(). */
543 public final long getLibraryHandle() {
544 return libraryHandle;
545 }
546
547 @Override
548 public final boolean isOpen() { return 0 != libraryHandle; }
549
550 /** Retrieves the path under which this library was opened. */
551 public final String getLibraryPath() {
552 return libraryPath.path;
553 }
554
555 /** Retrieves the path under which this library was opened. */
556 public final LibPath getLibPath() {
557 return libraryPath;
558 }
559
560 /** Returns the native library path of the opened native {@link #getLibraryHandle()}, maybe null if not supported by OS. */
561 public final String getNativeLibraryPath() {
562 return nativeLibraryPath;
563 }
564
565 /** Closes this native library. Further lookup operations are not
566 allowed after calling this method.
567 * @throws SecurityException if user is not granted access for the named library.
568 */
569 public final void close() throws SecurityException {
570 if (DEBUG) {
571 System.err.println("NativeLibrary.close(): closing " + this);
572 }
573 if ( 0 == libraryHandle ) {
574 throw new RuntimeException("Library already closed");
575 }
576 final long handle = libraryHandle;
577 libraryHandle = 0;
578 dynLink.closeLibrary(handle, DEBUG);
579 if (DEBUG) {
580 System.err.println("NativeLibrary.close(): Successfully closed " + this);
581 ExceptionUtils.dumpStack(System.err);
582 }
583 }
584
585 /**
586 * Comparison of prefix and suffix of the given libName's basename
587 * is performed case insensitive <br>
588 *
589 * @param libName the full path library name with prefix and suffix
590 * @param isLowerCaseAlready indicates if libName is already lower-case
591 *
592 * @return basename of libName w/o path, ie. /usr/lib/libDrinkBeer.so -> DrinkBeer on Unix systems, but null on Windows.
593 */
594 public static final String isValidNativeLibraryName(final String libName, final boolean isLowerCaseAlready) {
595 final String libBaseName;
596 try {
597 libBaseName = IOUtil.getBasename(libName);
598 } catch (final URISyntaxException uriEx) {
599 throw new IllegalArgumentException(uriEx);
600 }
601 final String libBaseNameLC = isLowerCaseAlready ? libBaseName : libBaseName.toLowerCase();
602 int prefixIdx = -1;
603 for(int i=0; i<prefixes.length && 0 > prefixIdx; i++) {
604 if (libBaseNameLC.startsWith(prefixes[i])) {
605 prefixIdx = i;
606 }
607 }
608 if( 0 <= prefixIdx ) {
609 for(int i=0; i<suffixes.length; i++) {
610 if (libBaseNameLC.endsWith(suffixes[i])) {
611 final int s = prefixes[prefixIdx].length();
612 final int e = suffixes[i].length();
613 return libBaseName.substring(s, libBaseName.length()-e);
614 }
615 }
616 }
617 return null;
618 }
619
620 /** Given the base library names (no prefixes/suffixes) for the
621 various platforms, enumerate the possible locations and names of
622 the indicated native library on the system not using the OS system path. */
623 public static final List<LibPath> enumerateLibraryPaths(final String windowsLibName,
624 final String unixLibName,
625 final String macOSXLibName,
626 final ClassLoader loader) {
627 return enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName,
628 false /* searchOSSystemPath */, false /* searchSystemPathFirst */,
629 loader);
630 }
631 /** Given the base library names (no prefixes/suffixes) for the
632 various platforms, enumerate the possible locations and names of
633 the indicated native library on the system using the OS system path. */
634 public static final List<LibPath> enumerateLibraryPaths(final String windowsLibName,
635 final String unixLibName,
636 final String macOSXLibName,
637 final boolean searchSystemPathFirst,
638 final ClassLoader loader) {
639 return enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName,
640 true /* searchOSSystemPath */, searchSystemPathFirst,
641 loader);
642 }
643
644 private static final List<LibPath> enumerateLibraryPaths(final String windowsLibName,
645 final String unixLibName,
646 final String macOSXLibName,
647 final boolean searchOSSystemPath,
648 final boolean searchSystemPathFirst,
649 final ClassLoader loader) {
651 final String libName = selectName(windowsLibName, unixLibName, macOSXLibName);
652 if (libName == null || libName.isEmpty()) {
653 if (DEBUG) {
654 System.err.println("NativeLibrary.enumerateLibraryPaths: empty, no libName selected");
655 }
656 return paths.getData();
657 }
658 if (DEBUG) {
659 System.err.println("NativeLibrary.enumerateLibraryPaths: libName '"+libName+"'");
660 }
661
662 // Allow user's full path specification to override our building of paths
663 final File file = new File(libName);
664 if (file.isAbsolute()) {
665 File cfile;
666 try {
667 cfile = file.getCanonicalFile();
668 } catch (final IOException e) {
669 System.err.println("NativeLibrary.enumerateLibraryPaths: absolute path: Exception "+e.getMessage()+", from path '"+libName+"'");
670 return paths.getData();
671 }
672 if( cfile.exists() ) {
673 final LibPath lp = LibPath.createExtra(cfile.getPath(), cfile.getParent());
674 if( paths.add(lp) ) {
675 if (DEBUG) {
676 System.err.println("NativeLibrary.enumerateLibraryPaths: absolute path: Done, found '"+lp+"'");
677 }
678 }
679 return paths.getData();
680 }
681 }
682
683 final String[] baseNames = buildNames(libName);
684 if (DEBUG) {
685 System.err.println("NativeLibrary.enumerateLibraryPaths: baseNames: "+Arrays.toString(baseNames));
686 }
687
688 // Add priority entries from jogamp.primary.library.path
689 addMultiLibPathsLibraries("jogamp.primary.library", getJogAmpPrimaryLibraryPaths(), libName, baseNames, paths, true);
690
691 if( searchSystemPathFirst ) {
692 if( searchOSSystemPath ) {
693 addOSSystemLibraryPaths(libName, baseNames, paths);
694 }
695 addClassLoaderPaths(libName, paths, loader);
696 addMultiLibPathsLibraries("sun.boot.library", getJavaLibrarySystemPaths(), libName, baseNames, paths, true);
697 }
698
699 // user path
700 addMultiLibPathsLibraries("java.library", getJavaLibraryUserPaths(), libName, baseNames, paths, true);
701
702 // Add current working directory
703 final String userDir = getJavaCurrentWorkingDir();
704 if(null != userDir && !userDir.isEmpty()) {
705 addCanonicalPaths("add.user.dir.std", userDir, baseNames, paths, true);
706
707 // Add current working directory + natives/os-arch/ + library names
708 // to handle Bug 1145 cc1 using an unpacked fat-jar
709 addCanonicalPaths("add.user.dir.fat", userDir+File.separator+"natives"+File.separator+PlatformPropsImpl.os_and_arch, baseNames, paths, true);
710 }
711
712 if( !searchSystemPathFirst ) {
713 addMultiLibPathsLibraries("sun.boot.library", getJavaLibrarySystemPaths(), libName, baseNames, paths, true);
714 addClassLoaderPaths(libName, paths, loader);
715 if( searchOSSystemPath ) {
716 addOSSystemLibraryPaths(libName, baseNames, paths);
717 }
718 }
719
720 if (DEBUG) {
721 System.err.println("NativeLibrary.enumerateLibraryPaths: done: "+paths.toString());
722 }
723 return paths.getData();
724 }
725
726 /** Add OS system library path, if found */
727 private static final void addOSSystemLibraryPaths(final String libName, final String[] baseNames, final List<LibPath> paths) {
728 // Utilize system's library path environment variable first
729 addMultiLibPathsLibraries("system", getSystemEnvLibraryPaths(), libName, baseNames, paths, false);
730
731 // Add just the library names to use the OS's search algorithm
732 for (int i = 0; i < baseNames.length; i++) {
733 final LibPath lp = LibPath.createRelative(baseNames[i]);
734 if( paths.add( lp ) ) {
735 if (DEBUG) {
736 System.err.println("NativeLibrary.enumerateLibraryPaths: add.ssp_default: " + lp);
737 }
738 }
739 }
740 // Add probable Mac OS X-specific paths
741 if (isOSX) {
742 // Add historical location
743 addCanonicalPaths("add.ssp_1st_macos_old", "/Library/Frameworks/" + libName + ".framework", baseNames, paths, false);
744 // Add current location
745 addCanonicalPaths("add.ssp_1st_macos_cur", "/System/Library/Frameworks/" + libName + ".framework", baseNames, paths, false);
746 }
747 }
748 /** Add Java ClassLoader library path, if found */
749 private static final void addClassLoaderPaths(final String libName, final List<LibPath> paths, final ClassLoader loader) {
750 // The idea to ask the ClassLoader to find the library is borrowed
751 // from the LWJGL library
752 final String clPath = findLibrary(libName, loader);
753 if (clPath != null) {
754 final LibPath lp = LibPath.createAbsolute(clPath);
755 if( paths.add( lp ) ) {
756 if (DEBUG) {
757 System.err.println("NativeLibrary.enumerateLibraryPaths: add.clp: "+clPath);
758 }
759 }
760 }
761 }
762 private static final void addMultiLibPathsLibraries(final String id, final String multidirs, final String libName, final String[] baseNames, final List<LibPath> paths,
763 final boolean addToSearchPath)
764 {
765 if( null == multidirs || multidirs.isEmpty() ) {
766 return;
767 }
768 int count = 0;
769 final StringTokenizer tokenizer = new StringTokenizer(multidirs, File.pathSeparator);
770 while (tokenizer.hasMoreTokens()) {
771 addAbstractPaths("add."+id+".path_"+count, tokenizer.nextToken(), baseNames, paths, addToSearchPath);
772 ++count;
773 }
774 }
775
776 private static final void addAbstractPaths(final String cause, final String parent, final String[] baseNames, final List<LibPath> paths, final boolean addToSearchPath) {
777 if( null == parent || parent.isEmpty() ) {
778 return;
779 }
780 addCanonicalPaths(cause, new File(parent), baseNames, paths, addToSearchPath); // we canonicalize again in addCanonicalPaths
781 }
782 private static final void addCanonicalPaths(final String cause, final String parent, final String[] baseNames, final List<LibPath> paths, final boolean addToSearchPath) {
783 if( null == parent || parent.isEmpty() ) {
784 return;
785 }
786 addCanonicalPaths(cause, new File(parent), baseNames, paths, addToSearchPath);
787 }
788 private static final void addCanonicalPaths(final String cause, final File can_parent, final String[] baseNames, final List<LibPath> paths, final boolean addToSearchPath) {
789 for (int j = 0; j < baseNames.length; j++) {
790 final String ps = can_parent.getPath() + File.separator + baseNames[j];
791 File fps;
792 try {
793 fps = new File(ps).getCanonicalFile(); // be sure with complete path
794 } catch (final IOException e) {
795 System.err.println("NativeLibrary.addCanonicalPaths: "+cause+": Exception "+e.getMessage()+", from path '"+ps+"'");
796 return;
797 }
798 if( fps.exists() ) {
799 final LibPath p = addToSearchPath ? LibPath.createExtra(fps.getPath(), fps.getParent()) : LibPath.createAbsolute(fps.getPath());
800 if( paths.add(p) ) {
801 if (DEBUG) {
802 System.err.println("NativeLibrary.addCanonicalPaths: "+cause+": Added "+p+", from path '"+ps+"'");
803 }
804 }
805 }
806 }
807 }
808
809 private static final String selectName(final String windowsLibName,
810 final String unixLibName,
811 final String macOSXLibName) {
812 switch (PlatformPropsImpl.OS_TYPE) {
813 case WINDOWS:
814 return windowsLibName;
815
816 case MACOS:
817 case IOS:
818 return macOSXLibName;
819
820 default:
821 return unixLibName;
822 }
823 }
824
825 private static final String[] buildNames(final String libName) {
826 // If the library name already has the prefix / suffix added
827 // (principally because we want to force a version number on Unix
828 // operating systems) then just return the library name.
829 final String libBaseNameLC;
830 try {
831 libBaseNameLC = IOUtil.getBasename(libName).toLowerCase();
832 } catch (final URISyntaxException uriEx) {
833 throw new IllegalArgumentException(uriEx);
834 }
835
836 int prefixIdx = -1;
837 for(int i=0; i<prefixes.length && 0 > prefixIdx; i++) {
838 if (libBaseNameLC.startsWith(prefixes[i])) {
839 prefixIdx = i;
840 }
841 }
842 if( 0 <= prefixIdx ) {
843 for(int i=0; i<suffixes.length; i++) {
844 if (libBaseNameLC.endsWith(suffixes[i])) {
845 return new String[] { libName };
846 }
847 }
848 int suffixIdx = -1;
849 for(int i=0; i<suffixes.length && 0 > suffixIdx; i++) {
850 suffixIdx = libBaseNameLC.indexOf(suffixes[i]);
851 }
852 boolean ok = true;
853 if (suffixIdx >= 0) {
854 // Check to see if everything after it is a Unix version number
855 for (int i = suffixIdx + suffixes[0].length();
856 i < libName.length();
857 i++) {
858 final char c = libName.charAt(i);
859 if (!(c == '.' || (c >= '0' && c <= '9'))) {
860 ok = false;
861 break;
862 }
863 }
864 if (ok) {
865 return new String[] { libName };
866 }
867 }
868 }
869
870 final String[] res = new String[prefixes.length * suffixes.length + ( isOSX ? 1 : 0 )];
871 int idx = 0;
872 for (int i = 0; i < prefixes.length; i++) {
873 for (int j = 0; j < suffixes.length; j++) {
874 res[idx++] = prefixes[i] + libName + suffixes[j];
875 }
876 }
877 if ( isOSX ) {
878 // Plain library-base-name in Framework folder
879 res[idx++] = libName;
880 }
881 return res;
882 }
883
884 private static boolean initializedFindLibraryMethod = false;
885 private static Method findLibraryMethod = null;
886 private static final String findLibraryImpl(final String libName, final ClassLoader loader) {
887 if( PlatformPropsImpl.JAVA_9 ) {
888 return null;
889 }
890 if (loader == null) {
891 return null;
892 }
893 if (!initializedFindLibraryMethod) {
894 SecurityUtil.doPrivileged(new PrivilegedAction<Object>() {
895 @Override
896 public Object run() {
897 try {
898 findLibraryMethod = ClassLoader.class.getDeclaredMethod("findLibrary",
899 new Class[] { String.class });
900 findLibraryMethod.setAccessible(true);
901 } catch (final Exception e) {
902 // Fail silently disabling this functionality
903 }
904 initializedFindLibraryMethod = true;
905 return null;
906 }
907 });
908 }
909 if (findLibraryMethod != null) {
910 try {
911 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
912 @Override
913 public String run() {
914 try {
915 return (String) findLibraryMethod.invoke(loader, new Object[] { libName });
916 } catch (final Exception e) {
917 throw new RuntimeException(e);
918 }
919 }
920 });
921 } catch (final Exception e) {
922 if (DEBUG) {
923 e.printStackTrace();
924 }
925 // Fail silently and continue with other search algorithms
926 }
927 }
928 return null;
929 }
930 public static final String findLibrary(final String libName, final ClassLoader loader) {
931 String res = null;
932 if( TempJarCache.isInitialized(true) ) {
933 res = TempJarCache.findLibrary(libName);
934 if (DEBUG) {
935 System.err.println("NativeLibrary.findLibrary(<"+libName+">) (TempJarCache): "+res);
936 }
937 }
938 if(null == res) {
939 res = findLibraryImpl(libName, loader);
940 if (DEBUG) {
941 System.err.println("NativeLibrary.findLibrary(<"+libName+">, "+loader+") (CL): "+res);
942 }
943 }
944 return res;
945 }
946}
static void dumpStack(final PrintStream out)
Native Library Path Specification.
final String searchPathPrepend
Search path prepend directories, separated by OS File#pathSeparator.
final String path
Relative or absolute library path.
static LibPath createRelative(final String p)
Returns new instance with relative path in system linker search path.
static LibPath createAbsolute(final String p)
Returns new instance with absolute path in system linker search path.
final boolean isAbsolute
True if path is an absolute path.
final boolean addToSearchPath
True if directory of absolute library path shall be added to the linker search path.
static LibPath createExtra(final String p, final String searchPathPrepend)
Returns new instance with absolute path not in system linker search path.
Provides low-level, relatively platform-independent access to shared ("native") libraries.
final String getLibraryPath()
Retrieves the path under which this library was opened.
static final String isValidNativeLibraryName(final String libName, final boolean isLowerCaseAlready)
Comparison of prefix and suffix of the given libName's basename is performed case insensitive
final boolean isFunctionAvailable(final String funcName)
Queries whether function 'funcName' is available.
static final NativeLibrary open(final String libName, final boolean searchOSSystemPath, final boolean searchSystemPathFirst, final ClassLoader loader, final boolean global, final String symbolName)
Opens the given native library, assuming it has the same base name on all platforms.
static final List< LibPath > enumerateLibraryPaths(final String windowsLibName, final String unixLibName, final String macOSXLibName, final ClassLoader loader)
Given the base library names (no prefixes/suffixes) for the various platforms, enumerate the possible...
final boolean isOpen()
Returns true if library is loaded and open, otherwise false.
final long dynamicLookupFunctionGlobal(final String funcName)
Looks up the given function name in all loaded libraries.
final long getLibraryHandle()
Retrieves the low-level library handle from this NativeLibrary object.
static final String getSystemEnvLibraryPaths()
Returns a system paths separated with File#pathSeparator, from the getSystemEnvLibraryPathVarname() v...
final String getNativeLibraryPath()
Returns the native library path of the opened native getLibraryHandle(), maybe null if not supported ...
final LibPath getLibPath()
Retrieves the path under which this library was opened.
static final NativeLibrary open(final String windowsLibName, final String unixLibName, final String macOSXLibName, final boolean searchOSSystemPath, final boolean searchSystemPathFirst, final ClassLoader loader, final boolean global, final String symbolName)
Opens the given native library, assuming it has the given base names (no "lib" prefix or "....
final long dynamicLookupFunction(final String funcName)
Returns the function handle for function 'funcName'.
static final String getSystemEnvLibraryPathVarname()
Returns the system's environment variable name used for the dynamic linker to resolve library locatio...
final void close()
Closes this native library.
static final List< LibPath > enumerateLibraryPaths(final String windowsLibName, final String unixLibName, final String macOSXLibName, final boolean searchSystemPathFirst, final ClassLoader loader)
Given the base library names (no prefixes/suffixes) for the various platforms, enumerate the possible...
static final NativeLibrary open(final String libName, final boolean searchOSSystemPath, final boolean searchSystemPathFirst, final ClassLoader loader, final boolean global)
Opens the given native library, assuming it has the same base name on all platforms.
static final String getJogAmpPrimaryLibraryPaths()
Returns JogAmp's primary library path jogamp.primary.library.path separated with File#pathSeparator.
static final String findLibrary(final String libName, final ClassLoader loader)
Utility class for querying platform specific properties.
Definition: Platform.java:58
static void initSingleton()
kick off static initialization of platform property information and native gluegen_rt lib loading
Definition: Platform.java:359
Hashed ArrayList implementation of the List and Collection interface.
final ArrayList< E > getData()
Returns this object ordered ArrayList.
static final float DEFAULT_LOAD_FACTOR
Default load factor: {@value}.
final boolean add(final E element)
Add element at the end of this list, if it is not contained yet.
static final int DEFAULT_INITIAL_CAPACITY
The default initial capacity: {@value}.
static String getBasename(String fname)
Returns the basename of the given fname w/o directory part.
Definition: IOUtil.java:486
static< T > T doPrivileged(final PrivilegedAction< T > o)
Call wrapper for java.security.AccessController#doPrivileged(PrivilegedAction).
Static Jar file cache handler using an underlying instance of TempFileCache, see getTempFileCache().
static boolean isInitialized(final boolean forExecutables)
static synchronized final String findLibrary(final String libName)
If isInitialized(true) is false due to lack of executable support only, this method always returns fa...
Low level secure dynamic linker access.
long openLibraryLocal(LibPath libpath, boolean debug)
If a SecurityManager is installed, user needs link permissions for the named library.
long lookupSymbolGlobal(String symbolName)
If a SecurityManager is installed, user needs link permissions for all libraries, i....
String getLastError()
Returns a string containing the last error.
String lookupLibraryPathname(long libraryHandle, String symbolName)
Security checks are implicit by previous call of openLibraryLocal(String, boolean) or openLibraryGlob...
long openLibraryGlobal(LibPath libpath, boolean debug)
If a SecurityManager is installed, user needs link permissions for the named library.
long lookupSymbol(long libraryHandle, String symbolName)
Security checks are implicit by previous call of openLibraryLocal(String, boolean) or openLibraryGlob...
Interface callers may use ProcAddressHelper's reset helper method to install function pointers into a...