[インデックス 13728] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにPtraceSyscall
関数を追加するものです。これにより、Linuxのptrace
システムコールにおいてPTRACE_SYSCALL
リクエストを使用できるようになり、デバッガやトレースツールがシステムコールレベルでプロセスの実行をより詳細に制御できるようになります。特に、既存のPtraceSingleStep
がシグナル引数をサポートできないというGo 1のAPI安定性制約を回避しつつ、PTRACE_SYSCALL
の機能を提供することを目的としています。
コミット
commit dd79b330c9115a62a482c655edb7911c8121744c
Author: Dave Cheney <dave@cheney.net>
Date: Sat Sep 1 09:17:14 2012 +1000
syscall: add PtraceSyscall(pid int, signal int)
Fixes #3525.
PTRACE_SYSCALL behaves like PTRACE_CONT and can deliver
a signal to the process. Ideally PtraceSingleStep should
support the signal argument, but its interface is frozen
by Go1.
R=golang-dev, r, rsc
CC=golang-dev
https://golang.org/cl/6353051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dd79b330c9115a62a482c655edb7911c8121744c
元コミット内容
syscall: add PtraceSyscall(pid int, signal int)
Fixes #3525.
PTRACE_SYSCALL behaves like PTRACE_CONT and can deliver
a signal to the process. Ideally PtraceSingleStep should
support the signal argument, but its interface is frozen
by Go1.
変更の背景
この変更の背景には、Go言語のsyscall
パッケージが提供するptrace
関連の機能の限界と、Go 1のAPI安定性保証があります。
ptrace
はLinuxにおける強力なシステムコールであり、デバッガやシステムコールトレーサーのようなツールが他のプロセスの実行を監視・制御するために不可欠です。Goのsyscall
パッケージは、これらの低レベルなOSプリミティブへのインターフェースを提供しています。
コミットメッセージによると、この変更はGoのIssue #3525を修正するものです。このIssueの具体的な内容はコミットメッセージからは直接読み取れませんが、ptrace
の特定の動作、特にシグナル配信に関する問題が関連していると推測されます。
既存のPtraceSingleStep
関数は、トレース対象プロセスを1命令だけ実行させて停止させる機能を提供しますが、シグナル引数を渡すことができませんでした。これは、PTRACE_SYSCALL
がPTRACE_CONT
と同様にシグナルをプロセスに配信できるという特性と対照的です。
Go 1のAPI安定性保証は、Go 1の仕様に準拠して書かれたプログラムが、その仕様のライフタイムを通じて変更なしにコンパイルおよび実行され続けることを保証するものです。これはGo言語の大きな特徴であり、後方互換性を非常に重視しています。この保証のため、一度公開されたAPIのインターフェースは原則として変更されません。したがって、PtraceSingleStep
の既存のインターフェースにシグナル引数を追加することは、Go 1のAPI安定性保証に反するため不可能でした。
この制約の中で、PTRACE_SYSCALL
の機能、特にシグナル配信の能力をGoプログラムから利用できるようにするために、新しい関数PtraceSyscall
を追加するというアプローチが取られました。これにより、既存のAPIを壊すことなく、必要な機能が提供されることになります。
前提知識の解説
ptrace
システムコール
ptrace
はLinuxカーネルが提供するシステムコールで、あるプロセス(トレーサー)が別のプロセス(トレース対象、tracee)の実行を監視・制御することを可能にします。これは主にデバッガ(例: GDB)やシステムコールトレーサー(例: strace
)の実装に利用されます。ptrace
は、トレース対象プロセスのメモリ、レジスタ、システムコールなどを操作する機能を提供します。
ptrace
システムコールは、通常以下のような形式で呼び出されます。
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
request
引数によって、ptrace
に実行させたい操作を指定します。主なリクエストタイプは以下の通りです。
PTRACE_CONT
: 停止しているトレース対象プロセスを再開させます。オプションでシグナルをプロセスに配信できます。プロセスは、シグナル受信、ブレークポイント到達、または別のptrace
リクエストによって再び停止するまで実行を続けます。PTRACE_SINGLESTEP
: 停止しているトレース対象プロセスを再開させ、次の単一命令の実行後に停止させます。これはデバッガが命令単位でステップ実行するために使用します。PTRACE_CONT
と同様に、シグナルを受信した場合も停止します。PTRACE_SYSCALL
: 停止しているトレース対象プロセスを再開させ、次のシステムコールへの入り口、またはシステムコールからの出口で停止させます。これはstrace
のようなツールがシステムコールの呼び出しと戻りを監視するために使用します。PTRACE_CONT
と同様に、オプションでシグナルをプロセスに配信できます。
Go言語のsyscall
パッケージ
Go言語の標準ライブラリに含まれるsyscall
パッケージは、オペレーティングシステムの低レベルなプリミティブへのインターフェースを提供します。これにより、Goプログラムはファイル操作、プロセス管理、ネットワーク通信など、OSカーネルが提供する機能を直接利用できます。
syscall
パッケージはプラットフォーム固有の性質が強く、OSによって利用できる関数やその動作が異なります。例えば、LinuxシステムコールはLinux固有の関数として、Windows APIはWindows固有の関数として提供されます。
しかし、Go 1.4以降、syscall
パッケージは「ロックダウン」され、新しい機能の追加や大幅な変更は行われなくなりました。これは、より高レベルでポータブルなos
、net
、time
などのパッケージが推奨されるためです。低レベルなシステムコールアクセスが必要な新しいコードでは、golang.org/x/sys
外部パッケージの使用が推奨されています。
このコミットが行われた2012年9月時点では、Go 1がリリースされたばかりであり、syscall
パッケージはまだ活発に開発・利用されていました。
Go 1のAPI安定性保証
Go言語は、Go 1のリリース以降、非常に強力なAPI安定性保証を提供しています。これは「Go 1 Compatibility Guarantee」として知られ、Go 1の仕様に準拠して書かれたプログラムは、将来のGo 1.xリリースでも変更なしにコンパイルおよび実行できることを約束するものです。
この保証は、Go言語が長期的な安定性と予測可能性を提供するための基盤となっています。標準ライブラリの主要な部分もこの保証の対象であり、既存のAPIを壊すような変更は原則として行われません。新しい機能は追加されることがありますが、それは既存のコードを壊さない形で行われます。
ただし、セキュリティ上の問題、未定義の動作への依存、バグの修正、またはOSインターフェースの変更など、ごく一部の例外が存在します。syscall
パッケージはOSインターフェースに直接依存するため、OS側の変更によって互換性が保証されない場合があります。しかし、Go言語自体のAPIとしては、この安定性保証が非常に厳格に適用されます。
今回のケースでは、PtraceSingleStep
の関数シグネチャを変更してシグナル引数を追加することが、このGo 1のAPI安定性保証に抵触するため、新しい関数としてPtraceSyscall
が追加されたという背景があります。
技術的詳細
このコミットは、Linuxのptrace
システムコールにおけるPTRACE_SYSCALL
リクエストをGo言語のsyscall
パッケージから利用可能にするものです。
PTRACE_SYSCALL
は、トレース対象プロセスを再開させ、次のシステムコールへの入り口、またはシステムコールからの出口で停止させる機能を提供します。これは、strace
のようなシステムコールトレーサーがプロセスのシステムコール活動を監視するために不可欠な機能です。
PTRACE_SYSCALL
の重要な特性の一つは、PTRACE_CONT
と同様に、再開時にシグナルをトレース対象プロセスに配信できる点です。これは、デバッガが特定のシグナルをプロセスに注入しつつ、システムコールレベルでのステップ実行を継続したい場合に有用です。
コミットメッセージでは、「理想的にはPtraceSingleStep
がシグナル引数をサポートすべきだが、そのインターフェースはGo 1によって凍結されている」と述べられています。これは、PtraceSingleStep
が単一命令実行後の停止を目的としているため、シグナル配信の機能が不足していることを示唆しています。しかし、Go 1のAPI安定性保証により、既存の関数のシグネチャを変更することはできません。
そこで、このコミットでは、PTRACE_SYSCALL
の機能を提供するために、PtraceSyscall
という新しい関数を導入しました。この関数はpid
(プロセスID)とsignal
(配信するシグナル)を引数に取り、内部でptrace(PTRACE_SYSCALL, pid, 0, uintptr(signal))
を呼び出します。
これにより、Goプログラムは以下のことが可能になります。
- システムコールレベルでのトレース: プロセスがシステムコールを呼び出す直前、またはシステムコールから戻った直後に停止させ、その状態を検査できます。
- シグナル配信:
PTRACE_SYSCALL
を介してプロセスにシグナルを配信しつつ、システムコールレベルのトレースを継続できます。これは、例えばデバッガが特定のシグナルを処理しつつ、次のシステムコールまで実行を進めたい場合に役立ちます。
このアプローチは、Go 1のAPI安定性保証を尊重しつつ、必要な低レベル機能を追加するための典型的なパターンです。既存のAPIを変更する代わりに、新しいAPIを追加することで、後方互換性を維持します。
コアとなるコードの変更箇所
変更はsrc/pkg/syscall/syscall_linux.go
ファイルに対して行われました。
--- a/src/pkg/syscall/syscall_linux.go
+++ b/src/pkg/syscall/syscall_linux.go
@@ -735,6 +735,10 @@ func PtraceCont(pid int, signal int) (err error) {
return ptrace(PTRACE_CONT, pid, 0, uintptr(signal))
}
+func PtraceSyscall(pid int, signal int) (err error) {
+ return ptrace(PTRACE_SYSCALL, pid, 0, uintptr(signal))
+}
+
func PtraceSingleStep(pid int) (err error) { return ptrace(PTRACE_SINGLESTEP, pid, 0, 0) }
func PtraceAttach(pid int) (err error) { return ptrace(PTRACE_ATTACH, pid, 0, 0) }
コアとなるコードの解説
追加されたコードは以下の通りです。
func PtraceSyscall(pid int, signal int) (err error) {
return ptrace(PTRACE_SYSCALL, pid, 0, uintptr(signal))
}
この関数は、syscall
パッケージ内で定義されている内部ヘルパー関数ptrace
を呼び出しています。
-
ptrace
: これはGoのsyscall
パッケージがLinuxのptrace
システムコールをラップするために使用する内部関数です。通常、ptrace
システムコールは4つの引数を取ります。request
: 実行するptrace
操作の種類(例:PTRACE_CONT
,PTRACE_SYSCALL
)。pid
: 操作対象のトレース対象プロセスのID。addr
: メモリ操作などで使用されるアドレス(このケースでは0)。data
: リクエストに依存するデータ(このケースではシグナル番号)。
-
PTRACE_SYSCALL
: これはLinuxカーネルが定義する定数で、ptrace
システムコールに「システムコールへの入り口または出口で停止する」という操作を指示します。 -
pid int
: トレース対象プロセスのプロセスIDを整数で受け取ります。 -
signal int
: トレース対象プロセスを再開する際に配信するシグナル番号を整数で受け取ります。この値はuintptr
にキャストされてptrace
関数に渡されます。シグナルを配信しない場合は0を指定します。
このシンプルな追加により、GoプログラムはLinuxのPTRACE_SYSCALL
機能を直接利用できるようになり、デバッガやトレースツールなどの開発において、よりきめ細やかなプロセス制御が可能になります。
関連リンク
- Go Issue #3525: https://github.com/golang/go/issues/3525 (ただし、検索結果からはこのコミットが修正した具体的なIssue 3525は見つかりませんでした。GoのIssueトラッカーは時間の経過とともにURLが変わる可能性があるため、直接的なリンクが見つからない場合があります。)
- Go CL 6353051: https://golang.org/cl/6353051
参考にした情報源リンク
ptrace
man page: https://man7.org/linux/man-pages/man2/ptrace.2.html- Go
syscall
package documentation: https://pkg.go.dev/syscall - Go 1 Compatibility Guarantee: https://go.dev/doc/go1compat
- Linux
ptrace
requests explained: https://nullprogram.com/blog/2018/06/22/ - Understanding
ptrace
: https://www.linuxjournal.com/article/6109 - Go
syscall
package overview: https://reintech.io/blog/golang-syscall-package-overview - Go
syscall
package andgolang.org/x/sys
: https://go.dev/blog/go1.4-syscall - Go 1 compatibility: https://go.dev/blog/compat
- Go 1 compatibility exceptions: https://surfin.sg/go-1-compatibility-exceptions/
PTRACE_SYSCALL
vsPTRACE_CONT
vsPTRACE_SINGLESTEP
: https://stackoverflow.com/questions/10073699/ptrace-syscall-vs-ptrace-cont-vs-ptrace-singlestep