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

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

このコミットは、Go言語の初期のコンパイラである6gが特定の入力ファイルでコアダンプ(プログラムの異常終了)を起こすバグを修正するために、そのバグを再現するテストケースを追加したものです。具体的には、selectステートメントのdefaultケースを含むエクスポートされた関数が、6gコンパイラでクラッシュを引き起こす問題に対処しています。

コミット

commit 48d111f0b929f3c22d27ea640c39bf8e5ea1df7b
Author: Ian Lance Taylor <iant@golang.org>
Date:   Wed Nov 5 15:27:40 2008 -0800

    6g dumps core on this input file.
    
    R=ken,rsc
    DELTA=14  (14 added, 0 deleted, 0 changed)
    OCL=18555
    CL=18598
---
 test/bugs/bug118.go | 15 +++++++++++++++
 test/golden.out     |  3 +++
 2 files changed, 18 insertions(+)

diff --git a/test/bugs/bug118.go b/test/bugs/bug118.go
new file mode 100644
index 0000000000..778b533c76
--- /dev/null
+++ b/test/bugs/bug118.go
@@ -0,0 +1,15 @@
+// $G $D/$F.go || echo BUG should compile
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+export func Send(c *chan int) int {
+  select {
+  default:
+    return 1;
+  }
+  return 2;
+}
diff --git a/test/golden.out b/test/golden.out
index 4bea55fb13..640267d301 100644
--- a/test/golden.out
+++ b/test/golden.out
@@ -150,6 +150,9 @@ bugs/bug117.go:10: illegal types for operand: RETURN
 	int
 BUG: should compile
 
+=========== bugs/bug118.go
+BUG should compile
+
 =========== fixedbugs/bug016.go
 fixedbugs/bug016.go:7: overflow converting constant to uint
 

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

https://github.com/golang/go/commit/48d111f0b929f3c22d27ea640c39bf8e5ea1df7b

元コミット内容

6gがこの入力ファイルでコアダンプする。

変更の背景

このコミットは、Go言語の初期開発段階におけるコンパイラの安定性に関する重要な問題に対処しています。当時のGoコンパイラの一つである6g(Go 1.5以前のamd64アーキテクチャ向けコンパイラ)が、特定のGoソースコードをコンパイルする際に異常終了(コアダンプ)するというバグが存在しました。

コアダンプは、プログラムが不正なメモリアクセスやその他の致命的なエラーに遭遇した際に発生する現象で、デバッグ情報を記録したコアファイルが生成されます。これはコンパイラ自体のバグであり、開発者がGoプログラムを記述・コンパイルする上で大きな障害となります。

このコミットの目的は、この6gコンパイラのクラッシュを引き起こす具体的なコードパターンを特定し、それを再現するテストケース(test/bugs/bug118.go)を追加することです。テストケースを追加することで、バグが修正されたことを確認し、将来の回帰を防ぐことができます。この問題は、selectステートメントのdefaultケースと、外部に公開される(exportされた)関数の組み合わせによって引き起こされていました。

前提知識の解説

6g コンパイラ

6gは、Go言語の初期バージョン(特にGo 1.5より前)で使用されていたコンパイラの一つです。Go言語は元々、異なるアーキテクチャ(例: amd64x86arm)向けにそれぞれ独立したコンパイラを持っていました。6gamd64(64ビットIntel/AMDプロセッサ)アーキテクチャ向けのコンパイラを指します。Go 1.5以降、これらの個別のコンパイラ(6g, 8g, 6a, 8a, 6l, 8lなど)はgo tool compileコマンドに統合され、通常はgo buildコマンドを通じて間接的に呼び出されるようになりました。このコミットが作成された2008年時点では、6gはGo開発において中心的な役割を担っていました。

Go言語の select ステートメント

selectステートメントは、Go言語における並行処理の強力な機能の一つです。複数の通信操作(チャネルの送受信)を同時に待機し、準備ができた最初の操作を実行するために使用されます。

selectの基本的な構文は以下の通りです。

select {
case <-ch1:
    // ch1からの受信
case ch2 <- value:
    // ch2への送信
default:
    // どのチャネル操作も準備ができていない場合に実行
}
  • case: チャネルの送受信操作を指定します。複数のcase句がある場合、Goランタイムは準備ができた操作をランダムに選択して実行します。
  • default: オプションの句で、selectブロック内のどのcase句もすぐに実行できない場合にのみ実行されます。default句が存在する場合、selectステートメントはブロックせず、すぐに実行可能なcaseがない場合はdefault句が実行されます。default句がない場合、selectはチャネル操作が準備できるまでブロックします。

このコミットで問題となったのは、default句のみを持つselectステートメントです。このようなselectは、常にdefault句が実行されるため、チャネル操作を待機するという通常のselectの目的とは異なり、即座に実行されるコードブロックとして機能します。

Go言語の export (エクスポート)

Go言語では、識別子(変数、関数、型など)の可視性(スコープ)は、その名前の最初の文字が大文字か小文字かによって決まります。

  • 大文字で始まる識別子: パッケージ外からアクセス可能です。これを「エクスポートされた(exported)」識別子と呼びます。
  • 小文字で始まる識別子: その識別子が定義されているパッケージ内でのみアクセス可能です。

このコミットのコードでは、export func Send(...)と記述されていますが、これはGo言語の通常の構文ではありません。Go言語では、関数名(例: Send)を大文字で始めることで自動的にエクスポートされます。exportキーワードはC言語との連携(cgo)などで使用される特殊な文脈で使われることがありますが、通常のGoコードでは関数をエクスポートするために明示的にexportキーワードを記述することはありません。このコミットが作成された初期のGo言語の文法では、このような記述が許容されていたか、あるいはテスト目的で特定のコンパイラの挙動を検証するために意図的に記述された可能性があります。いずれにせよ、Send関数が外部から呼び出し可能であることを意図している点に注目することが重要です。

技術的詳細

このバグは、6gコンパイラが、default句のみを持つselectステートメントを含むエクスポートされた関数を処理する際に、内部的なコンパイラエラー(コアダンプ)を引き起こすというものでした。

考えられる技術的詳細は以下の通りです。

  1. コンパイラの最適化の誤り: select { default: ... }というパターンは、チャネル操作を待機しないため、コンパイラがこれを特殊なケースとして最適化しようとした可能性があります。この最適化ロジックにバグがあり、特にそれがエクスポートされた関数(呼び出し規約やシンボル解決が異なる可能性がある)内で使用された場合に、不正なコード生成やメモリ破壊を引き起こした可能性があります。
  2. スタックフレームの破損: 関数が呼び出され、スタックフレームが設定される際に、selectステートメントの内部処理がスタックポインタやフレームポインタを誤って操作した可能性があります。これにより、関数からのリターン時に不正なアドレスにジャンプしようとしたり、ローカル変数が破壊されたりして、コアダンプに至ったと考えられます。
  3. シンボルテーブルまたは型情報の不整合: エクスポートされた関数は、コンパイラのシンボルテーブルに登録され、リンカによって他のモジュールから参照可能になります。select defaultの特殊な制御フローが、このシンボルテーブルの管理や型情報の伝播に予期せぬ影響を与え、コンパイラの内部状態を破損させた可能性も考えられます。
  4. 初期のコンパイラの未成熟さ: Go言語は当時まだ非常に初期の段階であり、コンパイラも活発に開発・改善されていました。このような複雑な制御フロー(select)と可視性(export)の組み合わせは、初期のコンパイラが完全に網羅できていなかったエッジケースであった可能性が高いです。

このバグは、コンパイラがGoソースコードを機械語に変換する過程で、特定の構文パターン(select default)と関数の属性(export)の組み合わせを正しく処理できなかったことに起因します。コアダンプは、コンパイラ自身が実行中に致命的なエラーに遭遇したことを意味し、これはコンパイラの堅牢性にとって重要な問題でした。

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

このコミットで追加された主要なファイルは test/bugs/bug118.go です。

// $G $D/$F.go || echo BUG should compile

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be LICENSE file.

package main

export func Send(c *chan int) int {
  select {
  default:
    return 1;
  }
  return 2;
}

また、テストの期待出力を定義する test/golden.out にも以下の行が追加されています。

=========== bugs/bug118.go
BUG should compile

これは、bug118.goがコンパイルに成功することを期待していることを示しています。もしコンパイルが失敗した場合、それはバグがまだ存在することを示します。

コアとなるコードの解説

test/bugs/bug118.go は、6gコンパイラのコアダンプバグを再現するために特別に作成されたGoプログラムです。

  • // $G $D/$F.go || echo BUG should compile: これはGoのテストスクリプトで使われる特殊なコメントで、このファイルが6gコンパイラ($G)でコンパイルされるべきであり、もしコンパイルが失敗した場合は「BUG should compile」というメッセージを出力すべきであることを示しています。これは、このファイルがコンパイルエラーではなく、コンパイラのクラッシュを引き起こすことを意図しているため、コンパイルが成功することが「修正」の条件であることを示唆しています。
  • package main: 実行可能なプログラムであることを示します。
  • export func Send(c *chan int) int:
    • export: 前述の通り、この関数が外部から呼び出し可能であることを示唆しています。Goの通常の構文では関数名を大文字で始めることでエクスポートされますが、初期のGoではこのような記述がテスト目的で使われた可能性があります。
    • func Send(c *chan int) int: Sendという名前の関数で、int型のチャネルへのポインタを受け取り、intを返すことを定義しています。
  • select { default: return 1; }:
    • これがバグの核心部分です。selectステートメント内にdefault句しかありません。
    • default句は、他のどのチャネル操作も準備ができていない場合に実行されます。この場合、他のcase句が存在しないため、selectステートメントは常に即座にdefault句を実行します。
    • return 1;: default句が実行された場合、関数は1を返して終了します。
  • return 2;: この行は、selectブロック内でreturn 1;が実行されるため、到達不能なコードです。しかし、コンパイラがこのコードをどのように処理するか(特に最適化の過程で)が、バグのトリガーとなった可能性があります。

このコードは、selectdefault句という特殊な制御フローが、エクスポートされた関数内で使用された場合に、6gコンパイラの内部的な処理を混乱させ、コアダンプを引き起こすことを狙っています。コンパイラがこのコードを正しく解析し、機械語に変換できるようになることが、このバグ修正の目標でした。

関連リンク

参考にした情報源リンク