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

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

このコミットは、Go言語のリンカ(cmd/ld)における履歴スタックのポップ処理に関するテストの改善を目的としています。特にWindowsビルドでの問題を修正するために行われました。

コミット

commit ff52cadc01756d097e5f3e3fe5ff2634d8825ffb
Author: Russ Cox <rsc@golang.org>
Date:   Thu Jul 18 11:40:59 2013 -0400

    cmd/ld: refine test for history stack pop (fix Windows build)
    
    This should fix the Windows build, or at least
    what's breaking it at the moment.
    
    Fixes #5904.
    
    TBR=golang-dev
    CC=golang-dev
    https://golang.org/cl/11519044

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

https://github.com/golang/go/commit/ff52cadc01756d097e5f3e3fe5ff2634d8825ffb

元コミット内容

このコミットは、cmd/ld(Goリンカ)における履歴スタックのポップ処理のテストを洗練し、Windowsビルドの問題を修正することを目的としています。具体的には、savehist関数内の条件分岐が変更され、histfrogpの値に基づいて履歴のポップを判断するように修正されています。これにより、Windows環境でのリンカの挙動が安定し、ビルドが成功するようになります。

変更の背景

この変更は、GoリンカのWindowsビルドが特定の状況下で失敗するという問題(Issue #5904)を解決するために導入されました。元のコードでは、履歴スタックのポップを検出するためにtmp[0]がヌル文字であるかどうかをチェックしていました。しかし、Windows環境のmisc/cgoテストにおいて、リンカが空文字列に対応するANAMEを受け取ることがあり、その結果histfrogpが0より大きいにもかかわらず、copyhistfrogが空文字列を返すという状況が発生していました。これにより、tmp[0]がヌル文字となり、本来ポップではない状況がポップと誤認識され、リンカの処理が不正になることがありました。

コミットメッセージによると、この問題はgo tool packPコマンドに起因する可能性も示唆されていますが、根本原因を追跡するよりも、条件分岐をhistfrogpの値で直接チェックする方が容易であると判断されました。histfrogpは履歴スタックの深さを示す変数であり、これが0より大きい場合は履歴が積まれている状態、つまりポップではない状態を示します。この修正により、Windowsビルドの安定性が向上しました。

前提知識の解説

  • Goリンカ (cmd/ld): Go言語のコンパイラツールチェーンの一部であり、コンパイルされたオブジェクトファイルやライブラリを結合して実行可能ファイルを生成する役割を担います。C言語で書かれており、Goプログラムのビルドプロセスにおいて重要なコンポーネントです。
  • 履歴スタック (History Stack): リンカがシンボルやファイルパスなどの情報を処理する際に、その履歴を追跡するために使用される内部的なデータ構造です。特に、デバッグ情報やスタックトレースの生成に関連して、どのファイルや関数からシンボルが参照されたかといった情報を保持するために利用されます。
  • savehist 関数: リンカの内部関数の一つで、履歴スタックに現在のファイルパスや行番号などの情報を保存する役割を担います。この関数は、リンカがオブジェクトファイルを処理し、シンボルを解決する過程で呼び出されます。
  • copyhistfrog 関数: 履歴スタックの現在の状態(通常はファイルパス)を文字列として取得する内部関数です。
  • histfrogp 変数: 履歴スタックの現在の深さ(要素数)を示すポインタまたはカウンタです。この値が0であればスタックは空であり、0より大きければ要素が積まれていることを示します。
  • ANAME: リンカ内部でシンボル名を表現するために使用されるデータ構造の一つです。
  • SFILEPATH: シンボルのタイプの一つで、ファイルパスを表すシンボルであることを示します。
  • Issue #5904: このコミットが修正したGo言語のバグトラッカー上の課題です。通常、GoのIssueトラッカーで詳細な議論や再現手順が確認できます。

技術的詳細

このコミットの技術的詳細は、src/cmd/ld/lib.cファイル内のsavehist関数の変更に集約されます。

元のコードでは、savehist関数内でcopyhistfrogを呼び出し、その結果が空文字列であるかどうか(tmp[0]がヌル文字であるかどうか)をチェックすることで、履歴スタックの「ポップ」を判断していました。このロジックは、histfrogp == 0(履歴スタックが空)の場合にcopyhistfrogが空文字列を返すという前提に基づいています。

しかし、Windows環境の特定のテストケース(misc/cgoテスト)において、リンカが空文字列に対応するANAMEを受け取ることがありました。このANAMEが唯一のhistfrog(履歴スタック上の要素)となる場合、histfrogpは0より大きい(スタックは空ではない)にもかかわらず、copyhistfrogは空文字列を返してしまいます。これにより、tmp[0]がヌル文字となり、本来ポップではない状況がポップと誤認識され、リンカの処理が不正になるという問題が発生していました。

このコミットでは、この誤認識を防ぐために条件分岐のロジックが変更されました。具体的には、if(tmp[0])という条件がif(histfrogp > 0)に変更されました。

  • 変更前:
    tmp[0] = '\0';
    copyhistfrog(tmp, sizeof tmp);
    
    if(tmp[0]) { // tmp[0]がヌル文字でない場合に実行
        // ...
    }
    
  • 変更後:
    // NOTE(rsc): We used to do the copyhistfrog first and this
    // condition was if(tmp[0] != '\0') to check for an empty string,
    // implying that histfrogp == 0, implying that this is a history pop.
    // However, on Windows in the misc/cgo test, the linker is
    // presented with an ANAME corresponding to an empty string,
    // that ANAME ends up being the only histfrog, and thus we have
    // a situation where histfrogp > 0 (not a pop) but the path we find
    // is the empty string. Really that shouldn't happen, but it doesn't
    // seem to be bothering anyone yet, and it's easier to fix the condition
    // to test histfrogp than to track down where that empty string is
    // coming from. Probably it is coming from go tool pack's P command.
    if(histfrogp > 0) { // histfrogpが0より大きい場合に実行
        tmp[0] = '\0';
        copyhistfrog(tmp, sizeof tmp);
        file = lookup(tmp, HistVersion);
        if(file->type != SFILEPATH) {
            file->value = ++nhistfile;
        }
    }
    

この変更により、savehist関数はhistfrogpが0より大きい場合にのみcopyhistfrogを呼び出し、その結果を処理するようになりました。これにより、履歴スタックが実際に空でない限り、空文字列のANAMEによって誤ってポップと判断されることがなくなります。これは、リンカの内部状態の整合性を保ち、特にWindows環境でのビルドの信頼性を向上させるための重要な修正です。

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

変更はsrc/cmd/ld/lib.cファイルのsavehist関数内で行われています。

--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -2112,10 +2112,20 @@ savehist(int32 line, int32 off)
 	Sym *file;
 	Hist *h;
 
-	tmp[0] = '\0';
-	copyhistfrog(tmp, sizeof tmp);
-
-	if(tmp[0]) {
+	// NOTE(rsc): We used to do the copyhistfrog first and this
+	// condition was if(tmp[0] != '\0') to check for an empty string,
+	// implying that histfrogp == 0, implying that this is a history pop.
+	// However, on Windows in the misc/cgo test, the linker is
+	// presented with an ANAME corresponding to an empty string,
+	// that ANAME ends up being the only histfrog, and thus we have
+	// a situation where histfrogp > 0 (not a pop) but the path we find
+	// is the empty string. Really that shouldn't happen, but it doesn't
+	// seem to be bothering anyone yet, and it's easier to fix the condition
+	// to test histfrogp than to track down where that empty string is
+	// coming from. Probably it is coming from go tool pack's P command.
+	if(histfrogp > 0) {
+		tmp[0] = '\0';
+		copyhistfrog(tmp, sizeof tmp);
 		file = lookup(tmp, HistVersion);
 		if(file->type != SFILEPATH) {
 			file->value = ++nhistfile;

コアとなるコードの解説

変更の核心は、savehist関数内の条件分岐の変更です。

  • 変更前: if(tmp[0])
    • これは、copyhistfrogによってtmpバッファにコピーされた文字列の最初の文字がヌル文字でないかどうかをチェックしていました。もしヌル文字であれば、文字列は空であると判断され、履歴スタックがポップされた(または空である)と見なされていました。
  • 変更後: if(histfrogp > 0)
    • この新しい条件は、histfrogpというグローバル変数(またはリンカの内部状態変数)が0より大きいかどうかを直接チェックします。histfrogpは履歴スタックに現在積まれている要素の数を示します。したがって、histfrogp > 0は、履歴スタックが空ではないことを意味します。

この変更により、savehist関数は、実際に履歴スタックに要素が存在する場合にのみ、ファイルパスのルックアップと処理を行うようになりました。これにより、Windows環境で発生していた、histfrogpが0より大きいにもかかわらずcopyhistfrogが空文字列を返すというエッジケースが適切に処理され、リンカの誤動作が修正されました。

コメントで述べられているように、この修正は根本的な原因(なぜ空文字列のANAMEが生成されるのか)を追跡するよりも、症状を直接修正するアプローチを取っています。これは、開発の効率性と、問題の緊急性を考慮した実用的な判断と言えます。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード(特にsrc/cmd/ld/ディレクトリ)
  • Go言語のIssueトラッカー
  • Go言語のコードレビューシステム (Gerrit)
  • Go言語のリンカに関する一般的なドキュメントや解説記事 (必要に応じてWeb検索)
    • 例: "Go linker internals", "Go toolchain cmd/ld" などのキーワードで検索。