[インデックス 16485] ファイルの概要
このコミットは、Go言語のリンカ (cmd/ld
) における gcdata
および gcbss
シンボルの取り扱いに関するバグ修正です。具体的には、これらのシンボルがGoのシンボルテーブルに適切に表示されない問題と、ELFシンボルテーブルに重複して表示される問題を解決します。
コミット
commit 251baea1afc07f3ca7ca10935aaba1476cfd34d9
Author: Anthony Martin <ality@pbrane.org>
Date: Tue Jun 4 07:07:22 2013 -0700
cmd/ld: fix gcdata and gcbss symbols
These two symbols don't show up in the Go symbol table
since they're defined in dodata which is called sometime
after symtab. They do, however, show up in the ELF symbol
table.
This regression was introduced in changeset 01c40d533367.
Also, remove the corresponding strings from the ELF strtab
section now that they're unused.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/8650043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/251baea1afc07f3ca7ca10935aaba1476cfd34d9
元コミット内容
cmd/ld: fix gcdata and gcbss symbols
このコミットは、Goリンカ (cmd/ld
) において、gcdata
および gcbss
という2つの重要なシンボルがGoのシンボルテーブルに正しく登録されない問題を修正します。これらのシンボルは、dodata
関数内で定義されており、この関数がシンボルテーブルの処理 (symtab
) の後に呼び出されるため、Goのシンボルテーブルには現れませんでした。しかし、ELFシンボルテーブルには表示されていました。
この問題は、01c40d533367
の変更セットで導入されたリグレッション(退行バグ)でした。
また、この修正の一環として、不要になったこれらのシンボルに対応する文字列がELFの文字列テーブル (strtab
) セクションから削除されました。
変更の背景
Goのリンカは、コンパイルされたGoプログラムを実行可能なバイナリに変換する重要な役割を担っています。このプロセスにおいて、プログラム内の様々なデータやコードに関する情報がシンボルとして管理されます。gcdata
と gcbss
は、Goのガベージコレクション(GC)が正しく機能するために必要なメタデータを含むセクションに関連するシンボルです。
以前の変更セット 01c40d533367
(これは "cmd/ld: remove unused STYPE symbols" というコミットで、gcdata
と gcbss
のシンボルタイプを STYPE
から削除し、dodata
での定義を簡略化したものでした) によって、gcdata
と gcbss
シンボルがGoのシンボルテーブルに登録されなくなりました。これは、これらのシンボルが dodata
関数内で定義されているにもかかわらず、dodata
がシンボルテーブルの構築 (symtab
) よりも後に実行されるためでした。結果として、Goのツールチェーンがこれらのシンボルを正しく認識できず、デバッグやプロファイリングツールに影響を与える可能性がありました。
このコミットは、このリグレッションを修正し、gcdata
と gcbss
シンボルがGoのシンボルテーブルに適切に登録されるようにすることで、リンカの正確性とGoプログラムの健全性を確保することを目的としています。また、不要になったELF文字列テーブルのエントリを削除することで、バイナリのサイズをわずかに削減し、クリーンアップも行っています。
前提知識の解説
- Goリンカ (
cmd/ld
): Go言語のコンパイラツールチェーンの一部であり、Goのソースコードから生成されたオブジェクトファイルを結合し、実行可能なバイナリを生成するプログラムです。シンボル解決、セクション配置、リロケーションなどの重要なタスクを実行します。 - シンボルテーブル: プログラム内の関数、変数、セクションなどの名前(シンボル)と、それらがメモリ上のどこに配置されるかという情報(アドレスやサイズなど)をマッピングするデータ構造です。リンカはシンボルテーブルを使用して、異なるオブジェクトファイル間の参照を解決します。Goのリンカは、Go固有のシンボルテーブルと、ELF(Executable and Linkable Format)などのOS固有のシンボルテーブルの両方を扱います。
- ELF (Executable and Linkable Format): Unix系OSで広く使われている実行可能ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。ELFファイルは、プログラムコード、データ、シンボルテーブル、セクションヘッダなどの様々なセクションで構成されます。
gcdata
とgcbss
: Goのランタイムにおけるガベージコレクション(GC)に関連する特別なセクションです。gcdata
: グローバル変数や静的変数の中で、ポインタを含むデータに関するメタデータ(どの部分がポインタであるかなど)を格納するセクションです。GCはこれを利用して、ヒープ上のオブジェクトだけでなく、スタックやグローバル変数からの参照も追跡します。gcbss
: 初期化されていないグローバル変数や静的変数の中で、ポインタを含むデータに関するメタデータを格納するセクションです。bss
セクションは通常、ゼロ初期化されるデータを含みますが、gcbss
はGCが追跡する必要があるポインタを含むbss
領域の情報を持ちます。
dodata
関数: Goリンカの内部関数で、プログラムのデータセクションを処理し、初期化されたデータやBSSセクションを配置する役割を担います。この関数内でgcdata
とgcbss
のシンボルが参照されます。symtab
関数: Goリンカの内部関数で、Goのシンボルテーブルを構築する役割を担います。STYPE
: Goリンカ内部で使われるシンボルタイプの一つで、特定のセクションや特殊なシンボルを示すために使われます。SRODATA
: Goリンカ内部で使われるシンボルタイプの一つで、読み取り専用データセクション(.rodata
)に配置されるシンボルを示します。
技術的詳細
このコミットの核心は、gcdata
と gcbss
シンボルの定義とリンカ内での処理順序の調整にあります。
元々、gcdata
と gcbss
は dodata
関数内で lookup
され、STYPE
としてマークされ、reachable
フラグが設定されていました。しかし、dodata
は symtab
(Goシンボルテーブルの構築) の後に実行されるため、これらのシンボルはGoのシンボルテーブルには登録されませんでした。一方で、ELFシンボルテーブルには、リンカが最終的に生成するバイナリのセクション情報としてこれらのシンボルが含まれていました。
変更セット 01c40d533367
は、STYPE
シンボルの不要な定義を削除する目的で、dodata
内での gcdata1->type = STYPE;
や gcbss1->type = STYPE;
といった行を削除しました。これにより、Goのシンボルテーブルにこれらのシンボルが登録されないというリグレッションが発生しました。
このコミットでは、以下の修正が行われています。
symtab.c
でのgcdata
とgcbss
の定義の移動と修正:- 以前は
symtab
関数内でegcdata
とegcbss
がSTYPE
としてxdefine
されていましたが、これは削除されました。 - 代わりに、
gcdata
とgcbss
シンボル自体がsymtab
関数内でlookup
され、そのtype
がSRODATA
に設定され、reachable
フラグが1
に設定されるようになりました。これにより、これらのシンボルがGoのシンボルテーブルに適切に登録されるようになります。 egcdata
とegcbss
もSRODATA
としてxdefine
されるようになりました。これは、これらのシンボルが読み取り専用データセクションの終端を示すためです。
- 以前は
data.c
でのgcdata
とgcbss
のSTYPE
設定の削除:dodata
関数内でgcdata1->type = STYPE;
とgcbss1->type = STYPE;
の行が削除されました。これは、symtab.c
で既にこれらのシンボルのタイプがSRODATA
として適切に設定されるようになったため、dodata
での重複した、かつ誤解を招く可能性のあるSTYPE
設定が不要になったためです。address
関数内でegcdata
とegcbss
のxdefine
のタイプがSTYPE
からSRODATA
に変更されました。これは、これらのシンボルが読み取り専用データセクションに関連付けられるべきであることを明確にします。
elf.c
からの不要な文字列の削除:doelf
関数内で、ELFの文字列テーブル (shstrtab
) に追加されていた.gcdata
と.gcbss
、およびそれらに関連するリロケーションセクション(.rela.gcdata
,.rela.gcbss
,.rel.gcdata
,.rel.gcbss
)の文字列が削除されました。これは、これらのセクションがGoのリンカによって直接管理されるようになり、ELFの文字列テーブルに明示的に含める必要がなくなったためです。これにより、生成されるバイナリのサイズがわずかに削減されます。
この修正により、gcdata
と gcbss
シンボルはGoのシンボルテーブルに正しく登録され、Goのツールチェーンがこれらの情報を利用できるようになります。また、ELFバイナリの構造もより正確に反映されるようになります。
コアとなるコードの変更箇所
src/cmd/ld/data.c
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -1047,13 +1047,8 @@ dodata(void)
Bprint(&bso, "%5.2f dodata\n", cputime());
Bflush(&bso);
- // define garbage collection symbols
gcdata1 = lookup("gcdata", 0);
- gcdata1->type = STYPE;
- gcdata1->reachable = 1;
gcbss1 = lookup("gcbss", 0);
- gcbss1->type = STYPE;
- gcbss1->reachable = 1;
// size of .data and .bss section. the zero value is later replaced by the actual size of the section.
adduintxx(gcdata1, 0, PtrSize);
@@ -1477,11 +1472,11 @@ address(void)
}
sym = lookup("gcdata", 0);
- xdefine("egcdata", STYPE, symaddr(sym) + sym->size);
+ xdefine("egcdata", SRODATA, symaddr(sym) + sym->size);
lookup("egcdata", 0)->sect = sym->sect;
sym = lookup("gcbss", 0);
- xdefine("egcbss", STYPE, symaddr(sym) + sym->size);
+ xdefine("egcbss", SRODATA, symaddr(sym) + sym->size);
lookup("egcbss", 0)->sect = sym->sect;
xdefine("symtab", SRODATA, symtab->vaddr);
src/cmd/ld/elf.c
--- a/src/cmd/ld/elf.c
+++ b/src/cmd/ld/elf.c
@@ -905,8 +905,6 @@ doelf(void)
addstring(shstrtab, ".typelink");
if(flag_shared)
addstring(shstrtab, ".data.rel.ro");
- addstring(shstrtab, ".gcdata");
- addstring(shstrtab, ".gcbss");
addstring(shstrtab, ".gosymtab");
addstring(shstrtab, ".gopclntab");
@@ -918,8 +916,6 @@ doelf(void)
addstring(shstrtab, ".rela.text");
addstring(shstrtab, ".rela.rodata");
addstring(shstrtab, ".rela.typelink");
- addstring(shstrtab, ".rela.gcdata");
- addstring(shstrtab, ".rela.gcbss");
addstring(shstrtab, ".rela.gosymtab");
addstring(shstrtab, ".rela.gopclntab");
addstring(shstrtab, ".rela.noptrdata");
@@ -928,8 +924,6 @@ doelf(void)
addstring(shstrtab, ".rel.text");
addstring(shstrtab, ".rel.rodata");
addstring(shstrtab, ".rel.typelink");
- addstring(shstrtab, ".rel.gcdata");
- addstring(shstrtab, ".rel.gcbss");
addstring(shstrtab, ".rel.gosymtab");
addstring(shstrtab, ".rel.gopclntab");
addstring(shstrtab, ".rel.noptrdata");
src/cmd/ld/symtab.c
--- a/src/cmd/ld/symtab.c
+++ b/src/cmd/ld/symtab.c
@@ -481,8 +481,6 @@ symtab(void)
xdefine("datarelro", SDATARELRO, 0);
xdefine("edatarelro", SDATARELRO, 0);
}
- xdefine("egcdata", STYPE, 0);
- xdefine("egcbss", STYPE, 0);
xdefine("noptrdata", SNOPTRDATA, 0);
xdefine("enoptrdata", SNOPTRDATA, 0);
xdefine("data", SDATA, 0);
@@ -495,6 +493,19 @@ symtab(void)
xdefine("epclntab", SRODATA, 0);
xdefine("esymtab", SRODATA, 0);
+ // garbage collection symbols
+ s = lookup("gcdata", 0);
+ s->type = SRODATA;
+ s->size = 0;
+ s->reachable = 1;
+ xdefine("egcdata", SRODATA, 0);
+
+ s = lookup("gcbss", 0);
+ s->type = SRODATA;
+ s->size = 0;
+ s->reachable = 1;
+ xdefine("egcbss", SRODATA, 0);
+
// pseudo-symbols to mark locations of type, string, and go string data.
s = lookup("type.*", 0);
s->type = STYPE;
コアとなるコードの解説
src/cmd/ld/data.c
の変更
dodata
関数内のgcdata1->type = STYPE;
とgcbss1->type = STYPE;
の行が削除されました。これは、これらのシンボルのタイプがsymtab.c
でSRODATA
として適切に設定されるようになったため、dodata
でのSTYPE
設定が不要になったためです。address
関数内のxdefine("egcdata", STYPE, ...)
とxdefine("egcbss", STYPE, ...)
がxdefine("egcdata", SRODATA, ...)
とxdefine("egcbss", SRODATA, ...)
に変更されました。これは、egcdata
とegcbss
が読み取り専用データセクションの終端を示すシンボルであり、SRODATA
タイプがより適切であることを示しています。
src/cmd/ld/elf.c
の変更
doelf
関数内で、ELFの文字列テーブル (shstrtab
) に追加されていた.gcdata
と.gcbss
、およびそれらに関連するリロケーションセクションの文字列が削除されました。これは、これらのセクションがGoのリンカによって内部的に管理されるようになり、ELFの文字列テーブルに明示的に含める必要がなくなったためです。これにより、ELFバイナリの冗長な情報が削減されます。
src/cmd/ld/symtab.c
の変更
symtab
関数内のxdefine("egcdata", STYPE, 0);
とxdefine("egcbss", STYPE, 0);
の行が削除されました。これは、これらのシンボルがdodata
で定義されるため、symtab
での初期定義が不要になったためです。- 新たに「garbage collection symbols」というコメントの下に、
gcdata
とgcbss
シンボルがsymtab
関数内でlookup
され、そのtype
がSRODATA
に設定され、size
が0
に、reachable
フラグが1
に設定されるようになりました。これにより、これらのシンボルがGoのシンボルテーブルに適切に登録され、Goのツールチェーンがこれらの情報を利用できるようになります。 egcdata
とegcbss
もSRODATA
としてxdefine
されるようになりました。これは、これらのシンボルが読み取り専用データセクションの終端を示すためです。
これらの変更により、gcdata
と gcbss
シンボルはGoのシンボルテーブルに正しく登録され、リンカの内部処理とELFバイナリの整合性が向上しました。
関連リンク
- Go言語のリンカに関するドキュメント(公式): Goのリンカの内部動作に関する詳細な公式ドキュメントは一般に公開されていませんが、Goのソースコード自体が最も正確な情報源です。
- Goのガベージコレクションに関するドキュメント:
参考にした情報源リンク
- golang/go GitHub repository
- Go CL 8650043 (このコミットに対応するGoのコードレビュー)
- Go commit 01c40d533367 (リグレッションを導入したコミット)
- ELF (Executable and Linkable Format) の仕様に関する一般的な情報源 (例: Wikipedia, man pages)
- Go言語のガベージコレクションに関するブログ記事やドキュメント (上記「関連リンク」に記載)
- Goリンカのソースコード (
src/cmd/ld/
)