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

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

このコミットは、Go言語のリンカ(cmd/ld)におけるWindowsビルドの不具合を修正するものです。具体的には、PE(Portable Executable)ファイル形式の処理に関連するシンボル参照の重複チェックロジックの順序を変更し、Windows環境でのレース検出器テストのスキップを解除しています。これにより、Windows上でのGoプログラムのビルドとテストの安定性が向上しました。

コミット

commit 8d732368c234a6c4ab2ad6c880ccbe978c6c376c
Author: Russ Cox <rsc@golang.org>
Date:   Fri Mar 1 05:03:25 2013 -0800

    cmd/ld: fix windows build

    Fixes #4948.

    R=golang-dev, alex.brainman
    CC=golang-dev
    https://golang.org/cl/7445045

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

https://github.com/golang/go/commit/8d732368c234a6c4ab2ad6c880ccbe978c6c376c

元コミット内容

このコミットは、GoリンカのWindowsビルドに関する問題を解決します。具体的には、src/cmd/ld/ldpe.c 内の重複シンボル参照チェックのコードブロックを移動し、src/run.bat からWindowsでのレース検出器テストをスキップする行を削除しています。これにより、以前のバグ(Issue #4948)が修正され、Windows環境でのビルドとテストが正常に機能するようになります。

変更の背景

この変更の背景には、Go言語のWindowsビルドにおける既知の不具合(Issue #4948)が存在していました。この不具合により、Windows環境でGoプログラムをビルドする際に、リンカがシンボルの重複参照を誤って検出し、ビルドが失敗する可能性がありました。また、この問題に関連して、Windows上でのレース検出器(Goの並行処理におけるデータ競合を検出するツール)のテストが一時的にスキップされていました。

Go言語はクロスプラットフォーム対応を重視しており、Windows環境での安定したビルドとテストは非常に重要です。このコミットは、これらの問題を解決し、WindowsユーザーがGo言語をよりスムーズに利用できるようにすることを目的としています。特に、リンカの挙動はプログラムの実行ファイル生成に直結するため、その正確性はGo開発環境の健全性にとって不可欠です。

前提知識の解説

このコミットを理解するためには、以下の前提知識が役立ちます。

  • Go言語のビルドプロセス: Go言語のプログラムは、ソースコードから実行ファイルを生成する際に、コンパイルとリンクの段階を経ます。コンパイルはソースコードをオブジェクトファイルに変換し、リンクは複数のオブジェクトファイルやライブラリを結合して最終的な実行ファイルを生成します。
  • リンカ (ld): リンカは、コンパイルされたオブジェクトファイルやライブラリを結合し、実行可能なプログラムを生成するツールです。Go言語のツールチェーンには、独自のリンカ cmd/ld が含まれています。
  • PE (Portable Executable) 形式: Windowsオペレーティングシステムで使用される実行可能ファイル、オブジェクトコード、DLL(Dynamic Link Library)などの標準ファイル形式です。Go言語のリンカは、Windows向けにビルドする際にこのPE形式のファイルを生成します。
  • シンボル: プログラム内の関数、変数、定数などの名前を指します。リンカは、これらのシンボルを解決し、それぞれのメモリ上のアドレスを決定します。
  • シンボル参照の重複: 複数のソースファイルやライブラリで同じ名前のシンボルが定義されている場合に発生する問題です。通常、リンカはこのような重複をエラーとして扱いますが、特定の条件下では許可される場合もあります(例: 弱いシンボル)。
  • レース検出器 (Race Detector): Go言語に組み込まれているツールで、並行処理を行うプログラムにおけるデータ競合(複数のゴルーチンが同時に同じメモリ領域にアクセスし、少なくとも一方が書き込みを行う場合に発生する競合状態)を検出します。これは、並行プログラムのデバッグにおいて非常に強力なツールです。
  • バッチファイル (.bat): Windowsのコマンドプロンプトで実行されるスクリプトファイルです。src/run.bat は、Goのテストスイートを実行するためのスクリプトの一部として使用されていました。
  • Gerrit Change-ID (golang.org/cl/7445045): Goプロジェクトでは、コードレビューにGerritというシステムを使用しています。golang.org/cl/ に続く番号は、特定の変更セット(Change-ID)を指し、その変更に関する詳細なレビュー履歴や議論を確認できます。

技術的詳細

このコミットの技術的詳細は、主にGoリンカのPEファイル処理におけるシンボル管理と、Windows環境でのテスト実行ロジックの修正にあります。

src/cmd/ld/ldpe.c の変更

ldpe.c は、GoリンカがWindowsのPE形式の実行ファイルを生成する際の処理を担当するC言語のソースファイルです。 このファイルにおける主要な変更は、重複シンボル参照のチェックロジックの移動です。

変更前:

@@ -320,12 +320,6 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
 	\t\tgoto bad;
 	\t
 	\t\ts = sym->sym;
-\t\tif(s->outer != S) {\n-\t\t\tif(s->dupok)\n-\t\t\t\tcontinue;\n-\t\t\tdiag(\"%s: duplicate symbol reference: %s in both %s and %s\", pn, s->name, s->outer->name, sect->sym->name);\n-\t\t\terrorexit();\n-\t\t}\n \t\tif(sym->sectnum == 0) {// extern
 \t\t\tif(s->type == SDYNIMPORT)
 \t\t\t\ts->plt = -2; // flag for dynimport in PE object files.

この部分では、sym->syms に代入された直後に、s->outer != S という条件で重複シンボル参照のチェックが行われていました。s->outer は、シンボル s がどこで定義されたか(またはどの「外部」シンボルに関連付けられているか)を示すポインタであると推測されます。S はおそらく、シンボルがまだ外部に定義されていない状態を示す特別な値です。s->dupok は、そのシンボルが重複を許容するかどうかを示すフラグです。もし重複が許容されず、かつ s->outerS でない(つまり、既にどこかで参照されている)場合、リンカはエラーを出力して終了していました。

変更後:

@@ -344,6 +338,13 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
 \n \t\tif(sect == nil) 
 \t\t\treturn;
+\n+\t\tif(s->outer != S) {\n+\t\t\tif(s->dupok)\n+\t\t\t\tcontinue;\n+\t\t\tdiag(\"%s: duplicate symbol reference: %s in both %s and %s\", pn, s->name, s->outer->name, sect->sym->name);\n+\t\t\terrorexit();\n+\t\t}\n \t\ts->sub = sect->sym->sub;
 \t\tsect->sym->sub = s;
 \t\ts->type = sect->sym->type | SSUB;

同じコードブロックが、if(sect == nil) return; の直後に移動されています。 この移動の意図は、シンボル処理の順序を修正することにあります。PEファイルにおけるシンボルの解決やセクションへの割り当ては複雑なプロセスであり、特定のシンボル情報(例えば sectnil でないことや、SDYNIMPORT のような特殊なシンボルタイプに対する処理)が確定する前に重複チェックを行うと、誤ったエラーを引き起こす可能性がありました。 この変更により、リンカはシンボルに関するより多くのコンテキストが確立された後に重複チェックを行うようになり、Windowsビルドにおける誤検出が解消されたと考えられます。これは、リンカがシンボルを処理する際の内部状態や依存関係の順序を正しく保つための重要な修正です。

src/run.bat の変更

src/run.bat は、Goのテストスイートを実行するためのバッチスクリプトです。 このファイルにおける変更は、Windowsでのレース検出器テストのスキップを解除することです。

変更前:

@@ -54,8 +54,6 @@ echo.
 \n if not "%GOHOSTOS%-%GOOS%-%GOARCH%-%CGO_ENABLED%" == "windows-windows-amd64-1" goto norace
 echo # Testing race detector.
-echo # skipping test due to bug (http://code.google.com/p/go/issues/detail?id=4948).
-goto norace
 go test -race -i flag
 if errorlevel 1 goto fail
 go test -race -short flag

以前は、windows-windows-amd64-1 の環境(Windows 64-bit、CGO有効)において、Issue #4948に関連するバグのためにレース検出器のテストがスキップされていました。echo コマンドでスキップの理由が表示され、goto norace でテストが実行されないように制御されていました。

変更後:

if not "%GOHOSTOS%-%GOOS%-%GOARCH%-%CGO_ENABLED%" == "windows-windows-amd64-1" goto norace
echo # Testing race detector.
go test -race -i flag
if errorlevel 1 goto fail
go test -race -short flag

スキップするための2行が削除されたことで、Windows 64-bit環境でもレース検出器のテストが実行されるようになりました。これは、ldpe.c の修正によってIssue #4948が解決され、レース検出器がWindows上で正しく機能するようになったことを示しています。

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

  • src/cmd/ld/ldpe.c:

    • 重複シンボル参照チェックのコードブロックが、ファイルの約24行下(元の320行目から344行目へ)に移動されました。

    • 具体的には、以下の6行が削除され、ほぼ同じ内容の7行が別の位置に挿入されています(インデントの変更による1行増)。

      // 削除された部分 (元の位置)
      		if(s->outer != S) {
      			if(s->dupok)
      				continue;
      			diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name);
      			errorexit();
      		}
      
      // 挿入された部分 (新しい位置)
      		if(s->outer != S) {
      			if(s->dupok)
      				continue;
      			diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name);
      			errorexit();
      		}
      
  • src/run.bat:

    • Windowsでのレース検出器テストをスキップしていた以下の2行が削除されました。

      echo # skipping test due to bug (http://code.google.com/p/go/issues/detail?id=4948).
      goto norace
      

コアとなるコードの解説

このコミットのコアとなる変更は、リンカのシンボル処理におけるタイミングの調整と、それによって可能になったテストの再有効化です。

ldpe.c におけるシンボル重複チェックの移動は、リンカがPEファイルを処理する際の内部的な状態遷移と密接に関連しています。シンボル ss->outer を持つかどうかをチェックする前に、sect(シンボルが属するセクション)が nil でないことを確認する処理が先行するように変更されました。これは、シンボルがどのセクションに属するかが確定する前に重複チェックを行うと、まだ完全に初期化されていないシンボル情報に基づいて誤ったエラーが発生する可能性があったためと考えられます。この順序の変更により、リンカはより正確なシンボル情報を基に重複を判断できるようになり、Windowsビルドでの誤検出が解消されました。

run.bat からのテストスキップ行の削除は、このリンカの修正が成功したことの直接的な結果です。以前はリンカのバグによりWindowsでのレース検出器テストが正しく実行できなかったため、一時的にスキップされていました。リンカの修正によってこの問題が解決されたため、テストを再有効化し、Windows環境でのGoの並行処理の健全性を継続的に検証できるようになりました。これは、Goの品質保証プロセスにおいて重要なステップです。

関連リンク

  • Go Gerrit Change-ID: https://golang.org/cl/7445045
    • このコミットの公式なコードレビューページです。詳細な変更内容、レビューコメント、関連する議論が確認できます。

参考にした情報源リンク

  • Go言語の公式ドキュメント: Go言語のビルドプロセス、リンカ、レース検出器に関する一般的な情報源として。
  • PEファイル形式の仕様: Windowsの実行ファイル形式に関する一般的な知識として。
  • Go Issue Tracker (旧 code.google.com/p/go/issues): コミットメッセージで参照されている http://code.google.com/p/go/issues/detail?id=4948 は、Goプロジェクトが以前使用していたIssueトラッカーのURLです。現在のGoプロジェクトのIssueはGitHubに移行しており、この古いURLは直接アクセスできないか、異なるIssueを指す可能性があります。今回のWeb検索では、現在のGitHubリポジトリで直接的なIssue #4948は見つかりませんでしたが、これはIssueトラッカーの移行や番号の再割り当てによるものと考えられます。このコミットの文脈では、このIssue番号は内部的な追跡番号として機能していたと理解できます。