3、工具链搭建(上):RISC-V GNU 工具链的源码编译与安装、配置环境变量、验证工具链
好,咱们开始动手了。
上一章我们把 QEMU 模拟器跑起来了,但光有硬件模拟还不够——你得有编译器,才能把 C 代码变成 RISC-V 能懂的机器码。这一章,我们就来搞定 RISC-V GNU 工具链。
说白了,这套工具链就是你的「翻译官」。它把高级语言翻译成 RISC-V 的二进制指令。没有它,你写再多代码也是白搭。
核心知识点速览:
- 从源码编译 RISC-V GNU 工具链
- 配置环境变量,让系统能找到工具
- 编写一个简单的 C 程序,验证工具链是否正常工作
3.1 为什么要从源码编译?
你可能会问:直接用 apt-get 安装不行吗?
嗯,我刚开始也这么想。但后来发现,发行版自带的交叉编译器版本往往比较老,而且不一定支持你想要的扩展指令集。比如你要用 RISC-V 的向量扩展(V 扩展),或者想定制一些编译参数,那就必须自己编译。
说白了,从源码编译虽然麻烦一点,但你能拿到最干净的、完全可控的工具链。我在一个项目里就吃过亏——用系统自带的工具链编译出来的程序,在 QEMU 上跑得好好的,换到真实芯片上就崩了。查了半天,发现是工具链版本不匹配。从那以后,我都是自己编译。
3.2 下载源码
首先,我们需要从 GitHub 上拉取 RISC-V GNU 工具链的源码。这个仓库是官方维护的,包含了 GCC、Binutils、GDB 等全套工具。
# 克隆仓库(建议用 --depth=1 只拉取最新版本,省时间)
git clone --depth=1 https://github.com/riscv-collab/riscv-gnu-toolchain.git
cd riscv-gnu-toolchain
小提示:这个仓库比较大,即使加了 --depth=1 也有几百 MB。如果你的网络不太好,可以考虑用国内镜像,或者提前下载好压缩包。我个人习惯用 git clone,因为后续更新方便。
3.3 安装依赖
编译工具链需要一些系统库和工具。不同发行版的包名略有不同,这里以 Ubuntu/Debian 为例:
sudo apt-get update
sudo apt-get install -y 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
如果你是 CentOS/RHEL 用户,把 apt-get 换成 yum 或 dnf,包名稍微调整一下就行。我记得有一次在 CentOS 7 上编译,缺了 libmpc,折腾了半天才发现——嗯,所以依赖这一步千万别跳过。
3.4 配置与编译
依赖装好之后,就可以配置了。这里我们选择编译「Newlib」版本——也就是面向裸机或嵌入式系统的 C 库。它体积小,适合没有操作系统的环境。
# 创建一个目录存放编译产物
mkdir build
cd build
# 配置,指定安装路径
../configure --prefix=/opt/riscv --enable-multilib
# 开始编译(-j 后面的数字是你的 CPU 核心数,可以加快速度)
make -j$(nproc)
注意:编译过程非常耗时!在我的机器上(8 核 i7),大概需要 30-40 分钟。如果你用的是虚拟机或者配置较低的电脑,可能得 1-2 小时。建议找个空闲时间,泡杯咖啡慢慢等。
这里解释一下几个关键参数:
--prefix=/opt/riscv:指定安装路径。我习惯放在 /opt 下,这样和系统自带的工具不会冲突。--enable-multilib:支持多种 RISC-V 架构变体(比如 RV32 和 RV64)。这个选项很实用,因为你以后可能会在不同位宽的目标上做实验。
编译完成后,你会看到 /opt/riscv 目录下多了一堆东西:bin、lib、share 等。核心的可执行文件都在 bin 目录里。
ls /opt/riscv/bin/
# 你应该能看到类似这样的输出:
# riscv64-unknown-elf-addr2line riscv64-unknown-elf-gcc
# riscv64-unknown-elf-ar riscv64-unknown-elf-gcc-12.2.0
# riscv64-unknown-elf-as riscv64-unknown-elf-gcc-ar
# ... 等等
3.5 配置环境变量
工具链装好了,但系统还不知道它在哪。你需要把它的路径加到 PATH 环境变量里。
我个人习惯把环境变量配置写到 ~/.bashrc 或 ~/.zshrc 里,这样每次打开终端都能直接用。
# 编辑你的 shell 配置文件
vim ~/.bashrc
# 在文件末尾添加以下内容
export PATH=$PATH:/opt/riscv/bin
保存退出后,执行 source ~/.bashrc 让配置生效。或者直接关掉终端重新打开也行。
一个小技巧:我还会额外加一个环境变量 RISCV,指向工具链的根目录。这样在写 Makefile 或者脚本时,引用起来更方便:
export RISCV=/opt/riscv
export PATH=$PATH:$RISCV/bin
3.6 验证工具链
环境配好了,我们来验证一下工具链能不能正常工作。
先检查版本信息:
riscv64-unknown-elf-gcc --version
# 输出类似:
# riscv64-unknown-elf-gcc (GCC) 12.2.0
# Copyright (C) 2022 Free Software Foundation, Inc.
# ...
看到版本号,说明工具链已经就绪了。接下来,我们写一个最简单的 C 程序,编译成 RISC-V 的可执行文件。
// hello.c
#include <stdio.h>
int main() {
printf("Hello, RISC-V World!\n");
return 0;
}
编译命令:
riscv64-unknown-elf-gcc -o hello hello.c
编译完成后,用 file 命令看看生成的文件:
file hello
# 输出:
# hello: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), ...
看到「UCB RISC-V」这几个字,说明编译成功了。这是一个货真价实的 RISC-V 64 位可执行文件。
你还可以用 riscv64-unknown-elf-objdump 反汇编看看:
riscv64-unknown-elf-objdump -d hello | head -20
你会看到一堆 RISC-V 指令,比如 addi、lui、jal 等等。嗯,这就是你的 C 代码被翻译成的机器语言。
验证清单:
- ✅
riscv64-unknown-elf-gcc --version能正常输出版本号 - ✅ 能编译 hello.c 生成 ELF 文件
- ✅
file命令确认是 RISC-V 架构 - ✅
objdump能看到 RISC-V 指令
3.7 避坑指南
编译过程中可能会遇到一些问题,我把自己踩过的坑列出来,你遇到了可以参考:
- 编译到一半报错,提示缺少某个头文件——多半是依赖没装全。回头检查一下 3.3 节的依赖列表,看看是不是漏了某个包。
- make 执行到 90% 左右卡住不动——可能是内存不够。编译 GCC 很吃内存,建议至少 4GB 可用内存。如果不够,可以关掉一些其他程序,或者增加 swap 空间。
- 编译完成后,执行 gcc 提示「找不到命令」——环境变量没配好。检查一下 PATH 里是否包含了 /opt/riscv/bin,以及是否执行了 source。
- 我曾经在 macOS 上编译过一次——嗯,那真是噩梦。GNU 工具链对 macOS 的支持不太好,各种依赖问题。如果你用的是 Mac,建议用 Docker 或者直接装 Linux 虚拟机。
3.8 本章小结
这一章我们完成了工具链的搭建。你学会了:
- 从 GitHub 拉取 RISC-V GNU 工具链源码
- 配置并编译出适合嵌入式开发的交叉编译器
- 配置环境变量,让系统能找到工具链
- 编写并编译了一个简单的 C 程序,验证了工具链的正确性
现在,你的开发环境已经具备了「编译」的能力。下一章,我们会把编译好的程序放到 QEMU 上运行,真正看到 RISC-V 的世界里跑起代码来。