[インデックス 18566] ファイルの概要
このコミットは、Go言語のリンカ (cmd/ld
) において、生成されるバイナリサイズを削減することを目的としています。具体的には、ガベージコレクション (GC) の情報を持つ .gcargs
および .gclocals
シンボルをシンボルテーブルから削除し、それらを go.func
データというメタシンボルに統合することで、バイナリの肥大化を抑制します。これにより、一般的なGoバイナリのサイズが約10%削減される効果があります。
コミット
commit 2541cc81978dc5e41e2e2db6345d8ca7a365ca8c
Author: Russ Cox <rsc@golang.org>
Date: Wed Feb 19 10:00:44 2014 -0500
cmd/ld: drop gcargs, gclocals symbols from symbol table
Update #6853
Every function now has a gcargs and gclocals symbol
holding associated garbage collection information.
Put them all in the same meta-symbol as the go.func data
and then drop individual entries from symbol table.
Removing gcargs and gclocals reduces the size of a
typical binary by 10%.
LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/65870044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2541cc81978dc5e41e2e2db6345d8ca7a365ca8c
元コミット内容
cmd/ld: drop gcargs, gclocals symbols from symbol table
Update #6853
Every function now has a gcargs and gclocals symbol
holding associated garbage collection information.
Put them all in the same meta-symbol as the go.func data
and then drop individual entries from symbol table.
Removing gcargs and gclocals reduces the size of a
typical binary by 10%.
変更の背景
この変更は、Go言語のバイナリサイズが肥大化しているという問題(Issue #6853)に対応するために行われました。Issue #6853 は「all: binaries too big and growing」と題されており、Goのリリース間でバイナリサイズが大幅に増加していることが指摘されていました。特に、symtab
(シンボルテーブル)やpclntab
(PC-lineテーブル)、リフレクト型、静的Go文字列データなどがバイナリサイズに寄与していると議論されていました。
Goの各関数は、ガベージコレクションに必要な情報(引数とローカル変数のポインタ情報)を保持するために、それぞれ .gcargs
と .gclocals
というシンボルを持っていました。これらのシンボルが個別にシンボルテーブルにエントリとして存在することで、バイナリサイズが増加していました。このコミットは、これらの情報を go.func
という既存のメタシンボルに統合し、個別のシンボルテーブルエントリを削除することで、バイナリサイズを削減することを目指しました。結果として、一般的なGoバイナリのサイズが10%削減されるという大きな効果が得られました。
前提知識の解説
Goリンカ (cmd/ld
) の役割
Goリンカ (cmd/ld
、または go tool link
として呼び出されることが多い) は、Goのビルドプロセスにおいて非常に重要な役割を担っています。その主な機能は以下の通りです。
- コンパイル済みコードの結合:
main
パッケージとそのすべての依存関係のコンパイル済みGoアーカイブまたはオブジェクトファイルを読み込み、それらを単一の実行可能バイナリに結合します。 - シンボル解決と再配置: コンパイルされたパッケージからすべてのシンボル(関数、変数)を収集し、最終的なバイナリ内でのアドレスを計算し、それらの間のすべての参照を解決します。これは、コンパイラがパッケージを個別に処理し、他のパッケージの関数やデータの最終アドレスを知らないため不可欠です。
- デッドコードの削除: リンカは、使用されていないコードを識別して削除します。これにより、最終的なバイナリサイズが削減されます。この最適化は、リンカがすべてのパッケージをグローバルに把握しているため、リンク段階で実行されます。
- 実行可能形式の生成: ターゲットオペレーティングシステムの実行可能形式(例: Linux上のELF、Windows上のPE)に必要なヘッダを準備し、最終的な実行可能ファイルを生成します。
- ビルド情報の埋め込み:
-X
などのフラグを使用して、バージョン番号、ビルドタイムスタンプ、コミットハッシュなどのビルド時情報をバイナリに埋め込むことができます。 - バイナリサイズの制御:
-s
や-w
などのフラグを使用して、シンボルテーブルやデバッグ情報を削除し、生成されるバイナリのサイズを大幅に削減できます。
gcargs
および gclocals
シンボル
Goのガベージコレクタ (GC) は、スタック上のポインタを正確に識別するために、各関数のスタックフレームのレイアウトに関するメタデータを必要とします。このメタデータは、コンパイラによって生成され、gcargs
および gclocals
というシンボルとしてバイナリに埋め込まれます。
gcargs
(Garbage Collector Arguments): 関数に渡される引数の中で、ポインタを含むものに関する情報を提供します。関数が呼び出されると、引数は通常スタックに配置されます。gcargs
メタデータは、Goランタイムのガベージコレクタに対し、これらの引数のうちどれがポインタであるかを伝え、GCがそれらをトレースして、それらが指すオブジェクトがまだ「ライブ」(到達可能)であり、回収されるべきではないかを判断できるようにします。gclocals
(Garbage Collector Locals): 同様に、gclocals
は、関数のスタックフレーム内のローカル変数でポインタを含むものに関する情報を提供します。関数が実行されると、ローカル変数のためにスタック上にスペースが割り当てられます。gclocals
メタデータは、GCがこれらのローカル変数のうちどれがポインタであるかを識別するのに役立ち、ヒープに割り当てられたオブジェクトへの参照をスタック上で正しくスキャンできるようにします。
これらのシンボルは、コンパイラが生成するアセンブリ出力に FUNCDATA
ディレクティブとして埋め込まれ、スタックフレームのメモリレイアウトを記述するテーブルやビットマップをバイナリに組み込みます。GCサイクル中、Goランタイムはこのメタデータを使用してスタックをスキャンし、ポインタを識別し、到達可能なすべてのオブジェクトを「ライブ」としてマークします。
技術的詳細
このコミット以前は、Goの各関数に対して生成される gcargs
および gclocals
シンボルは、リンカのシンボルテーブルに個別のエントリとして登録されていました。これは、バイナリのシンボルテーブルが肥大化する一因となっていました。
このコミットの変更は、src/cmd/ld/symtab.c
内の symtab
関数にロジックを追加することで実現されています。symtab
関数は、リンカがシンボルテーブルを構築する際に呼び出される関数です。
変更の核心は、gcargs
および gclocals
シンボルを、既存の go.func
データというメタシンボルに「隠す」ことです。具体的には、これらのシンボルが SGOFUNC
型としてマークされ、hide
フラグが 1
に設定され、outer
フィールドが symgofunc
(go.func
シンボルへのポインタ)に設定されます。
s->type = SGOFUNC;
: シンボルの型をSGOFUNC
に設定します。これは、Goの関数関連のメタデータであることを示します。s->hide = 1;
: このフラグを設定することで、シンボルが通常のシンボルテーブルから「隠され」、外部ツール(例えばnm
コマンド)から直接参照されなくなります。しかし、リンカ内部では引き続きその情報が利用可能です。s->outer = symgofunc;
: このシンボルがsymgofunc
(go.func
シンボル)の「内部」にあることを示します。これにより、gcargs
やgclocals
の情報がgo.func
という単一のメタシンボルに論理的にグループ化されます。
この変更により、個々の gcargs
および gclocals
シンボルがリンカの最終的なシンボルテーブルから削除されるため、バイナリサイズが削減されます。しかし、GCが必要とする情報は go.func
メタシンボルを通じて引き続き利用可能であるため、ランタイムの動作には影響を与えません。
コアとなるコードの変更箇所
変更は src/cmd/ld/symtab.c
ファイルの symtab
関数内で行われています。
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -409,5 +409,10 @@ symtab(void)
if(s->type == STEXT && s->outer == S) {
s->type = SGOFUNC;
s->hide = 1;
s->outer = symgofunc;
}
+ if(strstr(s->name, ".gcargs·") != nil || strstr(s->name, ".gclocals·") != nil || strncmp(s->name, "gcargs·", 8) == 0 || strncmp(s->name, "gclocals·", 10) == 0) {
+ s->type = SGOFUNC;
+ s->hide = 1;
+ s->outer = symgofunc;
+ }
}
}
コアとなるコードの解説
追加されたコードブロックは以下の通りです。
if(strstr(s->name, ".gcargs·") != nil || strstr(s->name, ".gclocals·") != nil || strncmp(s->name, "gcargs·", 8) == 0 || strncmp(s->name, "gclocals·", 10) == 0) {
s->type = SGOFUNC;
s->hide = 1;
s->outer = symgofunc;
}
このC言語のコードスニペットは、リンカがシンボルテーブルを処理するループ内で実行されます。
strstr(s->name, ".gcargs·") != nil || strstr(s->name, ".gclocals·") != nil
:- これは、現在のシンボル
s
の名前 (s->name
) に文字列.gcargs·
または.gclocals·
が含まれているかどうかをチェックします。Goのコンパイラは、これらのGC関連のシンボル名に特定のプレフィックスやサフィックスを付けることが一般的です。·
はGoの内部シンボル名で使われる特殊文字です。
- これは、現在のシンボル
strncmp(s->name, "gcargs·", 8) == 0 || strncmp(s->name, "gclocals·", 10) == 0
:- これは、シンボル名が
gcargs·
またはgclocals·
で始まるかどうかをチェックします。strncmp
は指定されたバイト数だけ文字列を比較します。 - これらの条件のいずれかが真である場合、つまり現在のシンボルがGCの引数またはローカル変数に関する情報を持つシンボルであると識別された場合、以下の処理が実行されます。
- これは、シンボル名が
s->type = SGOFUNC;
:- シンボルの型を
SGOFUNC
に変更します。これは、このシンボルがGoの関数に関連するメタデータであることを示します。
- シンボルの型を
s->hide = 1;
:- シンボルを「隠し」ます。これにより、このシンボルは通常のシンボルテーブルのリストには表示されなくなりますが、リンカ内部ではその情報が保持され、必要に応じてアクセスできます。これはバイナリサイズ削減の主要なメカニズムです。
s->outer = symgofunc;
:- このシンボルが
symgofunc
(go.func
シンボル)の「内部」にあることを示します。これにより、論理的にこれらのGC関連情報がgo.func
という単一のメタシンボルにまとめられ、個別のエントリが不要になります。
- このシンボルが
この変更により、リンカは gcargs
および gclocals
シンボルを特殊な方法で扱い、最終的なバイナリのシンボルテーブルからそれらを削除することで、バイナリサイズを効率的に削減します。
関連リンク
- Go Issue 6853: https://github.com/golang/go/issues/6853
- Go CL 65870044: https://golang.org/cl/65870044
参考にした情報源リンク
- Go
gcargs
andgclocals
symbols purpose: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHtpSi-jC0UdfPrQrq8t5IgFy6_5UzsOYzpr0SzcDfzHUpns4e0WP9ngCW0elzhrA0u727ugq3T4agd0pmssZJu8DE-wbKwAM2CyHIlbG-Zj5I= - Go linker
cmd/ld
role:- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFyiZxhlRIUraa7I0wi__IwUhZzy7iTlnjhTf5R5U0VZnI_f47BfDms6F5jGKLjdq95YT6SGjrVExCkagsBARPlndsQsbPYNJTKI3G40OsOX_pUUlz9
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFEE6LhgbFJsRvgQ9Cjj6lkV85YmuCdJPyRoMEcdtOlFklwBwB-FRazZ1btRj7fxsYm5UX6l7Fg4Xc3JVs6KqnvKMIYHK6LkQ3g6joTFHKuKq_TGg2lzWKjTBTnFwm-xkB8zyNWAFW-ad3v1E3rWYGAAWWiSxthA3abNb1aaat2sEDCqTd24g==
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGD9DBM7OQzAGoPCMxJETp68Vg12DKK2BySNNlJ1KHGFm_DMRBYJvbWSr11rWVn2BWs3cZut-cMoa5xOKf9G7KwssrrvGuTFOF9ogoreczpwFMYmhh5jxt1u6ENo9-zoEepckwPUezfktBgLqwWSFKSiDiWqSUWFGtIf0mZCh0G3eSddj3L2fuZuA8UiOqynZx0bncdB-q1
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHZbehzv-Yk4YLxHy2lNV_jXV4gf3snTj9z3tqleaS-BsSvmkHPkNswIiwrhKrCTtEWaO3i0y-Cy7OVjpRSnzYF0qVly3GNxhc3ZBrNoKTppDnwmsol7eGHJiuyytsWMjVJ-UD_hhwd-OpRa5mZu2AoKZqm3RXLRzj6c_aW5BQ8TEFCj0
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH04OFBT2dA6JSZzS-CtCVXfBS3dEMfkHqSCfhiRaK7LVEeDsY2d-n9oKg3TLxryfld5-5kZPNhiC3pypvy6jcYi1Fo1HhebkwuAoA9LvHInJ8uoRu_XgTdFIkzXpG2txAvNATyZ7TKUEB5HC-0BpKx-t4=
- Go Issue 6853 details: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFjX-vWVZ8IvKPhUzq9qmpwcvZ_3CaMbI_A7FPBzZrmLLlhfY8sC6v1pbt39Dhc-dM4K5krz-98D2m_iPTxIGT5nVvZJNcdRswGmPOsOvPwte5RnrqUUVSfszaH1F4_U1pGXuE=