rCore1

如何将程序移动到自己的平台上执行

程序执行的 目标平台三元组(CPU,操作系统,运行时库)

bash 复制代码
$ rustc --version --verbose
   rustc 1.61.0-nightly (68369a041 2022-02-22)
   binary: rustc
   commit-hash: 68369a041cea809a87e5bd80701da90e0e0a4799
   commit-date: 2022-02-22
   host: x86_64-unknown-linux-gnu
   release: 1.61.0-nightly
   LLVM version: 14.0.0

键入如上指令,CPU:X86_64,操作系统:linux,运行时库:gnu

复制代码
接下来,我们希望把 Hello, world! 移植到 RICV 目标平台 riscv64gc-unknown-none-elf 上运行。
  1. riscv64gc-unknown-none-elf 指无操作系统,无运行时库,但能生成elf文件
  2. rust基本上只有core库不依赖gnu libc。

移除标准库依赖(使cargo去新的平台编译)

rust 复制代码
# os/.cargo/config
[build]
target = "riscv64gc-unknown-none-elf"
  1. 创建config后,此时在os文件夹下编译即使用如上参数。
  2. 此时pirntln!无法使用
bash 复制代码
$ cargo build
   Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error: cannot find macro `println` in this scope
--> src/main.rs:4:5
  |
4 |     println!("Hello, world!");
  |     ^^^^^^^
  1. panic处理无法使用。
bash 复制代码
$ cargo build
   Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error: `#[panic_handler]` function required, but not found

构建裸机执行环境

panic处理

rust 复制代码
// os/src/lang_items.rs
use core::panic::PanicInfo;

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}
  1. 标记panic_handler属性,告知编译器使用该函数作为panic处理。

告知函数执行入口(去除main函数)

  • 此时有报错
bash 复制代码
$ cargo build
   Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error: requires `start` lang_item

缺少#[lang = "start"]

一个rust程序正常的执行流(by deepseek)

  • 以下是标准库std的处理
rust 复制代码
// 在 libstd 中大致是这样的(简化版)
#[lang = "start"]
fn start<T: Termination>(
    main: fn() -> T,
    argc: isize, 
    argv: *const *const u8,
    _sigpipe: u8,
) -> isize {
    // 1. 初始化Rust运行时
    init_runtime();
    
    // 2. 设置堆栈保护
    setup_stack_guard();
    
    // 3. 初始化线程局部存储(TLS)
    init_thread_local_storage();
    
    // 4. 设置panic处理
    setup_panic_handler();
    
    // 5. 处理命令行参数
    let args = parse_args(argc, argv);
    
    // 6. 调用main函数
    let result = main();
    
    // 7. 处理退出码
    result.report()
}
  • lang是language items的缩写,#[lang = "start"]意为将接下来的处理作为start语义项来处理。

最小化用户态执行环境

无操作系统程序执行流(引导加载程序bootloader)

  • Bootloader → 你的 _start 函数 → 代码------退出机制
  • 注:这是最小的执行环境

_start(),声明函数入口位置

rust 复制代码
// os/src/main.rs
#[no_mangle]
extern "C" fn _start() {
    loop{};
}
  • extern c可以保证在链接后函数名为原函数名,而不做任何重命名(rust常规会做一些重命名)。

退出机制:第一个系统调用

rust 复制代码
// os/src/main.rs

const SYSCALL_EXIT: usize = 93;

fn syscall(id: usize, args: [usize; 3]) -> isize {
    let mut ret;
    unsafe {
        core::arch::asm!(
            "ecall",
            inlateout("x10") args[0] => ret,
            in("x11") args[1],
            in("x12") args[2],
            in("x17") id,
        );
    }
    ret
}

pub fn sys_exit(xstate: i32) -> isize {
    syscall(SYSCALL_EXIT, [xstate as usize, 0, 0])
}

#[no_mangle]
extern "C" fn _start() {
    sys_exit(9);
}

第二个系统调用:输出字符串

  • 以下是AI生成的注释和system_wirte
rust 复制代码
// 执行 write 系统调用
fn sys_write(fd: i32, buffer: &[u8]) -> isize {
    let ptr = buffer.as_ptr();
    let len = buffer.len();
    let result: isize;
    
    unsafe {
        asm!(
            "syscall",                // 系统调用指令
            in("rax") 1,             // 系统调用号:write = 1
            in("rdi") fd,            // 第一个参数:文件描述符
            in("rsi") ptr,           // 第二个参数:缓冲区指针
            in("rdx") len,           // 第三个参数:缓冲区长度
            out("rcx") _,            // 被 syscall 修改的寄存器
            out("r11") _,            // 被 syscall 修改的寄存器  
            lateout("rax") result,   // 返回值(系统调用完成后)
            options(nostack, preserves_flags)
        );
    }
    
    result
}
  • rCore手册中对其进一步使用宏进行了封装

构建裸机执行环境(操作系统执行环境)

使用ecall退出(ecall可以让用户态切换到内核态)

加载

ld 复制代码
OUTPUT_ARCH(riscv)
ENTRY(_start)
BASE_ADDRESS = 0x80200000;

SECTIONS
{
    . = BASE_ADDRESS;   // . 为临时变量
    skernel = .;

    stext = .;
    .text : {
        *(.text.entry)
        *(.text .text.*)
    }

    . = ALIGN(4K);
    /*含义: . = ALIGN(4K)把当前位置指针.向上对齐到最近的4KB边界(4096字节)。
        数值公式: 对齐后的.等于(. + 4096 - 1) & ~(4096 - 1),也就是把当前值四舍五入到下一个4096的倍数。
        具体值: 取决于前面已放入段的大小。比如
        若 .text 结束时.为0x80203456,对齐后变为0x80204000。
        若刚好在边界,如.为0x80204000,对齐结果仍是0x80204000。*/
    etext = .;
    srodata = .;
    .rodata : {
        *(.rodata .rodata.*)
        *(.srodata .srodata.*)
    }   // 将文件中所有该段合并到.rodata,并将rodata置于此。
    //写下.rodata证明.rodata数据放在这里。
    . = ALIGN(4K);
    erodata = .;
    sdata = .;
    .data : {
        *(.data .data.*)
        *(.sdata .sdata.*)
    }

    . = ALIGN(4K);
    edata = .;
    .bss : {
        *(.bss.stack)
        sbss = .;
        *(.bss .bss.*)
        *(.sbss .sbss.*)
    }

    . = ALIGN(4K);
    ebss = .;
    ekernel = .;

    /DISCARD/ : {
        *(.eh_frame)
    }
}
相关推荐
DongLi012 天前
rustlings 学习笔记 -- exercises/05_vecs
rust
番茄灭世神3 天前
Rust学习笔记第2篇
rust·编程语言
shimly1234563 天前
(done) 速通 rustlings(20) 错误处理1 --- 不涉及Traits
rust
shimly1234563 天前
(done) 速通 rustlings(19) Option
rust
@atweiwei3 天前
rust所有权机制详解
开发语言·数据结构·后端·rust·内存·所有权
shimly1234563 天前
(done) 速通 rustlings(24) 错误处理2 --- 涉及Traits
rust
shimly1234563 天前
(done) 速通 rustlings(23) 特性 Traits
rust
shimly1234563 天前
(done) 速通 rustlings(17) 哈希表
rust
shimly1234563 天前
(done) 速通 rustlings(15) 字符串
rust
shimly1234563 天前
(done) 速通 rustlings(22) 泛型
rust