Skip to content

NativeBinaryLoader.loadLibrary() fails silently if clean extraction fails #46

Description

@stephengold
        try {
            loader.loadLibrary(LoadingCriterion.CLEAN_EXTRACTION);
        } catch (Exception exception) {
            throw new IllegalStateException(
                    "Failed to load a native library!", exception);
        }

If none of the registered native libraries is loadable (for instance because the libraries with matching predicates are missing from the JAR) the code above does not throw an exception. The failure isn't discovered until the application tries to invoke a native method, at which time a UnsatisfiedLinkError is thrown.

I expected an exception to be thrown during loadLibrary(). Ideally diagnostics should indicate:

  • which libraries it tried to extract and load
    and
  • why each one failed to load.

Activity

  1. pavly-gerges commented on Sep 18, 2026

    @pavly-gerges
    Member
  2. stephengold commented on Sep 22, 2026

    @stephengold
    ContributorAuthor

    Perhaps a concrete example will clarify the issue.

    I modified TestBasicFeatures.java to report whether loadLibrary() throws an exception:

    +++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBasicFeatures.java
    @@ -74,7 +74,14 @@
             loader.setRetryWithCleanExtraction(true);
             /* Native dynamic library properties */
             printDetails(loader);
    -        loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING);
    \ No newline at end of file
    +
    +        try {
    +            loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING);
    +        } catch (Exception exception) {
    +            System.err.println("loadLibrary threw an exception");
    +            System.exit(-1);
    +        }
    +        System.err.println("no exception thrown");
    \ No newline at end of file
         }
     
         protected static void printDetails(NativeBinaryLoader loader) {

    Then I replaced the NativeDynamicLibrary for my platform with an invalid entry:

    # This patch file was generated by NetBeans IDE
    # It uses platform neutral UTF-8 encoding and \n newlines.
    --- a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBasicFeatures.java
    +++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestBasicFeatures.java
    @@ -58,7 +58,7 @@
     
         public static final NativeDynamicLibrary[] libraries = new NativeDynamicLibrary[] {
                 DefaultDynamicLibraries.LINUX_X86,
    -            DefaultDynamicLibraries.LINUX_X86_64,
    +            new NativeDynamicLibrary("nonexistent/directory", "junk", PlatformPredicate.LINUX_X86_64),
                 new NativeDynamicLibrary("lib/windows/x86", "libjmealloc.dll", PlatformPredicate.WIN_X86),
                 new NativeDynamicLibrary("lib/windows/x86-64", "libjmealloc.dll", PlatformPredicate.WIN_X86_64),
                 DefaultDynamicLibraries.MAC_X86,

    When I execute this modified TestBasicFeatures, no exception is thrown.

  3. pavly-gerges commented on Sep 23, 2026

    @pavly-gerges
    Member

    During the initialization stage; this function should throw an UnSupportedSystemError prior to calling the loading command.

    public NativeBinaryLoader initPlatformLibrary() throws UnSupportedSystemError {
    final boolean[] isSystemFound = new boolean[]{false};
    // search for the compatible library using the predefined predicate
    // a predicate is a conditional statement composed of multiple propositions
    // representing the complete system variant (OS + ARCH + VM).
    registeredLibraries.forEach(nativeDynamicLibrary -> {
    if (isSystemFound[0]) {
    return;
    }
    // re-evaluate the library info part
    if (libraryInfo != null) {
    nativeDynamicLibrary.initWithLibraryInfo(libraryInfo);
    }
    if (nativeDynamicLibrary.getPlatformPredicate().evaluatePredicate()) {
    this.nativeDynamicLibrary = nativeDynamicLibrary;
    isSystemFound[0] = true;
    }
    });
    // execute a system found listeners
    if (isSystemFound[0]) {
    if (systemDetectionListener != null) {
    systemDetectionListener.onSystemFound(this, nativeDynamicLibrary);
    }
    } else {
    if (systemDetectionListener != null) {
    systemDetectionListener.onSystemNotFound(this);
    }
    throw new UnSupportedSystemError(NativeVariant.OS_NAME.getProperty(),
    NativeVariant.OS_ARCH.getProperty());
    }
    return this;
    }

    Please verify whether the output log has an UnSupportedSystemError.

  4. stephengold commented on Sep 23, 2026

    @stephengold
    ContributorAuthor

    That error was not thrown because isSystemFound[0] was true in line 146.

  5. pavly-gerges commented on Oct 4, 2026

    @pavly-gerges
    Member

    The file locator system throws these, when a non-existent library is trying to be located:

    > Task :snaploader-examples:run
    --------------------------------------------------------------
    OS: Linux
    ARCH: amd64
    VM: Java HotSpot(TM) 64-Bit Server VM
    --------------------------------------------------------------
    Jar Path: /media/pavl-g/pavl-g/Digital-Library/Projects/jSnapLoader/snaploader-examples/libs/jme3-alloc-desktop-1.0.0-pre-gamma-2.jar
    Library Directory: nonexistent/directory
    Compressed library path: nonexistent/directory/junk
    Extracted library absolute path: /media/pavl-g/pavl-g/Digital-Library/Projects/jSnapLoader/snaploader-examples/libs/junk
    Is Extracted: false
    --------------------------------------------------------------
    Oct 04, 2026 2:44:38 PM electrostatic4j.snaploader.library.LibraryExtractor initialize(int)
    INFO: File extractor initialized with hash key #1705710286
    Oct 04, 2026 2:44:38 PM electrostatic4j.snaploader.library.LibraryLocator close
    INFO: File locator #1848380775 resources closed!
    Oct 04, 2026 2:44:38 PM electrostatic4j.snaploader.NativeBinaryLoader$2 preInitLibraryLocator
    SEVERE: Locating native libraries has failed!
    electrostatic4j.snaploader.throwable.FilesystemResourceInitializationException: Cannot locate the file entry in the compression!
            at electrostatic4j.snaploader.util.StreamObjectValidator.validateAndThrow(StreamObjectValidator.java:62)
            at electrostatic4j.snaploader.filesystem.FileLocator.externalCompressionRoutine(FileLocator.java:179)
            at electrostatic4j.snaploader.filesystem.FileLocator.initialize(FileLocator.java:131)
            at electrostatic4j.snaploader.NativeBinaryLoader.initializeLibraryExtractor(NativeBinaryLoader.java:457)
            at electrostatic4j.snaploader.NativeBinaryLoader.cleanExtractBinary(NativeBinaryLoader.java:379)
            at electrostatic4j.snaploader.NativeBinaryLoader.loadLibrary(NativeBinaryLoader.java:196)
            at electrostatic4j.snaploader.examples.TestBasicFeatures.main(TestBasicFeatures.java:78)
    
    Oct 04, 2026 2:44:38 PM electrostatic4j.snaploader.library.LibraryExtractor close
    INFO: File extractor #1705710286 resources closed!
    Oct 04, 2026 2:44:38 PM electrostatic4j.snaploader.NativeBinaryLoader cleanExtractBinary
    INFO: File extractor handler initialized!
    Oct 04, 2026 2:44:38 PM electrostatic4j.snaploader.NativeBinaryLoader$1 cleanExtractBinary
    SEVERE: Extraction has failed!
    electrostatic4j.snaploader.throwable.FilesystemResourceInitializationException: Broken file locator stream provider!
            at electrostatic4j.snaploader.util.StreamObjectValidator.validateAndThrow(StreamObjectValidator.java:62)
            at electrostatic4j.snaploader.filesystem.FileExtractor.extract(FileExtractor.java:138)
            at electrostatic4j.snaploader.filesystem.ConcurrentFileExtractor.extract(ConcurrentFileExtractor.java:72)
            at electrostatic4j.snaploader.NativeBinaryLoader.cleanExtractBinary(NativeBinaryLoader.java:435)
            at electrostatic4j.snaploader.NativeBinaryLoader.loadLibrary(NativeBinaryLoader.java:196)
            at electrostatic4j.snaploader.examples.TestBasicFeatures.main(TestBasicFeatures.java:78)
    
    
    BUILD SUCCESSFUL in 2s
    4 actionable tasks: 1 executed, 3 up-to-date
    

    Do you recognize these at your runtime logs?

    However, if a broken file already exists; an UnSatisfiedLinkError is thrown because the file is too short (i.e., Corrupted file):

    java.lang.UnsatisfiedLinkError: /media/pavl-g/pavl-g/Digital-Library/Projects/jSnapLoader/snaploader-examples/libs/junk: /media/pavl-g/pavl-g/Digital-Library/Projects/jSnapLoader/snaploader-examples/libs/junk: file too short
            at java.base/jdk.internal.loader.NativeLibraries.load(Native Method)
            at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:331)
            at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:197)
            at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:139)
            at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2404)
            at java.base/java.lang.Runtime.load0(Runtime.java:817)
            at java.base/java.lang.System.load(System.java:2015)
            at electrostatic4j.snaploader.NativeBinaryLoader.loadBinary(NativeBinaryLoader.java:339)
            at electrostatic4j.snaploader.NativeBinaryLoader.loadLibrary(NativeBinaryLoader.java:192)
            at electrostatic4j.snaploader.examples.TestBasicFeatures.main(TestBasicFeatures.java:78)
    
  6. stephengold commented on Oct 4, 2026

    @stephengold
    ContributorAuthor

    Here's the complete output of "TestBasicFeatures.java" running on Linux with the changes described above:

    -------------------------------------------------------------
    OS: Linux
    ARCH: amd64
    VM: OpenJDK 64-Bit Server VM
    --------------------------------------------------------------
    Jar Path: /mnt/virginia/home/sgold/NetBeansProjects/ext/jSnapLoader/snaploader-examples/libs/jme3-alloc-desktop-1.0.0-pre-gamma-2.jar
    Library Directory: nonexistent/directory
    Compressed library path: nonexistent/directory/junk
    Extracted library absolute path: /mnt/virginia/home/sgold/NetBeansProjects/ext/jSnapLoader/snaploader-examples/libs/junk
    Is Extracted: true
    --------------------------------------------------------------
    OpenJDK 64-Bit Server VM warning: You have loaded library /mnt/virginia/home/sgold/NetBeansProjects/ext/jSnapLoader/snaploader-examples/libs/junk which might have disabled stack guard. The VM will try to fix the stack guard now.
    It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'.
    Oct 04, 2026 10:22:36 AM electrostatic4j.snaploader.NativeBinaryLoader loadBinary
    SEVERE: Cannot load the dynamic library: /mnt/virginia/home/sgold/NetBeansProjects/ext/jSnapLoader/snaploader-examples/libs/junk
    java.lang.UnsatisfiedLinkError: /mnt/virginia/home/sgold/NetBeansProjects/ext/jSnapLoader/snaploader-examples/libs/junk: /mnt/virginia/home/sgold/NetBeansProjects/ext/jSnapLoader/snaploader-examples/libs/junk: file too short
    	at java.base/jdk.internal.loader.NativeLibraries.load(Native Method)
    	at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:388)
    	at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:232)
    	at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:174)
    	at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2394)
    	at java.base/java.lang.Runtime.load0(Runtime.java:755)
    	at java.base/java.lang.System.load(System.java:1970)
    	at electrostatic4j.snaploader.NativeBinaryLoader.loadBinary(NativeBinaryLoader.java:339)
    	at electrostatic4j.snaploader.NativeBinaryLoader.loadLibrary(NativeBinaryLoader.java:192)
    	at electrostatic4j.snaploader.examples.TestBasicFeatures.main(TestBasicFeatures.java:79)
    
    Oct 04, 2026 10:22:36 AM electrostatic4j.snaploader.library.LibraryExtractor initialize(int)
    INFO: File extractor initialized with hash key #214123406
    Oct 04, 2026 10:22:36 AM electrostatic4j.snaploader.library.LibraryLocator close
    INFO: File locator #1848380775 resources closed!
    Oct 04, 2026 10:22:36 AM electrostatic4j.snaploader.NativeBinaryLoader$2 preInitLibraryLocator
    SEVERE: Locating native libraries has failed!
    electrostatic4j.snaploader.throwable.FilesystemResourceInitializationException: Cannot locate the file entry in the compression!
    	at electrostatic4j.snaploader.util.StreamObjectValidator.validateAndThrow(StreamObjectValidator.java:62)
    	at electrostatic4j.snaploader.filesystem.FileLocator.externalCompressionRoutine(FileLocator.java:179)
    	at electrostatic4j.snaploader.filesystem.FileLocator.initialize(FileLocator.java:131)
    	at electrostatic4j.snaploader.NativeBinaryLoader.initializeLibraryExtractor(NativeBinaryLoader.java:457)
    	at electrostatic4j.snaploader.NativeBinaryLoader.cleanExtractBinary(NativeBinaryLoader.java:379)
    	at electrostatic4j.snaploader.NativeBinaryLoader.loadBinary(NativeBinaryLoader.java:366)
    	at electrostatic4j.snaploader.NativeBinaryLoader.loadLibrary(NativeBinaryLoader.java:192)
    	at electrostatic4j.snaploader.examples.TestBasicFeatures.main(TestBasicFeatures.java:79)
    
    Oct 04, 2026 10:22:36 AM electrostatic4j.snaploader.library.LibraryExtractor close
    INFO: File extractor #214123406 resources closed!
    Oct 04, 2026 10:22:36 AM electrostatic4j.snaploader.NativeBinaryLoader cleanExtractBinary
    INFO: File extractor handler initialized!
    Oct 04, 2026 10:22:36 AM electrostatic4j.snaploader.NativeBinaryLoader$1 cleanExtractBinary
    SEVERE: Extraction has failed!
    electrostatic4j.snaploader.throwable.FilesystemResourceInitializationException: Broken file locator stream provider!
    	at electrostatic4j.snaploader.util.StreamObjectValidator.validateAndThrow(StreamObjectValidator.java:62)
    	at electrostatic4j.snaploader.filesystem.FileExtractor.extract(FileExtractor.java:138)
    	at electrostatic4j.snaploader.filesystem.ConcurrentFileExtractor.extract(ConcurrentFileExtractor.java:72)
    	at electrostatic4j.snaploader.NativeBinaryLoader.cleanExtractBinary(NativeBinaryLoader.java:435)
    	at electrostatic4j.snaploader.NativeBinaryLoader.loadBinary(NativeBinaryLoader.java:366)
    	at electrostatic4j.snaploader.NativeBinaryLoader.loadLibrary(NativeBinaryLoader.java:192)
    	at electrostatic4j.snaploader.examples.TestBasicFeatures.main(TestBasicFeatures.java:79)
    
    no exception thrown

    How is the application supposed to detect that extraction has failed?

  7. pavly-gerges commented on Oct 4, 2026

    @pavly-gerges
    Member

    How is the application supposed to detect that extraction has failed?

    I've just found the problem and its solution; the problem is that the Java Legacy FileOutputStream creates a new File when it's initialized regardless of whether a physical file pre-existed, which in our case leads to a creation of blank file that is too short or corrupt to be loadable by the dynamic loader.... The result cannot be avoided by jSnapLoader, so the solution is to delete that blank file when the library locator has failed...

    The UnSatisfiedLinkError thrown in this case is not as result of trying to invoke a native function, but rather as a result of trying to load a bad file that is not a native binary.

    I will submit a PR and let's continue discussion there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions