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

[インデックス 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プログラムを実行可能なバイナリに変換する重要な役割を担っています。このプロセスにおいて、プログラム内の様々なデータやコードに関する情報がシンボルとして管理されます。gcdatagcbss は、Goのガベージコレクション(GC)が正しく機能するために必要なメタデータを含むセクションに関連するシンボルです。

以前の変更セット 01c40d533367 (これは "cmd/ld: remove unused STYPE symbols" というコミットで、gcdatagcbss のシンボルタイプを STYPE から削除し、dodata での定義を簡略化したものでした) によって、gcdatagcbss シンボルがGoのシンボルテーブルに登録されなくなりました。これは、これらのシンボルが dodata 関数内で定義されているにもかかわらず、dodata がシンボルテーブルの構築 (symtab) よりも後に実行されるためでした。結果として、Goのツールチェーンがこれらのシンボルを正しく認識できず、デバッグやプロファイリングツールに影響を与える可能性がありました。

このコミットは、このリグレッションを修正し、gcdatagcbss シンボルがGoのシンボルテーブルに適切に登録されるようにすることで、リンカの正確性とGoプログラムの健全性を確保することを目的としています。また、不要になったELF文字列テーブルのエントリを削除することで、バイナリのサイズをわずかに削減し、クリーンアップも行っています。

前提知識の解説

  • Goリンカ (cmd/ld): Go言語のコンパイラツールチェーンの一部であり、Goのソースコードから生成されたオブジェクトファイルを結合し、実行可能なバイナリを生成するプログラムです。シンボル解決、セクション配置、リロケーションなどの重要なタスクを実行します。
  • シンボルテーブル: プログラム内の関数、変数、セクションなどの名前(シンボル)と、それらがメモリ上のどこに配置されるかという情報(アドレスやサイズなど)をマッピングするデータ構造です。リンカはシンボルテーブルを使用して、異なるオブジェクトファイル間の参照を解決します。Goのリンカは、Go固有のシンボルテーブルと、ELF(Executable and Linkable Format)などのOS固有のシンボルテーブルの両方を扱います。
  • ELF (Executable and Linkable Format): Unix系OSで広く使われている実行可能ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。ELFファイルは、プログラムコード、データ、シンボルテーブル、セクションヘッダなどの様々なセクションで構成されます。
  • gcdatagcbss: Goのランタイムにおけるガベージコレクション(GC)に関連する特別なセクションです。
    • gcdata: グローバル変数や静的変数の中で、ポインタを含むデータに関するメタデータ(どの部分がポインタであるかなど)を格納するセクションです。GCはこれを利用して、ヒープ上のオブジェクトだけでなく、スタックやグローバル変数からの参照も追跡します。
    • gcbss: 初期化されていないグローバル変数や静的変数の中で、ポインタを含むデータに関するメタデータを格納するセクションです。bss セクションは通常、ゼロ初期化されるデータを含みますが、gcbss はGCが追跡する必要があるポインタを含む bss 領域の情報を持ちます。
  • dodata 関数: Goリンカの内部関数で、プログラムのデータセクションを処理し、初期化されたデータやBSSセクションを配置する役割を担います。この関数内で gcdatagcbss のシンボルが参照されます。
  • symtab 関数: Goリンカの内部関数で、Goのシンボルテーブルを構築する役割を担います。
  • STYPE: Goリンカ内部で使われるシンボルタイプの一つで、特定のセクションや特殊なシンボルを示すために使われます。
  • SRODATA: Goリンカ内部で使われるシンボルタイプの一つで、読み取り専用データセクション(.rodata)に配置されるシンボルを示します。

技術的詳細

このコミットの核心は、gcdatagcbss シンボルの定義とリンカ内での処理順序の調整にあります。

元々、gcdatagcbssdodata 関数内で lookup され、STYPE としてマークされ、reachable フラグが設定されていました。しかし、dodatasymtab (Goシンボルテーブルの構築) の後に実行されるため、これらのシンボルはGoのシンボルテーブルには登録されませんでした。一方で、ELFシンボルテーブルには、リンカが最終的に生成するバイナリのセクション情報としてこれらのシンボルが含まれていました。

変更セット 01c40d533367 は、STYPE シンボルの不要な定義を削除する目的で、dodata 内での gcdata1->type = STYPE;gcbss1->type = STYPE; といった行を削除しました。これにより、Goのシンボルテーブルにこれらのシンボルが登録されないというリグレッションが発生しました。

このコミットでは、以下の修正が行われています。

  1. symtab.c での gcdatagcbss の定義の移動と修正:
    • 以前は symtab 関数内で egcdataegcbssSTYPE として xdefine されていましたが、これは削除されました。
    • 代わりに、gcdatagcbss シンボル自体が symtab 関数内で lookup され、その typeSRODATA に設定され、reachable フラグが 1 に設定されるようになりました。これにより、これらのシンボルがGoのシンボルテーブルに適切に登録されるようになります。
    • egcdataegcbssSRODATA として xdefine されるようになりました。これは、これらのシンボルが読み取り専用データセクションの終端を示すためです。
  2. data.c での gcdatagcbssSTYPE 設定の削除:
    • dodata 関数内で gcdata1->type = STYPE;gcbss1->type = STYPE; の行が削除されました。これは、symtab.c で既にこれらのシンボルのタイプが SRODATA として適切に設定されるようになったため、dodata での重複した、かつ誤解を招く可能性のある STYPE 設定が不要になったためです。
    • address 関数内で egcdataegcbssxdefine のタイプが STYPE から SRODATA に変更されました。これは、これらのシンボルが読み取り専用データセクションに関連付けられるべきであることを明確にします。
  3. elf.c からの不要な文字列の削除:
    • doelf 関数内で、ELFの文字列テーブル (shstrtab) に追加されていた .gcdata.gcbss、およびそれらに関連するリロケーションセクション(.rela.gcdata, .rela.gcbss, .rel.gcdata, .rel.gcbss)の文字列が削除されました。これは、これらのセクションがGoのリンカによって直接管理されるようになり、ELFの文字列テーブルに明示的に含める必要がなくなったためです。これにより、生成されるバイナリのサイズがわずかに削減されます。

この修正により、gcdatagcbss シンボルは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.cSRODATA として適切に設定されるようになったため、dodata での STYPE 設定が不要になったためです。
  • address 関数内の xdefine("egcdata", STYPE, ...)xdefine("egcbss", STYPE, ...)xdefine("egcdata", SRODATA, ...)xdefine("egcbss", SRODATA, ...) に変更されました。これは、egcdataegcbss が読み取り専用データセクションの終端を示すシンボルであり、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」というコメントの下に、gcdatagcbss シンボルが symtab 関数内で lookup され、その typeSRODATA に設定され、size0 に、reachable フラグが 1 に設定されるようになりました。これにより、これらのシンボルがGoのシンボルテーブルに適切に登録され、Goのツールチェーンがこれらの情報を利用できるようになります。
  • egcdataegcbssSRODATA として xdefine されるようになりました。これは、これらのシンボルが読み取り専用データセクションの終端を示すためです。

これらの変更により、gcdatagcbss シンボルはGoのシンボルテーブルに正しく登録され、リンカの内部処理とELFバイナリの整合性が向上しました。

関連リンク

参考にした情報源リンク

  • golang/go GitHub repository
  • Go CL 8650043 (このコミットに対応するGoのコードレビュー)
  • Go commit 01c40d533367 (リグレッションを導入したコミット)
  • ELF (Executable and Linkable Format) の仕様に関する一般的な情報源 (例: Wikipedia, man pages)
  • Go言語のガベージコレクションに関するブログ記事やドキュメント (上記「関連リンク」に記載)
  • Goリンカのソースコード (src/cmd/ld/)