最小限の#![no_std]プログラム

このセクションでは、コンパイルできる最小限の#![no_std]プログラムを書きます。

#![no_std]はどういう意味でしょうか?

#![no_std]は、クレートレベルのアトリビュートです。これは、このクレートにstdクレートではなくcoreクレートをリンクすることを示します。 しかし、アプリケーションにとって、これは何を意味するのでしょうか?

stdクレートはRustの標準ライブラリです。 標準ライブラリは、プログラムがベアメタルではなく、オペレーティングシステム上で動作することを仮定した機能を、含んでいます。 stdは、オペレーティングシステムが、サーバやデスクトップで使うような汎用オペレーティングシステムであることも仮定します。 この理由から、stdは、スレッド、ファイル、ソケット、ファイルシステム、プロセス、など汎用オペレーティングシステムにある機能に対して標準APIを提供します。

その一方、coreクレートは、stdクレートのサブセットで、プログラムが動作するシステムについて、一切の仮定を置きません。 そのため、coreクレートは、浮動小数点や文字列、スライスのような言語のプリミティブと、 アトミック操作やSIMD命令のようなプロセッサ機能を利用するためのAPIを提供します。 しかし、coreクレートは、ヒープメモリアロケーションやI/Oといったものに対するAPIがありません。

アプリケーションに対しては、stdは単に抽象化されたOS機能へのアクセス方法を提供するだけに留まりません。 stdは、とりわけ、スタックオーバーフロープロテクションの設定、コマンドライン引数の処理、 プログラムのmain関数が呼び出される前のメインスレッド生成、の面倒をみます。 #![no_std]アプリケーションは、これらの標準的なランタイムを持ちません。そのため、必要に応じて、自身のランタイムを初期化しなければなりません。

これらの性質から、#![no_std]アプリケーションは、システム上で動作する最初の / 唯一のコードになれます。 標準のRustアプリケーションでは決して作ることができない、次のようなプログラムを書くことができます。

  • OSのカーネル
  • ファームウェア
  • ブートローダ

コード

この普通でない方法で、コンパイル可能な最小限の#![no_std]プログラムに取り掛かることができます。

$ cargo new --edition 2018 --bin app

$ cd app
$ # main.rsを下記の内容に修正して下さい
$ cat src/main.rs

#![allow(unused)]
#![no_main]
#![no_std]

fn main() {
use core::panic::PanicInfo;

#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
    loop {}
}
}

このプログラムは、標準的なRustプログラムでは目にすることがない内容を含んでいます。

#![no_std]アトリビュートについては、既に十分に説明しました。

#![no_main]アトリビュートは、エントリポイントとして標準のmain関数を使わないプログラムであることを意味します。 本書を書いている時点では、Rustのmainインタフェースは、プログラムを実行する環境について、いくつかの仮定を置いています。 例えば、コマンドライン引数が存在していることですが、これは一般的に#![no_std]プログラムにはふさわしくありません。

#[panic_handler]アトリビュートでマーキングされた関数は、パニック発生時の動作を定義します。 ライブラリレベルのパニック(core::panic!)と言語レベルのパニック(範囲外のインデックスアクセス)両方が対象です。

このプログラムは、役に立つものではありません。実際、空のバイナリを生成します。

$ $ `size target/thumbv7m-none-eabi/debug/app`と同じです
$ cargo size --target thumbv7m-none-eabi --bin app
   text	   data	    bss	    dec	    hex	filename
      0	      0	      0	      0	      0	app

リンク前、このクレートはパニックのシンボルを含んでいます。

$ cargo rustc --target thumbv7m-none-eabi -- --emit=obj

$ cargo nm -- target/thumbv7m-none-eabi/debug/deps/app-*.o | grep '[0-9]* [^n] '
00000000 T rust_begin_unwind

しかしながら、これがスタート地点です。次のセクションでは、役に立つものをビルドします。 しかしその前に、Cargo呼び出しごとに--targetフラグを付けなくて良いように、デフォルトビルドターゲットを設定しましょう。

$ mkdir .cargo

$ # .cargo/configが下記内容になるように修正します
$ cat .cargo/config
[build]
target = "thumbv7m-none-eabi"