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

[インデックス 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コードのコンテキストで呼び出された場合など、gmが適切に設定されていない可能性があります。
  • 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は、現在のコンテキストにおけるgmの情報をロードしようとします。しかし、シグナルがGoランタイムが管理していない外部コードのコンテキストで発生した場合、mが適切に設定されていない(m=0)可能性があります。

従来のコードでは、_cgo_load_gmの呼び出し後にm0であった場合の明示的な処理が不足していました。コメントアウトされた行「// 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
  1. CMP $0, m:
    • これは、現在のOSスレッド(m)が有効な状態であるかどうかをチェックします。m0である場合、それはGoランタイムが管理する有効なOSスレッドのコンテキストではないことを意味します。
  2. BNE 3(PC):
    • CMP命令の結果、m0と等しくない(つまり、mが有効な状態である)場合、プログラムカウンタ(PC)から3命令分先に分岐します。これにより、新しく追加されたruntime.badsignalの呼び出しとRET命令をスキップし、既存のシグナルハンドラロジックの残りの部分に進みます。
  3. // signal number is already prepared in 4(R13):
    • これはコメントであり、runtime.badsignalを呼び出す前に、シグナル番号が既にスタック上の4(R13)に保存されていることを示しています。runtime.badsignalはこの情報を使用して、適切なエラー処理を行うことができます。
  4. BL runtime·badsignal(SB):
    • m0であった場合(つまり、BNE命令がスキップされた場合)、この命令が実行されます。runtime.badsignal関数を呼び出します。これは、Goランタイムが予期しないシグナル状態を検出したことを示し、通常はプログラムの異常終了やデバッグ情報の出力に繋がります。
  5. RET:
    • runtime.badsignalが呼び出された後、シグナルハンドラからリターンします。これは、m0である不正な状態では、それ以上シグナルハンドラの通常の処理を続行しないことを意味します。

この変更により、ARMアーキテクチャのGoランタイムは、mが設定されていないコンテキストでシグナルを受信した場合に、より堅牢にruntime.badsignalを呼び出すようになり、ランタイムの安定性とデバッグ可能性が向上します。

関連リンク

参考にした情報源リンク