Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java
67771 views
1
/*
2
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package jdk.internal.loader;
26
27
import jdk.internal.misc.VM;
28
import jdk.internal.ref.CleanerFactory;
29
import jdk.internal.util.StaticProperty;
30
31
import java.io.File;
32
import java.io.IOException;
33
import java.security.AccessController;
34
import java.security.PrivilegedAction;
35
import java.util.ArrayDeque;
36
import java.util.Deque;
37
import java.util.HashSet;
38
import java.util.Objects;
39
import java.util.Map;
40
import java.util.Set;
41
import java.util.concurrent.ConcurrentHashMap;
42
43
/**
44
* Native libraries are loaded via {@link System#loadLibrary(String)},
45
* {@link System#load(String)}, {@link Runtime#loadLibrary(String)} and
46
* {@link Runtime#load(String)}. They are caller-sensitive.
47
*
48
* Each class loader has a NativeLibraries instance to register all of its
49
* loaded native libraries. System::loadLibrary (and other APIs) only
50
* allows a native library to be loaded by one class loader, i.e. one
51
* NativeLibraries instance. Any attempt to load a native library that
52
* has already been loaded by a class loader with another class loader
53
* will fail.
54
*/
55
public final class NativeLibraries {
56
private static final boolean loadLibraryOnlyIfPresent = ClassLoaderHelper.loadLibraryOnlyIfPresent();
57
private final Map<String, NativeLibraryImpl> libraries = new ConcurrentHashMap<>();
58
private final ClassLoader loader;
59
// caller, if non-null, is the fromClass parameter for NativeLibraries::loadLibrary
60
// unless specified
61
private final Class<?> caller; // may be null
62
private final boolean searchJavaLibraryPath;
63
// loading JNI native libraries
64
private final boolean isJNI;
65
66
/**
67
* Creates a NativeLibraries instance for loading JNI native libraries
68
* via for System::loadLibrary use.
69
*
70
* 1. Support of auto-unloading. The loaded native libraries are unloaded
71
* when the class loader is reclaimed.
72
* 2. Support of linking of native method. See JNI spec.
73
* 3. Restriction on a native library that can only be loaded by one class loader.
74
* Each class loader manages its own set of native libraries.
75
* The same JNI native library cannot be loaded into more than one class loader.
76
*
77
* This static factory method is intended only for System::loadLibrary use.
78
*
79
* @see <a href="${docroot}/specs/jni/invocation.html##library-and-version-management">
80
* JNI Specification: Library and Version Management</a>
81
*/
82
public static NativeLibraries jniNativeLibraries(ClassLoader loader) {
83
return new NativeLibraries(loader);
84
}
85
86
/**
87
* Creates a raw NativeLibraries instance that has the following properties:
88
* 1. Native libraries loaded in this raw NativeLibraries instance are
89
* not JNI native libraries. Hence JNI_OnLoad and JNI_OnUnload will
90
* be ignored. No support for linking of native method.
91
* 2. Native libraries not auto-unloaded. They may be explicitly unloaded
92
* via NativeLibraries::unload.
93
* 3. No relationship with class loaders.
94
*
95
* This static factory method is restricted for JDK trusted class use.
96
*/
97
public static NativeLibraries rawNativeLibraries(Class<?> trustedCaller,
98
boolean searchJavaLibraryPath) {
99
return new NativeLibraries(trustedCaller, searchJavaLibraryPath);
100
}
101
102
private NativeLibraries(ClassLoader loader) {
103
// for null loader, default the caller to this class and
104
// do not search java.library.path
105
this.loader = loader;
106
this.caller = loader != null ? null : NativeLibraries.class;
107
this.searchJavaLibraryPath = loader != null ? true : false;
108
this.isJNI = true;
109
}
110
111
/*
112
* Constructs a NativeLibraries instance of no relationship with class loaders
113
* and disabled auto unloading.
114
*/
115
private NativeLibraries(Class<?> caller, boolean searchJavaLibraryPath) {
116
Objects.requireNonNull(caller);
117
if (!VM.isSystemDomainLoader(caller.getClassLoader())) {
118
throw new IllegalArgumentException("must be JDK trusted class");
119
}
120
this.loader = caller.getClassLoader();
121
this.caller = caller;
122
this.searchJavaLibraryPath = searchJavaLibraryPath;
123
this.isJNI = false;
124
}
125
126
/*
127
* Find the address of the given symbol name from the native libraries
128
* loaded in this NativeLibraries instance.
129
*/
130
public long find(String name) {
131
if (libraries.isEmpty())
132
return 0;
133
134
// the native libraries map may be updated in another thread
135
// when a native library is being loaded. No symbol will be
136
// searched from it yet.
137
for (NativeLibrary lib : libraries.values()) {
138
long entry = lib.find(name);
139
if (entry != 0) return entry;
140
}
141
return 0;
142
}
143
144
/*
145
* Load a native library from the given file. Returns null if the given
146
* library is determined to be non-loadable, which is system-dependent.
147
*
148
* @param fromClass the caller class calling System::loadLibrary
149
* @param file the path of the native library
150
* @throws UnsatisfiedLinkError if any error in loading the native library
151
*/
152
@SuppressWarnings("removal")
153
public NativeLibrary loadLibrary(Class<?> fromClass, File file) {
154
// Check to see if we're attempting to access a static library
155
String name = findBuiltinLib(file.getName());
156
boolean isBuiltin = (name != null);
157
if (!isBuiltin) {
158
name = AccessController.doPrivileged(new PrivilegedAction<>() {
159
public String run() {
160
try {
161
if (loadLibraryOnlyIfPresent && !file.exists()) {
162
return null;
163
}
164
return file.getCanonicalPath();
165
} catch (IOException e) {
166
return null;
167
}
168
}
169
});
170
if (name == null) {
171
return null;
172
}
173
}
174
return loadLibrary(fromClass, name, isBuiltin);
175
}
176
177
/**
178
* Returns a NativeLibrary of the given name.
179
*
180
* @param fromClass the caller class calling System::loadLibrary
181
* @param name library name
182
* @param isBuiltin built-in library
183
* @throws UnsatisfiedLinkError if the native library has already been loaded
184
* and registered in another NativeLibraries
185
*/
186
private NativeLibrary loadLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
187
ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
188
if (this.loader != loader) {
189
throw new InternalError(fromClass.getName() + " not allowed to load library");
190
}
191
192
synchronized (loadedLibraryNames) {
193
// find if this library has already been loaded and registered in this NativeLibraries
194
NativeLibrary cached = libraries.get(name);
195
if (cached != null) {
196
return cached;
197
}
198
199
// cannot be loaded by other class loaders
200
if (loadedLibraryNames.contains(name)) {
201
throw new UnsatisfiedLinkError("Native Library " + name +
202
" already loaded in another classloader");
203
}
204
205
/*
206
* When a library is being loaded, JNI_OnLoad function can cause
207
* another loadLibrary invocation that should succeed.
208
*
209
* We use a static stack to hold the list of libraries we are
210
* loading because this can happen only when called by the
211
* same thread because this block is synchronous.
212
*
213
* If there is a pending load operation for the library, we
214
* immediately return success; otherwise, we raise
215
* UnsatisfiedLinkError.
216
*/
217
for (NativeLibraryImpl lib : nativeLibraryContext) {
218
if (name.equals(lib.name())) {
219
if (loader == lib.fromClass.getClassLoader()) {
220
return lib;
221
} else {
222
throw new UnsatisfiedLinkError("Native Library " +
223
name + " is being loaded in another classloader");
224
}
225
}
226
}
227
228
NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin, isJNI);
229
// load the native library
230
nativeLibraryContext.push(lib);
231
try {
232
if (!lib.open()) {
233
return null; // fail to open the native library
234
}
235
// auto unloading is only supported for JNI native libraries
236
// loaded by custom class loaders that can be unloaded.
237
// built-in class loaders are never unloaded.
238
boolean autoUnload = isJNI && !VM.isSystemDomainLoader(loader)
239
&& loader != ClassLoaders.appClassLoader();
240
if (autoUnload) {
241
// register the loaded native library for auto unloading
242
// when the class loader is reclaimed, all native libraries
243
// loaded that class loader will be unloaded.
244
// The entries in the libraries map are not removed since
245
// the entire map will be reclaimed altogether.
246
CleanerFactory.cleaner().register(loader, lib.unloader());
247
}
248
} finally {
249
nativeLibraryContext.pop();
250
}
251
// register the loaded native library
252
loadedLibraryNames.add(name);
253
libraries.put(name, lib);
254
return lib;
255
}
256
}
257
258
/**
259
* Loads a native library from the system library path and java library path.
260
*
261
* @param name library name
262
*
263
* @throws UnsatisfiedLinkError if the native library has already been loaded
264
* and registered in another NativeLibraries
265
*/
266
public NativeLibrary loadLibrary(String name) {
267
assert name.indexOf(File.separatorChar) < 0;
268
assert caller != null;
269
270
return loadLibrary(caller, name);
271
}
272
273
/**
274
* Loads a native library from the system library path and java library path.
275
*
276
* @param name library name
277
* @param fromClass the caller class calling System::loadLibrary
278
*
279
* @throws UnsatisfiedLinkError if the native library has already been loaded
280
* and registered in another NativeLibraries
281
*/
282
public NativeLibrary loadLibrary(Class<?> fromClass, String name) {
283
assert name.indexOf(File.separatorChar) < 0;
284
285
NativeLibrary lib = findFromPaths(LibraryPaths.SYS_PATHS, fromClass, name);
286
if (lib == null && searchJavaLibraryPath) {
287
lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name);
288
}
289
return lib;
290
}
291
292
/**
293
* Unloads the given native library
294
*
295
* @param lib native library
296
*/
297
public void unload(NativeLibrary lib) {
298
if (isJNI) {
299
throw new UnsupportedOperationException("explicit unloading cannot be used with auto unloading");
300
}
301
Objects.requireNonNull(lib);
302
synchronized (loadedLibraryNames) {
303
NativeLibraryImpl nl = libraries.remove(lib.name());
304
if (nl != lib) {
305
throw new IllegalArgumentException(lib.name() + " not loaded by this NativeLibraries instance");
306
}
307
// unload the native library and also remove from the global name registry
308
nl.unloader().run();
309
}
310
}
311
312
private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
313
for (String path : paths) {
314
File libfile = new File(path, System.mapLibraryName(name));
315
NativeLibrary nl = loadLibrary(fromClass, libfile);
316
if (nl != null) {
317
return nl;
318
}
319
libfile = ClassLoaderHelper.mapAlternativeName(libfile);
320
if (libfile != null) {
321
nl = loadLibrary(fromClass, libfile);
322
if (nl != null) {
323
return nl;
324
}
325
}
326
}
327
return null;
328
}
329
330
/**
331
* NativeLibraryImpl denotes a loaded native library instance.
332
* Each NativeLibraries contains a map of loaded native libraries in the
333
* private field {@code libraries}.
334
*
335
* Every native library requires a particular version of JNI. This is
336
* denoted by the private {@code jniVersion} field. This field is set by
337
* the VM when it loads the library, and used by the VM to pass the correct
338
* version of JNI to the native methods.
339
*/
340
static class NativeLibraryImpl implements NativeLibrary {
341
// the class from which the library is loaded, also indicates
342
// the loader this native library belongs.
343
final Class<?> fromClass;
344
// the canonicalized name of the native library.
345
// or static library name
346
final String name;
347
// Indicates if the native library is linked into the VM
348
final boolean isBuiltin;
349
// Indicate if this is JNI native library
350
final boolean isJNI;
351
352
// opaque handle to native library, used in native code.
353
long handle;
354
// the version of JNI environment the native library requires.
355
int jniVersion;
356
357
NativeLibraryImpl(Class<?> fromClass, String name, boolean isBuiltin, boolean isJNI) {
358
assert !isBuiltin || isJNI : "a builtin native library must be JNI library";
359
360
this.fromClass = fromClass;
361
this.name = name;
362
this.isBuiltin = isBuiltin;
363
this.isJNI = isJNI;
364
}
365
366
@Override
367
public String name() {
368
return name;
369
}
370
371
@Override
372
public long find(String name) {
373
return findEntry0(this, name);
374
}
375
376
Runnable unloader() {
377
return new Unloader(name, handle, isBuiltin, isJNI);
378
}
379
380
/*
381
* Loads the named native library
382
*/
383
boolean open() {
384
if (handle != 0) {
385
throw new InternalError("Native library " + name + " has been loaded");
386
}
387
388
return load(this, name, isBuiltin, isJNI, loadLibraryOnlyIfPresent);
389
}
390
}
391
392
/*
393
* The run() method will be invoked when this class loader becomes
394
* phantom reachable to unload the native library.
395
*/
396
static class Unloader implements Runnable {
397
// This represents the context when a native library is unloaded
398
// and getFromClass() will return null,
399
static final NativeLibraryImpl UNLOADER =
400
new NativeLibraryImpl(null, "dummy", false, false);
401
402
final String name;
403
final long handle;
404
final boolean isBuiltin;
405
final boolean isJNI;
406
407
Unloader(String name, long handle, boolean isBuiltin, boolean isJNI) {
408
assert !isBuiltin || isJNI : "a builtin native library must be JNI library";
409
if (handle == 0) {
410
throw new IllegalArgumentException(
411
"Invalid handle for native library " + name);
412
}
413
414
this.name = name;
415
this.handle = handle;
416
this.isBuiltin = isBuiltin;
417
this.isJNI = isJNI;
418
}
419
420
@Override
421
public void run() {
422
synchronized (loadedLibraryNames) {
423
/* remove the native library name */
424
if (!loadedLibraryNames.remove(name)) {
425
throw new IllegalStateException(name + " has already been unloaded");
426
}
427
nativeLibraryContext.push(UNLOADER);
428
try {
429
unload(name, isBuiltin, isJNI, handle);
430
} finally {
431
nativeLibraryContext.pop();
432
}
433
}
434
}
435
}
436
437
/*
438
* Holds system and user library paths derived from the
439
* {@code java.library.path} and {@code sun.boot.library.path} system
440
* properties. The system properties are eagerly read at bootstrap, then
441
* lazily parsed on first use to avoid initialization ordering issues.
442
*/
443
static class LibraryPaths {
444
// The paths searched for libraries
445
static final String[] SYS_PATHS = ClassLoaderHelper.parsePath(StaticProperty.sunBootLibraryPath());
446
static final String[] USER_PATHS = ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath());
447
}
448
449
// All native libraries we've loaded.
450
// This also serves as the lock to obtain nativeLibraries
451
// and write to nativeLibraryContext.
452
private static final Set<String> loadedLibraryNames = new HashSet<>();
453
454
// native libraries being loaded
455
private static Deque<NativeLibraryImpl> nativeLibraryContext = new ArrayDeque<>(8);
456
457
// Invoked in the VM to determine the context class in JNI_OnLoad
458
// and JNI_OnUnload
459
private static Class<?> getFromClass() {
460
if (nativeLibraryContext.isEmpty()) { // only default library
461
return Object.class;
462
}
463
return nativeLibraryContext.peek().fromClass;
464
}
465
466
// JNI FindClass expects the caller class if invoked from JNI_OnLoad
467
// and JNI_OnUnload is NativeLibrary class
468
private static native boolean load(NativeLibraryImpl impl, String name,
469
boolean isBuiltin, boolean isJNI,
470
boolean throwExceptionIfFail);
471
private static native void unload(String name, boolean isBuiltin, boolean isJNI, long handle);
472
private static native String findBuiltinLib(String name);
473
private static native long findEntry0(NativeLibraryImpl lib, String name);
474
}
475
476