C
Rust
OS

xv6のechoコマンドをRustで書き換える

xv6はCで書かれたuniuxライクなミニマムOSです。
オリジナルechoはこちらのシンプルなコードです。

echo.c
#include "types.h"
#include "stat.h"
#include "user.h"

int
main(int argc, char *argv[])
{
  int i;

  for(i = 1; i < argc; i++)
    printf(1, "%s%s", argv[i], i+1 < argc ? " " : "\n");
  exit();
}

これをRustで書き換えます。

echo.rs
#![no_std]
#![feature(lang_items)]
#![no_main]

extern "C" {
    fn exit() -> i32;
    fn printf(fd: i32, fmt: *const u8, ...);
}

#[no_mangle]
pub extern "C" fn main(argc: i32, argv: *const *const u8) -> i32 {
    for i in 1..(argc as isize) {
        unsafe {
            printf(
                1,
                "%s%s\0".as_ptr(), // Cの文字列は\0 (null char)終端
                *argv.offset(i),   // deref必須
                if i + 1 < argc as isize { " \0" } else { "\n\0" }.as_ptr(),
            )
        };
    }
    unsafe { exit() }
}

#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
#[lang = "panic_fmt"]
extern "C" fn panic_fmt() -> ! { loop {} }

(※@tatsuya6502さんの修正を反映)

このプログラムはfreestandingなので、#[no_std]を指定します。
Cのexit(),printf()を呼びたいのでexternで指定します。
Cからも呼び出せるように#[no_main]と#[no_mangle]を指定して、pub externでmain関数を作成します。

MakefileにRustのコンパイルとリンクを追加します。

_echo: echo.rs
    rustc --target=i686-unknown-linux-gnu --emit=obj echo.rs
    ld -m    elf_i386 -N -e main -Ttext 0 -o _echo echo.o ulib.o usys.o printf.o umalloc.o

qemuで実行してみます。

$ echo a
a

参考

Introduction Rust for Creating Your Operating System
標準ライブラリーの不使用