[インデックス 17730] ファイルの概要
このコミットは、Go言語のツールチェインを静的にリンクしてビルドする機能を追加するものです。これにより、特定のlibc
(C標準ライブラリ)のバージョンに依存することなく、より幅広いLinuxディストリビューションやFreeBSD、OpenBSD、NetBSDなどのELFターゲットOSでGoツールチェインが動作するようになります。
コミット
commit a978b1e049268cd2726c521fa3526976c7af7351
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Oct 2 20:14:54 2013 -0400
misc/dist: support building statically linked toolchain.
so that we don't need worry about specifying the required
libc version (note: as cmd/go will still be dynamically
linked to libc, we still need to perform the build on OSes
with an old enough libc. But as cmd/go doesn't rely on many
libc symbols, the situation should be significantly better).
Fixes #3564.
R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/14261043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a978b1e049268cd2726c521fa3526976c7af7351
元コミット内容
misc/dist
: 静的にリンクされたツールチェインのビルドをサポート。
これにより、必要なlibc
のバージョンを指定する心配がなくなります(注:cmd/go
は引き続きlibc
に動的にリンクされるため、古いlibc
を持つOS上でビルドを実行する必要はありますが、cmd/go
は多くのlibc
シンボルに依存しないため、状況は大幅に改善されるはずです)。
Issue #3564 を修正。
変更の背景
Go言語のツールチェイン(go
コマンド、コンパイラ、リンカなど)は、通常、システムにインストールされているC標準ライブラリ(libc
)に動的にリンクされます。これは、多くのUnix系システムにおける一般的なソフトウェアの配布方法です。しかし、この動的リンクにはいくつかの問題がありました。
libc
のバージョン依存性: 異なるLinuxディストリビューションやOSバージョンでは、libc
のバージョンが異なることがあります。新しいバージョンのlibc
でビルドされたGoツールチェインが、古いバージョンのlibc
しか持たないシステムで実行しようとすると、必要なシンボルが見つからずにエラーとなる可能性がありました。これは、特にGoのバイナリ配布において、ユーザーがGoをインストールする環境のlibc
バージョンを考慮する必要があるという課題を生んでいました。- 配布の複雑さ: Goの公式バイナリ配布は、特定の
libc
バージョンに依存しないように、古いlibc
バージョンを持つ環境でビルドされる必要がありました。これは、ビルドプロセスの複雑さを増し、特定のビルド環境の維持を必要としました。
このコミットは、これらの問題を緩和するために、Goツールチェインの主要部分を静的にリンクするオプションを提供します。これにより、ツールチェイン自体がlibc
の特定のバージョンに依存しなくなり、よりポータブルなバイナリの配布が可能になります。ただし、コミットメッセージにもあるように、cmd/go
(Goコマンド自体)は一部のlibc
シンボルに動的にリンクされ続けるため、完全にlibc
から独立するわけではありませんが、依存性は大幅に軽減されます。
前提知識の解説
1. 静的リンクと動的リンク
プログラムが外部のライブラリ(例えば、C標準ライブラリであるlibc
)の機能を利用する場合、そのライブラリをプログラムに結合する方法には大きく分けて「静的リンク」と「動的リンク」の2種類があります。
-
静的リンク (Static Linking):
- プログラムのビルド時に、必要なライブラリのコードが実行可能ファイルに直接コピーされ、組み込まれます。
- 利点:
- 実行可能ファイルが自己完結型になり、外部ライブラリの存在に依存しないため、異なるシステム間での移植性が高いです。
- 実行時にライブラリのロードが不要なため、起動がわずかに速くなることがあります。
- 欠点:
- 実行可能ファイルのサイズが大きくなります。
- 複数のプログラムが同じライブラリを使用する場合、それぞれのプログラムがライブラリのコピーを持つため、ディスクスペースの無駄が生じます。
- ライブラリにセキュリティパッチが適用された場合、そのライブラリを静的リンクしているすべてのプログラムを再ビルドする必要があります。
-
動的リンク (Dynamic Linking):
- プログラムのビルド時には、ライブラリのコードは実行可能ファイルに組み込まれず、代わりにライブラリへの参照(シンボル情報など)のみが埋め込まれます。
- プログラムの実行時に、オペレーティングシステム(OS)のローダーが、必要なライブラリをメモリにロードし、プログラムと結合します。
- 利点:
- 実行可能ファイルのサイズが小さくなります。
- 複数のプログラムが同じライブラリを使用する場合、ライブラリのコードはメモリ上に一度だけロードされ、共有されるため、メモリ効率が良いです。
- ライブラリにセキュリティパッチが適用された場合、ライブラリを更新するだけで、そのライブラリを使用するすべてのプログラムが恩恵を受けられます(再ビルド不要)。
- 欠点:
- プログラムを実行するシステムに、必要なライブラリが適切なバージョンでインストールされている必要があります。ライブラリが見つからない、またはバージョンが合わない場合、プログラムは実行できません(「DLL地獄」や「共有ライブラリの依存性問題」として知られる)。
- 実行時にライブラリのロードとリンクのオーバーヘッドがあります。
2. libc
(C標準ライブラリ)
libc
は、C言語で書かれたプログラムがOSの機能(ファイルI/O、メモリ管理、文字列操作、数学関数など)を利用するための標準的なインターフェースを提供するライブラリです。Unix系OSでは、glibc
(GNU C Library) や musl libc
など、いくつかの実装が存在します。GoプログラムはC言語で書かれていないものの、OSとの低レベルなやり取りや、一部のネットワーク操作、DNS解決などにおいて、内部的にlibc
の機能を利用することがあります。
3. ELF (Executable and Linkable Format)
ELFは、Unix系OS(Linux、FreeBSD、Solarisなど)で広く使用されている実行可能ファイル、オブジェクトコード、共有ライブラリ、コアダンプファイルの標準フォーマットです。このコミットでは、「ELFターゲット」という言葉が出てきますが、これはELFフォーマットを使用するOS(主にLinux、BSD系)を指します。WindowsやmacOSはそれぞれ独自の実行可能ファイルフォーマット(PE、Mach-O)を使用しています。
4. Goツールチェイン
Goツールチェインとは、Go言語でプログラムを開発・実行するために必要な一連のツール群を指します。これには、以下のようなものが含まれます。
go
コマンド: ビルド、テスト、実行、フォーマットなど、Go開発の主要な操作を行うためのコマンドラインツール。go build
: Goソースコードを実行可能ファイルにコンパイルするコンパイラ。go link
: オブジェクトファイルを結合して実行可能ファイルを生成するリンカ。go vet
: 静的解析ツール。go fmt
: コードフォーマッタ。go test
: テストフレームワーク。
これらのツール自体もGo言語で書かれており、実行可能ファイルとして提供されます。
技術的詳細
このコミットは、Goツールチェインのビルドプロセスを制御するmisc/dist/bindist.go
ファイルに修正を加えています。具体的には、以下の機能が追加されています。
-
-static
フラグの追加:bindist.go
は、Goのバイナリ配布パッケージを生成するためのスクリプトです。このスクリプトに、--static
という新しいコマンドラインフラグが追加されました。このフラグがtrue
に設定されると、ツールチェインのビルド時に静的リンクが試みられます。デフォルト値はtrue
です。 -
静的リンクが可能なOSの定義:
staticLinkAvailable
という新しい変数(文字列スライス)が導入され、静的リンクがサポートされるOSのリストが定義されています。現時点では、"linux"
,"freebsd"
,"openbsd"
,"netbsd"
といったELFターゲットのOSがリストされています。これは、静的リンクがOSのリンカやローダーの挙動に依存するため、すべてのOSで一律にサポートできるわけではないことを示しています。 -
ビルドプロセスへの静的リンクオプションの組み込み:
Build
構造体にstatic
というブール型のフィールドが追加され、このビルドが静的リンクを行うべきかどうかを保持します。main
関数内で、--static
フラグが有効で、かつ現在のビルドターゲットのOSがstaticLinkAvailable
リストに含まれている場合、b.static
フィールドがtrue
に設定されます。 -
GO_DISTFLAGS
環境変数の利用:Build
構造体のenv()
メソッドは、ビルドプロセスに渡される環境変数を設定します。このメソッド内で、b.static
がtrue
の場合、GO_DISTFLAGS=-s
という環境変数が追加されます。 このGO_DISTFLAGS
は、Goの内部ビルドシステムが使用する環境変数であり、-s
フラグはリンカに対してシンボルテーブルを削除する(stripする)ことを指示するだけでなく、静的リンクを強制する効果も持っています。これにより、Goツールチェインのバイナリがlibc
などの外部ライブラリに動的にリンクされるのを防ぎ、可能な限り静的にリンクされるようになります。
この変更により、Goのビルドシステムは、特定のOS上でツールチェインをビルドする際に、静的リンクを試みるようになります。これにより、生成されるツールチェインのバイナリは、より少ない外部依存性で動作し、異なるlibc
バージョンを持つシステム間での互換性が向上します。
ただし、コミットメッセージにも明記されているように、cmd/go
(Goコマンド自体)は、一部のlibc
シンボルに動的にリンクされ続ける可能性があります。これは、GoのランタイムがOSの低レベルな機能(例えば、DNS解決など)を利用するために、どうしてもlibc
の特定の機能に依存せざるを得ない場合があるためです。しかし、その依存性は最小限に抑えられ、以前よりも古いlibc
バージョンでも動作する可能性が高まります。
コアとなるコードの変更箇所
misc/dist/bindist.go
ファイルに以下の変更が加えられています。
-
新しいフラグの追加:
--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -38,6 +38,7 @@ var ( addLabel = flag.String("label", "", "additional label to apply to file when uploading") includeRace = flag.Bool("race", true, "build race detector packages") versionOverride = flag.String("version", "", "override version name") + staticToolchain = flag.Bool("-static", true, "try to build statically linked toolchain (only supported on ELF targets)") username, password string // for Google Code upload )
-
静的リンクが可能なOSのリスト定義:
--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -106,6 +107,15 @@ var raceAvailable = []string{ "windows-amd64", } +// The OSes that support building statically linked toolchain +// Only ELF platforms are supported. +var staticLinkAvailable = []string{ + "linux", + "freebsd", + "openbsd", + "netbsd", +} + var fileRe = regexp.MustCompile( `^(go[a-z0-9-.]+)\.(src|([a-z0-9]+)-([a-z0-9]+)(?:-([a-z0-9.]))?)\.`)
-
ビルド構造体への
static
フィールド追加:--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -184,6 +201,7 @@ type Build struct { Label string root string gopath string + static bool // if true, build statically linked toolchain } func (b *Build) Do() error {
-
ビルドプロセスでの
static
フラグの適用ロジック:--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -169,6 +179,13 @@ func main() { } } } + if *staticToolchain { + for _, os := range staticLinkAvailable { + if b.OS == os { + b.static = true + } + } + } } if err := b.Do(); err != nil { log.Printf("%s: %v", targ, err)
-
環境変数
GO_DISTFLAGS
の設定:--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -582,6 +600,9 @@ func (b *Build) env() []string {\n "GOROOT_FINAL="+final,\n "GOPATH="+b.gopath,\n )\n + if b.static {\n + env = append(env, "GO_DISTFLAGS=-s")\n + }\n return env }
コアとなるコードの解説
このコミットの核心は、misc/dist/bindist.go
ファイルに新しいビルドオプションを追加し、Goツールチェインのビルド時に静的リンクを有効にするロジックを組み込んだ点にあります。
-
staticToolchain
フラグ:flag.Bool("-static", true, ...)
によって、コマンドラインから-static
オプションを受け付けるようにしています。デフォルトでtrue
に設定されているため、特に指定がなければ静的リンクが試みられます。これは、Goの公式バイナリ配布がデフォルトで静的リンクされたツールチェインを提供する意図があることを示唆しています。 -
staticLinkAvailable
リスト: このリストは、静的リンクがサポートされるOSを明示的に定義しています。これは、静的リンクがOSのリンカやシステムコールインターフェースに依存するため、すべてのOSで一様に機能するわけではないという現実を反映しています。特に、WindowsやmacOSのようなELF以外のシステムでは、この静的リンクオプションは適用されません。 -
Build
構造体のstatic
フィールド:Build
構造体は、特定のターゲットOS/アーキテクチャ向けのGoツールチェインのビルド設定をカプセル化します。このstatic
フィールドは、そのビルドが静的リンクを行うべきかどうかを示す内部フラグとして機能します。 -
main
関数内のロジック:main
関数内で、各ビルドターゲット(b
)に対して、staticToolchain
フラグが有効であり、かつそのターゲットのOSがstaticLinkAvailable
リストに含まれている場合にのみ、b.static
がtrue
に設定されます。これにより、静的リンクはサポートされるプラットフォームでのみ試みられるようになります。 -
env()
メソッドとGO_DISTFLAGS=-s
:Build
構造体のenv()
メソッドは、Goのビルドコマンド(go tool compile
,go tool link
など)に渡される環境変数を生成します。if b.static { env = append(env, "GO_DISTFLAGS=-s") }
という行が最も重要です。GO_DISTFLAGS
はGoの内部ビルドシステムが使用する環境変数で、ビルドプロセスに特定のフラグを渡すために使われます。-s
フラグは、Goのリンカ(go tool link
)に渡されると、通常はシンボルテーブルを削除する(stripする)ことを意味しますが、この文脈では、Goのツールチェインのビルドにおいて、可能な限り静的リンクを行うようにリンカに指示する役割も果たします。これにより、生成されるGoツールチェインのバイナリ(go
コマンド、コンパイラ、リンカなど)が、システムにインストールされているlibc
などの共有ライブラリに動的に依存するのではなく、必要なコードをバイナリ内部に含めるようになります。
この一連の変更により、Goの公式バイナリ配布や、ユーザーがmisc/dist/bindist.go
スクリプトを使用してツールチェインをビルドする際に、よりポータブルで依存性の少ないバイナリを生成できるようになりました。これは、特にコンテナ環境や、libc
のバージョンが古いシステムでのGoの利用を容易にする上で大きな改善となります。
関連リンク
- Go言語の公式ウェブサイト: https://golang.org/
- Go言語のソースコードリポジトリ (GitHub): https://github.com/golang/go
- Go言語のIssue Tracker: https://github.com/golang/go/issues (ただし、Issue #3564は直接見つかりませんでした。古いIssueトラッカーの参照かもしれません。)
- Go Code Review (Gerrit): https://go-review.googlesource.com/ (コミットメッセージにある
https://golang.org/cl/14261043
はGerritの変更リストへのリンクです。)
参考にした情報源リンク
- 静的リンクと動的リンクに関する一般的な情報源 (例: Wikipedia, 各種プログラミングブログ)
- C標準ライブラリ (libc) に関する一般的な情報源 (例: Wikipedia)
- ELFフォーマットに関する一般的な情報源 (例: Wikipedia)
- Go言語のビルドシステムに関するドキュメント (Goの公式ドキュメントやソースコード)
- Go言語のIssueトラッカー (過去のIssueや議論)
- Go言語のGerrit (コードレビューシステム)
- コミットメッセージとコード差分自体