Go1.8 on NetBSD/arm

はじめに

去る2017年2月16日にGo言語のメジャーバージョンアップであるGo 1.8がリリースされました (リリースノート)。 大きな新機能や特徴はリリースノートを読んでいただくとして、ここでは個人的に長年の懸案事項であったNetBSD/armで使用することについての2017年3月9日現在の状況を記しています。

なお、例によって個人の勝手な思い込みで書いている部分が多数あり、正確な表現ではなかったり、根本的に勘違いで間違っている部分もあるかもしれません。 もしも何かの参考にこの文書を利用しようなんて奇特な方がいましたらぜひとも自身で確認し、自分なりの認識の上で使用してください。

Go 1.8におけるNetBSD/armでの動作状況

まず最初に現在の動作状況をまとめておきます。

  1. Go 1.8リリースから、Goが動作する全ての開発環境でNetBSD/armのうちearmv6hfおよびearmv7hf環境をターゲットとした実行ファイルを作成することができます。ただし動作環境や作成した実行ファイルにいくつかの制限と不具合が存在します。
  2. Go 1.8リリースから、NetBSD/earmv6hfおよびNetBSD/earmv7hf環境上で動作するGoセルフ開発環境を構築できます。ただしいくつかの制限が存在します。
  3. いくつかの制限はGoのソースツリーにわずかな変更を加えることで解消するものもありますが、そうでないものもあります。

NetBSD/earmv6hfまたはearmv7hfをターゲットとした実行ファイルを作成する方法

ここではNetBSD/arm環境をターゲットにしたバイナリを(OSやCPUが異なる)他の環境でクロスビルドする方法を示します。これには以下のような前提条件があります。

この前提条件が満たされるのであれば、作成したいプログラムのビルド時に以下の様に環境変数を設定することでNetBSD/earm用の実行ファイルを作成することができます。

もちろん複雑なソース構造で単純なコンパイルでは済まない場合もあるでしょうが、基本的には環境変数を指定するだけでクロスでバイナリを作成できます。


注1:
Go 1.7までも同様にGOOS=netbsd GOARCH=armを指定してgo buildすることでNetBSD/arm用のバイナリを出力できましたが、これはNetBSD/arm上で正常に動作しないバイナリが生成されます。Abort trapで実行開始できなかったり、SIGKILL以外受け付けないハングアッププロセスとなったり、最悪カーネルが無応答になったりします(カーネル無応答はNetBSD側に原因があると思いますが)

NetBSD/arm環境上で動作するGoセルフ開発環境を構築

Goの開発環境(goコマンドやライブラリ等)はWindowsやLinuxの各種Arch, OS X(x86 64bit), それにFreeBSDのamd64/i386用にはオフィシャルのバイナリが提供されていますが当然NetBSD用はありませんのでソースから構築する必要があります。 一方、Goは1.5以降、基本的にすべてGoで記述されているため、Goコンパイラがなければ構築できません。標準手順ではGo 1.4のバイナリを使用するようです(Go 1.4はC言語で書かれていたのでGoコンパイラがなくても構築できました)。が、当然NetBSD/armにはGo 1.4バイナリなんかありません。Go 1.4をNetBSD/armに移植するという手段もありますが(注2)、通常はクロス環境でGo自身を構築します。

NetBSD/arm上で動作するGo自身のビルド環境に必要な前提条件は以下です。

条件が満たされたなら、次の様にクロスビルド用のbootstrap.bashを実行します。

$ cd /where/to/workdir/go/src
(NetBSD/earmv6hf用バイナリを作成する場合は以下)
$ GOROOT_BOOTSTRAP=/usr/pkg/go14 GOOS=netbsd GOARCH=ARM GOARM=6 ./bootstrap.bash
(NetBSD/earmv7hf用バイナリを作成する場合は以下)
$ GOROOT_BOOTSTRAP=/usr/pkg/go14 GOOS=netbsd GOARCH=ARM GOARM=7 ./bootstrap.bash

上記ではGo 1.8のビルドに使用するGo言語が/usr/pkg/go14に存在する場合です。/usr/local/go等、環境に合わせて指定します。また、最終的なターゲット環境のインストールディレクトリが決まっているのであれば、以下の様にGOROOT_FINALを指定しておくといいかもしれません。

$ GOROOT_BOOTSTRAP=/usr/pkg/go14 GOROOT_FINAL=/usr/local/go GOOS=netbsd GOARCH=ARM GOARM=7 ./bootstrap.bash

なお、v6hf用とv7hf用のバイナリは同時に作成することはできません。また、作成したv6hf用のバイナリはv7hfの「マルチCPU/マルチコア]環境では使用できません(シングルCPU環境では使用可能)。もちろんv7hf用バイナリはv6なCPUでは実行できません。ターゲットのCPU環境に合わせて選択します。

無事にビルドが終了すると、goディレクトリと同じ階層に1つのディレクトリと、それをアーカイブしたtar ballが作成されています。

$ cd /where/to/workdir
$ ls -F
go/                           go-netbsd-arm-bootstrap/
go-netbsd-arm-bootstrap.tbz

go-netbsd-arm-bootstrapディレクトリにターゲット用のバイナリ一式があり、それをアーカイブしたものがgo-netbsd-arm-bootstrap.tbzとなります。

なお、このディレクトリ名やファイル名はbootstrap.bashで$GOOS/$GOARCHからのみ作成されるようになっているため、GOARMの6と7を同時に作ることができません。両方を作成するには、スクリプトを変更するか、1つづつ作成して手動で移動させなければなりません。

作成したgo-netbsd-arm-bootstrap.tbzを、ターゲットであるNetBSD/earm環境に何らかの方法でコピーし、適当なディレクトリに展開、適切にGOROOTやPATHを設定することで、セルフ開発環境が構築できます。

rpi2$ uname -srpmp
NetBSD 7.1_RC2 evbarm earmv7hf
rpi2$ cd /home/user
rpi2$ /&tar zxpf go-netbsd-arm-bootstrap.tbz
rpi2$ mv go-netbsd-arm-bootstrap golang
rpi2$ export GOROOT=/home/user/golang PATH=$PATH:/home/user/golang/bin
rpi2$ go version
go version go1.8 netbsd/arm
rpi2$ file golang/bin/go
golang/bin/go: ELF 32-bit LSB executable, ARM, EABI5 version 1 (NetBSD), statically linked, for NetBSD 5.99, not stripped

これでNetBSD/arm用のGo開発環境が作成できました。


注2:
完全ではありませんが最低限Go自身のビルドが可能な程度の修正を加えたものを独自変更版として作成しています。

制限項目

現状では実行環境やその動作にいくつか制限があります。

Go言語開発環境およびターゲットとして作成した実行プログラムに関する制限

Go言語の標準的な機能や仕様に対する独自の制限


注3:
根本的な解決はできていませんが、独自変更版では実行時に動作しているコアが1つに制限している場合には動作するように変更しています。
注4:
非常にわずかな修正で外部リンクが可能になります。独自変更版に含んでいます。

残問題点

残念ながらまだ問題点、動作不良が残っています。とりあえず分かっている範囲で。

実行中になぜかハングアップしたりKernel panic(?)したりすることがある。

Go 1.8リリースノートのKnown Issueにかかれているのと同じではないかと思っていますが、確証はありませんし資料もとれていません。Go 標準のテスト項目(run.bashで実行するもの)を(OSを再起動せずに)何回も繰り返すと発生しやすいような気もします。

syscallのテストでTestPassFDが必ず失敗する

Go標準のテストケースでsyscallのうちTestPassFDという、ファイルディスクリプタをSocketを使って渡すテストが正常に動作しません(間違ったファイルディスクリプタを受け取る)。i386/amd64では正常に動作しているため、ARM独自の何か、アライメント関連等がどこかで影響しているのではと疑っていますが原因はまだ不明です。

Go独自変更版

いくつかの制限やまだ問題はあるもののGo 1.8の正式リリース版を使うことでとにもかくにもNetBSD/armのうちearmv6hfとearmv7hf上でGo環境が動くようになりました。これまで動作するように独自にパッチを作ったりしていましたが、主要な部分が本家とほぼ同等のコードとなったため、ようやくスタートラインに立った状態と言えます。

個人的に制限項目を解決したり緩和するものを含め、いくつかの変更を加えた独自変更版を作っています。

ソースはGithubに登録してあります。go1.4-netbsd branchなら独自変更版のGo 1.4, go1.8-netbsdならGo 1.8ブランチの独自変更版となります。netbsdブランチは一応Masterの開発版です。気の向いたときに本家に追従してますが、更新頻度はあんまり高くありません。

もうほとんど本家と差はありません。説明が難しいところもありますが、いくつかは本家に入れてもらえるように報告していくつもり。

pkgsrc化について

NetBSDではサードパーティアプリケーションはpkgsrc化するのが好ましいのですが、Go1.8でNetBSD/earm用にセルフ開発環境を用意できるようになったとはいえ、標準ではGo 1.4が必要かつGo 1.4はNetBSD/arm用は正式には作れません。現在のpkgsrcはセルフコンパイルが前提であり、ビルド途中で実行ホストのarchを切り替えてクロスコンパイルさせるような荒技はありません。

このため、現状および今後のGoのバージョンをpkgsrcで対応するには2つの方法が考えられます。

  1. ターゲットのGoをビルドするためだけのearm用のgoバイナリをクロスコンパイルなどで作ってどこかに置いておき、pkgsrcでのセルフビルド時はそれを取得してGOROOT_BOOTSTRAPに置きます。この方法は、例えば現在のlang/openjdk*等が採用しているようです。
  2. 自家製のGo1.4独自変更版の修正をpkgsrcのpatchesに取り込む。go1.4は本家ではもう修正されません。そのため独自変更部分はpkgsrc用に自力で保守し続ける必要がある

いずれにせよ、pkgsrcとはあまり相性は良くなさそうです。そもそもARMターゲットでセルフビルドってそんなに普通なのかは私にはわかりません。

一応現状のpkgsrcツリーに組み込んだものをGitHubに置いてあります(更新頻度はgolang本体よりも低くなってます)。