[インデックス 15290] ファイルの概要
このコミットは、Go言語のランタイムにおけるOpenBSD上でのビルド問題を修正するものです。具体的には、src/pkg/runtime/thread_openbsd.c
ファイル内のruntime·sigprocmask
関数の呼び出しにおいて、引数の渡し方がOpenBSDのsigprocmask
システムコールの期待するシグネチャと異なっていた点を修正しています。
コミット
commit f1037f0e86d6f02fd29fe859ff0b5ed2ded6ecf5
Author: Georg Reinke <guelfey@gmail.com>
Date: Sun Feb 17 02:06:59 2013 +1100
runtime: fix build on openbsd
R=golang-dev, jsing
CC=golang-dev
https://golang.org/cl/7312104
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f1037f0e86d6f02fd29fe859ff0b5ed2ded6ecf5
元コミット内容
runtime: fix build on openbsd
R=golang-dev, jsing
CC=golang-dev
https://golang.org/cl/7312104
変更の背景
このコミットの背景には、Go言語のランタイムがOpenBSDオペレーティングシステム上で正しくビルドできないという問題がありました。Goのランタイムは、OS固有のシステムコールやAPIを直接呼び出すC言語のコードを含んでいます。特にシグナルハンドリングのような低レベルな操作は、OSごとに微妙に異なるインターフェースを持つことがよくあります。
このケースでは、sigprocmask
というシグナルマスクを操作するPOSIX標準の関数が問題の原因でした。Goランタイムがこの関数を呼び出す際に、OpenBSDのsigprocmask
が期待する引数の型や数と、Goランタイムが渡していた引数が一致していなかったため、コンパイルエラーが発生していました。これは、他のOS(例えばLinuxやFreeBSDなど)では問題なく動作していたコードが、OpenBSDの特定のsigprocmask
の実装との互換性の問題によりビルドに失敗していたことを示唆しています。
前提知識の解説
Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理する低レベルなシステムです。これには、ガベージコレクション、スケジューラ(ゴルーチンの管理)、メモリ割り当て、システムコールインターフェースなどが含まれます。Goプログラムは、コンパイル時にこのランタイムとリンクされ、OSと直接対話する部分の多くをランタイムが担います。OS固有の処理は、C言語で書かれたファイル(例: thread_openbsd.c
)で実装されることが一般的です。
OpenBSD
OpenBSDは、セキュリティを最優先に設計されたUNIX系オペレーティングシステムです。厳格なコードレビュー、プロアクティブなセキュリティ対策、そして堅牢なシステム設計で知られています。POSIX標準に準拠していますが、他のUNIX系OSとは異なる独自のシステムコール実装やAPIのシグネチャを持つことが稀にあります。これは、セキュリティや堅牢性を高めるための設計判断によるものです。
sigprocmask
関数
sigprocmask
は、POSIX標準で定義されているシステムコールで、呼び出し元のスレッドのシグナルマスクを検査または変更するために使用されます。シグナルマスクは、スレッドがブロックする(つまり、受信してもすぐに処理しない)シグナルのセットを定義します。
関数の基本的なシグネチャは以下のようになります(一般的なUNIX系システムの場合):
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oldset);
how
: シグナルマスクの変更方法を指定します。SIG_BLOCK
:set
で指定されたシグナルを現在のマスクに追加します。SIG_UNBLOCK
:set
で指定されたシグナルを現在のマスクから削除します。SIG_SETMASK
: 現在のマスクをset
で指定されたシグナルセットに置き換えます。
set
:how
で指定された操作に使用するシグナルセットへのポインタです。oldset
: 変更前のシグナルマスクを格納するためのsigset_t
構造体へのポインタです。不要な場合はNULL
を指定できます。
sigset_t
型
sigset_t
は、シグナルのセットを表すために使用されるデータ型です。通常、ビットマスクとして実装され、各ビットが特定のシグナルに対応します。sigemptyset()
, sigaddset()
, sigdelset()
などの関数を使って操作します。
SIG_SETMASK
sigprocmask
関数のhow
引数に指定する定数の一つで、現在のシグナルマスクをset
引数で指定された新しいシグナルマスクに完全に置き換えることを指示します。
技術的詳細
このコミットの技術的な核心は、OpenBSDにおけるsigprocmask
関数のシグネチャの差異にあります。
一般的なPOSIXシステムでは、sigprocmask
は3つの引数を取ります。int how
, const sigset_t *set
, sigset_t *oldset
です。しかし、OpenBSDの特定のバージョンやコンパイラの構成によっては、sigprocmask
のプロトタイプが異なる解釈をされるか、あるいはGoランタイムがC言語の外部関数インターフェース(FFI)を介してこの関数を呼び出す際に、引数の型推論やポインタの扱いが他のシステムと異なっていた可能性があります。
元のコードでは、runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil, sizeof sigset_none);
と呼び出されていました。ここで注目すべきは、nil
の後にsizeof sigset_none
という4つ目の引数が渡されている点です。
これは、Goランタイムが内部的にsigprocmask
をラップしているruntime·sigprocmask
関数が、引数としてhow
, set
, oldset
, そしてoldset
のサイズ(sizeof sigset_t
)を期待していた可能性を示唆しています。これは、GoのFFI層がCの関数を呼び出す際に、ポインタのサイズ情報を必要とするような設計になっていたか、あるいは特定のOSでのみ必要となる追加の引数を抽象化するためにこのようなラッパー関数が用意されていたためと考えられます。
しかし、OpenBSDの実際のsigprocmask
システムコールは、標準的な3つの引数(how
, set
, oldset
)しか受け付けません。Goランタイムのruntime·sigprocmask
が、内部でOpenBSDのsigprocmask
を呼び出す際に、この余分なsizeof sigset_none
という引数を渡してしまっていたため、引数の不一致が発生し、コンパイルエラーや実行時エラー(未定義動作)を引き起こしていました。
この修正は、OpenBSDのsigprocmask
が期待する引数に合わせて、余分なsizeof sigset_none
引数を削除することで、この不一致を解消しています。これにより、GoランタイムがOpenBSD上で正しくビルドされ、シグナルハンドリングが意図通りに機能するようになります。
コアとなるコードの変更箇所
変更はsrc/pkg/runtime/thread_openbsd.c
ファイルの一箇所のみです。
--- a/src/pkg/runtime/thread_openbsd.c
+++ b/src/pkg/runtime/thread_openbsd.c
@@ -172,7 +172,7 @@ runtime·minit(void)\n // Initialize signal handling
m->gsignal = runtime·malg(32*1024);\n runtime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024);\
-\truntime·sigprocmask(SIG_SETMASK, &sigset_none, nil, sizeof sigset_none);\
+\truntime·sigprocmask(SIG_SETMASK, sigset_none);\
}\
\
void
コアとなるコードの解説
変更された行は以下の通りです。
変更前:
runtime·sigprocmask(SIG_SETMASK, &sigset_none, nil, sizeof sigset_none);
変更後:
runtime·sigprocmask(SIG_SETMASK, sigset_none);
この変更は、runtime·sigprocmask
関数への呼び出しから、3番目の引数であるnil
(oldset
に相当)と、4番目の引数であるsizeof sigset_none
を削除しています。
nil
の削除:sigprocmask
のoldset
引数は、変更前のシグナルマスクを保存するために使用されます。このケースでは、変更前のマスクを保存する必要がなかったため、nil
(C言語のNULL
に相当)が渡されていました。しかし、OpenBSDのsigprocmask
の実際のシグネチャが、set
引数のみをポインタとして受け取り、oldset
を省略できるような特殊なケースであったか、あるいはGoランタイムのruntime·sigprocmask
ラッパーが、oldset
が不要な場合に引数を省略するような設計になっていた可能性が考えられます。sizeof sigset_none
の削除: これがこの修正の最も重要な部分です。元のコードでは、sigset_none
のサイズを明示的に渡していました。これは、GoのFFIがCの構造体をポインタで渡す際に、そのサイズ情報も同時に渡すことを要求するような、Goランタイム内部の特定の抽象化メカニズムによるものかもしれません。しかし、OpenBSDのsigprocmask
システムコールは、このような追加のサイズ引数を期待しません。この余分な引数を削除することで、runtime·sigprocmask
の呼び出しがOpenBSDのsigprocmask
の期待するシグネチャ(おそらくhow
とset
のみ、またはoldset
がNULL
の場合に省略可能)と一致するようになり、コンパイルエラーが解消されました。
この修正により、GoランタイムはOpenBSD環境で正しくシグナルマスクを設定できるようになり、結果としてGoプログラムのビルドと実行が可能になります。
関連リンク
- Go CL 7312104: https://golang.org/cl/7312104
sigprocmask
(OpenBSD man page - 参考): https://man.openbsd.org/sigprocmask.2 (これは一般的なリンクであり、コミット当時の正確なバージョンではない可能性がありますが、関数のシグネチャの理解に役立ちます。)
参考にした情報源リンク
- https://github.com/golang/go/commit/f1037f0e86d6f02fd29fe859ff0b5ed2ded6ecf5
- Go言語のランタイムに関する一般的な知識
- POSIXシグナルハンドリングおよび
sigprocmask
に関する一般的な知識 - OpenBSDのシステムコールインターフェースに関する一般的な知識
- C言語の外部関数インターフェース(FFI)に関する一般的な知識