手动编译一个jdk8

学习JVM自然是要先自己构建一个jdk,做到管中窥豹,从而不会无从下手。

前置准备

系统准备

本次构建jdk-8使用的是 ubuntu-22.04.3-live-server-amd64,内核版本为:GNU/Linux 5.15.0-84-generic x86_64

硬件准备

基于VMware 的虚拟机,分配host AMD Ryzen 7 3800X 8-Core Processor 8核心 8GB内存

调试工具

Jetbrians Clion 使用Remote Development链接Ubuntu虚拟机

环境准备

根据 https://hg.openjdk.org/jdk8/jdk8/raw-file/tip/README-builds.html 得知道:编译环境推荐为:

  1. gcc: 4.8
  2. g++: 4.8
  3. make: 3.81
  4. bootstrap JDK: OpenJDK8

OpenJDK8源码

https://codeload.github.com/openjdk/jdk/zip/refs/tags/jdk8-b120

基本依赖

1
sudo apt install build-essential libxrender-dev xorg-dev libasound2-dev libcups2-dev gawk zip libxtst-dev libxi-dev libxt-dev gobjc

开始

由于在ubuntu22.04.3 中没有gcc 4.8 的源, 需要导入旧版的源

1
sudo vim /etc/apt/sources.list

在最下方添加旧版源地址并保存

1
2
deb http://archive.ubuntu.com/ubuntu xenial main
deb http://archive.ubuntu.com/ubuntu xenial universe

更新apt源信息,并安装gcc和g++

1
2
sudo apt update
sudo apt install gcc-4.8 g++-4.8

在更新过程中 我遇到了

1
2
3
4
5
6
7
Err:5 http://archive.ubuntu.com/ubuntu xenial InRelease
The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 40976EAF437D05B5 NO_PUBKEY 3B4FE6ACC0B21F32
Reading package lists... Done
W: GPG error: http://archive.ubuntu.com/ubuntu xenial InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 40976EAF437D05B5 NO_PUBKEY 3B4FE6ACC0B21F32
E: The repository 'http://archive.ubuntu.com/ubuntu xenial InRelease' is not signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
N: See apt-secure(8) manpage for repository creation and user configuration details.

这是因为系统无法识别某些GPG公钥(PUBKEY)导致的。需要做的是在系统中获取这些缺失的公钥。可以使用以下命令来获取这些公钥:

1
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5 3B4FE6ACC0B21F32

设置安装的gcc g++4.8 为首选

1
2
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 100

查看版本:

1
2
3
4
5
6
7
8
9
10
11
root@aki:~# g++ --version
g++ (Ubuntu 4.8.5-4ubuntu2) 4.8.5
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

root@aki:~# gcc --version
gcc (Ubuntu 4.8.5-4ubuntu2) 4.8.5
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

安装make 3.81版本

从官方下载:

1
wget https://ftp.gnu.org/gnu/make/make-3.81.tar.gz

下载完成之后:

1
2
tar -zxvf make-3.81.tar.gz 
cd make-3.81/

接着修改代码,打开glob/glob.c文件:

1
2
3
4
5
6
7
8
9
...
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#define __alloca alloca <- 添加这一句
#define __stat stat <- 添加这一句
/* Enable GNU extensions
...

防止之后出现报错:

1
2
3
4
5
6
/usr/bin/ld: /root/make-3.81/glob/glob.c:955: undefined reference to `__stat'
/usr/bin/ld: /root/make-3.81/glob/glob.c:809: undefined reference to `__stat'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:411: make] Error 1
make[1]: Leaving directory '/root/make-3.81'
make: *** [Makefile:603: install-recursive] Error 1

然后进行安装 make-3.81

1
2
bash configure
sudo make install

删除最新的make

1
apt remove make -y #删除之前apt装的make,其位置在/usr/bin/make

删除链接

1
rm /usr/bin/make

测试:

1
2
3
4
5
6
7
8
make -version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for x86_64-unknown-linux-gnu

安装bootstrap JDK: OpenJDK8

由于JDK中某些代码是Java编写的,所以我们还需要安装一个启动JDK,启动JDK可以是当前版本或低一版本,比如我们要编译JDK8的源码,那么就可以使用JDK7、JDK8作为启动JDK,对源码中的一些java文件进行编译。这里我们选择安装 openjdk8作为启动JDK:

1
sudo apt install openjdk-8-jdk

编译openjdk8

解压

1
unzip jdk8-b120

进入目录

1
cd jdk-jdk8-b120/

修改文件以支持当前linux内核:
首先是hotspot/make/linux/Makefile文件

1
2
原有的 SUPPORTED_OS_VERSION = 2.4% 2.5% 2.6% 3%
修改为 SUPPORTED_OS_VERSION = 2.4% 2.5% 2.6% 3% 4% 5%

接着是hotspot/make/linux/makefiles/gcc.make文件:

1
2
原有的 WARNINGS_ARE_ERRORS = -Werror
修改为 #WARNINGS_ARE_ERRORS = -Werror

接着是nashorn/make/BuildNashorn.gmk文件:

1
2
3
4
5
6
$(CP) -R -p $(NASHORN_OUTPUTDIR)/nashorn_classes/* $(@D)/
$(FIXPATH) $(JAVA) \
原有的 -cp "$(NASHORN_OUTPUTDIR)/nasgen_classes$(PATH_SEP)$(NASHORN_OUTPUTDIR)/nashorn_classes" \
修改为 -Xbootclasspath/p:"$(NASHORN_OUTPUTDIR)/nasgen_classes$(PATH_SEP)$(NASHORN_OUTPUTDIR)/nashorn_classes" \

jdk.nashorn.internal.tools.nasgen.Main $(@D) jdk.nashorn.internal.objects $(@D)

执行编译前的configure

1
bash configure --with-debug-level=slowdebug --enable-debug-symbols ZIP_DEBUGINFO_FIELS=0

含义解读:

  1. –with-debug-level=slowdebug 生成一个带有调试信息的OpenJDK8版本,slowdebug表示调试级别是最低的,也就是说生成的二进制文件会比较小,但是调试信息会比较少
  2. –enable-debug-symbols 在生成的二进制文件中包含调试符号,这样可以在使用调试工具时,看到源代码中的变量名和函数名等
  3. ZIP_DEBUGINFO_FIELS=0 不要将调试信息打包成zip文件,而是直接放在二进制文件中

编译

1
make all

遇到问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/home/jdk8/jdk/src/solaris/native/java/net/PlainDatagramSocketImpl.c:38:24: fatal error: sys/sysctl.h: 没有那个文件或目录
#include <sys/sysctl.h>
^
compilation terminated.
/home/jdk8/jdk/src/solaris/native/java/net/PlainSocketImpl.c:46:24: fatal error: sys/sysctl.h: 没有那个文件或目录
#include <sys/sysctl.h>
^
compilation terminated.
In file included from /home/jdk8/jdk/src/share/native/java/net/net_util.c:29:0:
/home/jdk8/jdk/src/share/native/java/net/net_util.c: In function ‘NET_SockaddrToInetAddress’:
/home/jdk8/jdk/src/share/native/java/net/net_util.h:46:41: warning: comparison between pointer and integer [enabled by default]
#define CHECK_NULL_RETURN(x, y) if ((x) == NULL) return y;
^
/home/jdk8/jdk/src/share/native/java/net/net_util.c:268:13: note: in expansion of macro ‘CHECK_NULL_RETURN’
CHECK_NULL_RETURN(ret, NULL);
^
gmake[2]: *** [lib/NetworkingLibraries.gmk:57:/home/jdk8/build/linux-x86_64-normal-server-release/jdk/objs/libnet/PlainDatagramSocketImpl.o] 错误 1
gmake[2]: *** 正在等待未完成的任务....
gmake[2]: *** [lib/NetworkingLibraries.gmk:57:/home/jdk8/build/linux-x86_64-normal-server-release/jdk/objs/libnet/PlainSocketImpl.o] 错误 1
gmake[1]: *** [BuildJdk.gmk:70:libs-only] 错误 2
make: *** [/home/jdk8//make/Main.gmk:116:jdk-only] 错误 2

这个报错的主要原因是随着 glibc 2.32 的发布,Linux 系统删除了sys/sysctl.h。

因此解决方式只需要把openjdk8目录下的:

jdk/src/solaris/native/java/net/PlainDatagramSocketImpl.c 中的38行
jdk/src/solaris/native/java/net/PlainSocketImpl.c中的46行注释掉就行

1
2
#include <sys/sysctl.h> 改成
// #include <sys/sysctl.h>

参考来自:https://blog.csdn.net/weixin_50955418/article/details/126716611

最后成功会显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
----- Build times -------
Start 2023-09-28 17:07:31
End 2023-09-28 17:09:01
00:00:00 corba
00:00:06 demos
00:00:56 docs
00:00:00 hotspot
00:00:08 images
00:00:00 jaxp
00:00:00 jaxws
00:00:14 jdk
00:00:00 langtools
00:00:05 nashorn
00:01:30 TOTAL
-------------------------
Finished building OpenJDK for target 'all'

测试

在jdk-jdk8-b120/ 目录中输入:

1
build/linux-x86_64-normal-server-slowdebug/jdk/bin/java -version

得到结果

1
2
3
openjdk version "1.8.0-internal-debug"
OpenJDK Runtime Environment (build 1.8.0-internal-debug-root_2023_09_28_16_55-b00)
OpenJDK 64-Bit Server VM (build 25.0-b62-debug, mixed mode)