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

[インデックス 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テストを実行する際のシグナルハンドラのライフサイクル管理の改善にあります。

  1. 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を実行。
  2. 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システムでは、この命令は通常通り実行されるか、または異なる動作をします。
  3. SIGILLハンドラの解除(追加された部分): EABIテストが完了した後、一時的に登録されたSIGILLハンドラを解除するために、再度sys_sigactionシステムコールが呼び出されます。

    • MOVW $4, R0 // SIGILL: シグナル番号SIGILLR0に設定。
    • MOVW R13, R1 // sa: シグナルハンドラ構造体のアドレスをR1に設定。
    • MOVW $0, R2 // old_sa: old_sa0に設定することで、ハンドラをデフォルトの状態に戻すことを示唆します(または、以前に保存した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ハンドラを登録した後、スタックポインタR13ADD $16, R13で元に戻しています。その後、EABIテストとしてsys_getpidSWI $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)

変更点とそれぞれの意味は以下の通りです。

  1. SUB $16, R13 の追加: SIGILLハンドラを登録する前に、スタックポインタR13を16バイト減らしています。これは、sys_sigactionシステムコールに渡すsasigaction構造体)やold_sa(古いsigaction構造体)のためのスタック上のスペースを確保するためです。以前はR13をそのままsaとして渡していましたが、これはスタックが上書きされる可能性がありました。SUB命令で明示的にスペースを確保することで、安全に構造体を配置できるようになります。

  2. MOVW R13, R2 // old_sa の変更: 以前はMOVW $0, R2としていましたが、SUB $16, R13で確保したスタック上のアドレスをold_saのポインタとしてR2に設定しています。これにより、sys_sigactionが古いシグナルハンドラ情報をこのアドレスに書き込むことができるようになります。

  3. 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テストのために一時的に設定されたハンドラが解除され、ランタイムの初期化がクリーンに完了します。

  4. ADD $32, R13 の変更: 以前はADD $16, R13でスタックを元に戻していましたが、新しいコードではADD $32, R13に変更されています。これは、最初のSUB $16, R13で確保した16バイトと、SWI $0システムコール呼び出しによってさらにスタックが使用された可能性を考慮し、合計で32バイトのスタック領域を解放するためです。これにより、スタックポインタが適切にクリーンアップされ、その後の処理に影響を与えないようにしています。

これらの変更により、GoランタイムはARM Linuxシステム上でEABIテストをより堅牢かつ安全に実行し、テスト後にシグナルハンドラを適切にクリーンアップすることで、アプリケーションの安定性と予測可能性を向上させています。

関連リンク

参考にした情報源リンク

このコミットは、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テストを実行する際のシグナルハンドラのライフサイクル管理の改善にあります。

  1. 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を実行。
  2. 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システムでは、この命令は通常通り実行されるか、または異なる動作をします。
  3. SIGILLハンドラの解除(追加された部分): EABIテストが完了した後、一時的に登録されたSIGILLハンドラを解除するために、再度sys_sigactionシステムコールが呼び出されます。

    • MOVW $4, R0 // SIGILL: シグナル番号SIGILLR0に設定。
    • MOVW R13, R1 // sa: シグナルハンドラ構造体のアドレスをR1に設定。
    • MOVW $0, R2 // old_sa: old_sa0に設定することで、ハンドラをデフォルトの状態に戻すことを示唆します(または、以前に保存した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ハンドラを登録した後、スタックポインタR13ADD $16, R13で元に戻しています。その後、EABIテストとしてsys_getpidSWI $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)

変更点とそれぞれの意味は以下の通りです。

  1. SUB $16, R13 の追加: SIGILLハンドラを登録する前に、スタックポインタR13を16バイト減らしています。これは、sys_sigactionシステムコールに渡すsasigaction構造体)やold_sa(古いsigaction構造体)のためのスタック上のスペースを確保するためです。以前はR13をそのままsaとして渡していましたが、これはスタックが上書きされる可能性がありました。SUB命令で明示的にスペースを確保することで、安全に構造体を配置できるようになります。

  2. MOVW R13, R2 // old_sa の変更: 以前はMOVW $0, R2としていましたが、SUB $16, R13で確保したスタック上のアドレスをold_saのポインタとしてR2に設定しています。これにより、sys_sigactionが古いシグナルハンドラ情報をこのアドレスに書き込むことができるようになります。

  3. 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テストのために一時的に設定されたハンドラが解除され、ランタイムの初期化がクリーンに完了します。

  4. ADD $32, R13 の変更: 以前はADD $16, R13でスタックを元に戻していましたが、新しいコードではADD $32, R13に変更されています。これは、最初のSUB $16, R13で確保した16バイトと、SWI $0システムコール呼び出しによってさらにスタックが使用された可能性を考慮し、合計で32バイトのスタック領域を解放するためです。これにより、スタックポインタが適切にクリーンアップされ、その後の処理に影響を与えないようにしています。

これらの変更により、GoランタイムはARM Linuxシステム上でEABIテストをより堅牢かつ安全に実行し、テスト後にシグナルハンドラを適切にクリーンアップすることで、アプリケーションの安定性と予測可能性を向上させています。

関連リンク

参考にした情報源リンク