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

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

このコミットは、Goコンパイラの5c(Plan 9 CコンパイラをベースとしたGoの初期のコンパイラの一つ)におけるバグ修正に関するものです。具体的には、AFUNCDATA(実際にはFUNCDATAの誤記である可能性が高い)が存在する場合のプログラムカウンタ(PC)の再計算ロジックの不具合を修正し、それによってARMアーキテクチャでのビルドが壊れる問題(CL 11683043によって引き起こされたもの)を解決しています。

コミット

commit de6da4a8bdce935ba71f585bb458272cc781dd9a
Author: Keith Randall <khr@golang.org>
Date:   Thu Jul 25 17:40:29 2013 -0700

    5c: fix pc recalculation in the presence of AFUNCDATA
    
    This makes CL 11683043 not break arm anymore.
    
    R=golang-dev, bradfitz, dave
    CC=golang-dev
    https://golang.org/cl/11888043

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

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

元コミット内容

diff --git a/src/cmd/5c/reg.c b/src/cmd/5c/reg.c
index 030b0c327e..25bfc58222 100644
--- a/src/cmd/5c/reg.c
+++ b/src/cmd/5c/reg.c
@@ -461,6 +461,7 @@ brk:
 			case AGLOBL:
 			case ANAME:
 			case ASIGNAME:
+			case AFUNCDATA:
 				break;
 			}
 		}

変更の背景

このコミットは、Goコンパイラの5cが生成するコードにおいて、AFUNCDATA(おそらくFUNCDATAの誤記)という特殊なデータが存在する場合に、プログラムカウンタ(PC)の再計算が正しく行われないというバグを修正するために導入されました。このバグは、以前の変更(CL 11683043)によってARMアーキテクチャ上でGoのビルドを壊すという具体的な問題を引き起こしていました。

Goのコンパイラとランタイムは、デバッグ情報、プロファイリング、ガベージコレクション(GC)などの目的で、実行中のコードのPC値とソースコード上の位置を正確にマッピングする必要があります。このマッピングが正しく行われないと、デバッガが誤った行番号を表示したり、プロファイラが不正確なパフォーマンスデータを提供したり、最悪の場合、GCがポインタを正しく識別できずにメモリ破損やクラッシュを引き起こす可能性があります。

特にARMのような特定のアーキテクチャで問題が発生したということは、PCの計算ロジックがアーキテクチャ固有の特性(例えば、命令セットの長さやアラインメントなど)に依存しているか、またはAFUNCDATAの配置がARMのコード生成に特有の影響を与えていた可能性を示唆しています。

前提知識の解説

1. Goコンパイラと5c

Go言語の初期のコンパイラは、Plan 9オペレーティングシステム由来のツールチェインをベースにしていました。5cはその一つで、ARMアーキテクチャ向けのC言語コンパイラを指します(5はARMのPlan 9におけるアーキテクチャコード)。Go 1.5以降、Goコンパイラ自体がGo言語で再実装され、5cのようなPlan 9由来のツールへの依存は大幅に減少しましたが、このコミットが作成された2013年時点では、まだGoのビルドプロセスにおいて重要な役割を担っていました。

2. プログラムカウンタ(PC)

プログラムカウンタ(Program Counter, PC)は、CPUが次に実行する命令のアドレスを保持するレジスタです。Goのランタイムやデバッグツールは、実行中のPC値から、それがどの関数、どのファイル、どの行のコードに対応するかを特定します。これは、スタックトレースの生成、デバッガでのステップ実行、プロファイリングなど、多くの低レベルな操作で不可欠です。

3. PCの再計算(PC Recalculation)

コンパイラは、ソースコードを機械語に変換する際に、様々な最適化を適用します。例えば、関数インライン化(呼び出し元のコードに呼び出される関数の本体を直接埋め込む)や、デッドコードの削除、命令の並べ替えなどです。これらの最適化は、元のソースコードの構造と生成された機械語のPC値との間に複雑な関係を生み出します。

「PCの再計算」とは、これらの最適化が適用された後でも、実行時のPC値から元のソースコードの正確な位置(ファイル名、行番号、関数名など)を導き出すプロセスを指します。これは、コンパイラが生成するデバッグ情報やメタデータ(例えば、GoにおけるPCDATAFUNCDATA)を利用して行われます。このプロセスが正しく機能しないと、デバッグやプロファイリングが困難になります。

4. FUNCDATA (Function Data)

FUNCDATAは、Goコンパイラが生成するバイナリに埋め込まれるメタデータの一種です。これは主にガベージコレクション(GC)のために使用されます。GoのGCは、スタックやヒープ上のポインタを正確に識別し、到達可能なオブジェクトをマークする必要があります。FUNCDATAは、特定の関数のスタックフレーム内でどの位置にポインタが存在するか(ポインタマップ)などの情報を提供し、GCが安全かつ効率的に動作することを可能にします。

コミットメッセージにあるAFUNCDATAは、おそらくFUNCDATAの誤記、またはFUNCDATAに関連する特定の内部的なデータ構造やアブストラクションを指していると考えられます。アセンブリ言語で書かれた関数では、開発者が明示的にFUNCDATAディレクティブを使用してこの情報を提供する必要があります。

5. CL (Change List)

Goプロジェクトでは、コード変更は「Change List (CL)」として管理されます。これは、Gerritというコードレビューシステムで使われる用語で、一連のコミットをまとめたものです。コミットメッセージに記載されているCL 11683043は、このコミットが修正しようとしている問題を引き起こした、以前の特定の変更セットを指します。

技術的詳細

このコミットは、src/cmd/5c/reg.cファイル内のbrk:ラベルが付いたコードブロックに、AFUNCDATAというケースを追加しています。このファイルは、5cコンパイラのレジスタ割り当てやコード生成に関連する部分であると推測されます。

brk:ラベルの周辺のコードは、おそらく特定の種類のオペランド(命令の引数)を処理するロジックの一部です。既存のケースには、AGLOBL(グローバル変数)、ANAME(名前付きエンティティ)、ASIGNAME(シグネチャ名)などがあります。これらはすべて、コンパイラがシンボルやデータ構造を扱う際に遭遇する可能性のあるオペランドの種類です。

AFUNCDATAがこのリストに追加されたということは、コンパイラがPCの再計算を行う際に、AFUNCDATAタイプのオペランドを特別に扱う必要があることを示唆しています。具体的には、AFUNCDATAがPCの計算に影響を与えるような形でコード内に存在する場合、その存在を認識し、PCのオフセットやアラインメントを適切に調整する必要があったと考えられます。

PCの再計算が壊れるシナリオとしては、以下のような可能性が考えられます。

  • 不正確なオフセット計算: AFUNCDATAがコードセクション内に挿入される際、そのサイズや配置がPCの計算ロジックに考慮されていなかったため、後続の命令のPC値がずれてしまった。
  • ポインタ情報の欠落/誤読: AFUNCDATAがGCのためのポインタ情報を含んでいる場合、PCの再計算ロジックがこのデータを正しく解釈できず、デバッグ情報やGCのメタデータとの整合性が失われた。
  • アーキテクチャ固有の挙動: ARMアーキテクチャでは、命令のエンコーディングやメモリのアラインメント要件が他のアーキテクチャと異なるため、AFUNCDATAの処理が特に問題を引き起こしやすかった。

この修正は、AFUNCDATAケースに対してbreak;文を実行するだけなので、これは「何もしない」ことを意味します。これは、AFUNCDATAがPCの再計算ロジックにおいて、特別な処理を必要とせず、単にスキップされるべきオペランドであることを示唆している可能性があります。つまり、以前はAFUNCDATAが未知のオペランドとして扱われ、それがPCの計算を狂わせていたが、この修正により、AFUNCDATAはPC計算に影響を与えない「安全な」要素として認識されるようになった、ということです。

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

変更はsrc/cmd/5c/reg.cファイルの一箇所のみです。

--- a/src/cmd/5c/reg.c
+++ b/src/cmd/5c/reg.c
@@ -461,6 +461,7 @@ brk:
 			case AGLOBL:
 			case ANAME:
 			case ASIGNAME:
+			case AFUNCDATA:
 				break;
 			}
 		}

コアとなるコードの解説

このコードスニペットは、C言語のswitch文の一部であり、brk:というラベルが付いたブロック内にあります。case文のリストは、Goコンパイラの5cが処理する様々な種類のオペランド(Aで始まる定数はおそらくenumで定義されたオペランドの種類を表す)を示しています。

追加された行 case AFUNCDATA: は、コンパイラがAFUNCDATAタイプのオペランドに遭遇した場合の処理を定義しています。その直後に続く break; は、このケースにマッチした場合に、現在のswitch文から抜け出すことを意味します。

この修正のポイントは、AFUNCDATAが既存のAGLOBLANAMEASIGNAMEと同様に、PCの再計算ロジックにおいて特別な処理を必要としない、あるいは無視しても安全なオペランドとして明示的に認識された点です。以前は、AFUNCDATAがこのswitch文のどのcaseにもマッチしなかったため、おそらくデフォルトの処理(またはフォールスルー)が実行され、それがPCの計算を誤らせていたと考えられます。この修正により、AFUNCDATAが正しく識別され、PCの計算に悪影響を与えないように処理されるようになりました。

関連リンク

参考にした情報源リンク