[インデックス 13644] ファイルの概要
このコミットは、Go言語のランタイムにおけるNetBSD/386アーキテクチャでのスタックポインタの取り扱いに関するバグ修正です。具体的には、スタックポインタを操作する際に、ESP
レジスタの代わりにUESP
レジスタを使用するように変更することで、NetBSD/386上でのテストの失敗を修正しています。
コミット
commit 1b6557a0cf54a901db38b13b78f919b7959286ee
Author: Joel Sing <jsing@google.com>
Date: Fri Aug 17 21:53:02 2012 +1000
runtime: fix netbsd/386 stack pointer handling
When manipulating the stack pointer use the UESP register instead
of the ESP register, since the UESP register is the one that gets
restored from the machine context. Fixes broken tests on netbsd/386.
R=golang-dev, minux.ma, r, bsiegert
CC=golang-dev
https://golang.org/cl/6465054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1b6557a0cf54a901db38b13b78f919b7959286ee
元コミット内容
Goランタイムにおいて、NetBSD/386環境でのスタックポインタの処理を修正します。スタックポインタを操作する際には、ESP
レジスタではなくUESP
レジスタを使用します。これは、マシンコンテキストから復元されるのがUESP
レジスタであるためです。この修正により、NetBSD/386上での壊れたテストが修正されます。
変更の背景
この変更の背景には、GoランタイムがNetBSD/386環境で正しく動作しないという問題がありました。特に、シグナルハンドリングやコンテキストスイッチの際に、スタックポインタの扱いが適切でなかったことが原因と考えられます。x86アーキテクチャでは、スタックポインタとしてESP
(Extended Stack Pointer)レジスタが一般的に使用されますが、特定のOS(この場合はNetBSD)や特定の状況下では、ユーザーモードのスタックポインタを指す別のレジスタ(UESP
)が使用されることがあります。
Goランタイムは、ガベージコレクション、ゴルーチンのスケジューリング、シグナルハンドリングなど、低レベルのシステム操作を頻繁に行います。これらの操作では、現在の実行コンテキスト(レジスタの状態、スタックポインタなど)を保存し、必要に応じて復元する必要があります。NetBSD/386環境において、Goランタイムがスタックポインタの保存・復元にESP
レジスタを使用していたため、マシンコンテキストから復元されるUESP
レジスタとの間に不整合が生じ、結果としてテストが失敗するなどの問題が発生していました。
このコミットは、この不整合を解消し、NetBSD/386環境でのGoランタイムの安定性と正確性を確保することを目的としています。
前提知識の解説
x86アーキテクチャにおけるレジスタとスタック
x86アーキテクチャでは、CPUの内部に多数のレジスタが存在し、プログラムの実行状態を保持します。その中でも、スタック操作に密接に関わるのが以下のレジスタです。
- ESP (Extended Stack Pointer): 32ビットモードにおけるスタックポインタレジスタです。通常、スタックの最上位(現在のスタックフレームの先頭)を指します。
PUSH
命令やPOP
命令によって自動的に値が変更されます。 - EBP (Extended Base Pointer): 32ビットモードにおけるベースポインタレジスタです。通常、現在のスタックフレームの基底アドレスを指し、ローカル変数や関数引数へのアクセスに利用されます。
スタックは、プログラムが関数呼び出しやローカル変数の保存に使用するメモリ領域です。通常、スタックはメモリの高いアドレスから低いアドレスに向かって成長します。
シグナルハンドリングとコンテキストスイッチ
オペレーティングシステム(OS)は、プログラムの実行中に発生する様々なイベント(例えば、不正なメモリアクセス、タイマー割り込み、ユーザーからのCtrl+Cなど)を「シグナル」として扱います。シグナルが発生すると、OSは通常のプログラム実行を一時中断し、事前に登録された「シグナルハンドラ」と呼ばれる特別な関数を実行します。
シグナルハンドラが実行される際には、元のプログラムの実行コンテキスト(レジスタの値、スタックポインタ、プログラムカウンタなど)を保存し、シグナルハンドラが終了した後に元のコンテキストを復元する必要があります。このコンテキストの保存と復元は、OSのカーネルによって行われます。
NetBSD/386におけるUESP
レジスタ
一般的なx86システムではESP
がスタックポインタとして機能しますが、NetBSDのような一部のUNIX系OSでは、ユーザーモードのスタックポインタを明示的に区別するためにUESP
(User Extended Stack Pointer)という概念を使用することがあります。これは、カーネルモードとユーザーモードの切り替えや、シグナルハンドリングの際に、ユーザー空間のスタックポインタを正確に管理するために導入されることがあります。
特に、シグナルハンドラが呼び出される際、OSは現在のプロセスのレジスタ状態をucontext_t
構造体などのコンテキスト構造体に保存し、シグナルハンドラに渡します。このコンテキスト構造体には、ユーザーモードのスタックポインタとしてUESP
の値が格納されており、シグナルハンドラからの復帰時にはこのUESP
の値が使用されて元のコンテキストが復元されます。
Goランタイムのような低レベルのコードが、OSのシグナルハンドリング機構と連携する際には、OSが期待するレジスタ(この場合はUESP
)を正しく操作することが不可欠となります。
技術的詳細
このコミットは、src/pkg/runtime/signal_netbsd_386.c
ファイルに対する変更です。このファイルは、NetBSD/386アーキテクチャにおけるGoランタイムのシグナルハンドリングに関連するコードを含んでいます。
変更の核心は、スタックポインタを参照または操作する際に、mc->__gregs[REG_ESP]
の代わりにmc->__gregs[REG_UESP]
を使用するように修正した点です。ここで、mc
はMcontextT
型のポインタであり、シグナルハンドラに渡されるマシンコンテキスト(レジスタの状態など)を保持しています。__gregs
は汎用レジスタの配列であり、REG_ESP
やREG_UESP
はそれぞれのレジスタに対応するインデックスです。
具体的には、以下の箇所が変更されています。
-
runtime·dumpregs
関数: デバッグ目的でレジスタの値をダンプする関数です。ここでesp
レジスタの値を表示する際に、REG_ESP
ではなくREG_UESP
から値を取得するように変更されました。- runtime·printf("esp %x\\n", mc->__gregs[REG_ESP]); + runtime·printf("esp %x\\n", mc->__gregs[REG_UESP]);
この変更は、表示されるスタックポインタの値が、実際にOSが管理しているユーザーモードのスタックポインタと一致するようにするためのものです。
-
runtime·sighandler
関数: シグナルハンドラの本体です。SIGPROF
シグナル(プロファイリングシグナル)の処理において、スタックポインタをruntime·sigprof
関数に渡す際にREG_ESP
からREG_UESP
に変更されました。- \t\t\t(uint8*)mc->__gregs[REG_ESP], nil, gp);\ + \t\t\t(uint8*)mc->__gregs[REG_UESP], nil, gp);\
- シグナルによるパニック(
sigpanic
)が発生した場合のスタックポインタの操作において、sp
変数の初期化とmc->__gregs[REG_ESP]
への代入がmc->__gregs[REG_UESP]
に変更されました。
この部分は、シグナルハンドラ内でスタックを操作し、パニック発生時のスタックトレースを正しく生成するために、OSが復元するスタックポインタ(- \t\t\tsp = (uintptr*)mc->__gregs[REG_ESP]; + \t\t\tsp = (uintptr*)mc->__gregs[REG_UESP]; - \t\t\tmc->__gregs[REG_ESP] = (uintptr)sp; + \t\t\tmc->__gregs[REG_UESP] = (uintptr)sp;
UESP
)を正確に扱うことが重要であることを示しています。
-
Throw
ラベル内の処理: エラー発生時などにトレースバックを行う部分です。ここでも、トレースバックの開始点としてスタックポインタを渡す際にREG_ESP
からREG_UESP
に変更されました。- \t\t\t(void*)mc->__gregs[REG_ESP], 0, gp);\ + \t\t\t(void*)mc->__gregs[REG_UESP], 0, gp);\
これにより、トレースバックが正しいスタックフレームから開始されるようになります。
これらの変更は、NetBSD/386環境において、Goランタイムがシグナルハンドリング中にスタックポインタを操作する際に、OSが期待するUESP
レジスタを正しく使用するようにすることで、コンテキストの不整合を防ぎ、プログラムの安定した動作を保証します。
コアとなるコードの変更箇所
src/pkg/runtime/signal_netbsd_386.c
ファイルにおいて、mc->__gregs[REG_ESP]
の参照および代入がmc->__gregs[REG_UESP]
に変更されています。
--- a/src/pkg/runtime/signal_netbsd_386.c
+++ b/src/pkg/runtime/signal_netbsd_386.c
@@ -29,7 +29,7 @@ runtime·dumpregs(McontextT *mc)
runtime·printf("edi %x\\n", mc->__gregs[REG_EDI]);
runtime·printf("esi %x\\n", mc->__gregs[REG_ESI]);
runtime·printf("ebp %x\\n", mc->__gregs[REG_EBP]);
- runtime·printf("esp %x\\n", mc->__gregs[REG_ESP]);
+ runtime·printf("esp %x\\n", mc->__gregs[REG_UESP]);
runtime·printf("eip %x\\n", mc->__gregs[REG_EIP]);
runtime·printf("eflags %x\\n", mc->__gregs[REG_EFL]);
runtime·printf("cs %x\\n", mc->__gregs[REG_CS]);
@@ -47,7 +47,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
if(sig == SIGPROF) {
runtime·sigprof((uint8*)mc->__gregs[REG_EIP],
- (uint8*)mc->__gregs[REG_ESP], nil, gp);
+ (uint8*)mc->__gregs[REG_UESP], nil, gp);
return;
}
@@ -71,9 +71,9 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
// (Otherwise the trace will end at runtime·sigpanic
// and we won't get to see who faulted.)
if(mc->__gregs[REG_EIP] != 0) {
- sp = (uintptr*)mc->__gregs[REG_ESP];
+ sp = (uintptr*)mc->__gregs[REG_UESP];
*--sp = mc->__gregs[REG_EIP];
- mc->__gregs[REG_ESP] = (uintptr)sp;
+ mc->__gregs[REG_UESP] = (uintptr)sp;
}
mc->__gregs[REG_EIP] = (uintptr)runtime·sigpanic;
return;
@@ -100,7 +100,7 @@ Throw:
if(runtime·gotraceback()){
runtime·traceback((void*)mc->__gregs[REG_EIP],
- (void*)mc->__gregs[REG_ESP], 0, gp);
+ (void*)mc->__gregs[REG_UESP], 0, gp);
runtime·tracebackothers(gp);
runtime·dumpregs(mc);
}
コアとなるコードの解説
このコミットのコード変更は、GoランタイムがNetBSD/386環境でシグナルハンドリングを行う際の、スタックポインタの参照と操作に関する根本的な修正です。
Goランタイムは、独自のゴルーチンスタック管理とOSのシグナルハンドリング機構との連携を必要とします。シグナルが発生すると、OSは現在のCPUの状態(レジスタの値など)を保存し、シグナルハンドラを呼び出します。この際、OSはユーザーモードのスタックポインタとしてUESP
レジスタの値をコンテキスト情報に格納します。
Goランタイムのシグナルハンドラ(runtime·sighandler
)は、このOSから渡されたコンテキスト情報(McontextT *mc
)を使用して、現在のスタックの状態を把握し、必要に応じてスタックを操作します。
-
runtime·dumpregs
: デバッグ時にレジスタの値を表示する関数です。以前はESP
レジスタの値を表示していましたが、NetBSD/386ではUESP
が実際のユーザーモードスタックポインタであるため、UESP
の値を表示するように変更されました。これにより、デバッグ情報がより正確になります。 -
runtime·sighandler
内のSIGPROF
処理: プロファイリングシグナルが受信された際に、現在のプログラムカウンタ(REG_EIP
)とスタックポインタ(REG_UESP
)をruntime·sigprof
関数に渡します。これにより、プロファイリングが正しいスタックの状態を基に行われるようになります。 -
runtime·sighandler
内のパニック処理: シグナルによってパニックが発生した場合、Goランタイムはスタックを操作して、パニック発生時のスタックトレースを生成するための準備を行います。この操作において、スタックポインタの取得と設定の両方でUESP
レジスタを使用するように変更されました。これにより、パニック時のスタックトレースが、OSが認識している正しいスタックの状態を反映するようになります。もしESP
を使用し続けていた場合、OSが復元するUESP
とGoランタイムが操作するESP
との間に不整合が生じ、スタックトレースが壊れたり、さらなるクラッシュを引き起こす可能性がありました。 -
Throw
ラベル内のトレースバック処理: エラー発生時などに呼び出されるトレースバック関数(runtime·traceback
)にスタックポインタを渡す際も、UESP
レジスタを使用するように変更されました。これにより、トレースバックが正しいスタックフレームから開始され、正確なコールスタック情報が提供されます。
これらの変更は、NetBSD/386のOSカーネルがスタックポインタをUESP
レジスタとして管理・復元するという特性にGoランタイムを適合させるものです。これにより、GoプログラムがNetBSD/386上でシグナルを正しく処理し、安定して動作することが保証されます。
関連リンク
- Go言語のランタイムに関するドキュメントやソースコード
- NetBSDのシグナルハンドリングに関するドキュメント
- x86アーキテクチャのレジスタに関する情報
参考にした情報源リンク
- https://golang.org/cl/6465054 (Go Gerrit Code Review)
- x86アーキテクチャのレジスタに関する一般的な情報源 (例: Intel Software Developer's Manual)
- NetBSDのシステムコールやカーネルに関するドキュメント (例: NetBSD man pages)
- Go言語のソースコード (特に
src/runtime
ディレクトリ)
[インデックス 13644] ファイルの概要
このコミットは、Go言語のランタイムにおけるNetBSD/386アーキテクチャでのスタックポインタの取り扱いに関するバグ修正です。具体的には、スタックポインタを操作する際に、ESP
レジスタの代わりにUESP
レジスタを使用するように変更することで、NetBSD/386上でのテストの失敗を修正しています。
コミット
commit 1b6557a0cf54a901db38b13b78f919b7959286ee
Author: Joel Sing <jsing@google.com>
Date: Fri Aug 17 21:53:02 2012 +1000
runtime: fix netbsd/386 stack pointer handling
When manipulating the stack pointer use the UESP register instead
of the ESP register, since the UESP register is the one that gets
restored from the machine context. Fixes broken tests on netbsd/386.
R=golang-dev, minux.ma, r, bsiegert
CC=golang-dev
https://golang.org/cl/6465054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1b6557a0cf54a901db38b13b78f919b7959286ee
元コミット内容
Goランタイムにおいて、NetBSD/386環境でのスタックポインタの処理を修正します。スタックポインタを操作する際には、ESP
レジスタではなくUESP
レジスタを使用します。これは、マシンコンテキストから復元されるのがUESP
レジスタであるためです。この修正により、NetBSD/386上での壊れたテストが修正されます。
変更の背景
この変更の背景には、GoランタイムがNetBSD/386環境で正しく動作しないという問題がありました。特に、シグナルハンドリングやコンテキストスイッチの際に、スタックポインタの扱いが適切でなかったことが原因と考えられます。x86アーキテクチャでは、スタックポインタとしてESP
(Extended Stack Pointer)レジスタが一般的に使用されますが、特定のOS(この場合はNetBSD)や特定の状況下では、ユーザーモードのスタックポインタを指す別の概念であるUESP
が使用されることがあります。
Goランタイムは、ガベージコレクション、ゴルーチンのスケジューリング、シグナルハンドリングなど、低レベルのシステム操作を頻繁に行います。これらの操作では、現在の実行コンテキスト(レジスタの状態、スタックポインタなど)を保存し、必要に応じて復元する必要があります。NetBSD/386環境において、Goランタイムがスタックポインタの保存・復元にESP
レジスタの値を使用していたため、OSがマシンコンテキストから復元する際に使用するUESP
の値との間に不整合が生じ、結果としてテストが失敗するなどの問題が発生していました。
このコミットは、この不整合を解消し、NetBSD/386環境でのGoランタイムの安定性と正確性を確保することを目的としています。
前提知識の解説
x86アーキテクチャにおけるレジスタとスタック
x86アーキテクチャでは、CPUの内部に多数のレジスタが存在し、プログラムの実行状態を保持します。その中でも、スタック操作に密接に関わるのが以下のレジスタです。
- ESP (Extended Stack Pointer): 32ビットモードにおけるスタックポインタレジスタです。通常、スタックの最上位(現在のスタックフレームの先頭)を指します。
PUSH
命令やPOP
命令によって自動的に値が変更されます。 - EBP (Extended Base Pointer): 32ビットモードにおけるベースポインタレジスタです。通常、現在のスタックフレームの基底アドレスを指し、ローカル変数や関数引数へのアクセスに利用されます。
スタックは、プログラムが関数呼び出しやローカル変数の保存に使用するメモリ領域です。通常、スタックはメモリの高いアドレスから低いアドレスに向かって成長します。
シグナルハンドリングとコンテキストスイッチ
オペレーティングシステム(OS)は、プログラムの実行中に発生する様々なイベント(例えば、不正なメモリアクセス、タイマー割り込み、ユーザーからのCtrl+Cなど)を「シグナル」として扱います。シグナルが発生すると、OSは通常のプログラム実行を一時中断し、事前に登録された「シグナルハンドラ」と呼ばれる特別な関数を実行します。
シグナルハンドラが実行される際には、元のプログラムの実行コンテキスト(レジスタの値、スタックポインタ、プログラムカウンタなど)を保存し、シグナルハンドラが終了した後に元のコンテキストを復元する必要があります。このコンテキストの保存と復元は、OSのカーネルによって行われます。
NetBSD/386におけるUESP
レジスタ
一般的なx86システムではESP
がスタックポインタとして機能しますが、NetBSDのような一部のUNIX系OSでは、ユーザーモードのスタックポインタを明示的に区別するためにUESP
(User Extended Stack Pointer)という概念を使用することがあります。これは、i386プロセッサ自体に存在する独立したハードウェアレジスタではなく、NetBSDのカーネルデータ構造、特にucontext
またはmcontext
構造体内で使用されるシンボリックな名前またはインデックスです。
シグナルハンドラが呼び出される際、OSは現在のプロセスのレジスタ状態をmcontext
構造体などのコンテキスト構造体に保存し、シグナルハンドラに渡します。このコンテキスト構造体には、ユーザーモードのスタックポインタとしてUESP
の値が格納されており、シグナルハンドラからの復帰時にはこのUESP
の値が使用されて元のコンテキストが復元されます。
Goランタイムのような低レベルのコードが、OSのシグナルハンドリング機構と連携する際には、OSが期待するレジスタ(この場合はUESP
)を正しく操作することが不可欠となります。
技術的詳細
このコミットは、src/pkg/runtime/signal_netbsd_386.c
ファイルに対する変更です。このファイルは、NetBSD/386アーキテクチャにおけるGoランタイムのシグナルハンドリングに関連するコードを含んでいます。
変更の核心は、スタックポインタを参照または操作する際に、mc->__gregs[REG_ESP]
の代わりにmc->__gregs[REG_UESP]
を使用するように修正した点です。ここで、mc
はMcontextT
型のポインタであり、シグナルハンドラに渡されるマシンコンテキスト(レジスタの状態など)を保持しています。__gregs
は汎用レジスタの配列であり、REG_ESP
やREG_UESP
はそれぞれのレジスタに対応するインデックスです。
具体的には、以下の箇所が変更されています。
-
runtime·dumpregs
関数: デバッグ目的でレジスタの値をダンプする関数です。ここでesp
レジスタの値を表示する際に、REG_ESP
ではなくREG_UESP
から値を取得するように変更されました。- runtime·printf("esp %x\\n", mc->__gregs[REG_ESP]); + runtime·printf("esp %x\\n", mc->__gregs[REG_UESP]);
この変更は、表示されるスタックポインタの値が、実際にOSが管理しているユーザーモードのスタックポインタと一致するようにするためのものです。
-
runtime·sighandler
関数: シグナルハンドラの本体です。SIGPROF
シグナル(プロファイリングシグナル)の処理において、スタックポインタをruntime·sigprof
関数に渡す際にREG_ESP
からREG_UESP
に変更されました。- \t\t\t(uint8*)mc->__gregs[REG_ESP], nil, gp);\ + \t\t\t(uint8*)mc->__gregs[REG_UESP], nil, gp);\
- シグナルによるパニック(
sigpanic
)が発生した場合のスタックポインタの操作において、sp
変数の初期化とmc->__gregs[REG_ESP]
への代入がmc->__gregs[REG_UESP]
に変更されました。
この部分は、シグナルハンドラ内でスタックを操作し、パニック発生時のスタックトレースを正しく生成するために、OSが復元するスタックポインタ(- \t\t\tsp = (uintptr*)mc->__gregs[REG_ESP]; + \t\t\tsp = (uintptr*)mc->__gregs[REG_UESP]; - \t\t\tmc->__gregs[REG_ESP] = (uintptr)sp; + \t\t\tmc->__gregs[REG_UESP] = (uintptr)sp;
UESP
)を正確に扱うことが重要であることを示しています。
-
Throw
ラベル内の処理: エラー発生時などにトレースバックを行う部分です。ここでも、トレースバックの開始点としてスタックポインタを渡す際にREG_ESP
からREG_UESP
に変更されました。- \t\t\t(void*)mc->__gregs[REG_ESP], 0, gp);\ + \t\t\t(void*)mc->__gregs[REG_UESP], 0, gp);\
これにより、トレースバックが正しいスタックフレームから開始されるようになります。
これらの変更は、NetBSD/386環境において、Goランタイムがシグナルハンドリング中にスタックポインタを操作する際に、OSが期待するUESP
レジスタを正しく使用するようにすることで、コンテキストの不整合を防ぎ、プログラムの安定した動作を保証します。
コアとなるコードの変更箇所
src/pkg/runtime/signal_netbsd_386.c
ファイルにおいて、mc->__gregs[REG_ESP]
の参照および代入がmc->__gregs[REG_UESP]
に変更されています。
--- a/src/pkg/runtime/signal_netbsd_386.c
+++ b/src/pkg/runtime/signal_netbsd_386.c
@@ -29,7 +29,7 @@ runtime·dumpregs(McontextT *mc)
runtime·printf("edi %x\\n", mc->__gregs[REG_EDI]);
runtime·printf("esi %x\\n", mc->__gregs[REG_ESI]);
runtime·printf("ebp %x\\n", mc->__gregs[REG_EBP]);
- runtime·printf("esp %x\\n", mc->__gregs[REG_ESP]);
+ runtime·printf("esp %x\\n", mc->__gregs[REG_UESP]);
runtime·printf("eip %x\\n", mc->__gregs[REG_EIP]);
runtime·printf("eflags %x\\n", mc->__gregs[REG_EFL]);
runtime·printf("cs %x\\n", mc->__gregs[REG_CS]);
@@ -47,7 +47,7 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
if(sig == SIGPROF) {
runtime·sigprof((uint8*)mc->__gregs[REG_EIP],
- (uint8*)mc->__gregs[REG_ESP], nil, gp);
+ (uint8*)mc->__gregs[REG_UESP], nil, gp);
return;
}
@@ -71,9 +71,9 @@ runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
// (Otherwise the trace will end at runtime·sigpanic
// and we won't get to see who faulted.)
if(mc->__gregs[REG_EIP] != 0) {
- sp = (uintptr*)mc->__gregs[REG_ESP];
+ sp = (uintptr*)mc->__gregs[REG_UESP];
*--sp = mc->__gregs[REG_EIP];
- mc->__gregs[REG_ESP] = (uintptr)sp;
+ mc->__gregs[REG_UESP] = (uintptr)sp;
}
mc->__gregs[REG_EIP] = (uintptr)runtime·sigpanic;
return;
@@ -100,7 +100,7 @@ Throw:
if(runtime·gotraceback()){
runtime·traceback((void*)mc->__gregs[REG_EIP],
- (void*)mc->__gregs[REG_ESP], 0, gp);
+ (void*)mc->__gregs[REG_UESP], 0, gp);
runtime·tracebackothers(gp);
runtime·dumpregs(mc);
}
コアとなるコードの解説
このコミットのコード変更は、GoランタイムがNetBSD/386環境でシグナルハンドリングを行う際の、スタックポインタの参照と操作に関する根本的な修正です。
Goランタイムは、独自のゴルーチンスタック管理とOSのシグナルハンドリング機構との連携を必要とします。シグナルが発生すると、OSは現在のCPUの状態(レジスタの値など)を保存し、シグナルハンドラを呼び出します。この際、OSはユーザーモードのスタックポインタとしてUESP
レジスタの値をコンテキスト情報に格納します。
Goランタイムのシグナルハンドラ(runtime·sighandler
)は、このOSから渡されたコンテキスト情報(McontextT *mc
)を使用して、現在のスタックの状態を把握し、必要に応じてスタックを操作します。
-
runtime·dumpregs
: デバッグ時にレジスタの値を表示する関数です。以前はESP
レジスタの値を表示していましたが、NetBSD/386ではUESP
が実際のユーザーモードスタックポインタであるため、UESP
の値を表示するように変更されました。これにより、デバッグ情報がより正確になります。 -
runtime·sighandler
内のSIGPROF
処理: プロファイリングシグナルが受信された際に、現在のプログラムカウンタ(REG_EIP
)とスタックポインタ(REG_UESP
)をruntime·sigprof
関数に渡します。これにより、プロファイリングが正しいスタックの状態を基に行われるようになります。 -
runtime·sighandler
内のパニック処理: シグナルによってパニックが発生した場合、Goランタイムはスタックを操作して、パニック発生時のスタックトレースを生成するための準備を行います。この操作において、スタックポインタの取得と設定の両方でUESP
レジスタを使用するように変更されました。これにより、パニック時のスタックトレースが、OSが認識している正しいスタックの状態を反映するようになります。もしESP
を使用し続けていた場合、OSが復元するUESP
とGoランタイムが操作するESP
との間に不整合が生じ、スタックトレースが壊れたり、さらなるクラッシュを引き起こす可能性がありました。 -
Throw
ラベル内のトレースバック処理: エラー発生時などに呼び出されるトレースバック関数(runtime·traceback
)にスタックポインタを渡す際も、UESP
レジスタを使用するように変更されました。これにより、トレースバックが正しいスタックフレームから開始され、正確なコールスタック情報が提供されます。
これらの変更は、NetBSD/386のOSカーネルがスタックポインタをUESP
レジスタとして管理・復元するという特性にGoランタイムを適合させるものです。これにより、GoプログラムがNetBSD/386上でシグナルを正しく処理し、安定して動作することが保証されます。
関連リンク
- Go言語のランタイムに関するドキュメントやソースコード
- NetBSDのシグナルハンドリングに関するドキュメント
- x86アーキテクチャのレジスタに関する情報
参考にした情報源リンク
- https://golang.org/cl/6465054 (Go Gerrit Code Review)
- NetBSD/386 UESP registerに関するWeb検索結果 (例: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHPrbOVRI__QPFmVbCkt_tJVYLENkISENdbu_GXsGfxfx-nMrH5cDQrsmdrRhFhO-OHuujTeQztARntoxF4SHjDevbxJiOZwu3iOulmOjkEmB6B1GPiY23ClfPTc5aybcaLffwPHsps6pj_W9RfS0RN-abr8PH8CjBILt7xXj9lRA==)
- x86アーキテクチャのレジスタに関する一般的な情報源 (例: Intel Software Developer's Manual)
- NetBSDのシステムコールやカーネルに関するドキュメント (例: NetBSD man pages)
- Go言語のソースコード (特に
src/runtime
ディレクトリ)