[インデックス 13070] ファイルの概要
このコミットは、Go言語のランタイムにおけるNetBSD固有の定義(defs_netbsd.go
)の修正に関するものです。特に、mcontext
構造体の扱いと、アーキテクチャに依存するレジスタ定義の取り扱いを改善することを目的としています。
コミット
commit 8a9edcf7906637ddb037296a86f664a50c47a199
Author: Joel Sing <jsing@google.com>
Date: Wed May 16 01:52:20 2012 +1000
runtime: fix netbsd runtime defs
Fix and regenerate runtime defs for NetBSD.
Whilst the mcontext struct can be handled across architectures,
the registers are provided as defines that index an array, rather
than as members of the struct. Since these are architecture
dependent, include them via a defs_netbsd_<arch>.go file.
R=golang-dev, m4dh4tt3r, rsc
CC=golang-dev
https://golang.org/cl/6190070
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8a9edcf7906637ddb037296a86f664a50c47a199
元コミット内容
runtime: fix netbsd runtime defs
Fix and regenerate runtime defs for NetBSD.
Whilst the mcontext struct can be handled across architectures,
the registers are provided as defines that index an array, rather
than as members of the struct. Since these are architecture
dependent, include them via a defs_netbsd_<arch>.go file.
R=golang-dev, m4dh4tt3r, rsc
CC=golang-dev
https://golang.org/cl/6190070
変更の背景
Go言語のランタイムは、各オペレーティングシステム(OS)やCPUアーキテクチャに特化した低レベルな定義(システムコール番号、構造体のレイアウト、シグナルハンドリング関連の定数など)を必要とします。これらの定義は、GoプログラムがOSと正しくインタラクトするために不可欠です。
NetBSD環境において、Goランタイムがシグナルハンドリングやコンテキスト切り替えを行う際に使用するmcontext
(マシンコンテキスト)構造体の扱いに関して問題がありました。mcontext
構造体自体は、異なるアーキテクチャ間である程度共通の形式で扱えるものの、その内部でレジスタ情報が「構造体のメンバー」としてではなく、「配列のインデックス」として定義されたマクロ(#define
)を通じて提供されていました。
この「配列のインデックス」としてのレジスタ定義は、CPUアーキテクチャ(例: 386とamd64)によって異なるため、Goランタイムがこれらのレジスタに正しくアクセスするためには、アーキテクチャごとに異なる定義を適用する必要がありました。従来のGoランタイムの定義では、このアーキテクチャ依存性が十分に考慮されておらず、NetBSD上でのGoプログラムの安定性や正確性に影響を与える可能性がありました。
このコミットは、この問題を解決し、NetBSDにおけるGoランタイムが、アーキテクチャ固有のレジスタ定義を適切に利用できるようにすることを目的としています。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念を把握しておく必要があります。
-
Goランタイム (Go Runtime): Go言語で書かれたプログラムは、Goランタイムと呼ばれる低レベルなコンポーネントに依存して動作します。Goランタイムは、ガベージコレクション、ゴルーチン(軽量スレッド)のスケジューリング、チャネル通信、システムコールへの橋渡しなど、Goプログラムの実行環境全体を管理します。OSとのインタフェース部分は、OSやアーキテクチャごとに異なる実装を持つことがあります。
-
NetBSD: NetBSDは、オープンソースのUNIXライクなオペレーティングシステムであり、BSD系OSの一つです。非常に移植性が高く、多くの異なるハードウェアアーキテクチャで動作することで知られています。Go言語は、NetBSDを含む様々なOSをサポートしています。
-
mcontext
(Machine Context):mcontext_t
は、UNIX系OSで定義される構造体で、特定の時点におけるCPUの実行状態(コンテキスト)を保存するために使用されます。これには、汎用レジスタ、セグメントレジスタ、命令ポインタ(プログラムカウンタ)、スタックポインタ、フラグレジスタ、浮動小数点ユニット(FPU)の状態などが含まれます。主にシグナルハンドリングの際に、シグナル発生時のプロセスの状態を保存し、シグナルハンドラが終了した後に元の状態に復元するために利用されます。 -
ucontext
(User Context):ucontext_t
は、mcontext_t
を内包する、より広範なユーザーレベルの実行コンテキストを表現する構造体です。mcontext_t
に加えて、現在のシグナルマスク、代替スタック情報、そしてコンテキストのリンクリスト(uc_link
)などが含まれます。setcontext()
やgetcontext()
といった関数を通じて、ユーザー空間でのコンテキスト切り替え(コルーチンやユーザーレベルスレッドの実装など)に利用されます。 -
シグナルハンドリング (Signal Handling): シグナルは、OSがプロセスに対して非同期に通知するイベントです。例えば、Ctrl+Cによる割り込み(SIGINT)、不正なメモリアクセス(SIGSEGV)、子プロセスの終了(SIGCHLD)などがあります。プロセスは、これらのシグナルを受け取った際に、事前に登録されたシグナルハンドラ関数を実行することで応答できます。シグナルハンドラが実行される際には、現在の実行コンテキストが保存され、ハンドラが終了すると元のコンテキストが復元されます。
-
cgo
:cgo
は、Go言語とC言語のコードを相互運用するためのGoツールチェーンの一部です。GoプログラムからC関数を呼び出したり、Cの構造体や定数をGoのコードで利用したりすることを可能にします。このコミットでは、cgo -cdefs
コマンドが使用されています。これは、CのヘッダファイルからGoの定義(定数や構造体)を自動生成するために使われます。これにより、Cのシステムヘッダで定義されているOS固有の定数や構造体を、Goのコードから型安全に利用できるようになります。 -
アーキテクチャ依存の定義: CPUアーキテクチャ(例: Intel/AMDのx86-32ビット版である386、x86-64ビット版であるamd64)によって、レジスタのセット、サイズ、命名規則、そしてメモリの配置などが異なります。そのため、低レベルなシステムプログラミングやランタイムの実装においては、これらのアーキテクチャ固有の差異を吸収するための定義やコードが必要となります。
技術的詳細
このコミットの核心は、NetBSDのmcontext
構造体におけるレジスタの定義方法と、Goランタイムがそれをどのように扱うかという点にあります。
従来のGoランタイムでは、NetBSDのレジスタ情報をSigcontext
というGoの構造体で抽象化していましたが、NetBSDの実際のmcontext
構造体は、レジスタを直接構造体のメンバーとして持つのではなく、__gregs
のような配列として持ち、その配列内の特定のインデックスに各レジスタが割り当てられていました。このインデックスはアーキテクチャ(386やamd64)によって異なるため、Goランタイムが汎用的なSigcontext
を使用していると、正しいレジスタ値にアクセスできないという問題が発生していました。
この問題を解決するため、以下の変更が行われました。
-
ucontext_t
とmcontext_t
の直接利用:src/pkg/runtime/defs_netbsd.go
において、従来のSigcontext
関連の定義(Sigval
,sfxsave64
,usavefpu
,Sigcontext
)が削除されました。代わりに、C言語の標準的なヘッダファイルである<sys/ucontext.h>
をインクルードし、そこからucontext_t
とmcontext_t
構造体をGoの型(UcontextT
とMcontextT
)として直接利用するように変更されました。これにより、GoランタイムはNetBSDが提供するネイティブなコンテキスト構造を正確に反映できるようになります。 -
アーキテクチャ固有のレジスタ定義の分離:
mcontext
内のレジスタが配列のインデックスでアクセスされるというNetBSDの特性に対応するため、アーキテクチャ固有のレジスタ定数(例:_REG_GS
,_REG_EAX
など)を定義する新しいファイルが導入されました。src/pkg/runtime/defs_netbsd_386.go
(新規): 386アーキテクチャ用のレジスタ定数を定義します。これらの定数は、Cのmachine/mcontext.h
で定義されている_REG_XXX
マクロに対応します。src/pkg/runtime/defs_netbsd_amd64.go
(新規): AMD64アーキテクチャ用のレジスタ定数を定義します。同様に、Cのmachine/mcontext.h
で定義されている_REG_XXX
マクロに対応します。
-
cgo -cdefs
コマンドの変更:src/pkg/runtime/defs_netbsd.go
内のコメントに示されているように、cgo -cdefs
コマンドの実行方法が変更されました。- 旧:
GOARCH=amd64 cgo -cdefs defs.go >amd64/defs.h
- 新:
GOARCH=amd64 go tool cgo -cdefs defs_netbsd.go defs_netbsd_amd64.go >defs_netbsd_amd64.h
この変更により、cgo
は汎用的なdefs_netbsd.go
だけでなく、アーキテクチャ固有のdefs_netbsd_<arch>.go
ファイルも入力として受け取り、それぞれのアーキテクチャに特化したヘッダファイル(defs_netbsd_386.h
およびdefs_netbsd_amd64.h
)を生成するようになりました。これにより、生成されるヘッダファイルには、各アーキテクチャに合わせた正確なMcontextT
構造体のレイアウトとレジスタ定数が含まれるようになります。
- 旧:
これらの変更により、GoランタイムはNetBSDのシグナルハンドリングメカニズムとより密接に連携できるようになり、特にシグナルハンドラが呼び出された際のCPUコンテキストの保存と復元が正確に行われるようになります。これは、Goプログラムの安定性と信頼性を向上させる上で非常に重要です。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルと、その変更の概要は以下の通りです。
-
src/pkg/runtime/defs_netbsd.go
:Sigval
,sfxsave64
,usavefpu
,Sigcontext
といった型定義が削除されました。これらはNetBSDのmcontext
の実際の構造と合致しない、あるいは不要になった抽象化でした。#include <sys/ucontext.h>
が追加され、NetBSDのネイティブなucontext_t
とmcontext_t
構造体を利用するための準備がされました。type McontextT C.mcontext_t
とtype UcontextT C.ucontext_t
が追加され、Cの構造体がGoの型として直接マッピングされるようになりました。cgo -cdefs
コマンドの生成指示が更新され、アーキテクチャ固有のdefs_netbsd_<arch>.go
ファイルも入力として含めるようになりました。
-
src/pkg/runtime/defs_netbsd_386.go
(新規ファイル):- 386(x86)アーキテクチャに特化したレジスタ定数(
REG_GS
,REG_FS
,REG_EAX
,REG_EIP
など)が定義されました。これらの定数は、Cのmachine/mcontext.h
で定義されている_REG_XXX
マクロに対応し、mcontext
構造体内のレジスタ配列のインデックスとして使用されます。
- 386(x86)アーキテクチャに特化したレジスタ定数(
-
src/pkg/runtime/defs_netbsd_386.h
:cgo -cdefs
によって生成されるヘッダファイルです。このコミットにより、McontextT
とUcontextT
の構造体定義がNetBSDの実際の構造に即したものに更新されました。特に、McontextT
内の__gregs
配列のサイズや、__fpregs
の定義が変更されています。また、defs_netbsd_386.go
で定義されたレジスタ定数もここに含まれるようになりました。
-
src/pkg/runtime/defs_netbsd_amd64.go
(新規ファイル):- AMD64(x86-64)アーキテクチャに特化したレジスタ定数(
REG_RDI
,REG_RSI
,REG_RAX
,REG_RIP
など)が定義されました。これもCのmachine/mcontext.h
で定義されている_REG_XXX
マクロに対応します。
- AMD64(x86-64)アーキテクチャに特化したレジスタ定数(
-
src/pkg/runtime/defs_netbsd_amd64.h
:cgo -cdefs
によって生成されるヘッダファイルです。McontextT
とUcontextT
の構造体定義がAMD64アーキテクチャのNetBSDの実際の構造に即したものに更新されました。__gregs
配列のサイズや__fpregs
の定義が変更され、defs_netbsd_amd64.go
で定義されたレジスタ定数も含まれるようになりました。
コアとなるコードの解説
このコミットの主要な変更は、GoランタイムがNetBSDのシグナルハンドリングとコンテキスト管理をより正確に行うための基盤を整備した点にあります。
-
defs_netbsd.go
の変更: このファイルは、GoランタイムがNetBSDのシステムコールや構造体とインタラクトするためのGo側の定義を生成する元となるものです。従来のSigcontext
のような抽象化された構造体ではなく、NetBSDが提供するネイティブなucontext_t
やmcontext_t
を直接Goの型としてマッピングすることで、OSとのインタフェースの正確性が向上しました。これにより、GoランタイムはNetBSDのシグナルハンドラが受け取るコンテキスト情報を、より忠実に解釈できるようになります。 -
defs_netbsd_386.go
とdefs_netbsd_amd64.go
の導入: これらの新しいファイルは、NetBSDのmcontext
構造体内でレジスタが配列のインデックスとして扱われるという特性に対応するために不可欠です。各アーキテクチャ固有のレジスタ定数をGoのコードで明示的に定義することで、cgo
が生成するヘッダファイル(.h
)にこれらの定数が含まれるようになります。Goランタイムは、これらの定数を利用して、mcontext
構造体内の正しい配列インデックスから特定のレジスタ値を取得できるようになります。例えば、386アーキテクチャではREG_EAX
が__gregs
配列の特定のインデックスに対応し、amd64アーキテクチャではREG_RAX
が別のインデックスに対応するといった具合です。 -
生成されるヘッダファイル(
defs_netbsd_386.h
,defs_netbsd_amd64.h
)の更新: これらのヘッダファイルは、cgo -cdefs
によって自動生成され、GoのランタイムコードがCの構造体や定数にアクセスする際に使用されます。今回の変更により、これらのファイルに含まれるMcontextT
やUcontextT
の構造体定義が、NetBSDの実際のシステムヘッダの定義と完全に一致するようになりました。特に、McontextT
内のレジスタ配列(__gregs
)のサイズや、浮動小数点レジスタの状態を保持する__fpregs
のオフセットなどが正確に反映されることで、Goランタイムがシグナルハンドリング時にCPUの状態を正しく保存・復元できるようになります。
これらの変更は、GoランタイムがNetBSD上でより堅牢に動作するための重要な修正であり、特にシグナル処理や低レベルなコンテキスト管理の正確性を保証する上で不可欠です。
関連リンク
- Go Code Review: https://golang.org/cl/6190070
参考にした情報源リンク
- NetBSD
mcontext_t
anducontext_t
man pages (一般的なUNIX系OSのコンテキスト構造に関する情報源として) - Go言語の
cgo
に関する公式ドキュメント (GoとCの相互運用に関する情報として) - Go言語のランタイムに関するドキュメントやソースコード (Goランタイムの内部構造に関する情報として)
- x86およびx86-64アーキテクチャのレジスタセットに関する情報 (アーキテクチャ依存のレジスタ定義に関する情報として)