[インデックス 10346] ファイルの概要
このコミットは、src/cmd/ld/ldpe.c
ファイルに対して行われた変更です。このファイルはGo言語のリンカの一部であり、特にWindowsの実行可能ファイル形式であるPE (Portable Executable) ファイルの生成に関連する処理を扱っています。
コミット
commit 3199a6ca8d06ef608080f1ef6c0523ebbbeb0025
Author: Wei Guangjing <vcc.163@gmail.com>
Date: Fri Nov 11 14:40:24 2011 -0500
ld: fix .bss for ldpe.
Fixes #2409.
R=mattn.jp, rsc, alex.brainman
CC=golang-dev
https://golang.org/cl/5334046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3199a6ca8d06ef608080f1ef6c0523ebbbeb0025
元コミット内容
ld: fix .bss for ldpe.
Fixes #2409.
R=mattn.jp, rsc, alex.brainman
CC=golang-dev
https://golang.org/cl/5334046
変更の背景
このコミットは、Go言語のリンカ(ld
)がWindowsのPE形式の実行ファイルを生成する際に発生していた問題、特に.bss
セクションの取り扱いに関するバグを修正することを目的としています。コミットメッセージにある「Fixes #2409」は、GoプロジェクトのIssue 2409を修正したことを示しています。
Go Issue 2409の概要は「crash while accessing static variables in CGO」とされており、CGO(C言語との相互運用機能)を通じて静的変数にアクセスする際にクラッシュが発生するというものでした。静的変数は通常、.bss
セクションまたは.data
セクションに配置されます。.bss
セクションは初期化されていない静的変数やグローバル変数を格納するために使用され、実行時にゼロで初期化されることが期待されます。
リンカがPEファイル内の.bss
セクションのシンボルやリロケーション(再配置)情報を正しく処理しない場合、プログラムがこれらの変数にアクセスしようとしたときに、誤ったアドレスを参照したり、未定義の動作を引き起こしたりする可能性があります。このコミットは、特にリロケーション処理において、.bss
セクションに関連するアドレス計算が誤っていたために発生していたクラッシュを解決するためのものです。
前提知識の解説
リンカ (ld)
リンカは、コンパイラによって生成されたオブジェクトファイル(機械語コードとデータを含む)を結合し、実行可能ファイルやライブラリを生成するプログラムです。リンカの主な役割は以下の通りです。
- シンボル解決: 異なるオブジェクトファイル間で参照される関数や変数のアドレスを解決します。
- 再配置 (Relocation): コードやデータ内のアドレス参照を、最終的なメモリレイアウトに合わせて調整します。
- セクションの結合: オブジェクトファイル内の同じ種類のセクション(例: コードセクション、データセクション)を結合します。
PE (Portable Executable) フォーマット
PEは、Windowsオペレーティングシステムで使用される実行可能ファイル、オブジェクトコード、DLL(ダイナミックリンクライブラリ)などのファイル形式です。PEファイルは、ヘッダ、セクションテーブル、および複数のセクション(例: .text
、.data
、.bss
)で構成されます。
.bss セクション
.bss
セクションは、初期化されていない静的変数およびグローバル変数を格納するために使用されるデータセクションです。プログラムのロード時、オペレーティングシステムは.bss
セクションのメモリ領域をゼロで初期化します。これにより、プログラマが明示的に初期化しなかった変数が、デフォルトでゼロ値を持つことが保証されます。
リロケーション (Relocation)
リロケーションとは、プログラムがメモリにロードされる際に、コードやデータ内のアドレス参照を修正するプロセスです。コンパイル時には、変数の正確なメモリ位置が不明な場合があるため、リンカは後でこれらの参照を修正するための「リロケーションエントリ」を生成します。
IMAGE_REL_I386_REL32
/IMAGE_REL_AMD64_REL32
: これらはPEファイルにおけるリロケーションタイプの一種で、それぞれ32ビットx86アーキテクチャと64ビットAMD64アーキテクチャにおける相対32ビットリロケーションを示します。これは、参照されるアドレスが、リロケーションが発生する場所からの相対オフセットとして表現されることを意味します。
rp->add
リロケーションエントリ(rp
はリロケーションポインタ)の一部であり、リロケーションの計算に使用される加算値(addend)を表します。相対リロケーションの場合、この値は通常、リロケーションの対象となるシンボルのアドレスに加算されるオフセットを含みます。
le32
le32
は "little-endian 32-bit" の略で、32ビットのデータをリトルエンディアン形式で読み取る関数を指します。リトルエンディアンは、データの最下位バイトが最初に格納されるバイト順序です。PEファイルは通常リトルエンディアン形式で格納されます。
rsect->base
rsect
はリロケーションが適用されるセクションの情報を保持する構造体で、rsect->base
はそのセクションのベースアドレス(メモリ上の開始アドレス)を示します。
rp->off
rp
はリロケーションエントリのポインタで、rp->off
はリロケーションが適用されるオフセット(セクションの開始位置からの相対位置)を示します。
IMAGE_SYM_CLASS_STATIC
PEファイル内のシンボルテーブルエントリのクラス(種類)を示す定数の一つです。IMAGE_SYM_CLASS_STATIC
は、シンボルが静的(ファイルスコープ)であることを示します。
sym->name[0] == '.'
PEファイルでは、セクション名が通常ピリオド(.
)で始まる慣習があります(例: .text
, .data
, .bss
)。このチェックは、シンボル名がセクション名であるかどうかを簡易的に判断するために使用されます。
技術的詳細
このコミットは、主にPEファイルのリロケーション処理とシンボル解決の2つの側面で修正を行っています。
-
リロケーション処理の修正: 以前のコードでは、
IMAGE_REL_I386_REL32
およびIMAGE_REL_AMD64_REL32
タイプのリロケーションに対して、rp->add = 0;
と設定されていました。これは、相対リロケーションの加算値が常にゼロであると仮定していたことを意味します。しかし、実際には、これらのリロケーションタイプでは、リロケーションが適用される場所の現在の値(通常はターゲットへの相対オフセット)が加算値として考慮される必要があります。 修正後のコードでは、rp->add = le32(rsect->base+rp->off);
となっています。これは、リロケーションが適用されるメモリ位置(rsect->base + rp->off
)から32ビットのリトルエンディアン値を読み取り、それをrp->add
に設定することを意味します。これにより、リンカは既存の相対オフセットを正しく読み取り、それに新しい相対オフセットを加算することで、正確な最終アドレスを計算できるようになります。この修正は、特に.bss
セクションのような、初期値がゼロであるべきセクション内の静的変数への参照が正しく解決されるために重要です。誤ったrp->add
の値は、最終的なアドレス計算の誤りにつながり、結果としてプログラムのクラッシュを引き起こす可能性がありました。 -
シンボル解決の修正 (
readsym
関数内):readsym
関数は、PEファイルのシンボルテーブルからシンボルを読み取る役割を担っています。以前のコードでは、シンボルがセクションを表すかどうかを判断するために、sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0 && sym->type == 0
という条件を使用していました。この条件は、静的クラス、値がゼロ、タイプがゼロのシンボルをセクションとして識別しようとしていました。しかし、この条件は必ずしも正確ではありませんでした。 修正後のコードでは、if(sym->name[0] == '.')
というより直接的なチェックに置き換えられています。PEファイルでは、セクション名が慣習的にピリオド(.
)で始まるため、このチェックはシンボルがセクション名であるかどうかをより確実に識別できます。この変更は、リンカがシンボルを正しく分類し、特に.bss
のようなセクションシンボルを適切に処理するために重要です。シンボルの誤った分類は、リンカがセクションのサイズや配置を誤って計算し、結果として実行可能ファイルの破損やランタイムエラーを引き起こす可能性がありました。
これらの修正は、Go言語でCGOを使用し、特にWindows環境で静的変数にアクセスする際の安定性と正確性を向上させるために不可欠でした。
コアとなるコードの変更箇所
diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c
index c112cb5394..8d175b1156 100644
--- a/src/cmd/ld/ldpe.c
+++ b/src/cmd/ld/ldpe.c
@@ -283,7 +283,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)\n case IMAGE_REL_I386_REL32:\n case IMAGE_REL_AMD64_REL32:\n rp->type = D_PCREL;\n- rp->add = 0;\n+ rp->add = le32(rsect->base+rp->off);\n break;\n case IMAGE_REL_I386_DIR32NB:\n case IMAGE_REL_I386_DIR32:\n@@ -408,7 +408,7 @@ readsym(PeObj *obj, int i, PeSym **y)\n sym = &obj->pesym[i];\n *y = sym;\n \n- if(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0 && sym->type == 0) // section\n+ if(sym->name[0] == '.') // .section\n name = obj->sect[sym->sectnum-1].sym->name;\n else {\n name = sym->name;\n```
## コアとなるコードの解説
### 1. リロケーション処理の修正 (`ldpe` 関数内)
```diff
@@ -283,7 +283,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)\n case IMAGE_REL_I386_REL32:\n case IMAGE_REL_AMD64_REL32:\n rp->type = D_PCREL;\n- rp->add = 0;\n+ rp->add = le32(rsect->base+rp->off);\n break;\n```
* **変更前 (`- rp->add = 0;`)**:
`IMAGE_REL_I386_REL32` および `IMAGE_REL_AMD64_REL32` という相対リロケーションタイプに対して、リロケーションの加算値 (`rp->add`) を無条件に `0` に設定していました。これは、リロケーションが適用されるメモリ位置に既に存在する相対オフセット値を無視することを意味します。結果として、リンカが最終的なアドレスを計算する際に、この既存のオフセットが考慮されず、誤ったアドレスが生成される可能性がありました。特に、`.bss`セクションのような初期化されていないデータセクションでは、この誤りがクラッシュの原因となることがありました。
* **変更後 (`+ rp->add = le32(rsect->base+rp->off);`)**:
リロケーションの加算値 (`rp->add`) を、リロケーションが適用されるメモリ位置 (`rsect->base + rp->off`) から32ビットのリトルエンディアン値 (`le32`) を読み取って設定するように変更されました。
* `rsect->base`: リロケーションが属するセクションのベースアドレス。
* `rp->off`: セクションの開始位置からのリロケーションのオフセット。
* `rsect->base + rp->off`: リロケーションが適用される実際のメモリ位置。
* `le32(...)`: そのメモリ位置から既存の32ビット値をリトルエンディアン形式で読み取ります。
この修正により、リンカはリロケーションが適用される場所の既存の相対オフセットを正しく取得し、それを加算値として使用できるようになります。これにより、最終的なアドレス計算が正確になり、特に`.bss`セクション内の静的変数への参照が正しく解決されるようになります。
### 2. シンボル解決の修正 (`readsym` 関数内)
```diff
@@ -408,7 +408,7 @@ readsym(PeObj *obj, int i, PeSym **y)\n sym = &obj->pesym[i];\n *y = sym;\n \n- if(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0 && sym->type == 0) // section\n+ if(sym->name[0] == '.') // .section\n name = obj->sect[sym->sectnum-1].sym->name;\n else {\n name = sym->name;\n```
* **変更前 (`- if(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0 && sym->type == 0) // section`)**:
シンボルがセクションを表すかどうかを判断するために、シンボルのクラスが `IMAGE_SYM_CLASS_STATIC` であり、かつ値とタイプが両方とも `0` であるという条件を使用していました。この条件は、一部のセクションシンボルを正しく識別できない場合がありました。例えば、セクションによっては `value` や `type` が `0` ではない場合があるため、この条件では不十分でした。
* **変更後 (`+ if(sym->name[0] == '.') // .section`)**:
シンボルがセクションを表すかどうかを判断するために、シンボル名 (`sym->name`) の最初の文字がピリオド(`.`)であるかどうかをチェックするように変更されました。PEファイルでは、セクション名(例: `.text`, `.data`, `.bss`)は慣習的にピリオドで始まるため、このチェックはセクションシンボルをより確実に識別するためのシンプルかつ効果的な方法です。この修正により、リンカはセクションシンボルを正確に認識し、それに応じてセクションの情報を適切に処理できるようになります。
これらの変更は、Go言語のリンカがWindowsのPEファイルを生成する際の正確性と堅牢性を向上させ、特にCGOを介した静的変数へのアクセスに関連するクラッシュを解決する上で重要な役割を果たしました。
## 関連リンク
* **GitHubコミットページ**: https://github.com/golang/go/commit/3199a6ca8d06ef608080f1ef6c0523ebbbeb0025
* **Go Issue 2409**: https://github.com/golang/go/issues/2409 (Web検索結果より「crash while accessing static variables in CGO」と特定)
* **Go CL 5334046**: https://golang.org/cl/5334046 (Goのコードレビューシステムにおける変更リスト)
## 参考にした情報源リンク
* **Portable Executable (PE) Format**:
* Microsoft Docs: PE Format (https://docs.microsoft.com/en-us/windows/win32/debug/pe-format)
* **Linkers and Loaders**:
* "Linkers and Loaders" by John R. Levine (一般的なリンカの概念について)
* **Go Issue Tracker**:
* Go Issue 2409 (Web検索結果より)I have generated the commit explanation based on the provided instructions and the retrieved commit data. I have included all the required sections in Japanese, with detailed explanations of the background, prerequisite knowledge, and technical details. I also included the core code changes and their explanations, along with relevant links.
Please let me know if you need any further modifications or explanations.