[インデックス 18517] ファイルの概要
このコミットは、Go言語のFreeBSD/ARM環境におけるシステムコール処理の修正に関するものです。特に、アラインメントされていない引数を持つシステムコールが正しく機能するように改善されています。
コミット
commit b0db7e870c50a38239c2bd541a862fe544b05350
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Fri Feb 14 12:22:13 2014 +0900
syscall: fix system calls with misaligned arguments on freebsd/arm
This CL enables the current tree to work with FreeBSD 10-STABLE
on ARM EABI platforms, though there are still a few test fails.
Also updates documentation.
LGTM=iant
R=iant, dave
CC=golang-codereviews
https://golang.org/cl/61060044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b0db7e870c50a38239c2bd541a862fe544b05350
元コミット内容
syscall: fix system calls with misaligned arguments on freebsd/arm
この変更は、FreeBSD 10-STABLE上のARM EABIプラットフォームで、アラインメントされていない引数を持つシステムコールが正しく動作するようにします。これにより、現在のGoのコードベースがFreeBSD 10-STABLEで動作するようになりますが、いくつかのテストはまだ失敗する可能性があります。また、関連するドキュメントも更新されています。
変更の背景
このコミットの背景には、FreeBSD 10-STABLEにおけるARMアーキテクチャでのシステムコール呼び出し規約、特に引数のアラインメントに関する問題がありました。ARMアーキテクチャでは、データアクセスにおいて特定のメモリアドレスアラインメントが要求されることが多く、これが守られないとパフォーマンスの低下や、場合によっては不正なメモリアクセスによるクラッシュを引き起こす可能性があります。
Go言語のランタイムは、システムコールを直接呼び出すためにアセンブリコードを使用しています。これは、GoのユーザーレベルのコードとOSカーネルの間の橋渡しをする重要な部分です。FreeBSD 10-STABLEのARM EABI(Embedded Application Binary Interface)環境において、Goランタイムがシステムコールに渡す引数のアラインメントがOSの期待するものと異なっていたため、システムコールが正しく機能しない、あるいは予期せぬ動作をする問題が発生していました。
この問題は、特にスタック上に配置される引数において顕著でした。ARM EABIでは、スタックポインタ(SP)が8バイト境界にアラインされていることを期待する場合があります。Goのシステムコールラッパーがこのアラインメントを維持せずに引数をスタックにプッシュすると、カーネル側で引数を読み取る際に問題が生じ、システムコールが失敗したり、誤った値を処理したりする可能性がありました。
このコミットは、FreeBSD 10-STABLE上でのGoの安定性と互換性を向上させることを目的としています。
前提知識の解説
1. システムコール (Syscall)
システムコールは、ユーザープログラムがオペレーティングシステム(OS)のカーネルが提供するサービスを利用するための唯一の手段です。ファイルI/O、ネットワーク通信、メモリ管理、プロセス制御など、OSの機能にアクセスするために使用されます。Go言語では、syscall
パッケージを通じてこれらのOS機能にアクセスします。
2. ARMアーキテクチャとEABI (Embedded Application Binary Interface)
ARMは、スマートフォン、タブレット、組み込みシステムなど、幅広いデバイスで使用されているCPUアーキテクチャです。EABIは、特定のCPUアーキテクチャ(この場合はARM)上でソフトウェアがどのようにコンパイルされ、実行されるかを定義する標準です。これには、レジスタの使用方法、スタックフレームのレイアウト、関数呼び出し規約、データ型のアラインメントなどが含まれます。
特に、ARM EABIでは、スタックポインタ(SP)が8バイト境界にアラインされていることが期待される場合があります。これは、ARMプロセッサがダブルワード(8バイト)アクセスを効率的に行うため、または特定の命令が8バイトアラインメントを要求するためです。
3. メモリアラインメント
メモリアラインメントとは、データがメモリ内で特定の境界(例えば、4バイト境界、8バイト境界など)に配置されることを指します。CPUは、アラインされたデータにアクセスする方が、アラインされていないデータにアクセスするよりも効率的です。アラインメントされていないアクセスは、パフォーマンスの低下を引き起こしたり、一部のアーキテクチャではハードウェア例外(アラインメントフォルト)を発生させたりすることがあります。
システムコールにおいて引数がスタックを通じて渡される場合、OSカーネルは特定のメモリアラインメントを期待することがあります。Goのランタイムがこの期待に応えられない場合、システムコールは失敗する可能性があります。
4. Goのアセンブリコード (.s
ファイル)
Go言語は、パフォーマンスが重要な部分や、OSとの直接的なインターフェースが必要な部分でアセンブリコードを使用します。src/pkg/syscall/asm_freebsd_arm.s
のようなファイルは、FreeBSD/ARMプラットフォームにおけるシステムコール呼び出しのためのアセンブリルーチンを定義しています。これらのルーチンは、Goの関数呼び出し規約とOSのシステムコール呼び出し規約の間で引数と戻り値を変換する役割を担います。
5. スタックポインタ (SP) とフレームポインタ (FP)
- スタックポインタ (SP): 現在のスタックの最上位(または最下位、アーキテクチャによる)を指すレジスタです。関数呼び出し時に引数をプッシュしたり、ローカル変数を確保したりする際にSPが移動します。
- フレームポインタ (FP): 現在のスタックフレームの基点を指すレジスタです。引数やローカル変数へのアクセスを相対アドレスで行うために使用されます。
Goのアセンブリコードでは、0(FP)
、4(FP)
などの表記で、フレームポインタからのオフセットで引数や戻り値にアクセスします。
技術的詳細
このコミットの主要な変更点は、FreeBSD/ARMにおけるシステムコール呼び出しのアセンブリコード、具体的にはsrc/pkg/syscall/asm_freebsd_arm.s
ファイル内のスタックポインタ(R13レジスタ)の操作方法にあります。
以前の実装では、Syscall6
やSyscall9
のような、より多くの引数を取るシステムコールにおいて、スタックに引数をプッシュする際にADD $24, R13
のような命令を使用していました。これは、スタックポインタを直接操作して、スタック上の引数領域を調整しようとするものです。しかし、この方法では、OSが期待するスタックのアラインメント(特に8バイトアラインメント)が維持されない可能性がありました。
新しい実装では、このスタックポインタの操作方法が変更されています。具体的には、MOVW R13, R4
で現在のスタックポインタの値を一時的にR4レジスタに保存し、MOVW $20(FP), R13
のように、フレームポインタからのオフセットを使ってスタックポインタを直接目的の引数領域に設定しています。システムコール呼び出し後には、MOVW R4, R13
で元のスタックポインタに戻しています。
この変更の意図は、システムコールに渡される引数がスタック上に正しくアラインされるようにすることです。ARM EABIでは、関数呼び出し規約やシステムコール規約において、スタックポインタが特定の境界(例えば8バイト)にアラインされていることを要求することがあります。以前のADD
やSUB
による相対的なスタックポインタ操作では、このアラインメントが崩れる可能性がありましたが、フレームポインタからの絶対的なオフセットでスタックポインタを設定することで、より確実にアラインメントを維持できるようになります。
また、コメントの変更も行われています。以前はint32
型で定義されていたSyscall
関数の引数と戻り値が、uintptr
型に変更されています。これは、Goのuintptr
型がポインタやメモリアドレスを表すために使用され、システムコール引数としてより適切であることを示しています。err
がerrno
に変わっているのも、システムコールが返すエラーコードが通常errno
として知られているためです。
コアとなるコードの変更箇所
変更はsrc/pkg/syscall/asm_freebsd_arm.s
ファイルに集中しています。
--- a/src/pkg/syscall/asm_freebsd_arm.s
+++ b/src/pkg/syscall/asm_freebsd_arm.s
@@ -8,13 +8,13 @@
// System call support for ARM, FreeBSD
//
-// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
-// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
-// func Syscall9(trap int32, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int32)
+// func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, errno uintptr);
+// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr);
+// func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, errno uintptr)
TEXT ·Syscall(SB),NOSPLIT,$0-28
BL runtime·entersyscall(SB)
- MOVW 0(FP), R7 // sigcall num
+ MOVW 0(FP), R7 // syscall number
MOVW 4(FP), R0 // a1
MOVW 8(FP), R1 // a2
MOVW 12(FP), R2 // a3
@@ -23,69 +23,71 @@ TEXT ·Syscall(SB),NOSPLIT,$0-28
BCS error
MOVW R0, 16(FP) // r1
MOVW R1, 20(FP) // r2
- MOVW R2, 24(FP) // err
+ MOVW R2, 24(FP) // errno
BL runtime·exitsyscall(SB)
RET
error:
MOVW $-1, R3
MOVW R3, 16(FP) // r1
MOVW R2, 20(FP) // r2
- MOVW R0, 24(FP) // err
+ MOVW R0, 24(FP) // errno
BL runtime·exitsyscall(SB)
RET
TEXT ·Syscall6(SB),NOSPLIT,$0-40
BL runtime·entersyscall(SB)
- MOVW 0(FP), R7 // sigcall num
+ MOVW 0(FP), R7 // syscall number
MOVW 4(FP), R0 // a1
MOVW 8(FP), R1 // a2
MOVW 12(FP), R2 // a3
MOVW 16(FP), R3 // a4
- ADD $24, R13 // a5 to a6 are passed on stack
+ MOVW R13, R4
+ MOVW $20(FP), R13 // a5 to a6 are passed on stack
SWI $0 // syscall
- SUB $24, R13
+ MOVW R4, R13
MOVW $0, R2
BCS error6
MOVW R0, 28(FP) // r1
MOVW R1, 32(FP) // r2
- MOVW R2, 36(FP) // err
+ MOVW R2, 36(FP) // errno
BL runtime·exitsyscall(SB)
RET
error6:
MOVW $-1, R3
MOVW R3, 28(FP) // r1
MOVW R2, 32(FP) // r2
- MOVW R0, 36(FP) // err
+ MOVW R0, 36(FP) // errno
BL runtime·exitsyscall(SB)
RET
TEXT ·Syscall9(SB),NOSPLIT,$0-52
BL runtime·entersyscall(SB)
- MOVW 0(FP), R7 // sigcall num
+ MOVW 0(FP), R7 // syscall number
MOVW 4(FP), R0 // a1
MOVW 8(FP), R1 // a2
MOVW 12(FP), R2 // a3
MOVW 16(FP), R3 // a4
- ADD $24, R13 // a5 to a9 are passed on stack
+ MOVW R13, R4
+ MOVW $20(FP), R13 // a5 to a9 are passed on stack
SWI $0 // syscall
- SUB $24, R13
+ MOVW R4, R13
MOVW $0, R2
BCS error9
MOVW R0, 40(FP) // r1
MOVW R1, 44(FP) // r2
- MOVW R2, 48(FP) // err
+ MOVW R2, 48(FP) // errno
BL runtime·exitsyscall(SB)
RET
error9:
MOVW $-1, R3
MOVW R3, 40(FP) // r1
MOVW R2, 44(FP) // r2
- MOVW R0, 48(FP) // err
+ MOVW R0, 48(FP) // errno
BL runtime·exitsyscall(SB)
RET
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
- MOVW 0(FP), R7 // sigcall num
+ MOVW 0(FP), R7 // syscall number
MOVW 4(FP), R0 // a1
MOVW 8(FP), R1 // a2
MOVW 12(FP), R2 // a3
@@ -94,33 +96,34 @@ TEXT ·RawSyscall(SB),NOSPLIT,$0-28
BCS errorr
MOVW R0, 16(FP) // r1
MOVW R1, 20(FP) // r2
- MOVW R2, 24(FP) // err
+ MOVW R2, 24(FP) // errno
RET
errorr:
MOVW $-1, R3
MOVW R3, 16(FP) // r1
MOVW R2, 20(FP) // r2
- MOVW R0, 24(FP) // err
+ MOVW R0, 24(FP) // errno
RET
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
- MOVW 0(FP), R7 // sigcall num
+ MOVW 0(FP), R7 // syscall number
MOVW 4(FP), R0 // a1
MOVW 8(FP), R1 // a2
MOVW 12(FP), R2 // a3
MOVW 16(FP), R3 // a4
- ADD $24, R13 // a5 to a6 are passed on stack
+ MOVW R13, R4
+ MOVW $20(FP), R13 // a5 to a6 are passed on stack
SWI $0 // syscall
- SUB $24, R13
+ MOVW R4, R13
MOVW $0, R2
BCS errorr6
MOVW R0, 28(FP) // r1
MOVW R1, 32(FP) // r2
- MOVW R2, 36(FP) // err
+ MOVW R2, 36(FP) // errno
RET
errorr6:
MOVW $-1, R3
MOVW R3, 28(FP) // r1
MOVW R2, 32(FP) // r2
- MOVW R0, 36(FP) // err
+ MOVW R0, 36(FP) // errno
RET
コアとなるコードの解説
このコミットの核心は、Syscall6
、Syscall9
、RawSyscall6
といった複数の引数を取るシステムコールのアセンブリルーチンにおけるスタックポインタ(R13)の操作方法の変更です。
変更前:
ADD $24, R13 // a5 to a6 are passed on stack
SWI $0 // syscall
SUB $24, R13
このコードは、システムコールに渡す引数の一部(a5以降)がスタックに配置されることを想定し、スタックポインタR13を直接ADD
およびSUB
命令で操作していました。ADD $24, R13
はスタックポインタを24バイト分進める(スタックが下方に伸びる場合)ことで、引数用の領域を確保しようとしています。しかし、この相対的な操作では、R13が常に8バイト境界にアラインされているという保証がありませんでした。FreeBSD/ARMのシステムコール規約が8バイトアラインメントを要求する場合、このアラインメントの不一致が問題を引き起こしていました。
変更後:
MOVW R13, R4
MOVW $20(FP), R13 // a5 to a6 are passed on stack
SWI $0 // syscall
MOVW R4, R13
この新しいアプローチでは、以下の点が異なります。
- スタックポインタの保存:
MOVW R13, R4
命令により、現在のスタックポインタR13の値を一時的にR4レジスタに保存します。これは、システムコール呼び出し後に元のスタック状態に戻すために必要です。 - フレームポインタからの絶対オフセット:
MOVW $20(FP), R13
命令が導入されました。これは、フレームポインタ(FP)からの絶対的なオフセット20(FP)
を計算し、そのアドレスを新しいスタックポインタR13として設定します。フレームポインタは通常、関数エントリ時に8バイト境界にアラインされているため、この方法でスタックポインタを設定することで、システムコールに渡される引数領域が確実にアラインされるようになります。20(FP)
は、Syscall6
の場合、trap
(0),a1
(4),a2
(8),a3
(12),a4
(16) の後に続くa5
の開始位置を示唆しています。 - スタックポインタの復元: システムコール呼び出し後、
MOVW R4, R13
命令で、システムコール呼び出し前の元のスタックポインタR13の値をR4から復元します。これにより、Goランタイムのスタックフレームが正しく維持されます。
この変更により、FreeBSD/ARM環境において、GoのシステムコールがOSの期待するスタックアラインメント規約に準拠するようになり、アラインメントに起因する問題が解決されました。
また、関数のシグネチャに関するコメントも更新されています。
func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
からfunc Syscall(trap, a1, a2, a3 uintptr) (r1, r2, errno uintptr);
へ変更。sigcall num
からsyscall number
へ変更。err
からerrno
へ変更。
これらの変更は、GoのシステムコールAPIがより汎用的なuintptr
型を使用し、エラー戻り値が標準的なerrno
という用語で表現されるように、ドキュメントとコードの整合性を高めるものです。uintptr
は、ポインタを保持できる整数型であり、システムコール引数としてアドレスやサイズなど様々な値を渡すのに適しています。
関連リンク
- Go言語の
syscall
パッケージ: https://pkg.go.dev/syscall - ARM EABI (Wikipedia): https://en.wikipedia.org/wiki/ARM_architecture#EABI
- FreeBSD ARM (公式ドキュメントなど): 関連するFreeBSDの公式ドキュメントやメーリングリストの議論が参考になる可能性がありますが、特定のURLはコミットメッセージには含まれていません。
参考にした情報源リンク
- Go言語のソースコード (特に
src/pkg/syscall/asm_freebsd_arm.s
): https://github.com/golang/go/blob/b0db7e870c50a38239c2bd541a862fe544b05350/src/pkg/syscall/asm_freebsd_arm.s - Goのコードレビューシステム (Gerrit): https://golang.org/cl/61060044 (コミットメッセージに記載されているCLリンク)
- ARMアーキテクチャおよびEABIに関する一般的な情報源(例: ARMの公式ドキュメント、組み込みシステム関連の書籍や記事)
- FreeBSDのシステムコールおよびARMポートに関するドキュメント
I have generated the comprehensive technical explanation in Markdown format, following all the instructions, including the chapter structure and detailed explanations. The output is provided directly to standard output.I have generated the comprehensive technical explanation in Markdown format, following all the instructions, including the chapter structure and detailed explanations. The output is provided directly to standard output.