[TOC]
-
New a Rust project with the following commands.
root@iZuf6hkk04t6lezxufufk6Z:~# cd os
-
Build the created Rust project.
root@iZuf6hkk04t6lezxufufk6Z:~# cargo build
-
Run the Rust project.
root@iZuf6hkk04t6lezxufufk6Z:~# cargo run
remove the dependencies of standard libs.
Modify target to riscv64. Add build config to the configuration file.
Modify file "main.rs". Delete main function and add some codes.
// main.rs
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
In the end, analyze the independent executable program.
We can find out that the binary generated by the compilation was found to be an empty program because the compiler could not find the entry function, so no subsequent code was generated.
root@iZuf6hkk04t6lezxufufk6Z:~# file target/riscv64gc-unknown-none-elf/debug/os
root@iZuf6hkk04t6lezxufufk6Z:~# rust-readobj -h target/riscv64gc-unknown-none-elf/debug/os
root@iZuf6hkk04t6lezxufufk6Z:~# rust-objdump -S target/riscv64gc-unknown-none-elf/debug/os
Add the following codes to "main.rs". Then run the program.
A Segmentation fault occurs. This is because the program currently lacks a proper exit mechanism.
#[no_mangle]
extern "C" fn _start() {
loop{};
}
To implement the exit mechanism, we need to add the following codes to "main.rs".
#![feature(asm)]
const SYSCALL_EXIT: usize = 93;
fn syscall(id: usize, args: [usize; 3]) -> isize {
let mut ret: isize;
unsafe {
asm!("ecall",
in("x10") args[0],
in("x11") args[1],
in("x12") args[2],
in("x17") id,
lateout("x10") ret
);
}
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);
}
-
First, encapsulate the SYSCALL_WRITE system call. This is a system call provided by the Linux operating system kernel with the ID SYSCALL_WRITE.
const SYSCALL_WRITE: usize = 64; pub fn sys_write(fd: usize, buffer: &[u8]) -> isize { syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()]) }
-
Then, implement the data structure based on Write Trait, complete the write_str function required for Write Trait, and wrap it with the print function.
struct Stdout; impl Write for Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { sys_write(1, s.as_bytes()); Ok(()) } } pub fn print(args: fmt::Arguments) { Stdout.write_fmt(args).unwrap(); }
-
Finally, implement Rust language formatting macros based on print function.
use core::fmt::{self, Write}; #[macro_export] macro_rules! print { ($fmt: literal $(, $($arg: tt)+)?) => { $crate::console::print(format_args!($fmt $(, $($arg)+)?)); } } #[macro_export] macro_rules! println { ($fmt: literal $(, $($arg: tt)+)?) => { print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); } }