いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-200 (static linkedなバイナリを作る)(os/user, net, 静的リンク)

概要

以下自分用のメモです。よく忘れるのでここにメモメモ。。。

以下はLinux上での話です。

Goでは、標準ライブラリを使っているだけのアプリケーションであれば、大抵ビルドすると static linked なバイナリになります。

静的リンクされたバイナリってことですね。自分を動かすのに必要なものが自分自身で完結しているバイナリです。

昨今、コンテナで動作させることが多いので、依存しているものが少なくて、シングルバイナリなアプリケーションが求められるときも多いです。

軽くてセキュリティ的にも強いコンテナを作るために、シェルやlibcが入っていないイメージを使うこともあります。

普通にアプリ作って確認

なので、以下のような

package main

import "fmt"

func main() {
    ch := make(chan int)

    go func(out chan<- int) {
        defer close(out)
        for i := 0; i < 10; i++ {
            out <- i
        }
    }(ch)

    for v := range ch {
        fmt.Println(v)
    }
}

をビルドして、確認してみると

$ go build

$ file ./app | tr , '\n' | grep -F 'linked'
 statically linked

$ ldd ./app
        not a dynamic executable

と出ます。static linkedなバイナリは自分だけで動けるはずなので、何も無い環境に持って行っても動きます。

簡単に実現できるのは chroot してやることですね。

$ sudo chroot . ./app
0
1
2
3
4
5
6
7
8
9

ちゃんと動きます。

os/user or net パッケージを使うと?

問題はここからです。 os/user パッケージ or net パッケージを使うと結果が変わります。(補足: os/userパッケージに関しては、Windowsの場合Go実装のもの一つしか存在しないので、以下のように動的リンクにはなりません。以下はLinuxの場合。)

package main

import (
    "fmt"
    "os/user"
)

func main() {
    u, _ := user.Current()
    fmt.Println(u.Username)
}

確認してみます。

$ go build

$ file ./app | tr , '\n' | grep -F 'linked'
 dynamically linked

$ ldd ./app
        linux-vdso.so.1 (0x00007ffe695fc000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9e7e3c1000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9e7e1cf000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f9e7e3ee000)

dynamically linked に表示が変わりました。 Go では os/user と net パッケージに関しては cgo を使った実装がデフォルトでは適用されます。

動的リンクしているアプリケーションになっているので、chroot した環境では動きません。

$ ./app
gitpod

$ sudo chroot . ./app
chroot: failed to run command ‘./app’: No such file or directory

./app が存在しないってメッセージですが、実際には依存している ld-linux-x86-64.so.2 が無いからです。

んで、これを static linked なバイナリにするためのやり方が大きく2つあります。

CGO_ENABLED=0 を指定

最も手っ取り早いのが cgo を無効にしちゃうことですね。 アプリが cgo に依存していなければこれで終わりです。

$ CGO_ENABLED=0 go build

$ file ./app | tr , '\n' | grep -F 'linked'
 statically linked

$ ./app
gitpod

$ sudo chroot . ./app
root

osusergo, netgo のタグをビルド時に付与

Pure Goな実装を使うようにタグ指定することでも出来ます。

$ go build -tags osusergo,netgo

$ file ./app | tr , '\n' | grep -F 'linked'
 statically linked

$ ./app
gitpod

$ sudo chroot . ./app
root

参考情報


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。