Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18567] ファイルの概要

このコミットは、Goツールチェーンにおいて、一時的に生成され実行後に削除されるバイナリ(go rungo 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 rungo testコマンドによって生成され、実行後に削除される実行ファイル)に対してDWARFデバッグ情報を書き込まないようにする変更です。これらのバイナリは通常、デバッガ(GDBなど)で使用されることがないため、デバッグ情報を生成する必要がありません。リンカに-wフラグを渡すことで、デバッグ情報の生成にかかる時間とディスクスペースを節約します。この変更により、ほとんどのバイナリのサイズが約25%削減されます。

変更の背景

Goのビルドプロセスでは、デフォルトで生成されるバイナリにDWARFデバッグ情報が含まれています。この情報は、デバッガがプログラムの実行中に変数、関数、ソースコードの行番号などを特定するために不可欠です。しかし、go rungo testコマンドによって生成されるバイナリは、通常、一時的な目的で使用され、実行が完了するとすぐに削除されます。これらの「一時的なバイナリ(ephemeral binaries)」は、デバッグツールによって検査されることがほとんどないため、デバッグ情報を含めることは無駄なオーバーヘッドとなります。

具体的には、以下の問題がありました。

  1. バイナリサイズの増大: DWARFデバッグ情報は非常に大きく、最終的な実行ファイルのサイズを大幅に増加させます。これにより、ディスクスペースの消費が増え、ネットワーク経由での転送時間も長くなります。
  2. ビルド時間の増加: デバッグ情報の生成は、リンカの処理に時間を要します。特に大規模なプロジェクトでは、このオーバーヘッドがビルド全体の時間を延ばす要因となります。

これらの問題を解決し、開発者の生産性を向上させるために、一時的なバイナリから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/compile)がGoのソースコードをオブジェクトファイルに変換した後、Goリンカ(cmd/link)がこれらのオブジェクトファイルと必要なライブラリを結合して、最終的な実行可能なバイナリを生成します。リンカは、シンボル解決、アドレスの割り当て、デバッグ情報の埋め込みなど、バイナリ生成の最終段階を担当します。

リンカの-wフラグ

Goリンカには、バイナリの生成を制御するための様々なフラグがあります。その一つが-wフラグです。

  • -w: このフラグをリンカに渡すと、DWARFデバッグ情報の生成を抑制します。これにより、生成されるバイナリのサイズが大幅に削減されます。ただし、デバッグ情報がなくなるため、デバッガでそのバイナリをソースコードレベルでデバッグすることはできなくなります。

このコミットでは、一時的なバイナリに対してこの-wフラグを自動的に適用することで、開発者の手動操作なしにバイナリサイズとビルド時間の最適化を図っています。

技術的詳細

この変更は、Goツールチェーンのcmd/goコマンド内部で、リンカを呼び出す際の挙動を修正することで実現されています。

  1. Package構造体の拡張: src/cmd/go/pkg.goにあるPackage構造体にomitDWARF boolという新しいフィールドが追加されました。このフィールドは、そのパッケージから生成されるバイナリがDWARFデバッグ情報を省略すべきかどうかを示すブール値です。
  2. go runコマンドの変更: src/cmd/go/run.gorunRun関数内で、go runによって実行されるパッケージのomitDWARFフィールドがtrueに設定されるようになりました。これにより、go runが生成する一時バイナリにはデバッグ情報が含まれなくなります。
  3. go testコマンドの変更: src/cmd/go/test.gotest関数内で、テストバイナリ(特にgo test -cgo test -iのようにバイナリを保持しない場合)のomitDWARFフィールドが条件付きでtrueに設定されるようになりました。具体的には、!testC && !testNeedBinaryの場合にomitDWARFtrueになります。これは、テストバイナリが一時的なものであり、デバッグ情報が不要な場合に適用されます。
  4. リンカ呼び出しの変更: src/cmd/go/build.gogcToolchain構造体のld(リンカを呼び出す)メソッド内で、PackageomitDWARFフィールドがtrueの場合に、リンカオプションに-wフラグが追加されるようになりました。これにより、リンカはDWARFデバッグ情報を生成しなくなります。
  5. テストの更新: test/run.golinkFile関数(テストヘルパー)でも、リンカ呼び出しに明示的に-wフラグが追加されました。これは、この変更が正しく機能することを確認するためのテスト関連の修正です。

この一連の変更により、go rungo testが生成するバイナリは、自動的にデバッグ情報が省略され、サイズが小さくなります。この最適化は、開発者が意識することなく、Goツールチェーンの効率を向上させます。

コアとなるコードの変更箇所

  • src/cmd/go/build.go: リンカ呼び出しロジックに-wフラグを追加する条件分岐が追加されました。
  • src/cmd/go/pkg.go: Package構造体にomitDWARFフィールドが追加されました。
  • src/cmd/go/run.go: go runコマンドの処理で、生成されるパッケージのomitDWARFtrueに設定するようになりました。
  • 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.

この変更は、gcToolchainldメソッド(リンカを呼び出す部分)にあります。p.omitDWARFtrueの場合、リンカに渡すフラグリスト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のリリースサイクル中に作成された可能性があります)