GlueGen v2.6.0-rc-20250822
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 Subroutines
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 First
85 * - If `searchOSSystemPath`
86 * - Perform described `OS System Search` above
87 * - Java's ClassLoader `findLibrary` mechanism
88 * - Java's Java system library path property
89 * - `sun.boot.library.path`
90 *
91 * ### System Search Last
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 First` 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 Last` 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 * May be used independent to `addToSearchPath`.
175 *
176 * @see #addToSearchPath
177 */
178 public final String searchPathPrepend;
179
180 /** Returns new instance with relative path in system linker search path */
181 public static LibPath createRelative(final String p) { return new LibPath(p, false, false, null); }
182
183 /** Returns new instance with absolute path in system linker search path */
184 public static LibPath createAbsolute(final String p) { return new LibPath(p, true, false, null); }
185
186 /**
187 * Returns new instance with absolute path not in system linker search path.
188 * @see #addToSearchPath
189 */
190 public static LibPath createExtra(final String p, final String searchPathPrepend) { return new LibPath(p, true, true, searchPathPrepend); }
191
192 private LibPath(final String _path, final boolean _isAbsolute, final boolean _addToSearchPath, final String _searchPathPrepend) {
193 path = _path;
194 isAbsolute = _isAbsolute;
195 addToSearchPath = _addToSearchPath;
196 searchPathPrepend = _searchPathPrepend;
197 fixedHashCode = calcHashCode();
198 }
199 private int calcHashCode() {
200 // 31 * x == (x << 5) - x
201 int hash = path.hashCode();
202 if(null != searchPathPrepend) {
203 hash = ((hash << 5) - hash) + searchPathPrepend.hashCode();
204 }
205 hash = ((hash << 5) - hash) + (isAbsolute? 1 : 0);
206 hash = ((hash << 5) - hash) + (addToSearchPath? 1 : 0);
207 return hash;
208 }
209 private final int fixedHashCode;
210
211 @Override
212 public int hashCode() { return fixedHashCode; }
213
214 @Override
215 public boolean equals(final Object o) {
216 if(!(o instanceof LibPath)) {
217 return false;
218 }
219 final LibPath o2 = (LibPath)o;
220 return path.equals(o2.path) &&
221 isAbsolute == o2.isAbsolute &&
222 addToSearchPath == o2.addToSearchPath &&
223 ( ( searchPathPrepend == null && o2.searchPathPrepend == null ) ||
224 ( searchPathPrepend != null && o2.searchPathPrepend != null && searchPathPrepend.equals(o2.searchPathPrepend) ) );
225 }
226
227 @Override
228 public String toString() {
229 return "LibPath['"+path+"', "+(isAbsolute?"abs":"rel")+", "+(addToSearchPath?"xsp "+searchPathPrepend:"sys")+"]";
230 }
231 }
232
233 private final DynamicLinker dynLink;
234
235 // Platform-specific representation for the handle to the open
236 // library. This is an HMODULE on Windows and a void* (the result of
237 // a dlopen() call) on Unix and Mac OS X platforms.
238 private long libraryHandle;
239
240 // May as well keep around the path to the library we opened
241 private final LibPath libraryPath;
242
243 // Native library path of the opened native libraryHandle, maybe null
244 private final String nativeLibraryPath;
245
246 private final boolean global;
247
248 // Private constructor to prevent arbitrary instances from floating around
249 private NativeLibrary(final DynamicLinker dynLink, final long libraryHandle, final LibPath libraryPath, final boolean global, final String symbolName) {
250 this.dynLink = dynLink;
251 this.libraryHandle = libraryHandle;
252 this.libraryPath = libraryPath;
253 this.nativeLibraryPath = dynLink.lookupLibraryPathname(libraryHandle, symbolName);
254 this.global = global;
255 if (DEBUG) {
256 System.err.println("NativeLibrary.open(): Successfully loaded: " + this);
257 }
258 }
259
260 @Override
261 public final String toString() {
262 final String nlp_s = null != nativeLibraryPath ? ", native '"+nativeLibraryPath+"'" : "";
263 return "NativeLibrary[" + dynLink.getClass().getSimpleName() + ", path[given '" + libraryPath + "'"+nlp_s+"], 0x" +
264 Long.toHexString(libraryHandle) + ", global " + global + "]";
265 }
266
267 /**
268 * Returns the system's environment variable name used for the dynamic linker to resolve library locations, e.g.
269 * - Windows: PATH
270 * - MacOS: DYLD_LIBRARY_PATH
271 * - Unix: LD_LIBRARY_PATH
272 */
273 public static final String getSystemEnvLibraryPathVarname() { return sys_env_lib_path_varname; }
274
275 /**
276 * Returns a system paths separated with {@link File#pathSeparator}, from the {@link #getSystemEnvLibraryPathVarname()} variable.
277 */
278 public static final String getSystemEnvLibraryPaths() {
279 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
280 @Override
281 public String run() {
282 return System.getenv(getSystemEnvLibraryPathVarname());
283 }
284 });
285 }
286
287 /** Returns JogAmp's primary library path `jogamp.primary.library.path` separated with {@link File#pathSeparator}. */
288 public static final String getJogAmpPrimaryLibraryPaths() {
289 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
290 @Override
291 public String run() {
292 return System.getProperty("jogamp.primary.library.path");
293 }
294 });
295 }
296
297 /** Returns Java library system path `sun.boot.library.path` separated with {@link File#pathSeparator}. */
298 private static final String getJavaLibrarySystemPaths() {
299 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
300 @Override
301 public String run() {
302 final String p = System.getProperty("sun.boot.library.path");
303 if(null != p && !p.isEmpty()) {
304 return p;
305 }
306 return null;
307 }
308 });
309 }
310 /** Returns Java library user path `java.library.path` separated with {@link File#pathSeparator}. */
311 private static final String getJavaLibraryUserPaths() {
312 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
313 @Override
314 public String run() {
315 final String p = System.getProperty("java.library.path");
316 if(null != p && !p.isEmpty()) {
317 return p;
318 }
319 return null;
320 }
321 });
322 }
323
324 /** Returns Java library user path `user.dir` */
325 private static final String getJavaCurrentWorkingDir() {
326 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
327 @Override
328 public String run() {
329 return System.getProperty("user.dir");
330 }
331 });
332 }
333
334 /** Opens the given native library, assuming it has the same base
335 name on all platforms.
336 <p>
337 The {@code searchOSSystemPath} argument changes the behavior to
338 either use the default system path or not at all.
339 </p>
340 <p>
341 Assuming {@code searchOSSystemPath} is {@code true},
342 the {@code searchSystemPathFirst} argument changes the behavior to first
343 search the default system path rather than searching it last.
344 </p>
345 * @param libName library name, with or without prefix and suffix
346 * @param searchOSSystemPath if {@code true} library shall be searched in the system path <i>(default)</i>, otherwise {@code false}.
347 * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
348 * if {@code searchOSSystemPath} is {@code true} this includes the order of the OS system path as well.
349 * @param loader {@link ClassLoader} to locate the library
350 * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process.
351 * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded.
352 * @throws SecurityException if user is not granted access for the named library.
353 * @since 2.4.0
354 */
355 public static final NativeLibrary open(final String libName,
356 final boolean searchOSSystemPath,
357 final boolean searchSystemPathFirst,
358 final ClassLoader loader, final boolean global) throws SecurityException {
359 return open(libName, libName, libName, searchOSSystemPath, searchSystemPathFirst, loader, global, null);
360 }
361
362 /** Opens the given native library, assuming it has the same base
363 name on all platforms.
364 <p>
365 The {@code searchOSSystemPath} argument changes the behavior to
366 either use the default system path or not at all.
367 </p>
368 <p>
369 Assuming {@code searchOSSystemPath} is {@code true},
370 the {@code searchSystemPathFirst} argument changes the behavior to first
371 search the default system path rather than searching it last.
372 </p>
373 * @param libName library name, with or without prefix and suffix
374 * @param searchOSSystemPath if {@code true} library shall be searched in the OS system path <i>(default)</i>, otherwise {@code false}.
375 * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
376 * if {@code searchOSSystemPath} is {@code true} this includes the order of the OS system path as well.
377 * @param loader {@link ClassLoader} to locate the library
378 * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process.
379 * @param symbolName optional symbol name for an OS which requires the symbol's address to retrieve the path of the containing library
380 * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded.
381 * @throws SecurityException if user is not granted access for the named library.
382 * @since 2.4.0
383 */
384 public static final NativeLibrary open(final String libName,
385 final boolean searchOSSystemPath,
386 final boolean searchSystemPathFirst,
387 final ClassLoader loader, final boolean global, final String symbolName) throws SecurityException {
388 return open(libName, libName, libName, searchOSSystemPath, searchSystemPathFirst, loader, global, symbolName);
389 }
390
391 /** Opens the given native library, assuming it has the given base
392 names (no "lib" prefix or ".dll/.so/.dylib" suffix) on the
393 Windows, Unix and Mac OS X platforms, respectively, and in the
394 context of the specified ClassLoader, which is used to help find
395 the library in the case of e.g. Java Web Start.
396 <p>
397 The {@code searchOSSystemPath} argument changes the behavior to
398 either use the default OS system path or not at all.
399 </p>
400 <p>
401 Assuming {@code searchOSSystemPath} is {@code true},
402 the {@code searchSystemPathFirst} argument changes the behavior to first
403 search the default system path rather than searching it last.
404 </p>
405 Note that we do not currently handle DSO versioning on Unix.
406 Experience with JOAL and OpenAL has shown that it is extremely
407 problematic to rely on a specific .so version (for one thing,
408 ClassLoader.findLibrary on Unix doesn't work with files not
409 ending in .so, for example .so.0), and in general if this
410 dynamic loading facility is used correctly the version number
411 will be irrelevant.
412 * @param windowsLibName windows library name, with or without prefix and suffix
413 * @param unixLibName unix library name, with or without prefix and suffix
414 * @param macOSXLibName mac-osx library name, with or without prefix and suffix
415 * @param searchOSSystemPath if {@code true} library shall be searched in the OS system path <i>(default)</i>, otherwise {@code false}.
416 * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
417 * if {@code searchOSSystemPath} is {@code true} this includes the order of the OS system path as well.
418 * @param loader {@link ClassLoader} to locate the library
419 * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process.
420 * @param symbolName optional symbol name for an OS which requires the symbol's address to retrieve the path of the containing library
421 * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded.
422 * @throws SecurityException if user is not granted access for the named library.
423 */
424 public static final NativeLibrary open(final String windowsLibName,
425 final String unixLibName,
426 final String macOSXLibName,
427 final boolean searchOSSystemPath,
428 final boolean searchSystemPathFirst,
429 final ClassLoader loader, final boolean global, final String symbolName) throws SecurityException {
430 final List<LibPath> possiblePaths = enumerateLibraryPaths(windowsLibName,
431 unixLibName,
432 macOSXLibName,
433 searchOSSystemPath, searchSystemPathFirst,
434 loader);
435 Platform.initSingleton(); // loads native gluegen_rt library
436
437 final DynamicLinker dynLink = getDynamicLinker();
438
439 // Iterate down these and see which one if any we can actually find.
440 for (final Iterator<LibPath> iter = possiblePaths.iterator(); iter.hasNext(); ) {
441 final LibPath path = iter.next();
442 if (DEBUG) {
443 System.err.println("NativeLibrary.open(global "+global+"): Trying to load " + path);
444 }
445 long res;
446 Throwable t = null;
447 try {
448 if(global) {
449 res = dynLink.openLibraryGlobal(path, DEBUG);
450 } else {
451 res = dynLink.openLibraryLocal(path, DEBUG);
452 }
453 } catch (final Throwable t1) {
454 t = t1;
455 res = 0;
456 }
457 if ( 0 != res ) {
458 return new NativeLibrary(dynLink, res, path, global, symbolName);
459 } else if( DEBUG ) {
460 if( null != t ) {
461 System.err.println("NativeLibrary.open: Caught "+t.getClass().getSimpleName()+": "+t.getMessage());
462 }
463 String errstr;
464 try {
465 errstr = dynLink.getLastError();
466 } catch (final Throwable t2) { errstr=null; }
467 System.err.println("NativeLibrary.open: Last error "+errstr);
468 if( null != t ) {
469 t.printStackTrace();
470 }
471 }
472 }
473
474 if (DEBUG) {
475 System.err.println("NativeLibrary.open(global "+global+"): Did not succeed in loading (" + windowsLibName + ", " + unixLibName + ", " + macOSXLibName + ")");
476 }
477
478 // For now, just return null to indicate the open operation didn't
479 // succeed (could also throw an exception if we could tell which
480 // of the openLibrary operations actually failed)
481 return null;
482 }
483
484 @Override
485 public final void claimAllLinkPermission() throws SecurityException {
486 dynLink.claimAllLinkPermission();
487 }
488 @Override
489 public final void releaseAllLinkPermission() throws SecurityException {
490 dynLink.releaseAllLinkPermission();
491 }
492
493 @Override
494 public final long dynamicLookupFunction(final String funcName) throws SecurityException {
495 if ( 0 == libraryHandle ) {
496 throw new RuntimeException("Library is not open");
497 }
498 return dynLink.lookupSymbol(libraryHandle, funcName);
499 }
500
501 @Override
502 public final boolean isFunctionAvailable(final String funcName) throws SecurityException {
503 if ( 0 == libraryHandle ) {
504 throw new RuntimeException("Library is not open");
505 }
506 return 0 != dynLink.lookupSymbol(libraryHandle, funcName);
507 }
508
509 /** Looks up the given function name in all loaded libraries.
510 * @throws SecurityException if user is not granted access for the named library.
511 */
512 public final long dynamicLookupFunctionGlobal(final String funcName) throws SecurityException {
513 return dynLink.lookupSymbolGlobal(funcName);
514 }
515
516 /* pp */ final DynamicLinker dynamicLinker() { return dynLink; }
517
518 /* pp */ static DynamicLinker getDynamicLinker() {
519 final DynamicLinker dynLink;
520 switch (PlatformPropsImpl.OS_TYPE) {
521 case WINDOWS:
522 dynLink = new WindowsDynamicLinkerImpl();
523 break;
524
525 case MACOS:
526 case IOS:
527 dynLink = new MacOSXDynamicLinkerImpl();
528 break;
529
530 case ANDROID:
531 if( PlatformPropsImpl.CPU_ARCH.is32Bit ) {
532 dynLink = new BionicDynamicLinker32bitImpl();
533 } else {
534 dynLink = new BionicDynamicLinker64BitImpl();
535 }
536 break;
537
538 default:
539 dynLink = new PosixDynamicLinkerImpl();
540 break;
541 }
542 return dynLink;
543 }
544
545 /** Retrieves the low-level library handle from this NativeLibrary
546 object. On the Windows platform this is an HMODULE, and on Unix
547 and Mac OS X platforms the void* result of calling dlopen(). */
548 public final long getLibraryHandle() {
549 return libraryHandle;
550 }
551
552 @Override
553 public final boolean isOpen() { return 0 != libraryHandle; }
554
555 /** Retrieves the path under which this library was opened. */
556 public final String getLibraryPath() {
557 return libraryPath.path;
558 }
559
560 /** Retrieves the path under which this library was opened. */
561 public final LibPath getLibPath() {
562 return libraryPath;
563 }
564
565 /** Returns the native library path of the opened native {@link #getLibraryHandle()}, maybe null if not supported by OS. */
566 public final String getNativeLibraryPath() {
567 return nativeLibraryPath;
568 }
569
570 /** Closes this native library. Further lookup operations are not
571 allowed after calling this method.
572 * @throws SecurityException if user is not granted access for the named library.
573 */
574 public final void close() throws SecurityException {
575 if (DEBUG) {
576 System.err.println("NativeLibrary.close(): closing " + this);
577 }
578 if ( 0 == libraryHandle ) {
579 throw new RuntimeException("Library already closed");
580 }
581 final long handle = libraryHandle;
582 libraryHandle = 0;
583 dynLink.closeLibrary(handle, DEBUG);
584 if (DEBUG) {
585 System.err.println("NativeLibrary.close(): Successfully closed " + this);
586 ExceptionUtils.dumpStack(System.err);
587 }
588 }
589
590 /**
591 * Comparison of prefix and suffix of the given libName's basename
592 * is performed case insensitive <br>
593 *
594 * @param libName the full path library name with prefix and suffix
595 * @param isLowerCaseAlready indicates if libName is already lower-case
596 *
597 * @return basename of libName w/o path, ie. /usr/lib/libDrinkBeer.so -> DrinkBeer on Unix systems, but null on Windows.
598 */
599 public static final String isValidNativeLibraryName(final String libName, final boolean isLowerCaseAlready) {
600 final String libBaseName;
601 try {
602 libBaseName = IOUtil.getBasename(libName);
603 } catch (final URISyntaxException uriEx) {
604 throw new IllegalArgumentException(uriEx);
605 }
606 final String libBaseNameLC = isLowerCaseAlready ? libBaseName : libBaseName.toLowerCase();
607 int prefixIdx = -1;
608 for(int i=0; i<prefixes.length && 0 > prefixIdx; i++) {
609 if (libBaseNameLC.startsWith(prefixes[i])) {
610 prefixIdx = i;
611 }
612 }
613 if( 0 <= prefixIdx ) {
614 for(int i=0; i<suffixes.length; i++) {
615 if (libBaseNameLC.endsWith(suffixes[i])) {
616 final int s = prefixes[prefixIdx].length();
617 final int e = suffixes[i].length();
618 return libBaseName.substring(s, libBaseName.length()-e);
619 }
620 }
621 }
622 return null;
623 }
624
625 /** Given the base library names (no prefixes/suffixes) for the
626 various platforms, enumerate the possible locations and names of
627 the indicated native library on the system not using the OS system path. */
628 public static final List<LibPath> enumerateLibraryPaths(final String windowsLibName,
629 final String unixLibName,
630 final String macOSXLibName,
631 final ClassLoader loader) {
632 return enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName,
633 false /* searchOSSystemPath */, false /* searchSystemPathFirst */,
634 loader);
635 }
636 /** Given the base library names (no prefixes/suffixes) for the
637 various platforms, enumerate the possible locations and names of
638 the indicated native library on the system using the OS system path. */
639 public static final List<LibPath> enumerateLibraryPaths(final String windowsLibName,
640 final String unixLibName,
641 final String macOSXLibName,
642 final boolean searchSystemPathFirst,
643 final ClassLoader loader) {
644 return enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName,
645 true /* searchOSSystemPath */, searchSystemPathFirst,
646 loader);
647 }
648
649 private static final List<LibPath> enumerateLibraryPaths(final String windowsLibName,
650 final String unixLibName,
651 final String macOSXLibName,
652 final boolean searchOSSystemPath,
653 final boolean searchSystemPathFirst,
654 final ClassLoader loader) {
656 final String libName = selectName(windowsLibName, unixLibName, macOSXLibName);
657 if (libName == null || libName.isEmpty()) {
658 if (DEBUG) {
659 System.err.println("NativeLibrary.enumerateLibraryPaths: empty, no libName selected");
660 }
661 return paths.getData();
662 }
663 if (DEBUG) {
664 System.err.println("NativeLibrary.enumerateLibraryPaths: libName '"+libName+"'");
665 }
666
667 // Allow user's full path specification to override our building of paths
668 final File file = new File(libName);
669 if (file.isAbsolute()) {
670 File cfile;
671 try {
672 cfile = file.getCanonicalFile();
673 } catch (final IOException e) {
674 System.err.println("NativeLibrary.enumerateLibraryPaths: absolute path: Exception "+e.getMessage()+", from path '"+libName+"'");
675 return paths.getData();
676 }
677 if( cfile.exists() ) {
678 final LibPath lp = LibPath.createExtra(cfile.getPath(), cfile.getParent());
679 if( paths.add(lp) ) {
680 if (DEBUG) {
681 System.err.println("NativeLibrary.enumerateLibraryPaths: absolute path: Done, found '"+lp+"'");
682 }
683 }
684 return paths.getData();
685 }
686 }
687
688 final String[] baseNames = buildNames(libName);
689 if (DEBUG) {
690 System.err.println("NativeLibrary.enumerateLibraryPaths: baseNames: "+Arrays.toString(baseNames));
691 }
692
693 // Add priority entries from jogamp.primary.library.path
694 addMultiLibPathsLibraries("jogamp.primary.library", getJogAmpPrimaryLibraryPaths(), libName, baseNames, paths, true);
695
696 if( searchSystemPathFirst ) {
697 if( searchOSSystemPath ) {
698 addOSSystemLibraryPaths(libName, baseNames, paths);
699 }
700 addClassLoaderPaths(libName, paths, loader);
701 addMultiLibPathsLibraries("sun.boot.library", getJavaLibrarySystemPaths(), libName, baseNames, paths, true);
702 }
703
704 // user path
705 addMultiLibPathsLibraries("java.library", getJavaLibraryUserPaths(), libName, baseNames, paths, true);
706
707 // Add current working directory
708 final String userDir = getJavaCurrentWorkingDir();
709 if(null != userDir && !userDir.isEmpty()) {
710 addCanonicalPaths("add.user.dir.std", userDir, baseNames, paths, true);
711
712 // Add current working directory + natives/os-arch/ + library names
713 // to handle Bug 1145 cc1 using an unpacked fat-jar
714 addCanonicalPaths("add.user.dir.fat", userDir+File.separator+"natives"+File.separator+PlatformPropsImpl.os_and_arch, baseNames, paths, true);
715 }
716
717 if( !searchSystemPathFirst ) {
718 addMultiLibPathsLibraries("sun.boot.library", getJavaLibrarySystemPaths(), libName, baseNames, paths, true);
719 addClassLoaderPaths(libName, paths, loader);
720 if( searchOSSystemPath ) {
721 addOSSystemLibraryPaths(libName, baseNames, paths);
722 }
723 }
724
725 if (DEBUG) {
726 System.err.println("NativeLibrary.enumerateLibraryPaths: done: "+paths.toString());
727 }
728 return paths.getData();
729 }
730
731 /** Add OS system library path, if found */
732 private static final void addOSSystemLibraryPaths(final String libName, final String[] baseNames, final List<LibPath> paths) {
733 // Utilize system's library path environment variable first
734 addMultiLibPathsLibraries("system", getSystemEnvLibraryPaths(), libName, baseNames, paths, false);
735
736 // Add just the library names to use the OS's search algorithm
737 for (int i = 0; i < baseNames.length; i++) {
738 final LibPath lp = LibPath.createRelative(baseNames[i]);
739 if( paths.add( lp ) ) {
740 if (DEBUG) {
741 System.err.println("NativeLibrary.enumerateLibraryPaths: add.ssp_default: " + lp);
742 }
743 }
744 }
745 // Add probable Mac OS X-specific paths
746 if (isOSX) {
747 // Add historical location
748 addCanonicalPaths("add.ssp_1st_macos_old", "/Library/Frameworks/" + libName + ".framework", baseNames, paths, false);
749 // Add current location
750 addCanonicalPaths("add.ssp_1st_macos_cur", "/System/Library/Frameworks/" + libName + ".framework", baseNames, paths, false);
751 }
752 }
753 /** Add Java ClassLoader library path, if found */
754 private static final void addClassLoaderPaths(final String libName, final List<LibPath> paths, final ClassLoader loader) {
755 // The idea to ask the ClassLoader to find the library is borrowed
756 // from the LWJGL library
757 final String clPath = findLibrary(libName, loader);
758 if (clPath != null) {
759 final LibPath lp = LibPath.createAbsolute(clPath);
760 if( paths.add( lp ) ) {
761 if (DEBUG) {
762 System.err.println("NativeLibrary.enumerateLibraryPaths: add.clp: "+clPath);
763 }
764 }
765 }
766 }
767 private static final void addMultiLibPathsLibraries(final String id, final String multidirs, final String libName, final String[] baseNames, final List<LibPath> paths,
768 final boolean addToSearchPath)
769 {
770 if( null == multidirs || multidirs.isEmpty() ) {
771 return;
772 }
773 int count = 0;
774 final StringTokenizer tokenizer = new StringTokenizer(multidirs, File.pathSeparator);
775 while (tokenizer.hasMoreTokens()) {
776 addAbstractPaths("add."+id+".path_"+count, tokenizer.nextToken(), baseNames, paths, addToSearchPath);
777 ++count;
778 }
779 }
780
781 private static final void addAbstractPaths(final String cause, final String parent, final String[] baseNames, final List<LibPath> paths, final boolean addToSearchPath) {
782 if( null == parent || parent.isEmpty() ) {
783 return;
784 }
785 addCanonicalPaths(cause, new File(parent), baseNames, paths, addToSearchPath); // we canonicalize again in addCanonicalPaths
786 }
787 private static final void addCanonicalPaths(final String cause, final String parent, final String[] baseNames, final List<LibPath> paths, final boolean addToSearchPath) {
788 if( null == parent || parent.isEmpty() ) {
789 return;
790 }
791 addCanonicalPaths(cause, new File(parent), baseNames, paths, addToSearchPath);
792 }
793 private static final void addCanonicalPaths(final String cause, final File can_parent, final String[] baseNames, final List<LibPath> paths, final boolean addToSearchPath) {
794 for (int j = 0; j < baseNames.length; j++) {
795 final String ps = can_parent.getPath() + File.separator + baseNames[j];
796 File fps;
797 try {
798 fps = new File(ps).getCanonicalFile(); // be sure with complete path
799 } catch (final IOException e) {
800 System.err.println("NativeLibrary.addCanonicalPaths: "+cause+": Exception "+e.getMessage()+", from path '"+ps+"'");
801 return;
802 }
803 if( fps.exists() ) {
804 final LibPath p = addToSearchPath ? LibPath.createExtra(fps.getPath(), fps.getParent()) : LibPath.createAbsolute(fps.getPath());
805 if( paths.add(p) ) {
806 if (DEBUG) {
807 System.err.println("NativeLibrary.addCanonicalPaths: "+cause+": Added "+p+", from path '"+ps+"'");
808 }
809 }
810 }
811 }
812 }
813
814 private static final String selectName(final String windowsLibName,
815 final String unixLibName,
816 final String macOSXLibName) {
817 switch (PlatformPropsImpl.OS_TYPE) {
818 case WINDOWS:
819 return windowsLibName;
820
821 case MACOS:
822 case IOS:
823 return macOSXLibName;
824
825 default:
826 return unixLibName;
827 }
828 }
829
830 private static final String[] buildNames(final String libName) {
831 // If the library name already has the prefix / suffix added
832 // (principally because we want to force a version number on Unix
833 // operating systems) then just return the library name.
834 final String libBaseNameLC;
835 try {
836 libBaseNameLC = IOUtil.getBasename(libName).toLowerCase();
837 } catch (final URISyntaxException uriEx) {
838 throw new IllegalArgumentException(uriEx);
839 }
840
841 int prefixIdx = -1;
842 for(int i=0; i<prefixes.length && 0 > prefixIdx; i++) {
843 if (libBaseNameLC.startsWith(prefixes[i])) {
844 prefixIdx = i;
845 }
846 }
847 if( 0 <= prefixIdx ) {
848 for(int i=0; i<suffixes.length; i++) {
849 if (libBaseNameLC.endsWith(suffixes[i])) {
850 return new String[] { libName };
851 }
852 }
853 int suffixIdx = -1;
854 for(int i=0; i<suffixes.length && 0 > suffixIdx; i++) {
855 suffixIdx = libBaseNameLC.indexOf(suffixes[i]);
856 }
857 boolean ok = true;
858 if (suffixIdx >= 0) {
859 // Check to see if everything after it is a Unix version number
860 for (int i = suffixIdx + suffixes[0].length();
861 i < libName.length();
862 i++) {
863 final char c = libName.charAt(i);
864 if (!(c == '.' || (c >= '0' && c <= '9'))) {
865 ok = false;
866 break;
867 }
868 }
869 if (ok) {
870 return new String[] { libName };
871 }
872 }
873 }
874
875 final String[] res = new String[prefixes.length * suffixes.length + ( isOSX ? 1 : 0 )];
876 int idx = 0;
877 for (int i = 0; i < prefixes.length; i++) {
878 for (int j = 0; j < suffixes.length; j++) {
879 res[idx++] = prefixes[i] + libName + suffixes[j];
880 }
881 }
882 if ( isOSX ) {
883 // Plain library-base-name in Framework folder
884 res[idx++] = libName;
885 }
886 return res;
887 }
888
889 private static boolean initializedFindLibraryMethod = false;
890 private static Method findLibraryMethod = null;
891 private static final String findLibraryImpl(final String libName, final ClassLoader loader) {
892 if( PlatformPropsImpl.JAVA_9 ) {
893 return null;
894 }
895 if (loader == null) {
896 return null;
897 }
898 if (!initializedFindLibraryMethod) {
899 SecurityUtil.doPrivileged(new PrivilegedAction<Object>() {
900 @Override
901 public Object run() {
902 try {
903 findLibraryMethod = ClassLoader.class.getDeclaredMethod("findLibrary",
904 new Class[] { String.class });
905 findLibraryMethod.setAccessible(true);
906 } catch (final Exception e) {
907 // Fail silently disabling this functionality
908 }
909 initializedFindLibraryMethod = true;
910 return null;
911 }
912 });
913 }
914 if (findLibraryMethod != null) {
915 try {
916 return SecurityUtil.doPrivileged(new PrivilegedAction<String>() {
917 @Override
918 public String run() {
919 try {
920 return (String) findLibraryMethod.invoke(loader, new Object[] { libName });
921 } catch (final Exception e) {
922 throw new RuntimeException(e);
923 }
924 }
925 });
926 } catch (final Exception e) {
927 if (DEBUG) {
928 e.printStackTrace();
929 }
930 // Fail silently and continue with other search algorithms
931 }
932 }
933 return null;
934 }
935 public static final String findLibrary(final String libName, final ClassLoader loader) {
936 String res = null;
937 if( TempJarCache.isInitialized(true) ) {
938 res = TempJarCache.findLibrary(libName);
939 if (DEBUG) {
940 System.err.println("NativeLibrary.findLibrary(<"+libName+">) (TempJarCache): "+res);
941 }
942 }
943 if(null == res) {
944 res = findLibraryImpl(libName, loader);
945 if (DEBUG) {
946 System.err.println("NativeLibrary.findLibrary(<"+libName+">, "+loader+") (CL): "+res);
947 }
948 }
949 return res;
950 }
951}
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...