tags:

views:

2809

answers:

5

I have a Java program that is calling C code through JNI that I'm attempting to run on Linux. The external code consists of two .so files: one for the JNI bindings (built with swig) and the other with the actual functions. I have the two libraries in the same directory and LD_LIBRARY_PATH is set correctly. ldd reports no problems when running from the command line, but when I set the LD_LIBRARY_PATH to the same value in the "run configurations" dialog in the Eclipse editor and attempt to execute the program, it gets the following error:

java.lang.UnsatisfiedLinkError: [path to libraries]/[JNI binding library].so: [actual code library].so: cannot open shared object file: No such file or directory

This leads me to believe that the JNI wrapper library is loaded successfully, but there is a failure when that library attempts to load the library containing the actual code. Is there any way to debug this further?

I will further note that this problem is happening in the eclipse editor itself and that I haven't attempted to package the code into a jar and run it within a free-standing jvm instance.

A: 

You could try -Djava.library.path=actual.so in command line parameters perhaps?

On windows, I had similar problems with a 3rd party library, which used a JNI wrapper DLL for its DLLs. My project had the DLL in the lib directory so I added lib to the PATH (e.g. PATH=%PATH%;./lib environment variable and everything started working.

kd304
A: 

As far as I know the Eclipse doesn't use the LD_LIBRARY_PATH. The easiest way to set up the right native library path is to go to Project properties -> Java Build Path -> Libraries Then expand either the JRE System Library entry or (if available) the Jar File that uses you native Library, choose "Native Library Location" then click "Edit..." and choose the folder your libraries are in. Actually it does set the -Djava.library.path variable so you have to include this in your command line if you start your program from outside eclipse.

Daff
+1  A: 

I think the issue is with the call to System.loadLibrary(String) and using LD_LIBRARY_PATH. Using loadLibrary("foo") will look in your java.library.path for something named libfoo.so. If nothing named libfoo.so is found you will get this error.

Now if you just set up the LD_LIBRARY_PATH, the native symbols you want will automatically be picked up by the linker, so you don't need to set up -Djava.library.path.

In my experience with swig in the gdal project, this error is actually harmless and since the LD_LIBRARY_PATH is set up, this will work fine.

I would recommend using -Djava.library.path and calling loadLibrary explitly, the reason being that if you ever decide to deploy your app with webstart, you will explicitly need to call loadLibrary to get your native libs picked up.

When I use eclipse I follow the instructions that Daff gave where you edit the native library under the jar in the Libraries tab in the Build Path. Just to mention again, this just sets java.library.path under the covers.

carrino
A: 

It may be that you just have to find the right place on the run config dialog to put the -Djava.library.path=... option. Offhand I think you want -D defines in the "vm arguments" on the arguments tab, whereas if you want to define LD_LIBRARY_PATH that goes on the environment tab. Eclipse will merrily let you put things in places where they won't mean what you think they mean. Anyway, I've used libraries this way before and if I get a chance I will look up what I did and edit my answer here.

Another thing to try is to play with LD_DEBUG. You can set the environment variable LD_DEBUG to various things (try ALL), and then the linux loader will divulge all sorts of useful information about what an application is trying to load, where it's looking for things, etc. Of course, this pre-supposes you launch eclipse from a command line, so you can both set the env vars and see the loader diagnostics; but as far as the system is concerned, when you run your app from inside eclipse, your app is just something eclipse is doing, so any library loading behavior can be seen in this way.

JustJeff
A: 

Are there any other libraries that your two libraries depend on? If so, you need to make sure they are also accessible to the JVM.

Be aware, manually setting "-Djava.library.path" seems to erase the default library path.

So with the following code:

public class LibTest {
    public static void main(String[] args) {
        String property = System.getProperty("java.library.path");
        StringTokenizer parser = new StringTokenizer(property, ":");
        while (parser.hasMoreTokens()) {
            System.err.println(parser.nextToken());
        }
    }
}

Launched from eclipse with Java 1.6.0_14 outputs:

/opt/java/jre/lib/i386/client
/opt/java/jre/lib/i386
/opt/java/jre/../lib/i386
/opt/java/jre/lib/i386/client
/opt/java/jre/lib/i386
/usr/lib/xulrunner-devel-1.9.0.11
/usr/lib/xulrunner-devel-1.9.0.11
/usr/java/packages/lib/i386
/lib
/usr/lib

But when I set the JVM arg "-Djava.library.path=/tmp/" I only get:

/tmp/

If you are manually setting java.library.path this may explain why ldd works from the command line but your .so does not from eclipse/java.

You can try not setting java.library.path and use System.load with the absolute path to your library instead of calling System.loadLibrary. This may allow the JVM to find your .so and still use the default path when searching for its dependencies.

Of course, if this is no use then you can also try turning on jni debug output with "-verbose:jni" on the command line. This may give you some clues to the problem.

Aaron