环己三烯的冬眠舱

天天网抑云,偶尔读点书。

0%

WSL2 + CLion 编译并调试JDK12源码

引言

最近打算开始读《深入理解Java虚拟机》,作者在第一章推荐读者自己编译一份JDK,并使用CLion来阅读和调试源码。由于笔者没有原生的Linux或MacOS环境,只能使用WSL2来平替。在配置过程中踩了一些坑,遂记录一下,没准以后会有人需要。

获取源码

源码的仓库在https://hg.openjdk.java.net/jdk/jdk12 ,点击左边的browse就是源码的文件目录了。本来想直接在WSL里使用wget来下载.tar.gz文件,发现下载不动,于是选择使用Windows的浏览器直接下载.tar.gz,再从文件系统里找到WSL的目录复制进去。

然后从WSL里打开对应目录就能找到源码文件,虽然多了一个不知道有什么用的Identifier(猜测跟Windows的文件系统有关),但是并不影响源码的解压。使用tar指令解压压缩包即可获得源码。

1
tar -xzvf jdk12-06222165c35f.tar.gz

构建编译环境

不得不说Linux下配环境确实比Windows方便不少。

在开始之前建议先更新一下软件源:sudo apt-get update

安装GCC

1
sudo apt-get install build-essential

安装一些依赖库

这个表是书的作者提供的,实际操作的时候发现我的环境还缺了一个ZIPEXE(提示Could not find required tool for ZIPEXE),安装后就好了:sudo apt-get install zip

安装Bootstrap JDK

编译大版本号为N的JDK时需要用到另一个大版本号至少为N-1的已经编译好的JDK。因为OpenJDK只有部分代码使用C/C++编写,更多的是使用Java编写的,所以需要另一个编译期可用的JDK来编译这部分代码,官方称这个JDK为“Bootstrap JDK”

1
sudo apt-get install openjdk-11-jdk

进行编译

源码目录中有一个叫configure的bash文件,提供检查依赖项是否齐全和设置参数等功能。作者给出的配置指令是bash configure --enable-debug --with-jvm-variants=server,配置完毕后使用make images进行编译。但是实际上我遇到了两个问题。

没有jre目录

报错信息如下:

1
错误的路径元素 "/usr/lib/jvm/java-17-openjdk-amd64/jre/lib": 没有这种文件或目录

我顺着这个目录找下去,发现我只有/usr/lib/jvm/java-17-openjdk-amd64目录,因为从JDK11开始jre已经不是默认安装了。我们可以手动生成一个(需要切换到需要生成jre的JDK的目录):

1
./bin/jlink --module-path jmods --add-modules java.desktop --output jre

这里需要调用的应该是上面提到的Bootstrap JDK,但是我环境变量之前配过JDK17,所以这里就调用了JDK17,而不是新下载的JDK11。

调用strncpy产生的奇怪问题

报错信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
=== Output from failing command(s) repeated here ===
* For target hotspot_variant-server_libjvm_objs_arguments.o:
In file included from /usr/include/string.h:495,
from /home/dzc/JVM/jdk12-06222165c35f/src/hotspot/share/utilities/globalDefinitions_gcc.hpp:35,
from /home/dzc/JVM/jdk12-06222165c35f/src/hotspot/share/utilities/globalDefinitions.hpp:32,
from /home/dzc/JVM/jdk12-06222165c35f/src/hotspot/share/utilities/align.hpp:28,
from /home/dzc/JVM/jdk12-06222165c35f/src/hotspot/share/runtime/globals.hpp:29,
from /home/dzc/JVM/jdk12-06222165c35f/src/hotspot/share/memory/allocation.hpp:28,
from /home/dzc/JVM/jdk12-06222165c35f/src/hotspot/share/classfile/classLoaderData.hpp:28,
from /home/dzc/JVM/jdk12-06222165c35f/src/hotspot/share/precompiled/precompiled.hpp:34:
In function ‘char* strncpy(char*, const char*, size_t)’,
inlined from ‘static jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs*, bool*, JVMFlag::Flags)’ at /home/dzc/JVM/jdk12-06222165c35f/src/hotspot/share/runtime/arguments.cpp:2472:29:
/usr/include/x86_64-linux-gnu/bits/string_fortified.h:106:34: error: ‘char* __builtin_strncpy(char*, const char*, long unsigned int)’ output truncated before terminating nul copying as many bytes from a string as its length [-Werror=stringop-truncation]
106 | return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
... (rest of output omitted)

* All command lines available in /home/dzc/JVM/jdk12-06222165c35f/build/linux-x86_64-server-fastdebug/make-support/failure-logs.
=== End of repeated output ===

说实话没怎么看懂,似乎和什么字符串被截断有关,在网上查了很久都没找到有类似情况的。然后发现官方有在源码目录里附上docs,于是不抱希望地点开查看,在building.htmlTroubleshooting里找到了这样一句:

1
Hint: If caused by a warning, try configure --disable-warnings-as-errors

又想到报错信息里有这么一句:

1
cc1plus: all warnings being treated as errors

于是清除原来的配置之后重新配置,添加不把warnings视作errors的配置:

1
2
3
make clean
make dist-clean
bash configure --enable-debug --with-jvm-variants=server --disable-warnings-as-errors

之后再make images就编译成功了。进入build/配置名称/jdk/bin目录下执行./java -version可以看到版本信息:

1
2
3
openjdk version "12-internal" 2019-03-19
OpenJDK Runtime Environment (fastdebug build 12-internal+0-adhoc.dzc.jdk12-06222165c35f)
OpenJDK 64-Bit Server VM (fastdebug build 12-internal+0-adhoc.dzc.jdk12-06222165c35f, mixed mode)

在CLion中进行源码调试

由于不是原生的Linux环境,我在尝试从源码导入CMake项目的时候也卡了很久,最后按照这篇文章的流程完成了配置。

CLion连接Ubuntu

打开CLion,左侧选择WSL,再选择New Project,然后根据指引操作。选择文件目录时选择刚才的JDK源码目录(即解压之后得到的目录)。之后CLion会自动开始在WSL里下载服务端并启动。

创建自定义Build Target

点击IDE右上角的齿轮,选择Settings,左边找到Custom Build Targets,点击“+”新建一个Build Target,Toolchain选择Default,并填写Name。

然后点击Build或Clean右边的“…”,新建两个External Tools:

新建完后把make填到Build里,make clean填到Clean里。

创建自定义Run/Debug configuration

点击这个位置的Edit Configurations,并新建一个Custom Build Application。

Target选择刚才自定义的Build Target,Executable选择编译出来的JDK(刚才查看version时执行的那个程序),Program arguments可以先填一个-version测试一下。下面的build记得删掉。

至此配置完成。

测试运行、打断点

完成以上配置之后,右上角的Run和Debug应该已经变成绿色的了。

点击Run,可以发现输出了JDK的版本信息。

接下来测试断点功能。JVM的入口函数是src/java.base/share/native/libjli/java.c文件下的JavaMain(void * _args)函数。在这个函数打上断点后点击右上角的Debug,程序就会在断点处停止运行,并看到我们传入的参数信息。


有的没的

Long time no see

又有快一年没更新了。去年暑假在上海实习,回来之后彻底从e人变成了i人,没事就喜欢宅着。顺利拿了转正offer,人生的下一个阶段就是在上海当牛马了。现在又开始觉得自己没有什么可以失去的了,孩子我无敌了。上个学期一直在摆烂,最近肝完了毕业论文的初稿,趁着还算是有一点点学习的状态学一点以后工作可能用得上的东西。希望这段时间能经常更新吧。

没事听点歌(橘子海 - 夏日漱石)