Search code examples
javagradlejava-native-interfacecompatibilityportability

How to know if a .jar built on AMD64 will run flawlessly on ARM?


I built a .jar in docker on the ARM architecture and one on AMD64.

The two .jar files have the identical size, but vbindiff says their contents are quite different.

I tested both .jar files on my AMD64 computer and the reversed slogan "build anywhere, run once" holds.

My hypothesis is that this has to do with Java Native Interface (JNI). The .jar is a Spring Boot Webflux backend. Unfortunately, I don't know if it or any other dependency uses JNI.

I noted that the ARM image has JDK 17.0.3 installed, while the AMD64 image has JDK 17.0.2. But this should not be a problem, since I built both .jars using the Gradle wrapper, which specifies the exact toolchain to be downloaded and used to build the project:

kotlin("jvm") version "1.6.10"

What could be the reason for the difference? Can I assume that either .jar can be used on any platform that has a compatible JVM?

EDIT: I followed Thomas' advice and used diff -r to compare the extracted contents of the .jar files. They are identical.

However, diff confirms that the .jar files themselves are different.

I just learned that the .jar format is based on .zip, which can use various compression methods, as well as include extra information in file headers, such as 'last modified' or optional OS-specific attributes. Mystery solved.


Solution

  • You can list the files in the .jar using jar tf myfile.jar. If the only contents are .class files and the manifest data in META-INF/, then chances are good that it's 100% portable. If you see other files like .so, .dll or .dylib, then there is native code in there which might give trouble.

    Here's how you can list all the files which might warrant a closer look:

    jar tf myfile.jar | grep -Pv '^META-INF/|(\.class|/)$'
    

    Since you have already built the .jars on two different platforms, you can also extract their contents using jar xf myfile.jar and use diff -r to compare them recursively. This is a more robust way to detect differences than comparing the archives directly, although I imagine that .class files might not be byte-wise identical either even if they're semantically the same.