Search code examples
javalinuxspring-bootjna

Issues loading .so file with JNA in Spring Boot on Linux


I have an application written in Java with Spring Boot. This application needs to load .so files that are bundled with the project, I am using JNA to accomplish this. However, the JNA loader doesn't seem to be able to pick up the .so files and the application fails to start.

I've tried running the application from within IntelliJ and by running the packaged .jar with "java -jar". The .so files are kept in src/main/resources/linux-x86-64 for now. I've tried keeping them in a different directory, e.g src/main/resources/libs/linux-x86-64 and set the property "jna.library.path", but JNA still fails to find the files.

This is the debug log for JNA:

Trying (via loadLibrary) jnidispatch
Looking in classpath from sun.misc.Launcher$AppClassLoader@18b4aac2 for /com/sun/jna/linux-x86-64/libjnidispatch.so
Found library resource at jar:file:/home/dalivi/.m2/repository/net/java/dev/jna/jna/4.5.0/jna-4.5.0.jar!/com/sun/jna/linux-x86-64/libjnidispatch.so
Trying /tmp/jna--1339148563/jna4246531844315283838.tmp
Found jnidispatch at /tmp/jna--1339148563/jna4246531844315283838.tmp
Looking for library 'GTransTF'
Adding paths from jna.library.path: null
Trying libGTransTF.so
Adding system paths: [/usr/lib/x86_64-linux-gnu, /lib/x86_64-linux-gnu, /lib64, /usr/lib, /lib, /lib/i386-linux-gnu, /usr/lib/i386-linux-gnu, /usr/lib/x86_64-linux-gnu/libfakeroot]
Trying libGTransTF.so
Looking for version variants
Looking in classpath from sun.misc.Launcher$AppClassLoader@18b4aac2 for GTransTF
Found library resource at file:/home/dalivi/Workspace/java/geotransboot/target/classes/linux-x86-64/libGTransTF.so
Looking in /home/dalivi/Workspace/java/geotransboot/target/classes/linux-x86-64/libGTransTF.so
2019-04-25 12:43:38.032 ERROR 25897 --- [o-auto-1-exec-1] s.l.g.c.TransformationRestController     : Handler dispatch failed; nested exception is java.lang.UnsatisfiedLinkError: libCoreGTrans.so: cannot open shared object file: No such file or directory

I does seem to find one of the files in the directory: libGTransTF.so, but then immediately fails when trying to find the file libCoreGTrans.so which is present in the same directory as the previous file.

I should mention, on Windows, this works just fine. JNA finds the corresponding dll files in the directory specified with "jna.library.path".


Solution

  • The windows behaviour is to search for dependent libraries in the directory that the .dll comes from, so when jna loads the library into memory the dependent library is loaded from there as well.

    If you fire up a terminal window and cd to the directory that the .so exists in and run the command:

    ldd ./libGTransTF.so
    

    and it indicates that it's unable to find the library libCoreGTrans.so then you can see that the search order won't find this location.

    The run-time link-loader (ld.so) uses a set of decisions as to where to find libraries. The default behaviour doesn't include the directory that the library was found.

    You can add an option to the library when building to search in specific locations to find libraries. When you're building the library, you can say to search in the directory that the .so comes from at run time by adding the line:

    -Wl,-rpath,'$ORIGIN'
    

    to the link line. It needs to populate with the constant value $ORIGIN or else this doesn't work, so this can be a bit tricky to get right in a makefile. This is a value that gets resolved at run-time.

    This is all very fine and well if you're building the library yourself, but if you're getting libraries from somewhere else, or you've already built them and don't want to rebuild them, you can use a tool such as patchelf to edit the search path for an .so to add it's origin location:

    patchelf --set-rpath '$ORIGIN' libGTransTF.so
    

    Then when you run:

    ldd ./libGTransTF.so
    

    it should be able to successfully find the libCoreGTrans.so library.