go1.3でNative Client(NaCl)アプリをビルドする #golang

今朝、golang 1.3がリリースされました。
このバージョンからPlan9などの新しい環境に対応しましたが、特にNative Client(NaCl)が気になりましたので、バージョンアップのついでに試してみました。

# Native Client
Native Client、通称NaClはブラウザ上でx86ネイティブコードが実行出来る仕組みであって、JavaScriptの諸問題の一部を解消できると期待されています。バイナリはNaClのサンドボックスに閉じ込めて実行されるため、周りに影響を与えないという特徴もあります。
類似の技術にMozillaの[asm.js](http://asmjs.org/)がありますが、asm.jsがJavaScriptを単純にしたサブセットで実行速度を改善しているのに対し、Native Clientはコンパイル済みのマジで本気なバイナリがゴリゴリ動くので、大変に萌え度が高いです。

とは言ったものの、go1.3ではNativeClientをブラウザで動かすためのPepperPluginAPIに対応していないため、普通にサンドボックス内でバイナリを動かすことしかできません。(Go1.4かそれ以降でPPAPIにも対応する予定)
また、go1.3で対応したのはx86/x64のバイトコードを用いたNaClのみで、LLVM版のPNaCl(Portable Native Client)には対応していません。こちらは対応に向けての確固たる計画もないそうです。まぁ、成り立ちを考えれば難しいでしょうしね。

 * Google Native Client
   * http://ja.wikipedia.org/wiki/Google_Native_Client

# go1.3で環境を作る
## NaCl SDKをダウンロードする
go1.3だけでなく、NaCl SDKが必要になるので、ダウンロードします。
このページ([Download the Native Client SDK](https://developer.chrome.com/native-client/sdk/download))中の「Download and install the SDK」から「nacl_sdk.zip」を落としてきて、適当にunzipして、「/opt」にmvします。
最後に、中に居る「naclsdk」のスクリプトを蹴ります。

```shell-session
 $ unzip nacl_sdk.zip
 $ mv nack_sdk /opt
 $ cd /opt/nacl_sdk
 $ ./nacksdk install
```

つぎに、必要なスクリプトをパスの通ったディレクトリへ移します。("pepper_35"の数字部分は、インストールしたnaclsdkのバージョンで変わるので要確認)

```shell-session
 $ ln -nfs /opt/nacl_sdk/pepper_35/tools/sel_ldr_x86_32 $GOPATH/bin/sel_ldr_x86_32
 $ ln -nfs /opt/nacl_sdk/pepper_35/tools/sel_ldr_x86_64 $GOPATH/bin/sel_ldr_x86_64
```

## サポートスクリプトを準備する
go/misc/nacl以下にサポートスクリプト達が居ますので、これらをパスの通ったディレクトリへ移します。

```shell-session
 $ ln -nfs $GOROOT/misc/nacl/go_nacl_amd64p32_exec $GOPATH/bin/go_nacl_amd64p32_exec
 $ ln -nfs $GOROOT/misc/nacl/go_nacl_386_exec $GOPATH/bin/go_nacl_386_exec
```

## 環境をビルド＆テスト
仕上げに、例えばx86のnaclなら386をサポートしたサポートスクリプトを叩いてから、nacl用go環境をビルド＆テストします。

```shell-session
 $ go_nacl_386_exec
 $ cd $GOROOT/src
 $ env GOARCH=amd64p32 ./nacltest.bash
```

# NaClアプリをビルドしてみる
環境はできたので、NaClアプリをビルドします。
先に書いたとおり、go1.3の時点ではブラウザで動かすことはできませんので、Native ClientのSDKについているサンドボックスを使ってなんとなく動かしてみます。

```go:test.go
package main

import (
        "fmt"
)

func main() {
        fmt.Println("hello")
}
```

```shell-session
 $ env GOOS=nacl GOARCH=amd64p32 go build test.go
 $ sel_ldr_x86_64 test
[28083,2572449600:13:50:19.491746] Native Client module will be loaded at base address 0x00004a0c00000000
runtime: nacl_exception_stack: error 38
runtime: nacl_exception_handler: error 38
runtime: nacl_exception_stack: error 38
runtime: nacl_exception_handler: error 38
runtime: nacl_exception_stack: error 38
runtime: nacl_exception_handler: error 38
runtime: nacl_exception_stack: error 38
runtime: nacl_exception_handler: error 38
hello
[28083,2572236544:13:50:20.140744] NaClRuntimeHostInterfaceReportExitStatus(0x7f9a9a0a9f50, 0x0)
```
nacl_exception_stackってのがたくさん出ていていますが、ふわっと検索した感じ、そういうものらしい。ウザければ「2>/dev/null」でも付けてエラー出力を無視するといい感じになります。

一応、サンドボックス内で動いていることを確認しておきましょう。

```go:test2.go
package main

import (
        "fmt"
        "os"
)

func main() {
        f, err := os.Open("test2.go")
        if err != nil {
                fmt.Println(err)
        }

        fmt.Println(f)
        return
}
```

```shell-session
 $ env GOOS=nacl GOARCH=amd64p32 go build test2.go
 $ sel_ldr_x86_64 test2
open /home/umisama/main.go: No such file or directory
<nil>
```

予定通り、ファイルを開くことが出来ませんでした。
とりあえず満足したので、ここまで。

# 実用例
go playgroundでユーザが書いたコードは、NaCl用にビルドされてサンドボックス内で動作しているらしい。
こんな感じの用途になら、現状でもなんとなく使えます。（やっぱまだ用途が微妙だな〜）

 * http://play.golang.org/

# 参考
 * http://blog.golang.org/go1.3
 * https://code.google.com/p/go-wiki/wiki/NativeClient