ししかわです。
社員研修の一環で二足歩行ロボットを作って、競技会「Humanoid Autonomous Challenge(HAC)」に参加します。
M5Stackの画像認識モジュール「M5Stack UnitV2」をM5Stackのユニットとしてではなく単独で使い、ロボットを制御できるようにしよう!というマニアックな計画を進めています。
今回はクロスコンパイル環境の構築です。
クロスコンパイル用のツールチェインをインストールする
前回説明したとおり、クロスコンパイルとは、開発用の環境と異なる環境で動くプログラムをコンパイルすることです。組込みLinuxでは多くの場合、CPUの性能やメモリの容量がデスクトップと比べて非力で、自身のプログラムをコンパイルすることが難しいです。そんなときはクロスコンパイルの出番です。
まずは図中の左枠の環境を開発用PC上に整えていきます。なお開発用PCの環境はLinux(Ubuntu20.04)を使っています。
ソースコードを実行可能なバイナリに変換するためにはコンパイラやアセンブラなどのツール群(ツールチェイン)が必要です。クロスコンパイルを行う場合、プログラム実行環境のアーキテクチャに合わせたツールチェインを用意する必要があります。自分でクロスコンパイラをコンパイルすることもできますが、主要なアーキテクチャ向けのツールチェインは大抵aptなどのパッケージリポジトリにあるのでインストールします。
$ sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
パッケージ名のうちarm-linux-gnueabihf
がアーキテクチャを表現する3つ組の文字列で、ターゲットトリプルと呼ばれます。arm-linux-gnueabihf
の場合次のようなアーキテクチャになります。
- ArmアーキテクチャのCPU
- Linux上で動作する
- EABI(Embedded Application Binary Interface)というインタフェースを使う
- Hard-float=浮動小数点演算をハードウェアで行う
Hello Worldをコンパイルする
試しに簡単なプログラムをコンパイルして実行してみましょう。次のC++のソースコードをコンパイルします。標準出力に1行「Hello World!!」と表示するだけのプログラムです。
#include <iostream> int main() { std::cout << "Hello World!!"; }
次のコマンドを実行してコンパイルし、hello-world
という名前のバイナリを生成します。
$ arm-linux-gnueabihf-g++ ./hello-world.cpp -o hello-world
arm-linux-gnueabihf-g++
がarm版のコンパイラ(クロスコンパイラ)です。こうして作ったバイナリはターゲット(UnitV2)のアーキテクチャ専用のバイナリとなります。試しに開発用PCの環境で実行しようとしてもExec format error
というエラーになります。
$ ./hello-world bash: ./hello-world: cannot execute binary file: Exec format error
file
コマンドを使うとファイルのフォーマットを確認できます。
$ file ./hello-world ./hello-world: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, BuildID[sha1]=f9241dad4584b7b02039a8865c3f6b24af0e0ba6, for GNU/Linux 3.2.0, not stripped
先程のターゲットトリプルにも出ていた”ARM, EABI5″という文字列が見えますね。正しくUnitV2向けのバイナリが生成されていることが分かります。
プログラムを転送する(UnitV2でscpが使えない問題に対処)
続いてビルドしたプログラムをUnitV2にscpで転送してみましょう。
$ scp ./hello-world m5stack@unitv2.local:/home/m5stack/ m5stack@unitv2.local's password: Couldn't open /dev/null: Permission denied lost connection
おや?エラーが出て失敗しました。どうやら/dev/null
が開けない様子。
UnitV2で/dev/null
を確認してみると、全ユーザに対する読み書きの権限が与えられていません。このファイルのパーミッションは通常666(全ユーザが読み書き可能)のはずですが、何かの対策なんでしょうか。
unitv2% ls -l /dev/null crw-rw---- 1 root root 1, 3 Jan 1 00:00 /dev/null unitv2% echo hoge > /dev/null zsh: permission denied: /dev/null
暫定的にchmod
コマンドで /dev/null
の権限を修正することで対処します。(前回の記事を参考にUnitV2でsudo
が使えるようにしておきます)
unitv2% sudo chmod 666 /dev/null Password: unitv2% ls -l /dev/null crw-rw-rw- 1 root root 1, 3 Jan 1 00:00 /dev/null
これで転送ができるようになりました。
$ scp ./hello-world m5stack@unitv2.local:/home/m5stack/ m5stack@unitv2.local's password: hello-world 100% 8720 3.7MB/s 00:00
UnitV2を再起動するとパーミッションが元に戻ってしまうので、都度↑のコマンドを打つ必要があります。根本原因と解決策が分かったら改めて記事にします。
プログラムを実行する
最後にUnitV2でプログラムを実行します。
unitv2% ./hello-world ./hello-world: /lib/libstdc++.so.6: no version information available (required by ./hello-world) ./hello-world: /lib/libstdc++.so.6: no version information available (required by ./hello-world) Hello World!!%
無事に「Hello World!!」の文字が表示されました!
このときno version information available
という警告が出ています。これはビルドした環境のライブラリよりも実行環境のライブラリのバージョンが古い時に出るもののようです。今の所支障無いので無視しています。
以上です。「クロスコンパイルのツールチェインを使ってバイナリをビルドする」「ターゲットデバイスに転送する」「実行する」という基本的な流れを行いました。
今回使ったarm向けのツールチェインや、UnitV2の他の依存ライブラリを含む開発環境一式をGitHubで公開中です。Docker一発で開発環境が整って便利ですのでぜひ使ってみてください。
→次の記事
