[インデックス 13053] ファイルの概要
このコミットは、Go言語のランタイムがNetBSDオペレーティングシステム上で正しく動作するように、システムコール番号と引数を修正し、シグナルハンドリングのためのトランポリンメカニズムを導入するものです。具体的には、src/pkg/runtime/sys_netbsd_386.s
とsrc/pkg/runtime/sys_netbsd_amd64.s
という、それぞれ32ビット(i386)および64ビット(amd64)アーキテクチャ向けのNetBSD固有のアセンブリコードが変更されています。
コミット
commit 5374ded1f3c35a9c98f4a80e35b4b11fa3caa1c7
Author: Joel Sing <jsing@google.com>
Date: Fri May 11 03:48:43 2012 +1000
runtime: fix syscalls for netbsd
Use correct syscall numbers and arguments for NetBSD.
Provide a trampoline for signal returns (using signal API 3).
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6209048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5374ded1f3c35a9c98f4a80e35b4b11fa3caa1c7
元コミット内容
このコミットの元のメッセージは以下の通りです。
runtime: fix syscalls for netbsd
Use correct syscall numbers and arguments for NetBSD.
Provide a trampoline for signal returns (using signal API 3).
これは、GoランタイムがNetBSD上でシステムコールを正しく呼び出し、シグナル処理を適切に行うための修正であることを明確に示しています。特に、シグナル処理においては「シグナルAPI 3」を使用するトランポリンの提供が言及されています。
変更の背景
Go言語のランタイムは、様々なオペレーティングシステム(OS)とアーキテクチャ上で動作するように設計されています。各OSは独自のシステムコールインターフェースとシグナル処理メカニズムを持っています。Goランタイムが特定のOS上で安定して動作するためには、これらのOS固有のインターフェースに正確に適合する必要があります。
このコミットが行われた背景には、NetBSDにおけるシステムコール番号の不一致と、シグナルハンドラからの正しい復帰メカニズムの欠如があったと考えられます。Goランタイムがシグナルを受信し、カスタムのシグナルハンドラを実行した後、元の実行コンテキストに安全に戻るためには、OSが提供する特定のメカニズム(多くの場合、シグナルフレームとトランポリンコード)を利用する必要があります。NetBSDのシグナルAPIのバージョンアップや、システムコール番号の変更に対応していなかったため、GoプログラムがNetBSD上でクラッシュしたり、シグナル処理が正しく行われない問題が発生していた可能性があります。
特に、シグナルハンドラからの復帰は非常にデリケートな処理であり、スタックの状態、レジスタの値、シグナルマスクなどが正確に復元される必要があります。OSは通常、このために特別なシステムコールや、ユーザー空間で実行される小さなアセンブリコード(トランポリン)を提供します。このコミットは、NetBSDのシグナルAPIバージョン3に準拠したトランポリンを導入することで、この問題を解決しようとしています。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
-
システムコール (Syscall): オペレーティングシステムが提供するサービス(ファイルI/O、メモリ管理、プロセス制御など)をユーザープログラムが利用するためのインターフェースです。ユーザープログラムは、特定のシステムコール番号をレジスタに設定し、
int $0x80
(i386) やsyscall
(amd64) といった命令を実行することで、カーネルモードに移行し、システムコールを呼び出します。システムコール番号や引数の渡し方はOSやアーキテクチャによって異なります。 -
アセンブリ言語: CPUが直接実行できる機械語に1対1で対応する低レベルプログラミング言語です。OSのカーネルやランタイム、デバイスドライバなど、ハードウェアに密接に関わる部分でよく使用されます。Go言語のランタイムも、OS固有のシステムコール呼び出しやコンテキストスイッチなど、パフォーマンスやOSとの連携が重要な部分でアセンブリ言語を使用しています。
-
シグナル (Signal): Unix系OSにおけるプロセス間通信や非同期イベント通知のメカニズムです。例えば、Ctrl+Cによる割り込み(SIGINT)、不正なメモリアクセス(SIGSEGV)、子プロセスの終了(SIGCHLD)などがあります。プロセスはシグナルを受信すると、デフォルトの動作(終了、コアダンプなど)を実行するか、事前に登録されたシグナルハンドラ関数を実行します。
-
シグナルハンドラとシグナルフレーム: シグナルハンドラは、シグナルを受信した際に実行されるユーザー定義の関数です。シグナルハンドラが呼び出される際、OSは現在のプロセスの実行コンテキスト(レジスタの状態、スタックポインタなど)を保存し、シグナルハンドラが実行されるための新しいスタックフレーム(シグナルフレーム)を設定します。シグナルハンドラが終了すると、OSは保存されたコンテキストを復元し、シグナルが配送された時点の命令から実行を再開します。
-
シグナルリターンとトランポリン: シグナルハンドラから元の実行フローに戻るプロセスを「シグナルリターン」と呼びます。多くのUnix系OSでは、シグナルハンドラが終了する際に、特別なシステムコール(例:
sigreturn
)を呼び出すか、またはカーネルがシグナルフレームの末尾に配置した小さなアセンブリコード(「シグナルリターントランポリン」または単に「トランポリン」)にジャンプすることで、コンテキストの復元とシグナルマスクの更新を行います。このトランポリンは、ユーザー空間で実行されますが、最終的にカーネルのsigreturn
システムコールを呼び出す役割を担います。これにより、シグナルハンドラが通常の関数呼び出しのようにret
命令で戻るだけで、安全に元の実行コンテキストに戻れるようになります。 -
NetBSDのシグナルAPI: NetBSDは、シグナル処理に関して複数のAPIバージョンを持つことがあります。新しいAPIバージョンでは、より堅牢なシグナル処理や、特定のアーキテクチャに最適化された機能が提供されることがあります。このコミットで言及されている「シグナルAPI 3」は、NetBSDにおけるシグナル処理の特定のバージョンまたは実装を指しており、これに対応するために
sys___sigaction_sigtramp
のような新しいシステムコールが導入されたと考えられます。 -
TLS (Thread Local Storage): スレッドごとに独立したデータを保持するためのメカニズムです。Goランタイムでは、ゴルーチン(Goの軽量スレッド)のコンテキスト管理やスケジューリングにTLSが利用されることがあります。
settls
システムコールは、このTLSのベースアドレスを設定するために使用されます。
技術的詳細
このコミットの主要な技術的変更点は以下の通りです。
-
NetBSDシステムコール番号の修正: GoランタイムがNetBSDカーネルと対話するために使用するシステムコール番号が、NetBSDの最新の定義に合わせて更新されました。
runtime·sigaction
(シグナルハンドラの設定):- 旧:
sys_sigaction
(システムコール番号46
) を使用。 - 新:
sys___sigaction_sigtramp
(システムコール番号340
) を使用。これは、シグナルリターントランポリンのアドレスを引数として受け取る、より新しいシグナルアクション設定システムコールです。
- 旧:
runtime·sigaltstack
(代替シグナルスタックの設定):- 旧:
sys_sigaltstack
(システムコール番号288
) を使用。 - 新:
sys___sigaltstack14
(システムコール番号281
) を使用。これは、NetBSD 1.4以降で導入されたシグナルスタック設定のシステムコールです。
- 旧:
runtime·settls
(TLSベースアドレスの設定):- i386:
I386_SET_GSBASE
(値9
) からX86_SET_GSBASE
(値16
) へ変更。これはsys_sysarch
システムコールのサブコマンドです。 - amd64:
AMD64_SET_FSBASE
(値12
) からX86_64_SET_FSBASE
(値17
) へ変更。これもsys_sysarch
システムコールのサブコマンドです。 これらの変更は、NetBSDにおけるTLS設定のためのsysarch
システムコールの引数が更新されたことを示しています。
- i386:
-
シグナルリターントランポリン
runtime·sigreturn_tramp
の導入: NetBSDのシグナルAPI 3に準拠するために、runtime·sigreturn_tramp
という新しいアセンブリ関数が導入されました。この関数は、シグナルハンドラが終了した後に実行されるトランポリンコードとして機能します。- このトランポリンは、シグナルハンドラが実行された際にカーネルがスタックにプッシュした
ucontext
構造体のアドレスを取得します。 - その後、
sys_setcontext
(システムコール番号308
) を呼び出し、保存されたucontext
を使用してプロセスのコンテキストを復元し、シグナルが配送された時点の命令から実行を再開させます。 - もし
sys_setcontext
が失敗した場合は、sys_exit
システムコールを呼び出してプロセスを終了させます。
- このトランポリンは、シグナルハンドラが実行された際にカーネルがスタックにプッシュした
-
sigaction
システムコールへのトランポリンアドレスとAPIバージョンの渡し方:runtime·sigaction
関数は、sys___sigaction_sigtramp
システムコールを呼び出す際に、新しく導入されたruntime·sigreturn_tramp
のアドレスと、シグナルAPIのバージョン(3
)を引数として渡すようになりました。これにより、カーネルはGoランタイムが提供するカスタムのシグナルリターントランポリンを使用するよう設定されます。
これらの変更は、GoランタイムがNetBSDの特定のバージョン(おそらく当時の最新版)のシステムコールインターフェースとシグナル処理メカニズムに正確に適合するようにするためのものであり、Goプログラムの安定性と信頼性を向上させます。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルとコードスニペットは以下の通りです。
src/pkg/runtime/sys_netbsd_386.s
--- a/src/pkg/runtime/sys_netbsd_386.s
+++ b/src/pkg/runtime/sys_netbsd_386.s
@@ -128,8 +128,27 @@ TEXT runtime·nanotime(SB),7,$32
MOVL DX, 4(DI)
RET
-TEXT runtime·sigaction(SB),7,$-4
-\tMOVL\t$46, AX\t\t\t// sys_sigaction
+TEXT runtime·sigreturn_tramp(SB),7,$0
+\tLEAL\t140(SP), AX\t\t// Load address of ucontext
+\tMOVL\tAX, 4(SP)
+\tMOVL\t$308, AX\t\t// sys_setcontext
+\tINT\t$0x80
+\tMOVL\t$-1, 4(SP)\t\t// Something failed...
+\tMOVL\t$1, AX\t\t\t// sys_exit
+\tINT\t$0x80
+\n+TEXT runtime·sigaction(SB),7,$24
+\tLEAL\targ0+0(FP), SI
+\tLEAL\t4(SP), DI
+\tCLD
+\tMOVSL\t\t\t\t// arg 1 - sig
+\tMOVSL\t\t\t\t// arg 2 - act
+\tMOVSL\t\t\t\t// arg 3 - oact
+\tLEAL\truntime·sigreturn_tramp(SB), AX
+\tSTOSL\t\t\t\t// arg 4 - tramp
+\tMOVL\t$3, AX
+\tSTOSL\t\t\t\t// arg 5 - vers
+\tMOVL\t$340, AX\t\t// sys___sigaction_sigtramp
\tINT\t$0x80
\tJAE\t2(PC)
\tMOVL\t$0xf1, 0xf1 // crash
@@ -259,7 +278,7 @@ TEXT runtime·rfork_thread(SB),7,$8
RET
TEXT runtime·sigaltstack(SB),7,$-8
-\tMOVL\t$288, AX\t\t// sys_sigaltstack
+\tMOVL\t$281, AX\t\t// sys___sigaltstack14
\tMOVL\tnew+4(SP), BX
\tMOVL\told+8(SP), CX
\tINT\t$0x80
@@ -281,7 +300,7 @@ TEXT runtime·settls(SB),7,$16
ADDL\t$8, CX
MOVL\tCX, 0(CX)
MOVL\t$0, 0(SP)\t\t// syscall gap
-\tMOVL\t$9, 4(SP)\t\t// I386_SET_GSBASE (machine/sysarch.h)
+\tMOVL\t$16, 4(SP)\t\t// X86_SET_GSBASE (x86/sysarch.h)
MOVL\tCX, 8(SP)\t\t// pointer to base
MOVL\t$165, AX\t\t// sys_sysarch
INT\t$0x80
src/pkg/runtime/sys_netbsd_amd64.s
--- a/src/pkg/runtime/sys_netbsd_amd64.s
+++ b/src/pkg/runtime/sys_netbsd_amd64.s
@@ -163,11 +163,23 @@ TEXT runtime·nanotime(SB),7,$32
ADDQ\tDX, AX
RET
+TEXT runtime·sigreturn_tramp(SB),7,$-8
+\tMOVQ\tR15, DI\t\t\t// Load address of ucontext
+\tMOVQ\t$308, AX\t\t// sys_setcontext
+\tSYSCALL
+\tMOVQ\t$-1, DI\t\t\t// Something failed...
+\tMOVL\t$1, AX\t\t\t// sys_exit
+\tSYSCALL
+\n TEXT runtime·sigaction(SB),7,$-8
\tMOVL\t8(SP), DI\t\t// arg 1 - signum
\tMOVQ\t16(SP), SI\t\t// arg 2 - nsa
\tMOVQ\t24(SP), DX\t\t// arg 3 - osa
-\tMOVL\t$46, AX
+\t\t\t\t\t// arg 4 - tramp
+\tLEAQ\truntime·sigreturn_tramp(SB), R10
+\tMOVQ\t$3, R8\t\t\t// arg 5 - version
+\tMOVL\t$340, AX\t\t// sys___sigaction_sigtramp
+\n \tSYSCALL
\tJCC\t2(PC)
\tMOVL\t$0xf1, 0xf1 // crash
@@ -232,7 +244,7 @@ TEXT runtime·munmap(SB),7,$0
TEXT runtime·sigaltstack(SB),7,$-8
\tMOVQ\tnew+8(SP), DI\t\t// arg 1 - nss
\tMOVQ\told+16(SP), SI\t\t// arg 2 - oss
-\tMOVQ\t$288, AX\t\t// sys_sigaltstack
+\tMOVQ\t$281, AX\t\t// sys___sigaltstack14
\tSYSCALL
\tJCC\t2(PC)
\tMOVL\t$0xf1, 0xf1 // crash
@@ -244,7 +256,7 @@ TEXT runtime·settls(SB),7,$8
\tADDQ\t$16, DI
\tMOVQ\tDI, 0(SP)
\tMOVQ\tSP, SI
-\tMOVQ\t$12, DI\t\t\t// AMD64_SET_FSBASE (machine/sysarch.h)
+\tMOVQ\t$17, DI\t\t\t// X86_64_SET_FSBASE (x86/sysarch.h)
\tMOVQ\t$165, AX\t\t// sys_sysarch
\tSYSCALL
\tJCC\t2(PC)
コアとなるコードの解説
runtime·sigreturn_tramp
(i386 および amd64)
この新しいアセンブリ関数は、シグナルハンドラが実行された後に制御が移るトランポリンです。
-
i386版:
LEAL 140(SP), AX
: スタックポインタSP
から140バイトオフセットしたアドレスをAX
レジスタにロードします。このオフセットは、シグナルハンドラが呼び出される際にカーネルがスタックにプッシュするucontext
構造体のアドレスを指していると推測されます。MOVL AX, 4(SP)
:AX
の値をスタックのSP+4
にプッシュします。これはsys_setcontext
システムコールの最初の引数(ucontext
へのポインタ)として準備されます。MOVL $308, AX
:AX
レジスタに308
(sys_setcontext
のシステムコール番号)を設定します。INT $0x80
: システムコールを実行します。これにより、保存されたucontext
に基づいてプロセスのコンテキストが復元され、シグナルが配送された時点の命令から実行が再開されます。MOVL $-1, 4(SP)
/MOVL $1, AX
/INT $0x80
:sys_setcontext
が失敗した場合(通常は発生しないはずですが、フォールバックとして)、sys_exit
システムコールを呼び出してプロセスを終了させます。
-
amd64版:
MOVQ R15, DI
:R15
レジスタの値をDI
レジスタに移動します。amd64では、システムコール呼び出し規約により、最初の引数はDI
レジスタで渡されます。R15
には、シグナルハンドラが呼び出される際にカーネルがucontext
構造体のアドレスを格納していると推測されます。MOVQ $308, AX
:AX
レジスタに308
(sys_setcontext
のシステムコール番号)を設定します。SYSCALL
: システムコールを実行します。MOVQ $-1, DI
/MOVL $1, AX
/SYSCALL
:sys_setcontext
が失敗した場合のフォールバックとして、sys_exit
システムコールを呼び出してプロセスを終了させます。
runtime·sigaction
(i386 および amd64)
この関数は、シグナルハンドラを設定するために使用されます。
-
i386版:
MOVL $340, AX
:AX
レジスタに340
(sys___sigaction_sigtramp
のシステムコール番号)を設定します。LEAL runtime·sigreturn_tramp(SB), AX
/STOSL
:runtime·sigreturn_tramp
のアドレスをスタックにプッシュします。これはsys___sigaction_sigtramp
の第4引数(トランポリンのアドレス)として渡されます。MOVL $3, AX
/STOSL
:3
(シグナルAPIバージョン)をスタックにプッシュします。これはsys___sigaction_sigtramp
の第5引数として渡されます。- これにより、NetBSDカーネルは、Goランタイムが提供する
runtime·sigreturn_tramp
をシグナルリターントランポリンとして使用するように設定されます。
-
amd64版:
MOVL $340, AX
:AX
レジスタに340
(sys___sigaction_sigtramp
のシステムコール番号)を設定します。LEAQ runtime·sigreturn_tramp(SB), R10
:runtime·sigreturn_tramp
のアドレスをR10
レジスタにロードします。これはsys___sigaction_sigtramp
の第4引数として渡されます。MOVQ $3, R8
:3
(シグナルAPIバージョン)をR8
レジスタにロードします。これはsys___sigaction_sigtramp
の第5引数として渡されます。- amd64のシステムコール規約に従い、引数はレジスタ(
DI
,SI
,DX
,R10
,R8
,R9
)で渡されます。
runtime·sigaltstack
(i386 および amd64)
代替シグナルスタックを設定する関数です。
- i386版:
MOVL $281, AX
:AX
レジスタに281
(sys___sigaltstack14
のシステムコール番号)を設定します。
- amd64版:
MOVQ $281, AX
:AX
レジスタに281
(sys___sigaltstack14
のシステムコール番号)を設定します。
runtime·settls
(i386 および amd64)
スレッドローカルストレージのベースアドレスを設定する関数です。
- i386版:
MOVL $16, 4(SP)
:sys_sysarch
システムコールのサブコマンドとして16
(X86_SET_GSBASE
)をスタックにプッシュします。
- amd64版:
MOVQ $17, DI
:sys_sysarch
システムコールのサブコマンドとして17
(X86_64_SET_FSBASE
)をDI
レジスタにロードします。
これらの変更は、NetBSDのシステムコールインターフェースの進化に対応し、GoランタイムがNetBSD上でより堅牢かつ正確に動作するための基盤を強化するものです。特にシグナル処理におけるトランポリンの導入は、低レベルなOS連携の正確性を保証する上で不可欠な要素です。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
- NetBSDの公式ウェブサイト: https://www.netbsd.org/
- NetBSDのシステムコールに関するドキュメント (一般的な情報): https://man.netbsd.org/
- GoのChange List (CL) 6209048: https://golang.org/cl/6209048 (このコミットの元となったコードレビューページ)
参考にした情報源リンク
- NetBSDのソースコード(特に
sys/kern/syscalls.master
やsys/arch/x86/include/
以下のヘッダファイル) - Unix系OSにおけるシグナル処理とシグナルフレームに関する一般的な技術記事
- Go言語のランタイムに関する技術ブログやドキュメント
- x86およびx86-64アーキテクチャのアセンブリ言語に関する資料
sys_setcontext
に関するNetBSDのmanページsigaction
に関するNetBSDのmanページsigaltstack
に関するNetBSDのmanページsysarch
に関するNetBSDのmanページ- NetBSDのシステムコール番号リスト (例: NetBSD 5.0) (具体的なバージョンによって異なるため、当時のNetBSDのバージョンに依存します)
- NetBSDの
ucontext
構造体に関する情報 - NetBSDの
x86/sysarch.h
に関する情報