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

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

本ドキュメントは、Go言語のリンカであるcmd/ldにおける、PE (Portable Executable) ファイルのセクションフラグに関するエラーハンドリングの改善を目的としたコミット(インデックス16167)について、その背景、技術的詳細、およびコード変更点を包括的に解説します。

コミット

commit 813590b145448decb4de52e12ce522fe9c16f9a7
Author: Ian Lance Taylor <iant@golang.org>
Date:   Fri Apr 12 07:58:50 2013 -0700

    cmd/ld: if we fail from an unexpected PE flags value, print it
    
    R=golang-dev, minux.ma, r
    CC=golang-dev
    https://golang.org/cl/8709043

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

https://github.com/golang/go/commit/813590b145448decb4de52e12ce522fe9c16f9a7

元コミット内容

このコミットの目的は、cmd/ld(Goリンカ)が予期しないPEフラグ値によって処理に失敗した場合に、そのフラグ値をエラーメッセージとして出力するように改善することです。これにより、デバッグや問題の特定が容易になります。

変更の背景

Go言語のリンカであるcmd/ldは、Windows環境において実行可能ファイル(PEフォーマット)を生成する役割を担っています。PEファイルは、コード、データ、リソースなどのセクションに分割されており、それぞれのセクションにはその特性を示す「フラグ」(またはセクション特性)が設定されています。これらのフラグは、セクションが実行可能であるか、読み取り可能であるか、書き込み可能であるか、初期化済みデータを含むか、といった情報を示します。

リンカがPEファイルを処理する際、特定のセクションに対して予期しない、または無効なフラグ値が検出されることがあります。このような場合、リンカはエラーを報告して処理を中断しますが、元の実装では「unexpected flags for PE section %s」という一般的なメッセージしか出力されませんでした。このメッセージだけでは、具体的にどのフラグ値が問題を引き起こしたのかが分からず、問題の原因特定が困難でした。

このコミットは、このデバッグの困難さを解消するために導入されました。予期しないフラグ値が検出された際に、その具体的な数値をエラーメッセージに含めることで、開発者がより迅速に問題を診断し、解決できるようにすることが目的です。

前提知識の解説

PE (Portable Executable) フォーマット

PEフォーマットは、Windowsオペレーティングシステムで使用される実行可能ファイル、オブジェクトコード、DLL (Dynamic Link Library) などのファイル形式です。PEファイルは、ヘッダ、セクションテーブル、および複数のセクションで構成されます。各セクションは、プログラムの異なる部分(例: コード、初期化済みデータ、未初期化データ、リソース)を格納し、それぞれに特定の属性(フラグ)が割り当てられています。

PEセクションフラグ (Section Characteristics)

PEセクションフラグは、各セクションの特性を定義するビットフラグの集合です。これらはIMAGE_SECTION_HEADER構造体のCharacteristicsフィールドに格納されます。一般的なフラグには以下のようなものがあります。

  • IMAGE_SCN_CNT_CODE: セクションに実行可能なコードが含まれることを示します。
  • IMAGE_SCN_CNT_INITIALIZED_DATA: セクションに初期化済みデータが含まれることを示します。
  • IMAGE_SCN_CNT_UNINITIALIZED_DATA: セクションに未初期化データが含まれることを示します。
  • IMAGE_SCN_MEM_READ: セクションが読み取り可能であることを示します。
  • IMAGE_SCN_MEM_WRITE: セクションが書き込み可能であることを示します。
  • IMAGE_SCN_MEM_EXECUTE: セクションが実行可能であることを示します。
  • IMAGE_SCN_ALIGN_1BYTES: セクションの配置アライメントを指定します。
  • IMAGE_SCN_MEM_DISCARDABLE: セクションがメモリから破棄可能であることを示します。

これらのフラグは、リンカやOSがPEファイルをロードし、実行する際に、各セクションをどのように扱うべきかを決定するために使用されます。

cmd/ld (Goリンカ)

cmd/ldは、Go言語のコンパイラツールチェーンの一部であり、Goのソースコードから実行可能ファイルを生成するリンカです。リンカは、コンパイルされたオブジェクトファイルやライブラリを結合し、最終的な実行可能ファイルを生成します。Windows環境では、このリンカがPEフォーマットの実行可能ファイルを生成します。

werrstr 関数

werrstrは、Goリンカの内部でエラーメッセージを報告するために使用される関数です。これは、リンカが予期しない状況や無効なデータに遭遇した際に呼び出され、エラー文字列をフォーマットして出力します。

技術的詳細

このコミットが対象としているのは、cmd/ldがPEファイルのセクションを処理する際のロジックです。具体的には、ldpe関数内でセクションのタイプを判断し、それに応じてs->typeを設定するswitch文のdefaultケースです。

リンカは、PEファイルのセクションヘッダを読み込み、そのCharacteristicsフィールド(PEフラグ)を解析して、セクションの目的(コード、データなど)を識別します。通常、リンカは特定のフラグの組み合わせを予期しており、それに基づいてセクションを適切に処理します。しかし、もしリンカが予期しない、または不正なフラグの組み合わせに遭遇した場合、それはPEファイルの構造が破損しているか、またはリンカが対応していない新しいタイプのセクションである可能性を示します。

元のコードでは、このような「予期しないフラグ値」が検出された際に、werrstr("unexpected flags for PE section %s", sect->name);というエラーメッセージを出力していました。このメッセージは、どのセクションで問題が発生したかは示しますが、具体的なフラグ値は示しません。

変更後のコードでは、werrstr("unexpected flags %#08x for PE section %s", sect->sh.Characteristics, sect->name);とすることで、sect->sh.Characteristics、つまり予期しないフラグの実際の16進数値をエラーメッセージに含めるようにしました。%#08xというフォーマット指定子は、数値を0xプレフィックス付きの8桁の16進数として出力することを意味します。これにより、エラーが発生した際に、開発者は具体的なフラグ値を確認し、その値が何を意味するのか、なぜ予期されなかったのかを調査できるようになります。これは、特にPEフォーマットの仕様を理解している開発者にとって、デバッグの効率を大幅に向上させます。

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

--- a/src/cmd/ld/ldpe.c
+++ b/src/cmd/ld/ldpe.c
@@ -232,7 +232,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)\n 			s->type = STEXT;\n 			break;\n 		default:\n-			werrstr("unexpected flags for PE section %s", sect->name);\n+			werrstr("unexpected flags %#08x for PE section %s", sect->sh.Characteristics, sect->name);\n 			goto bad;\n 		}\n 		s->p = sect->base;\

コアとなるコードの解説

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

変更が行われたのは、セクションの特性(フラグ)に基づいてセクションのタイプを決定するswitch文のdefaultケースです。

  • 変更前:

    werrstr("unexpected flags for PE section %s", sect->name);
    

    ここでは、エラーメッセージとして「unexpected flags for PE section [セクション名]」が出力されていました。問題のセクション名は表示されますが、具体的なフラグ値は不明でした。

  • 変更後:

    werrstr("unexpected flags %#08x for PE section %s", sect->sh.Characteristics, sect->name);
    

    変更後では、werrstr関数の引数にsect->sh.Characteristicsが追加されました。sect->sh.Characteristicsは、現在のセクションのPEフラグ(特性)の値を保持しています。%#08xというフォーマット指定子を使用することで、このフラグ値が0xプレフィックス付きの8桁の16進数としてエラーメッセージに埋め込まれるようになります。

この修正により、リンカが予期しないPEセクションフラグに遭遇した場合、エラーメッセージにはセクション名だけでなく、その具体的なフラグ値も含まれるようになり、デバッグ情報が大幅に強化されました。

関連リンク

参考にした情報源リンク