[インデックス 17650] ファイルの概要
このコミットは、Go言語のリンカである cmd/ld
において、WindowsのPortable Executable (PE) ファイル形式で用いられる IMAGE_SYM_CLASS_LABEL
シンボルクラスの取り扱いを修正するものです。この修正により、「invalid symbol binding」や「malformed pe file」といったエラーが解消されます。具体的には、src/cmd/ld/ldpe.c
ファイルに1行の追加が行われています。
コミット
- コミットハッシュ:
c742179c49e4b67ce7230e5529657a00d4eaaccc
- Author: Shenghou Ma minux.ma@gmail.com
- Date: Thu Sep 19 02:27:53 2013 -0400
- Summary:
cmd/ld
:IMAGE_SYM_CLASS_LABEL
シンボルを処理する。これにより「invalid symbol binding」および「malformed pe file」エラーが引き起こされていた。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c742179c49e4b67ce7230e5529657a00d4eaaccc
元コミット内容
cmd/ld: handle IMAGE_SYM_CLASS_LABEL symbols.
Was causing "invalid symbol binding" and thus "malformed pe file" error.
R=golang-dev, alex.brainman, adg
CC=golang-dev
https://golang.org/cl/13722050
変更の背景
このコミットが行われた背景には、Go言語でWindows向けの実行ファイルをビルドする際に発生していた特定のエラーがあります。具体的には、IMAGE_SYM_CLASS_LABEL
というシンボルクラスがGoリンカ (cmd/ld
) によって適切に処理されていなかったため、「invalid symbol binding」(無効なシンボルバインディング)というエラーが発生し、その結果として「malformed pe file」(不正なPEファイル)が生成されてしまう問題がありました。
PEファイルはWindowsの実行可能ファイル形式であり、その内部にはプログラムの構造やシンボル情報が格納されています。シンボルは変数や関数の名前とアドレスを関連付けるもので、リンカはこれらのシンボルを解決して最終的な実行ファイルを生成します。IMAGE_SYM_CLASS_LABEL
は、COFF (Common Object File Format) シンボルテーブルにおけるシンボルの一種であり、通常はラベルやコンパイラが内部的に生成するシンボルを指します。
Goリンカがこの特定のシンボルクラスを認識せず、適切に処理しない場合、シンボル解決の段階で問題が生じ、最終的に生成されるPEファイルがWindowsの仕様に準拠しない「不正な」状態になっていました。このコミットは、この問題を解決し、Windows環境でのGoプログラムのビルドの信頼性を向上させることを目的としています。
前提知識の解説
Portable Executable (PE) ファイル形式
PEファイルは、Windowsオペレーティングシステムで使用される実行可能ファイル、オブジェクトコード、DLL (Dynamic Link Library) などのファイル形式です。PEファイルは、プログラムのコード、データ、リソース、およびメタデータ(シンボルテーブルなど)を構造化された形式で格納します。リンカは、複数のオブジェクトファイルやライブラリを結合して、最終的なPEファイルを生成します。
COFF (Common Object File Format) シンボルテーブル
PEファイル内部には、COFFシンボルテーブルと呼ばれるセクションが存在します。これは、プログラム内のシンボル(関数名、変数名、ラベルなど)に関する情報(名前、アドレス、型、ストレージクラスなど)を格納するものです。リンカは、このシンボルテーブルを参照して、異なるモジュール間でシンボルを解決し、参照を適切にリンクします。
IMAGE_SYM_CLASS_LABEL
IMAGE_SYM_CLASS_LABEL
は、COFFシンボルテーブルにおけるシンボルの「ストレージクラス(Storage Class)」の一つです。ストレージクラスは、シンボルの種類やリンカによる扱われ方を示します。IMAGE_SYM_CLASS_LABEL
は、通常、コード内の特定の場所を示すラベルや、コンパイラが内部的に生成する一時的なシンボルに対して使用されます。これらのシンボルは、通常、外部から参照されることを意図しておらず、リンカが内部的に処理するために存在します。
cmd/ld
(Goリンカ)
cmd/ld
は、Go言語のツールチェインに含まれるリンカです。Goコンパイラによって生成されたオブジェクトファイル(.o
ファイル)や、Goの標準ライブラリ、外部ライブラリなどを結合し、最終的な実行可能ファイル(WindowsではPEファイル)を生成する役割を担います。リンカは、シンボル解決、アドレスの割り当て、セクションの結合など、実行ファイル生成に必要な様々な処理を行います。
「invalid symbol binding」と「malformed pe file」エラー
- invalid symbol binding: これは、リンカがシンボルを解決しようとした際に、そのシンボルのバインディング(結合方法や参照方法)が不正であると判断した場合に発生するエラーです。例えば、シンボルクラスがリンカの期待するものでなかったり、シンボルが参照しているアドレスが不正であったりする場合に起こりえます。
- malformed pe file: これは、生成されたPEファイルがWindowsのPEファイル形式の仕様に準拠していない場合に発生するエラーです。シンボルテーブルの破損、セクションの不正な配置、ヘッダ情報の誤りなど、様々な原因で発生する可能性があります。今回のケースでは、「invalid symbol binding」が原因で、最終的に不正なPEファイルが生成されていました。
技術的詳細
このコミットの技術的な核心は、Goリンカ (cmd/ld
) がPEファイル内のCOFFシンボルを読み込む際の処理にあります。src/cmd/ld/ldpe.c
ファイルの readsym
関数は、PEオブジェクトファイルからシンボルを読み込み、Goリンカの内部表現に変換する役割を担っています。
readsym
関数内では、読み込んだシンボルの StorageClass
に応じて異なる処理が行われます。以前のバージョンでは、IMAGE_SYM_CLASS_LABEL
というストレージクラスを持つシンボルが明示的に処理されていませんでした。これは、リンカがこの種のシンボルを認識せず、その結果として不正なシンボルバインディングを引き起こし、最終的に不正なPEファイルが生成される原因となっていました。
具体的には、IMAGE_SYM_CLASS_NULL
や IMAGE_SYM_CLASS_STATIC
といった他のストレージクラスのシンボルと同様に、IMAGE_SYM_CLASS_LABEL
のシンボルも lookup
関数を通じてリンカのシンボルテーブルに登録されるべきでした。lookup
関数は、シンボル名とバージョンに基づいて既存のシンボルを検索するか、新しいシンボルを作成します。そして、s->dupok = 1;
という行は、このシンボルが重複を許容する(つまり、複数の場所で定義されていても問題ない)ことを示しています。これは、ラベルのような内部シンボルが複数の場所で出現する可能性があるため、重要な設定です。
このコミットは、readsym
関数内の switch
ステートメントに case IMAGE_SYM_CLASS_LABEL:
を追加することで、IMAGE_SYM_CLASS_LABEL
シンボルが IMAGE_SYM_CLASS_NULL
や IMAGE_SYM_CLASS_STATIC
と同じ方法で処理されるように変更しました。これにより、リンカは IMAGE_SYM_CLASS_LABEL
シンボルを正しく認識し、内部シンボルとして適切に処理できるようになり、シンボルバインディングのエラーやPEファイルの破損を防ぐことができます。
コアとなるコードの変更箇所
diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c
index 6ed861d7f0..6bcda2cb66 100644
--- a/src/cmd/ld/ldpe.c
+++ b/src/cmd/ld/ldpe.c
@@ -468,6 +468,7 @@ readsym(PeObj *obj, int i, PeSym **y)\n \t\t\tbreak;\n \t\tcase IMAGE_SYM_CLASS_NULL:\n \t\tcase IMAGE_SYM_CLASS_STATIC:\n+\t\tcase IMAGE_SYM_CLASS_LABEL:\n \t\t\ts = lookup(name, version);\n \t\t\ts->dupok = 1;\n \t\t\tbreak;\n```
## コアとなるコードの解説
変更は `src/cmd/ld/ldpe.c` ファイルの `readsym` 関数内で行われています。
元のコードでは、`switch` ステートメントがシンボルのストレージクラス (`s->Sym.StorageClass`) に基づいて処理を分岐していました。`IMAGE_SYM_CLASS_NULL` と `IMAGE_SYM_CLASS_STATIC` のケースは既に存在し、これらのシンボルに対しては `lookup` 関数を呼び出してシンボルテーブルに登録し、`dupok = 1` を設定していました。
追加された行は以下の通りです。
```c
+ case IMAGE_SYM_CLASS_LABEL:
この1行が追加されたことで、IMAGE_SYM_CLASS_LABEL
というストレージクラスを持つシンボルも、IMAGE_SYM_CLASS_NULL
や IMAGE_SYM_CLASS_STATIC
と同じ処理パスを通るようになりました。つまり、IMAGE_SYM_CLASS_LABEL
シンボルも lookup(name, version)
によってリンカのシンボルテーブルに登録され、s->dupok = 1;
が設定されます。
この変更により、Goリンカは IMAGE_SYM_CLASS_LABEL
シンボルを正しく認識し、内部シンボルとして適切に処理できるようになります。これにより、以前発生していた「invalid symbol binding」エラーが解消され、結果として「malformed pe file」が生成される問題も解決されます。これは、Windows環境でGoプログラムをビルドする際の安定性と互換性を向上させるための重要な修正です。
関連リンク
- Go CL 13722050: https://golang.org/cl/13722050
参考にした情報源リンク
- go.dev:
debug/pe
package documentation (COFFSymbol, StorageClass) - documentation.help:
IMAGE_SYM_CLASS_LABEL
definition - github.com: Go linker source code related to PE files
- Microsoft Portable Executable and Common Object File Format Specification
- Go言語のリンカに関するドキュメントや議論