jaulib v1.3.0
Jau Support Library (C++, Java, ..)
NativeLibrary.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Author: Kenneth Bradley Russell
4 * Copyright (c) 2020 Gothel Software e.K.
5 * Copyright (c) 2011 Gothel Software e.K.
6 * Copyright (c) 2011 JogAmp Community.
7 * Copyright (c) 2006 Sun Microsystems, Inc.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining
10 * a copy of this software and associated documentation files (the
11 * "Software"), to deal in the Software without restriction, including
12 * without limitation the rights to use, copy, modify, merge, publish,
13 * distribute, sublicense, and/or sell copies of the Software, and to
14 * permit persons to whom the Software is furnished to do so, subject to
15 * the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 */
28
29package org.jau.sys.dl;
30
31import java.util.Iterator;
32import java.util.List;
33
34import org.jau.lang.ExceptionUtils;
35import org.jau.sys.JNILibrary;
36import org.jau.sys.PlatformProps;
37import org.jau.sys.RuntimeProps;
38
39import jau.sys.dl.BionicDynamicLinker32bitImpl;
40import jau.sys.dl.BionicDynamicLinker64BitImpl;
41import jau.sys.dl.MacOSXDynamicLinkerImpl;
42import jau.sys.dl.PosixDynamicLinkerImpl;
43import jau.sys.dl.WindowsDynamicLinkerImpl;
44
45/** Provides low-level, relatively platform-independent access to
46 shared ("native") libraries. The core library routines
47 <code>System.load()</code> and <code>System.loadLibrary()</code>
48 in general provide suitable functionality for applications using
49 native code, but are not flexible enough to support certain kinds
50 of glue code generation and deployment strategies. This class
51 supports direct linking of native libraries to other shared
52 objects not necessarily installed on the system (in particular,
53 via the use of dlopen(RTLD_GLOBAL) on Unix platforms) as well as
54 manual lookup of function names to support e.g. GlueGen's
55 ProcAddressTable glue code generation style without additional
56 supporting code needed in the generated library. */
57
58public final class NativeLibrary implements DynamicLookupHelper {
59 private final DynamicLinker dynLink;
60
61 // Platform-specific representation for the handle to the open
62 // library. This is an HMODULE on Windows and a void* (the result of
63 // a dlopen() call) on Unix and Mac OS X platforms.
64 private long libraryHandle;
65
66 // May as well keep around the path to the library we opened
67 private final String libraryPath;
68
69 private final boolean global;
70
71 // Private constructor to prevent arbitrary instances from floating around
72 private NativeLibrary(final DynamicLinker dynLink, final long libraryHandle, final String libraryPath, final boolean global) {
73 this.dynLink = dynLink;
74 this.libraryHandle = libraryHandle;
75 this.libraryPath = libraryPath;
76 this.global = global;
77 if (DEBUG) {
78 System.err.println("NativeLibrary.open(): Successfully loaded: " + this);
79 }
80 }
81
82 @Override
83 public final String toString() {
84 return "NativeLibrary[" + dynLink.getClass().getSimpleName() + ", " + libraryPath + ", 0x" + Long.toHexString(libraryHandle) + ", global " + global + "]";
85 }
86
87 /** Opens the given native library, assuming it has the same base
88 name on all platforms.
89 <p>
90 The {@code searchSystemPath} argument changes the behavior to
91 either use the default system path or not at all.
92 </p>
93 <p>
94 Assuming {@code searchSystemPath} is {@code true},
95 the {@code searchSystemPathFirst} argument changes the behavior to first
96 search the default system path rather than searching it last.
97 </p>
98 * @param libName library name, with or without prefix and suffix
99 * @param searchSystemPath if {@code true} library shall be searched in the system path <i>(default)</i>, otherwise {@code false}.
100 * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
101 * if {@code searchSystemPath} is {@code false} this argument is ignored.
102 * @param loader {@link ClassLoader} to locate the library
103 * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process.
104 * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded.
105 * @throws SecurityException if user is not granted access for the named library.
106 * @since 0.3.0
107 */
108 public static final NativeLibrary open(final String libName,
109 final boolean searchSystemPath,
110 final boolean searchSystemPathFirst,
111 final ClassLoader loader, final boolean global) throws SecurityException {
112 return open(libName, libName, libName, searchSystemPath, searchSystemPathFirst, loader, global);
113 }
114
115 /** Opens the given native library, assuming it has the given base
116 names (no "lib" prefix or ".dll/.so/.dylib" suffix) on the
117 Windows, Unix and Mac OS X platforms, respectively, and in the
118 context of the specified ClassLoader, which is used to help find
119 the library in the case of e.g. Java Web Start.
120 <p>
121 The {@code searchSystemPath} argument changes the behavior to
122 either use the default system path or not at all.
123 </p>
124 <p>
125 Assuming {@code searchSystemPath} is {@code true},
126 the {@code searchSystemPathFirst} argument changes the behavior to first
127 search the default system path rather than searching it last.
128 </p>
129 Note that we do not currently handle DSO versioning on Unix.
130 Experience with JOAL and OpenAL has shown that it is extremely
131 problematic to rely on a specific .so version (for one thing,
132 ClassLoader.findLibrary on Unix doesn't work with files not
133 ending in .so, for example .so.0), and in general if this
134 dynamic loading facility is used correctly the version number
135 will be irrelevant.
136 * @param libName windows library name, with or without prefix and suffix
137 * @param unixLibName unix library name, with or without prefix and suffix
138 * @param macOSXLibName mac-osx library name, with or without prefix and suffix
139 * @param searchSystemPath if {@code true} library shall be searched in the system path <i>(default)</i>, otherwise {@code false}.
140 * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
141 * if {@code searchSystemPath} is {@code false} this argument is ignored.
142 * @param loader {@link ClassLoader} to locate the library
143 * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process.
144 * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded.
145 * @throws SecurityException if user is not granted access for the named library.
146 */
147 public static final NativeLibrary open(final String libName,
148 final String unixLibName,
149 final String macOSXLibName,
150 final boolean searchSystemPath,
151 final boolean searchSystemPathFirst,
152 final ClassLoader loader, final boolean global) throws SecurityException {
153 final List<String> possiblePaths = JNILibrary.enumerateLibraryPaths(libName,
154 unixLibName,
155 macOSXLibName,
156 searchSystemPath, searchSystemPathFirst,
157 loader);
158 RuntimeProps.initSingleton(); // loads native jaulib library
159
160 final DynamicLinker dynLink = getDynamicLinker();
161
162 // Iterate down these and see which one if any we can actually find.
163 for (final Iterator<String> iter = possiblePaths.iterator(); iter.hasNext(); ) {
164 final String path = iter.next();
165 if (DEBUG) {
166 System.err.println("NativeLibrary.open(global "+global+"): Trying to load " + path);
167 }
168 long res;
169 Throwable t = null;
170 try {
171 if(global) {
172 res = dynLink.openLibraryGlobal(path, DEBUG);
173 } else {
174 res = dynLink.openLibraryLocal(path, DEBUG);
175 }
176 } catch (final Throwable t1) {
177 t = t1;
178 res = 0;
179 }
180 if ( 0 != res ) {
181 return new NativeLibrary(dynLink, res, path, global);
182 } else if( DEBUG ) {
183 if( null != t ) {
184 System.err.println("NativeLibrary.open: Caught "+t.getClass().getSimpleName()+": "+t.getMessage());
185 }
186 String errstr;
187 try {
188 errstr = dynLink.getLastError();
189 } catch (final Throwable t2) { errstr=null; }
190 System.err.println("NativeLibrary.open: Last error "+errstr);
191 if( null != t ) {
192 t.printStackTrace();
193 }
194 }
195 }
196
197 if (DEBUG) {
198 System.err.println("NativeLibrary.open(global "+global+"): Did not succeed in loading (" + libName + ", " + unixLibName + ", " + macOSXLibName + ")");
199 }
200
201 // For now, just return null to indicate the open operation didn't
202 // succeed (could also throw an exception if we could tell which
203 // of the openLibrary operations actually failed)
204 return null;
205 }
206
207 @Override
208 public final void claimAllLinkPermission() throws SecurityException {
209 dynLink.claimAllLinkPermission();
210 }
211 @Override
212 public final void releaseAllLinkPermission() throws SecurityException {
213 dynLink.releaseAllLinkPermission();
214 }
215
216 @Override
217 public final long dynamicLookupFunction(final String funcName) throws SecurityException {
218 if ( 0 == libraryHandle ) {
219 throw new RuntimeException("Library is not open");
220 }
221 return dynLink.lookupSymbol(libraryHandle, funcName);
222 }
223
224 @Override
225 public final boolean isFunctionAvailable(final String funcName) throws SecurityException {
226 if ( 0 == libraryHandle ) {
227 throw new RuntimeException("Library is not open");
228 }
229 return 0 != dynLink.lookupSymbol(libraryHandle, funcName);
230 }
231
232 /** Looks up the given function name in all loaded libraries.
233 * @throws SecurityException if user is not granted access for the named library.
234 */
235 public final long dynamicLookupFunctionGlobal(final String funcName) throws SecurityException {
236 return dynLink.lookupSymbolGlobal(funcName);
237 }
238
239 /* pp */ final DynamicLinker dynamicLinker() { return dynLink; }
240
241 /* pp */ static DynamicLinker getDynamicLinker() {
242 final DynamicLinker dynLink;
243 switch (PlatformProps.OS) {
244 case WINDOWS:
245 dynLink = new WindowsDynamicLinkerImpl();
246 break;
247
248 case MACOS:
249 case IOS:
250 dynLink = new MacOSXDynamicLinkerImpl();
251 break;
252
253 case ANDROID:
255 dynLink = new BionicDynamicLinker32bitImpl();
256 } else {
257 dynLink = new BionicDynamicLinker64BitImpl();
258 }
259 break;
260
261 default:
262 dynLink = new PosixDynamicLinkerImpl();
263 break;
264 }
265 return dynLink;
266 }
267
268 /** Retrieves the low-level library handle from this NativeLibrary
269 object. On the Windows platform this is an HMODULE, and on Unix
270 and Mac OS X platforms the void* result of calling dlopen(). */
271 public final long getLibraryHandle() {
272 return libraryHandle;
273 }
274
275 /** Retrieves the path under which this library was opened. */
276 public final String getLibraryPath() {
277 return libraryPath;
278 }
279
280 /** Closes this native library. Further lookup operations are not
281 allowed after calling this method.
282 * @throws SecurityException if user is not granted access for the named library.
283 */
284 public final void close() throws SecurityException {
285 if (DEBUG) {
286 System.err.println("NativeLibrary.close(): closing " + this);
287 }
288 if ( 0 == libraryHandle ) {
289 throw new RuntimeException("Library already closed");
290 }
291 final long handle = libraryHandle;
292 libraryHandle = 0;
293 dynLink.closeLibrary(handle, DEBUG);
294 if (DEBUG) {
295 System.err.println("NativeLibrary.close(): Successfully closed " + this);
296 ExceptionUtils.dumpStack(System.err);
297 }
298 }
299}
static void dumpStack(final PrintStream out)
Static JNI Native Libraries handler.
Definition: JNILibrary.java:47
static final List< String > enumerateLibraryPaths(final String libName, final boolean searchSystemPath, final boolean searchSystemPathFirst, final ClassLoader loader)
Given the base library names (no prefixes/suffixes) for the various platforms, enumerate the possible...
Platform Properties derived from Java properties.
static final OSType OS
static final CPUType CPU
Runtime platform properties derived from PlatformProps and runtime query.
Provides low-level, relatively platform-independent access to shared ("native") libraries.
static final NativeLibrary open(final String libName, final boolean searchSystemPath, final boolean searchSystemPathFirst, final ClassLoader loader, final boolean global)
Opens the given native library, assuming it has the same base name on all platforms.
final String getLibraryPath()
Retrieves the path under which this library was opened.
static final NativeLibrary open(final String libName, final String unixLibName, final String macOSXLibName, final boolean searchSystemPath, final boolean searchSystemPathFirst, final ClassLoader loader, final boolean global)
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'.
final long dynamicLookupFunctionGlobal(final String funcName)
Looks up the given function name in all loaded libraries.
final boolean isFunctionAvailable(final String funcName)
Queries whether function 'funcName' is available.
final long getLibraryHandle()
Retrieves the low-level library handle from this NativeLibrary object.
final void close()
Closes this native library.
Low level secure dynamic linker access.
long lookupSymbol(long libraryHandle, String symbolName)
Security checks are implicit by previous call of openLibraryLocal(String, boolean) or openLibraryGlob...
long openLibraryLocal(String pathname, boolean debug)
If a SecurityManager is installed, user needs link permissions for the named library.
long openLibraryGlobal(String pathname, boolean debug)
If a SecurityManager is installed, user needs link permissions for the named library.
String getLastError()
Returns a string containing the last error.
long lookupSymbolGlobal(String symbolName)
If a SecurityManager is installed, user needs link permissions for all libraries, i....