Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java
41159 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
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 file does not exist.
146
*
147
* @param fromClass the caller class calling System::loadLibrary
148
* @param file the path of the native library
149
* @throws UnsatisfiedLinkError if any error in loading the native library
150
*/
151
@SuppressWarnings("removal")
152
public NativeLibrary loadLibrary(Class<?> fromClass, File file) {
153
// Check to see if we're attempting to access a static library
154
String name = findBuiltinLib(file.getName());
155
boolean isBuiltin = (name != null);
156
if (!isBuiltin) {
157
name = AccessController.doPrivileged(new PrivilegedAction<>() {
158
public String run() {
159
try {
160
return file.exists() ? file.getCanonicalPath() : null;
161
} catch (IOException e) {
162
return null;
163
}
164
}
165
});
166
if (name == null) {
167
return null;
168
}
169
}
170
return loadLibrary(fromClass, name, isBuiltin);
171
}
172
173
/**
174
* Returns a NativeLibrary of the given name.
175
*
176
* @param fromClass the caller class calling System::loadLibrary
177
* @param name library name
178
* @param isBuiltin built-in library
179
* @throws UnsatisfiedLinkError if the native library has already been loaded
180
* and registered in another NativeLibraries
181
*/
182
private NativeLibrary loadLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
183
ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
184
if (this.loader != loader) {
185
throw new InternalError(fromClass.getName() + " not allowed to load library");
186
}
187
188
synchronized (loadedLibraryNames) {
189
// find if this library has already been loaded and registered in this NativeLibraries
190
NativeLibrary cached = libraries.get(name);
191
if (cached != null) {
192
return cached;
193
}
194
195
// cannot be loaded by other class loaders
196
if (loadedLibraryNames.contains(name)) {
197
throw new UnsatisfiedLinkError("Native Library " + name +
198
" already loaded in another classloader");
199
}
200
201
/*
202
* When a library is being loaded, JNI_OnLoad function can cause
203
* another loadLibrary invocation that should succeed.
204
*
205
* We use a static stack to hold the list of libraries we are
206
* loading because this can happen only when called by the
207
* same thread because this block is synchronous.
208
*
209
* If there is a pending load operation for the library, we
210
* immediately return success; otherwise, we raise
211
* UnsatisfiedLinkError.
212
*/
213
for (NativeLibraryImpl lib : nativeLibraryContext) {
214
if (name.equals(lib.name())) {
215
if (loader == lib.fromClass.getClassLoader()) {
216
return lib;
217
} else {
218
throw new UnsatisfiedLinkError("Native Library " +
219
name + " is being loaded in another classloader");
220
}
221
}
222
}
223
224
NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin, isJNI);
225
// load the native library
226
nativeLibraryContext.push(lib);
227
try {
228
if (!lib.open()) {
229
return null; // fail to open the native library
230
}
231
// auto unloading is only supported for JNI native libraries
232
// loaded by custom class loaders that can be unloaded.
233
// built-in class loaders are never unloaded.
234
boolean autoUnload = isJNI && !VM.isSystemDomainLoader(loader)
235
&& loader != ClassLoaders.appClassLoader();
236
if (autoUnload) {
237
// register the loaded native library for auto unloading
238
// when the class loader is reclaimed, all native libraries
239
// loaded that class loader will be unloaded.
240
// The entries in the libraries map are not removed since
241
// the entire map will be reclaimed altogether.
242
CleanerFactory.cleaner().register(loader, lib.unloader());
243
}
244
} finally {
245
nativeLibraryContext.pop();
246
}
247
// register the loaded native library
248
loadedLibraryNames.add(name);
249
libraries.put(name, lib);
250
return lib;
251
}
252
}
253
254
/**
255
* Loads a native library from the system library path and java library path.
256
*
257
* @param name library name
258
*
259
* @throws UnsatisfiedLinkError if the native library has already been loaded
260
* and registered in another NativeLibraries
261
*/
262
public NativeLibrary loadLibrary(String name) {
263
assert name.indexOf(File.separatorChar) < 0;
264
assert caller != null;
265
266
return loadLibrary(caller, name);
267
}
268
269
/**
270
* Loads a native library from the system library path and java library path.
271
*
272
* @param name library name
273
* @param fromClass the caller class calling System::loadLibrary
274
*
275
* @throws UnsatisfiedLinkError if the native library has already been loaded
276
* and registered in another NativeLibraries
277
*/
278
public NativeLibrary loadLibrary(Class<?> fromClass, String name) {
279
assert name.indexOf(File.separatorChar) < 0;
280
281
NativeLibrary lib = findFromPaths(LibraryPaths.SYS_PATHS, fromClass, name);
282
if (lib == null && searchJavaLibraryPath) {
283
lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name);
284
}
285
return lib;
286
}
287
288
/**
289
* Unloads the given native library
290
*
291
* @param lib native library
292
*/
293
public void unload(NativeLibrary lib) {
294
if (isJNI) {
295
throw new UnsupportedOperationException("explicit unloading cannot be used with auto unloading");
296
}
297
Objects.requireNonNull(lib);
298
synchronized (loadedLibraryNames) {
299
NativeLibraryImpl nl = libraries.remove(lib.name());
300
if (nl != lib) {
301
throw new IllegalArgumentException(lib.name() + " not loaded by this NativeLibraries instance");
302
}
303
// unload the native library and also remove from the global name registry
304
nl.unloader().run();
305
}
306
}
307
308
private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
309
for (String path : paths) {
310
File libfile = new File(path, System.mapLibraryName(name));
311
NativeLibrary nl = loadLibrary(fromClass, libfile);
312
if (nl != null) {
313
return nl;
314
}
315
libfile = ClassLoaderHelper.mapAlternativeName(libfile);
316
if (libfile != null) {
317
nl = loadLibrary(fromClass, libfile);
318
if (nl != null) {
319
return nl;
320
}
321
}
322
}
323
return null;
324
}
325
326
/**
327
* NativeLibraryImpl denotes a loaded native library instance.
328
* Each NativeLibraries contains a map of loaded native libraries in the
329
* private field {@code libraries}.
330
*
331
* Every native library requires a particular version of JNI. This is
332
* denoted by the private {@code jniVersion} field. This field is set by
333
* the VM when it loads the library, and used by the VM to pass the correct
334
* version of JNI to the native methods.
335
*/
336
static class NativeLibraryImpl implements NativeLibrary {
337
// the class from which the library is loaded, also indicates
338
// the loader this native library belongs.
339
final Class<?> fromClass;
340
// the canonicalized name of the native library.
341
// or static library name
342
final String name;
343
// Indicates if the native library is linked into the VM
344
final boolean isBuiltin;
345
// Indicate if this is JNI native library
346
final boolean isJNI;
347
348
// opaque handle to native library, used in native code.
349
long handle;
350
// the version of JNI environment the native library requires.
351
int jniVersion;
352
353
NativeLibraryImpl(Class<?> fromClass, String name, boolean isBuiltin, boolean isJNI) {
354
assert !isBuiltin || isJNI : "a builtin native library must be JNI library";
355
356
this.fromClass = fromClass;
357
this.name = name;
358
this.isBuiltin = isBuiltin;
359
this.isJNI = isJNI;
360
}
361
362
@Override
363
public String name() {
364
return name;
365
}
366
367
@Override
368
public long find(String name) {
369
return findEntry0(this, name);
370
}
371
372
Runnable unloader() {
373
return new Unloader(name, handle, isBuiltin, isJNI);
374
}
375
376
/*
377
* Loads the named native library
378
*/
379
boolean open() {
380
if (handle != 0) {
381
throw new InternalError("Native library " + name + " has been loaded");
382
}
383
384
return load(this, name, isBuiltin, isJNI);
385
}
386
}
387
388
/*
389
* The run() method will be invoked when this class loader becomes
390
* phantom reachable to unload the native library.
391
*/
392
static class Unloader implements Runnable {
393
// This represents the context when a native library is unloaded
394
// and getFromClass() will return null,
395
static final NativeLibraryImpl UNLOADER =
396
new NativeLibraryImpl(null, "dummy", false, false);
397
398
final String name;
399
final long handle;
400
final boolean isBuiltin;
401
final boolean isJNI;
402
403
Unloader(String name, long handle, boolean isBuiltin, boolean isJNI) {
404
assert !isBuiltin || isJNI : "a builtin native library must be JNI library";
405
if (handle == 0) {
406
throw new IllegalArgumentException(
407
"Invalid handle for native library " + name);
408
}
409
410
this.name = name;
411
this.handle = handle;
412
this.isBuiltin = isBuiltin;
413
this.isJNI = isJNI;
414
}
415
416
@Override
417
public void run() {
418
synchronized (loadedLibraryNames) {
419
/* remove the native library name */
420
if (!loadedLibraryNames.remove(name)) {
421
throw new IllegalStateException(name + " has already been unloaded");
422
}
423
nativeLibraryContext.push(UNLOADER);
424
try {
425
unload(name, isBuiltin, isJNI, handle);
426
} finally {
427
nativeLibraryContext.pop();
428
}
429
}
430
}
431
}
432
433
/*
434
* Holds system and user library paths derived from the
435
* {@code java.library.path} and {@code sun.boot.library.path} system
436
* properties. The system properties are eagerly read at bootstrap, then
437
* lazily parsed on first use to avoid initialization ordering issues.
438
*/
439
static class LibraryPaths {
440
// The paths searched for libraries
441
static final String[] SYS_PATHS = ClassLoaderHelper.parsePath(StaticProperty.sunBootLibraryPath());
442
static final String[] USER_PATHS = ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath());
443
}
444
445
// All native libraries we've loaded.
446
// This also serves as the lock to obtain nativeLibraries
447
// and write to nativeLibraryContext.
448
private static final Set<String> loadedLibraryNames = new HashSet<>();
449
450
// native libraries being loaded
451
private static Deque<NativeLibraryImpl> nativeLibraryContext = new ArrayDeque<>(8);
452
453
// Invoked in the VM to determine the context class in JNI_OnLoad
454
// and JNI_OnUnload
455
private static Class<?> getFromClass() {
456
if (nativeLibraryContext.isEmpty()) { // only default library
457
return Object.class;
458
}
459
return nativeLibraryContext.peek().fromClass;
460
}
461
462
// JNI FindClass expects the caller class if invoked from JNI_OnLoad
463
// and JNI_OnUnload is NativeLibrary class
464
private static native boolean load(NativeLibraryImpl impl, String name, boolean isBuiltin, boolean isJNI);
465
private static native void unload(String name, boolean isBuiltin, boolean isJNI, long handle);
466
private static native String findBuiltinLib(String name);
467
private static native long findEntry0(NativeLibraryImpl lib, String name);
468
}
469
470