技書博出展レポート〜初めての同人誌執筆で組込みRustの本を頒布しました〜
はじめに
記憶の新しいうちに、経緯などをまとめておきます。 自分用メモの側面が強いですが、今後初めて同人誌を頒布される方の参考になると嬉しいです。
BOOTHで物理本と電子版を販売しているので、もし良ければお買い求め下さい。 (物理本は倉庫への搬送作業は完了しており、入荷待ちです)
経緯
4月下旬、@hidemi_ishihara さんから出展のお誘いがありました。
組込みRustで書くことに決めて、今まで翻訳した組込みRustのドキュメントや、自分でこれまでやってきたことをクックブックとしてまとめることにしました。 クックブックにしたのは理由があり、急に執筆ができなくなっても読める内容になっている形式で執筆を進めたかったからです。 第二子の出産予定日が6月初旬だったため、この判断は良かったと思います。
性格的にギリギリにやるのは性分ではないこともあり、手持ちで書籍化できそうなものが、組込みRustかZephyrしかなかった、というのもありますが。 (Zephyrはそこまで思い入れないですからねぇ…)
執筆の進行
gitの履歴を見てみると、4/29にレポジトリを作っていました。 2週間後の5月中盤には、頒布した内容の半分は書けています。 この頃は、平日は1時間前後、休日は2〜3時間執筆していました。
ここまでは順調でしたが、まさかの第二子が3週間早く産まれてきてしまう、というハプニングに見舞われます! ガクッと執筆ペースが落ちて、残り半分の内容を埋めるのに6週間ほどかかっています。 この頃は、平日30分時間が取れれば良い方で、休日も2時間執筆できれば万々歳でした…。
7月に入り、組版作業に入ることを決めました。 この時点で、まだ書きたかった内容を全て切り捨てました。
初めてなので、どのくらい製本すれば良いかわかりませんでした。 とりあえずtwitterランドの住人に聞いたろ、ということで、聞いてみると、100イイねついたので、100冊刷ることにしました。
組込み / ベアメタルRustクックブック
— 錆ありはぐれベアメタル (@LDScell) June 29, 2019
目次はこんな感じで80ページくらい。
わからんけど、紙媒体1000円くらい?
初同人誌なのでクオリティはお察し。
7/27(土)技術書博覧会で販売予定。
買ってあげても良いよ、という方はイイね下さい。
印刷部数の参考にします。 pic.twitter.com/5U3NEQhMD3
製本をどこに頼もうか調べていたところ、@hidemi_ishihara さんから、いつもポプルスさんで印刷している、という情報を得ました。 調べる時間がもったいないので、「じゃあ、そこで!」ということにして、早速アカウント作って見積もりと製本予約をしました。
ということで、組版作業を都合2週間ほどやっていました。
こんなに時間がかかったのは少し理由があって、mdbook
というRust製のドキュメントビルダーで原稿を執筆していました。
mdbook
には未完成品ですがEPUB形式で出力する機能があります。
mdbook
から出力されたEPUB形式の原稿を、calibreという電子書籍エディタで編集していました。
慣れないCSSをいじったり、なぜかPDF出力するときにコードブロックの強調が消えてしまうバグと不毛な争いをしていました。 ということで、2週間前に組版を始めたのも、良い判断でした。
表紙作成もこの辺りの期間にやりました。 割と面倒で、時間かかりました。
7/12には入稿を済ませて、一段落つきました。
当日まで
不安しかねぇ!
というのも、初めての同人誌執筆(厳密にはD論という名の同人誌製本していますが)で会場直接搬入なので、実物が読めたものになっているかどうか、わかりません!
当初より今回の本は、組込みRustの知名度向上のため、価格を安くしてばら撒く作戦でした。
さらにmdbook
は静的なページを作るツールなので、HTML版も合わせて配れば、安いし誰も怒らないでしょ!みたいな開き直りをすることで、心の安寧を保ちました。
その裏で、名刺を自炊したり、ダウンロードカード作ったりしていました。
後、twitterやブログで必死の宣伝活動していました。 どこかの記事で、技書博は集客1000人を目指している、と聞いて、来場者の10%もこんなニッチな本を買うわけがない!という焦りがありましたね。
値段は500円にしたので、とりあえず500円玉を30枚、お釣りとして用意しました。
当日
とにかく本のできを見て、一安心しました。 これなら500円でも怒られはしないでしょ!というクオリティになっていました。 少し上下左右の余白取りすぎた気がしますが、文字が詰まっている圧迫感も感じないので、悪くない気がします。 読者の皆様からの感想をお待ちしております。
@hidemi_ishihara さんに導かれるまま、見本誌にカバーかけたり、スペースの準備をしました。 カッターナイフも何度かお借りしました。カッターナイフ、意外と要るで?
午前中は、全然売れなかったです! 11時〜12時の間の売上は3冊でした。 内心すごい焦燥感に駆られていました。
イベント自体は、ゆっくりスペースを回れて、著者とお話しする余裕が十分にあるので、良い感じだなぁ、と思いました。 物が売れない焦燥感を除けば!
13時以降、徐々に売上が伸びていき、14時〜15時くらいの間に20冊近く販売できました。 最終的には、53冊頒布しました。
一般入場者が640名ほどとのことなので、このニッチなジャンルで53冊はだいぶ頑張った方ではないでしょうか笑 ゆっくり見れたり、話した結果ご購入下さった方もいらっしゃったので、購入する側としても満足度が高かったのかもしれません。
中には、「Rustはやったことないのだけど、気になるし、安いから買います(意訳)」という方や「Rust勉強してからまた来ます」、と言って下さった方も複数名いらっしゃったので、狙いは良かったと思います。
頒布時間終了後、40冊はBOOTHさんに入庫することにしました。 スーツケース持ってきておけば、持って帰って技術書典7で頒布できたなぁ、と思いましたが、後の祭りですね。 荷物軽くするために、リュック1つで行ったのが間違いでした…。
技術書典7では、また50冊くらい刷ることにします。
お金の話
100冊製本して、約37,500円でした。 ばらまきたいので、1冊500円で頒布することにしました。
現状、BOOTHでの売上も含めて、なんとか損益分岐点に到達しました! お買上げ、ありがとうございます! Boostまでして下さる方もいらっしゃって、非常にありがたいことです。
今回は、赤字にさえならなければ勝ちなので、満足です!
今後について
HTML版は、適宜更新していこうと考えています。 時間不足でバッサリ切ってしまった部分が残っているので、そちらの加筆も行う予定です。 組版はぼちぼち手間がかかるので、PDF版および紙版の更新は余裕があれば、やります。
読者の皆様からフィードバックがあれば、加筆修正する大きなモチベーションになるため、フィードバックをお待ちしております。
7/27(土) 技書博で組込み/ベアメタルRustクックブックを販売します!
はじめに
宣伝です!
来週開催される技術書同人誌博覧会にて、組込み/ベアメタルRustクックブックを販売します。 A-9 AQUAXISさんのブースにご一緒させて頂きます。 ブース主の石原ひでみさんはFPGAの薄い本とMarkdown組版の本を、みつきんさんはNuttxの本を頒布されます。
販売情報
価格は、500円です。 PDF版とHTML版がデフォルトで含まれます。 当日の会場にてお買い上げいただくと、先着で100名様に、紙媒体をお渡しします。 1冊のご購入につき、紙媒体を1冊まで、お渡しします。
当日お越し頂けなかった方にも、後日何らかの形でPDF版とHTML版が入手できる手段を用意します。
目次は、次の通りで、表紙込で78ページです。
- はじめに
- 環境構築
- ベアメタルテクニック
- no_std
- panic
- print!マクロ
- リンカ
- アセンブリ
- メモリアロケータ
- entryポイント
- ツール
- Cargo
- コンパイラサポート
- rustc
- ライブラリ / フレームワーク
- heapless
- no_stdクレート
- svd2rust
- RTFM
- Tock
- testing
- FFI
- RustからCを呼ぶ
- CからRustを呼ぶ
- ケーススタディ Zephyr
- 組込みLinux
- ビルド/テスト
- Yocto
- あとがき
正直なところ、HTML版が一番見やすいので、HTML版に500円払う気持ちで購入して頂けると嬉しいです。
ところで
100イイねついたから、100部刷ったので、買って下さいね!(切実)
組込み / ベアメタルRustクックブック
— 錆ありはぐれベアメタル (@LDScell) June 29, 2019
目次はこんな感じで80ページくらい。
わからんけど、紙媒体1000円くらい?
初同人誌なのでクオリティはお察し。
7/27(土)技術書博覧会で販売予定。
買ってあげても良いよ、という方はイイね下さい。
印刷部数の参考にします。 pic.twitter.com/5U3NEQhMD3
LLVM IRファイルを読み込む
はじめに
ファイルに出力されたLLVM IRをC++で読み込んで遊んでみます。
環境
$ clang++ --version clang version 8.0.1-svn360950-1~exp1~20190517004233.70 (branches/release_80) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin
$ llvm-config --cxxflags --ldflags --system-libs --libs core -I/usr/lib/llvm-8/include -std=c++11 -fno-exceptions -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -L/usr/lib/llvm-8/lib -lLLVM-8
LLVM IRファイルの読み込み
アプリケーションの引数にLLVM IRファイルパスを指定する想定です。
#include <llvm/IR/LLVMContext.h> #include <llvm/IR/Module.h> #include <llvm/IRReader/IRReader.h> #include <llvm/Support/SourceMgr.h> int main(int argc, char** argv) { if (argc < 2) { llvm::errs() << "Expected an argument - IR file name\n"; exit(1); } llvm::LLVMContext Context; llvm::SMDiagnostic Err; auto module = llvm::parseIRFile(argv[1], Err, Context); if (!module) { Err.print(argv[0], llvm::errs()); return 1; } return 0; }
llvm::parseIRFile
に、ファイルパスを与えるだけで、llvm::Module
(のunique_ptr) を得ることができます。
下のコマンドでLLVM IRファイルが読み込めます。
clang++ main.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core` -o ir_reader ./ir_reader main.ll
少し内部を覗く
main.ll
にはmain
関数があり、いくつか命令があります。
そのオペコードを表示してみます。
次のようなコードで実現できました。
auto func = module->getFunction("main"); std::cout << "function name: " << func->getName().data() << std::endl; for (auto &bb: *func) { auto i = 0; for (auto &instr: bb) { std::cout << "%" << i << ": " << instr.getOpcodeName() << std::endl; i++; } }
$ ./ir_reader main.ll function name: main %0: alloca %1: bitcast %2: call %3: getelementptr %4: load %5: call %6: ret
お手軽ですね!
Rust × 組込みで前代未聞のInterfaceオフ会レポート
はじめに
昨日、2019年6月17日、巣鴨のCQ出版社様セミナールームにおいて記念すべき組込みRustのオフ会が開催されました! 今回のオフ会は、雑誌掲載前にオフ会を開催する、という前代未聞のオフ会、とのことでした。
非常に盛り上がったオフ会になったので、そのレポートです。
プレゼン
1. 組込みRustのすゝめ
— 錆ありはぐれベアメタル (@LDScell) 2019年6月17日
2. PC上でRustドライバ開発
3. Rustで組込みOS自作
4. モダンマイコンOS Nuttxでlibstdを動かす#Ifcqmeetup
さらに、LTとして2つの発表がありました。
- M5StackでRust
- Rustファームウェア搭載の自作キーボード
組込みRustのすゝめ
まず、私から導入のお話をさせていただきました。
最初に会場内でアンケートを取ったところ、
- まだRustをやったことがない、が半分ほど
- 入門はした、はまさの0人
- 組込み以外でRustをやっている、が2割弱
- 組込みRustをやっている、が3割弱
といった感じでした。 知らない人か、プロしかいない!という両極端な結果が印象的でした。
導入と言いつつ、所有権システムの説明を少し入れており、ここの部分はRust知らない方々にどの程度理解して頂けたか、が気になっています。
Rustでチョット気軽にセンサドライバ開発
@ryochack さんからホストPC上で組込みセンサドライバ開発を行う発表です。
「いつまで僕らはC/C++を使い続けなければならないのか…」
全くその通りです!
発表内容は、下のデバイスを使って、PC上で直接Rustのセンサドライバを開発し、マイコンに持っていけるようにしよう!というものでした。
MPSSEのOSS実装、libmpsseからbindgenを使って、Rustバインディングを生成しています。 その時のラッパ作成の苦労話が、涙なしでは語れません。
お行儀の悪いCのコード!
— 錆ありはぐれベアメタル (@LDScell) 2019年6月17日
ちゃんとconstを付けないとbindingを作りにくので、Cのコードもちゃんと作ろう!#Ifcqmeetup
デモされていたもののレポジトリは、こちらです。
Rustで始める自作組込みOS
@garasubo さんから、組込みOSを自作したお話しです。
RustでOSを作る際の良かった話や、苦労話が生々しい発表でした! ライフタイムやテストフレームワークなど、良かった点もありますが、データ構造の実装が難しい、クレートが不足している、という課題もあります。
自作OSをRustで開発すべきか
— 錆ありはぐれベアメタル (@LDScell) 2019年6月17日
メリットは大きいが、発展途上なので、リスクも大きい。#Ifcqmeetup
自作OSのレポジトリは、こちらです。
Nuttxでlibstdを動かす
杉野さんから、POSIX likeな組込みOSであるNuttxでlibstdを動かした発表です。 今のところ、資料公開はされていません。
茨の道をひたすら突き進むような発表を前に、参加者一同、「これはツラすぎないか…」みたいな空気になっていましたが、ある程度動くようになっていて、圧巻の内容でした!
Rustの安全性や利便性を最大限享受するためにlibstdを使いたい、というのは組込みRustやっている人が共通で持っている望みだと思います。 リンカのバグを踏んだり、OS側にAPIが不足していたり、OS側とRust側とで構造体のメンバが違ったり、と数々の難関をくぐり抜け、動いた先は--。
M5 StackをRustで動かすまで
@ciniml さんから、ESP32上でRustを動かすようにするまでに試したことの発表です。
www.slideshare.net
Rust (というかLLVM) は公式にXtensaに対応していないため、LLVMのforkを使ってrustcのXtensa対応を行われていました。 ESP-IDF側のmalloc/freeをGlobalAllocでラップして、libcoreの機能を使えるようにしていました。
@ryochackさんと同様に、bindgenを使用されており、その苦労話もありました。
embedded_graphicsクレートを使って、画像を描画することまでできていて、次はWi-Fiを動かしたい、とのことです!
Rustで書いたファームウェアが乗った自作キーボードのデモ
@KOBA789 さんからキーボード自作のインターン向けに作成したRustファームウェア搭載の自作キーボードについてデモがありました。
【オフ会】
— コンピュータ技術実験雑誌「Interface」(毎月25日発売) (@If_CQ) June 17, 2019
デモ:Rustで自作キーボードのデモ
クックパッド開発者ブログってところで
クックMyパッド的に紹介されているらしい#ifcqmeetup pic.twitter.com/q0ulROP6iI
会社での無茶ぶり?から産まれたようです。 ステートマシン実装時、ステートが移るときにデータを引き継ぐところに苦労された、ということでした。
懇親会
組込みRustをやっている者同士のぶっちゃけトークが楽しかったです。
身代わりパターンめっちゃ使うよね〜、とかunsafe
使いたくないんだけど、やりたいことができなくてツラい、などなど。
組込みRustでまだまだ不足している部分や、課題もありますが、やらないといつまで経っても使えるようにならないから、やる!という認識が共有されたのも、個人的には非常に嬉しかったです。
まだRustを触っていない方々から、実際のところどーなの?的な質問にも正直な回答をさせていただきました。
告知
まさかの記事やってないのに
— コンピュータ実験雑誌の編集ラームー (@if_CQ_naka) 2019年6月17日
「ポインタが使えるモダンC++風言語Rust×組み込みの研究」
のオフ会やっちゃったので
逆順で特集執筆者会議やります
興味がある方はご連絡ください
あと新たな試みなので至らないところがあったら許してください#ifcqmeetup pic.twitter.com/WjUsqKWYoM
WebAssembly Micro RuntimeでRustアプリをマイコンで動かす!
はじめに
前回、もう一歩のところだったのですが、RAMが2MB搭載されていないと動かない状態でした。
tomo-wait-for-it-yuki.hatenablog.com
私はそんなマイコン持っていないため、今回は、256KB RAMが搭載されているマイコン (これも高性能品ですが) 上でRustアプリを動作させます。
問題点
データセグメントが0x100000番地に配置されていることが問題でした。
$ wasm-objdump -x target/wasm32-unknown-unknown/release/hello_wasm.wasm ... Data[1]: - segment[0] size=16 - init i32=1048576 - 0100000: 4865 6c6c 6f20 6672 6f6d 2052 7573 7400 Hello from Rust.
リンカオプションによる解決
調べていると、次のissueに行き当たりました。
The large memory size here is because we default to asking LLD to allocate a 1MB stack for all Rust binaries. If you'd like to reduce that though you can pass -C link-arg=-zstack-size=16 to rustc, for an appropriate stack size for your application. That should allow you to shrink quite a bit!
LLDにスタックを1MB割り当てるようなデフォルトになっており、このサイズを減らせば良い、ということでした。
ということで、リンカにオプションを与えます。
|> .cargo/config
[target.wasm32-unknown-unknown] rustflags = [ "-C", "link-arg=-zstack-size=16" ]
ビルドします。
$ cargo build --target=wasm32-unknown-unknown --release $ wasm-strip target/wasm32-unknown-unknown/release/hello_wasm.wasm
中身を確認してみましょう。
$ wasm-objdump -x target/wasm32-unknown-unknown/release/hello_wasm.wasm hello_wasm.wasm: file format wasm 0x1 Section Details: Type[3]: - type[0] (i32, i32) -> i32 - type[1] () -> nil - type[2] () -> i32 ... Code[2]: - func[1] size=2 - func[2] size=16 <main> Data[1]: - segment[0] size=16 - init i32=16 - 0000010: 4865 6c6c 6f20 6672 6f6d 2052 7573 7400 Hello from Rust.
これでデータセグメントが0x10 (16) から置かれるようになりました。
QEMUでの動作確認
実機で試す前に、QEMUを使って、RAM 64KB搭載のCortex-M3をターゲットにして、動作確認します。 (無理矢理動かすために、WebAssembly Micro Runtimeのページサイズを4KBにしています)
$ ninja run [0/1] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3 qemu-system-arm: warning: nic stellaris_enet.0 has no peer ***** Booting Zephyr OS zephyr-v1.14.0 ***** Hello from Rust
よし!
実機動作
手元に偶然あるnRF52840-DKで動かしてみます。 RAMも256KBあるので、なんとかなるはずです。
$ mkdir nrf52840 && cd $_ $ cmake -GNinja -DBOARD=nrf52840_pca10056 .. $ ninja [10/14] Linking C executable zephyr/zephyr_prebuilt.elf Memory region Used Size Region Size %age Used FLASH: 76436 B 1 MB 7.29% SRAM: 144328 B 256 KB 55.06% IDT_LIST: 56 B 2 KB 2.73% [14/14] Linking C executable zephyr/zephyr.elf
ビルドはOKです。
minicomでUARTを受信できるようにして、ファームウェアを書き込みます。
ninja flash
Welcome to minicom 2.7.1 OPTIONS: I18n Compiled on Aug 13 2017, 15:25:34. Port /dev/ttyUSB0, 20:35:14 Press CTRL-A Z for help on special keys ***** Booting Zephyr OS zephyr-v1.14.0 ***** Hello from Rust
OK! 無事動きました!
WebAssembly Micro Runtimeの方は、次の通りです。
|> src/main.c
// デフォルトの512 KBでは実機のRAM 256KBに入らないため static char global_heap_buf[128 * 1024] = { 0 };
|> iwasm/runtime/vmcore-wasm/wasm.h
// WASMは1ページ64KBとのことなので4KBから元に戻しています #define NumBytesPerPage 65536 #define NumBytesPerPageLog2 16
WebAssembly Micro RuntimeでRustのアプリを動かす!
はじめに
WebAssembly Micro Runtimeでは、64KB程度のRAMが搭載されたマイコンであれば、wasmアプリケーションを動かすことができます。
Rustはwasmをターゲットにビルドできます。
後は、わかるね?
アプリケーションの作り方
WebAssembly Micro Runtimeのサンプルコードを修正します。 下のようにおもむろにwasmのバイナリが埋め込まれているので、これを自作のバイナリに差し替えます。
|> src/test_wasm.h
unsigned char wasm_test_file[] = { 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x06, 0x64, 0x79, 0x6C, 0x69, 0x6E, 0x6B, 0xC0, 0x80, 0x04, 0x04, 0x00, 0x00, 0x01, 0x13, 0x04, 0x60, 0x01, 0x7F, 0x00, 0x60, // ... 0x70, 0x69, 0x6E, 0x67, 0x55, 0x52, 0x4C, 0x0E, 0x61, 0x2E, 0x6F, 0x75, 0x74, 0x2E, 0x77, 0x61, 0x73, 0x6D, 0x2E, 0x6D, 0x61, 0x70 };
将来的にはよりスマートな方法が必要ですが、当面は良いでしょう。
Rustツールチェイン
wasm
のターゲットをインストールするだけです。
rustup target add wasm32-unknown-unknown
wasmバイナリ作成
cargo new hello_wasm --lib
|> Cargo.toml
[package] name = "hello_wasm" version = "0.1.0" authors = ["tomoyuki-nakabayashi <tomo.wait.for.it.yuki@gmail.com>"] edition = "2018" [dependencies] cty = "0.2.0" [lib] crate-type = ["cdylib"]
crate-typeはcdylib
になるようです。
wasmバイナリのサイズを小さくするために、#![no_std]
アトリビュートを使用するため、std
のC言語の型定義が使えません。
そこで、cty
クレートを使って、C言語の型を表現します。
WebAssembly Micro Runtimeの方でprintf
のbindingが用意されている(要確認)ので、一旦FFIを手動で用意して、呼び出します。
|> src/lib.rs
#![no_std] use cty; extern "C" { pub fn printf(fmt: *const cty::c_char, ...) -> cty::c_int; } #[no_mangle] pub extern "C" fn main() -> i32 { unsafe { printf(b"Hello from Rust\0".as_ptr() as *const cty::c_char) } } use core::panic::PanicInfo; #[panic_handler] #[no_mangle] pub extern "C" fn panic(_: &PanicInfo) -> ! { loop {} }
cargo build --target wasm32-unknown-unknown --release
この時点で約300バイトと十分に小さいですが、念の為、wasm-strip
を使ってさらにバイナリを小さくします。
$ ls -lha target/wasm32-unknown-unknown/release/ -rwxr-xr-x 2 tomoyuki tomoyuki 306 Jun 15 14:12 hello_wasm.wasm
wasm-strip target/wasm32-unknown-unknown/release/hello_wasm.wasm
strip後のサイズです。180バイトまで小さくなります。
$ ls -lha target/wasm32-unknown-unknown/release/ -rwxr-xr-x 2 tomoyuki tomoyuki 180 Jun 15 14:13 hello_wasm.wasm
wasm-objdump
を使って逆アセンブルすると、次のような感じです。
$ wasm-objdump -x target/wasm32-unknown-unknown/release/hello_wasm.wasm hello_wasm.wasm: file format wasm 0x1 Section Details: Type[3]: - type[0] (i32, i32) -> i32 - type[1] () -> nil - type[2] () -> i32 Import[1]: - func[0] sig=0 <env.printf> <- env.printf Function[2]: - func[1] sig=1 - func[2] sig=2 <main> Table[1]: - table[0] type=funcref initial=1 max=1 Memory[1]: - memory[0] pages: initial=17 Global[3]: - global[0] i32 mutable=1 - init i32=1048576 - global[1] i32 mutable=0 <__heap_base> - init i32=1048592 - global[2] i32 mutable=0 <__data_end> - init i32=1048592 Export[4]: - memory[0] -> "memory" - global[1] -> "__heap_base" - global[2] -> "__data_end" - func[2] <main> -> "main" Code[2]: - func[1] size=2 - func[2] size=16 <main> Data[1]: - segment[0] size=16 - init i32=1048576 - 0100000: 4865 6c6c 6f20 6672 6f6d 2052 7573 7400 Hello from Rust.
hexdump
を使って、適当にC言語に埋め込める形式を出力します。
$ hexdump wasm.wasm -e '16/1 "0x%02x, "' -e '"\n"' 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x03, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x60, 0x00, 0x01, 0x7f, 0x02, 0x0e, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x06, ...
これを、WebAssembly Micro Runtimeのサンプルコードに埋め込みます。
|> src/test_wasm.h
unsigned char wasm_test_file[] = { 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x03, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x60, 0x00, 0x01, 0x7f, 0x02, 0x0e, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x06, // ... };
実行
qemu_x86をターゲットに実行します。 OSはZephyrです。
$ ninja run SeaBIOS (version rel-1.12.0-0-ga698c8995f-prebuilt.qemu.org) Booting from ROM..***** Booting Zephyr OS zephyr-v1.14.0 ***** Hello from Rust
無事動きました! と言いたいところですが、少しごまかしが入っています。
data segmentの置き場所問題
どうもRustでwasmをターゲットにビルドすると、データセグメントが0x10_0000
(1MB)から始まる領域に置かれるようです。
$ wasm-objdump -x target/wasm32-unknown-unknown/release/hello_wasm.wasm ... Data[1]: - segment[0] size=16 - init i32=1048576 - 0100000: 4865 6c6c 6f20 6672 6f6d 2052 7573 7400 Hello from Rust.
一方(?)、サンプルアプリケーションの方では、wasm用のヒープ領域を512KB確保しています。
|> src/main.c
static char global_heap_buf[512 * 1024] = { 0 };
この状態でアプリケーションを実行するとメモリ確保が失敗します。
Booting from ROM..***** Booting Zephyr OS zephyr-v1.14.0 ***** Instantiate memory failed: allocate memory failed.
どうもランタイムでは、データセグメントも含めて1つのメモリ領域に割り当てているように見えます。
データセグメントが1MBから始まるので、ヒープ領域を2MB確保すると、上述の通り、アプリケーションが動作します。
intelさんとこは8MBもRAMが載っていてようござんすねぇ
|> src/main.c
static char global_heap_buf[2048 * 1024] = { 0 };
ということで、後は、Rustでサンプルアプリケーションをビルドする際にデータセグメントのアドレスを指定できれば、 64KB程度のRAMが載ったマイコンボードで動きそうです!
WebAssembly Micro Runtimeお試し②
はじめに
少し前に、組込みで使えるWebAssembly Micro Runtimeが公開されました。 また、いつの間にかSTMでのデモアプリが公開されています。
リファレンスが普段触っているZephyrなので、少し動かしてみます。
前回のおさらい
試しに、qemu_cortex_m3をターゲットにビルドすると、RAM不足でビルドが失敗していました。
$ cmake -GNinja -DBOARD=qemu_cortex_m3 .. $ ninja run Memory region Used Size Region Size %age Used FLASH: 69112 B 256 KB 26.36% SRAM: 535984 B 64 KB 817.85% IDT_LIST: 120 B 2 KB 5.86/opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/8.3.0/../../../../arm-zephyr-eabi/bin/ld: zephyr/zephyr_prebuilt.elf section `bss' will not fit in region `SRAM' /opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/8.3.0/../../../../arm-zephyr-eabi/bin/ld: section .intList VMA [0000000020010000,0000000020010077] overlaps section bss VMA [0000000020000000,0000000020080354] /opt/zephyr-sdk/arm-zephyr-eabi/bin/../lib/gcc/arm-zephyr-eabi/8.3.0/../../../../arm-zephyr-eabi/bin/ld: region `SRAM' overflowed by 470448 bytes collect2: error: ld returned 1 exit status % ninja: build stopped: subcommand failed.
64KBくらいRAMがあれば動いて欲しいところなので、なんとか動かせないか試してみます。
利用RAMサイズを減らす
twitterで@nodamushiさんがヒントをくれました。
ば、倍プッシュは冗談ですからねっ!?ざわ…ざわ…
— nodamushi (@nodamushi) 2019年6月13日
私の所為で余計な調査をさせてしまったのなら申し訳ない。でも待ってます(オイ
15分程ざっくり眺めてみた限りですが、メモリのページが64KB単位になってるぽいです。つまり、128KBって2ページになりますね。0ページ目はVM専用とかと予想(?)
|> core/iwasm/runtime/vmcore-wasm/wasm.h
#define MaxMemoryPages 65536 #define MaxTableElems UINT32_MAX #define NumBytesPerPage 65536 #define NumBytesPerPageLog2 16 #define MaxReturnValues 16
wasmに詳しくないので、ページの概念が何を表現しているのかわかりませんが、とりあえず1ページ4KBくらいでよくない?ということで、定義を修正します。
#define NumBytesPerPage 4096 #define NumBytesPerPageLog2 12
main.c
で確保されるヒープサイズを32KBにします。
|> core/iwasm/products/zephyr/simple/src/main.c
static char global_heap_buf[32 * 1024] = { 0 };
$ ninja run [9/14] Linking C executable zephyr/zephyr_prebuilt.elf Memory region Used Size Region Size %age Used FLASH: 69112 B 256 KB 26.36% SRAM: 44464 B 64 KB 67.85% IDT_LIST: 120 B 2 KB 5.86% [13/14] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3 qemu-system-arm: warning: nic stellaris_enet.0 has no peer ***** Booting Zephyr OS zephyr-v1.14.0 ***** Hello world! buf ptr: 0x40000178 buf: 1234
お、動きますね! OS、アプリケーションとラインタイム合わせて、RAMの使用量は44KBくらいです。
ちなみに、global_heap_buf
は26KBまで小さくしても動作します。
25KBからはRuntimeのインスタンス化に失敗します。
Runtimeに最低でも26KB程度は必要、ということでしょう。
memory_usage.txtに、RAM利用量について、次の記述がありました。
Current memory usage, take samples/littlevgl in Zephyr for example: (1) WASM app binary: 142K for littlevgl ui_app.wasm (2) WASM app memory space: 64K for littlevgl ui_app.wasm (3) WASM app heap space: 8K by default (4) WASM app thread native stack: 4K by default (5) WASM interpreter stack: 8K by default (6) WASM block address hash cache: 3K (7) timer thread stack: 4K (8) sensor thread stack: 4K (9) touch screen thread stack: 4K (10) others: vm, app mgr, queue, native lib: ~22K Total memory usage: ~263K
(10) others: vm, app mgr, queue, native lib: ~22K
となっているので、プラスαで何かがあって、26KBが最低ライン、というのは有り得そうです。
次回以降で、もう少し詳しく内部を見ていきましょう。