[インデックス 12057] ファイルの概要
このコミットは、Go言語のリンカである6l
(amd64アーキテクチャ用) と 8l
(386アーキテクチャ用) において、ELF (Executable and Linkable Format) システムでのビルドを修正するものです。具体的には、ELFバイナリに.noptrdata
セクションを追加することで、ポインタを含まないデータ領域の扱いを改善し、ガベージコレクションの効率化と正確性を確保します。
コミット
- コミットハッシュ:
cf52e39ee0e0b1a28a69a67866fea79728335f17
- 作者: Shenghou Ma minux.ma@gmail.com
- コミット日時: 2012年2月19日 (日) 13:16:47 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/cf52e39ee0e0b1a28a69a67866fea79728335f17
元コミット内容
6l, 8l: fix build for ELF systems
R=rsc
CC=golang-dev
https://golang.org/cl/5677095
変更の背景
Go言語のランタイムは、ガベージコレクション (GC) を効率的かつ正確に行うために、メモリ上のどの領域にポインタが含まれているかを正確に知る必要があります。従来のELFバイナリでは、初期化されたデータは通常.data
セクションに、初期化されていないデータは.bss
セクションに配置されます。しかし、これらのセクションにはポインタを含むデータと含まないデータが混在する可能性があります。
Goのガベージコレクタは、ヒープ上のオブジェクトだけでなく、グローバル変数やスタック上の値もスキャンして到達可能なオブジェクトを特定します。この際、データセクション内にポインタではない値(例えば整数や文字列のバイト列など)がポインタとして誤って解釈されると、ガベージコレクタが誤ったメモリ領域を参照したり、本来解放すべきメモリを解放しなかったりする問題が発生する可能性があります。
このコミットは、ELFシステムにおいて、ポインタを含まないグローバル変数を格納するための専用のセクションである.noptrdata
を導入することで、この問題を解決しようとしています。これにより、ガベージコレクタは.noptrdata
セクションをスキャンする必要がなくなり、GCの効率が向上し、正確性が保証されます。
前提知識の解説
ELF (Executable and Linkable Format)
ELFは、Unix系オペレーティングシステム(Linux、BSDなど)で広く使用されている実行可能ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。ELFファイルは、プログラムのコード、データ、シンボル情報などを構造化して格納します。
主要なセクションには以下のようなものがあります。
.text
: 実行可能な機械語コードが格納されます。.data
: 初期化されたグローバル変数や静的変数が格納されます。.bss
: 初期化されていないグローバル変数や静的変数が格納されます。プログラムの起動時にゼロで初期化されます。.rodata
: 読み取り専用のデータ(定数など)が格納されます。
Go言語のリンカ (6l
, 8l
)
Go言語のビルドプロセスでは、コンパイラがGoのソースコードをオブジェクトファイルに変換し、その後リンカがこれらのオブジェクトファイルと必要なライブラリを結合して最終的な実行可能バイナリを生成します。
6l
:go tool 6l
として知られ、amd64 (64-bit) アーキテクチャ向けのGoプログラムをリンクするために使用されます。8l
:go tool 8l
として知られ、386 (32-bit) アーキテクチャ向けのGoプログラムをリンクするために使用されます。
これらのリンカは、Goランタイムの特性(特にガベージコレクション)を考慮して、ELFファイル内のセクションを適切に配置する役割を担っています。
Go言語における.noptrdata
セクション
Go言語のガベージコレクタは、メモリ上のポインタを追跡することで、到達可能なオブジェクトを特定し、到達不能なオブジェクトを解放します。このプロセスを効率化し、誤ったポインタ解釈を防ぐために、Goは特定のデータセクションを導入しています。
.noptrdata
セクションは、Goのランタイムが「ポインタを含まない」と認識しているグローバル変数を格納するために使用されます。例えば、純粋な数値データ、文字列のバイト配列、構造体でポインタフィールドを持たないものなどがここに含まれます。ガベージコレクタは、このセクション内のデータをポインタとしてスキャンする必要がないため、GCのオーバーヘッドを削減し、パフォーマンスを向上させることができます。
技術的詳細
このコミットの技術的詳細としては、GoリンカがELFバイナリを生成する際に、.noptrdata
という新しいセクション名を認識し、それをELFファイルのセクションヘッダテーブルに適切に追加するように変更された点にあります。
具体的には、src/cmd/6l/asm.c
とsrc/cmd/8l/asm.c
のenum
定義にElfStrNoPtrData
という新しいエントリが追加され、これがELFセクション名文字列のインデックスとして使用されます。そして、doelf
関数内で、このElfStrNoPtrData
に対応する文字列として実際に.noptrdata
がshstrtab
(セクション名文字列テーブル)に追加されます。
これにより、Goコンパイラやリンカは、ポインタを含まないグローバル変数をこの.noptrdata
セクションに配置するようになり、ELFバイナリの構造がGoのガベージコレクションの要件に適合するようになります。これは、特にクロスコンパイルや異なるOS/アーキテクチャ間でのGoプログラムの互換性と安定性を高める上で重要な変更です。
この変更は、Goのガベージコレクタがより正確に動作するための基盤を強化し、Goプログラムの実行時パフォーマンスと信頼性を向上させることに貢献します。
コアとなるコードの変更箇所
このコミットによる変更は、src/cmd/6l/asm.c
とsrc/cmd/8l/asm.c
の2つのファイルにわたります。両ファイルで同様の変更が行われています。
diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c
index 121aa8613e..b64a6dabbc 100644
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -95,6 +95,7 @@ enum {
ElfStrGnuVersion,
ElfStrGnuVersionR,
ElfStrNoteNetbsdIdent,
+\tElfStrNoPtrData,
NElfStr
};
@@ -569,6 +570,7 @@ doelf(void)
elfstr[ElfStrEmpty] = addstring(shstrtab, "");
elfstr[ElfStrText] = addstring(shstrtab, ".text");
+\telfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata");
elfstr[ElfStrData] = addstring(shstrtab, ".data");
elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
if(HEADTYPE == Hnetbsd)
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c
index 54bda1ac84..27881d8088 100644
--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -91,6 +91,7 @@ enum {
ElfStrGnuVersion,
ElfStrGnuVersionR,
ElfStrNoteNetbsdIdent,
+\tElfStrNoPtrData,
NElfStr
};
@@ -526,6 +527,7 @@ doelf(void)
elfstr[ElfStrEmpty] = addstring(shstrtab, "");
elfstr[ElfStrText] = addstring(shstrtab, ".text");
+\telfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata");
elfstr[ElfStrData] = addstring(shstrtab, ".data");
elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
if(HEADTYPE == Hnetbsd)
コアとなるコードの解説
変更は主に2つの部分から構成されます。
-
enum
へのElfStrNoPtrData
の追加:src/cmd/6l/asm.c
とsrc/cmd/8l/asm.c
のそれぞれで、ELFセクション名を表す定数を定義しているenum
ブロックに、ElfStrNoPtrData
が追加されています。これは、リンカが内部的に.noptrdata
セクションを識別するための新しい識別子です。enum { // ... 既存の定義 ... ElfStrNoteNetbsdIdent, ElfStrNoPtrData, // <-- 追加された行 NElfStr };
-
doelf
関数内での.noptrdata
セクション名の登録:doelf
関数は、ELFファイルの生成に関連する処理を行う関数です。この関数内で、elfstr
配列(ELFセクション名文字列へのポインタを格納する配列)に、新しく定義されたElfStrNoPtrData
に対応する文字列として実際に.noptrdata
が追加されています。addstring(shstrtab, ".noptrdata")
は、セクション名文字列テーブル (shstrtab
) に文字列.noptrdata
を追加し、そのインデックスを返します。doelf(void) { // ... 既存の処理 ... elfstr[ElfStrEmpty] = addstring(shstrtab, ""); elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata"); // <-- 追加された行 elfstr[ElfStrData] = addstring(shstrtab, ".data"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); // ... 既存の処理 ... }
これらの変更により、GoのリンカはELFバイナリを生成する際に、.noptrdata
というセクションを認識し、Goコンパイラが生成したポインタを含まないデータをこのセクションに配置できるようになります。これは、Goのガベージコレクションの正確性と効率性をELFシステム上で保証するために不可欠な変更です。
関連リンク
- Go CL 5677095: https://golang.org/cl/5677095
参考にした情報源リンク
- ELF (Executable and Linkable Format) - Wikipedia: https://ja.wikipedia.org/wiki/Executable_and_Linkable_Format
- Go言語のガベージコレクションに関するドキュメントやブログ記事 (一般的な情報源として参照)
- Go言語のリンカのソースコード (一般的な情報源として参照)
- Goの
.noptrdata
セクションに関する議論やドキュメント (具体的な情報源として参照)- 例: Goのソースコード内のコメントや、Goの設計に関するドキュメントで
.noptrdata
について言及されている箇所。 - Goのガベージコレクションの仕組みに関する記事。
- 例: Goのソースコード内のコメントや、Goの設計に関するドキュメントで
[インデックス 12057] ファイルの概要
このコミットは、Go言語のリンカである6l
(amd64アーキテクチャ用) と 8l
(386アーキテクチャ用) において、ELF (Executable and Linkable Format) システムでのビルドを修正するものです。具体的には、ELFバイナリに.noptrdata
セクションを追加することで、ポインタを含まないデータ領域の扱いを改善し、ガベージコレクションの効率化と正確性を確保します。
コミット
- コミットハッシュ:
cf52e39ee0e0b1a28a69a67866fea79728335f17
- 作者: Shenghou Ma minux.ma@gmail.com
- コミット日時: 2012年2月19日 (日) 13:16:47 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/cf52e39ee0e0b1a28a69a67866fea79728335f17
元コミット内容
6l, 8l: fix build for ELF systems
R=rsc
CC=golang-dev
https://golang.org/cl/5677095
変更の背景
Go言語のランタイムは、ガベージコレクション (GC) を効率的かつ正確に行うために、メモリ上のどの領域にポインタが含まれているかを正確に知る必要があります。従来のELFバイナリでは、初期化されたデータは通常.data
セクションに、初期化されていないデータは.bss
セクションに配置されます。しかし、これらのセクションにはポインタを含むデータと含まないデータが混在する可能性があります。
Goのガベージコレクタは、ヒープ上のオブジェクトだけでなく、グローバル変数やスタック上の値もスキャンして到達可能なオブジェクトを特定します。この際、データセクション内にポインタではない値(例えば整数や文字列のバイト列など)がポインタとして誤って解釈されると、ガベージコレクタが誤ったメモリ領域を参照したり、本来解放すべきメモリを解放しなかったりする問題が発生する可能性があります。
このコミットは、ELFシステムにおいて、ポインタを含まないグローバル変数を格納するための専用のセクションである.noptrdata
を導入することで、この問題を解決しようとしています。これにより、ガベージコレクタは.noptrdata
セクションをスキャンする必要がなくなり、GCの効率が向上し、正確性が保証されます。
前提知識の解説
ELF (Executable and Linkable Format)
ELFは、Unix系オペレーティングシステム(Linux、BSDなど)で広く使用されている実行可能ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。ELFファイルは、プログラムのコード、データ、シンボル情報などを構造化して格納します。
主要なセクションには以下のようなものがあります。
.text
: 実行可能な機械語コードが格納されます。.data
: 初期化されたグローバル変数や静的変数が格納されます。このセクションにはポインタを含むデータが格納されることがあります。.bss
: 初期化されていないグローバル変数や静的変数が格納されます。プログラムの起動時にゼロで初期化されます。.rodata
: 読み取り専用のデータ(定数など)が格納されます。
Go言語のリンカ (6l
, 8l
)
Go言語のビルドプロセスでは、コンパイラがGoのソースコードをオブジェクトファイルに変換し、その後リンカがこれらのオブジェクトファイルと必要なライブラリを結合して最終的な実行可能バイナリを生成します。
6l
:go tool 6l
として知られ、amd64 (64-bit) アーキテクチャ向けのGoプログラムをリンクするために使用されます。8l
:go tool 8l
として知られ、386 (32-bit) アーキテクチャ向けのGoプログラムをリンクするために使用されます。
これらのリンカは、Goランタイムの特性(特にガベージコレクション)を考慮して、ELFファイル内のセクションを適切に配置する役割を担っています。
Go言語における.noptrdata
セクションとガベージコレクション
Go言語のガベージコレクタは、メモリ上のポインタを追跡することで、到達可能なオブジェクトを特定し、到達不能なオブジェクトを解放します。このプロセスを効率化し、誤ったポインタ解釈を防ぐために、Goは特定のデータセクションを導入しています。
.noptrdata
セクションは、Goのランタイムが「ポインタを含まない」と認識しているグローバル変数を格納するために使用されます。例えば、純粋な数値データ、文字列のバイト配列、構造体でポインタフィールドを持たないものなどがここに含まれます。
Goのガベージコレクタは、ヒープ上の動的に割り当てられたメモリを管理します。ガベージコレクタは、ルートオブジェクト(グローバル変数やゴルーチンのスタックなど)からポインタをたどって「生きている」オブジェクトを識別し、マークします。これらのポインタを介して到達できないメモリは「ガベージ」と見なされ、スイープフェーズで回収されます。
.noptrdata
セクションは、明示的にポインタを含まないデータが格納されるため、Goのガベージコレクタはヒープメモリを管理するのと同じ方法でこのセクション内のメモリを直接管理したり「ガベージコレクション」したりすることはありません。.noptrdata
内のデータは、コンパイルされたプログラムの静的イメージの一部であり、ヒープオブジェクトの動的な割り当てと解放サイクルには関係しません。したがって、未使用メモリの回収という文脈での「ガベージコレクション」は、.noptrdata
セクションには適用されません。これにより、ガベージコレクタは.noptrdata
セクションをスキャンする必要がなくなり、GCのオーバーヘッドを削減し、パフォーマンスを向上させることができます。
技術的詳細
このコミットの技術的詳細としては、GoリンカがELFバイナリを生成する際に、.noptrdata
という新しいセクション名を認識し、それをELFファイルのセクションヘッダテーブルに適切に追加するように変更された点にあります。
具体的には、src/cmd/6l/asm.c
とsrc/cmd/8l/asm.c
のenum
定義にElfStrNoPtrData
という新しいエントリが追加され、これがELFセクション名文字列のインデックスとして使用されます。そして、doelf
関数内で、このElfStrNoPtrData
に対応する文字列として実際に.noptrdata
がshstrtab
(セクション名文字列テーブル)に追加されます。
これにより、Goコンパイラやリンカは、ポインタを含まないグローバル変数をこの.noptrdata
セクションに配置するようになり、ELFバイナリの構造がGoのガベージコレクションの要件に適合するようになります。これは、特にクロスコンパイルや異なるOS/アーキテクチャ間でのGoプログラムの互換性と安定性を高める上で重要な変更です。
この変更は、Goのガベージコレクタがより正確に動作するための基盤を強化し、Goプログラムの実行時パフォーマンスと信頼性を向上させることに貢献します。
コアとなるコードの変更箇所
このコミットによる変更は、src/cmd/6l/asm.c
とsrc/cmd/8l/asm.c
の2つのファイルにわたります。両ファイルで同様の変更が行われています。
diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c
index 121aa8613e..b64a6dabbc 100644
--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -95,6 +95,7 @@ enum {
ElfStrGnuVersion,
ElfStrGnuVersionR,
ElfStrNoteNetbsdIdent,
+\tElfStrNoPtrData,
NElfStr
};
@@ -569,6 +570,7 @@ doelf(void)
elfstr[ElfStrEmpty] = addstring(shstrtab, "");
elfstr[ElfStrText] = addstring(shstrtab, ".text");
+\telfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata");
elfstr[ElfStrData] = addstring(shstrtab, ".data");
elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
if(HEADTYPE == Hnetbsd)
diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c
index 54bda1ac84..27881d8088 100644
--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -91,6 +91,7 @@ enum {
ElfStrGnuVersion,
ElfStrGnuVersionR,
ElfStrNoteNetbsdIdent,
+\tElfStrNoPtrData,
NElfStr
};
@@ -526,6 +527,7 @@ doelf(void)
elfstr[ElfStrEmpty] = addstring(shstrtab, "");
elfstr[ElfStrText] = addstring(shstrtab, ".text");
+\telfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata");
elfstr[ElfStrData] = addstring(shstrtab, ".data");
elfstr[ElfStrBss] = addstring(shstrtab, ".bss");
if(HEADTYPE == Hnetbsd)
コアとなるコードの解説
変更は主に2つの部分から構成されます。
-
enum
へのElfStrNoPtrData
の追加:src/cmd/6l/asm.c
とsrc/cmd/8l/asm.c
のそれぞれで、ELFセクション名を表す定数を定義しているenum
ブロックに、ElfStrNoPtrData
が追加されています。これは、リンカが内部的に.noptrdata
セクションを識別するための新しい識別子です。enum { // ... 既存の定義 ... ElfStrNoteNetbsdIdent, ElfStrNoPtrData, // <-- 追加された行 NElfStr };
-
doelf
関数内での.noptrdata
セクション名の登録:doelf
関数は、ELFファイルの生成に関連する処理を行う関数です。この関数内で、elfstr
配列(ELFセクション名文字列へのポインタを格納する配列)に、新しく定義されたElfStrNoPtrData
に対応する文字列として実際に.noptrdata
が追加されています。addstring(shstrtab, ".noptrdata")
は、セクション名文字列テーブル (shstrtab
) に文字列.noptrdata
を追加し、そのインデックスを返します。doelf(void) { // ... 既存の処理 ... elfstr[ElfStrEmpty] = addstring(shstrtab, ""); elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata"); // <-- 追加された行 elfstr[ElfStrData] = addstring(shstrtab, ".data"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); // ... 既存の処理 ... }
これらの変更により、GoのリンカはELFバイナリを生成する際に、.noptrdata
というセクションを認識し、Goコンパイラが生成したポインタを含まないデータをこのセクションに配置できるようになります。これは、Goのガベージコレクションの正確性と効率性をELFシステム上で保証するために不可欠な変更です。
関連リンク
- Go CL 5677095: https://golang.org/cl/5677095
参考にした情報源リンク
- ELF (Executable and Linkable Format) - Wikipedia: https://ja.wikipedia.org/wiki/Executable_and_Linkable_Format
- Goのガベージコレクションに関する情報源:
- Goの
.noptrdata
セクションに関する情報源:- https://github.com/golang/go/blob/master/src/cmd/link/internal/ld/data.go (Goリンカの関連ソースコード)
- https://howardjohn.info/posts/go-binary-sections/ (Goバイナリセクションに関するブログ記事)
- https://github.io/golang/go/blob/master/src/runtime/malloc.go (Goランタイムのメモリ割り当て関連ソースコード)