简体   繁体   中英

Loading Native Library from java.library.path specifically

I was just trying to make a toy program to bind native code to java just for fun. I have successfully been able to make the program run using System.load("/FULLPATH/mylib.so") , but having trouble loading libraries specifically from java.library.path using System.loadLibrary() .

Tools I'm using:

  • gcc 4.8.5 20150623 (Red Hat 4.8.5-4)
  • java/javac version "1.8.0_66" (HotSpot 64 bit)
  • CentOS 7 (64 bit)

Build sequence (same for both versions mentioned in first paragraph).

rm TestIt.class mylib.so TestIt.h
javac TestIt.java
javah -stubs TestIt
gcc -shared -I/$JDK8_HOME/include/ -I/$JDK8_HOME/include/linux/ -fPIC nativeTestItImpl.c -o mylib.so

Java code to load the library (simply a static initializer):

static {
        System.out.println("System.getProperty(\"java.library.path\") is: " + System.getProperty("java.library.path"));
        System.loadLibrary("mylib");
        //System.load("/FULLPATH/mylib.so");// ***This works***
    }

How I've tried to set the library path:

java -Djava.library.path=/FULLPATH TestIt
java -Djava.library.path=/FULLPATH/mylib.so TestIt
java -Djava.library.path=. TestIt                 #appeared as '.'
java -Djava.library.path=/FULLPATH:$PATH TestIt
#NOTE: DEFLT_LIB_PATH  was the output of the above print statement when
#      running "java TestIt"
java -Djava.library.path=/FULLPATH:$DEFLT_LIB_PATH TestIt
set LD_LIBRARY_PATH=/FULLPATH && java TestIt      ##did not appear in printout of lib
export LD_LIBRARY_PATH=/FULLPATH && java TestIt   ##preppended /FULLPATH: to DEFLT_LIB_PATH

All version printed out as, expected exception those labeled with the "##" above.

Error in all cases: Exception in thread "main" java.lang.UnsatisfiedLinkError: no mylib in java.library.path

Full path has no spaces or special characters so I'm quite lost why this is the case.

Also have tried System.loadLibrary("mylib.so"); just for completeness.

Let's check what the Javadoc says:

Loads the dynamic library with the specified library name. A file containing native code is loaded from the local file system from a place where library files are conventionally obtained. The details of this process are implementation-dependent. The mapping from a library name to a specific filename is done in a system-specific manner.

Since you are using CentOS 7 so I will focus on Linux.

The issue you are facing is that you don't follow the unwritten system-specific contract for Linux. What the JVM does on Linux is to transform a call to System.loadLibrary("foo") to a call to dlopen("libfoo.so") . If you rename your library and fix the argument passed to System.loadLibrary it should work.

I am not aware of an "official" documentation stating this contract but it most likely exist somewhere.

If you want to have fun, you can download OpenJDK and follow the call chain. You should eventually end up on this piece of code defined in hotspot/src/os/linux/vm/os_linux.cpp :

bool os::dll_build_name(char* buffer, size_t buflen,
                        const char* pname, const char* fname) {
  bool retval = false;
  // Copied from libhpi
  const size_t pnamelen = pname ? strlen(pname) : 0;

  // Return error on buffer overflow.
  if (pnamelen + strlen(fname) + 10 > (size_t) buflen) {
    return retval;
  }

  if (pnamelen == 0) {
    snprintf(buffer, buflen, "lib%s.so", fname);
    retval = true;
  } else [...] 

os::dll_build_name is used to get the filename parameter which is passed to os::dll_load which itself invokes dlopen(filename) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM