riscv 工具链的了解和使用
- 在 RISC-V 开发中, 交叉编译工具链允许我们在一个平台(如 x86 主机)上,为另一个平台(如RISC-V 开发板)生成可执行代码。
1. 核心概念:工具链的“三元组” (Triplet)
你经常会看到像 riscv64-unknown-linux-gnu-
这样的名称,这就是工具链的“三元组”,其标准格式为:
1 |
|
<arch>
(架构):指定目标 CPU 架构,例如riscv64
或riscv32
。<vendor>
(供应商):通常是unknown
或公司名。<os>
(操作系统/环境):这是最关键的部分,它决定了工具链的目标环境和使用的 C 标准库 (libc)。最常见的两个是:elf
: 面向裸机 (Bare-metal) 或嵌入式实时操作系统 (RTOS)。linux-gnu
: 面向完整的 GNU/Linux 操作系统。
2. 两大主流工具链详解
1. riscv64-unknown-elf
用于裸机和嵌入式开发的标准工具链。
- 目标系统: 没有任何操作系统的环境(裸机),或者使用了轻量级实时操作系统(如 FreeRTOS, RT-Thread)的环境。
- C 标准库 (Libc): 使用 Newlib。
- Newlib 是一个轻量级的 C 库,专为嵌入式系统设计。它只提供最基础的 C 语言函数(如
strcpy
,printf
),并且不依赖任何操作系统的系统调用(Syscall)。如果需要文件操作或内存管理,需要实现底层的“桩函数”(stubs)。
- Newlib 是一个轻量级的 C 库,专为嵌入式系统设计。它只提供最基础的 C 语言函数(如
- 应用场景:
- 编写 Bootloader(如 U-Boot)。
- 开发 RISC-V 的“特权二进制接口”固件(如 OpenSBI)。
- 为微控制器 (MCU) 编写固件。
- 开发简单的操作系统内核。
2. riscv64-linux-gnu
用于在 RISC-V 平台上开发 Linux 应用的工具链。
- 目标系统: 运行完整 Linux 内核的系统。
- C 标准库 (Libc): 使用 glibc (GNU C Library)。
- glibc 是功能完备的标准 C 库,提供了丰富的 POSIX API 支持(如
fork
,pthread
, 文件系统操作等)。它深度依赖 Linux 内核提供的系统调用来完成工作。
- glibc 是功能完备的标准 C 库,提供了丰富的 POSIX API 支持(如
- 典型应用场景:
- 编译一个标准的 C/C++ 应用程序(如 Nginx, Redis),让它运行在 RISC-V 架构的 Linux 发行版上(如 Ubuntu, Debian for RISC-V)。
- 开发 Linux 用户态驱动或服务程序。
Tip:
riscv64-unknown-linux-gnu-
和riscv64-linux-gnu-
在功能上是等价的,可以互换使用。unknown
字段在这里没有实际影响。
3. 如何获取和安装工具链
方式一:使用包管理器 (简单快捷)
对于 linux-gnu
工具链,这是最简单的方法。以 Ubuntu/Debian 为例:
1 |
|
- 优点: 安装简单
- 缺点: 版本可能不是最新的
方式二:从源码编译 (推荐,灵活且最新)
获取最新版本工具链(包括 elf
和 linux-gnu
)的最佳方式。
安装相关依赖
1
2sudo apt install libncurses-dev libncursesw5-dev pkg-config autoconf automake bison flex gawk gcc g++ libtool make patch python3-dev texinfo wget
sudo apt-get install 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 make bison flex texinfo gawk libncurses5-dev libexpat1-dev libgmp-dev libmpfr-dev libmpc-dev libgmp-dev libmpfr-dev libmpc-dev克隆官方仓库
1
2
3#`--recursive` 参数至关重要,它会同时下载 `gcc`, `binutils` 等所有子模块。
git clone --recursive https://github.com/riscv-collab/riscv-gnu-toolchain
cd riscv-gnu-toolchain1
2
3
4
5
6
7
* 检查当前子模块情况。
git submodule status
* 拉取子模块(init: 子模块未初始化时初始化,recursive: 嵌套子模块也一起拉取)
* 主仓库换分支时同步子模块
git submodule update --init --recursive配置与编译
需要指定安装路径 (--prefix
) 和目标架构 (--with-arch
,--with-abi
)。- 编译
linux-gnu
工具链 (用于Linux):
1
2
3
4
5
6
7
8# 创建安装目录
mkdir -p /opt/riscv-linux
# 配置: 目录,目标是为linux构建工具链
./configure --prefix=/opt/riscv-linux --enable-linux
# `make linux` 会自动处理多阶段编译的复杂流程(构建临时gcc->构建glibc->构建最终gcc)
time make -j$(nproc) linux
# 安装
sudo make install- 编译
elf
工具链 (用于裸机):riscv64-unknown-elf-
1
2
3
4
5
6
7
8
9# 创建一个安装目录
mkdir -p /opt/riscv-elf
# 配置: 其中 `rv64gc` 指支持 64 位基础整数指令集(I)、乘除法(M)、原子(A)、浮点(F、D)、压缩(C)等扩展;
# `lp64d` 表示 long 和 pointer 为 64 位,使用 double 精度浮点。
./configure --prefix=/opt/riscv-elf --with-arch=rv64gc --with-abi=lp64d
# 编译 (-j`nproc` 使用所有CPU核心加速)
time make -j$(nproc)
# 安装
sudo make install- 编译
添加到环境变量
为了方便使用,将工具链的bin
目录添加到PATH
。编辑~/.bashrc
或~/.zshrc
文件:1
2
3
4
5
6# 添加这行到文件末尾 (根据编译的类型选择)
export PATH="/opt/riscv-elf/bin:$PATH" # For elf toolchain
export PATH="/opt/riscv-linux/bin:$PATH" # For linux toolchain
# 使配置生效
source ~/.bashrc
4. 简单使用
1 |
|
使用 elf
工具链编译
1 |
|
这个 hello.elf
是一个静态链接的裸机程序。它不能直接在 x86 Linux 主机上运行,也不能在 RISC-V Linux 系统上直接运行,因为它缺少操作系统加载器所需的信息。它需要被烧录到裸机环境或通过模拟器(如 QEMU-system)加载执行。
这个
hello.elf
文件虽然是标准的 ELF 格式,但它与 Linux 可执行文件有本质区别:
- 不含
INTERP
段:它不指定动态链接器,因为它不依赖任何动态库。- 静态链接: 它静态链接了轻量级的
newlib
C 库,而非glibc
。- 无系统调用: 其中的
printf
函数最终依赖开发者实现的底层 I/O 桩函数(如通过 UART 发送字符),而不是 Linux 的write
系统调用。- 不同的程序入口: 它的启动代码 (
_start
) 负责初始化 C 运行环境后调用main
,但main
返回后程序通常会进入死循环,因为它没有“退出”到操作系统的概念。
使用 linux-gnu
工具链编译
1 |
|
这个 hello.linux
是一个动态链接的 Linux 程序。它需要一个 RISC-V Linux 环境来运行,因为它依赖于该环境中的动态链接器 (ld-linux-riscv64-lp64d.so.1
) 和 glibc 库。
总结:
特性 | riscv64-unknown-elf |
riscv64-linux-gnu |
---|---|---|
目标平台 | 裸机 (Bare-metal)、RTOS | GNU/Linux 系统 |
C 库 | newlib (轻量级,无 OS 依赖) |
glibc (功能完备,依赖 Linux 内核) |
核心用途 | 固件、Bootloader、RTOS 应用、简单操作系统内核 | 编译可在 RISC-V Linux 上运行的应用程序 |
选择场景 | “为一块开发板从零开始写程序。” | “在启动的 Linux 上面运行软件。” |
riscv 工具链的了解和使用