Go 1.8のMIPS32バイナリをルータ上で動かしてみた（成功編）

> （追記 2020/1/10）続編もご覧ください→ [Go 1\.10にMIPS32 softfloat対応が来たよー \- Qiita](https://qiita.com/hnw/items/ee6e3269c38d415bf8e5)

Go 1.8から32bit MIPS Linux環境がサポートされました（参考：https://tip.golang.org/doc/go1.8#ports ）。32bit MIPSは組み込み系開発ボードなどに多く採用されているCPUで、身近なところでは家庭用ブロードバンドルータの半分以上[^1]でMIPS32が採用されています。つまりブロードバンドルータ上でGoプログラムが動くというわけです。これはアツいですね！

本稿ではまず簡単なGoプログラムをMIPSベースのブロードバンドルータ上で動かします。次に、GoのMIPS32サポートの現状と今後について紹介します。

[^1]: 組み込み系素人の感覚値です

# 動かしてみた

では、さっそくGoでMIPS32バイナリを生成してみましょう。まずはGo 1.8RC1をインストールします。

```
$ go get golang.org/x/build/version/go1.8rc1
$ go1.8rc1 download
```

次に、下記のGoプログラムを用意します。

```go:hello.go
package main

import "fmt"

func main() {
	fmt.Printf("Hello, world!\n")
}
```

次のようにコンパイルすれば32-bit MIPS用の`hello`バイナリが得られます。

```
$ GOOS=linux GOARCH=mips go1.8rc1 build hello.go
$ file hello
hello: ELF 32-bit MSB executable, MIPS, MIPS32 version 1 (SYSV), statically linked, not stripped
```

さて、実際に動かしてみましょう。筆者はBuffalo社の有線ブロードバンドルータBHR-4GRV上にOpenWrtというLinuxディストリビューションをインストールしています。

このブロードバンドルータにscpでバイナリを送り込むと、バイナリが動作しました。

```
$ scp hello openwrt01:/tmp
$ ssh openwrt01


BusyBox v1.23.2 (2017-01-19 17:27:24 UTC) built-in shell (ash)

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 CHAOS CALMER (Chaos Calmer, unknown)
 -----------------------------------------------------
  * 1 1/2 oz Gin            Shake with a glassful
  * 1/4 oz Triple Sec       of broken ice and pour
  * 3/4 oz Lime Juice       unstrained into a goblet.
  * 1 1/2 oz Orange Juice
  * 1 tsp. Grenadine Syrup
 -----------------------------------------------------
# /tmp/hello
Hello, world!
```

こう書くと簡単に動くように見えますが、実はこのバイナリは多くの環境で動作しません。上記の環境ではこのバイナリを動かすため自前のカーネルに入れ替えています。

# 現時点のGoのMIPS32サポート状況

Go 1.8のリリースノートには、次のような記述があります。

>  Go now supports 32-bit MIPS on Linux for both big-endian (linux/mips) and little-endian machines (linux/mipsle) that implement the MIPS32r1 instruction set with FPU or kernel FPU emulation. Note that many common MIPS-based routers lack an FPU and have firmware that doesn't enable kernel FPU emulation; Go won't run on such machines.

Go 1.8の32-bit MIPSサポートはFPU（浮動小数点演算ユニット）またはカーネルによるFPUエミュレーションが必須だと書いてあります。これを満たす32-bit MIPS環境はごく少数であるため、かなり厳しい制約だと言えるでしょう。

実際、私の持っているブロードバンドルータにはFPUはありませんし、OpenWrtでもカーネルFPUエミュレーションはデフォルトで無効になっています。FPUエミュエーション有効にしてカーネルビルドしなおしたのが上の環境というわけです。

# カーネルFPUエミュレーションとsoft-float

ところで、FPUが無い環境というのは組み込み系では珍しくありません。そうした環境でもC言語で`float`や`double`などの型が使えるのはなぜでしょうか。

実は、整数演算命令のみで浮動小数点数演算を実現する機構が大半のCコンパイラで提供されています。これはsoftware floating pointとかsoft-floatなどと呼ばれます。この仕組みにより、FPUの無い環境でも浮動小数点数演算が実現できているわけです。逆に、Go 1.8でFPU必須という制約がついているのはsoft-floatが未実装であるという意味でもあります。

一方で、今回の実験で使ったようにカーネル側でFPU命令のエミュレーションを行うアプローチも考えられます。これはARM LinuxとMIPS Linuxで提供されている機構のようです（私が知らないだけで、他の環境でも珍しくないのかもしれません）。これを使えばコンパイラ側の実装コストは減りますが、FPU命令が発効されるたびにカーネル割り込みが走るため速度面ではかなり不利です。カーネルFPUエミュレーションとsoft-floatの好きな方が選べるのであれば、当然後者を選ぶべきです。

もちろんGoの中の人も同じ認識のようで、既にMIPS32のsoft-floatサポートについて議論されています（参考：「[runtime: mips32 soft float point support · Issue \#18162 · golang/go](https://github.com/golang/go/issues/18162)」）。ただ、Go 1.8のタイミングでは時間切れだったということのようです。Go 1.9以降に期待したいですね。

# まとめ

* Go 1.8でMIPS32バイナリが出力できるようになった
    * ただし、このバイナリが動く環境は少ないと思われる。大抵の環境ではカーネルリコンパイルが必要。
* Go 1.9以降MIPS32のsoft-floatサポートが入るはず、そのタイミングが本命