kernel data abort是什么原因,row data是什么意思

  

  在本章中,我们将把上一章创建的独立可执行程序编译到内核中,并与bootloader链接,成为一个可以被qemu加载的bootimage。为此,我们将介绍:   

  

  使用目标三元组描述目标操作系统。使用cargo xbuild目标三元组.编译内核,将内核bootloader链接到bootimage.修改_start,以便它可以做一些简单的堆栈初始化。目标三元组:   

  

  Cargo在编译内核时可以使用- target target triple来支持不同的系统。包括:cpu架构、供应商、操作系统和ABI。   

  

  因为我们正在编写我们自己的操作系统,所有当前的target triple都不适用。幸运的是,rust允许我们用目标三元组文档定义自己的JSON。首先,让我们看看目标三元组'sx86_64-unknown-linux-gnu的文件:   

  

  { ' llvm-target ' : ' x86 _ 64-unknown-Linux-GNU ',' data-layout ' : ' e-m : e-I 64:64-f 803:128-n 83:32:64-S128 ',' arch': 'x86_64 ',' target-endian': 'little ',' target-pointer-width ' 338   

  

  //in riscv 32-xy _ OS . JSON { ' llvm-target ' : ' riscv 32 ',' data-layout ' : ' e-m :32:32-I 64:64-n32-S128 ',' target-endian': 'little ',' target-pointer-width': '32 ',' target-c-int-width': '32在这里,只解释前置链接参数:   

  

  pre-link-args ' 3360 { ' LD . lld ' 3360 '-tsrc/boot/linker.ld ' },这里需要用到指定的链接器,这里也直接给出了linker . LD的实现。请创建您自己的JSON文件:   

  

/* 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: 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_osbbl_path := $(abspath riscv-pk)mode := debugkernel := target/$(target)/$(mode)/xy_osbin := target/$(target)/$(mode)/kernel.bin.PHONY: all clean run build asm qemu kernelall: 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 qemukernel: @cargo xbuild --target riscv32-xy_os.jsonasm: @riscv64-unknown-elf-objdump -d $(kernel) | lessqemu: qemu-system-riscv32 -kernel $(bin) -nographic -machine virtdocker: sudo docker run -it --mount type=bind,source=$(shell pwd)/..,destination=/mnt panqinglin/rust_riscv bash未安装 Prebuilt RISCV GCC Toolchain 会导致编译错误,请参考 how to use "Prebuilt RISCV 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 virtbbl loader至此,我们的 最小内核 已经“成功”跑起来了!!!吗???

  

Hello World!我们将用最简单的方法来验证 os 是否已经正确的被加载了:打印 Hello World!

  

#! // don't link the Rust standard library#! // disable all Rust-level entry pointsuse core::panic::PanicInfo;use bbl::sbi;#fn panic(_info: &PanicInfo) -> ! { loop {}}static HELLO: & = b"Hello World!";#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 中添加对其的依赖:

  

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 bootstackbootstack: .space 4096 * 16 * 8 .global bootstacktopbootstacktop:然后在 main.rs 中通过 global_asm 引入 _start ,并实现 rust_main 。现在 main.rs 应该长成这样:

  

#! // don't link the Rust standard library#! // disable all Rust-level entry points#!use core::panic::PanicInfo;use bbl::sbi;global_asm!(include_str!("boot/entry.asm"));#fn panic(_info: &PanicInfo) -> ! { loop {}}static HELLO: & = b"Hello World!";#pub extern "C" fn rust_main() -> ! { for &c in HELLO { sbi::console_putchar(c as usize); } loop {}}#pub extern fn abort() { panic!("abort!");}#! 使得我们能够使用 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 virtbbl loaderHello World!以后若无特殊说明,编译运行的命令就是make run

  

预告最黑暗的日子已经过去,我们已经完成了一个可以正常运行的 最小内核 !下一章我们将在此基础上,实现 rust 中最经典的宏: println! ,以便于后续的调试输出。

相关文章