Skip to content

Build OpenJDK11 for RV32G

zifeihan edited this page Nov 15, 2023 · 1 revision

PLCT目前在进行RV32G的OpenJDK移植工作,由于目前RV32的工具链支持并不完善,暂时还没有RV64上Fedora一样的镜像,使用riscv-yocto等构建的镜像需要手动编译大量的lib,所以我们暂时先采用qemu user mode的方法来进行构建后的调试。

下面我们来试着自己编译一个OpenJDK11.0.9的RV32G zero版本。

0.编译环境

本次实验基于 Ubuntu 20.04 LTS

$ lsb_release -a
LSB Version:    core-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.3 LTS
Release:        20.04
Codename:       focal

提前需要安装的工具链相关的软件如下:

$ sudo apt-get update -qq \
	&& \
	DEBIAN_FRONTEND=noninteractive \
	sudo apt install -y -qq --no-install-recommends \
        autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \
        gawk build-essential bison flex texinfo gperf libtool patchutils bc \
        zlib1g-dev libexpat-dev git \
        libglib2.0-dev libfdt-dev libpixman-1-dev \
        libncurses5-dev libncursesw5-dev ninja-build \
        python3 autopoint pkg-config zip unzip screen \
        make libxext-dev libxrender-dev libxtst-dev \
        libxt-dev libcups2-dev libfreetype6-dev \
        mercurial libasound2-dev cmake libfontconfig1-dev python3-pip \
        gettext

除此之外,额外依赖库还需要:

$ pip3 install docwriter

1.编译交叉工具链 riscv-gnu-toolchain

1.1 下载源码
$ git clone https://github.com/riscv/riscv-gnu-toolchain

进入源码目录并checkout主目录到固定版本:

$ cd riscv-gnu-toolchain
$ git checkout b39e36

移除qemu子目录(后续在官网下载最新的源码)及riscv-newlib子目录,拉取子目录代码并checkout到固定版本:

$ git rm qemu
$ git rm riscv-newlib
$ git submodule update --init --recursive

# riscv-gcc
$ cd riscv-gcc
$ git checkout 5964b5
$ cd -

# riscv-binutils
$ cd riscv-binutils
$ git checkout 116a73
$ cd -

# riscv-gdb
$ cd riscv-gdb
$ git checkout 5da071
$ cd -

# riscv-glibc
$ cd riscv-glibc
$ git checkout 9826b0
$ cd -

# riscv-dejagnu
$ cd riscv-dejagnu
$ git checkout 4ea498
$ cd -
1.2 编译 Linux 的交叉工具链

修改 /path/to/riscv32为自己指定的目录,随后编译构建:

$ ./configure --prefix=/path/to/riscv32 --with-arch=rv32gc --with-abi=ilp32d
$ make linux -j

tips:如果在执行make命令时出现out of memory的报错问题,可以去掉参数-j重新尝试。

之后将toolchain 的安装路径写入.bashrc文件:

$ echo -e '\nexport PATH="/path/to/riscv32/bin:$PATH"' >> ~/.bashrc
$ source ~/.bashrc

(不推荐) 或者也可以直接执行export命令,但是要注意重启或新开终端后需要再次执行:

$ export PATH="/path/to/riscv32/bin:$PATH"

2. 编译 QEMU

2.1 下载源码
$ wget https://download.qemu.org/qemu-5.2.0.tar.xz
$ tar xvJf qemu-5.2.0.tar.xz
2.2 编译和安装
$ cd qemu-5.2.0/
$ ./configure --target-list=riscv32-softmmu,riscv32-linux-user --prefix=/path/to/qemu
$ make -j && make install
2.3 验证安装是否正确
$ /path/to/qemu/bin/qemu-system-riscv32 --version

出现类似如下输出,则安装成功

qemu-riscv32 version 5.2.0
Copyright (c) 2003-2020 Fabrice Bellard and the QEMU Project developers

3. 安装额外的库文件

在编译之前我们还需要安装一些额外的库的riscv版本,列表如下:

  • ALSA

  • CUPS

  • LIBPNG

  • Freetype2

  • Fontconfig

  • zlib

  • libuuid

  • libxml2

  • libpthread-stubs

  • libffi

  • X11

    • xproto
    • xtrans
    • xextproto
    • renderproto
    • xcb-proto
    • fixesproto
    • kbproto
    • recordproto
    • inputproto
    • xorgproto
    • libICE
    • libSM
    • libXau
    • libXcb
    • libX11
    • libXt
    • libXfixes
    • libXrender
    • libXext
    • libXi
    • libXtst
    • libXrandr

方案一 自动构建脚本

编译JDK所需额外库的安装脚本

直接执行上述构建脚本即可

方案二 根据构建指导手动构建

可以参考毕昇JDK官方的构建指导和azul-research的构建指导,还可以参考azul-research的Dockerfile 进行构建,在编译的时候请将其中riscv64-unknown-linux-gnu更改为riscv32-unknown-linux-gnu。

上述手动构建脚本中的tips:

  • cups的相关脚本需要有所改动:

    $ cd cups && ./configure --host=riscv32-unknown-linux-gnu --disable-ssl --disable-gssapi --disable-avahi --disable-libusb --disable-dbus --disable-systemd
    $ make CFLAGS="-Wno-error=sign-conversion -Wno-error=format-truncation" CXXFLAGS="-Wno-error=sign-conversion -Wno-error=format-truncation" && make install DSTROOT=$sysroot
    
  • 脚本中给出freetype仓库有些问题,需要去官网下载:

    $ wget https://download.savannah.gnu.org/releases/freetype/freetype-2.10.4.tar.gz
    $ tar -xzvf freetype-2.10.4.tar.gz
    
  • json-c需要切换到json-c-0.13分支

  • util-linux只需要libuuid.so和相关头文件即可,其他模块报错可以忽略

  • 使用xorg安装libxshmfence的时候可能会出现报错:

    这时候需要进入/path/to/xorg/lib/libxshmfence并手动修改打上patch:

    diff --git a/src/xshmfence_futex.h b/src/xshmfence_futex.h
    index 673ac0e..de6c3d6 100644
    --- a/src/xshmfence_futex.h
    +++ b/src/xshmfence_futex.h
    @@ -53,6 +53,14 @@ static inline int futex_wait(int32_t *addr, int32_t value) {
     #include <sys/time.h>
     #include <sys/syscall.h>
     
    +#ifndef __NR_futex
    +#define __NR_futex 240
    +#endif
    +
    +#ifndef SYS_futex
    +#define SYS_futex __NR_futex
    +#endif
    +
     static inline long sys_futex(void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3)
     {
            return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);

4. 获取JDK的源码并切换到开发分支

下载源码:

$ git clone https://github.com/openjdk-riscv/jdk11u.git

进入目录并切换分支:

$ cd jdk11u
$ git checkout 96943a

5. 下载用于交叉编译的bootJDK

因为编译JDK需要自举,即假设要编译大版本号为N的JDK,我们还要另外准备一个大版本号至少为N-1的、已经编译好的JDK,这是因为OpenJDK由多个部分(HotSpot、JDK类库、JAXWS、JAXP等等)构成,其中一部分(HotSpot)代码使用C、C++编写,而更多的代码则是使用Java语言来实现,因此编译这些Java代码就需要用到另一个编译期可用的JDK,官方称这个JDK为“Bootstrap JDK”。

下载openJDK10:

$ wget https://download.java.net/openjdk/jdk10/ri/jdk-10_linux-x64_bin_ri.tar.gz

解压:

$ tar -xzvf jdk-10_linux-x64_bin_ri.tar.gz

6. 开始构建JDK

Zero 版本构建运行以下命令:

$ bash configure \
--openjdk-target=riscv32-unknown-linux-gnu \
--disable-warnings-as-errors \
--with-sysroot=/path/to/riscv32/sysroot \
--with-boot-jdk=/path/to/jdk-10 \
--with-native-debug-symbols=none \
--with-jvm-variants=zero \
--with-jvm-interpreter=cpp

make JOBS=$(nproc) && make install

模版解释器构建运行以下命令:

bash configure \
--openjdk-target=riscv32-unknown-linux-gnu \
--disable-warnings-as-errors \
--with-sysroot=/home/zifeihan/riscv/riscv32/sysroot \
--with-boot-jdk=/home/zifeihan/jre/jre11 \
--with-jvm-variants=core \
--with-debug-level=slowdebug \
--with-native-debug-symbols=internal

make JOBS=$(nproc) && make install

C2 版本构建命令:

bash configure \
--openjdk-target=riscv32-unknown-linux-gnu \
--disable-warnings-as-errors \
--with-sysroot=/home/zifeihan/riscv/riscv32/sysroot \
--with-boot-jdk=/home/zifeihan/jre/jre11 \
--with-jvm-variants=custom \
--with-jvm-features=cds,cmsgc,compiler2,epsilongc,g1gc,jfr,jni-check,jvmti,management,nmt,parallelgc,serialgc,services,vm-structs \
--with-debug-level=slowdebug \
--with-native-debug-symbols=internal

make JOBS=$(nproc) && make install

7. 使用QEMU进行测试

由于我们之前工具链构建的linux headers为v5.4版本,而且QEMU在user mode下会使用主机系统的kernel来进行模拟系统调用等操作,所以如果主机kernel版本为v5.4以下,还需要升级kernel版本。

ubuntu查看kernel版本:

$ uname -a
Linux linux-ubuntu-1804-01 5.4.0-050400-generic #201911242031 SMP Mon Nov 25 01:35:10 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
7.1 升级x86主机的kernel版本:

获取kernel的安装包:

$ wget https://kernel.ubuntu.com/~kernel-ppa/mainline/v5.4/linux-headers-5.4.0-050400_5.4.0-050400.201911242031_all.deb
$ wget https://kernel.ubuntu.com/~kernel-ppa/mainline/v5.4/linux-headers-5.4.0-050400-generic_5.4.0-050400.201911242031_amd64.deb
$ wget https://kernel.ubuntu.com/~kernel-ppa/mainline/v5.4/linux-image-unsigned-5.4.0-050400-generic_5.4.0-050400.201911242031_amd64.deb
$ wget https://kernel.ubuntu.com/~kernel-ppa/mainline/v5.4/linux-modules-5.4.0-050400-generic_5.4.0-050400.201911242031_amd64.deb

安装并重启:

$ sudo dpkg -i *.deb
$ sudo reboot
7.2 测试JDK

之后使用QEMU的用户模式来进行测试:

$ /path/to/qemu/bin/qemu-riscv32 -L /path/to/riscv32/sysroot ./java -version

测试结果:

openjdk version "11.0.9-internal" 2020-10-20
OpenJDK Runtime Environment (build 11.0.9-internal+0-adhoc.dingli.jdk11u)
OpenJDK Zero VM (build 11.0.9-internal+0-adhoc.dingli.jdk11u, interpreted mode)