网站首页 > 技术教程 正文
本章我们将把上一章创建的 独立可执行程序 编译为内核,并和 bootloader 链接成为可以被 qemu 加载的 bootimage 。为此我们将介绍:
- 使用 目标三元组 描述目标操作系统。
- 使用 cargo xbuild 和 目标三元组 编译内核。
- 将 内核 和 bootloader 链接成 bootimage 。
- 修改 _start ,使其能够对堆栈进行一些简单的初始化。
目标三元组:
cargo 在编译内核时,可以用过 --target <target triple> 支持不同的系统。 target triple 包含:cpu 架构、供应商、操作系统和 ABI 。
由于我们在编写自己的操作系统,所以所有目前的 目标三元组 都不适用。幸运的是,rust 允许我们用 JSON 文件定义自己的 目标三元组 。首先我们来看一下 x86_64-unknown-linux-gnu 的 JSON 文件:
{
  "llvm-target": "x86_64-unknown-linux-gnu",
  "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
  "arch": "x86_64",
  "target-endian": "little",
  "target-pointer-width": "64",
  "target-c-int-width": "32",
  "os": "linux",
  "executables": true,
  "linker-flavor": "gcc",
  "pre-link-args": ["-m64"],
  "morestack": false
}
因为我们的主要目的是编写 os ,所以这里直接给出目标文件的实现:因为我们的主要目的是编写 os ,所以这里直接给出目标文件的实现:
// in riscv32-xy_os.json
{
  "llvm-target": "riscv32",
  "data-layout": "e-m:e-p:32:32-i64:64-n32-S128",
  "target-endian": "little",
  "target-pointer-width": "32",
  "target-c-int-width": "32",
  "os": "none",
  "arch": "riscv32",
  "cpu": "generic-rv32",
  "features": "+m,+a,+c",
  "max-atomic-width": "32",
  "linker": "rust-lld",
  "linker-flavor": "ld.lld",
  "pre-link-args": {
    "ld.lld": ["-Tsrc/boot/linker.ld"]
  },
  "executables": true,
  "panic-strategy": "abort",
  "relocation-model": "static",
  "eliminate-frame-pointer": false
}对文件各参数细节感兴趣的读者可以自行研究,这里只对 pre-link-args 进行解释:
"pre-link-args": {
    "ld.lld": [
      "-Tsrc/boot/linker.ld"
    ]
  },这里我们需要使用指定的链接器,这里同样直接给出 linker.ld 的实现,请自行创建好 src/boot/linker.ld 文件:
/* Copy from bbl-ucore : https://ring00.github.io/bbl-ucore      */
/* Simple linker script for the ucore kernel.
   See the GNU ld 'info' manual ("info ld") to learn the syntax. */
OUTPUT_ARCH(riscv)
ENTRY(_start)
BASE_ADDRESS = 0xC0020000;
SECTIONS
{
    . = 0xC0000000;
    .boot : {
        KEEP(*(.text.boot))
    }
    /* Load the kernel at this address: "." means the current address */
    . = BASE_ADDRESS;
    start = .;
    .text : {
        stext = .;
        *(.text.entry)
        *(.text .text.*)
        . = ALIGN(4K);
        etext = .;
    }
    .rodata : {
        srodata = .;
        *(.rodata .rodata.*)
        . = ALIGN(4K);
        erodata = .;
    }
    .data : {
        sdata = .;
        *(.data .data.*)
        edata = .;
    }
    .stack : {
        *(.bss.stack)
    }
    .bss : {
        sbss = .;
        *(.bss .bss.*)
        ebss = .;
    }
    PROVIDE(end = .);
}运行 cargo build --target riscv32-xy_os.json ,发现编译失败了:
error[E0463]: can't find crate for `core`错误的原因是:no_std 的上下文隐式地链接到 core 库 。 core 库 包含基础的 Rust 类型,如 Result、Option 和迭代器等。core 库 只支持原生的 目标三元组 ,而我们在编写 os 时使用的是自定义的 目标三元组 。
如果我们想为其他系统编译代码,我们需要为这些系统重新编译整个 core 库 。这就是为什么我们需要 cargo xbuild 。
Cargo xbuild
这个工具封装了 cargo build。同时,它将自动交叉编译 core 库 和一些 编译器内建库(compiler built-in libraries) 。我们可以用下面的命令安装它:
cargo install cargo-xbuild现在运行 cargo xbuild --target riscv32-xy_os.json ,我们的内核已经可以正确编译了。接下来的任务就是将他和 bootloader 链接,得到可以被 qemu 加载的 os 。
创建引导映象(Bootimage)
编写一个 bootloader 并将其与内核链接成 引导映像 并不是一个简单的事情,所以我们直接使用已有的 bootloader :
下载 并将其中名为 related items in lab2 的文件夹中的两个子文件夹拷贝至 Cargo.toml 的同级目录下。
有了 bootloader ,那么只需要将其与我们的内核链接就可以了。这里我们需要使用到 riscv-pk 中的 configure 。为了以后能够方便的进行编译链接,我们需要编写一个 Makefile 文件(与 Cargo.toml 位于同级目录):
target := riscv32-xy_os
bbl_path := $(abspath riscv-pk)
mode := debug
kernel := target/$(target)/$(mode)/xy_os
bin := target/$(target)/$(mode)/kernel.bin
.PHONY: all clean run build asm qemu kernel
all: kernel
$(bin): kernel
    mkdir -p target/$(target)/bbl && \
    cd target/$(target)/bbl && \
    $(bbl_path)/configure \
        --with-arch=rv32imac \
        --disable-fp-emulation \
        --host=riscv64-unknown-elf \
        --with-payload=$(abspath $(kernel)) && \
    make -j32 && \
    cp bbl $(abspath $@)
build: $(bin)
run: build qemu
kernel:
    @cargo xbuild --target riscv32-xy_os.json
asm:
    @riscv64-unknown-elf-objdump -d $(kernel) | less
qemu:
    qemu-system-riscv32 -kernel $(bin) -nographic -machine virt
docker:
    sudo docker run -it --mount type=bind,source=$(shell pwd)/..,destination=/mnt panqinglin/rust_riscv bash未安装 Prebuilt RISC?V GCC Toolchain 会导致编译错误,请参考 how to use "Prebuilt RISC?V GCC Toolchain" 下载使用;
执行 make kernel 生成的 kernel.bin 就是我们需要的 可以被 qemu 加载的 os 。执行 make run :
> make run
...
qemu-system-riscv32 -kernel target/riscv32-xy_os/debug/kernel.bin -nographic -machine virt
bbl loader至此,我们的 最小内核 已经“成功”跑起来了!!!吗???
Hello World!
我们将用最简单的方法来验证 os 是否已经正确的被加载了:打印 Hello World! :
#![no_std] // don't link the Rust standard library
#![no_main] // disable all Rust-level entry points
use core::panic::PanicInfo;
use bbl::sbi;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}
static HELLO: &[u8] = b"Hello World!";
#[no_mangle]
pub extern "C" fn main() -> ! {
    for &c in HELLO {
        sbi::console_putchar(c as usize);
    }
    loop {}
}bbl::sbi 是 依赖项目 中已经完成的库,可以使用 sbi::console_putchar(usize) 打印一个 ASCII 字符 。使用前需要在 Cargo.toml 中添加对其的依赖:
[dependencies]
bbl = { path = "crate/bbl" }编译运行!很遗憾的发现,这位“新生儿”还没有学会说话(屏幕并没有显示 Hello World!)。还记得上一章的 _start 吗?
- “你已经是一个成熟的 _start 了,需要学会自己设置堆栈。”
- “我不是,我没有,别瞎说!”
一个 成熟的 _start 需要能够设置一些简单的堆栈信息,然后跳转至 main 函数。所以我们需要使用 汇编语言 重写 _start 。在 src/boot 中创建 entry.asm :
.section .text.entry
    .globl _start
_start:
    add t0, a0, 1
    slli t0, t0, 16
    lui sp, %hi(bootstack)
    addi sp, sp, %lo(bootstack)
    add sp, sp, t0
    call rust_main
    .section .bss.stack
    .align 12  #PGSHIFT
    .global bootstack
bootstack:
    .space 4096 * 16 * 8
    .global bootstacktop
bootstacktop:然后在 main.rs 中通过 global_asm 引入 _start ,并实现 rust_main 。现在 main.rs 应该长成这样:
#![no_std] // don't link the Rust standard library
#![no_main] // disable all Rust-level entry points
#![feature(global_asm)]
use core::panic::PanicInfo;
use bbl::sbi;
global_asm!(include_str!("boot/entry.asm"));
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}
static HELLO: &[u8] = b"Hello World!";
#[no_mangle]
pub extern "C" fn rust_main() -> ! {
    for &c in HELLO {
        sbi::console_putchar(c as usize);
    }
    loop {}
}
#[no_mangle]
pub extern fn abort() {
    panic!("abort!");
}#![feature(global_asm)] 使得我们能够使用 global_asm!(include_str!("boot/entry.asm")); 引入外部汇编代码。 entry.asm 中的 call rust_main 告诉我们,需要在 rust_main 中进行打印 Hello World! 的工作。所以修改函数名为 rust_main 。最下方的 abort() 并无意义,只是为了避免一个 error ,参见 rust lld: error: undefined symbol: abort 。
那么,接下来,就是见证奇迹的时刻:
> make run
...
qemu-system-riscv32 -kernel target/riscv32-xy_os/debug/kernel.bin -nographic -machine virt
bbl loader
Hello World!以后若无特殊说明,编译运行的命令就是make run
预告
最黑暗的日子已经过去,我们已经完成了一个可以正常运行的 最小内核 !下一章我们将在此基础上,实现 rust 中最经典的宏: println! ,以便于后续的调试输出。
猜你喜欢
- 2024-10-23 基于S12 MagniV系列的LIN Bootloader设计
- 2024-10-23 怎么使用USBasp给arduino烧写bootloader?
- 2024-10-23 腾讯 ROG 游戏手机 6 / Pro 公开内核源码并支持解锁 bootloader
- 2024-10-23 浅谈基于uds的bootloader设计(简要概述u-boot的主要功能)
- 2024-10-23 Bootloader引导程序uboot(bootice 引导)
- 2024-10-23 基于CAN总线的Bootloader设计与实现
- 2024-10-23 基于RT-Thread完整版搭建的极简Bootloader
- 2024-10-23 海信阅读手机A9现可解锁bootloader并获取root权限
- 2024-10-23 三星警告Galaxy Z Fold 3解锁bootloader将失去相机功能
- 2024-10-23 Realme全机型刷入TWRP_Recvery面具Root刷机包下载最简单教程
欢迎 你 发表评论:
- 10-23Excel计算工龄和年份之差_excel算工龄的公式year
- 10-23Excel YEARFRAC函数:时间的"年份比例尺"详解
- 10-23最常用的10个Excel函数,中文解读,动图演示,易学易用
- 10-23EXCEL中如何计算截止到今日(两个时间中)的时间
- 10-2390%人不知道的Excel神技:DATEDIF 精准计算年龄,告别手动算错!
- 10-23计算工龄及工龄工资(90%的人搞错了):DATE、DATEDIF组合应用
- 10-23Excel中如何计算工作日天数?用这两个函数轻松计算,附新年日历
- 10-23怎样快速提取单元格中的出生日期?用「Ctrl+E」批量搞定
- 最近发表
- 
- Excel计算工龄和年份之差_excel算工龄的公式year
- Excel YEARFRAC函数:时间的"年份比例尺"详解
- 最常用的10个Excel函数,中文解读,动图演示,易学易用
- EXCEL中如何计算截止到今日(两个时间中)的时间
- 90%人不知道的Excel神技:DATEDIF 精准计算年龄,告别手动算错!
- 计算工龄及工龄工资(90%的人搞错了):DATE、DATEDIF组合应用
- Excel中如何计算工作日天数?用这两个函数轻松计算,附新年日历
- 怎样快速提取单元格中的出生日期?用「Ctrl+E」批量搞定
- Excel日期函数之DATEDIF函数_excel函数datedif在哪里
- Excel函数-DATEDIF求司龄_exceldatedif函数计算年龄
 
- 标签列表
- 
- 下划线是什么 (87)
- 精美网站 (58)
- qq登录界面 (90)
- nginx 命令 (82)
- nginx .http (73)
- nginx lua (70)
- nginx 重定向 (68)
- Nginx超时 (65)
- nginx 监控 (57)
- odbc (59)
- rar密码破解工具 (62)
- annotation (71)
- 红黑树 (57)
- 智力题 (62)
- php空间申请 (61)
- 按键精灵 注册码 (69)
- 软件测试报告 (59)
- ntcreatefile (64)
- 闪动文字 (56)
- guid (66)
- abap (63)
- mpeg 2 (65)
- column (63)
- dreamweaver教程 (57)
- excel行列转换 (56)
 

本文暂时没有评论,来添加一个吧(●'◡'●)