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