10

I am wrapping a shared library (written in C) with Java using JNA. The shared library is written internally, but that library uses functions from another external library, which again depends another external library. So the situation is something like this:

ext1 <- ext2 <- internal

I.e. the internal uses external library ext2 which again uses external library ext1. What I have tried is:

System.loadLibrary("ext1");
System.loadLibrary("ext2");
NativeLIbrary.loadLibrary("internal",xxx.class);  

This approach fails with "UnresolvedException" when loading the library "ext2"; the linker complains about symbols which are indeed present in the library "ext1". So it semmes that the System.loadLibrary() function does not make the symbols from "ext1" globally available? When using the stdlib function dlopen() as:

handle = dlopen( lib_name , RTLD_GLOBAL );

All the symbols found in @lib_name will be available for symbol resolution in subsequent loads; I guess what I would like was something similar for the java variety System.loadLibrary()?

Regards - Joakim Hove

Isa_R
  • 77
  • 6
user422005
  • 1,989
  • 16
  • 34
  • Is the path to the libraries defined in the Java property `java.library.path`? – Mark Elliot Mar 24 '11 at 20:48
  • Well - locating the libraries is actually another gripe, and I have resorted to using System.load( full_path_to_shared_library ) - but that problem I think is mostly a netbeans problem. The exception when loading library "ext" is during the symbol resolvment phase, i.e. the library has been correctly located and so on. I think/fear this is related to the RTLD_GLOBAL flag (or lack thereof) in the low level dlopen() call deep down in the runtime. – user422005 Mar 24 '11 at 21:03

6 Answers6

5

It's an old question, but I've found an acceptable solution, which should also be portable, and I thought I should post an answer. The solution is to use JNA's NativeLibrary#getInstance(), because on Linux this will pass RTLD_GLOBAL to dlopen() (and on Windows this is not needed).

Now, if you are using this library to implement a Java native method, you will also need to call System.load() (or Sysem.loadLibrary()) on the same library, after calling NativeLibrary#getInstance().

First, a link to a JNA bug: JNA-61

A comment in there says that basically one should load dependencies before the actual library to use from within JNA, not the standard Java way. I'll just copy-paste my code, it's a typical scenario:

String libPath =
        "/path/to/my/lib:" + // My library file
        "/usr/local/lib:" +  // Libraries lept and tesseract
        System.getProperty("java.library.path");

System.setProperty("jna.library.path", libPath);

NativeLibrary.getInstance("lept");
NativeLibrary.getInstance("tesseract");
OcrTesseractInterf ocrInstance = (OcrTesseractInterf)
        Native.loadLibrary(OcrTesseractInterf.JNA_LIBRARY_NAME, OcrTesseractInterf.class);

I've written a small library to provide OCR capability to my Java app using Tesseract. Tesseract dependes on Leptonica, so to use my library, I need to load libraries lept and tesseract first. Loading the libraries with the standard means (System.load() and System.loadLibrary()) doesn't do the trick, neither does setting properties jna.library.path or java.library.path. Obviously, JNA likes to load libraries its own way.

This works for me in Linux, I guess if one sets the proper library path, this should work in other OSs as well.

Owen
  • 38,836
  • 14
  • 95
  • 125
vagelis
  • 334
  • 3
  • 9
3

There is yet another solution for that. You can dlopen directly inside JNI code, like this:

void loadLibrary() {
  if(handle == NULL) {
    handle = dlopen("libname.so", RTLD_LAZY | RTLD_GLOBAL);
    if (!handle) {
      fprintf(stderr, "%s\n", dlerror());
      exit(EXIT_FAILURE);
    }
  }
}

...
...

loadLibrary();

This way, you will open library with RTLD_GLOBAL.

You can find detailed description here: http://www.owsiak.org/?p=3640

Oo.oO
  • 12,464
  • 3
  • 23
  • 45
2

OK;

I have found an acceptable solution in the end, but not without significant amount of hoops. What I do is

  1. Use the normal JNA mechanism to map the dlopen() function from the dynamic linking library (libdl.so).
  2. Use the dlopen() function mapped in with JNA to load external libraries "ext1" and "ext2" with the option RTLD_GLOBAL set.

It actually seems to work :-)

user422005
  • 1,989
  • 16
  • 34
1

As described at http://www.owsiak.org/?p=3640, an easy but crude solution on Linux is to use LD_PRELOAD.

If that's not acceptable, then I'd recommend the answer by Oo.oO: dlopen the library with RTLD_GLOBAL within JNI code.

mhsmith
  • 6,675
  • 3
  • 41
  • 58
0

Try this, add this function to your code. Call it before you load your dlls. For the parameter, use the location of your dlls.


    public boolean addDllLocationToPath(String dllLocation)
    {
        try
        {
            System.setProperty("java.library.path", System.getProperty("java.library.path") + ";" + dllLocation);
            Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
            fieldSysPath.setAccessible(true);
            fieldSysPath.set(null, null);
        }
        catch (Exception e)
        {
            System.err.println("Could not modify path");
            return false;
        }
        return true;
    }
}

user489041
  • 27,916
  • 55
  • 135
  • 204
  • 1
    Thank you for the effort - your suggestion works nicely when it comes to locating the libraries. But that is unfortunately not my problem, the problem is that the symbols in library "ext2" are not available to functions in "ext1" - when using the dlopen() library call directly (maybe that is what I must do ???) this behaviour is controlled by an integer flag, and the behaviour I am seeking is achieved with the RTLD_GLOBAL flag. This seemed to be more difficult than I thought?! – user422005 Mar 24 '11 at 21:27
0

In order to fix your issue you can use this package: https://github.com/victor-paltz/global-load-library. It loads the libraries directly with the RTLD_GLOBAL flag.

Here is an example:

import com.globalload.LibraryLoaderJNI;

public class HelloWorldJNI {
 
    static {
        // Loaded with RTLD_GLOBAL flag
        try {
            LibraryLoaderJNI.loadLibrary("/path/to/my_native_lib_A");
        } catch (UnsatisfiedLinkError e) {
            System.Println("Couldn't load my_native_lib_A");
            System.Println(e.getMessage());
            e.printStackTrace();
        }

        // Not loaded with RTLD_GLOBAL flag
        try {
            System.load("/path/to/my_native_lib_B");
        } catch (UnsatisfiedLinkError e) {
            System.Println("Couldn't load my_native_lib_B");
            System.Println(e.getMessage());
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        new HelloWorldJNI().sayHello();
    }
 
    private native void sayHello();
}

It is using the same dlopen() trick as the previous answers, but it is packaged in a standalone code.

programort
  • 140
  • 5
  • When I load with LibraryLoaderJNI.loadLibrary() I then get a UnsatisfiedLinkError from java. I find I need to "load" the library twice, once with LibraryLoader... and once with System.load. Is that to be expected? – dan.m was user2321368 Mar 19 '21 at 16:00