riscv-collab/riscv-gnu-toolchain
GitHub: riscv-collab/riscv-gnu-toolchain
RISC-V 官方维护的 GNU 工具链仓库,支持构建 GCC 和 LLVM 编译器,涵盖裸机与 Linux 交叉编译环境。
Stars: 4420 | Forks: 1371
# RISC-V GNU Compiler Toolchain
这是 RISC-V C 和 C++ 交叉编译器。它支持两种构建模式:
通用的 ELF/Newlib 工具链和更复杂的 Linux-ELF/glibc
工具链。
### 获取源码
此仓库使用子模块,但子模块会根据需要自动获取,
因此不需要 `--recursive` 或 `git submodule update --init --recursive`。
```
$ git clone https://github.com/riscv/riscv-gnu-toolchain
```
**警告:git clone 大约需要 6.65 GB 的磁盘空间和下载流量**
### 前置条件
构建工具链需要几个标准软件包。
在 Ubuntu 上,执行以下命令即可:
```
$ sudo apt-get install autoconf automake autotools-dev curl python3 python3-pip python3-tomli libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev ninja-build git cmake libglib2.0-dev libslirp-dev libncurses-dev
```
在 Fedora/CentOS/RHEL OS 上,执行以下命令即可:
```
$ sudo yum install autoconf automake python3 libmpc-devel mpfr-devel gmp-devel gawk bison flex texinfo patchutils gcc gcc-c++ zlib-devel expat-devel libslirp-devel ncurses-devel
```
在 Arch Linux 上,执行以下命令即可:
```
$ sudo pacman -Syu curl python3 libmpc mpfr gmp base-devel texinfo gperf patchutils bc zlib expat libslirp ncurses
```
Arch 用户也可以在 AUR 上找到:[https://aur.archlinux.org/packages/riscv-gnu-toolchain-bin](https://aur.archlinux.org/packages/riscv-gnu-toolchain-bin)
在 macOS 上,你可以使用 [Homebrew](http://brew.sh) 来安装依赖:
```
$ brew install python3 gawk gnu-sed make gmp mpfr libmpc isl zlib expat texinfo flock libslirp ncurses ninja bison m4 wget
```
在执行本 README 中的说明时,请使用 `gmake` 代替 `make`,以使用新安装的 make 版本。
要在 macOS 上构建 glibc (Linux),你需要在不区分大小写的文件系统中进行构建。最简单的方法是创建并挂载一个具有区分大小写格式的新磁盘映像。确保挂载点不包含空格。这在 macOS 上构建 newlib 或 gcc 本身时不是必需的。
此过程将首先下载约 200 MiB 的上游源码,然后
打补丁、构建并安装工具链。如果
$(DISTDIR) 中存在上游源码的本地缓存,则会被使用;默认位置
是 /var/cache/distfiles。你的计算机将需要约 8 GiB 的磁盘空间来
完成此过程。
### 安装
要构建 Newlib 交叉编译器,请选择一个安装路径(可写入)。
如果你选择,例如 `/opt/riscv`,则将 `/opt/riscv/bin` 添加到你的 `PATH` 中。
然后,只需运行以下命令:
```
./configure --prefix=/opt/riscv
make
```
你现在应该能够使用 riscv64-unknown-elf-gcc 及其相关工具了。
注意:如果你计划使用替换部分 newlib 的外部库(例如 `libgloss-htif`),请[阅读 FAQ](#ensuring-code-model-consistency)。
### 安装
要构建 Linux 交叉编译器,请选择一个安装路径(可写入)。
如果你选择,例如 `/opt/riscv`,则将 `/opt/riscv/bin` 添加到你的 `PATH` 中。
然后,只需运行以下命令:
```
./configure --prefix=/opt/riscv
make linux
```
构建默认针对带有 glibc 的 RV64GC (64位),即使在 32 位
构建环境中也是如此。要构建 32 位 RV32GC 工具链,请使用:
```
./configure --prefix=/opt/riscv --with-arch=rv32gc --with-abi=ilp32d
make linux
```
如果你更喜欢 musl libc 而不是 glibc,配置方式与上面相同,但选择
`make musl` 而不是 `make linux`。
支持的架构是 rv32i 或 rv64i 加上标准扩展:原子
、乘法和除法、单精度浮点、双精度浮点,或 MAFD 的通用。
支持的 ABI 包括 ilp32 (32位软浮点)、ilp32d (32位硬浮点)、
ilp32f (32位,单精度在寄存器中,双精度在内存中,仅限小众
用途),lp64 lp64f lp64d (相同,但具有 64 位 long 和指针)。
### 安装 (Newlib/Linux multilib)
要构建支持 32 位和 64 位的交叉编译器,
运行以下命令:
```
./configure --prefix=/opt/riscv --enable-multilib
```
然后分别运行 `make`、`make linux` 或 `make musl` 来构建 Newlib、基于
Linux glibc 或基于 Linux musl libc 的交叉编译器。
multilib 编译器将具有前缀 riscv64-unknown-elf- 或
riscv64-unknown-linux-gnu-,但将能够针对 32 位和 64 位
系统。
它将支持最常见的 `-march`/`-mabi` 选项,可以通过
在任一交叉编译器上使用 `--print-multi-lib` 标志来查看。
Linux 工具链有一个额外的选项 `--enable-default-pie` 来控制
GCC 的默认 PIE 启用,默认情况下是禁用的。
要自定义启用的语言,使用选项 `--with-languages=`。例如,
如果你想启用 `c,c++,fortran`,请使用 `./configure --with-languages=c,c++,fortran`。
此选项仅对 GNU 工具链有效。
工具链有一个选项 `--enable-strip` 来控制主机二进制文件的剥离,
默认情况下剥离是禁用的。
### 安装
首先,确保你已在区分大小写的卷中克隆了工具链仓库。
现在 source `macos.zsh` 以设置 PATH 变量,以便构建脚本可以使用 homebrew 中的工具,这些是构建 GNU 工具链所需的。
然后,使用所需的标志运行 configure - 例如:
```
./configure --prefix=/Volumes/case-sensitive/opt/riscv --with-arch=rv64gc_zifencei --with-abi=lp64d --enable-linux --disable-gdb
```
然后,提高打开文件的限制。运行:`ulimit -n 65536`
MacOS 上的构建高度特定于操作系统版本和已安装的开发者工具版本。我们建议先运行 `make check-binutils`,这将有助于我们在不必开始完整构建的情况下发现一些我们见过的更频繁的构建错误。
如果 `make check-binutils` 报错,请查看[以下文档](./macos-build.md),了解在 MacOS 上构建时的常见错误列表及其解决方案。
当 `make check-binutils` 成功完成后,你可以正常运行 `make` 或 `make linux` 进行构建。
### Big Endian 配置
要构建支持大端目标的交叉编译器,运行以下命令:
```
./configure --prefix=/opt/riscv --with-endian=big
make (target)
```
### 故障排除
如果安装到空目录中,构建效果最好。如果你构建了
一个硬浮点工具链,然后尝试使用
相同的 --prefix 目录构建软浮点工具链,那么构建脚本可能会混淆
并退出,并出现链接器错误,抱怨硬浮点代码无法
与软浮点代码链接。先删除现有的工具链,或
为第二次构建使用不同的前缀,可以避免此问题。在
相同的前缀下构建一个 newlib 和一个 linux 工具链是可以的。
但你应该避免在
相同的前缀下构建两个 newlib 或两个 linux 工具链。
如果在 MacOS 系统上构建 linux 工具链,或者在使用 Linux 子系统或 cygwin 的 Windows 系统上
,你必须确保文件系统
是区分大小写的。在不区分大小写的文件系统上构建将在
构建 glibc 时失败,因为 \*.os 和 \*.oS 文件会在构建过程中相互覆盖,最终导致令人困惑的链接错误。
CentOS (和 RHEL) 提供的 GNU 工具版本可能太旧,无法构建
RISC-V 工具链。有一个提供的替代工具集,包括
当前版本的 GNU 工具。这是作为软件集合服务一部分提供的 devtoolset。有关更多信息,请参阅
[devtoolset-7](https://www.softwarecollections.org/en/scls/rhscl/devtoolset-7/)
URL。有各种版本的 devtoolset 可用,所以你
也可以尝试它的其他版本,但我们至少有一份报告称
devtoolset-7 有效。
### 高级选项
有许多额外的选项可以传递给
configure。有关更多详细信息,请参阅 './configure --help'。
你也可以定义额外的标志传递给特定项目:```BINUTILS_NATIVE_FLAGS_EXTRA,
BINUTILS_TARGET_FLAGS_EXTRA, GCC_EXTRA_CONFIGURE_FLAGS, GDB_NATIVE_FLAGS_EXTRA,
GDB_TARGET_FLAGS_EXTRA, GLIBC_TARGET_FLAGS_EXTRA, NEWLIB_TARGET_FLAGS_EXTRA,
LLVM_EXTRA_CONFIGURE_FLAGS, QEMU_EXTRA_CONFIGURE_FLAGS```.
示例:```GCC_EXTRA_CONFIGURE_FLAGS=--with-gmp=/opt/gmp make linux```
#### 设置默认 ISA 规范版本
`--with-isa-spec=` 可以指定 RISC-V 非特权
(原用户级)ISA 规范的默认版本。
可能的选项有:`2.2`、`20190608` 和 `20191213`。
默认版本是 `20191213`。
关于此选项的更多细节,你可以参考这篇文章 [RISC-V GNU toolchain bumping default ISA spec to 20191213](https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/aE1ZeHHCYf4)。
#### 使用自定义的 multi-lib 配置构建。
`--with-multilib-generator=` 可以指定要构建哪些 multilib。参数
是一个以分号分隔的值列表,可能由单个值组成。
目前仅支持 riscv*-*-elf*。接受的值和含义
如下所示。
每个配置由四个部分组成:架构字符串、ABI、
带有架构字符串的重用规则和带有子扩展的重用规则。
重用部分支持扩展操作符 (*) 以简化不同子扩展的组合,示例 4 演示了它的用法和工作原理。
示例 1:为带有 ilp32 的 rv32i 添加 multi-lib 支持。
```
./configure --with-multilib-generator="rv32i-ilp32--"
```
示例 2:为带有 ilp32 的 rv32i 和带有 ilp32 的 rv32imafd 添加 multi-lib 支持。
```
./configure --with-multilib-generator="rv32i-ilp32--;rv32imafd-ilp32--"
```
示例 3:为带有 ilp32 的 rv32i 添加 multi-lib 支持;带有 ilp32 的 rv32im 和
带有 ilp32 的 rv32ic 将重用此 multi-lib 集。
```
./configure --with-multilib-generator="rv32i-ilp32-rv32im-c"
```
示例 4:为带有 lp64 的 rv64ima 添加 multi-lib 支持;带有 lp64 的 rv64imaf,
带有 lp64 的 rv64imac 和带有 lp64 的 rv64imafc 将重用此 multi-lib 集。
```
./configure --with-multilib-generator="rv64ima-lp64--f*c"
```
#### 启用 QEMU 系统目标
`--enable-qemu-system` 配置标志允许你包含 QEMU 系统模拟目标,除了默认的用户模式模拟。
- **启用的目标**:
- `riscv64-linux-user`
- `riscv32-linux-user`
- `riscv64-softmmu`
- `riscv32-softmmu`
- **默认目标**(不带此标志):
- `riscv64-linux-user`
- `riscv32-linux-user`
如果你需要完整的 RISC-V 系统模拟,请使用此选项。配置示例:
```
./configure --enable-qemu-system --prefix=/opt/riscv
make build-sim SIM=qemu
```
此标志对于测试和模拟完整 RISC-V 系统(而不仅仅是用户空间应用程序)的开发人员特别有用。
### 测试套件
Dejagnu 测试套件已移植到 RISC-V。这可以通过
模拟器为 elf 和 linux 工具链运行。可以通过 Makefile 中的 SIM 变量选择模拟器,例如 SIM=qemu、SIM=gdb 或 SIM=spike
(实验性)。此外,也可以通过
配置时选项 `--with-sim=` 选择模拟器。但是,测试套件白名单
仅为 qemu 维护。其他模拟器可能会出现额外的失败。
#### 额外前置条件
设置测试环境的辅助脚本需要
[pyelftools](https://github.com/eliben/pyelftools)。
在较新版本的 Ubuntu 上,执行以下命令
即可:
```
$ sudo apt-get install python3-pyelftools
```
在较新版本的 Fedora 和 CentOS/RHEL OS (9 或更高版本) 上,执行
以下命令即可:
```
$ sudo yum install python3-pyelftools
```
在 Arch Linux 上,执行以下命令即可:
```
$ sudo pacman -Syyu python-pyelftools python-sphinx python-sphinx_rtd_theme ninja
```
如果你的发行版/操作系统没有 pyelftools 软件包,你可以使用
PIP 安装它。
```
# 假设已安装 PIP
$ pip3 install --user pyelftools
```
#### 测试 GCC
要测试 GCC,运行以下命令:
```
./configure --prefix=$RISCV --disable-linux --with-arch=rv64ima # or --with-arch=rv32ima
make newlib
make report-newlib SIM=gdb # Run with gdb simulator
./configure --prefix=$RISCV
make linux
make report-linux SIM=qemu # Run with qemu
./configure --prefix=$RISCV --with-sim=spike
make linux
make report # Run with spike
```
注意:
- spike 仅支持 rv64* bare-metal/elf 工具链。
- gdb 模拟器仅支持 bare-metal/elf 工具链。
#### 在 GCC 回归测试套件中选择要运行的测试
默认情况下,GCC 将执行其回归测试套件中的所有测试。
虽然并行运行它们(例如 `make -j$(nproc) report`)将
显著加快多处理器系统上的执行时间,
但执行所有测试所需的时间通常对于
典型的开发周期来说太长了。因此 GCC 允许使用环境变量 `RUNTESTFLAGS` 选择
正在执行的测试。
要将测试运行限制为仅 RISC-V 特定测试,
可以使用以下命令:
```
RUNTESTFLAGS="riscv.exp" make report
```
要将测试运行限制为仅匹配
模式 "zb*.c" 和 "sm*.c" 的 RISC-V 特定测试,可以使用以下命令:
```
RUNTESTFLAGS="riscv.exp=zb*.c\ sm*.c" make report
```
#### 测试 Linux 工具链的 GCC、Binutils 和 glibc
运行工具链测试的默认 Makefile 目标是 `report`。
这将运行 GCC 回归测试套件的所有测试。
或者,可以使用以下命令执行相同操作:
```
make check-gcc
```
以下命令可用于运行 Binutils 测试:
```
make check-binutils
```
下面的命令可用于运行 glibc 测试:
```
make check-glibc-linux
```
##### 添加更多 arch/abi 组合进行测试而不引入 multilib
当你想要测试更多 arch/ABI 组合时,可以使用 `--with-extra-multilib-test`,
例如:构建了一个带有 multilib 的 linux 工具链,包含
`rv64gc/lp64d` 和 `rv64imac/lp64`,但你想测试更多配置,如
`rv64gcv/lp64d` 或 `rv64gcv_zba/lp64d`,那么你可以使用 --with-extra-multilib-test
通过 `--with-extra-multilib-test="rv64gcv-lp64d;rv64gcv_zba-lp64d"` 进行指定,
然后测试将针对 `rvgc/lp64d`、`rv64imac/lp64`、`rv64gcv/lp64d`
和 `rv64gcv_zba/lp64d` 运行。
`--with-extra-multilib-test` 支持 bare-metal 和 linux 工具链,并且
即使禁用 multilib 也支持,但用户必须确保额外的 multilib 测试
配置可以与现有的 lib/multilib 一起工作,例如 rv32gcv/ilp32 测试
如果 multilib 没有任何 rv32 multilib 则无法工作。
`--with-extra-multilib-test` 也支持更复杂的格式以适应
最终用户的需求。首先,参数是一个测试
配置列表。每个测试配置由 `;` 分隔。例如:
`rv64gcv-lp64d;rv64_zvl256b_zvfh-lp64d`
对于每个测试配置,它有两个部分,即所需的 arch-abi 部分和
可选的构建标志。我们利用 `:` 分隔它们,并有一些限制。
* arch-abi 应该是必需的,并且在测试配置的开头必须只有一个。
* 构建标志是 arch-abi 之后的类似数组的标志,将有两种
排列方式,即 AND、OR 操作。
* 如果你希望构建标志数组中的标志
__同时__ 作用于 arch-abi,你可以使用 `:` 分隔它们。例如:
```
rv64gcv-lp64d:--param=riscv-autovec-lmul=dynamic:--param=riscv-autovec-preference=fixed-vlmax
```
将被视为一个目标板,与下面相同:
```
riscv-sim/-march=rv64gcv/-mabi=lp64d/-mcmodel=medlow/--param=riscv-autovec-lmul=dynamic/--param=riscv-autovec-preference=fixed-vlmax
```
* 如果你希望构建标志数组中的标志
__分别__ 作用于 arch-abi,你可以使用 ',' 分隔它们。例如:
```
rv64gcv-lp64d:--param=riscv-autovec-lmul=dynamic,--param=riscv-autovec-preference=fixed-vlmax
```
将被视为两个目标板,与下面相同:
```
riscv-sim/-march=rv64gcv/-mabi=lp64d/-mcmodel=medlow/--param=riscv-autovec-preference=fixed-vlmax
riscv-sim/-march=rv64gcv/-mabi=lp64d/-mcmodel=medlow/--param=riscv-autovec-lmul=dynamic
```
* 但是,你也可以一起利用 AND(`:`)、OR(`,`) 操作符,但
OR(`,`) 将始终具有更高的优先级。例如:
```
rv64gcv-lp64d:--param=riscv-autovec-lmul=dynamic:--param=riscv-autovec-preference=fixed-vlmax,--param=riscv-autovec-lmul=m2
```
将被视为两个目标板,与下面相同:
```
riscv-sim/-march=rv64gcv/-mabi=lp64d/-mcmodel=medlow/--param=riscv-autovec-lmul=dynamic/--param=riscv-autovec-preference=fixed-vlmax
riscv-sim/-march=rv64gcv/-mabi=lp64d/-mcmodel=medlow/--param=riscv-autovec-lmul=m2
```
### LLVM / clang
LLVM 可以与 RISC-V GNU Compiler Toolchain 结合使用
来构建 RISC-V 应用程序。要构建支持 C 和 C++ 的 LLVM,
可以使用配置标志 `--enable-llvm`。
例如,要在 RV64 Linux 工具链之上构建 LLVM,可以使用以下命令:
./configure --prefix=$RISCV --enable-llvm --enable-linux
make
注意,不支持 `--enable-llvm` 和 multilib 配置标志的组合。
以下是如何构建支持 LLVM 的 rv64gc Linux/newlib 工具链,
如何使用它通过 clang 构建 C 和 C++ 应用程序,以及如何
使用 QEMU 执行生成的二进制文件的示例。
构建 Linux 工具链并运行示例:
```
# 使用 LLVM 构建 rv64gc toolchain
./configure --prefix=$RISCV --enable-llvm --enable-linux --with-arch=rv64gc --with-abi=lp64d
make -j$(nproc) all build-sim SIM=qemu
# 使用 clang 构建 C application
$RISCV/bin/clang -march=rv64imafdc -o hello_world hello_world.c
$RISCV/bin/qemu-riscv64 -L $RISCV/sysroot ./hello_world
# 使用 clang 构建 C++ application
$RISCV/bin/clang++ -march=rv64imafdc -stdlib=libc++ -o hello_world_cpp hello_world_cpp.cxx
$RISCV/bin/qemu-riscv64 -L $RISCV/sysroot ./hello_world_cpp
```
构建 newlib 工具链并运行示例(不适用于 `--with-multilib-generator=`):
```
# 使用 LLVM 构建 rv64gc bare-metal toolchain
./configure --prefix=$RISCV --enable-llvm --disable-linux --with-arch=rv64gc --with-abi=lp64d
make -j$(nproc) all build-sim SIM=qemu
# 使用 clang 构建 C application
$RISCV/bin/clang -march=rv64imafdc -o hello_world hello_world.c
$RISCV/bin/qemu-riscv64 -L $RISCV/sysroot ./hello_world
# 使用 clang 和 static link 构建 C++ application
$RISCV/bin/clang++ -march=rv64imafdc -static -o hello_world_cpp hello_world_cpp.cxx
$RISCV/bin/qemu-riscv64 -L $RISCV/sysroot ./hello_world_cpp
```
### 开发
本节仅适用于开发者或高级用户,或者如果你想
使用自己的源码树构建工具链。
#### 更新源码树
`riscv-gnu-toolchain` 包含每个子模块的稳定但非最新源码,
如果你想使用最新的开发树,可以使用以下命令
升级所有子模块。
```
git submodule update --remote
```
或者你可以只升级特定的子模块。
```
git submodule update --remote
```
例如,仅升级 gcc,你可以使用以下命令:
```
git submodule update --remote gcc
```
#### 如何检查特定子模块使用的分支
分支信息已记录在 `.gitmodules` 文件中,可以通过
`git submodule add -b` 或 `git submodule set-branch` 设置或更新。
但是,检查正在使用哪个分支的唯一方法是检查 `.gitmodules`
文件,这是 `gcc` 的示例,它使用的是 releases/gcc-12 分支,所以
它将有一个名为 `gcc` 的部分,并且有一个字段 `branch` 是
`releases/gcc-12`。
```
[submodule "gcc"]
path = gcc
url = ../gcc.git
branch = releases/gcc-12
```
#### 使用 `riscv-gnu-toolchain` 以外的源码树
`riscv-gnu-toolchain` 也支持使用树外源码构建工具链。
有几个配置选项用于指定每个
子模块/组件的源码树。
例如,如果你在 `$HOME/gcc` 中有 GCC 源码,请使用 `--with-gcc-src` 使用这些源码构建工具链:
```
./configure ... --with-gcc-src=$HOME/gcc
```
以下是用于指定各种子模块/组件的备用源码的配置选项列表:
```
--with-binutils-src
--with-dejagnu-src
--with-gcc-src
--with-gdb-src
--with-glibc-src
--with-linux-headers-src
--with-llvm-src
--with-musl-src
--with-newlib-src
--with-pk-src
--with-qemu-src
--with-spike-src
--with-uclibc-src
```
#### 构建主机 GCC 以检查编译器警告
GCC 贡献必须满足几个要求才能有资格被上游
收录。使用从相同
源码构建的编译器进行无警告编译是其中之一。标志 `--enable-host-gcc` 正是做这件事:
* 首先构建主机 GCC
* 然后使用此主机 GCC 构建交叉编译器
* 交叉编译器将使用 `-Werror` 构建以识别代码问题
### FAQ
#### 确保代码模型一致性
如果 newlib 的部分内容将被外部库替换(例如用于 Berkeley Host-Target Interface 的 [libgloss-htif](https://github.com/ucb-bar/libgloss-htif)),
你应该注意确保 newlib 和外部库都使用相同的代码模型构建。有关 RISC-V 代码模型的更多信息,
[请阅读这篇 SiFive 博客文章](https://www.sifive.com/blog/all-aboard-part-4-risc-v-code-models)。
表明代码模型不匹配的错误包括链接器发出的“relocation overflow”或“relocation truncated”错误,因为无法成功重定位可执行文件中的符号。
默认情况下,`riscv-gnu-toolchain` 使用 `-mcmodel=medlow` 构建 newlib。你可以通过向配置脚本传递 `--with-cmodel=medany` 来使用替代的 `medany` 代码模型(如 libgloss-htif 中所使用的)。
标签:C/C++, GCC, glibc, GNU Toolchain, Newlib, RISC-V, 事务性I/O, 交叉编译器, 客户端加密, 嵌入式开发, 工具链, 开源硬件, 指令集架构, 编译器, 计算机体系结构, 身份验证强制, 逆向工具