Skip to content

Fix darwin-aarch64 (Apple Silicon Mac) native loading#15

Open
foontinz wants to merge 1 commit intoburningwave:mainfrom
foontinz:feat/darwin-aarch64-build
Open

Fix darwin-aarch64 (Apple Silicon Mac) native loading#15
foontinz wants to merge 1 commit intoburningwave:mainfrom
foontinz:feat/darwin-aarch64-build

Conversation

@foontinz
Copy link
Copy Markdown

@foontinz foontinz commented Apr 8, 2026

Problem

On Apple Silicon Macs running native arm64 Java, loading NativeExecutor crashes JVM init with NoClassDefFoundError on StaticComponentContainer. dlopen rejects a Linux ELF named .so:

Files$NotLoadedException: Unable to load file org/burningwave/jvm/libNativeExecutor-aarch64.so
UnsatisfiedLinkError: ... slice is not valid mach-o file

Two-part bug:

  1. Libraries.java picks extension = "so" for all aarch64 platforms, even macOS. The aarch64 branch runs before the macOS branch that would have set dylib.
  2. No darwin-aarch64 build target in native/pom.xml and no Mach-O ARM64 binary in native/bin/. Even with the loader fixed there's nothing to load.

Affects downstream libraries that bundle jvm-driver on Apple Silicon. Reported via TheComputerizer/The-Impossible-Library#35.

Fix (3 files)

  • Libraries.java — when osArch=aarch64 AND macOS, use dylib. Five lines, no behavior change on Linux/Windows.
  • native/pom.xml — extract the artifact suffix into a linker.arch.suffix property (default x${sun.arch.data.model}); add a mac-aarch64 profile activated on os.family=mac + os.arch=aarch64 that overrides it to aarch64. Inherits the existing mac profile compile flags. Apple clang++ targets arm64-apple-darwin natively on a native arm64 JVM, so no -arch flag needed.
  • native/bin/libNativeExecutor-aarch64.dylib — pre-built (44864 bytes, 40 JNI exports, identical symbol set to the existing Linux ARM64 .so). Compiled with Apple clang++ 21.0 against OpenJDK 17.0.18 ARM headers from this commit's source.

Verified

Functional test (real workload)

Class.forName("org.burningwave.jvm.NativeExecutor") and Class.forName("org.burningwave.core.assembler.StaticComponentContainer") both succeed on Apple M5 Pro / OpenJDK 17.0.18 ARM Homebrew. Also reproduced inside a real Forge 1.20.1 modpack (Biohazard: Project Genesis): game launches and connects to a Forge server with full mod parity.

Full test suite across JDK versions (Apple Silicon aarch64)

Ran the upstream AllTestsSuite (60 tests — DefaultDriver, HybridDriver, NativeDriver, DynamicDriver) on every ARM64 macOS JDK available from the three distributions the CI uses. Removing the dylib causes all 15 NativeDriverTest cases to error out, confirming the binary is exercised.

Distribution Java Result
Zulu 11.0.30 Pass — 60/60
Zulu 17.0.18 Pass — 60/60
Zulu 21.0.10 Pass — 60/60
Zulu 23.0.2 Pass — 60/60
Zulu 24.0.2 Pass — 60/60
Zulu 25.0.2 Pass — 60/60
Zulu 26 Pass — 60/60
Temurin 11.0.30 Pass — 60/60
Temurin 23.0.2 Pass — 60/60
Temurin 24.0.2 Pass — 60/60
Temurin 25.0.2 Pass — 60/60
Temurin 26 Pass — 60/60
Semeru 11.0.30 Pass — 60/60
Semeru 22.0.2 OpenJ9 GPF — pre-existing; pure-Java DefaultDriverTest also crashes (no JNI involved)
Semeru 26 OpenJ9 GPF — same as above

The Semeru 22/26 crashes are an OpenJ9 bug on aarch64 macOS, unrelated to this PR — the pure-Java DefaultDriverTest (which never touches JNI) crashes identically. The upstream CI only runs Semeru on x64, so this is not a regression.

Intermediate Java versions (9, 12–16, 18–20) have no ARM64 macOS builds from any vendor (they predate Apple Silicon). The upstream CI tests those only on x64.

Out of scope

CI workflow doesn't yet have a macos-latest aarch64 matrix entry to auto-regenerate this binary. Can send a separate workflow PR if you'd rather have CI regenerate the binary.

Apple Silicon Macs running native arm64 Java crash at JVM init when
loading NativeExecutor with NoClassDefFoundError on
StaticComponentContainer, caused by dlopen rejecting a Linux ELF named .so:

  Files$NotLoadedException: Unable to load file
    org/burningwave/jvm/libNativeExecutor-aarch64.so
  UnsatisfiedLinkError: ... slice is not valid mach-o file

Two-part fix:

1. Libraries.java: when osArch=aarch64 AND os is macOS, use the
   .dylib extension. The previous aarch64 branch hardcoded "so" for
   ALL aarch64 platforms — correct on Linux but wrong on macOS,
   where it was reached *before* the macOS branch that would have
   set extension="dylib".

2. native/pom.xml: introduce a linker.arch.suffix property
   (default x${sun.arch.data.model}, overridden to "aarch64" in a
   new mac-aarch64 profile activated on os.family=mac AND
   os.arch=aarch64). This makes a native build on a native arm64
   macOS runner produce libNativeExecutor-aarch64.dylib instead of
   libNativeExecutor-x64.dylib.

3. Pre-built native/bin/libNativeExecutor-aarch64.dylib compiled
   with Apple clang++ 21.0 against OpenJDK 17.0.18 ARM headers,
   targeting arm64-apple-darwin. CI can regenerate this once a
   macos-latest aarch64 workflow entry is added (left as a
   follow-up PR to keep this one focused on the actual fix).

The C++ source is purely portable JNI (zero #ifdef __APPLE__) so it
compiles cleanly with no source changes; verified by exact symbol-set
match against the existing libNativeExecutor-aarch64.so (40 JNI exports).

Tested end-to-end on Apple M5 Pro / OpenJDK 17.0.18 ARM Homebrew with
the smoke test:

  Class.forName("org.burningwave.jvm.NativeExecutor");
  Class.forName("org.burningwave.core.assembler.StaticComponentContainer");

Both classes load and statically initialize successfully. Also
reproducible inside a real Forge 1.20.1 modpack (Biohazard: Project
Genesis) that bundles jvm-driver via TheImpossibleLibrary.

Refs: TheComputerizer/The-Impossible-Library#35
@foontinz foontinz marked this pull request as ready for review April 8, 2026 17:20
@foontinz
Copy link
Copy Markdown
Author

foontinz commented Apr 8, 2026

@Roberto-Gentili , let me know if you have any issues with this patch. Happy to change as needed!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant