在上一篇文章《Java虚拟机原理分析之Win7下VS2010编译OpenJDK8与单步调试HotSpot VM过程详细记录》中,我们在Win7+VS2010环境下成功编译出了x86版本的OpenJDK。然而VS2010毕竟有些年头了,我也只是在开发机上才装了这个经典的VS版本,而在自己的电脑上使用的是VS2017。而通过远程桌面连到开发机是一件很不爽的事,并且VS2010的IDE自然是不如VS2017的好用。因此萌发了使用VS2017编译OpenJDK的想法,然而经过搜索并没能在网上找到类似的文章和教程,于是只得靠自己摸索了。
首先放上我的环境信息:
Microsoft Windows 10 LTSB 2016版本(补丁更新到最新)
Visual Studio 2017(补丁更新到最新,装有VisualAssistX插件)
JDK Win64 8u152版本
Cygwin x64版本,已装好了需要的组件
freetype 2.81版本,已用VS2017编译好x86的Dll版本,放在I:\jvm\freetype目录下
OpenJDK v8源码最新版本,放在I:\jvm\jdk8u-dev目录下
msvcr100.dll的x86版本(只是用来骗配置过程的),放在I:\jvm\目录下
由于之前已经详细记录过Win7下使用VS2010编译x86版本OpenJDK的过程了,因此这里对于重复部分就不再详细讲解了,直接一句话带过,如有需要可以参考上一篇文章中的详细记叙。
下载好源码、安装好Cygwin及工具并编译生成dll版本的typefree(这部分操作与VS2010编译时一致,在此不再赘述),然后使用如下命令:
bash ./configure --with-freetype=/cygdrive/i/jvm/freetype -with-target-bits=32 --with-debug-level=slowdebug --with-jvm-variants=client
接着在寻找VS时会报错:
打开/common/autoconf/generated-configure.sh文件,搜索“Cannot locate a valid Visual Studio”进行定位,发现可以通过传入–with-tools-dir参数指定vcvars32.bat的路径,从而定位到头文件、库文件等的路径。针对VS2017,该批处理位于以下路径:
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build
修改代码,主要匹配其vcvars32.bat和VS100BASE路径。值得注意的是,VS2017默认的vcvars32xp.bat没有包含WinSDK 7.1,并且生成的可执行文件也无法在XP上运行。因此拷贝vcvars32.bat为vcvars32xp.bat,修改其内容如下:
@call "%~dp0vcvarsall.bat" x86 %*
set INCLUDE=%ProgramFiles(x86)%\Microsoft SDKs\Windows\v7.1A\Include;%INCLUDE%
set PATH=%ProgramFiles(x86)%\Microsoft SDKs\Windows\v7.1A\Bin;%PATH%
set LIB=%ProgramFiles(x86)%\Microsoft SDKs\Windows\v7.1A\Lib;%LIB%
set CL=/D_USING_V110_SDK71_;%CL%
set LINK=/SUBSYSTEM:CONSOLE,5.01 %LINK%
然后对generated-configure.sh文件中的代码进行修改:
# First-hand choice is to locate and run the vsvars bat file.
if test "x$OPENJDK_TARGET_CPU_BITS" = x32; then
VCVARSFILE="VC/Auxiliary/Build/vcvars32xp.bat" # 这里改为VS2017的路径
else
VCVARSFILE="VC/Auxiliary/Build/vcvars64.bat" # 这里改为VS2017的路径
fi
VS_ENV_CMD=""
VS_ENV_ARGS=""
if test "x$with_toolsdir" != x; then
if test "x$VS_ENV_CMD" = x; then
VS100BASE="$with_toolsdir/../../.." # 这里改为VS2017的路径
METHOD="--with-tools-dir"
修改后,重新输入配置命令如下:
bash ./configure --with-freetype=/cygdrive/i/JVM/freetype -with-target-bits=32 --with-debug-level=slowdebug --with-jvm-variants=client with_toolsdir="/cygdrive/c/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/VC/Auxiliary/Build"
执行后继续报错:
看来程序考虑的比较周到,如果是在VS2010环境下编译,还会检查msvcr100.dll的版本,估计最后生成阶段会有一步CRT库Dll拷贝的动作。反正这里我们是使用VS2017编译,生成的可执行文件肯定不会依赖msvcr100.dll。所以这里我们就做做样子,给它传一个x86版本的msvcr100.dll路径吧。用下列命令进行配置
bash ./configure --with-freetype=/cygdrive/i/JVM/freetype -with-target-bits=32 --with-debug-level=slowdebug --with-jvm-variants=client with_toolsdir="/cygdrive/c/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/VC/Auxiliary/Build" --with-msvcr-dll="/cygdrive/i/jvm/msvcr100.dll"
执行后报了一个我们很熟悉的错误:
这个错误在之前用VS2010编译时也出现过,因此这里直接修改,在jdk8u-dev\common\autoconf下的generated-configure.sh中做如下修改:
# First line typically looks something like:
# Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
COMPILER_VERSION_TEST="19.12.15831" # 直接指定
COMPILER_VERSION="19" # 直接指定
COMPILER_VENDOR="Microsoft CL.EXE"
COMPILER_CPU_TEST="x86" # 直接指定
# 下面的一堆if全部删掉
再进行配置,万里长征第一步终于顺利走完了。
下面到了激动人心的编译时刻了。
make images
很快,一堆错误出现了。首先是MSC_VER没能被正确识别,然后是一堆编译错误——首先出现的就是JVM源码部分的一堆编译错误。于是决定先编译HotSpot VM项目,生成VS工程文件再说,即先将HostSpot项目导入VS2017中,利用VS2017快速修改其源码,使其通过编译。然而执行create.bat时马上就报错了:
打开jdk8u-dev\hotspot\make\windows下的create.bat,将以下代码删除掉。
cl 2>NUL >NUL
if %errorlevel% == 0 goto nexttest
echo Make sure cl.exe is in your PATH before running this script.
goto end
:nexttest
grep -V 2>NUL >NUL
if %errorlevel% == 0 goto testit
echo Make sure grep.exe is in your PATH before running this script. Either cygwin or MKS should work.
goto end
:testi
# 然后在其中设置MSC_VER=1700即可
由于下面有一堆判断Visual Studio版本的逻辑,因此这里将版本设置到其支持的最高版本——VS2012的1700即可。然后在I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug中创建images和jdk两个目录,其中images下创建j2sdk-image,其中再创建bin文件。
完成工程文件的生成后,将其导入VS2017中。下面的操作在VS2017的IDE中完成。
首先解决C2220错误。
error C2220: warning treated as error - no object file generated”
到工程属性中关闭“将警告视为错误”选项即可,如下图所示:
然后是函数体重定义的编译错误:
jdk8u-dev\hotspot\src\share\vm\utilities/globalDefinitions_visCPP.hpp(190): error C2084: function 'int vsnprintf(char *const ,const ::size_t,const char *const ,va_list)' already has a body
这个问题是JVM中定义了自己的vsnprintf函数,和C库的vsnprintf函数撞衫了。于是直接使用VisualAssistx的重构功能,将JVM自定义的vsnprintf函数重命名为jvmvsnprintf即可:
然后是一大堆宏定义的报错。
这个问题比较在HotSpot VM中很常见,到处都有。其产生原因https://stackoverflow.com/questions/31738796/using-macro-with-string-fails-on-vc-2015
值得注意的是,使用旧版本的编译器不会有问题,因为这是启用C++11特性所带来的坑。其实就是这些宏本身就是字符串,其与两侧的分号应该有空格间隔。解决方案就是补加空格。如上图中由上角所示。在整个解决方案中搜索替换报错的语句,将其替换为两侧带空格的即可。以”ABCD”为例,需要进行下述两次替换:
"ABCD->" ABCD
ABCD"->ABCD "
全部完成后,编译错误瞬间降到了一个:
error C2065: 'timezone': undeclared identifier
经过搜索,在https://bugs.chromium.org/p/webrtc/issues/detail?id=4521找到了解决方案。在VS2017中要换一种获取方式,如下所示:
#if defined(_ALLBSD_SOURCE)
const time_t zone = (time_t) time_struct.tm_gmtoff;
#else
#if _MSC_VER < 1900
const time_t zone = timezone;
#else
const time_t zone = 0;
_get_timezone((long *)&zone);
#endif
#endif
把上面这些坑都填平后,HotSpot VM的编译终于告一段落了。
继续脚本编译,果不其然又报错了:
先找出负责link版本的那个make文件:jdk8u-dev\hotspot\make\windows\makefiles\sanity.make,将对cl和link版本进行判断的脚本注释掉。
然后打开jdk8u-dev\hotspot\make\windows\makefiles下的compile.make,做如下修改:
# 第56行 去掉/WX
CXX_FLAGS=$(EXTRA_CFLAGS) /nologo /W3
# 第123行,去掉判定逻辑,强制指定
COMPILER_NAME=VS2012
保存后继续编译,眼看快成功时,又报错了:
这个问题貌似是动态CRT库和静态CRT库打架了,但JVM貌似全部都依赖的是动态库啊,不然也不会让传入msvcr100.dll路径了。上网查了半天,终于发现问题了:
We resolved the issue. Seems we had the following in one of our header files to work around in issue with one of the earlier versions of VS 2012.
说白了这是历史遗留问题,VS2012时代的历史遗留产物到了VS2017时代不好用了,也就是下面这两个家伙需要被干掉:
#define _STATIC_CPPLIB
#define _DISABLE_DEPRECATE_STATIC_CPPLIB
没招了,在文件夹中全局搜索“_STATIC_CPPLIB”字样,只要不是出现在PDB文件里,咱们统统将其干掉。
----------------------------------------
“在 'I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\config.status' 中查找 '_STATIC_CPPLIB' (2017/12/19 星期二 15:04:22; 2017/12/19 星期二 15:04:24):”
I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\config.status(662): S["CXXFLAGS_JDKEXE"]=" -nologo -Zi -MD -Zc:wchar_t- -W3 -wd4800 -D_STATIC_CPPLIB -D_DISABLE_DEPRECATE_STATIC_CPPLIB -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_DE"\
I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\config.status(667): S["CXXFLAGS_JDKLIB"]=" -nologo -Zi -MD -Zc:wchar_t- -W3 -wd4800 -D_STATIC_CPPLIB -D_DISABLE_DEPRECATE_STATIC_CPPLIB -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_DE"\
I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\config.status(673): S["CFLAGS_JDKEXE"]=" -nologo -Zi -MD -Zc:wchar_t- -W3 -wd4800 -D_STATIC_CPPLIB -D_DISABLE_DEPRECATE_STATIC_CPPLIB -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_DE"\
I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\config.status(678): S["CFLAGS_JDKLIB"]=" -nologo -Zi -MD -Zc:wchar_t- -W3 -wd4800 -D_STATIC_CPPLIB -D_DISABLE_DEPRECATE_STATIC_CPPLIB -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_DE"\
找到 '_STATIC_CPPLIB' 8 次。
----------------------------------------
“在 'I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\spec.gmk' 中查找 '_STATIC_CPPLIB' (2017/12/19 星期二 15:04:25; 2017/12/19 星期二 15:04:25):”
I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\spec.gmk(327): CFLAGS_JDKLIB:= -nologo -Zi -MD -Zc:wchar_t- -W3 -wd4800 -D_STATIC_CPPLIB -D_DISABLE_DEPRECATE_STATIC_CPPLIB -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DWIN32 -DIAL -D_X86_ -Dx86 -D_LITTLE_ENDIAN -DWINDOWS -DDEBUG -DARCH='"i586"' -Di586 -DRELEASE='"$(RELEASE)"' -I/cygdrive/i/jvm/jdk8u-dev/build/windows-x86-normal-client-slowdebug/jdk/include -I/cygdrive/i/jvm/jdk8u-dev/build/windows-x86-normal-client-slowdebug/jdk/include/windows -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/share/javavm/export -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/windows/javavm/export -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/share/native/common -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/windows/native/common
I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\spec.gmk(328): CXXFLAGS_JDKLIB:= -nologo -Zi -MD -Zc:wchar_t- -W3 -wd4800 -D_STATIC_CPPLIB -D_DISABLE_DEPRECATE_STATIC_CPPLIB -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DWIN32 -DIAL -D_X86_ -Dx86 -D_LITTLE_ENDIAN -DWINDOWS -DDEBUG -DARCH='"i586"' -Di586 -DRELEASE='"$(RELEASE)"' -I/cygdrive/i/jvm/jdk8u-dev/build/windows-x86-normal-client-slowdebug/jdk/include -I/cygdrive/i/jvm/jdk8u-dev/build/windows-x86-normal-client-slowdebug/jdk/include/windows -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/share/javavm/export -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/windows/javavm/export -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/share/native/common -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/windows/native/common
I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\spec.gmk(331): CFLAGS_JDKEXE:= -nologo -Zi -MD -Zc:wchar_t- -W3 -wd4800 -D_STATIC_CPPLIB -D_DISABLE_DEPRECATE_STATIC_CPPLIB -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DWIN32 -DIAL -D_X86_ -Dx86 -D_LITTLE_ENDIAN -DWINDOWS -DDEBUG -DARCH='"i586"' -Di586 -DRELEASE='"$(RELEASE)"' -I/cygdrive/i/jvm/jdk8u-dev/build/windows-x86-normal-client-slowdebug/jdk/include -I/cygdrive/i/jvm/jdk8u-dev/build/windows-x86-normal-client-slowdebug/jdk/include/windows -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/share/javavm/export -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/windows/javavm/export -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/share/native/common -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/windows/native/common
I:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\spec.gmk(332): CXXFLAGS_JDKEXE:= -nologo -Zi -MD -Zc:wchar_t- -W3 -wd4800 -D_STATIC_CPPLIB -D_DISABLE_DEPRECATE_STATIC_CPPLIB -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DWIN32 -DIAL -D_X86_ -Dx86 -D_LITTLE_ENDIAN -DWINDOWS -DDEBUG -DARCH='"i586"' -Di586 -DRELEASE='"$(RELEASE)"' -I/cygdrive/i/jvm/jdk8u-dev/build/windows-x86-normal-client-slowdebug/jdk/include -I/cygdrive/i/jvm/jdk8u-dev/build/windows-x86-normal-client-slowdebug/jdk/include/windows -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/share/javavm/export -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/windows/javavm/export -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/share/native/common -I/cygdrive/i/jvm/jdk8u-dev/jdk/src/windows/native/common
找到 '_STATIC_CPPLIB' 8 次。
----------------------------------------
“在 'I:\jvm\jdk8u-dev\common\autoconf\generated-configure.sh' 中查找 '_STATIC_CPPLIB' (2017/12/19 星期二 13:06:27; 2017/12/19 星期二 15:03:25):”
I:\jvm\jdk8u-dev\common\autoconf\generated-configure.sh(30017): -D_STATIC_CPPLIB -D_DISABLE_DEPRECATE_STATIC_CPPLIB -DWIN32_LEAN_AND_MEAN \
找到 '_STATIC_CPPLIB' 2 次。
----------------------------------------
“在 'I:\jvm\jdk8u-dev\common\autoconf\toolchain.m4' 中查找 '_STATIC_CPPLIB' (2017/12/19 星期二 13:06:27; 2017/12/17 星期日 11:07:41):”
I:\jvm\jdk8u-dev\common\autoconf\toolchain.m4(1068): -D_STATIC_CPPLIB -D_DISABLE_DEPRECATE_STATIC_CPPLIB -DWIN32_LEAN_AND_MEAN \
找到 '_STATIC_CPPLIB' 2 次。
----------------------------------------
“在 'I:\jvm\jdk8u-dev\hotspot\make\windows\makefiles\adlc.make' 中查找 '_STATIC_CPPLIB' (2017/12/19 星期二 13:07:40; 2017/12/17 星期日 11:08:56):”
I:\jvm\jdk8u-dev\hotspot\make\windows\makefiles\adlc.make(29): # $(MS_RUNTIME_OPTION) ( with /D_STATIC_CPPLIB)
找到 '_STATIC_CPPLIB' 1 次。
----------------------------------------
“在 'I:\jvm\jdk8u-dev\hotspot\make\windows\makefiles\compile.make' 中查找 '_STATIC_CPPLIB' (2017/12/19 星期二 13:07:40; 2017/12/19 星期二 20:34:01):”
I:\jvm\jdk8u-dev\hotspot\make\windows\makefiles\compile.make(160): # /D _STATIC_CPPLIB /D _DISABLE_DEPRECATE_STATIC_CPPLIB
I:\jvm\jdk8u-dev\hotspot\make\windows\makefiles\compile.make(162): # Always add the _STATIC_CPPLIB flag
I:\jvm\jdk8u-dev\hotspot\make\windows\makefiles\compile.make(163): STATIC_CPPLIB_OPTION = /D _STATIC_CPPLIB /D _DISABLE_DEPRECATE_STATIC_CPPLIB
找到 '_STATIC_CPPLIB' 5 次。
这么多文件里,一个个修改。好在这两个宏基本定义都在一起,所以删起来倒也挺方便。都改好以后,执行一次清理操作:
make clean
继续构建,然后出现了上一篇文章中提到过的换行符问题,解决完成后,编译通过。
此时强制使用VS2017重新生成jvm.dll后,解压其符号与java.exe的符号即可开始调试。
20171221注:在VS的调试命令参数中,加入-XXaltjvm=$(TargetDir)参数,则默认使用VS2010编译得到的jvm.dll,由于jvm.pdb也在该目录下,所以可以直接调试,无需对jvm相应的压缩包进行任何解压操作了(java.exe的还是需要的)。另外加入的类路径最好放在最后。