Skip to content

Latest commit

 

History

History
302 lines (223 loc) · 11.1 KB

asm.md

File metadata and controls

302 lines (223 loc) · 11.1 KB

stableでのアセンブリ

ここまで、デバイスの起動と割り込み処理とを、1行のアセンブリも書くことなくうまくやって来ました。 これはかなりの偉業です!しかし、ターゲットアーキテクチャ次第では、 ここまで到達するためにアセンブリが必要になるかもしれません。 他にも、コンテキストスイッチのようなアセンブリを必要とする操作があります。

問題は、インラインアセンブリ(asm!)も自由形式アセンブリ(global_asm!)もunstableなことです。 そして、これらがいつ安定化されるかは分かっていないため、stableでは使えません。 これから説明するように、いくつかのワークアラウンドがあるため、致命的な問題ではありません。

本セクションの動機付けとして、HardFaultハンドラを、 例外を発生させたスタックフレームの情報を提供するように修正します。

やりたいことは下記の通りです。

ベクタテーブルにユーザーがHardFaultハンドラを直接配置する代わりに、 rtクレートがユーザー定義のHardFaultをトランポリンするハンドラをベクタテーブルに配置します。

$ tail -n36 ../rt/src/lib.rs
{{#include ../ci/asm/rt/src/lib.rs:61:96}}

このトランポリンはスタックポインタを読んで、ユーザーのHardFaultハンドラを呼びます。 トランポリンはアセンブリで次のように書かなければなりません。

{{#include ../ci/asm/rt/asm.s:5:6}}

ARM ABIでは、このメインスタックポインタ(MSP; Main Stack Pointer)の設定は、HardFault関数/ルーチンの第一引数になります。 このMSPの値は、例外によってスタックにプッシュされたレジスタへのポインタです。 これらの変更により、ユーザーのHardFaultハンドラは、fn(&StackedRegisters) -> !というシグネチャを持たなければなりません。

.sファイル

stableでアセンブリを書く方法の1つは、アセンブリを外部ファイルに書くことです。

$ cat ../rt/asm.s
{{#include ../ci/asm/rt/asm.s}}

そして、rtクレートのビルドスクリプト内で、アセンブリファイルをオブジェクトファイル(.o)にアセンブルし、 アーカイブ(.a)にするために、ccクレートを使います。

$ cat ../rt/build.rs
{{#include ../ci/asm/rt/build.rs}}
$ tail -n2 ../rt/Cargo.toml
{{#include ../ci/asm/rt/Cargo.toml:7:8}}

これで全てです!

とても簡単なプログラムを書くだけで、ベクタテーブルがHardFaultTrampolineへのポインタを持つことが確認できます。

{{#include ../ci/asm/app/src/main.rs}}

逆アセンブリの結果は、以下の通りです。HardFaultTrampolineのアドレスを見て下さい。

$ cargo objdump --bin app --release -- -d -no-show-raw-insn -print-imm-hex
{{#include ../ci/asm/app/release.objdump}}

注記 この逆アセンブリ結果を小さくするために、RAMの初期化をコメントアウトしています。

ここで、ベクタテーブルを見ます。 4つ目のエントリは、HardFaultTrampolineに1を足したアドレスになっているはずです。

$ cargo objdump --bin app --release -- -s -j .vector_table
{{#include ../ci/asm/app/release.vector_table}}

.o / .aファイル

ccクレートを使う欠点は、ビルドマシンにアセンブラプログラムが必要なことです。 例えば、ARM Cortex-Mをターゲットにする時、ccクレートはアセンブラとしてarm-none-eabi-gccを使います。

ビルドマシン上でファイルをアセンブルする代わりに、rtクレートと一緒にあらかじめアセンブルしたファイルを配布できます。 この方法なら、ビルドマシンにアセンブラプログラムは必要ありません。 しかしながら、rtクレートをパッケージして発行するマシン上には、アセンブラが必要です。

アセンブリファイル(.s)と、コンパイルしたオブジェクトファイル(.o)とは、それほど違いがありません。 アセンブラは最適化を行いません。単純にターゲットアーキテクチャ向けに正しいオブジェクトファイル形式を選ぶだけです。

Cargoは、クレートとアーカイブ(.a)をまとめる機能を提供しています。arコマンドを使ってオブジェクトファイルをアーカイブにパッケージできます。 その後、アーカイブをクレートにまとめます。実は、これはccクレートが行っていることなのです。 ccクレートが呼び出しているコマンドは、targetディレクトリのoutputという名前のファイルを探すと見つかります。

$ grep running $(find target -name output)
running: "arm-none-eabi-gcc" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "-mthumb" "-march=armv7-m" "-Wall" "-Wextra" "-o" "/tmp/app/target/thumbv7m-none-eabi/debug/build/rt-6ee84e54724f2044/out/asm.o" "-c" "asm.s"
running: "ar" "crs" "/tmp/app/target/thumbv7m-none-eabi/debug/build/rt-6ee84e54724f2044/out/libasm.a" "/home/japaric/rust-embedded/embedonomicon/ci/asm/app/target/thumbv7m-none-eabi/debug/build/rt-6ee84e54724f2044/out/asm.o"
$ grep cargo $(find target -name output)
cargo:rustc-link-search=/tmp/app/target/thumbv7m-none-eabi/debug/build/rt-6ee84e54724f2044/out
cargo:rustc-link-lib=static=asm
cargo:rustc-link-search=native=/tmp/app/target/thumbv7m-none-eabi/debug/build/rt-6ee84e54724f2044/out

アーカイブを作成するために似たことを行います。

$ # `cc`が使う多くのフラグはアセンブル時には意味がないため、それらは取り除きます
$ arm-none-eabi-as -march=armv7-m asm.s -o asm.o

$ ar crs librt.a asm.o

$ arm-none-eabi-objdump -Cd librt.a
{{#include ../ci/asm/rt2/librt.objdump}}

次に、rt rlibにアーカイブをまとめるために、ビルドスクリプトを修正します。

$ cat ../rt/build.rs
{{#include ../ci/asm/rt2/build.rs}}

ここで、新バージョンが前のシンプルなプログラムと同じ出力をすることをテストできます。

$ cargo objdump --bin app --release -- -d -no-show-raw-insn -print-imm-hex
{{#include ../ci/asm/app2/release.objdump}}

注記 前回同様、逆アセンブリの結果を小さくするために、RAMの初期化をコメントアウトしています。

$ cargo objdump --bin app --release -- -s -j .vector_table
{{#include ../ci/asm/app2/release.vector_table}}

あらかじめアセンブルしたアーカイブを配布する欠点は、最悪の場合、 ライブラリがサポートするターゲットごとにビルド生成物を配布しないといけないことです。