1 /*
2 * Copyright (c) 2020, 2024, 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
138 * @param name library name
139 * @param isBuiltin built-in library
140 * @throws UnsatisfiedLinkError if the native library has already been loaded
141 * and registered in another NativeLibraries
142 */
143 private NativeLibrary loadLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
144 ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
145 if (this.loader != loader) {
146 throw new InternalError(fromClass.getName() + " not allowed to load library");
147 }
148
149 acquireNativeLibraryLock(name);
150 try {
151 // find if this library has already been loaded and registered in this NativeLibraries
152 NativeLibrary cached = libraries.get(name);
153 if (cached != null) {
154 return cached;
155 }
156
157 // cannot be loaded by other class loaders
158 if (loadedLibraryNames.contains(name)) {
159 throw new UnsatisfiedLinkError("Native Library " + name +
160 " already loaded in another classloader");
161 }
162
163 /*
164 * When a library is being loaded, JNI_OnLoad function can cause
165 * another loadLibrary invocation that should succeed.
166 *
167 * Each thread maintains its own stack to hold the list of
168 * libraries it is loading.
169 *
170 * If there is a pending load operation for the library, we
171 * immediately return success; if the pending load is from
172 * a different class loader, we raise UnsatisfiedLinkError.
173 */
174 for (NativeLibraryImpl lib : NativeLibraryContext.current()) {
175 if (name.equals(lib.name())) {
176 if (loader == lib.fromClass.getClassLoader()) {
177 return lib;
178 } else {
188 try {
189 if (!lib.open()) {
190 return null; // fail to open the native library
191 }
192 // auto unloading is only supported for JNI native libraries
193 // loaded by custom class loaders that can be unloaded.
194 // built-in class loaders are never unloaded.
195 boolean autoUnload = !VM.isSystemDomainLoader(loader) && loader != ClassLoaders.appClassLoader();
196 if (autoUnload) {
197 // register the loaded native library for auto unloading
198 // when the class loader is reclaimed, all native libraries
199 // loaded that class loader will be unloaded.
200 // The entries in the libraries map are not removed since
201 // the entire map will be reclaimed altogether.
202 CleanerFactory.cleaner().register(loader, lib.unloader());
203 }
204 } finally {
205 NativeLibraryContext.pop();
206 }
207 // register the loaded native library
208 loadedLibraryNames.add(name);
209 libraries.put(name, lib);
210 return lib;
211 } finally {
212 releaseNativeLibraryLock(name);
213 }
214 }
215
216 /**
217 * Loads a native library from the system library path and java library path.
218 *
219 * @param name library name
220 *
221 * @throws UnsatisfiedLinkError if the native library has already been loaded
222 * and registered in another NativeLibraries
223 */
224 public NativeLibrary loadLibrary(String name) {
225 assert name.indexOf(File.separatorChar) < 0;
226 return loadLibrary(caller, name);
227 }
228
229 /**
230 * Loads a native library from the system library path and java library path.
231 *
232 * @param name library name
233 * @param fromClass the caller class calling System::loadLibrary
234 *
235 * @throws UnsatisfiedLinkError if the native library has already been loaded
236 * and registered in another NativeLibraries
237 */
238 public NativeLibrary loadLibrary(Class<?> fromClass, String name) {
239 assert name.indexOf(File.separatorChar) < 0;
240
241 NativeLibrary lib = findFromPaths(LibraryPaths.SYS_PATHS, fromClass, name);
242 if (lib == null && searchJavaLibraryPath) {
243 lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name);
244 }
245 return lib;
246 }
247
248 private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
249 for (String path : paths) {
250 File libfile = new File(path, System.mapLibraryName(name));
251 NativeLibrary nl = loadLibrary(fromClass, libfile);
252 if (nl != null) {
253 return nl;
254 }
255 libfile = ClassLoaderHelper.mapAlternativeName(libfile);
256 if (libfile != null) {
257 nl = loadLibrary(fromClass, libfile);
258 if (nl != null) {
259 return nl;
260 }
261 }
262 }
263 return null;
264 }
265
266 /**
267 * NativeLibraryImpl denotes a loaded native library instance.
358 final String name;
359 final long handle;
360 final boolean isBuiltin;
361
362 Unloader(String name, long handle, boolean isBuiltin) {
363 if (handle == 0) {
364 throw new IllegalArgumentException(
365 "Invalid handle for native library " + name);
366 }
367
368 this.name = name;
369 this.handle = handle;
370 this.isBuiltin = isBuiltin;
371 }
372
373 @Override
374 public void run() {
375 acquireNativeLibraryLock(name);
376 try {
377 /* remove the native library name */
378 if (!loadedLibraryNames.remove(name)) {
379 throw new IllegalStateException(name + " has already been unloaded");
380 }
381 NativeLibraryContext.push(UNLOADER);
382 try {
383 unload(name, isBuiltin, handle);
384 } finally {
385 NativeLibraryContext.pop();
386 }
387 } finally {
388 releaseNativeLibraryLock(name);
389 }
390 }
391 }
392
393 /*
394 * Holds system and user library paths derived from the
395 * {@code java.library.path} and {@code sun.boot.library.path} system
396 * properties. The system properties are eagerly read at bootstrap, then
397 * lazily parsed on first use to avoid initialization ordering issues.
398 */
399 static class LibraryPaths {
400 // The paths searched for libraries
401 static final String[] SYS_PATHS = ClassLoaderHelper.parsePath(StaticProperty.sunBootLibraryPath());
402 static final String[] USER_PATHS = ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath());
403 }
404
405 // All native libraries we've loaded.
406 private static final Set<String> loadedLibraryNames =
407 ConcurrentHashMap.newKeySet();
408
409 // reentrant lock class that allows exact counting (with external synchronization)
410 @SuppressWarnings("serial")
411 private static final class CountedLock extends ReentrantLock {
412
413 private int counter = 0;
414
415 public void increment() {
416 if (counter == Integer.MAX_VALUE) {
417 // prevent overflow
418 throw new Error("Maximum lock count exceeded");
419 }
420 ++counter;
421 }
422
423 public void decrement() {
424 --counter;
425 }
426
427 public int getCounter() {
|
1 /*
2 * Copyright (c) 2020, 2025, 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
138 * @param name library name
139 * @param isBuiltin built-in library
140 * @throws UnsatisfiedLinkError if the native library has already been loaded
141 * and registered in another NativeLibraries
142 */
143 private NativeLibrary loadLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
144 ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
145 if (this.loader != loader) {
146 throw new InternalError(fromClass.getName() + " not allowed to load library");
147 }
148
149 acquireNativeLibraryLock(name);
150 try {
151 // find if this library has already been loaded and registered in this NativeLibraries
152 NativeLibrary cached = libraries.get(name);
153 if (cached != null) {
154 return cached;
155 }
156
157 // cannot be loaded by other class loaders
158 if (Holder.loadedLibraryNames.contains(name)) {
159 throw new UnsatisfiedLinkError("Native Library " + name +
160 " already loaded in another classloader");
161 }
162
163 /*
164 * When a library is being loaded, JNI_OnLoad function can cause
165 * another loadLibrary invocation that should succeed.
166 *
167 * Each thread maintains its own stack to hold the list of
168 * libraries it is loading.
169 *
170 * If there is a pending load operation for the library, we
171 * immediately return success; if the pending load is from
172 * a different class loader, we raise UnsatisfiedLinkError.
173 */
174 for (NativeLibraryImpl lib : NativeLibraryContext.current()) {
175 if (name.equals(lib.name())) {
176 if (loader == lib.fromClass.getClassLoader()) {
177 return lib;
178 } else {
188 try {
189 if (!lib.open()) {
190 return null; // fail to open the native library
191 }
192 // auto unloading is only supported for JNI native libraries
193 // loaded by custom class loaders that can be unloaded.
194 // built-in class loaders are never unloaded.
195 boolean autoUnload = !VM.isSystemDomainLoader(loader) && loader != ClassLoaders.appClassLoader();
196 if (autoUnload) {
197 // register the loaded native library for auto unloading
198 // when the class loader is reclaimed, all native libraries
199 // loaded that class loader will be unloaded.
200 // The entries in the libraries map are not removed since
201 // the entire map will be reclaimed altogether.
202 CleanerFactory.cleaner().register(loader, lib.unloader());
203 }
204 } finally {
205 NativeLibraryContext.pop();
206 }
207 // register the loaded native library
208 Holder.loadedLibraryNames.add(name);
209 libraries.put(name, lib);
210 return lib;
211 } finally {
212 releaseNativeLibraryLock(name);
213 }
214 }
215
216 /**
217 * Loads a native library from the system library path and java library path.
218 *
219 * @param name library name
220 *
221 * @throws UnsatisfiedLinkError if the native library has already been loaded
222 * and registered in another NativeLibraries
223 */
224 public NativeLibrary loadLibrary(String name) {
225 assert name.indexOf(File.separatorChar) < 0;
226 return loadLibrary(caller, name);
227 }
228
229 /**
230 * Loads a native library from the system library path and java library path.
231 *
232 * @param name library name
233 * @param fromClass the caller class calling System::loadLibrary
234 *
235 * @throws UnsatisfiedLinkError if the native library has already been loaded
236 * and registered in another NativeLibraries
237 */
238 public NativeLibrary loadLibrary(Class<?> fromClass, String name) {
239 assert name.indexOf(File.separatorChar) < 0;
240
241 NativeLibrary lib = findFromPaths(LibraryPaths.SYS_PATHS, fromClass, name);
242 if (lib == null && searchJavaLibraryPath) {
243 lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name);
244 }
245 return lib;
246 }
247
248 // Called at the end of AOTCache assembly phase.
249 public void clear() {
250 libraries.clear();
251 }
252
253 private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
254 for (String path : paths) {
255 File libfile = new File(path, System.mapLibraryName(name));
256 NativeLibrary nl = loadLibrary(fromClass, libfile);
257 if (nl != null) {
258 return nl;
259 }
260 libfile = ClassLoaderHelper.mapAlternativeName(libfile);
261 if (libfile != null) {
262 nl = loadLibrary(fromClass, libfile);
263 if (nl != null) {
264 return nl;
265 }
266 }
267 }
268 return null;
269 }
270
271 /**
272 * NativeLibraryImpl denotes a loaded native library instance.
363 final String name;
364 final long handle;
365 final boolean isBuiltin;
366
367 Unloader(String name, long handle, boolean isBuiltin) {
368 if (handle == 0) {
369 throw new IllegalArgumentException(
370 "Invalid handle for native library " + name);
371 }
372
373 this.name = name;
374 this.handle = handle;
375 this.isBuiltin = isBuiltin;
376 }
377
378 @Override
379 public void run() {
380 acquireNativeLibraryLock(name);
381 try {
382 /* remove the native library name */
383 if (!Holder.loadedLibraryNames.remove(name)) {
384 throw new IllegalStateException(name + " has already been unloaded");
385 }
386 NativeLibraryContext.push(UNLOADER);
387 try {
388 unload(name, isBuiltin, handle);
389 } finally {
390 NativeLibraryContext.pop();
391 }
392 } finally {
393 releaseNativeLibraryLock(name);
394 }
395 }
396 }
397
398 /*
399 * Holds system and user library paths derived from the
400 * {@code java.library.path} and {@code sun.boot.library.path} system
401 * properties. The system properties are eagerly read at bootstrap, then
402 * lazily parsed on first use to avoid initialization ordering issues.
403 */
404 static class LibraryPaths {
405 // The paths searched for libraries
406 static final String[] SYS_PATHS = ClassLoaderHelper.parsePath(StaticProperty.sunBootLibraryPath());
407 static final String[] USER_PATHS = ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath());
408 }
409
410 // Holder has the fields that need to be initialized during JVM bootstrap even if
411 // the outer is aot-initialized.
412 static class Holder {
413 // All native libraries we've loaded.
414 private static final Set<String> loadedLibraryNames =
415 ConcurrentHashMap.newKeySet();
416 }
417
418 // reentrant lock class that allows exact counting (with external synchronization)
419 @SuppressWarnings("serial")
420 private static final class CountedLock extends ReentrantLock {
421
422 private int counter = 0;
423
424 public void increment() {
425 if (counter == Integer.MAX_VALUE) {
426 // prevent overflow
427 throw new Error("Maximum lock count exceeded");
428 }
429 ++counter;
430 }
431
432 public void decrement() {
433 --counter;
434 }
435
436 public int getCounter() {
|