RustでHello, World!

環境構築

実験用のOSはUbuntu 20.10、x86_64を想定します。

まず、Rustの環境を整えます。Rustの環境はrustupで構築できるので簡単です。Rustには大きく分けてstableとnightlyがあるのですが、今回はnightlyを利用します。具体的には以下のようにコマンドを入力するとインストールできます。

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
  sh -s -- --default-toolchain nightly-2021-03-06 \
  --component clippy rust-src llvm-tools-preview rustfmt rls rust-analysis rustdoc -y

インストール後はenvファイルを読み込んで環境変数を設定します。

$ source $HOME/.cargo/env

次に、必要なソフトウェアをaptからインストールします。

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install build-essential zsh git curl libncurses5-dev libtinfo5 clang lld fakeroot xz-utils libssl-dev bc flex libelf-dev bison llvm cpio qemu-system

Linuxカーネルコンパイル

次に、Linuxカーネルソースコードを取得します。今回は、linux-nextという次世代Linux向けの実験的なソースコードを使います。

$ git clone --depth 1 git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git

続いて、Linuxカーネルコンフィグを設定します。

$ cd linux-next
$ make menuconfig

設定項目は、以下の通りです。General setup ---> Rust support ---> yesと、Device Drivers ---> Character devices ---> Rust Example ---> yesは必ずオンにしておきましょう。他の項目は、以下にチェックがあれば問題ないと思います。様々なオプションをオンにしたままだと、コンパイルに時間がかかるので最小オプションにしておきましょう。

64-bit kernel ---> yes
General setup ---> Rust support ---> yes
General setup ---> Initial RAM filesystem and RAM disk (initramfs/initrd) support ---> yes
General setup ---> Configure standard kernel features ---> Enable support for printk ---> yes
Executable file formats / Emulations ---> Kernel support for ELF binaries ---> yes
Executable file formats / Emulations ---> Kernel support for scripts starting with #! ---> yes
Device Drivers ---> Generic Driver Options ---> Maintain a devtmpfs filesystem to mount at /dev ---> yes
Device Drivers ---> Generic Driver Options ---> Automount devtmpfs at /dev, after the kernel mounted the rootfs ---> yes
Device Drivers ---> Character devices ---> Rust Example ---> yes
Device Drivers ---> Character devices ---> Enable TTY ---> yes
Device Drivers ---> Character devices ---> Serial drivers ---> 8250/16550 and compatible serial support ---> yes
Device Drivers ---> Character devices ---> Serial drivers ---> Console on 8250/16550 and compatible serial port ---> yes
File systems ---> Pseudo filesystems ---> /proc file system support ---> yes
File systems ---> Pseudo filesystems ---> sysfs file system support ---> yes

つづいて、Hello Worldを出力するようにします。

./drivers/char/rust_example.rsを修正して、Hello Worldを出力するように修正します。

$ cat ./drivers/char/rust_example.rs

修正箇所は、FileOperationsの実装にTO_USEを設定し、read関数を追加します。read関数内でHello, World!を出力するようにします。具体的な修正箇所は以下の通りです。そのほかの部分は修正しなくても大丈夫です。

use kernel::{
    chrdev, condvar_init, cstr,
    file_operations::{FileOperations, File, ToUse},
    user_ptr::UserSlicePtrWriter,
    miscdev, mutex_init, spinlock_init,
    sync::{CondVar, Mutex, SpinLock},
};


impl FileOperations for RustFile {
    type Wrapper = Box<Self>;

    const TO_USE: ToUse = ToUse {
        read: true, // read関数を有効化
        write: false,
        seek: false,
        ioctl: false,
        compat_ioctl: false,
        fsync: false,
    };

    fn read(&self, file: &File, data: &mut UserSlicePtrWriter, _offset: u64) -> KernelResult { // read関数を定義
        println!("rust file: read, pos = {}", file.pos());
        if file.pos() == 0 {
            let hello = b"Hello, World!\n";
            data.write_slice(hello)?;
            Ok(())
        } else {
            Ok(())
        }
    }

    fn open() -> KernelResult<Self::Wrapper> {
        println!("rust file was opened!");
        Ok(Box::try_new(Self)?)
    }
}

コンパイルします。

$ make -j 8

busyboxコンパイル

次にbusyboxソースコードを取得します。

$ git clone git://busybox.net/busybox.git

設定します。

$ cd busybox
$ make defconfig
$ make menuconfig

設定では以下のように静的リンクのオプションをチェックします。他の設定は触らなくて大丈夫です。

Settings ---> Build Options ---> Build static binary (no shared libs) ---> yes

コンパイルしinitial RAMディスクを作成します。initial RAMディスクはlinux-nextにおいておきましょう。

$ make -j 8
$ make install
$ cd _install
$ mkdir dev proc sys
$ find . | cpio --quiet -H newc -o | gzip > ../../linux-next/initrd.img

Qemuで実行

最後に仕上げとして、Qemuで実行します。

$ cd linux-next
$ qemu-system-x86_64 -m 128M -kernel arch/x86_64/boot/bzImage -append "console=ttyS0 rdinit=/bin/sh root=/dev/ram0" -initrd initrd.img -nographic

QemuLinux起動後は、devtmpfsをマウントします。

# mount -t devtmpfs devtmpfs /dev

最後に、/dev/rust_miscdevファイルをcatして、Hello, World!を出力します。

# cat /dev/rust_miscdev
[ 4362.809581] rust file was opened!
[ 4362.809787] rust file: read, pos = 0
Hello, World!
[ 4362.810056] rust file: read, pos = 14

素晴らしい!Rustで無事にHello, World!を出力することが出来ました。