[インデックス 13132] ファイルの概要
このコミットは、Go言語のリンカ (cmd/ld
) に関連する変更です。具体的には、Windowsの実行可能ファイル形式であるPE (Portable Executable) ファイルを生成する際に、セクションシンボルの値の扱いを修正しています。変更が加えられたファイルは src/cmd/ld/ldpe.c
の1ファイルのみです。
コミット
- コミットハッシュ:
1c4b77a7c8adc6eed393a83991a3a67b88739050
- Author: Shenghou Ma minux.ma@gmail.com
- Date: Wed May 23 02:27:44 2012 +0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1c4b77a7c8adc6eed393a83991a3a67b88739050
元コミット内容
cmd/ld: take section symbols' value into account for PE
ld -r could generate multiple section symbols for the same section,
but with different values, we have to take that into account.
Fixes #3322.
Part of issue 3261.
For CL 5822049.
R=golang-dev, iant, rsc, iant
CC=golang-dev
https://golang.org/cl/5823059
変更の背景
このコミットは、Goリンカ (cmd/ld
) がWindowsのPE形式の実行可能ファイルを生成する際に発生していたバグを修正するものです。具体的には、ld -r
コマンド(再配置可能なオブジェクトファイルを生成するオプション)を使用した場合に、同じセクションに対して複数のセクションシンボルが異なる値で生成されることがありました。リンカはこれらの異なる値を適切に考慮していなかったため、結果として不正な再配置情報が生成され、プログラムの実行に問題が生じる可能性がありました。
この問題はGoのIssue #3322として報告されており、そのタイトルは「cmd/ld: bad handling of multiple section symbols (with different addresses)」です。これは、リンカが同じセクション名を持つが異なるメモリアドレスを持つシンボルを適切に処理できていなかったことを示唆しています。
前提知識の解説
Goリンカ (cmd/ld
)
Go言語のコンパイラツールチェーンの一部であり、Goのソースコードから実行可能ファイルやライブラリを生成する際に、コンパイルされたオブジェクトファイルを結合する役割を担います。リンカは、プログラム内のシンボル(関数名、変数名など)を解決し、それらがメモリ上のどこに配置されるかを決定し、最終的な実行可能ファイルを生成します。
ld -r
(再配置可能な出力)
リンカのオプションの一つで、最終的な実行可能ファイルを生成するのではなく、再配置可能なオブジェクトファイル(または共有ライブラリ)を生成します。これは、複数のオブジェクトファイルを結合して、後で別のリンカプロセスによってさらに結合される中間ファイルを生成する際に使用されます。このモードでは、シンボルやアドレスが最終的に決定されるわけではなく、後で再配置が必要な情報(再配置エントリ)が含まれます。
セクションシンボル
実行可能ファイルやオブジェクトファイルは、コード、データ、読み取り専用データなどの論理的なブロックに分割されており、これらは「セクション」と呼ばれます。セクションシンボルは、これらのセクションの開始アドレスやサイズを示す特殊なシンボルです。例えば、.text
セクション(コード)や .data
セクション(初期化されたデータ)などがあります。
Portable Executable (PE) 形式
Microsoft Windowsオペレーティングシステムで使用される実行可能ファイル、オブジェクトコード、DLL (Dynamic Link Library) などのファイル形式です。PEファイルは、ヘッダ、セクションテーブル、セクションデータなどで構成され、プログラムの実行に必要な情報(コード、データ、リソース、再配置情報など)を含んでいます。
再配置 (Relocation)
コンパイル時やリンク時には、プログラム内の絶対アドレスがまだ確定していない場合があります。例えば、共有ライブラリ内の関数呼び出しやグローバル変数の参照などは、プログラムがメモリにロードされるまで正確なアドレスが分かりません。再配置とは、リンカやローダがこれらの未解決のアドレスを、プログラムがメモリにロードされた後の実際のアドレスに修正するプロセスです。再配置エントリは、どの場所を、どのシンボルのアドレスで、どのように修正するかをリンカに指示する情報です。
技術的詳細
この問題は、ld -r
を使用してPE形式のオブジェクトファイルを生成する際に、同じセクション(例: .text
セクション)に対して、リンカが異なる値を持つ複数のセクションシンボルを誤って生成してしまうことに起因していました。通常、同じセクションには一意のセクションシンボルが対応し、その値はセクションの開始アドレスを示します。しかし、このバグにより、例えば .text
セクションの開始アドレスを示すはずのシンボルが、異なる値(オフセット)を持つ複数のエントリとして存在してしまう状況が発生していました。
リンカが再配置処理を行う際、特定のシンボルを参照してアドレスを計算します。このとき、もし参照しているセクションシンボルが複数存在し、かつそれぞれが異なる値を持っている場合、リンカはどの値を使用すべきか判断できず、誤ったアドレスを計算してしまう可能性がありました。特に、再配置エントリがセクションシンボルを参照している場合、そのシンボルの値(セクションのベースアドレスからのオフセット)が再配置の計算に影響を与えます。
このコミットの修正は、PE形式の再配置処理において、セクションシンボル(名前が .
で始まるシンボル)の value
フィールドを再配置の加算値 (rp->add
) に追加することで、この問題を解決しています。これにより、同じセクションに対する複数のセクションシンボルが存在しても、それぞれのシンボルが持つオフセット値が適切に再配置計算に反映され、正しいアドレスが導き出されるようになります。
コアとなるコードの変更箇所
変更は src/cmd/ld/ldpe.c
ファイルの ldpe
関数内で行われています。
--- a/src/cmd/ld/ldpe.c
+++ b/src/cmd/ld/ldpe.c
@@ -300,6 +300,11 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
rp->add = le64(rsect->base+rp->off);
break;
}
+ // ld -r could generate multiple section symbols for the
+ // same section but with different values, we have to take
+ // that into account
+ if (obj->pesym[symindex].name[0] == '.')
+ rp->add += obj->pesym[symindex].value;
}
qsort(r, rsect->sh.NumberOfRelocations, sizeof r[0], rbyoff);
コアとなるコードの解説
追加されたコードは以下の部分です。
// ld -r could generate multiple section symbols for the
// same section but with different values, we have to take
// that into account
if (obj->pesym[symindex].name[0] == '.')
rp->add += obj->pesym[symindex].value;
このコードブロックは、PE形式の再配置処理ループ内で実行されます。
obj->pesym[symindex].name[0] == '.'
: これは、現在のシンボルがセクションシンボルであるかどうかをチェックしています。PE形式では、セクションシンボルは通常、.text
や.data
のように名前が.
で始まります。rp->add += obj->pesym[symindex].value;
: もしシンボルがセクションシンボルであれば、そのシンボルが持つvalue
を再配置の加算値 (rp->add
) に追加しています。rp->add
は、再配置エントリのオフセットや加算値を保持するフィールドです。obj->pesym[symindex].value
は、PEシンボルテーブル内の現在のシンボル (symindex
で指定される) のvalue
フィールドです。セクションシンボルの場合、このvalue
は通常、そのセクションのベースアドレスからのオフセットを示します。
この修正により、ld -r
が同じセクションに対して異なる value
を持つ複数のセクションシンボルを生成した場合でも、リンカはそれぞれのシンボルが持つオフセット値を適切に再配置計算に反映できるようになります。これにより、再配置後のアドレスが正確になり、PE形式の実行可能ファイルが正しく動作するようになります。
関連リンク
- Go Issue #3322: https://github.com/golang/go/issues/3322
- Go Issue #3261: https://github.com/golang/go/issues/3261 (このコミットが部分的に解決する別の関連Issue)
- Go CL 5823059: https://golang.org/cl/5823059 (このコミットに対応するGoのコードレビューリンク)
参考にした情報源リンク
- Go Issue 3322 (Web検索結果): https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFS_xFS_PXtORGeDZ9GpZdQc56eaQD6QVVlHqEJf4WPq2F2mKQOtNpYCdMsk0QI8nvGrBb76nggsM9cr6CvuyYll4R_NP7JVArEZNDgK8-uAZdj5OtQx2s=
- Portable Executable (PE) Format (一般的な知識)
- リンカの動作原理 (一般的な知識)
- Go言語のツールチェーンに関するドキュメント (一般的な知識)