Monday, April 7, 2014

JNI and LD_LIBRARY_PATH


Global variables are evil, even if they still appear everywhere, often as environment variables. If you use Tibco on Unix, you have to set LD_LIBRARY_PATH to include the native libraries. Among other things, this complicates using a continuous build server that needs different variables for different builds.

If we remove LD_LIBRARY_PATH and set java.library.path instead, we can load native libraries. But if that loaded library then loads another, it naturally looks for it in LD_LIBRARY_PATH and you get something like:

java.lang.UnsatisfiedLinkError: /opt/mds/tibco_8_1/tibrv/8.1/lib/libtibrvj.so: libtibrvcmq.so: cannot open shared object file: No such file or directory

that is, the first one is loaded but the second one fails as it has not been loaded via the JVM but directly by Linux (which knows nothing about java.library.path).

Taking a look at the dependencies between the native libs looks like this:

> readelf -a /opt/mds/tibco_8_1/tibrv/8.1/lib/libtibrv.so | grep NEEDED
 0x00000001 (NEEDED)                    Shared library: [libc.so.6]
 0x00000001 (NEEDED)                    Shared library: [libpthread.so.0]

So, libtibrv.so just needs standard Linux libraries.

> readelf -a /opt/mds/tibco_8_1/tibrv/8.1/lib/libtibrvcm.so | grep NEEDED
 0x00000001 (NEEDED)                    Shared library: [libtibrv.so.6]
 0x00000001 (NEEDED)                    Shared library: [libc.so.6]

And in turn, libtibrvcm needs libtibrv and so on.

Using readelf we can then build up the dependency tree (more on the ELF format can be found here).

We can programatically get around this by just setting java.library.path and calling java code like this:

System.loadLibrary("tibrv");
System.loadLibrary("tibrvcm");
System.loadLibrary("tibrvft");
System.loadLibrary("tibrvcmq");

This loads the libraries in order. Changing the order causes a java.lang.UnsatisfiedLinkError.