应用程序与基本执行环境
本章我们会先抛开操作系统 先用 裸机 跑一段RISCV的Rust程序.
因为是裸机 目前也没有实现完整的系统调用 所以 我们只能使用rust的core 且加上#![no_std]属性
build.rs
在这个build.rs中 我们会为这个程序使用 链接器脚本 指定程序 各个段的内存布局。 因为目前 我们没有 虚拟内存 所以只能让程序 利用好每个物理内存。
这是build.rs的主函数 将链接脚本写到
fn main() {
use std::{env, fs, path::PathBuf};
// 仅在交叉编译到 RISC-V64 时生成链接脚本
if env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default() == "riscv64" {
let ld = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld");
fs::write(&ld, LINKER_SCRIPT).unwrap();
// 告诉 rustc 使用此链接脚本
println!("cargo:rustc-link-arg=-T{}", ld.display());
}
}
这是链接脚本
const LINKER_SCRIPT: &[u8] = b"
OUTPUT_ARCH(riscv) // 目标架构
ENTRY(_m_start) // 入口点符号为_m_start, 会在汇编程序中找_m_start这个全局符号
M_BASE_ADDRESS = 0x80000000; // M态的基地址 在QEMU中一般是0x80000000 一般SBI或者bootloader放这里
S_BASE_ADDRESS = 0x80200000; // S态的基地址 一般kernel放这里
// 程序分段设置
SECTIONS {
. = M_BASE_ADDRESS; // . 为当前地址的意思 即 将当前位置赋值为0x8000000
.text.m_entry : { *(.text.m_entry) } // text
.text.m_trap : { *(.text.m_trap) }
.bss.m_stack : { *(.bss.m_stack) }
.bss.m_data : { *(.bss.m_data) }
/* ===== S-mode region (this program) ===== */
. = S_BASE_ADDRESS;
.text : {
*(.text.entry) /* _start entry, must come first */
*(.text .text.*) /* other code */
}
.rodata : {
*(.rodata .rodata.*)
*(.srodata .srodata.*)
}
.data : {
*(.data .data.*)
*(.sdata .sdata.*)
}
.bss : {
*(.bss.uninit) /* stack space */
*(.bss .bss.*)
*(.sbss .sbss.*)
}
}";