[インデックス 18567] ファイルの概要
このコミットは、Goツールチェーンにおいて、一時的に生成され実行後に削除されるバイナリ(go run
やgo test
などで生成されるもの)に対して、DWARFデバッグ情報の書き込みをスキップするように変更を加えるものです。これにより、バイナリのサイズが削減され、ビルド時間も短縮されます。
コミット
commit ae38b03f6cab6a25f9d8d34a39e33db9857dce2e
Author: Russ Cox <rsc@golang.org>
Date: Wed Feb 19 10:01:15 2014 -0500
cmd/go: skip writing dwarf debug info for ephemeral binaries
Update #6853
For an ephemeral binary - one created, run, and then deleted -
there is no need to write dwarf debug information, since the
binary will not be used with gdb. In this case, instruct the linker
not to spend time and disk space generating the debug information
by passing the -w flag to the linker.
Omitting dwarf information reduces the size of most binaries by 25%.
We may be more aggressive about this in the future.
LGTM=bradfitz, r
R=r, bradfitz
CC=golang-codereviews
https://golang.org/cl/65890043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ae38b03f6cab6a25f9d8d34a39e33db9857dce2e
元コミット内容
このコミットは、Goのcmd/go
ツールが、一時的なバイナリ(go run
やgo test
コマンドによって生成され、実行後に削除される実行ファイル)に対してDWARFデバッグ情報を書き込まないようにする変更です。これらのバイナリは通常、デバッガ(GDBなど)で使用されることがないため、デバッグ情報を生成する必要がありません。リンカに-w
フラグを渡すことで、デバッグ情報の生成にかかる時間とディスクスペースを節約します。この変更により、ほとんどのバイナリのサイズが約25%削減されます。
変更の背景
Goのビルドプロセスでは、デフォルトで生成されるバイナリにDWARFデバッグ情報が含まれています。この情報は、デバッガがプログラムの実行中に変数、関数、ソースコードの行番号などを特定するために不可欠です。しかし、go run
やgo test
コマンドによって生成されるバイナリは、通常、一時的な目的で使用され、実行が完了するとすぐに削除されます。これらの「一時的なバイナリ(ephemeral binaries)」は、デバッグツールによって検査されることがほとんどないため、デバッグ情報を含めることは無駄なオーバーヘッドとなります。
具体的には、以下の問題がありました。
- バイナリサイズの増大: DWARFデバッグ情報は非常に大きく、最終的な実行ファイルのサイズを大幅に増加させます。これにより、ディスクスペースの消費が増え、ネットワーク経由での転送時間も長くなります。
- ビルド時間の増加: デバッグ情報の生成は、リンカの処理に時間を要します。特に大規模なプロジェクトでは、このオーバーヘッドがビルド全体の時間を延ばす要因となります。
これらの問題を解決し、開発者の生産性を向上させるために、一時的なバイナリからDWARFデバッグ情報を省略する変更が提案されました(Issue #6853)。これにより、開発サイクルが高速化され、リソースの消費が抑えられます。
前提知識の解説
DWARF (Debugging With Attributed Record Formats)
DWARFは、プログラムのデバッグを可能にするための標準的なデバッグ情報フォーマットです。コンパイラやリンカによって生成された実行ファイルやライブラリに埋め込まれます。デバッガ(例: GDB, LLDB)は、このDWARF情報を使用して、ソースコードの行番号、変数名、型情報、関数名、スタックフレームの構造などを、実行中のバイナリの機械語と関連付けます。これにより、開発者はソースコードレベルでプログラムをステップ実行したり、変数の値を検査したりすることができます。
DWARF情報は非常に詳細であるため、バイナリのサイズを大幅に増加させる傾向があります。デバッグ情報が不要な本番環境のデプロイメントでは、通常、バイナリからDWARF情報を除去する(stripする)ことが推奨されます。
エフェメラルバイナリ (Ephemeral Binaries)
「エフェメラルバイナリ」とは、一時的に生成され、特定のタスクの実行後にすぐに破棄される実行ファイルを指します。Goの文脈では、主に以下のコマンドによって生成されるバイナリがこれに該当します。
go run
: ソースファイルをコンパイルし、その場で実行可能なバイナリを生成して実行します。実行後、この一時バイナリは削除されます。go test
: テストコードをコンパイルし、テストを実行するためのバイナリを生成します。テスト実行後、このバイナリも削除されます。
これらのバイナリは、開発者がコードを迅速にテストしたり、小さなスクリプトを実行したりするために使用されるため、デバッガで詳細に検査されることは稀です。
Goリンカ (cmd/link
)
Goコンパイラ(cmd/compile
)がGoのソースコードをオブジェクトファイルに変換した後、Goリンカ(cmd/link
)がこれらのオブジェクトファイルと必要なライブラリを結合して、最終的な実行可能なバイナリを生成します。リンカは、シンボル解決、アドレスの割り当て、デバッグ情報の埋め込みなど、バイナリ生成の最終段階を担当します。
リンカの-w
フラグ
Goリンカには、バイナリの生成を制御するための様々なフラグがあります。その一つが-w
フラグです。
-w
: このフラグをリンカに渡すと、DWARFデバッグ情報の生成を抑制します。これにより、生成されるバイナリのサイズが大幅に削減されます。ただし、デバッグ情報がなくなるため、デバッガでそのバイナリをソースコードレベルでデバッグすることはできなくなります。
このコミットでは、一時的なバイナリに対してこの-w
フラグを自動的に適用することで、開発者の手動操作なしにバイナリサイズとビルド時間の最適化を図っています。
技術的詳細
この変更は、Goツールチェーンのcmd/go
コマンド内部で、リンカを呼び出す際の挙動を修正することで実現されています。
Package
構造体の拡張:src/cmd/go/pkg.go
にあるPackage
構造体にomitDWARF bool
という新しいフィールドが追加されました。このフィールドは、そのパッケージから生成されるバイナリがDWARFデバッグ情報を省略すべきかどうかを示すブール値です。go run
コマンドの変更:src/cmd/go/run.go
のrunRun
関数内で、go run
によって実行されるパッケージのomitDWARF
フィールドがtrue
に設定されるようになりました。これにより、go run
が生成する一時バイナリにはデバッグ情報が含まれなくなります。go test
コマンドの変更:src/cmd/go/test.go
のtest
関数内で、テストバイナリ(特にgo test -c
やgo test -i
のようにバイナリを保持しない場合)のomitDWARF
フィールドが条件付きでtrue
に設定されるようになりました。具体的には、!testC && !testNeedBinary
の場合にomitDWARF
がtrue
になります。これは、テストバイナリが一時的なものであり、デバッグ情報が不要な場合に適用されます。- リンカ呼び出しの変更:
src/cmd/go/build.go
のgcToolchain
構造体のld
(リンカを呼び出す)メソッド内で、Package
のomitDWARF
フィールドがtrue
の場合に、リンカオプションに-w
フラグが追加されるようになりました。これにより、リンカはDWARFデバッグ情報を生成しなくなります。 - テストの更新:
test/run.go
のlinkFile
関数(テストヘルパー)でも、リンカ呼び出しに明示的に-w
フラグが追加されました。これは、この変更が正しく機能することを確認するためのテスト関連の修正です。
この一連の変更により、go run
やgo test
が生成するバイナリは、自動的にデバッグ情報が省略され、サイズが小さくなります。この最適化は、開発者が意識することなく、Goツールチェーンの効率を向上させます。
コアとなるコードの変更箇所
src/cmd/go/build.go
: リンカ呼び出しロジックに-w
フラグを追加する条件分岐が追加されました。src/cmd/go/pkg.go
:Package
構造体にomitDWARF
フィールドが追加されました。src/cmd/go/run.go
:go run
コマンドの処理で、生成されるパッケージのomitDWARF
をtrue
に設定するようになりました。src/cmd/go/test.go
:go test
コマンドの処理で、テストバイナリのomitDWARF
を条件付きでtrue
に設定するようになりました。test/run.go
: テスト用のリンカ呼び出しに-w
フラグが追加されました。
コアとなるコードの解説
src/cmd/go/build.go
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1714,6 +1714,10 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action,\
if buildContext.InstallSuffix != "" {
ldflags = append(ldflags, "-installsuffix", buildContext.InstallSuffix)
}
+ if p.omitDWARF {
+ ldflags = append(ldflags, "-w")
+ }
+
// If the user has not specified the -extld option, then specify the
// appropriate linker. In case of C++ code, use the compiler named
// by the CXX environment variable or defaultCXX if CXX is not set.
この変更は、gcToolchain
のld
メソッド(リンカを呼び出す部分)にあります。p.omitDWARF
がtrue
の場合、リンカに渡すフラグリストldflags
に-w
が追加されます。これにより、DWARFデバッグ情報の生成が抑制されます。
src/cmd/go/pkg.go
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -89,6 +89,7 @@ type Package struct {\n exeName string // desired name for temporary executable\n coverMode string // preprocess Go source files with the coverage tool in this mode\n coverVars map[string]*CoverVar // variables created by coverage analysis\n+\tomitDWARF bool // tell linker not to write DWARF information\n }\n \n // CoverVar holds the name of the generated coverage variables targeting the named file.\n```
`Package`構造体に`omitDWARF bool`フィールドが追加されました。このフィールドは、そのパッケージのビルド時にDWARF情報を省略するかどうかを制御します。
### `src/cmd/go/run.go`
```diff
--- a/src/cmd/go/run.go
+++ b/src/cmd/go/run.go
@@ -58,6 +58,7 @@ func runRun(cmd *Command, args []string) {\n if p.Error != nil {\n fatalf("%s", p.Error)\n }\n+\tp.omitDWARF = true\n for _, err := range p.DepsErrors {\n errorf("%s", err)\n }\n```
`runRun`関数は`go run`コマンドのエントリポイントです。ここで、実行されるパッケージ`p`の`omitDWARF`フィールドが`true`に設定されます。これにより、`go run`が生成する一時バイナリはデバッグ情報なしでリンクされます。
### `src/cmd/go/test.go`
```diff
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -654,6 +654,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\n pkgdir: testDir,\n fake: true,\n Stale: true,\n+\t\tomitDWARF: !testC && !testNeedBinary,\n }\n if pxtest != nil {\n pmain.imports = append(pmain.imports, pxtest)\n```
`test`関数は`go test`コマンドのビルドロジックを含みます。ここで、テスト実行用のメインパッケージ`pmain`の`omitDWARF`フィールドが設定されます。条件`!testC && !testNeedBinary`は、`go test -c`(テストバイナリをコンパイルして残す)や、テストバイナリが必要な場合でない限り、DWARF情報を省略することを示しています。
### `test/run.go`
```diff
--- a/test/run.go
+++ b/test/run.go
@@ -200,7 +200,7 @@ func compileInDir(runcmd runCmd, dir string, names ...string) (out []byte, err e\n \n func linkFile(runcmd runCmd, goname string) (err error) {\n pfile := strings.Replace(goname, ".go", "."+letter, -1)\n-\t_, err = runcmd("go", "tool", ld, "-o", "a.exe", "-L", ".", pfile)\n+\t_, err = runcmd("go", "tool", ld, "-w", "-o", "a.exe", "-L", ".", pfile)\n return\n }\n \n```
このファイルはGoのテストスイートの一部です。`linkFile`関数はテスト中にリンカを呼び出すヘルパー関数であり、この変更により明示的に`-w`フラグが追加されました。これは、このコミットの変更が正しく機能することを確認するためのテストの調整です。
## 関連リンク
* Go Gerrit Code Review: [https://golang.org/cl/65890043](https://golang.org/cl/65890043)
* Go Issue 6853: [https://golang.org/issue/6853](https://golang.org/issue/6853)
## 参考にした情報源リンク
* DWARF Debugging Format: [https://dwarfstd.org/](https://dwarfstd.org/)
* Go Command Documentation (`go run`, `go test`): [https://pkg.go.dev/cmd/go](https://pkg.go.dev/cmd/go)
* Go Linker (`cmd/link`) documentation (unofficial, but useful): [https://go.dev/src/cmd/link/internal/ld/main.go](https://go.dev/src/cmd/link/internal/ld/main.go) (Goソースコード内のリンカのメインファイル)
* Stack Overflow - What does `go build -ldflags "-s -w"` do?: [https://stackoverflow.com/questions/22463126/what-does-go-build-ldflags-s-w-do](https://stackoverflow.com/questions/22463126/what-does-go-build-ldflags-s-w-do)
* Go Wiki - BuildModes: [https://go.dev/wiki/BuildModes](https://go.dev/wiki/BuildModes) (ビルドモードに関する情報)
* Go Wiki - Debugging: [https://go.dev/wiki/Debugging](https://go.dev/wiki/Debugging) (Goのデバッグに関する情報)
* Go Blog - Go 1.2 Release Notes: [https://go.dev/doc/go1.2](https://go.dev/doc/go1.2) (このコミットはGo 1.2のリリースサイクル中に作成された可能性があります)