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

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

このコミットは、Go言語のランタイムがNetBSDオペレーティングシステム上で正しく動作するように、システムコール番号と引数を修正し、シグナルハンドリングのためのトランポリンメカニズムを導入するものです。具体的には、src/pkg/runtime/sys_netbsd_386.ssrc/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に準拠したトランポリンを導入することで、この問題を解決しようとしています。

前提知識の解説

このコミットを理解するためには、以下の前提知識が必要です。

  1. システムコール (Syscall): オペレーティングシステムが提供するサービス(ファイルI/O、メモリ管理、プロセス制御など)をユーザープログラムが利用するためのインターフェースです。ユーザープログラムは、特定のシステムコール番号をレジスタに設定し、int $0x80 (i386) や syscall (amd64) といった命令を実行することで、カーネルモードに移行し、システムコールを呼び出します。システムコール番号や引数の渡し方はOSやアーキテクチャによって異なります。

  2. アセンブリ言語: CPUが直接実行できる機械語に1対1で対応する低レベルプログラミング言語です。OSのカーネルやランタイム、デバイスドライバなど、ハードウェアに密接に関わる部分でよく使用されます。Go言語のランタイムも、OS固有のシステムコール呼び出しやコンテキストスイッチなど、パフォーマンスやOSとの連携が重要な部分でアセンブリ言語を使用しています。

  3. シグナル (Signal): Unix系OSにおけるプロセス間通信や非同期イベント通知のメカニズムです。例えば、Ctrl+Cによる割り込み(SIGINT)、不正なメモリアクセス(SIGSEGV)、子プロセスの終了(SIGCHLD)などがあります。プロセスはシグナルを受信すると、デフォルトの動作(終了、コアダンプなど)を実行するか、事前に登録されたシグナルハンドラ関数を実行します。

  4. シグナルハンドラとシグナルフレーム: シグナルハンドラは、シグナルを受信した際に実行されるユーザー定義の関数です。シグナルハンドラが呼び出される際、OSは現在のプロセスの実行コンテキスト(レジスタの状態、スタックポインタなど)を保存し、シグナルハンドラが実行されるための新しいスタックフレーム(シグナルフレーム)を設定します。シグナルハンドラが終了すると、OSは保存されたコンテキストを復元し、シグナルが配送された時点の命令から実行を再開します。

  5. シグナルリターンとトランポリン: シグナルハンドラから元の実行フローに戻るプロセスを「シグナルリターン」と呼びます。多くのUnix系OSでは、シグナルハンドラが終了する際に、特別なシステムコール(例: sigreturn)を呼び出すか、またはカーネルがシグナルフレームの末尾に配置した小さなアセンブリコード(「シグナルリターントランポリン」または単に「トランポリン」)にジャンプすることで、コンテキストの復元とシグナルマスクの更新を行います。このトランポリンは、ユーザー空間で実行されますが、最終的にカーネルのsigreturnシステムコールを呼び出す役割を担います。これにより、シグナルハンドラが通常の関数呼び出しのようにret命令で戻るだけで、安全に元の実行コンテキストに戻れるようになります。

  6. NetBSDのシグナルAPI: NetBSDは、シグナル処理に関して複数のAPIバージョンを持つことがあります。新しいAPIバージョンでは、より堅牢なシグナル処理や、特定のアーキテクチャに最適化された機能が提供されることがあります。このコミットで言及されている「シグナルAPI 3」は、NetBSDにおけるシグナル処理の特定のバージョンまたは実装を指しており、これに対応するためにsys___sigaction_sigtrampのような新しいシステムコールが導入されたと考えられます。

  7. TLS (Thread Local Storage): スレッドごとに独立したデータを保持するためのメカニズムです。Goランタイムでは、ゴルーチン(Goの軽量スレッド)のコンテキスト管理やスケジューリングにTLSが利用されることがあります。settlsシステムコールは、このTLSのベースアドレスを設定するために使用されます。

技術的詳細

このコミットの主要な技術的変更点は以下の通りです。

  1. 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システムコールの引数が更新されたことを示しています。
  2. シグナルリターントランポリン runtime·sigreturn_tramp の導入: NetBSDのシグナルAPI 3に準拠するために、runtime·sigreturn_trampという新しいアセンブリ関数が導入されました。この関数は、シグナルハンドラが終了した後に実行されるトランポリンコードとして機能します。

    • このトランポリンは、シグナルハンドラが実行された際にカーネルがスタックにプッシュしたucontext構造体のアドレスを取得します。
    • その後、sys_setcontext (システムコール番号 308) を呼び出し、保存されたucontextを使用してプロセスのコンテキストを復元し、シグナルが配送された時点の命令から実行を再開させます。
    • もしsys_setcontextが失敗した場合は、sys_exitシステムコールを呼び出してプロセスを終了させます。
  3. 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レジスタに308sys_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レジスタに308sys_setcontextのシステムコール番号)を設定します。
    • SYSCALL: システムコールを実行します。
    • MOVQ $-1, DI / MOVL $1, AX / SYSCALL: sys_setcontextが失敗した場合のフォールバックとして、sys_exitシステムコールを呼び出してプロセスを終了させます。

runtime·sigaction (i386 および amd64)

この関数は、シグナルハンドラを設定するために使用されます。

  • i386版:

    • MOVL $340, AX: AXレジスタに340sys___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レジスタに340sys___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レジスタに281sys___sigaltstack14のシステムコール番号)を設定します。
  • amd64版:
    • MOVQ $281, AX: AXレジスタに281sys___sigaltstack14のシステムコール番号)を設定します。

runtime·settls (i386 および amd64)

スレッドローカルストレージのベースアドレスを設定する関数です。

  • i386版:
    • MOVL $16, 4(SP): sys_sysarchシステムコールのサブコマンドとして16X86_SET_GSBASE)をスタックにプッシュします。
  • amd64版:
    • MOVQ $17, DI: sys_sysarchシステムコールのサブコマンドとして17X86_64_SET_FSBASE)をDIレジスタにロードします。

これらの変更は、NetBSDのシステムコールインターフェースの進化に対応し、GoランタイムがNetBSD上でより堅牢かつ正確に動作するための基盤を強化するものです。特にシグナル処理におけるトランポリンの導入は、低レベルなOS連携の正確性を保証する上で不可欠な要素です。

関連リンク

参考にした情報源リンク

  • NetBSDのソースコード(特にsys/kern/syscalls.mastersys/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に関する情報