[インデックス 16415] ファイルの概要
このコミットは、Go言語のランタイムにおけるARMアーキテクチャ向けのシグナルハンドラに関する変更です。具体的には、以下の3つのファイルが変更されています。
src/pkg/runtime/sys_freebsd_arm.s
src/pkg/runtime/sys_linux_arm.s
src/pkg/runtime/sys_netbsd_arm.s
これらのファイルは、それぞれFreeBSD、Linux、NetBSDという異なるOS上でのARMアーキテクチャにおけるGoランタイムのシステムコールやシグナル処理に関連するアセンブリコードを含んでいます。
コミット
commit ae73b903696981ae3c4da203a3ea4e083e401b46
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Mon May 27 20:46:53 2013 +0800
runtime: make arm signal handler call runtime.badsignal
In preparation for CL 9249043 (already submitted).
Fixes #5553.
R=golang-dev, iant, capnm9, dave
CC=golang-dev
https://golang.org/cl/9251043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ae73b903696981ae3c4da203a3ea4e083e401b46
元コミット内容
runtime: make arm signal handler call runtime.badsignal
In preparation for CL 9249043 (already submitted).
Fixes #5553.
変更の背景
このコミットは、GoランタイムのARMアーキテクチャにおけるシグナルハンドラの堅牢性を向上させることを目的としています。特に、シグナルがGoランタイムが予期しない状況(例えば、m
、すなわちOSスレッドを表す構造体が設定されていない状態)で発生した場合の挙動を改善します。
コミットメッセージには「In preparation for CL 9249043 (already submitted)」とあり、これはこの変更が別の大きな変更(CL 9249043)のための準備作業であることを示唆しています。また、「Fixes #5553」とあることから、GoのIssueトラッカーで報告されていた特定のバグや問題(Issue 5553)を修正するものであることがわかります。
Goのシグナルハンドリングは、プログラムの実行中に発生する様々なイベント(例えば、不正なメモリアクセス、ゼロ除算、外部からの終了シグナルなど)を処理するために非常に重要です。特に、GoプログラムがC言語などの外部コード(cgo経由)と連携する場合、シグナル処理の複雑性が増し、予期せぬ状態でのシグナル発生が問題を引き起こす可能性があります。runtime.badsignal
は、このような「不正なシグナル」の状態を適切に処理するためのメカニズムであり、このコミットはARM環境でのその呼び出しを保証することで、ランタイムの安定性を高めています。
前提知識の解説
Goランタイムとシグナルハンドリング
Goプログラムは、Goランタイムと呼ばれる独自の実行環境上で動作します。Goランタイムは、ガベージコレクション、スケジューリング、そしてシグナルハンドリングなど、多くの低レベルなタスクを管理します。
- シグナル (Signal): オペレーティングシステムがプロセスに送信する非同期イベントです。例えば、
SIGSEGV
(セグメンテーション違反)、SIGBUS
(バスエラー)、SIGFPE
(浮動小数点例外)などの同期シグナルは、プログラムの実行エラーによって発生します。SIGINT
(割り込み)やSIGTERM
(終了)などの非同期シグナルは、外部からのイベントによって発生します。 - シグナルハンドラ (Signal Handler): シグナルがプロセスに送信されたときに実行される特別な関数です。Goランタイムは、これらのシグナルを捕捉し、Goのパニックに変換したり、適切に処理したりします。
sigtramp
: シグナルハンドラのエントリポイントとなるアセンブリコードのルーチンです。OSからシグナルを受け取ると、このsigtramp
が呼び出され、Goランタイムのシグナル処理ロジックに制御が渡されます。g
(goroutine): Go言語の軽量スレッドです。Goランタイムによってスケジューリングされます。m
(machine/OS thread): オペレーティングシステムのスレッドです。Goランタイムは、複数のg
を少数のm
上で実行します。_cgo_load_gm
: cgo(GoとC言語の相互運用機能)に関連する関数で、現在のOSスレッド(m
)とゴルーチン(g
)の情報をロードするために使用されます。シグナルハンドラがCコードのコンテキストで呼び出された場合など、g
やm
が適切に設定されていない可能性があります。runtime.badsignal
: Goランタイム内部の関数で、予期せぬ、または不正なシグナルが受信された場合に呼び出されます。これは通常、ランタイムが回復できない状態にあることを示し、プログラムの異常終了やデバッグ情報の出力に繋がります。
ARMアセンブリの基本
このコミットで変更されているファイルはARMアセンブリ言語で書かれています。主要な命令は以下の通りです。
MOVW R0, 4(R13)
: レジスタR0
の値を、スタックポインタR13
から4バイトオフセットしたメモリ位置にストアします。R13
は通常、スタックポインタ(SP)として使用されます。MOVW _cgo_load_gm(SB), R0
: シンボル_cgo_load_gm
のアドレスをレジスタR0
にロードします。SB
はStatic Baseレジスタを示し、グローバルシンボルへの参照に使用されます。CMP $0, R0
: レジスタR0
の値と即値0
を比較します。この比較の結果は、次の条件分岐命令に影響を与えます。BL.NE (R0)
:R0
の値が0
と等しくない(Not Equal)場合に、R0
が指すアドレスに分岐し、そのアドレスの関数を呼び出します(Branch with Link, Not Equal)。BL
は関数呼び出しのためにリターンアドレスをリンクレジスタ(LR)に保存します。CMP $0, m
:m
(OSスレッドを表すランタイムのグローバル変数)の値と即値0
を比較します。BNE 3(PC)
: プログラムカウンタ(PC)から3命令分先に分岐します。BNE
は比較結果がNot Equalの場合に分岐します。BL runtime·badsignal(SB)
:runtime.badsignal
関数を呼び出します。RET
: 関数からリターンします。
技術的詳細
このコミットの技術的な核心は、ARMアーキテクチャのシグナルハンドラ(runtime·sigtramp
)内で、m
(現在のOSスレッドを表すランタイムの構造体)がnil
(または0
)である場合にruntime.badsignal
を呼び出すロジックを追加することです。
Goランタイムのシグナルハンドラは、シグナルがGoコードのコンテキストで発生したか、それともCコードのコンテキストで発生したかによって異なる挙動をします。_cgo_load_gm
は、現在のコンテキストにおけるg
とm
の情報をロードしようとします。しかし、シグナルがGoランタイムが管理していない外部コードのコンテキストで発生した場合、m
が適切に設定されていない(m=0
)可能性があります。
従来のコードでは、_cgo_load_gm
の呼び出し後にm
が0
であった場合の明示的な処理が不足していました。コメントアウトされた行「// TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
」が示すように、他のプラットフォームでは既にm=0
の場合にruntime.badsignal
を呼び出すロジックが存在しており、ARMでも同様の処理が必要とされていました。
runtime.badsignal
を呼び出すことで、ランタイムは不正な状態でのシグナル発生を検出し、適切なエラー処理やデバッグ情報の収集を行うことができます。これにより、シグナル処理の堅牢性が向上し、特にcgoを使用するGoプログラムにおいて、予期せぬクラッシュやデバッグの困難さを軽減することが期待されます。
追加されたアセンブリコードは、_cgo_load_gm
が呼び出された後、m
レジスタ(またはm
が格納されているレジスタ)の値を0
と比較し、もし0
であればruntime.badsignal
を呼び出してリターンするというシンプルなロジックを実装しています。これにより、m
が有効な状態でない場合に、それ以降のGoランタイムのシグナル処理ロジックに進むことを防ぎ、badsignal
による適切なエラーパスに誘導します。
コアとなるコードの変更箇所
変更は、以下の3つのファイルのruntime·sigtramp
関数内にあります。
src/pkg/runtime/sys_freebsd_arm.s
--- a/src/pkg/runtime/sys_freebsd_arm.s
+++ b/src/pkg/runtime/sys_freebsd_arm.s
@@ -154,12 +154,17 @@ TEXT runtime·sigtramp(SB),7,$24
// this might be called in external code context,
// where g and m are not set.
// first save R0, because _cgo_load_gm will clobber it
- // TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
MOVW R0, 4(R13) // signum
MOVW _cgo_load_gm(SB), R0
CMP $0, R0
BL.NE (R0)
+ CMP $0, m
+ BNE 3(PC)
+ // signal number is already prepared in 4(R13)
+ BL runtime·badsignal(SB)
+ RET
+
// save g
MOVW R10, R4
MOVW R10, 20(R13)
src/pkg/runtime/sys_linux_arm.s
--- a/src/pkg/runtime/sys_linux_arm.s
+++ b/src/pkg/runtime/sys_linux_arm.s
@@ -286,12 +286,17 @@ TEXT runtime·sigtramp(SB),7,$24
// this might be called in external code context,
// where g and m are not set.
// first save R0, because _cgo_load_gm will clobber it
- // TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
MOVW R0, 4(R13)
MOVW _cgo_load_gm(SB), R0
CMP $0, R0
BL.NE (R0)
+ CMP $0, m
+ BNE 3(PC)
+ // signal number is already prepared in 4(R13)
+ BL runtime·badsignal(SB)
+ RET
+
// save g
MOVW g, R3
MOVW g, 20(R13)
src/pkg/runtime/sys_netbsd_arm.s
--- a/src/pkg/runtime/sys_netbsd_arm.s
+++ b/src/pkg/runtime/sys_netbsd_arm.s
@@ -201,12 +201,17 @@ TEXT runtime·sigtramp(SB),7,$24
// this might be called in external code context,
// where g and m are not set.
// first save R0, because _cgo_load_gm will clobber it
- // TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
MOVW R0, 4(R13) // signum
MOVW _cgo_load_gm(SB), R0
CMP $0, R0
BL.NE (R0)
+ CMP $0, m
+ BNE 3(PC)
+ // signal number is already prepared in 4(R13)
+ BL runtime·badsignal(SB)
+ RET
+
// save g
MOVW R10, R4
MOVW R10, 20(R13)
コアとなるコードの解説
各ファイルで追加されたアセンブリコードは、ほぼ同一のロジックを持っています。
CMP $0, m
BNE 3(PC)
// signal number is already prepared in 4(R13)
BL runtime·badsignal(SB)
RET
CMP $0, m
:- これは、現在のOSスレッド(
m
)が有効な状態であるかどうかをチェックします。m
が0
である場合、それはGoランタイムが管理する有効なOSスレッドのコンテキストではないことを意味します。
- これは、現在のOSスレッド(
BNE 3(PC)
:CMP
命令の結果、m
が0
と等しくない(つまり、m
が有効な状態である)場合、プログラムカウンタ(PC)から3命令分先に分岐します。これにより、新しく追加されたruntime.badsignal
の呼び出しとRET
命令をスキップし、既存のシグナルハンドラロジックの残りの部分に進みます。
// signal number is already prepared in 4(R13)
:- これはコメントであり、
runtime.badsignal
を呼び出す前に、シグナル番号が既にスタック上の4(R13)
に保存されていることを示しています。runtime.badsignal
はこの情報を使用して、適切なエラー処理を行うことができます。
- これはコメントであり、
BL runtime·badsignal(SB)
:m
が0
であった場合(つまり、BNE
命令がスキップされた場合)、この命令が実行されます。runtime.badsignal
関数を呼び出します。これは、Goランタイムが予期しないシグナル状態を検出したことを示し、通常はプログラムの異常終了やデバッグ情報の出力に繋がります。
RET
:runtime.badsignal
が呼び出された後、シグナルハンドラからリターンします。これは、m
が0
である不正な状態では、それ以上シグナルハンドラの通常の処理を続行しないことを意味します。
この変更により、ARMアーキテクチャのGoランタイムは、m
が設定されていないコンテキストでシグナルを受信した場合に、より堅牢にruntime.badsignal
を呼び出すようになり、ランタイムの安定性とデバッグ可能性が向上します。
関連リンク
- Go Issue 5553: https://code.google.com/p/go/issues/detail?id=5553
- Go CL 9251043: https://golang.org/cl/9251043
参考にした情報源リンク
- Goのシグナルハンドリングに関する一般的な情報:
- Goランタイムのシグナル処理と
badsignal
に関する議論: - ARMアセンブリの基本(一般的な情報源)
- Goの
m
とg
の概念に関する情報: _cgo_load_gm
に関する情報:- Goのcgo関連のソースコード(例:
go/src/runtime/cgo/cgo.go
やgo/src/runtime/cgo/gcc_linux_arm.c
など)を参照することで、その役割を理解できます。
- Goのcgo関連のソースコード(例:
- CL 9249043に関する情報は見つかりませんでした。