最小限の#![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"