diff --git a/articles/20220721-riscv-gcc.md b/articles/20220721-riscv-gcc.md new file mode 100644 index 0000000000000000000000000000000000000000..04bed5ca85d37df0ee37e9cf68dc38bf80e0b6f6 --- /dev/null +++ b/articles/20220721-riscv-gcc.md @@ -0,0 +1,190 @@ +![](./diagrams/linker-loader.png) + +文章标题:**制作交叉工具链 riscv-gnu-toolchain** + +- 作者:汪辰 +- 联系方式: / + +文章大纲 + + +- [1. 预置条件](#1-预置条件) +- [2. 下载源码](#2-下载源码) +- [3. 编译 RISC-V 的交叉工具链](#3-编译-risc-v-的交叉工具链) +- [4. 编译其他版本的 RISC-V GCC](#4-编译其他版本的-risc-v-gcc) +- [5. 附录 - 采用国内 mirror 下载相关仓库](#5-附录---采用国内-mirror-下载相关仓库) + + + +# 1. 预置条件 + +参考 [RISC-V 工具链官方 README 说明][1],因为这里演示在 Ubuntu 上制作,所以采用 apt 安装如下软件: + +``` +$ sudo apt-get install autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev +``` + +# 2. 下载源码 + +注意下面的描述是参考的 [RISC-V 工具链官方 README 说明][1],如果你本地因为不可描述的原因无法顺利地下载相关仓库,可以参考本文附录 - 采用国内 mirror 下载相关仓库。 + +假设我们的工作目录所在的路径为 `$WS`。执行如下命令下载 RISC-V 基金会维护的构建脚本仓库,下载完成后进入 clone 的仓库目录 `riscv-gnu-toolchain`。 + +``` +$ cd $WS +$ git clone git@github.com:riscv-collab/riscv-gnu-toolchain.git +$ cd riscv-gnu-toolchain +``` + +注意上面 clone 的仓库,我们称其为构建脚本仓库,其并不包含 gcc 等工具源码,这些源码以 git 的 submodule 的形式作为子仓库的内容发布,所以需要继续更新子仓库。注意这里我们首先排除了 qemu 这个子仓库,一来因为 qemu 完整下载太大;二来 qemu 对 toolchain 的编译本身来说其实并不需要。 + +``` +$ git rm qemu +``` + +然后就可以下载子仓库了,运行如下命令,注意加上 `--progress`,方便查看进度,因为下载内容较多,好几个 G,需要等待一会时间。 + +``` +$ git submodule update --init --recursive --progress +``` + +# 3. 编译 RISC-V 的交叉工具链 + +编译时为尽量不污染 `riscv-gnu-toolchain` 目录,我们退出 `riscv-gnu-toolchain`,在 `riscv-gnu-toolchain` 外面新建一个同级的 build 目录,同时将编译完的工具链安装到同级的 install 目录下。我们这里编一个 Linux cross-compiler,如果要编译其他的 cross-compiler,请自行阅读 [RISC-V 工具链官方 README 说明][1]。 + +``` +$ cd $WS +$ mkdir build && cd build +$ ../riscv-gnu-toolchain/configure --prefix=$WS/install +$ make linux -j $(nproc) +``` + +测试 toolchain 是否安装成功。后面也可以导出 toolchain 的安装路径,譬如将 `export PATH="$PATH:$WS/install/bin"` 写入 `.bashrc` 文件,以后就可以不用每次输入完整路径了。 + +``` +$ $WS/install/bin/riscv64-unknown-linux-gnu-gcc -v +``` + +出现类似如下输出表示工具链编译安装正常。 +``` +Using built-in specs. +COLLECT_GCC=../install/bin/riscv64-unknown-linux-gnu-gcc +COLLECT_LTO_WRAPPER=/home/u/ws/test-gcc/install/libexec/gcc/riscv64-unknown-linux-gnu/11.1.0/lto-wrapper +Target: riscv64-unknown-linux-gnu +Configured with: /home/u/ws/test-gcc/build/../riscv-gnu-toolchain/riscv-gcc/configure --target=riscv64-unknown-linux-gnu --prefix=/home/u/ws/test-gcc/install --with-sysroot=/home/u/ws/test-gcc/install/sysroot --with-pkgversion=g5964b5cd727 --with-system-zlib --enable-shared --enable-tls --enable-languages=c,c++,fortran --disable-libmudflap --disable-libssp --disable-libquadmath --disable-libsanitizer --disable-nls --disable-bootstrap --src=../../riscv-gnu-toolchain/riscv-gcc --disable-multilib --with-abi=lp64d --with-arch=rv64imafdc --with-tune=rocket --with-isa-spec=2.2 'CFLAGS_FOR_TARGET=-O2 -mcmodel=medlow' 'CXXFLAGS_FOR_TARGET=-O2 -mcmodel=medlow' +Thread model: posix +Supported LTO compression algorithms: zlib +gcc version 11.1.0 (g5964b5cd727) +``` + +编译一个简单的 hello 程序,这里采用静态链接,方便测试。 + +``` +$ cat hello.c +#include + +int main() +{ + printf("hello!\n"); + return 0; +} +$ $WS/install/bin/riscv64-unknown-linux-gnu-gcc -static hello.c -o a.out +``` + +看一下生成的 `a.out`,出现 "RISC-V" 字样,说明交叉编译成功。 +``` +$ file a.out +a.out: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), statically linked, for GNU/Linux 4.15.0, with debug_info, not stripped +``` + +用 qemu 简单测试一下,打印 "hello!" 说明成功。这个 qemu 可以自己安装或者自己构建,这里就不赘述了。 + +``` +$ qemu-riscv64 a.out +hello! +``` + +# 4. 编译其他版本的 RISC-V GCC + +目前上面构建出来的 GCC 版本,默认是 11.1.0,如果我们想构建其他版本的 GCC 可以这么做: + +进入 `riscv-gnu-toolchain` 下的 `riscv-gcc` 子目录,把 gcc 官方的 git 源加上, 然后同步一下。如果觉得 gnu 的官方源比较慢,也可以尝试换成国内的 mirror,譬如: `https://mirrors.tuna.tsinghua.edu.cn/git/gcc.git`。 + +``` +$ cd $WS/riscv-gnu-toolchain/riscv-gcc +$ git remote add upstream git://gcc.gnu.org/git/gcc.git +$ git fetch --all +``` + +然后就可以切换到你想要构建的版本上去了,正式发布版本都有对应的 tag,譬如写作本文时最新的 12.1.0,可以如下切换 + +``` +$ git checkout releases/gcc-12.1.0 +``` + +其他的子仓库一般不用动,gcc 可能依赖比较紧密的 c 库或者 binutils 最好取比较新的。具体构建 gcc 的依赖参考 GCC 官方文档 ["Prerequisites for GCC"][2]。 + +后面的构建操作都是类似的,就不赘述了。 + +# 5. 附录 - 采用国内 mirror 下载相关仓库 + +``` +$ git clone https://gitee.com/mirrors/riscv-gnu-toolchain +$ cd riscv-gnu-toolchain +``` + +同样地删除一些子仓库,一般情况下我们制作 gcc 工具链只需要保留几个必要的仓库,所以这里我除了删除了 qemu 还删除了 musl、pk 和 spike + +``` +$ git rm qemu +$ git rm musl +$ git rm spike +$ git rm pk +``` + +然后还要修改一下 riscv-gnu-toolchain 目录下的 `.gitmodules` 这个文件,因为默认下载下来的仓库里的 submodule 所指向的 URL 下载速度会很慢,所以我们把它们替换为国内的一些 mirror URL,我这里简单显示一下 diff +``` +@@ -1,23 +1,23 @@ + [submodule "riscv-binutils"] + path = riscv-binutils +- url = https://github.com/riscv-collab/riscv-binutils-gdb.git +- branch = riscv-binutils-2.38 ++ url = https://gitee.com/mirrors/riscv-binutils-gdb.git ++ branch = riscv-binutils-2.36.1 + [submodule "riscv-gcc"] + path = riscv-gcc +- url = https://github.com/riscv-collab/riscv-gcc.git ++ url = https://gitee.com/mirrors/riscv-gcc.git + branch = riscv-gcc-10.2.0 +-[submodule "glibc"] +- path = glibc +- url = git://sourceware.org/git/glibc.git ++[submodule "riscv-glibc"] ++ path = riscv-glibc ++ url = https://mirrors.tuna.tsinghua.edu.cn/git/glibc.git + [submodule "riscv-dejagnu"] + path = riscv-dejagnu +- url = https://github.com/riscv-collab/riscv-dejagnu.git ++ url = https://gitee.com/mirrors/riscv-dejagnu.git + branch = riscv-dejagnu-1.6 +-[submodule "newlib"] +- path = newlib +- url = git://sourceware.org/git/newlib-cygwin.git ++[submodule "riscv-newlib"] ++ path = riscv-newlib ++ url = https://gitee.com/mirrors/riscv-newlib.git + branch = master + [submodule "riscv-gdb"] + path = riscv-gdb +- url = https://github.com/riscv-collab/riscv-binutils-gdb.git ++ url = https://gitee.com/mirrors/riscv-binutils-gdb.git + branch = fsf-gdb-10.1-with-sim +``` + +然后就可以同步了,其他操作同上。 + + + +[1]: https://github.com/riscv-collab/riscv-gnu-toolchain#readme +[2]: https://gcc.gnu.org/install/prerequisites.html +