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

[インデックス 12177] ファイルの概要

このコミットは、Go言語のランタイムにおけるARM Linuxアーキテクチャ固有のシステムコール処理に関するものです。具体的には、src/pkg/runtime/sys_linux_arm.sというアセンブリファイルが変更されており、システムコールが失敗した場合のチェック機構が追加されています。これにより、ランタイムの堅牢性が向上し、システムコールエラーが適切に処理されるようになります。また、関連するコメントの更新も行われています。

コミット

commit 1bddfb52031fd6bbe602e0d586758eac9ffd592a
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Thu Feb 23 15:43:14 2012 -0500

    runtime: check for ARM syscall failures
        While we are at it, also update some comments.
        Tested on Linux/ARM builder.
    
    R=rsc, golang-dev
    CC=golang-dev
    https://golang.org/cl/5696047

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/1bddfb52031fd6bbe602e0d586758eac9ffd592a

元コミット内容

runtime: check for ARM syscall failures
    While we are at it, also update some comments.
    Tested on Linux/ARM builder.

R=rsc, golang-dev
CC=golang-dev
https://golang.org/cl/5696047

変更の背景

Go言語のランタイムは、OSの機能を利用するためにシステムコールを頻繁に発行します。システムコールは、ファイル操作、メモリ管理、プロセス制御など、OSカーネルが提供する低レベルなサービスへのインターフェースです。これらのシステムコールは、様々な理由(例えば、メモリ不足、不正な引数、パーミッションエラーなど)で失敗する可能性があります。

このコミット以前のGoランタイムのARM Linux向けアセンブリコードでは、一部のシステムコール(mmap, munmap, madvise, sigaltstackなど)の戻り値に対するエラーチェックが不十分でした。システムコールが失敗した場合、通常は負の値(エラーコード)を返しますが、この戻り値が適切にチェックされないと、ランタイムは不正な状態に陥り、予期せぬ動作やクラッシュを引き起こす可能性がありました。

この変更の背景には、ランタイムの堅牢性と信頼性を向上させる目的があります。システムコールが失敗した際にそれを検出し、適切なエラー処理(この場合は致命的なエラーとしてプログラムを終了させる)を行うことで、より安定したGoプログラムの実行環境を提供できるようになります。また、コメントの更新は、コードの可読性と保守性を高めるための副次的な変更です。特に、ARMのバイナリインターフェースであるEABIとOABIのサポート状況を明確にすることは、将来的な開発やデバッグにおいて重要な情報となります。

前提知識の解説

ARMアーキテクチャとアセンブリ言語

ARM (Advanced RISC Machine) は、モバイルデバイスや組み込みシステムで広く使用されているCPUアーキテクチャです。Go言語のランタイムは、パフォーマンスとOSとの密接な連携のために、特定の部分でアセンブリ言語を使用します。

  • レジスタ: ARMプロセッサには、R0からR15までの汎用レジスタがあります。
    • R0: 関数呼び出しの最初の引数、または関数の戻り値を格納します。
    • R6, R9: このコードでは一時的な値を格納するために使用されます。
    • R7: Linuxシステムコール番号を格納するために使用されます。
    • PC (Program Counter): 現在実行中の命令のアドレスを指します。
  • 命令:
    • MOVW <dest>, <src>: srcの値をdestに移動します。
    • CMP <op1>, <op2>: op1op2を比較し、ステータスフラグを設定します。
    • SWI $0 (Software Interrupt): システムコールをトリガーします。R7レジスタにシステムコール番号、R0からR6に引数を設定して呼び出します。
    • B <label>: 無条件分岐(ジャンプ)します。
    • BL <label>: 分岐し、戻りアドレスをLR (Link Register) に保存します。関数呼び出しに使用されます。
    • .HI (Higher): 条件コードサフィックスの一つで、比較結果が「より大きい」場合に命令を実行します。符号なし比較では>、符号あり比較では>に対応します。この文脈では、システムコールの戻り値が負のエラーコードであるかどうかのチェックに使われます。
    • RSB <dest>, <op1>, <op2> (Reverse Subtract): dest = op2 - op1 を計算します。

Linuxシステムコールとエラー処理

Linuxカーネルは、ユーザー空間のプログラムがOSの機能を利用するためのインターフェースとしてシステムコールを提供します。

  • システムコールの呼び出し規約: ARM Linuxでは、システムコール番号はR7レジスタに、引数はR0からR6レジスタに渡されます。システムコールはSWI命令によって実行されます。
  • 戻り値: システムコールは通常、成功した場合は0以上の値をR0レジスタに返します。失敗した場合は、負の値(通常は-errno、ここでerrnoはエラーの種類を示す正の整数)をR0レジスタに返します。例えば、EFAULT (不正なアドレス) は-14として返されることがあります。
  • MAP_FAILED: mmapシステムコールの場合、メモリマップに失敗すると、通常は(void *)-1が返されます。これは32ビットシステムでは0xFFFFFFFFに相当します。このコミットでチェックされている0xfffff001は、特定のシステムコールエラーを示すマジックナンバー、またはエラーコードの範囲を示す閾値として使用されています。

Goランタイム

Goランタイムは、Goプログラムの実行を管理する低レベルなコンポーネントです。ガベージコレクション、スケジューリング、メモリ管理、システムコールインターフェースなどを担当します。アセンブリコードは、OSとの直接的なやり取りや、特定のアーキテクチャに最適化された処理を行うために使用されます。

EABIとOABI

ARMアーキテクチャには、異なるバイナリインターフェース(ABI: Application Binary Interface)が存在します。

  • EABI (Embedded Application Binary Interface): 組み込みシステム向けに設計されたABIで、現代のARM Linuxシステムで広く採用されています。
  • OABI (Old Application Binary Interface): EABI以前の古いABIです。

Goランタイムは、このコミットの時点ではEABIのみをサポートしており、OABIのサポートは行っていません。コメントの更新はこの事実を明確にしています。

関連するシステムコール

  • mmap (memory map): ファイルやデバイスをプロセスのアドレス空間にマッピングしたり、匿名メモリ領域を確保したりするために使用されます。
  • munmap (memory unmap): mmapで確保したメモリ領域のマッピングを解除します。
  • madvise (memory advise): カーネルにメモリ使用に関するヒント(例えば、このメモリ領域はすぐにアクセスされる、またはアクセスされないなど)を与えます。
  • sigaltstack (signal alternate stack): シグナルハンドラが実行されるための代替スタックを設定します。

技術的詳細

このコミットの主要な変更点は、ARM Linuxシステムコールが失敗した場合の堅牢なエラーチェック機構の導入です。

  1. notok<> 関数の追加: この関数は、システムコールが致命的なエラーを返した場合に呼び出されます。

    TEXT notok<>(SB),7,$0
    	MOVW	$0, R9
    	MOVW	R9, (R9)
    	B   	0(PC)
    
    • MOVW $0, R9: レジスタR9に値0をロードします。
    • MOVW R9, (R9): R9の値(つまり0)を、R9が指すアドレス(つまりアドレス0)に書き込もうとします。アドレス0は通常、NULLポインタであり、ユーザー空間のプログラムが書き込みを試みるとセグメンテーション違反(Segmentation Fault)が発生し、プログラムがクラッシュします。
    • B 0(PC): 現在のプログラムカウンタ(PC)からオフセット0の位置に無条件分岐します。これは無限ループを引き起こすか、あるいは不正な命令フェッチによりクラッシュを加速させる可能性があります。 このnotok<>関数は、Goランタイムが回復不能なシステムコールエラーに遭遇した際に、プログラムを確実に異常終了させるためのメカニズムとして機能します。
  2. システムコールエラーチェックのロジック: mmap, munmap, madvise, sigaltstackといったシステムコールの呼び出し後、以下の共通のパターンでエラーチェックが行われます。

    	SWI	$0             ; システムコール実行
    	MOVW	$0xfffff001, R6 ; エラー閾値をR6に設定
    	CMP	\tR6, R0       ; システムコールの戻り値(R0)とR6を比較
    	; ... エラー処理 ...
    
    • $0xfffff001という値は、ARM Linuxシステムコールにおいて、エラーを示す負の戻り値の範囲を識別するための閾値として使用されています。システムコールが成功した場合、R0は通常0以上の値を返します。失敗した場合、R0は負の値(例えば-1-errno)を返します。32ビット符号付き整数として0xfffff001は非常に大きな負の数です。R0がこの値よりも「大きい」(HI条件)ということは、R0が負のエラーコードであることを意味します。

    • mmap の場合:

      	RSB.HI\t$0, R0
      

      mmapシステムコールは、失敗した場合にMAP_FAILED(通常は(void *)-1、32ビットでは0xFFFFFFFF)を返します。しかし、Linuxカーネルのシステムコール規約では、エラーは負のerrno値として返されることもあります。 RSB.HI $0, R0命令は、CMP命令の結果、R0R6より大きい(つまり、R0が負のエラーコードである)場合に実行されます。この命令はR0 = 0 - R0を計算します。これにより、例えばR0-14EFAULT)であれば14に変換され、正のerrno値として扱われます。これは、mmapMAP_FAILEDを返す場合と、他のシステムコールのように負のerrnoを返す場合の両方に対応するためのロジックと考えられます。

    • munmap, madvise, sigaltstack の場合:

      	BL.HI\tnotok<>(SB)
      

      これらのシステムコールでは、CMP R6, R0の結果、R0R6より大きい(負のエラーコードである)場合に、notok<>関数に分岐し、プログラムをクラッシュさせます。これは、これらのシステムコールの失敗がランタイムにとって回復不能な致命的エラーと見なされていることを示しています。

  3. コメントの更新:

    • OABIに関する古いコメントが削除され、EABIのみをサポートしていることが明確にされました。
    • cas<>関数のコメント内のパスが修正され、より正確な参照先が示されました。これはコードの保守性と理解を向上させます。

これらの変更により、GoランタイムはARM Linux環境下でシステムコールエラーをより適切に検出し、プログラムの異常終了を通じて問題の早期発見とデバッグを促進するようになります。

コアとなるコードの変更箇所

--- a/src/pkg/runtime/sys_linux_arm.s
+++ b/src/pkg/runtime/sys_linux_arm.s
@@ -8,10 +8,7 @@
 
 #include "zasm_GOOS_GOARCH.h"
 
-// OABI
-//#define SYS_BASE 0x00900000
-//
-// EABI
+// for EABI, as we don't support OABI
 #define SYS_BASE 0x0
 
 #define SYS_exit (SYS_BASE + 1)
@@ -40,6 +37,11 @@
 #define ARM_BASE (SYS_BASE + 0x0f0000)
 #define SYS_ARM_cacheflush (ARM_BASE + 2)
 
+TEXT notok<>(SB),7,$0
+\tMOVW\t$0, R9
+\tMOVW\tR9, (R9)
+\tB   \t0(PC)
+
 TEXT runtime·open(SB),7,$0
 \tMOVW\t0(FP), R0
 \tMOVW\t4(FP), R1
@@ -104,6 +106,9 @@ TEXT runtime·mmap(SB),7,$0
 	MOVW	20(FP), R5
 	MOVW	$SYS_mmap2, R7
 	SWI	$0
+	MOVW	$0xfffff001, R6
+	CMP	\tR6, R0
+	RSB.HI\t$0, R0
 	RET
 
 TEXT runtime·munmap(SB),7,$0
@@ -111,6 +116,9 @@ TEXT runtime·munmap(SB),7,$0
 	MOVW	4(FP), R1
 	MOVW	$SYS_munmap, R7
 	SWI	$0
+	MOVW	$0xfffff001, R6
+	CMP \tR6, R0
+	BL.HI\tnotok<>(SB)
 	RET
 
 TEXT runtime·madvise(SB),7,$0
@@ -119,6 +127,9 @@ TEXT runtime·madvise(SB),7,$0
 	MOVW	8(FP), R2
 	MOVW	$SYS_madvise, R7
 	SWI	$0
+	MOVW	$0xfffff001, R6
+	CMP \tR6, R0
+	BL.HI\tnotok<>(SB)
 	RET
 
 TEXT runtime·setitimer(SB),7,$0
@@ -270,6 +281,9 @@ TEXT runtime·sigaltstack(SB),7,$0
 	MOVW	4(FP), R1
 	MOVW	$SYS_sigaltstack, R7
 	SWI	$0
+	MOVW	$0xfffff001, R6
+	CMP \tR6, R0
+	BL.HI\tnotok<>(SB)
 	RET
 
 TEXT runtime·sigtramp(SB),7,$24
@@ -333,8 +347,8 @@ TEXT runtime·usleep(SB),7,$12
 	SWI	$0
 	RET
 
-// Use kernel version instead of native armcas in ../../arm.s.
-// See ../../../sync/atomic/asm_linux_arm.s for details.
+// Use kernel version instead of native armcas in asm_arm.s.
+// See ../sync/atomic/asm_linux_arm.s for details.
 TEXT cas<>(SB),7,$0
 	MOVW	$0xffff0fc0, PC
 

コアとなるコードの解説

1. コメントの変更 (行 8-11)

- // OABI
- //#define SYS_BASE 0x00900000
- //
- // EABI
+ // for EABI, as we don't support OABI

古いOABIに関するコメントが削除され、EABIのみをサポートしていることが明確にされました。これは、GoランタイムがARM Linux環境でEABIに準拠していることを明示し、コードの意図を明確にするための変更です。

2. notok<> 関数の追加 (行 37-41)

+TEXT notok<>(SB),7,$0
+\tMOVW\t$0, R9
+\tMOVW\tR9, (R9)
+\tB   \t0(PC)

この新しいアセンブリ関数は、システムコールが致命的なエラーを返した場合に呼び出されます。MOVW R9, (R9)は、レジスタR9に格納された値(この場合は0)を、その値が指すメモリ位置(アドレス0)に書き込もうとします。これは通常、セグメンテーション違反を引き起こし、プログラムをクラッシュさせます。B 0(PC)は、現在のPCからオフセット0の位置に無条件分岐し、無限ループまたは不正な命令フェッチによるクラッシュを保証します。これは、回復不能なエラー状態からの脱出メカニズムとして機能します。

3. runtime·mmap のエラーチェック追加 (行 106-108)

 	SWI	$0
+	MOVW	$0xfffff001, R6
+	CMP	\tR6, R0
+	RSB.HI\t$0, R0
 	RET

mmapシステムコール実行後 (SWI $0)、以下のエラーチェックが追加されました。

  • MOVW $0xfffff001, R6: エラーを示す閾値0xfffff001R6レジスタにロードします。
  • CMP R6, R0: システムコールの戻り値(R0)とR6を比較します。
  • RSB.HI $0, R0: R0R6より大きい(HI条件、つまりR0が負のエラーコードである)場合にのみ実行されます。この命令はR0 = 0 - R0を計算し、負のエラーコードを対応する正のerrno値に変換します。これは、mmapMAP_FAILED(通常-1)を返す場合と、負のerrnoを返す場合の両方に対応するための処理です。

4. runtime·munmap, runtime·madvise, runtime·sigaltstack のエラーチェック追加 (行 116-118, 124-126, 283-285)

 	SWI	$0
+	MOVW	$0xfffff001, R6
+	CMP \tR6, R0
+	BL.HI\tnotok<>(SB)
 	RET

これらのシステムコール実行後も同様にエラーチェックが追加されました。

  • MOVW $0xfffff001, R6: エラーを示す閾値0xfffff001R6レジスタにロードします。
  • CMP R6, R0: システムコールの戻り値(R0)とR6を比較します。
  • BL.HI notok<>(SB): R0R6より大きい(HI条件、つまりR0が負のエラーコードである)場合にのみ実行されます。この命令は、notok<>関数に分岐し、プログラムをクラッシュさせます。これは、これらのシステムコールの失敗がランタイムにとって致命的であると判断されたためです。

5. cas<> 関数のコメント修正 (行 347-348)

-// Use kernel version instead of native armcas in ../../arm.s.
-// See ../../../sync/atomic/asm_linux_arm.s for details.
+// Use kernel version instead of native armcas in asm_arm.s.
+// See ../sync/atomic/asm_linux_arm.s for details.

cas<> (Compare And Swap) 関数に関するコメント内のファイルパスが修正されました。これにより、参照されているアセンブリファイルの場所がより正確になり、コードの理解と保守が容易になります。

これらの変更は、GoランタイムのARM Linux環境における堅牢性を大幅に向上させ、システムコールエラー発生時の挙動をより予測可能で安全なものにすることを目的としています。

関連リンク

参考にした情報源リンク