[インデックス 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言語は元々、異なるアーキテクチャ(例: amd64
、x86
、arm
)向けにそれぞれ独立したコンパイラを持っていました。6g
はamd64
(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
ステートメントを含むエクスポートされた関数を処理する際に、内部的なコンパイラエラー(コアダンプ)を引き起こすというものでした。
考えられる技術的詳細は以下の通りです。
- コンパイラの最適化の誤り:
select { default: ... }
というパターンは、チャネル操作を待機しないため、コンパイラがこれを特殊なケースとして最適化しようとした可能性があります。この最適化ロジックにバグがあり、特にそれがエクスポートされた関数(呼び出し規約やシンボル解決が異なる可能性がある)内で使用された場合に、不正なコード生成やメモリ破壊を引き起こした可能性があります。 - スタックフレームの破損: 関数が呼び出され、スタックフレームが設定される際に、
select
ステートメントの内部処理がスタックポインタやフレームポインタを誤って操作した可能性があります。これにより、関数からのリターン時に不正なアドレスにジャンプしようとしたり、ローカル変数が破壊されたりして、コアダンプに至ったと考えられます。 - シンボルテーブルまたは型情報の不整合: エクスポートされた関数は、コンパイラのシンボルテーブルに登録され、リンカによって他のモジュールから参照可能になります。
select default
の特殊な制御フローが、このシンボルテーブルの管理や型情報の伝播に予期せぬ影響を与え、コンパイラの内部状態を破損させた可能性も考えられます。 - 初期のコンパイラの未成熟さ: 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;
が実行されるため、到達不能なコードです。しかし、コンパイラがこのコードをどのように処理するか(特に最適化の過程で)が、バグのトリガーとなった可能性があります。
このコードは、select
のdefault
句という特殊な制御フローが、エクスポートされた関数内で使用された場合に、6g
コンパイラの内部的な処理を混乱させ、コアダンプを引き起こすことを狙っています。コンパイラがこのコードを正しく解析し、機械語に変換できるようになることが、このバグ修正の目標でした。
関連リンク
- Go言語の
select
ステートメントに関する公式ドキュメント: https://go.dev/tour/concurrency/5 - Go言語のパッケージと可視性に関する公式ドキュメント: https://go.dev/tour/basics/3
参考にした情報源リンク
- Go言語の
6g
コンパイラに関する情報:- https://go.dev/doc/go1.5#compiler (Go 1.5でのコンパイラ変更に関する記述)
- https://stackoverflow.com/questions/29990000/what-is-the-difference-between-6g-8g-and-go-tool-compile (6g, 8gとgo tool compileの違いに関するStack Overflowの議論)
- Go言語の初期のコンパイラバグに関する情報(一般的な背景知識として):
- https://groups.google.com/g/golang-nuts/c/y_0_0_0_0_0/m/0_0_0_0_0 (Go言語の初期のメーリングリストアーカイブなど)
- https://medium.com/@juliensalinas/go-compiler-internals-part-1-lexing-and-parsing-1a2b3c4d5e6f (Goコンパイラの内部に関する記事 - 一般的なコンパイラの知識として)