1

I'm using Google OR-tools library (v6.4) for a project (though my question is not specific to this library). This consists of one jar, which has a few native dependencies (a bunch of ".so"/".dylib" object files, depending on the OS). This build for my project is being made on Ubuntu 14.04

The problem I'm facing: On trying to load a specific object file at runtime (using System.load()), I'm getting an UnsatisfiedLinkError with the message as "undefined symbol" (I've added the stacktrace below). However, I am loading the object file defining this symbol just before this, so I'm not sure why this error is being thrown.

I'm loading the dependencies in the following way: The object files are being packed into the jar created by Maven during build, and are being extracted and loaded (using System.load()) at runtime. The method for that is as follows:

public class EnvironmentUtils {

    public static void loadResourceFromJar(String prefix, String suffix) {
        String tempFilesDirectory = System.getProperty("java.io.tmpdir");
        File tempFile = null;
        try {
            tempFile = new File(tempFilesDirectory + "/" + prefix + suffix);
            tempFile.deleteOnExit();
            try (final InputStream inputStream = EnvironmentUtils.class.getClassLoader().
                    getResourceAsStream(prefix+suffix)) {
                if (inputStream == null) {
                    throw new RuntimeException(prefix + suffix + " was not found inside JAR.");
                } else {
                    Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                }
            }
            System.load(tempFile.getAbsolutePath());
        } catch (Exception e) {
            //Log top 10 lines of stack trace
        }
    }
}

This method is being called inside a static block for all dependencies:

public class DummyClass {
    static {
        String sharedLibraryExtension = EnvironmentUtils.getSharedLibraryExtension(); //.so for linux, .dylib for Mac
        String jniLibraryExtension = EnvironmentUtils.getJniLibraryExtension(); //.so for linux, .jnilib for Mac
        EnvironmentUtils.loadResourceFromJar("libfap", sharedLibraryExtension);
        EnvironmentUtils.loadResourceFromJar("libcvrptw_lib", sharedLibraryExtension);
        EnvironmentUtils.loadResourceFromJar("libortools", sharedLibraryExtension);
        EnvironmentUtils.loadResourceFromJar("libdimacs", sharedLibraryExtension);
        EnvironmentUtils.loadResourceFromJar("libjniortools", jniLibraryExtension);
    }
}

On running System.load() for libdimacs.so, an UnsatisfiedLinkError is thrown. Stacktrace:

java.lang.UnsatisfiedLinkError: /tmp/libdimacs.so: /tmp/libdimacs.so: undefined symbol: _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_
    at java.lang.ClassLoader$NativeLibrary.load(Native Method)
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824)
    at java.lang.Runtime.load0(Runtime.java:809)
    at java.lang.System.load(System.java:1086)
    at com.(PROJECT_NAME).utils.EnvironmentUtils.loadResourceFromJar(EnvironmentUtils.java:78)
    at com.(PROJECT_NAME).DummyClass.<clinit>(DummyClass.java:28)

However, this symbol "_ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_" is present in libortools.so, which is being loaded before libdimacs. I verified this by running the following command:

objdump -t (LIBRARY_PATH)/libortools.so | grep _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_

This gave me the following output:

0000000000ce12cc gw    F .text       00000091 _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_

So it would seem that the symbol should have been defined at the time of the System.load() call, unless there was some issue in loading the containing object file. To check if the object file had been loaded correctly, I used the approach detailed in this solution. Apart from the class detailed in that answer, I added the following lines after System.load() call in EnvironmentUtils.loadResourceFromJar() to print the most recently loaded library name:

public class EnvironmentUtils {

    public static void loadResourceFromJar(String prefix, String suffix) {
        ...
        System.load(tempFile.getAbsolutePath());
        final String[] libraries = ClassScope.getLoadedLibraries(ClassLoader.getSystemClassLoader());
        System.out.println(libraries[libraries.length - 1]);
    }
}

The output (till just before the UnsatisfiedLinkError) is as follows:

/tmp/libfap.so
/tmp/libcvrptw_lib.so
/tmp/libortools.so

So libortools.so seems to be loading correctly, which means the symbol should be loaded in memory. The exact same code is working perfectly with the corresponding Mac (".dylib") dependencies (Built on MacOS Sierra 10.12.5). Would appreciate any advice on resolving this. Thank you.

  • Why do you need libfap, libcvrptw and libdimac ? There are only used by ortools examples to avoid copy/paste between examples i.e. libortools.so should be use as standalone (with libjniortools for wrapper) IMHO – Mizux May 16 '18 at 06:32
  • @MizuxDev - Oh, that solves it then! I was under the impression that all the object files would be necessary, so I was loading all of them. Thanks. – Yash Ugrankar May 16 '18 at 09:15

1 Answers1

0

I'm apologize that the java artifact may be broken currently...

you can use c++filt to demangle the symbol ;)

c++filt _ZN6google14FlagRegistererC1IbEEPKcS3_S3_PT_S5_
google::FlagRegisterer::FlagRegisterer<bool>(char const*, char const*, char const*, bool*, bool*)

In fact gflag has recently change its namespace from google:: to gflags:: and glog or protobobuf? try to find the correct one and I guess it failed...
note: Still not completely sure whose is the bad guy who use the google:: namespace since libortools merge all its static dependencies but I guess now you understand the bug...

note2: I have a patch in mizux/shared branch https://github.com/google/or-tools/commit/805bc0600f4b5645114da704a0eb04a0b1058e28#diff-e8590fe6fb5044985c8bf8c9e73c0d88R114
warning: this branch is currently broken and not ready yet. I'm trying ,for unix, to move from static to dynamic dependencies, so I need to fix all rpath, transitives deps etc... and in the process I also had to fix this issue (that I didn't reproduced while using static dependencies)

If too long to finish (we should create a release 6.7.2 or 6.8 (i.e. new artifact) by the end of May 2018) which maybe only contains this fix and not my branch...

Mizux
  • 8,222
  • 7
  • 32
  • 48