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