🦀

RustのプログラムをWSLでクロスコンパイルして Raspberry Pi Zero 2 で動かすまで

2024/12/27に公開

はじめに

こんにちは。aq2rです。
Rustのプログラムを Raspberry Pi Zero 2 で動かしたいなと思ったのですが、動かすまでに苦労したので残しておこうと思います。多少うろ覚えですが方法だけでなく、クロスコンパイルができるまでの道のりも残しておこうと思うので、

さっさと一番簡単な方法教えて!

という方は その後見つけたもっと簡単な方法 に飛んでください。

対象読者

WSL2上で Raspberry Pi Zero 2 向けにRustプログラムをクロスコンパイルしたい人

クロスコンパイルするまでの道のり

私がクロスコンパイルを行いたいと思った理由は、ラズパイ上で DiscordのBot を動かしたいと思ったからなのですが、Zero 2 だと性能があまり高くないので、ラズパイ上でのコンパイルは時間がかかりすぎてしまい難しかったからです。(試したところ1時間以上たっても終わらなかった気がします)

まず試したのが、
ラズパイもWSLもLinuxだからWSL上でコンパイルしてそれを持っていけばいいんじゃない?
ということで、WSL上でコンパイルしてラズパイに持って行ったのですが... もちろん動きません。

アーキテクチャが違う からでした。
わかっている人にとっては当然でしょという感じだと思うのですが、それすら知りませんでした。
(てっきりOSが同じだったら動くものなのかと...^^;)

いろいろ調べたところ、

  • aarch64-unknown-linux-gnu
  • aarch64-unknown-linux-musl

このどちらかに、クロスコンパイルすればいいらしい。

aarch64-unknown-linux-gnu 向けにビルドしてみる

$ rustup target add aarch64-unknown-linux-gnu
$ cargo build -r --target aarch64-unknown-linux-gnu
--- stderr
error occurred: Failed to find tool. Is `aarch64-linux-gnu-gcc` installed?
warning: build failed, waiting for other jobs to finish...

あれ、エラー...? どうやらaarch64-linux-gnu-gccというものが必要らしい?
https://qiita.com/syoyo/items/5008aef05f0b51090393

$ sudo apt install -y gcc-aarch64-linux-gnu

すると、ビルドが進んだ!と思ったら

error: linking with `cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH= ...

とエラーが発生しました。linker の設定が必要らしいので、

nano ~/.cargo/config.toml

以下を追記しました。

[target.aarch64-unknown-linux-gnu]
linker = "/usr/bin/aarch64-linux-gnu-gcc"
$ cargo build -r --target aarch64-unknown-linux-gnu
...
Finished `release` profile [optimized] target(s) in 1m 07s

ビルドできました!早速ラズパイに持って行って動かそう!と思いラズパイに持って行ったところ、

$ chmod 755 ./my-app
$ ./my-app
./my-app: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ./my-app)
./my-app: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by ./my-app)
./my-app: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./my-app)

あれ、動かない...

...-gnu ではなく ...-musl にビルドする

どうやらgnuの方だと環境が違うと動かないことがある? らしいので、muslの方でやってみます。
https://osanshouo.github.io/blog/2021/04/15-cross-compilation/

$ rustup target add aarch64-unknown-linux-musl

nano ~/.cargo/config.toml でconfigを変更します。

[target.aarch64-unknown-linux-musl]
linker = "/usr/bin/aarch64-linux-gnu-gcc"
$ cargo build --target aarch64-unknown-linux-musl

...
error: linking with `/usr/bin/aarch64-linux-gnu-gcc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="
...

Hello Worldのプログラムはコンパイルできたのですが、discordのbotプログラムはコンパイルできませんでした。 muslのlinker? が必要みたいなので、https://musl.cc/ から入手するか、自分でコンパイルします。ここでは自分でビルドする方法を残しておこうと思います。

aarch64-linux-musl-gcc をビルドする

こちらを使用します。
https://github.com/richfelker/musl-cross-make

$ git clone https://github.com/richfelker/musl-cross-make.git
$ cd musl-cross-make

nano config.mak を使用して以下を書きます。

TARGET = aarch64-unknown-linux-musl
OUTPUT = /opt/cross

ビルドします。

$ sudo make install -j $(nproc)

もし failed: Network is unreachable. このようなエラーが発生する場合は、
https://qiita.com/Keichan_15/items/99cdf7e3abdbdd840cc8
~/.ssh/config に以下を追記します。

Host *
  IPQoS lowdelay

ビルドできたらPATHに追加します。

$ echo 'export PATH="/opt/cross/bin:$PATH"' >> ~/.bashrc
$ source ~/.bashrc

aarch64-unknown-linux-musl-gcc --version を使用してバージョンが返ってきたら完了です!

rustプログラムをコンパイルする

nano ~/.cargo/config.toml でconfigを変更して...

[target.aarch64-unknown-linux-musl]
linker = "/opt/cross/bin/aarch64-unknown-linux-musl-gcc"
$ cargo build -r --target aarch64-unknown-linux-musl

...
Finished `release` profile [optimized] target(s) in 1m 19s

無事コンパイルが完了してラズパイ上での動作も確認できました!

その後見つけたもっと簡単な方法

こちらを使用した方法です。
https://github.com/cross-rs/cross
実はこの方法は見かけていたのですが、dockerが必要で、なぜかWSL上でdockerが動かず...
https://qiita.com/motoJinC25/items/c6fbb10f00230f6a5a97
こちらで解決したので残しておきます。

docker をインストール

https://zenn.dev/sky_sato/articles/b7c7b19a1be694

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

sudo なしで実行できるように変更

$ sudo groupadd docker
$ sudo usermod -aG docker $USER
$ newgrp docker 

https://qiita.com/motoJinC25/items/c6fbb10f00230f6a5a97

$ sudo update-alternatives --config iptables

ここで 1 の iptables-legacy を選択します。

動作するかチェック

$ sudo service docker start
$ docker run hello-world

cross のインストールとクロスコンパイル

$ cargo install cross
$ cross build --release --target aarch64-unknown-linux-musl

これで Raspberry Pi Zero 2 向けにクロスコンパイルすることができました!

Discussion