Incorrect return value for Java_jdk_internal_loader_NativeLibraries_load() in java.base/share/native/libjava/NativeLibraries.c
Mandy Chung
mandy.chung at oracle.com
Thu Feb 24 17:42:17 UTC 2022
This is not an issue and does not affect the runtiime. When it fails to
load a native library, the VM will throw UnsatisfiedLinkError (see
JVM_LoadLibrary). The return value is only important for the JNI case
and calling System::loadLibrary on the system with dynamic linker cache
(Big Sur) since throwExceptionIfFail == false.
I agree that the JNI_TRUE return value for non-JNI case (i.e. raw
library library mechanism) is confusing to the readers. We should fix
it to return handle != 0L to match the specified return value (true only
when succeed).
I will file an issue.
Mandy
On 2/23/22 4:08 PM, Cheng Jin wrote:
> Hi there,
>
> The return value from Java_jdk_internal_loader_NativeLibraries_load() athttps://github.com/openjdk/jdk/blob/master/src/java.base/share/native/libjava/NativeLibraries.c
> seems incorrect according to the code logic in there.
>
> Looking at the calling path from loadLibrary() to load() at src/java.base/share/classes/jdk/internal/loader/NativeLibraries.java as follows:
>
> public NativeLibrary loadLibrary(Class<?> fromClass, String name) {
> ...
> NativeLibrary lib = findFromPaths(LibraryPaths.SYS_PATHS, fromClass, name); <----------
> if (lib == null && searchJavaLibraryPath) {
> lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name); <----
> }
> return lib;
> }
>
> private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
> for (String path : paths) { <---------------- keep searching the library paths till the library is located at the correct lib path
> File libfile = new File(path, System.mapLibraryName(name));
> NativeLibrary nl = loadLibrary(fromClass, libfile); ?
> if (nl != null) {
> return nl; <--------------------
> }
> libfile = ClassLoaderHelper.mapAlternativeName(libfile);
> if (libfile != null) {
> nl = loadLibrary(fromClass, libfile);
> if (nl != null) {
> return nl;
> }
> }
> }
> return null;
> }
>
> public NativeLibrary loadLibrary(Class<?> fromClass, File file) {
> // Check to see if we're attempting to access a static library
> String name = findBuiltinLib(file.getName());
> boolean isBuiltin = (name != null);
> ...
> return loadLibrary(fromClass, name, isBuiltin); <------------
> }
>
> private NativeLibrary loadLibrary(Class<?> fromClass, String name, boolean isBuiltin) {
> ...
> NativeLibraryImpl lib = new NativeLibraryImpl(fromClass, name, isBuiltin, isJNI);
>
> // load the native library
> NativeLibraryContext.push(lib);
> try {
> if (!lib.open()) { <--------------------- call load()
> return null; // fail to open the native library
> }
> ...
> // register the loaded native library
> loadedLibraryNames.add(name);
> libraries.put(name, lib);
> return lib; <--------------- return an invalid lib as lib.open() returns true
> } finally {
> releaseNativeLibraryLock(name);
> }
> ...
> }
>
> /*
> * Loads the named native library
> */
> boolean open() {
> ...
> return load(this, name, isBuiltin, isJNI, loadLibraryOnlyIfPresent);
> }
>
> private static native boolean load(NativeLibraryImpl impl, String name, boolean isBuiltin, boolean isJNI, boolean throwExceptionIfFail);
>
> and then native code in java.base/share/native/libjava/NativeLibraries.c
>
> Java_jdk_internal_loader_NativeLibraries_load
> (JNIEnv *env, jobject this, jobject lib, jstring name,
> jboolean isBuiltin, jboolean isJNI, jboolean throwExceptionIfFail)
> {
> const char *cname;
> jint jniVersion;
> jthrowable cause;
> void * handle;
> jboolean loaded = JNI_FALSE;
> ...
> handle = isBuiltin ? procHandle : JVM_LoadLibrary(cname, throwExceptionIfFail);
> if (isJNI) {
> ...
> }
> (*env)->SetLongField(env, lib, handleID, ptr_to_jlong(handle));
> loaded = JNI_TRUE; <----- always return true when isJNI is false and a null handle
>
> done:
> JNU_ReleaseStringPlatformChars(env, name, cname);
> return loaded;
> }
>
> Assuming there are multiple library paths existing in the jdk/lib which are added to sun.boot.library.path and java.library.path
> and the expected library (libnative.so) is only placed in jdk/lib. e.g.
> lib/libA
> lib/libB
> lib/libC
> lib/libnative.so
>
> when the code in findFromPaths() searches the library paths set in sun.boot.library.path and java.library.path starting from lib/LibA,
> it will end up with an invalid value if load() returns true in the case of a false isJNI and a null handle returned from JVM_LoadLibrary()
> as the library doesn't exist in lib/libA passed to JVM_LoadLibrary().
>
> To ensure findFromPaths() keeps searching the remaining lib paths, the simple fix should look like:
> Java_jdk_internal_loader_NativeLibraries_load( ...)
> {
> ...
> (*env)->SetLongField(env, lib, handleID, ptr_to_jlong(handle));
> if (handle) { <------------------------- return true only for a non-null handle
> loaded = JNI_TRUE;
> }
> ...
> }
>
> Please help check the code around there to see whether the fix is reasonable to avoid the unexpected situation as explained above (the problem was spotted since JDK17)
>
> Best Regards
> Cheng Jin
More information about the core-libs-dev
mailing list