应用程序与基本执行环境

本章我们会先抛开操作系统 先用 裸机 跑一段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.*)
    }
}";