一般我们使用 Rust 编写的代码已经由 Rust 工具链帮我们构建好了代码所需要的操作系统下的运行环境,但在编写裸机代码的话,由于没有操作系统支持,按照我们平时的写法是行不通的,这里将简单介绍如何使用 Rust 编写 Risc-V 裸机代码。
环境与工具链
我们要编译成的目标平台是 Risc-V,但是我们编写的代码的平台可能不是,也许是 x84、ARM等,像这种编译成其他平台叫做交叉编译。在这之前,我们需要安装一些组件。
target
使用命令
rustup target list
能够查看 Rust 支持的目标平台,你能够看到输出的列表,以我们要编译的 Risc-V 64 位目标平台为例:riscv64gc-unknown-none-elf
,这种格式被一个横杠分隔成四个部分(一般来说是三个部分,架构和厂家是一个部分,但是为什么三个部分被分成了四个?):
- 架构
- 厂商
- 操作系统
- 环境
以上面为例就是:架构为 riscv64gc(Risc-V 64 位,g 代表通用指令集扩展,支持整数、浮点、乘除法等功能,c 代表精简指令),厂家无,裸机环境,生成的文件为 ELF。
确定好目标平台后,使用命令安装:
rustup target add riscv64gc-unknown-none-elf
component
我们继续安装其他所需要的组件:
rustup component add llvm-tools-preview
链接器
如果说在我们的裸机代码中需要链接一些在链接器中定义的符号,就需要指定我们的链接器脚本。
在项目根目录下创建文件 .cargo/config.toml
,输入以下内容:
# .cargo/config.toml
[build]
target = "riscv64gc-unknown-none-elf"
[target.riscv64gc-unknown-none-elf]
rustflags = ["-Clink-arg=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes"]
其中 [build]
中指定了编译的目标平台,[target.riscv64gc-unknown-none-elf]
中的 rustflags
指定了链接器脚本: src/linker.ld
如果你不需要的话这个可以忽略。
构建代码
环境准备好了,但是在正式编写我们的第一句代码之前还要写一些代码。
no_std
Rust 为我们提供了 std
标准库,但是在裸机环境下无法使用标准库,如果此时编译的话编译器抱怨:can't find crate for "std"
,所以需要告诉编译器我们不使用标准库。
在 main 文件顶部写:
#![no_std]
此时就不会出现找不到标准库的错误了,但同时我们也无法使用任何标准库提供的功能,比如 println!
。
no_main
在裸机中 main 函数不在成为我们编写代码的入口,因为在 main 函数执行之前实际上是有一个 _start
函数作为程序真正的入口用于初始化等工作,这一步编译器和操作系统帮我们做完了,但在裸机中没有操作系统,所以我们实际上会直接在 _start
这个真正的入口里编写代码。
所以我们要告诉编译器不再使用 main 函数作为入口,并提供一个 _start
函数作为入口,在 main 文件里写:
// src/main.rs
#![no_main]
#[no_mangle]
pub extern "C" fn _start() -> ! {
loop {}
}
#![no_main]
告诉编译器不使用 main 函数作为入口。然后我们提供了一个 _start
函数,这个函数名是大多数系统的约定,会将这个符号作为入口。我们在 _start
函数上指定了 宏 #[no_mangle]
,用于告诉编译器不要对它命名重整,这样 _start
函数编译出来的符号就是 _start
,如果没有这个宏, _start
函数编译出来的符号会大变样,而找不到 _start
符号作为入口。
panic_handler
Rust 编译器会要你提供一个错误处理函数,这个函数会在 panic 的时候被使用,我们直接在 main 文件下写:
// src/main.rs
use core::panic::PanicInfo;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
loop {}
}
到这里,我们的代码应能编译通过了,你可以在 _start
里编写你的 Risc-V 的裸机代码了。
这篇博客介绍了如何使用Rust编写Risc-V裸机代码。作者首先介绍了编写裸机代码与常规的Rust代码的不同之处,因为裸机代码没有操作系统的支持,所以需要进行一些特殊的配置和编写方式。
博客中提到了几个关键点。首先是环境与工具链的配置,作者介绍了如何安装Rust的目标平台和其他组件。其次是链接器的使用,作者详细说明了如何指定链接器脚本。然后是代码的构建,包括使用
no_std
指令告诉编译器不使用标准库,使用no_main
指令告诉编译器不使用main
函数作为入口,以及提供panic_handler
函数来处理错误。这篇博客的闪光点在于作者提供了清晰的步骤和示例代码,让读者能够快速了解如何使用Rust编写Risc-V裸机代码。这对于那些想要进入裸机编程领域的开发者来说是非常有帮助的。
然而,这篇博客也有一些可以改进的地方。首先,作者可以在介绍环境与工具链配置的部分提供更多的细节,例如如何安装Rust和相关组件。其次,博客中的示例代码比较简单,可能无法满足一些读者的需求。作者可以考虑提供更复杂的示例代码,以帮助读者更好地理解如何编写实际的Risc-V裸机代码。最后,博客中没有提到如何进行调试,这对于初学者来说可能是一个挑战。作者可以考虑在博客中添加一些关于调试的内容,例如如何使用调试器来调试裸机代码。
总体而言,这篇博客提供了一个很好的起点,让读者能够了解如何使用Rust编写Risc-V裸机代码。作者可以通过提供更多的细节和示例代码来改进这篇博客,并考虑添加关于调试的内容。