ラズパイで動くバイナリプログラムをRustでクロスコンパイルするための基本手順


2022/04/14
蛸壺の中の工作室|ラズパイで動くバイナリプログラムをRustでクロスコンパイルするための基本手順

普段からラズパイを使う身としては、簡単なお役立ちツールをバイナリから動かしたい時が多々あります。

ただ、ラズパイはCPUアーキテクチャがARMv6~8で設計されているので、Linuxで動作していた自作のシェルコマンドを簡単に移植できるとは限りません。

ここでは、Rustのクロスコンパイルを使って、別の開発環境からビルドしたシェルプログラムを実際のラズパイ4で動かした際の手順を防備録的にまとめておきます。


開発機でラズパイ用バイナリプログラムをRustでビルドする

まずRustのビルドターゲットの設定についてですが、

            ARMv6(ラズパイゼロ/1系):
    rustup target add arm-unknown-linux-gnueabihf

ARMv7(2/3/4系):
    rustup target add armv7-unknown-linux-gnueabihf
        
で使い分けます。

なお、出力させるバイナリをgnuではなくmusl版で仕上げることを所望する場合には、

            ARMv6(ラズパイゼロ/1系):
    rustup target add arm-unknown-linux-musleabihf

ARMv7(2/3/4系):
    rustup target add armv7-unknown-linux-musleabihf
        
とするのが作法のようです。

ツールチェーンを準備する

次にARMアーキテクチャ向けのツールチェーンを確認しましょう。

ARMデベロッパーサイト公式から最新のGNUツールチェーンをダウンロードします。

GNU Toolchain Downloads

今回は開発環境をLinuxOSで行うのでターゲットに合わせて、

            ラズパイゼロ/1用:
    AArch32 target with hard float (arm-linux-gnueabihf)

ラズパイ2/3/4用:
    AArch32 target with hard float (arm-none-linux-gnueabihf)
        
を拾ってきます。

ファイルがダウンロードできたら、解凍後に中身を何処かに保管しておきます。

コンパイラが利用できるようにするため、PATH変数を通しておきましょう。

            export PATH="$HOME/ツールチェーンを展開したフォルダ/bin:$PATH"
#例としてラズパイゼロ/1の場合:
#export PATH="$HOME/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin:$PATH"
#例としてラズパイ2/3/4の場合:
#export PATH="$HOME/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/bin:$PATH"
        
必要に応じて.bashrc等に記述しておけば楽に使えます。

なお、直接公式からツールチェーンをダウンロードして来なくとも、特定のOSではパッケージ版が提供されているので、場合によりそちらを使った方が楽にインストールできます。

例えばUbuntu/Debian系の場合、

            $ sudo apt install gcc-arm-linux-gnueabihf
        
からダウンロードできます。

Debian系の場合には、

            $ sudo apt install binutils-arm-linux-gnueabihf
        
が同様に利用できます。

Rust公式のDockerイメージはDebianベースになりますので、Dockerコンテナで開発する場合にはこちらを使うと良いでしょう。

また、MacOSからはhomebrewを使って、

            $ brew install arm-linux-gnueabihf-binutils
        
からインストール可能です。

新規でRustプロジェクトを作成する

Cargoから簡単なプロジェクトを新規作成します。

ここでは
my_appという名前でRustプロジェクトを作成してみましょう。

            $ cargo new my_app
$ cd my_app && mkdir .cargo
$ touch .cargo/config.toml
        
ここで作った.cargo/config.tomlは、以下の構成で編集します。

            [build]

#ラズパイゼロ/1のgnuリンカ
[target.arm-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"

#ラズパイ2/3/4のgnuリンカ
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-none-linux-gnueabihf-gcc"
        
もしもgnu(動的リンク)ではなくmusl(静的リンク)でリンカーを構成する際にはターゲットの方を、

            #...以下を追記

[target.armv7-unknown-linux-musleabihf]
linker = "arm-linux-gnueabihf-ld"

[target.arm-unknown-linux-musleabihf]
linker = "arm-linux-gnueabihf-ld"
        
として追加してあげると良いでしょう。

プログラムのビルド

では先程のプロジェクト・my-appをビルドしてみます。

今回はラズパイ4でmuslターゲットでビルドするようにします。

            #👇ラズパイゼロ/1でgnuの場合
#TARGET=arm-unknown-linux-gnueabihf
#👇ラズパイゼロ/1でmuslの場合
#TARGET=arm-unknown-linux-musleabihf
#👇ラズパイ2/3/4でgnuターゲット
#TARGET=armv7-unknown-linux-gnueabihf
#👇ラズパイ2/3/4でmuslターゲット
$ TARGET=armv7-unknown-linux-musleabihf

#バイナリをリリースビルド
$ cargo build --target $TARGET --release
        
これでバイナリが正常に生成されていればラズパイで動くバイナリが仕上がりました。

なお、出力されたバイナリはプロジェクトフォルダ内の
target/[ターゲット名]/release/my_appとして出ているものを使います。

デフォルトでは、プロジェクト名がそのままバイナリプログラム名になるのでご注意ください。


ラズパイ実機での動作確認

先程作ったバイナリがラズパイ4で動くかどうか確認してみます。

ラズパイ実機とは基本的にSSHで接続して利用することの方が多いと思いますので、scpで先程のバイナリを送ってみます。

とりあえずは例としてユーザー名を
pi、ローカルの固定IPを192.168.1.234、SSHのポート番号(デフォルトでは22)を12345と割り振ったラズパイ4で動かすとして、現在のプロジェクトからこのラズパイ実機へscpコマンドでファイル送信します。

            $ scp -P 12345 -r ./target/$TARGET/release/my_app pi@192.168.1.234:/home/pi
        
後はSSHでインタラクティブでバイナリを実行してみます。

ターミナルからSSH接続を介し、

            $ ssh pi@192.168.1.234 -p 12345 './my_app'
Hello, world!
        
と標準出力されたら正常に動作しています。


まとめ

今回はラズパイ用のバイナリプログラムを自作する際に、別環境でRustからクロスコンパイルを使って作成する方法を解説してみました。

参考サイト

Cross Compiling Rust for the Raspberry Pi

Cross-compiling static Rust binaries in Docker for Raspberry Pi

RustのLinux muslターゲット (その1:Linux向けのポータブルなバイナリを作る)

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

電子工作を身近に知っていただけるように、材料調達からDIYのハウツーまで気になったところをできるだけ細かく記事にしてブログ配信してます。