[インデックス 12870] ファイルの概要
このコミットは、GoランタイムのARM Linux版初期化コード(src/pkg/runtime/rt0_linux_arm.s
)における重要な修正です。具体的には、ARM EABI(Embedded Application Binary Interface)のテスト後に、誤って登録されたSIGILL
(不正命令シグナル)ハンドラを解除する処理を追加しています。これにより、EABIテストが完了した後に、システムが通常のシグナル処理に戻ることを保証し、潜在的な問題を防ぎます。
コミット
commit e133ee95384c98aed9306043cda130a4f74df6d5
Author: Quan Yong Zhai <qyzhai@gmail.com>
Date: Tue Apr 10 15:05:22 2012 -0400
runtime: unregister the SIGILL handler after ARM EABI test
Part of issue 3381
R=rsc, minux.ma, dave
CC=golang-dev
https://golang.org/cl/5969064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e133ee95384c98aed9306043cda130a4f74df6d5
元コミット内容
runtime: unregister the SIGILL handler after ARM EABI test
Part of issue 3381
変更の背景
この変更は、Go言語のIssue 3381「runtime: ARM EABI test leaves SIGILL handler installed
」の一部として行われました。
Goランタイムは、ARM Linuxシステム上で起動する際に、そのシステムがEABI(Embedded Application Binary Interface)に準拠しているかどうかを検出するためのテストを実行します。このテストは、意図的に不正な命令(SWI $0
)を実行し、それによって発生するSIGILL
シグナルを捕捉することで行われます。もしSIGILL
が発生すれば、それはシステムがOABI(Old Application Binary Interface)である可能性を示唆し、Goランタイムはそれに応じて動作を調整します。
しかし、このEABIテストのプロセスにおいて、SIGILL
ハンドラが一時的に登録されます。元のコードでは、このハンドラがテスト後に適切に解除されていませんでした。その結果、ランタイムの初期化が完了した後もSIGILL
ハンドラがアクティブなまま残り、予期せぬSIGILL
シグナルがアプリケーションの実行中に発生した場合に、Goランタイムがそれを捕捉してしまい、本来のアプリケーションの動作やデバッグを妨げる可能性がありました。
このコミットは、この問題を解決するために、EABIテストが完了した直後にSIGILL
ハンドラを元の状態に戻す(unregisterする)処理を追加しています。これにより、ランタイムの初期化プロセスがクリーンに完了し、その後のアプリケーションの実行に影響を与えないようにしています。
前提知識の解説
1. ARMアーキテクチャとABI (Application Binary Interface)
- ARM (Advanced RISC Machine): モバイルデバイスや組み込みシステムで広く使用されているRISC(Reduced Instruction Set Computer)プロセッサアーキテクチャです。
- ABI (Application Binary Interface): オペレーティングシステムとアプリケーション、またはアプリケーションの異なるモジュール間で、バイナリレベルでの互換性を保証するための規約のセットです。これには、レジスタの使用方法、関数呼び出し規約、データ型のメモリ配置、システムコールインターフェースなどが含まれます。
- EABI (Embedded Application Binary Interface): ARMアーキテクチャ向けのABIの一種で、特に組み込みシステムやLinuxのようなOSで広く採用されています。EABIは、より効率的なコード生成、より良いパフォーマンス、そしてより標準化されたインターフェースを提供します。
- OABI (Old Application Binary Interface): EABI以前の古いARM Linuxシステムで使用されていたABIです。EABIとは互換性がなく、システムコールやレジスタの使用方法に違いがあります。Goランタイムは、これらの異なるABIに対応するために、起動時にどちらのABIが使用されているかを検出する必要があります。
2. シグナル (Signals)
- シグナル: Unix系OSにおいて、プロセスに対して非同期的にイベントを通知するメカニズムです。シグナルは、エラー条件(例: ゼロ除算、不正なメモリアクセス)、外部イベント(例: Ctrl+Cによる割り込み)、または他のプロセスからの通知など、様々な理由で発生します。
SIGILL
(Illegal Instruction Signal): プロセスが不正な機械語命令を実行しようとしたときにOSが送信するシグナルです。これは、プログラムのバグ、データ破損、またはCPUがサポートしていない命令の実行などによって発生します。- シグナルハンドラ: 特定のシグナルがプロセスに送信されたときに実行される関数です。
sigaction
システムコールなどを使用して登録されます。シグナルハンドラを登録することで、プロセスはシグナルに対してカスタムの応答を行うことができます(例: エラーログの記録、クリーンアップ処理、プログラムの終了)。
3. rt0_linux_arm.s
- Go言語のランタイムには、各OSとアーキテクチャの組み合わせに対応するアセンブリ言語で書かれた初期化コードが含まれています。
rt0_linux_arm.s
は、ARM Linuxシステム向けのGoランタイムの初期化エントリポイントです。 - このファイルは、Goプログラムが実行される前に、スタックの設定、レジスタの初期化、システムコールインターフェースの確立、そしてABIの検出など、低レベルのセットアップを行います。
4. システムコール (syscall
)
- システムコール: アプリケーションがオペレーティングシステムのカーネルサービスを要求するためのインターフェースです。ファイルI/O、メモリ管理、プロセス制御、ネットワーク通信など、OSの機能にアクセスするために使用されます。
sys_sigaction
: Linuxカーネルが提供するシステムコールの一つで、シグナルハンドラを設定または変更するために使用されます。sys_getpid
: 現在のプロセスのプロセスID(PID)を取得するためのシステムコールです。SWI
(Software Interrupt): ARMアーキテクチャにおける命令の一つで、ソフトウェア割り込みを発生させます。Linuxカーネルでは、SWI
命令は通常、システムコールを呼び出すために使用されます。OABIシステムでは、SWI $0
がシステムコールをトリガーする一般的な方法でした。EABIシステムでは、通常はSVC
(Supervisor Call)命令が使用されますが、古いシステムとの互換性のためにSWI
もサポートされることがあります。
技術的詳細
このコミットの核心は、GoランタイムがARM Linuxシステム上でEABIテストを実行する際のシグナルハンドラのライフサイクル管理の改善にあります。
-
SIGILL
ハンドラの登録: Goランタイムは、EABIテストの前にSIGILL
ハンドラを一時的に登録します。これは、sys_sigaction
システムコール(システムコール番号174
)を使用して行われます。MOVW $4, R0 // SIGILL
: シグナル番号(SIGILL
は4)をR0
に設定。MOVW R13, R1 // sa
: シグナルハンドラ構造体のアドレスをR1
に設定。ここではスタックポインタR13
を一時的に使用しています。SUB $16, R13
: スタックを16バイト減らし、sa
構造体を格納するためのスペースを確保。MOVW R13, R2 // old_sa
: 古いシグナルハンドラ構造体を保存するためのポインタをR2
に設定。MOVW $8, R3 // c
: シグナルハンドラ設定のフラグ(SA_RESTORER
など)をR3
に設定。MOVW $174, R7 // sys_sigaction
:sys_sigaction
システムコール番号をR7
に設定。BL oabi_syscall<>(SB)
: OABI互換のシステムコールラッパーを呼び出し、sys_sigaction
を実行。
-
EABIテストの実行:
sys_getpid
システムコール(システムコール番号20
)をSWI $0
命令で呼び出すことで、EABIテストが実行されます。MOVW $20, R7 // sys_getpid
:sys_getpid
システムコール番号をR7
に設定。SWI $0 // this will trigger SIGILL on OABI systems
: この命令が実行されます。もしシステムがOABIであれば、このSWI $0
は不正な命令として扱われ、SIGILL
シグナルが発生します。Goランタイムは、このSIGILL
を捕捉することで、OABIシステムであることを検出します。EABIシステムでは、この命令は通常通り実行されるか、または異なる動作をします。
-
SIGILL
ハンドラの解除(追加された部分): EABIテストが完了した後、一時的に登録されたSIGILL
ハンドラを解除するために、再度sys_sigaction
システムコールが呼び出されます。MOVW $4, R0 // SIGILL
: シグナル番号SIGILL
をR0
に設定。MOVW R13, R1 // sa
: シグナルハンドラ構造体のアドレスをR1
に設定。MOVW $0, R2 // old_sa
:old_sa
を0
に設定することで、ハンドラをデフォルトの状態に戻すことを示唆します(または、以前に保存したold_sa
を復元します)。MOVW $8, R3 // c
: フラグをR3
に設定。MOVW $174, R7 // sys_sigaction
:sys_sigaction
システムコール番号をR7
に設定。SWI $0 // restore signal handler
:SWI $0
を使用してシステムコールを実行し、SIGILL
ハンドラを解除します。ADD $32, R13
: スタックポインタを32バイト進め、一時的に使用したスタック領域を解放します。これは、最初のSUB $16, R13
と、その後のSWI $0
の呼び出しでスタックがさらに使用された可能性を考慮したものです。
この修正により、GoランタイムはEABIテストを安全に実行し、その後のシグナル処理がシステムのデフォルト動作に戻ることを保証します。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/rt0_linux_arm.s
+++ b/src/pkg/runtime/rt0_linux_arm.s
@@ -20,15 +20,23 @@ TEXT _rt0_arm_linux(SB),7,$-4
MOVM.DB.W [R0-R3], (R13)
MOVW $4, R0 // SIGILL
MOVW R13, R1 // sa
- MOVW $0, R2 // old_sa
+ SUB $16, R13
+ MOVW R13, R2 // old_sa
MOVW $8, R3 // c
MOVW $174, R7 // sys_sigaction
BL oabi_syscall<>(SB)
-\tADD \t$16, R13
+\n // do an EABI syscall
+\tMOVW\t$20, R7 // sys_getpid
+\tSWI \t$0 // this will trigger SIGILL on OABI systems
+\t\n+\tMOVW\t$4, R0 // SIGILL
+\tMOVW\tR13, R1 // sa
+\tMOVW\t$0, R2 // old_sa
+\tMOVW\t$8, R3 // c
+\tMOVW\t$174, R7 // sys_sigaction
+\tSWI\t$0 // restore signal handler
+\tADD\t$32, R13
B\t_rt0_arm(SB)
TEXT bad_abi<>(SB),7,$-4
コアとなるコードの解説
変更はsrc/pkg/runtime/rt0_linux_arm.s
ファイル内の_rt0_arm_linux
関数に集中しています。
変更前:
MOVW $4, R0 // SIGILL
MOVW R13, R1 // sa
MOVW $0, R2 // old_sa
MOVW $8, R3 // c
MOVW $174, R7 // sys_sigaction
BL oabi_syscall<>(SB)
ADD $16, R13 ; スタックを元に戻す
// do an EABI syscall
MOVW $20, R7 // sys_getpid
SWI $0 // this will trigger SIGILL on OABI systems
このコードでは、SIGILL
ハンドラを登録した後、スタックポインタR13
をADD $16, R13
で元に戻しています。その後、EABIテストとしてsys_getpid
をSWI $0
で呼び出しています。問題は、このテスト後に登録したSIGILL
ハンドラを解除する処理がないことでした。
変更後:
MOVW $4, R0 // SIGILL
MOVW R13, R1 // sa
SUB $16, R13 ; スタックを16バイト減らす
MOVW R13, R2 // old_sa
MOVW $8, R3 // c
MOVW $174, R7 // sys_sigaction
BL oabi_syscall<>(SB)
// do an EABI syscall
MOVW $20, R7 // sys_getpid
SWI $0 // this will trigger SIGILL on OABI systems
MOVW $4, R0 // SIGILL
MOVW R13, R1 // sa
MOVW $0, R2 // old_sa
MOVW $8, R3 // c
MOVW $174, R7 // sys_sigaction
SWI $0 // restore signal handler
ADD $32, R13 ; スタックを32バイト進める
B _rt0_arm(SB)
変更点とそれぞれの意味は以下の通りです。
-
SUB $16, R13
の追加:SIGILL
ハンドラを登録する前に、スタックポインタR13
を16バイト減らしています。これは、sys_sigaction
システムコールに渡すsa
(sigaction
構造体)やold_sa
(古いsigaction
構造体)のためのスタック上のスペースを確保するためです。以前はR13
をそのままsa
として渡していましたが、これはスタックが上書きされる可能性がありました。SUB
命令で明示的にスペースを確保することで、安全に構造体を配置できるようになります。 -
MOVW R13, R2 // old_sa
の変更: 以前はMOVW $0, R2
としていましたが、SUB $16, R13
で確保したスタック上のアドレスをold_sa
のポインタとしてR2
に設定しています。これにより、sys_sigaction
が古いシグナルハンドラ情報をこのアドレスに書き込むことができるようになります。 -
SIGILL
ハンドラ解除コードの追加: EABIテスト(SWI $0
)の直後に、以下の新しいコードブロックが追加されました。MOVW $4, R0 // SIGILL MOVW R13, R1 // sa MOVW $0, R2 // old_sa MOVW $8, R3 // c MOVW $174, R7 // sys_sigaction SWI $0 // restore signal handler
これは、最初の
SIGILL
ハンドラ登録とほぼ同じシステムコール呼び出しですが、old_sa
に$0
を設定することで、SIGILL
ハンドラをデフォルトの動作に戻す(または、以前に保存したold_sa
の内容を復元する)ことを意図しています。これにより、EABIテストのために一時的に設定されたハンドラが解除され、ランタイムの初期化がクリーンに完了します。 -
ADD $32, R13
の変更: 以前はADD $16, R13
でスタックを元に戻していましたが、新しいコードではADD $32, R13
に変更されています。これは、最初のSUB $16, R13
で確保した16バイトと、SWI $0
システムコール呼び出しによってさらにスタックが使用された可能性を考慮し、合計で32バイトのスタック領域を解放するためです。これにより、スタックポインタが適切にクリーンアップされ、その後の処理に影響を与えないようにしています。
これらの変更により、GoランタイムはARM Linuxシステム上でEABIテストをより堅牢かつ安全に実行し、テスト後にシグナルハンドラを適切にクリーンアップすることで、アプリケーションの安定性と予測可能性を向上させています。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/e133ee95384c98aed9306043cda130a4f74df6d5
- Gerrit Change-Id: https://golang.org/cl/5969064
- Go Issue 3381:
runtime: ARM EABI test leaves SIGILL handler installed
(このコミットの背景となったIssue) - 検索結果から推測されるリンク: https://github.com/golang/go/issues/3381 (GoのIssueトラッカーは通常GitHubに移行しています)
参考にした情報源リンク
- ARM ABI (Application Binary Interface) の概要:
- Linuxシグナルプログラミング:
- ARMアセンブリ言語とシステムコール:
- https://www.arm.com/ (ARM公式ドキュメント)
- https://en.wikipedia.org/wiki/SWI_instruction
- https://www.kernel.org/doc/html/latest/arm/syscalls.html (Linux ARMシステムコール)
- Go言語のランタイムソースコード:
- https://github.com/golang/go/tree/master/src/runtime
src/pkg/runtime/rt0_linux_arm.s
(Go 1.0時代のパス) -> 現在はsrc/runtime/rt0_linux_arm.s
に相当します。
- Go Issue 3381 (検索結果に基づく):
- https://github.com/golang/go/issues/3381# [インデックス 12870] ファイルの概要
このコミットは、GoランタイムのARM Linux版初期化コード(src/pkg/runtime/rt0_linux_arm.s
)における重要な修正です。具体的には、ARM EABI(Embedded Application Binary Interface)のテスト後に、誤って登録されたSIGILL
(不正命令シグナル)ハンドラを解除する処理を追加しています。これにより、EABIテストが完了した後に、システムが通常のシグナル処理に戻ることを保証し、潜在的な問題を防ぎます。
コミット
commit e133ee95384c98aed9306043cda130a4f74df6d5
Author: Quan Yong Zhai <qyzhai@gmail.com>
Date: Tue Apr 10 15:05:22 2012 -0400
runtime: unregister the SIGILL handler after ARM EABI test
Part of issue 3381
R=rsc, minux.ma, dave
CC=golang-dev
https://golang.org/cl/5969064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e133ee95384c98aed9306043cda130a4f74df6d5
元コミット内容
runtime: unregister the SIGILL handler after ARM EABI test
Part of issue 3381
変更の背景
この変更は、Go言語のIssue 3381「runtime: ARM EABI test leaves SIGILL handler installed
」の一部として行われました。
Goランタイムは、ARM Linuxシステム上で起動する際に、そのシステムがEABI(Embedded Application Binary Interface)に準拠しているかどうかを検出するためのテストを実行します。このテストは、意図的に不正な命令(SWI $0
)を実行し、それによって発生するSIGILL
シグナルを捕捉することで行われます。もしSIGILL
が発生すれば、それはシステムがOABI(Old Application Binary Interface)である可能性を示唆し、Goランタイムはそれに応じて動作を調整します。
しかし、このEABIテストのプロセスにおいて、SIGILL
ハンドラが一時的に登録されます。元のコードでは、このハンドラがテスト後に適切に解除されていませんでした。その結果、ランタイムの初期化が完了した後もSIGILL
ハンドラがアクティブなまま残り、予期せぬSIGILL
シグナルがアプリケーションの実行中に発生した場合に、Goランタイムがそれを捕捉してしまい、本来のアプリケーションの動作やデバッグを妨げる可能性がありました。
このコミットは、この問題を解決するために、EABIテストが完了した直後にSIGILL
ハンドラを元の状態に戻す(unregisterする)処理を追加しています。これにより、ランタイムの初期化プロセスがクリーンに完了し、その後のアプリケーションの実行に影響を与えないようにしています。
前提知識の解説
1. ARMアーキテクチャとABI (Application Binary Interface)
- ARM (Advanced RISC Machine): モバイルデバイスや組み込みシステムで広く使用されているRISC(Reduced Instruction Set Computer)プロセッサアーキテクチャです。
- ABI (Application Binary Interface): オペレーティングシステムとアプリケーション、またはアプリケーションの異なるモジュール間で、バイナリレベルでの互換性を保証するための規約のセットです。これには、レジスタの使用方法、関数呼び出し規約、データ型のメモリ配置、システムコールインターフェースなどが含まれます。
- EABI (Embedded Application Binary Interface): ARMアーキテクチャ向けのABIの一種で、特に組み込みシステムやLinuxのようなOSで広く採用されています。EABIは、より効率的なコード生成、より良いパフォーマンス、そしてより標準化されたインターフェースを提供します。
- OABI (Old Application Binary Interface): EABI以前の古いARM Linuxシステムで使用されていたABIです。EABIとは互換性がなく、システムコールやレジスタの使用方法に違いがあります。Goランタイムは、これらの異なるABIに対応するために、起動時にどちらのABIが使用されているかを検出する必要があります。
2. シグナル (Signals)
- シグナル: Unix系OSにおいて、プロセスに対して非同期的にイベントを通知するメカニズムです。シグナルは、エラー条件(例: ゼロ除算、不正なメモリアクセス)、外部イベント(例: Ctrl+Cによる割り込み)、または他のプロセスからの通知など、様々な理由で発生します。
SIGILL
(Illegal Instruction Signal): プロセスが不正な機械語命令を実行しようとしたときにOSが送信するシグナルです。これは、プログラムのバグ、データ破損、またはCPUがサポートしていない命令の実行などによって発生します。- シグナルハンドラ: 特定のシグナルがプロセスに送信されたときに実行される関数です。
sigaction
システムコールなどを使用して登録されます。シグナルハンドラを登録することで、プロセスはシグナルに対してカスタムの応答を行うことができます(例: エラーログの記録、クリーンアップ処理、プログラムの終了)。
3. rt0_linux_arm.s
- Go言語のランタイムには、各OSとアーキテクチャの組み合わせに対応するアセンブリ言語で書かれた初期化コードが含まれています。
rt0_linux_arm.s
は、ARM Linuxシステム向けのGoランタイムの初期化エントリポイントです。 - このファイルは、Goプログラムが実行される前に、スタックの設定、レジスタの初期化、システムコールインターフェースの確立、そしてABIの検出など、低レベルのセットアップを行います。
4. システムコール (syscall
)
- システムコール: アプリケーションがオペレーティングシステムのカーネルサービスを要求するためのインターフェースです。ファイルI/O、メモリ管理、プロセス制御、ネットワーク通信など、OSの機能にアクセスするために使用されます。
sys_sigaction
: Linuxカーネルが提供するシステムコールの一つで、シグナルハンドラを設定または変更するために使用されます。sys_getpid
: 現在のプロセスのプロセスID(PID)を取得するためのシステムコールです。SWI
(Software Interrupt): ARMアーキテクチャにおける命令の一つで、ソフトウェア割り込みを発生させます。Linuxカーネルでは、SWI
命令は通常、システムコールを呼び出すために使用されます。OABIシステムでは、SWI $0
がシステムコールをトリガーする一般的な方法でした。EABIシステムでは、通常はSVC
(Supervisor Call)命令が使用されますが、古いシステムとの互換性のためにSWI
もサポートされることがあります。
技術的詳細
このコミットの核心は、GoランタイムがARM Linuxシステム上でEABIテストを実行する際のシグナルハンドラのライフサイクル管理の改善にあります。
-
SIGILL
ハンドラの登録: Goランタイムは、EABIテストの前にSIGILL
ハンドラを一時的に登録します。これは、sys_sigaction
システムコール(システムコール番号174
)を使用して行われます。MOVW $4, R0 // SIGILL
: シグナル番号(SIGILL
は4)をR0
に設定。MOVW R13, R1 // sa
: シグナルハンドラ構造体のアドレスをR1
に設定。ここではスタックポインタR13
を一時的に使用しています。SUB $16, R13
: スタックを16バイト減らし、sa
構造体を格納するためのスペースを確保。MOVW R13, R2 // old_sa
: 古いシグナルハンドラ構造体を保存するためのポインタをR2
に設定。MOVW $8, R3 // c
: シグナルハンドラ設定のフラグ(SA_RESTORER
など)をR3
に設定。MOVW $174, R7 // sys_sigaction
:sys_sigaction
システムコール番号をR7
に設定。BL oabi_syscall<>(SB)
: OABI互換のシステムコールラッパーを呼び出し、sys_sigaction
を実行。
-
EABIテストの実行:
sys_getpid
システムコール(システムコール番号20
)をSWI $0
命令で呼び出すことで、EABIテストが実行されます。MOVW $20, R7 // sys_getpid
:sys_getpid
システムコール番号をR7
に設定。SWI $0 // this will trigger SIGILL on OABI systems
: この命令が実行されます。もしシステムがOABIであれば、このSWI $0
は不正な命令として扱われ、SIGILL
シグナルが発生します。Goランタイムは、このSIGILL
を捕捉することで、OABIシステムであることを検出します。EABIシステムでは、この命令は通常通り実行されるか、または異なる動作をします。
-
SIGILL
ハンドラの解除(追加された部分): EABIテストが完了した後、一時的に登録されたSIGILL
ハンドラを解除するために、再度sys_sigaction
システムコールが呼び出されます。MOVW $4, R0 // SIGILL
: シグナル番号SIGILL
をR0
に設定。MOVW R13, R1 // sa
: シグナルハンドラ構造体のアドレスをR1
に設定。MOVW $0, R2 // old_sa
:old_sa
を0
に設定することで、ハンドラをデフォルトの状態に戻すことを示唆します(または、以前に保存したold_sa
を復元します)。MOVW $8, R3 // c
: フラグをR3
に設定。MOVW $174, R7 // sys_sigaction
:sys_sigaction
システムコール番号をR7
に設定。SWI $0 // restore signal handler
:SWI $0
を使用してシステムコールを実行し、SIGILL
ハンドラを解除します。ADD $32, R13
: スタックポインタを32バイト進め、一時的に使用したスタック領域を解放します。これは、最初のSUB $16, R13
と、その後のSWI $0
の呼び出しでスタックがさらに使用された可能性を考慮したものです。
この修正により、GoランタイムはEABIテストを安全に実行し、その後のシグナル処理がシステムのデフォルト動作に戻ることを保証します。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/rt0_linux_arm.s
+++ b/src/pkg/runtime/rt0_linux_arm.s
@@ -20,15 +20,23 @@ TEXT _rt0_arm_linux(SB),7,$-4
MOVM.DB.W [R0-R3], (R13)
MOVW $4, R0 // SIGILL
MOVW R13, R1 // sa
- MOVW $0, R2 // old_sa
+ SUB $16, R13
+ MOVW R13, R2 // old_sa
MOVW $8, R3 // c
MOVW $174, R7 // sys_sigaction
BL oabi_syscall<>(SB)
-\tADD \t$16, R13
+\n // do an EABI syscall
+\tMOVW\t$20, R7 // sys_getpid
+\tSWI \t$0 // this will trigger SIGILL on OABI systems
+\t\n+\tMOVW\t$4, R0 // SIGILL
+\tMOVW\tR13, R1 // sa
+\tMOVW\t$0, R2 // old_sa
+\tMOVW\t$8, R3 // c
+\tMOVW\t$174, R7 // sys_sigaction
+\tSWI\t$0 // restore signal handler
+\tADD\t$32, R13
B\t_rt0_arm(SB)
TEXT bad_abi<>(SB),7,$-4
コアとなるコードの解説
変更はsrc/pkg/runtime/rt0_linux_arm.s
ファイル内の_rt0_arm_linux
関数に集中しています。
変更前:
MOVW $4, R0 // SIGILL
MOVW R13, R1 // sa
MOVW $0, R2 // old_sa
MOVW $8, R3 // c
MOVW $174, R7 // sys_sigaction
BL oabi_syscall<>(SB)
ADD $16, R13 ; スタックを元に戻す
// do an EABI syscall
MOVW $20, R7 // sys_getpid
SWI $0 // this will trigger SIGILL on OABI systems
このコードでは、SIGILL
ハンドラを登録した後、スタックポインタR13
をADD $16, R13
で元に戻しています。その後、EABIテストとしてsys_getpid
をSWI $0
で呼び出しています。問題は、このテスト後に登録したSIGILL
ハンドラを解除する処理がないことでした。
変更後:
MOVW $4, R0 // SIGILL
MOVW R13, R1 // sa
SUB $16, R13 ; スタックを16バイト減らす
MOVW R13, R2 // old_sa
MOVW $8, R3 // c
MOVW $174, R7 // sys_sigaction
BL oabi_syscall<>(SB)
// do an EABI syscall
MOVW $20, R7 // sys_getpid
SWI $0 // this will trigger SIGILL on OABI systems
MOVW $4, R0 // SIGILL
MOVW R13, R1 // sa
MOVW $0, R2 // old_sa
MOVW $8, R3 // c
MOVW $174, R7 // sys_sigaction
SWI $0 // restore signal handler
ADD $32, R13 ; スタックを32バイト進める
B _rt0_arm(SB)
変更点とそれぞれの意味は以下の通りです。
-
SUB $16, R13
の追加:SIGILL
ハンドラを登録する前に、スタックポインタR13
を16バイト減らしています。これは、sys_sigaction
システムコールに渡すsa
(sigaction
構造体)やold_sa
(古いsigaction
構造体)のためのスタック上のスペースを確保するためです。以前はR13
をそのままsa
として渡していましたが、これはスタックが上書きされる可能性がありました。SUB
命令で明示的にスペースを確保することで、安全に構造体を配置できるようになります。 -
MOVW R13, R2 // old_sa
の変更: 以前はMOVW $0, R2
としていましたが、SUB $16, R13
で確保したスタック上のアドレスをold_sa
のポインタとしてR2
に設定しています。これにより、sys_sigaction
が古いシグナルハンドラ情報をこのアドレスに書き込むことができるようになります。 -
SIGILL
ハンドラ解除コードの追加: EABIテスト(SWI $0
)の直後に、以下の新しいコードブロックが追加されました。MOVW $4, R0 // SIGILL MOVW R13, R1 // sa MOVW $0, R2 // old_sa MOVW $8, R3 // c MOVW $174, R7 // sys_sigaction SWI $0 // restore signal handler
これは、最初の
SIGILL
ハンドラ登録とほぼ同じシステムコール呼び出しですが、old_sa
に$0
を設定することで、SIGILL
ハンドラをデフォルトの動作に戻す(または、以前に保存したold_sa
の内容を復元する)ことを意図しています。これにより、EABIテストのために一時的に設定されたハンドラが解除され、ランタイムの初期化がクリーンに完了します。 -
ADD $32, R13
の変更: 以前はADD $16, R13
でスタックを元に戻していましたが、新しいコードではADD $32, R13
に変更されています。これは、最初のSUB $16, R13
で確保した16バイトと、SWI $0
システムコール呼び出しによってさらにスタックが使用された可能性を考慮し、合計で32バイトのスタック領域を解放するためです。これにより、スタックポインタが適切にクリーンアップされ、その後の処理に影響を与えないようにしています。
これらの変更により、GoランタイムはARM Linuxシステム上でEABIテストをより堅牢かつ安全に実行し、テスト後にシグナルハンドラを適切にクリーンアップすることで、アプリケーションの安定性と予測可能性を向上させています。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/e133ee95384c98aed9306043cda130a4f74df6d5
- Gerrit Change-Id: https://golang.org/cl/5969064
- Go Issue 3381:
runtime: ARM EABI test leaves SIGILL handler installed
(このコミットの背景となったIssue) - 検索結果から推測されるリンク: https://github.com/golang/go/issues/3381 (GoのIssueトラッカーは通常GitHubに移行しています)
参考にした情報源リンク
- ARM ABI (Application Binary Interface) の概要:
- Linuxシグナルプログラミング:
- ARMアセンブリ言語とシステムコール:
- https://www.arm.com/ (ARM公式ドキュメント)
- https://en.wikipedia.org/wiki/SWI_instruction
- https://www.kernel.org/doc/html/latest/arm/syscalls.html (Linux ARMシステムコール)
- Go言語のランタイムソースコード:
- https://github.com/golang/go/tree/master/src/runtime
src/pkg/runtime/rt0_linux_arm.s
(Go 1.0時代のパス) -> 現在はsrc/runtime/rt0_linux_arm.s
に相当します。
- Go Issue 3381 (検索結果に基づく):