[インデックス 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>:op1とop2を比較し、ステータスフラグを設定します。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システムコールが失敗した場合の堅牢なエラーチェック機構の導入です。
-
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ランタイムが回復不能なシステムコールエラーに遭遇した際に、プログラムを確実に異常終了させるためのメカニズムとして機能します。
-
システムコールエラーチェックのロジック:
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, R0mmapシステムコールは、失敗した場合にMAP_FAILED(通常は(void *)-1、32ビットでは0xFFFFFFFF)を返します。しかし、Linuxカーネルのシステムコール規約では、エラーは負のerrno値として返されることもあります。RSB.HI $0, R0命令は、CMP命令の結果、R0がR6より大きい(つまり、R0が負のエラーコードである)場合に実行されます。この命令はR0 = 0 - R0を計算します。これにより、例えばR0が-14(EFAULT)であれば14に変換され、正のerrno値として扱われます。これは、mmapがMAP_FAILEDを返す場合と、他のシステムコールのように負のerrnoを返す場合の両方に対応するためのロジックと考えられます。 -
munmap,madvise,sigaltstackの場合:BL.HI\tnotok<>(SB)これらのシステムコールでは、
CMP R6, R0の結果、R0がR6より大きい(負のエラーコードである)場合に、notok<>関数に分岐し、プログラムをクラッシュさせます。これは、これらのシステムコールの失敗がランタイムにとって回復不能な致命的エラーと見なされていることを示しています。
-
-
コメントの更新:
- 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: エラーを示す閾値0xfffff001をR6レジスタにロードします。CMP R6, R0: システムコールの戻り値(R0)とR6を比較します。RSB.HI $0, R0:R0がR6より大きい(HI条件、つまりR0が負のエラーコードである)場合にのみ実行されます。この命令はR0 = 0 - R0を計算し、負のエラーコードを対応する正のerrno値に変換します。これは、mmapがMAP_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: エラーを示す閾値0xfffff001をR6レジスタにロードします。CMP R6, R0: システムコールの戻り値(R0)とR6を比較します。BL.HI notok<>(SB):R0がR6より大きい(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環境における堅牢性を大幅に向上させ、システムコールエラー発生時の挙動をより予測可能で安全なものにすることを目的としています。
関連リンク
- Go言語公式ドキュメント: https://go.dev/
- ARMアーキテクチャリファレンスマニュアル (ARMv7-A/R): https://developer.arm.com/documentation/ddi0406/latest/
- Linuxシステムコール: https://man7.org/linux/man-pages/man2/syscalls.2.html
- Goのソースコードリポジトリ: https://github.com/golang/go
参考にした情報源リンク
- ARM Assembly Language Programming: https://www.arm.com/ (ARMの公式ドキュメントや開発者向けリソース)
- Linux man pages (e.g.,
mmap(2),errno(3)): https://man7.org/linux/man-pages/ - Go言語のソースコード (特に
src/runtimeディレクトリ): https://github.com/golang/go/tree/master/src/runtime - Go CL (Code Review) 5696047: https://golang.org/cl/5696047 (元の変更提案ページ)
- Stack Overflowや技術ブログ記事 (ARMアセンブリ、Linuxシステムコールエラー処理に関する一般的な情報)