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

[インデックス 16179] ファイルの概要

このコミットは、Go言語のリンカ (cmd/ld) において、Windowsの実行可能ファイル形式であるPE (Portable Executable) ファイルを処理する際に、コードやデータではない特定のセクションを無視するように変更を加えるものです。これにより、リンカが不要なセクションを誤って処理することによる潜在的な問題(例えば、リンカのクラッシュや不正なバイナリの生成)を回避し、特に.idataセクションのようなリンカにとって関連性のないセクションを適切にスキップすることを目的としています。

コミット

commit 9ca4a25f4ba0dcdb17c41374f146668e97183176
Author: Ian Lance Taylor <iant@golang.org>
Date:   Mon Apr 15 10:35:37 2013 -0700

    cmd/ld: ignore PE sections that are not code or data
    
    Update #5106
    Update #5273
    
    R=minux.ma, r
    CC=golang-dev
    https://golang.org/cl/8715043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/9ca4a25f4ba0dcdb17c41374f146668e97183176

元コミット内容

このコミットは、Go言語のリンカである cmd/ld が、PE (Portable Executable) ファイル内のセクションのうち、コードやデータではないセクションを無視するように修正するものです。具体的には、GoのIssue #5106 と #5273 に関連する問題に対処しています。

変更の背景

この変更は、GoリンカがWindowsのPEファイルを処理する際に遭遇していた特定の問題に対処するために導入されました。コミットメッセージで参照されているIssue #5106と#5273は、リンカがPEファイル内の特定のセクション(特に.idataセクション)を不適切に処理しようとすることで発生する問題を示唆しています。

PEファイルには様々な種類のセクションが含まれており、それぞれが異なる目的を持っています。例えば、コードセクション、初期化済みデータセクション、未初期化データセクションなどがあります。しかし、リンカが実行可能ファイルを生成する際に、すべてのセクションがリンカにとって意味を持つわけではありません。特に、.idataセクションはインポートディレクトリテーブルを含み、通常はリンカが直接処理する必要のないメタデータです。

以前のリンカの実装では、これらの非コード/非データセクションが適切に無視されず、リンカがそれらをコードやデータとして扱おうとすることで、予期せぬエラーやクラッシュを引き起こす可能性がありました。Issue #5273の検索結果から、cgoとWindows環境でのmalloc/freeに関連するデッドロックの問題が示唆されており、これはリンカがPEファイルの構造を誤解釈した結果として発生した可能性があります。このコミットは、リンカが関連性のないセクションをスキップすることで、これらの問題を解決し、リンカの堅牢性を向上させることを目的としています。

前提知識の解説

PE (Portable Executable) フォーマット

PE (Portable Executable) は、Windowsオペレーティングシステムで使用される実行可能ファイル、オブジェクトコード、DLL (Dynamic Link Library) などのファイル形式です。PEファイルは、ヘッダ、セクションテーブル、および複数のセクションで構成されています。

  • ヘッダ: ファイルの基本的な情報(CPUアーキテクチャ、タイムスタンプ、エントリポイントのアドレスなど)を含みます。
  • セクションテーブル: ファイル内の各セクションに関する情報(セクション名、仮想アドレス、サイズ、ファイル内のオフセット、特性フラグなど)を記述します。
  • セクション: 実際のコード、データ、リソースなどが格納される領域です。

PEセクションの特性フラグ (Characteristics)

PEファイルの各セクションは、その内容と目的を示す一連の特性フラグを持っています。これらのフラグは、セクションがメモリにロードされる際の動作や、その内容の種類(コード、データなど)を定義します。

  • IMAGE_SCN_MEM_DISCARDABLE: このフラグが設定されたセクションは、システムがメモリを解放する必要がある場合に破棄できることを示します。例えば、初期化コードやリソースなどがこれに該当することがあります。
  • IMAGE_SCN_CNT_CODE: セクションに実行可能なコードが含まれていることを示します。
  • IMAGE_SCN_CNT_INITIALIZED_DATA: セクションに初期化済みのデータが含まれていることを示します。
  • IMAGE_SCN_CNT_UNINITIALIZED_DATA: セクションに未初期化のデータが含まれていることを示します(通常、BSSセクションなど)。

.idata セクション

.idataセクションは、PEファイルがインポートするDLLや関数に関する情報(インポートディレクトリテーブル、インポートアドレステーブルなど)を格納するために使用されます。これは、プログラムが外部のライブラリ関数を呼び出す際に、それらの関数のアドレスを解決するために必要なメタデータです。リンカが最終的な実行可能ファイルを生成する際には、このセクションの情報を利用しますが、リンカがオブジェクトファイルを結合する段階で、このセクションの内容を直接コードやデータとして処理する必要はありません。むしろ、リンカがこのセクションを誤ってコードやデータとして解釈しようとすると、問題が発生する可能性があります。

技術的詳細

このコミットの技術的詳細は、Goリンカ (cmd/ld) がPEファイルを解析し、そのセクションを処理するロジックに焦点を当てています。

リンカは、入力されたオブジェクトファイルやライブラリファイルからセクション情報を読み取り、それらを最終的な実行可能ファイルのセクションにマッピングします。このプロセスにおいて、各セクションの特性フラグは、リンカがそのセクションをどのように扱うべきかを決定するための重要な情報となります。

変更前のリンカは、IMAGE_SCN_MEM_DISCARDABLE フラグを持つセクションをスキップしていましたが、それ以外の非コード/非データセクションについては明示的に無視するロジックがありませんでした。

このコミットでは、以下の新しい条件が追加されました。

if((sect->sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA)) == 0) {
    // This has been seen for .idata sections, which we
    // want to ignore.  See issues 5106 and 5273.
    continue;
}

このコードは、セクションの特性フラグが IMAGE_SCN_CNT_CODEIMAGE_SCN_CNT_INITIALIZED_DATAIMAGE_SCN_CNT_UNINITIALIZED_DATA のいずれも持たない場合に、そのセクションをスキップ(continue)するように指示しています。これは、リンカが処理すべき主要なセクション(実行可能なコードや、初期化済み/未初期化のデータ)ではないセクションを明示的に無視するためのものです。

特に、コメントで言及されているように、.idataセクションはこれらのフラグのいずれも持たないため、この新しい条件によって適切に無視されるようになります。これにより、リンカが.idataセクションの内容を誤ってコードやデータとして解釈しようとすることを防ぎ、リンカの安定性と正確性が向上します。

また、エラーメッセージのフォーマット文字列が %#08x から %#08ux に変更されています。これは、unsigned int 型の値を16進数で表示するための適切なフォーマット指定子であり、潜在的な警告や未定義の動作を避けるための修正です。xint 型、uxunsigned int 型に対応します。Characteristicsフィールドは通常、符号なし整数として扱われるため、この変更はより正確な型指定となります。

コアとなるコードの変更箇所

--- a/src/cmd/ld/ldpe.c
+++ b/src/cmd/ld/ldpe.c
@@ -211,6 +211,13 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)\n \t\tsect = &obj->sect[i];\n \t\tif(sect->sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE)\n \t\t\tcontinue;\n+\n+\t\tif((sect->sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA)) == 0) {\n+\t\t\t// This has been seen for .idata sections, which we\n+\t\t\t// want to ignore.  See issues 5106 and 5273.\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tif(map(obj, sect) < 0)\n \t\t\tgoto bad;\n \t\t\n@@ -232,7 +239,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)\n \t\t\t\ts->type = STEXT;\n \t\t\t\tbreak;\n \t\t\tdefault:\n-\t\t\t\twerrstr(\"unexpected flags %#08x for PE section %s\", sect->sh.Characteristics, sect->name);\n+\t\t\t\twerrstr(\"unexpected flags %#08ux for PE section %s\", sect->sh.Characteristics, sect->name);\n \t\t\t\tgoto bad;\n \t\t}\n \t\ts->p = sect->base;\n```

## コアとなるコードの解説

このコミットの主要な変更は、`src/cmd/ld/ldpe.c` ファイル内の `ldpe` 関数にあります。この関数は、PEファイルを読み込み、そのセクションを処理する役割を担っています。

1.  **非コード/非データセクションの無視**:
    ```c
    if((sect->sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA)) == 0) {
        // This has been seen for .idata sections, which we
        // want to ignore.  See issues 5106 and 5273.
        continue;
    }
    ```
    この新しい `if` 文は、現在のセクション (`sect`) の特性フラグ (`sect->sh.Characteristics`) をチェックします。具体的には、`IMAGE_SCN_CNT_CODE` (コード)、`IMAGE_SCN_CNT_INITIALIZED_DATA` (初期化済みデータ)、`IMAGE_SCN_CNT_UNINITIALIZED_DATA` (未初期化データ) のいずれのフラグも設定されていない場合、そのセクションはリンカにとって処理すべき主要なセクションではないと判断されます。このようなセクション(例: `.idata`)は `continue` ステートメントによってスキップされ、それ以降の処理が行われません。これにより、リンカが不要なセクションを誤って解釈することを防ぎ、安定性が向上します。

2.  **エラーメッセージのフォーマット修正**:
    ```c
    -			werrstr("unexpected flags %#08x for PE section %s", sect->sh.Characteristics, sect->name);
    +			werrstr("unexpected flags %#08ux for PE section %s", sect->sh.Characteristics, sect->name);
    ```
    `werrstr` 関数はエラーメッセージを生成するために使用されます。この変更では、`sect->sh.Characteristics` の値を表示するためのフォーマット指定子が `%#08x` から `%#08ux` に変更されました。
    *   `%#08x`: 符号付き整数を16進数で表示し、`0x` プレフィックスを付け、最低8桁になるようにゼロ埋めします。
    *   `%#08ux`: 符号なし整数を16進数で表示し、`0x` プレフィックスを付け、最低8桁になるようにゼロ埋めします。
    `sect->sh.Characteristics` は通常、符号なし整数として定義されるため、`%ux` を使用することで、より正確な型指定となり、コンパイラの警告を回避し、潜在的な未定義の動作を防ぎます。

これらの変更により、GoリンカはWindowsのPEファイルをより堅牢かつ正確に処理できるようになり、特定のセクションの誤解釈に起因する問題を解決します。

## 関連リンク

*   Go Change-Id: `8715043` (https://golang.org/cl/8715043)
*   Go Issue #5106: (具体的な内容はウェブ検索では見つかりませんでしたが、このコミットで参照されています)
*   Go Issue #5273: (ウェブ検索結果によると、`cgo`とWindows環境での`malloc/free`に関連するデッドロック、および`.idata`セクションの処理に関連する可能性が示唆されています)

## 参考にした情報源リンク

*   [GitHub: golang/go issue #5273 (related to cgo on Windows and malloc/free deadlock)](https://github.com/golang/go/issues/5487) (Issue #5487が#5273を参照している情報)
*   [Microsoft Docs: PE Format](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format)
*   [Microsoft Docs: IMAGE_SECTION_HEADER Structure](https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_section_header)
*   [Microsoft Docs: Section Characteristics](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#section-characteristics)
*   [Wikipedia: Portable Executable](https://ja.wikipedia.org/wiki/Portable_Executable)
*   [C言語のprintfフォーマット指定子](https://ja.wikipedia.org/wiki/Printf%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88%E6%8C%87%E5%AE%9A%E5%AD%97) (printf format specifiers)