Help us understand the problem. What is going on with this article?

ARM64をターゲットとしたGoのクロスコンパイル

SENSYN ROBOTICS(センシンロボティクス)の中山です。
Webアプリやそのインフラ周り、Web側とドローンの接続を行うデバイスドライバ的なモジュールを担当しています。

今回は、ドローン内部で動かすソフトウェアにGoを使った話です。上記の担当範囲から外れているように見えますが、そういうこともあります。

なんのために?

ドローンといっても色々あるのですが、今回はARM64のCPU(NVIDIA TX1)が載ったドローンです。CPUの上ではUbuntu 16.04が動いています。具体的にはSENSYN DRONE HUBというドローンです。

このドローンは、あらかじめ設定しておいた時刻になると自動的に基地から離陸し、事前に設定しておいたルートを飛行して動画・静止画を撮影し、基地に返ってきます。返ってきたら自動的に充電して次回の飛行に備えつつ、撮影したデータを自動的にクラウドにアップロードします。

つまり、ドローンの存在を意識せずにドローンによる設備点検や警備・監視ができるソリューションです。

今回、Goを使ったのはデータをクラウドにアップロードするモジュールで、ドローンの内部で動いています。

なぜGoなのか?

当初はGoを使うつもりはなく、Pythonで書く予定でした。ただ、Ubuntu上には既にドローンの制御機構が構築されており、そちらが一部にPythonを使っている都合上、既存の環境への干渉は絶対に避ける必要があります。ところが事前に入っているUbuntuにはpipすら入っておらず、環境分離ツールを入れること自体が難しい状況です。そのため、既存の環境を汚さずに新しいモジュールを入れるのは不可能ではないにしても、非常に面倒でした。

Pythonが使えないとなると…と悩んだ末、Goならシングルバイナリにできるので既存環境への干渉を避けたいときに理想的ではないか、と気づいて採用を決定しました。ARM64 Linux対応、というのも必須条件ですが、Goならこれも満たしています。

上記の理由だけならC++でも良かったのですが、Goの方がメンテできるひとが将来的に多くなりそう、というのもあります。

実際にやってみると?

環境を汚したくないので、コンパイル環境をドローンの中に作るわけにはいきません。そうなると、必然的にクロスコンパイルが必要になります。手元の開発環境で試行錯誤すると後が怖いので、Dockerを使いました。

docker run \
  -v $(pwd):/go \
  --rm \
  -it \
  dockercore/golang-cross:1.12.7 \
  sh -c 'GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go build src/main.go'

(少し前の話なので、バージョンは古めです)

# runtime/cgo
gcc_arm64.S: Assembler messages:
gcc_arm64.S:27: Error: no such instruction: `stp x19,x20,[sp,'
gcc_arm64.S:28: Error: no such instruction: `stp x21,x22,[sp,'
gcc_arm64.S:29: Error: no such instruction: `stp x23,x24,[sp,'
gcc_arm64.S:30: Error: no such instruction: `stp x25,x26,[sp,'
gcc_arm64.S:31: Error: no such instruction: `stp x27,x28,[sp,'
gcc_arm64.S:32: Error: no such instruction: `stp x29,x30,[sp,'
gcc_arm64.S:33: Error: too many memory references for `mov'
gcc_arm64.S:35: Error: too many memory references for `mov'
gcc_arm64.S:36: Error: too many memory references for `mov'
gcc_arm64.S:37: Error: too many memory references for `mov'
gcc_arm64.S:39: Error: no such instruction: `blr x20'
gcc_arm64.S:40: Error: no such instruction: `blr x19'
gcc_arm64.S:42: Error: no such instruction: `ldp x29,x30,[sp],'
gcc_arm64.S:43: Error: no such instruction: `ldp x27,x28,[sp],'
gcc_arm64.S:44: Error: no such instruction: `ldp x25,x26,[sp],'
gcc_arm64.S:45: Error: no such instruction: `ldp x23,x24,[sp],'
gcc_arm64.S:46: Error: no such instruction: `ldp x21,x22,[sp],'
gcc_arm64.S:47: Error: no such instruction: `ldp x19,x20,[sp],'

エラーになりました…。

色々調べて、

FROM dockercore/golang-cross:1.12.7

RUN apt-get update \
    && apt-get install -y gcc-aarch64-linux-gnu

というDockerfileを作り、このイメージでgo buildしても、同じ結果…。

更に調べ回って、上記のDockerイメージに加えてCC=aarch64-linux-gnu-gccをコマンドラインに足した

docker run \
  -v $(pwd):/go \
  --rm \
  -it \
  my-golang-image \
  sh -c 'GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc go build src/main.go'

で最終的に通るようになりました。

結論

  • 環境を汚さないためにGoを使うのは有効
  • クロスコンパイルはできるようになれば簡単けど、そこまでがちょっと大変
  • Dockerを使うとトライ&エラーを気楽にできて便利
  • ARM64 Linuxをターゲットにクロスコンパイルするならgcc-aarch64-linux-gnuを入れて、CC=aarch64-linux-gnu-gccを環境変数に設定する
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした