Building the OpenJDK Zero Variant on Windows
I was recently investigating the behavior of the OpenJDK interpreter on Windows. Demystifying the JVM: JVM Variants, Cppinterpreter and TemplateInterpreter describes two different interpreter implementations. I wanted to try reproducing the bug I was investigating using the simple BytecodeInterpreter. I was wondering how to enable it but the post led me to the realization that it’s not a configurable option. I needed to build the JVM specifically to include this interpreter. We need to get Julian Waters’ Windows/Zero branch with the changes I made to build using Visual C++: swesonga/jdk at swesonga/TheShermanTanker/experimental. The next sections cover the steps for building for x64 and ARM64.
Building Zero for Windows x64
Build libffi for Windows x64 (see Building libffi for Windows x64 with Visual C++) then set up the repo for use by OpenJDK as follows:
cd /c/repos/libffi
mkdir lib
cp x86_64-w64-mingw32/.libs/libffi-8.lib lib/libffi.lib
cp x86_64-w64-mingw32/include/ffi.h include/
cp x86_64-w64-mingw32/include/ffitarget.h include/
Configure the OpenJDK repo using these commands:
git checkout swesonga/TheShermanTanker/experimental
bash configure --with-jvm-variants=zero --with-libffi=/cygdrive/c/repos/libffi --disable-warnings-as-errors --with-debug-level=slowdebug --with-jtreg=/cygdrive/c/java/binaries/jtreg/jtreg-7.5.1+1 --with-gtest=/cygdrive/c/repos/googletest --with-extra-ldflags=-profile --with-boot-jdk=/cygdrive/c/java/binaries/jdk/x64/jdk-24+36
Build OpenJDK:
time /cygdrive/c/repos/scratchpad/scripts/java/cygwin/build-jdk.sh windows x86_64 slowdebug zero
The build will fail with Error: Failed to load D:\java\forks\dups11\openjdk\jdk\build\windows-x86_64-zero-slowdebug\jdk\bin\zero\jvm.dll. However, the build is still usable (all the binaries and symbols should be present). I am saving that investigation for another day. For now, verify that the build works by running this command:
$ build/windows-x86_64-zero-slowdebug/jdk/bin/java.exe -version
java version "25-internal" 2025-09-16
Java Runtime Environment (slowdebug build 25-internal-adhoc.USERsaint.jdk)
Java HotSpot 64-Bit Zero VM (slowdebug build 25-internal-adhoc.USERsaint.jdk, interpreted mode)
Building Zero for Windows AArch64
Once the steps in Building libffi for Windows ARM64 with Visual C++ in MSYS are complete, set up the repo for use by OpenJDK as follows:
cd /c/repos/libffi
mkdir lib
cp aarch64-w64-mingw32/.libs/libffi-8.lib lib/libffi.lib
cp aarch64-w64-mingw32/include/ffi.h include/
cp aarch64-w64-mingw32/include/ffitarget.h include/
Configure the OpenJDK repo using these commands:
git checkout swesonga/TheShermanTanker/experimental
bash configure --with-jvm-variants=zero --with-libffi=/cygdrive/d/repos/dups/libffi --disable-warnings-as-errors --with-debug-level=slowdebug --with-jtreg=/cygdrive/c/java/binaries/jtreg/jtreg-7.5.1+1 --with-gtest=/cygdrive/c/repos/googletest --with-extra-ldflags=-profile --with-boot-jdk=/cygdrive/c/java/binaries/jdk/x64/jdk-24+36
Build OpenJDK:
time /cygdrive/c/repos/scratchpad/scripts/java/cygwin/build-jdk.sh windows aarch64 slowdebug zero
Background Investigation
OpenJDK’s building.md says to use --with-jvm-variants configure argument to specify the “zero” variant:
bash configure --with-jvm-variants=zero --with-debug-level=slowdebug --with-jtreg=/cygdrive/c/java/binaries/jtreg/jtreg-7.5.1+1 --with-gtest=/cygdrive/c/repos/googletest --with-extra-ldflags=-profile --with-boot-jdk=/cygdrive/c/java/binaries/jdk/x64/jdk-24+36
The configure script failed with this error:
checking for --enable-hsdis-bundling... disabled, default
checking what hsdis backend to use... 'none', hsdis will not be built
checking if hsdis should be bundled... no
checking for --enable-libffi-bundling... disabled, default
checking for LIBFFI... checking for ffi.h... no
configure: error: Could not find libffi!
configure exiting with result code 1
I took at look at the source of the error message in jdk/make/autoconf/lib-ffi.m4 and realized that I need to have the repo.
git clone https://github.com/libffi/libffi.git
cd libffi
git checkout v3.4.8
I then added the --with-libffi argument to the configure script.
bash configure --with-jvm-variants=zero --with-debug-level=slowdebug --with-jtreg=/cygdrive/c/java/binaries/jtreg/jtreg-7.5.1+1 --with-gtest=/cygdrive/c/repos/googletest --with-extra-ldflags=-profile --with-boot-jdk=/cygdrive/c/java/binaries/jdk/x64/jdk-24+36 --with-libffi=/cygdrive/d/repos/libffi
This didn’t address the failure but closer inspection of the .m4 led me to realize that I need to build libffi. This was the genesis of the post on Building libffi for Windows x64 with Visual C++. Once I had built libffi and created the .lib file in the expected location of its repo, the configure script succeeded. I started the build with this command:
time /cygdrive/c/repos/scratchpad/scripts/java/cygwin/build-jdk.sh windows x86_64 slowdebug zero
The build failed!
* For target hotspot_variant-zero_libjvm_gtest_objs_BUILD_GTEST_LIBJVM_pch.obj:
BUILD_GTEST_LIBJVM_pch.cpp
d:\java\forks\dups11\openjdk\jdk\src\hotspot\share\runtime/globals.hpp(35): fatal error C1083: Cannot open include file: 'globals_windows_zero.hpp': No such file or directory
... (rest of output omitted)
This failure reminded me of the Windows/Zero mail where Julian Waters fixed build zero for windows (although his suggestion to fix this was getting shot down). I was wondering whether this was working at some point in a previous release so I tried openjdk/jdk11u-dev at c5407b6a8464fcc1eed31a2e9e30651e9011dbd3. I got the same error. The next step was to build Julian Waters’ Windows/Zero branch: Windows/Zero Port · TheShermanTanker/jdk@f504cd8. I imported it into my fork as follows:
git remote add TheShermanTanker https://github.com/TheShermanTanker/jdk
git fetch TheShermanTanker
git checkout experimental
git checkout -b swesonga/TheShermanTanker/experimental
git push --set-upstream origin swesonga/TheShermanTanker/experimental
Building this branch failed with this error:
* For target support_gensrc_java.base__SocketOptionRegistry.java:
/*
* Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
*
...
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
... (rest of output omitted)
* All command lines available in /cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/make-support/failure-logs.
=== End of repeated output ===
There was a .log file and a .cmd file in the failure-logs folder. build\windows-x86_64-zero-slowdebug\make-support\failure-logs\support_gensrc_java.base__SocketOptionRegistry.java.log contained this:
/*
* Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
/cygdrive/d/java/forks/dups11/openjdk/jdk/make/scripts/fixpath.sh: line 486: c:\progra~1\mib055~1\2022\enterp~1\vc\tools\msvc\1444~1.352\bin\hostx64\x64\cl.exe: command not found
build\windows-x86_64-zero-slowdebug\make-support\failure-logs\support_gensrc_java.base__SocketOptionRegistry.java.cmdline contained this:
( /usr/bin/gawk '/@@END_COPYRIGHT@@/{exit}1' /cygdrive/d/java/forks/dups11/openjdk/jdk/src/java.base/share/classes/sun/nio/ch/SocketOptionRegistry.java.template && /cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/fixpath exec /cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/fixpath exec /cygdrive/c/progra~1/mib055~1/2022/enterp~1/vc/tools/msvc/1444~1.352/bin/hostx64/x64/cl.exe -E -nologo -I/cygdrive/c/progra~1/mib055~1/2022/enterp~1/vc/tools/msvc/1444~1.352/include -I/cygdrive/c/progra~1/mib055~1/2022/enterp~1/vc/tools/msvc/1444~1.352/atlmfc/include -I/cygdrive/c/progra~1/mib055~1/2022/enterp~1/vc/auxili~1/vs/include -I/cygdrive/c/progra~2/wi3cf2~1/10/include/100261~1.0/ucrt -I/cygdrive/c/progra~2/wi3cf2~1/10/include/100261~1.0/um -I/cygdrive/c/progra~2/wi3cf2~1/10/include/100261~1.0/shared -I/cygdrive/c/progra~2/wi3cf2~1/10/include/100261~1.0/winrt -I/cygdrive/c/progra~2/wi3cf2~1/10/include/100261~1.0/cppwinrt -I/cygdrive/c/progra~2/wi3cf2~1/netfxsdk/4.8/include/um -D_CRT_DECLARE_NONSTDC_NAMES -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -nologo -MD -Zc:preprocessor -Zc:inline -Zc:throwingNew -permissive- -volatile:iso -utf-8 -Zc:wchar_t- -DLIBC=default -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=0x0602 -DWIN32 -DIAL -DWINDOWS -DDEBUG -W3 -Z7 -experimental:deterministic -std:c11 -D_LITTLE_ENDIAN -DARCH='"amd64"' -Damd64 -D_LP64=1 -D_AMD64_ -Damd64 /cygdrive/d/java/forks/dups11/openjdk/jdk/src/java.base/share/classes/sun/nio/ch/SocketOptionRegistry.java.template 2> >(/usr/bin/grep -v '^SocketOptionRegistry.java.template$' >&2) | /usr/bin/gawk '/@@START_HERE@@/,0' | /usr/bin/sed -e 's/@@START_HERE@@/\/\/ AUTOMATICALLY GENERATED FILE - DO NOT EDIT/' -e 's/PREFIX_//' -e 's/^#.*//' ) > /cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/support/gensrc/java.base/sun/nio/ch/SocketOptionRegistry.java
This might be the first time I ever looked up AWK – Wikipedia since I needed to understand what was happening here. The hack I used to address this is Fix ‘command not found’ error · swesonga/jdk@6145ebb.
There were several warnings (e.g. warning C4267: ‘initializing’: conversion from ‘size_t’ to ‘int’, possible loss of data) and a build error that I fixed in Fix MSVC warnings and build errors · swesonga/jdk@bd519ea. I later realized that I could have used the --disable-warnings-as-errors configure argument but it was good for me to be aware of which issues I could run into later when using the zero interpreter. At this point, the build failed with this message:
* For target buildtools_create_symbols_javac__the.COMPILE_CREATE_SYMBOLS_batch:
Error: Failed to load D:\java\forks\dups11\openjdk\jdk\build\windows-x86_64-zero-slowdebug\jdk\bin\zero\jvm.dll
* All command lines available in /cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/make-support/failure-logs.
That error in the only line in the build\windows-x86_64-zero-slowdebug\make-support\failure-logs\buildtools_create_symbols_javac__the.COMPILE_CREATE_SYMBOLS_batch.log file. This was the .cmdline file (next to the .log file):
/cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/fixpath exec /cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/jdk/bin/javac -J-Djava.io.tmpdir=/cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/support/javatmp -g -Xlint:all -source 25 -target 25 -implicit:none -Xprefer:source -XDignore.symbol.file=true -encoding ascii --add-modules jdk.compiler,jdk.jdeps --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED -Werror -Xlint:-options -XDmodifiedInputs=/cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/buildtools/create_symbols_javac/_the.COMPILE_CREATE_SYMBOLS_batch.modfiles.fixed -d /cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/buildtools/create_symbols_javac @/cygdrive/d/java/forks/dups11/openjdk/jdk/build/windows-x86_64-zero-slowdebug/buildtools/create_symbols_javac/_the.COMPILE_CREATE_SYMBOLS_batch.filelist
However, the build process appeared to have actually created a usable JDK!
build/windows-x86_64-zero-slowdebug/jdk/bin/java.exe -version
That meant that I didn’t need to look any further into this error (at least not right away) so I moved on to building for the ARM64 platform. I initially tried cross compiling but this didn’t work. This is the configure command I tried for cross compiling.
bash configure --with-jvm-variants=zero --with-libffi=/cygdrive/d/repos/dups/libffi --openjdk-target=aarch64-unknown-cygwin --disable-warnings-as-errors --with-debug-level=slowdebug --with-jtreg=/cygdrive/c/java/binaries/jtreg/jtreg-7.5.1+1 --with-gtest=/cygdrive/c/repos/googletest --with-extra-ldflags=-profile --with-boot-jdk=/cygdrive/c/java/binaries/jdk/x64/jdk-24+36
The linker failed with a series of 19 unresolved symbols and a warning that the library machine type ‘ARM64’ conflicts with target machine type ‘x64’. Notice that the === Output from failing command(s) repeated here === section did not contain the linker warning LNK4272 since it omits the rest of the input after showing the first 14 lines. Scanning the build log is important in such cases where some of the output is omitted at the end.
...
fallbackLinker.o : error LNK2019: unresolved external symbol __imp_ffi_type_double referenced in function Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1double
fallbackLinker.o : error LNK2019: unresolved external symbol __imp_ffi_type_pointer referenced in function Java_jdk_internal_foreign_abi_fallback_LibFallback_ffi_1type_1pointer
d:\repos\dups\libffi\lib\libffi.lib : warning LNK4272: library machine type 'ARM64' conflicts with target machine type 'x64'
d:\java\forks\dups11\openjdk\jdk\build\windows-aarch64-zero-slowdebug\buildjdk\support\modules_libs\java.base\fallbackLinker.dll : fatal error LNK1120: 19 unresolved externals
At this point, I switched to my ARM64 machine (to avoid these mismatch issues) and copied the the libffi files onto it (after cloning the libffi repo). The --openjdk-target argument is no longer necessary for a native build on Windows ARM64. The build failed since the CONTEXT (x86 64-bit) struct (defined in C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um\winnt.h is a platform specific struct. This fix was needed for ARM64: Fix build errors on Windows AArch64 · swesonga/jdk@d3ec3c7. OpenJDK now built successfully, well, until the same error about being unable to load jvm.dll.
I moved on to testing the build but this time it didn’t work! I added some debug output in Display GetLastError() on failure to load DLL · swesonga/jdk@ce7143e. GetLastError was 126, i.e. “the specified module could not be found” as per the System Error Codes (0-499) (WinError.h). I verified that the path to jvm.dll was valid then used Process Monitor to record events on the system while running java.exe. Turns out I needed to copy libffi-8.dll into the directory containing jvm.dll.
Leave a Reply