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.ref.CleanerFactory; 28 import jdk.internal.util.StaticProperty; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.security.AccessController; 33 import java.security.PrivilegedAction; 34 import java.util.ArrayDeque; 35 import java.util.Deque; 36 import java.util.HashSet; 37 import java.util.Map; 38 import java.util.Set; 39 import java.util.concurrent.ConcurrentHashMap; 40 41 /** 42 * Native libraries are loaded via {@link System#loadLibrary(String)}, 43 * {@link System#load(String)}, {@link Runtime#loadLibrary(String)} and 44 * {@link Runtime#load(String)}. They are caller-sensitive. 45 * 46 * Each class loader has a NativeLibraries instance to register all of its 47 * loaded native libraries. System::loadLibrary (and other APIs) only 48 * allows a native library to be loaded by one class loader, i.e. one 49 * NativeLibraries instance. Any attempt to load a native library that 50 * has already been loaded by a class loader with another class loader 51 * will fail. 52 */ 53 public final class NativeLibraries { 54 55 private final Map<String, NativeLibrary> libraries = new ConcurrentHashMap<>(); 56 private final ClassLoader loader; 57 private final Class<?> caller; // may be null. If not null, this is used as 58 // fromClass as a fast-path. See loadLibrary(String name). 59 private final boolean searchJavaLibraryPath; 60 61 public NativeLibraries(ClassLoader loader) { 62 // for null loader, default the caller to this class and 63 // do not search java.library.path 64 this(loader, loader != null ? null : NativeLibraries.class, loader != null ? true : false); 65 } 66 public NativeLibraries(ClassLoader loader, Class<?> caller, boolean searchJavaLibraryPath) { 67 if (caller != null && caller.getClassLoader() != loader) { 68 throw new IllegalArgumentException(caller.getName() + " must be defined by " + loader); 69 } 70 this.loader = loader; 71 this.caller = caller; 72 this.searchJavaLibraryPath = searchJavaLibraryPath; 73 } 74 75 /* 76 * Find the address of the given symbol name from the native libraries 77 * loaded in this NativeLibraries instance. 78 */ 79 public long find(String name) { 80 if (libraries.isEmpty()) 81 return 0; 82 83 // the native libraries map may be updated in another thread 84 // when a native library is being loaded. No symbol will be 85 // searched from it yet. 86 for (NativeLibrary lib : libraries.values()) { 87 long entry = lib.find(name); 88 if (entry != 0) return entry; 89 } 90 return 0; 91 } 92 152 * 153 * We use a static stack to hold the list of libraries we are 154 * loading because this can happen only when called by the 155 * same thread because this block is synchronous. 156 * 157 * If there is a pending load operation for the library, we 158 * immediately return success; otherwise, we raise 159 * UnsatisfiedLinkError. 160 */ 161 for (NativeLibraryImpl lib : nativeLibraryContext) { 162 if (name.equals(lib.name())) { 163 if (loader == lib.fromClass.getClassLoader()) { 164 return lib; 165 } else { 166 throw new UnsatisfiedLinkError("Native Library " + 167 name + " is being loaded in another classloader"); 168 } 169 } 170 } 171 172 NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin); 173 // load the native library 174 nativeLibraryContext.push(lib); 175 try { 176 if (!lib.open()) return null; 177 } finally { 178 nativeLibraryContext.pop(); 179 } 180 // register the loaded native library 181 loadedLibraryNames.add(name); 182 libraries.put(name, lib); 183 return lib; 184 } 185 } 186 187 /** 188 * Loads a native library from the system library path and java library path. 189 * 190 * @param name library name 191 * 192 * @throws UnsatisfiedLinkError if the native library has already been loaded 193 * and registered in another NativeLibraries 194 */ 195 public NativeLibrary loadLibrary(String name) { 196 assert name.indexOf(File.separatorChar) < 0; 201 202 /** 203 * Loads a native library from the system library path and java library path. 204 * 205 * @param name library name 206 * @param fromClass the caller class calling System::loadLibrary 207 * 208 * @throws UnsatisfiedLinkError if the native library has already been loaded 209 * and registered in another NativeLibraries 210 */ 211 public NativeLibrary loadLibrary(Class<?> fromClass, String name) { 212 assert name.indexOf(File.separatorChar) < 0; 213 214 NativeLibrary lib = findFromPaths(LibraryPaths.SYS_PATHS, fromClass, name); 215 if (lib == null && searchJavaLibraryPath) { 216 lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name); 217 } 218 return lib; 219 } 220 221 private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) { 222 for (String path : paths) { 223 File libfile = new File(path, System.mapLibraryName(name)); 224 NativeLibrary nl = loadLibrary(fromClass, libfile); 225 if (nl != null) { 226 return nl; 227 } 228 libfile = ClassLoaderHelper.mapAlternativeName(libfile); 229 if (libfile != null) { 230 nl = loadLibrary(fromClass, libfile); 231 if (nl != null) { 232 return nl; 233 } 234 } 235 } 236 return null; 237 } 238 239 /** 240 * NativeLibraryImpl denotes a loaded native library instance. 241 * Each NativeLibraries contains a map of loaded native libraries in the 242 * private field {@code libraries}. 243 * 244 * Every native library requires a particular version of JNI. This is 245 * denoted by the private {@code jniVersion} field. This field is set by 246 * the VM when it loads the library, and used by the VM to pass the correct 247 * version of JNI to the native methods. 248 */ 249 static class NativeLibraryImpl implements NativeLibrary { 250 // the class from which the library is loaded, also indicates 251 // the loader this native library belongs. 252 final Class<?> fromClass; 253 // the canonicalized name of the native library. 254 // or static library name 255 final String name; 256 // Indicates if the native library is linked into the VM 257 final boolean isBuiltin; 258 259 // opaque handle to native library, used in native code. 260 long handle; 261 // the version of JNI environment the native library requires. 262 int jniVersion; 263 264 NativeLibraryImpl(Class<?> fromClass, String name, boolean isBuiltin) { 265 this.fromClass = fromClass; 266 this.name = name; 267 this.isBuiltin = isBuiltin; 268 } 269 270 @Override 271 public String name() { 272 return name; 273 } 274 275 @Override 276 public long find(String name) { 277 return findEntry0(this, name); 278 } 279 280 /* 281 * Loads the native library and registers for cleanup when its 282 * associated class loader is unloaded 283 */ 284 boolean open() { 285 if (handle != 0) { 286 throw new InternalError("Native library " + name + " has been loaded"); 287 } 288 289 if (!load(this, name, isBuiltin)) return false; 290 291 // register the class loader for cleanup when unloaded 292 // builtin class loaders are never unloaded 293 ClassLoader loader = fromClass != null ? fromClass.getClassLoader() : null; 294 if (loader != null && 295 loader != ClassLoaders.platformClassLoader() && 296 loader != ClassLoaders.appClassLoader()) { 297 CleanerFactory.cleaner().register(loader, new Unloader(name, handle, isBuiltin)); 298 } 299 return true; 300 } 301 } 302 303 /* 304 * The run() method will be invoked when this class loader becomes 305 * phantom reachable to unload the native library. 306 */ 307 static class Unloader implements Runnable { 308 // This represents the context when a native library is unloaded 309 // and getFromClass() will return null, 310 static final NativeLibraryImpl UNLOADER = 311 new NativeLibraryImpl(null, "dummy", false); 312 313 final String name; 314 final long handle; 315 final boolean isBuiltin; 316 317 Unloader(String name, long handle, boolean isBuiltin) { 318 if (handle == 0) { 319 throw new IllegalArgumentException( 320 "Invalid handle for native library " + name); 321 } 322 323 this.name = name; 324 this.handle = handle; 325 this.isBuiltin = isBuiltin; 326 } 327 328 @Override 329 public void run() { 330 synchronized (NativeLibraries.loadedLibraryNames) { 331 /* remove the native library name */ 332 NativeLibraries.loadedLibraryNames.remove(name); 333 NativeLibraries.nativeLibraryContext.push(UNLOADER); 334 try { 335 unload(name, isBuiltin, handle); 336 } finally { 337 NativeLibraries.nativeLibraryContext.pop(); 338 } 339 } 340 } 341 } 342 343 /* 344 * Holds system and user library paths derived from the 345 * {@code java.library.path} and {@code sun.boot.library.path} system 346 * properties. The system properties are eagerly read at bootstrap, then 347 * lazily parsed on first use to avoid initialization ordering issues. 348 */ 349 static class LibraryPaths { 350 // The paths searched for libraries 351 static final String[] SYS_PATHS = ClassLoaderHelper.parsePath(StaticProperty.sunBootLibraryPath()); 352 static final String[] USER_PATHS = ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath()); 353 } 354 355 // All native libraries we've loaded. 356 // This also serves as the lock to obtain nativeLibraries 357 // and write to nativeLibraryContext. 358 private static final Set<String> loadedLibraryNames = new HashSet<>(); 359 360 // native libraries being loaded 361 private static Deque<NativeLibraryImpl> nativeLibraryContext = new ArrayDeque<>(8); 362 363 // Invoked in the VM to determine the context class in JNI_OnLoad 364 // and JNI_OnUnload 365 private static Class<?> getFromClass() { 366 if (nativeLibraryContext.isEmpty()) { // only default library 367 return Object.class; 368 } 369 return nativeLibraryContext.peek().fromClass; 370 } 371 372 // JNI FindClass expects the caller class if invoked from JNI_OnLoad 373 // and JNI_OnUnload is NativeLibrary class 374 private static native boolean load(NativeLibraryImpl impl, String name, boolean isBuiltin); 375 private static native void unload(String name, boolean isBuiltin, long handle); 376 private static native String findBuiltinLib(String name); 377 private static native long findEntry0(NativeLibraryImpl lib, String name); 378 } | 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 203 * 204 * We use a static stack to hold the list of libraries we are 205 * loading because this can happen only when called by the 206 * same thread because this block is synchronous. 207 * 208 * If there is a pending load operation for the library, we 209 * immediately return success; otherwise, we raise 210 * UnsatisfiedLinkError. 211 */ 212 for (NativeLibraryImpl lib : nativeLibraryContext) { 213 if (name.equals(lib.name())) { 214 if (loader == lib.fromClass.getClassLoader()) { 215 return lib; 216 } else { 217 throw new UnsatisfiedLinkError("Native Library " + 218 name + " is being loaded in another classloader"); 219 } 220 } 221 } 222 223 NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin, isJNI); 224 // load the native library 225 nativeLibraryContext.push(lib); 226 try { 227 if (!lib.open()) { 228 return null; // fail to open the native library 229 } 230 // auto unloading is only supported for JNI native libraries 231 // loaded by custom class loaders that can be unloaded. 232 // built-in class loaders are never unloaded. 233 boolean autoUnload = isJNI && !VM.isSystemDomainLoader(loader) 234 && loader != ClassLoaders.appClassLoader(); 235 if (autoUnload) { 236 // register the loaded native library for auto unloading 237 // when the class loader is reclaimed, all native libraries 238 // loaded that class loader will be unloaded. 239 // The entries in the libraries map are not removed since 240 // the entire map will be reclaimed altogether. 241 CleanerFactory.cleaner().register(loader, lib.unloader()); 242 } 243 } finally { 244 nativeLibraryContext.pop(); 245 } 246 // register the loaded native library 247 loadedLibraryNames.add(name); 248 libraries.put(name, lib); 249 return lib; 250 } 251 } 252 253 /** 254 * Loads a native library from the system library path and java library path. 255 * 256 * @param name library name 257 * 258 * @throws UnsatisfiedLinkError if the native library has already been loaded 259 * and registered in another NativeLibraries 260 */ 261 public NativeLibrary loadLibrary(String name) { 262 assert name.indexOf(File.separatorChar) < 0; 267 268 /** 269 * Loads a native library from the system library path and java library path. 270 * 271 * @param name library name 272 * @param fromClass the caller class calling System::loadLibrary 273 * 274 * @throws UnsatisfiedLinkError if the native library has already been loaded 275 * and registered in another NativeLibraries 276 */ 277 public NativeLibrary loadLibrary(Class<?> fromClass, String name) { 278 assert name.indexOf(File.separatorChar) < 0; 279 280 NativeLibrary lib = findFromPaths(LibraryPaths.SYS_PATHS, fromClass, name); 281 if (lib == null && searchJavaLibraryPath) { 282 lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name); 283 } 284 return lib; 285 } 286 287 /** 288 * Unloads the given native library 289 * 290 * @param lib native library 291 */ 292 public void unload(NativeLibrary lib) { 293 if (isJNI) { 294 throw new UnsupportedOperationException("explicit unloading cannot be used with auto unloading"); 295 } 296 Objects.requireNonNull(lib); 297 synchronized (loadedLibraryNames) { 298 NativeLibraryImpl nl = libraries.remove(lib.name()); 299 if (nl != lib) { 300 throw new IllegalArgumentException(lib.name() + " not loaded by this NativeLibraries instance"); 301 } 302 // unload the native library and also remove from the global name registry 303 nl.unloader().run(); 304 } 305 } 306 307 private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) { 308 for (String path : paths) { 309 File libfile = new File(path, System.mapLibraryName(name)); 310 NativeLibrary nl = loadLibrary(fromClass, libfile); 311 if (nl != null) { 312 return nl; 313 } 314 libfile = ClassLoaderHelper.mapAlternativeName(libfile); 315 if (libfile != null) { 316 nl = loadLibrary(fromClass, libfile); 317 if (nl != null) { 318 return nl; 319 } 320 } 321 } 322 return null; 323 } 324 325 /** 326 * NativeLibraryImpl denotes a loaded native library instance. 327 * Each NativeLibraries contains a map of loaded native libraries in the 328 * private field {@code libraries}. 329 * 330 * Every native library requires a particular version of JNI. This is 331 * denoted by the private {@code jniVersion} field. This field is set by 332 * the VM when it loads the library, and used by the VM to pass the correct 333 * version of JNI to the native methods. 334 */ 335 static class NativeLibraryImpl implements NativeLibrary { 336 // the class from which the library is loaded, also indicates 337 // the loader this native library belongs. 338 final Class<?> fromClass; 339 // the canonicalized name of the native library. 340 // or static library name 341 final String name; 342 // Indicates if the native library is linked into the VM 343 final boolean isBuiltin; 344 // Indicate if this is JNI native library 345 final boolean isJNI; 346 347 // opaque handle to native library, used in native code. 348 long handle; 349 // the version of JNI environment the native library requires. 350 int jniVersion; 351 352 NativeLibraryImpl(Class<?> fromClass, String name, boolean isBuiltin, boolean isJNI) { 353 assert !isBuiltin || isJNI : "a builtin native library must be JNI library"; 354 355 this.fromClass = fromClass; 356 this.name = name; 357 this.isBuiltin = isBuiltin; 358 this.isJNI = isJNI; 359 } 360 361 @Override 362 public String name() { 363 return name; 364 } 365 366 @Override 367 public long find(String name) { 368 return findEntry0(this, name); 369 } 370 371 Runnable unloader() { 372 return new Unloader(name, handle, isBuiltin, isJNI); 373 } 374 375 /* 376 * Loads the named native library 377 */ 378 boolean open() { 379 if (handle != 0) { 380 throw new InternalError("Native library " + name + " has been loaded"); 381 } 382 383 return load(this, name, isBuiltin, isJNI); 384 } 385 } 386 387 /* 388 * The run() method will be invoked when this class loader becomes 389 * phantom reachable to unload the native library. 390 */ 391 static class Unloader implements Runnable { 392 // This represents the context when a native library is unloaded 393 // and getFromClass() will return null, 394 static final NativeLibraryImpl UNLOADER = 395 new NativeLibraryImpl(null, "dummy", false, false); 396 397 final String name; 398 final long handle; 399 final boolean isBuiltin; 400 final boolean isJNI; 401 402 Unloader(String name, long handle, boolean isBuiltin, boolean isJNI) { 403 assert !isBuiltin || isJNI : "a builtin native library must be JNI library"; 404 if (handle == 0) { 405 throw new IllegalArgumentException( 406 "Invalid handle for native library " + name); 407 } 408 409 this.name = name; 410 this.handle = handle; 411 this.isBuiltin = isBuiltin; 412 this.isJNI = isJNI; 413 } 414 415 @Override 416 public void run() { 417 synchronized (loadedLibraryNames) { 418 /* remove the native library name */ 419 if (!loadedLibraryNames.remove(name)) { 420 throw new IllegalStateException(name + " has already been unloaded"); 421 } 422 nativeLibraryContext.push(UNLOADER); 423 try { 424 unload(name, isBuiltin, isJNI, handle); 425 } finally { 426 nativeLibraryContext.pop(); 427 } 428 } 429 } 430 } 431 432 /* 433 * Holds system and user library paths derived from the 434 * {@code java.library.path} and {@code sun.boot.library.path} system 435 * properties. The system properties are eagerly read at bootstrap, then 436 * lazily parsed on first use to avoid initialization ordering issues. 437 */ 438 static class LibraryPaths { 439 // The paths searched for libraries 440 static final String[] SYS_PATHS = ClassLoaderHelper.parsePath(StaticProperty.sunBootLibraryPath()); 441 static final String[] USER_PATHS = ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath()); 442 } 443 444 // All native libraries we've loaded. 445 // This also serves as the lock to obtain nativeLibraries 446 // and write to nativeLibraryContext. 447 private static final Set<String> loadedLibraryNames = new HashSet<>(); 448 449 // native libraries being loaded 450 private static Deque<NativeLibraryImpl> nativeLibraryContext = new ArrayDeque<>(8); 451 452 // Invoked in the VM to determine the context class in JNI_OnLoad 453 // and JNI_OnUnload 454 private static Class<?> getFromClass() { 455 if (nativeLibraryContext.isEmpty()) { // only default library 456 return Object.class; 457 } 458 return nativeLibraryContext.peek().fromClass; 459 } 460 461 // JNI FindClass expects the caller class if invoked from JNI_OnLoad 462 // and JNI_OnUnload is NativeLibrary class 463 private static native boolean load(NativeLibraryImpl impl, String name, boolean isBuiltin, boolean isJNI); 464 private static native void unload(String name, boolean isBuiltin, boolean isJNI, long handle); 465 private static native String findBuiltinLib(String name); 466 private static native long findEntry0(NativeLibraryImpl lib, String name); 467 } |