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

[インデックス 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_SYSCALLPTRACE_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パッケージは「ロックダウン」され、新しい機能の追加や大幅な変更は行われなくなりました。これは、より高レベルでポータブルなosnettimeなどのパッケージが推奨されるためです。低レベルなシステムコールアクセスが必要な新しいコードでは、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プログラムは以下のことが可能になります。

  1. システムコールレベルでのトレース: プロセスがシステムコールを呼び出す直前、またはシステムコールから戻った直後に停止させ、その状態を検査できます。
  2. シグナル配信: 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つの引数を取ります。

    1. request: 実行するptrace操作の種類(例: PTRACE_CONT, PTRACE_SYSCALL)。
    2. pid: 操作対象のトレース対象プロセスのID。
    3. addr: メモリ操作などで使用されるアドレス(このケースでは0)。
    4. 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

参考にした情報源リンク