[インデックス 14139] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにFreeBSDオペレーティングシステムとARMアーキテクチャの組み合わせに対するサポートを追加するものです。具体的には、FreeBSD/ARM環境でGoプログラムがシステムコールを直接呼び出せるようにするためのアセンブリコード、Go言語のラッパー、および関連する定数や型定義が追加されています。これにより、Go言語がFreeBSD/ARMプラットフォーム上でより深く、効率的にシステムリソースと対話できるようになります。
コミット
commit 378d7ef59fd25d3003915ee2437de2274a61d505
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Fri Oct 12 16:26:42 2012 +0800
syscall: FreeBSD/ARM support
R=rsc, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/6657048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/378d7ef59fd25d3003915ee2437de2274a61d505
元コミット内容
syscall: FreeBSD/ARM support
R=rsc, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/6657048
変更の背景
Go言語は、その設計思想の一つとして、様々なプラットフォームへの高い移植性を持っています。これは、Goランタイムが各オペレーティングシステム(OS)とアーキテクチャの組み合わせ(OS/Arch)に特化した低レベルのインターフェース、特にシステムコール(syscall)を抽象化して提供することで実現されています。
このコミットが行われた2012年当時、Go言語は既に主要なOS/Archの組み合わせ(例: Linux/amd64, Darwin/amd64など)をサポートしていましたが、FreeBSDとARMアーキテクチャの組み合わせに対する公式なサポートはまだ限定的でした。FreeBSDは堅牢で高性能なUnix系OSであり、ARMアーキテクチャは組み込みシステム、モバイルデバイス、そして近年ではサーバー分野でも広く採用されています。
GoプログラムがFreeBSD/ARM環境で動作し、ファイルI/O、ネットワーク通信、プロセス管理などのOS機能にアクセスするためには、その環境固有のシステムコールを正しく呼び出すメカニズムが必要です。このコミットは、Go言語がFreeBSD/ARMプラットフォーム上で完全に機能し、そのエコシステムを拡大するための基盤を構築することを目的としています。これにより、開発者はFreeBSD/ARMデバイス上でGoアプリケーションを開発・デプロイできるようになり、Go言語の適用範囲が広がります。
前提知識の解説
システムコール (System Calls)
システムコールは、ユーザー空間で実行されるプログラムが、カーネル空間で提供されるオペレーティングシステム(OS)のサービスにアクセスするための唯一の手段です。OSは、ファイルシステム操作、プロセス管理、メモリ管理、ネットワーク通信など、様々な低レベルの機能を提供しますが、これらの機能は通常、特権モード(カーネルモード)で実行されます。
ユーザープログラムがこれらのサービスを利用したい場合、直接カーネルのコードを実行することはできません。代わりに、特定のシステムコールインターフェースを介してカーネルに要求を送信します。このプロセスは以下のようになります。
- 引数の準備: ユーザープログラムは、システムコールに必要な引数をレジスタまたはスタックに配置します。
- システムコール番号の指定: 呼び出したいシステムコールを一意に識別する番号(システムコール番号)を特定のレジスタに設定します。
- トラップ命令の実行:
SWI
(Software Interrupt) やSYSCALL
のような特殊なCPU命令を実行します。この命令は、CPUをユーザーモードからカーネルモードに切り替え、カーネル内の特定のシステムコールハンドラに制御を移します。 - カーネルでの処理: カーネルはシステムコール番号を読み取り、対応するシステムコールルーチンを実行します。
- 結果の返却: カーネルは処理結果(成功/失敗、戻り値など)をレジスタに格納し、CPUをユーザーモードに戻して、ユーザープログラムに制御を返します。
Go言語では、syscall
パッケージがこの低レベルなシステムコールインターフェースを抽象化し、GoプログラムからOSの機能にアクセスできるようにしています。
FreeBSD
FreeBSDは、カリフォルニア大学バークレー校で開発されたBSD (Berkeley Software Distribution) をルーツに持つ、オープンソースのUnix系オペレーティングシステムです。堅牢性、高性能、セキュリティ、そして優れたドキュメントで知られており、サーバー、組み込みシステム、デスクトップなど幅広い用途で利用されています。Linuxと同様に、カーネルとユーザーランドが一体となって開発・配布される特徴があります。
ARMアーキテクチャ (ARM Architecture)
ARM(Advanced RISC Machine)は、主にモバイルデバイス、組み込みシステム、IoTデバイス、そして近年ではサーバーや高性能コンピューティング(HPC)分野でも広く採用されているRISC(Reduced Instruction Set Computer)ベースのCPUアーキテクチャです。低消費電力と高い性能効率が特徴であり、スマートフォン、タブレット、Raspberry Piなどのシングルボードコンピュータ、ネットワーク機器など、多岐にわたるデバイスに搭載されています。
Go言語のsyscall
パッケージ
Go言語の標準ライブラリには、syscall
というパッケージが存在します。このパッケージは、Goプログラムがオペレーティングシステムが提供する低レベルな機能(システムコール)に直接アクセスするためのインターフェースを提供します。os
パッケージなどの高レベルなAPIは、内部的にこのsyscall
パッケージを利用してOSと対話しています。
syscall
パッケージは、OSとアーキテクチャの組み合わせごとに異なる実装を持ちます。これは、システムコールの呼び出し規約(引数の渡し方、戻り値の受け取り方)、システムコール番号、エラーコード、およびOS固有のデータ構造がプラットフォームによって異なるためです。
クロスコンパイル (Cross-compilation)
Go言語の大きな特徴の一つに、クロスコンパイルの容易さがあります。これは、あるプラットフォーム(例: Linux/amd64)でコンパイルされたGoプログラムが、別のプラットフォーム(例: FreeBSD/ARM)で実行可能なバイナリを生成できる能力を指します。Goのツールチェインは、GOOS
(ターゲットOS)とGOARCH
(ターゲットアーキテクチャ)の環境変数を設定するだけで、異なる環境向けのバイナリを生成できます。この機能は、組み込みシステムや異なるOS環境へのデプロイにおいて非常に有用です。
アセンブリ言語 (Assembly Language)
アセンブリ言語は、CPUが直接理解できる機械語に非常に近い低レベルのプログラミング言語です。システムコールの呼び出しは、OSのカーネルに制御を移すという特権的な操作を伴うため、多くの場合、アセンブリ言語で記述されたコードを介して行われます。これは、レジスタの直接操作や特定のCPU命令(トラップ命令など)の実行が必要となるためです。Go言語のsyscall
パッケージでも、各OS/Archのシステムコールエントリポイントはアセンブリ言語で実装されています。
技術的詳細
このコミットは、Go言語がFreeBSD/ARM上でシステムコールを適切に実行できるようにするための、多岐にわたる技術的詳細を含んでいます。
Go言語のsyscall
パッケージは、各OS/Archの組み合わせに対して、以下のようなファイル群で構成されます。
- アセンブリファイル (
asm_GOOS_GOARCH.s
):- Goのランタイムからシステムコールを呼び出すための低レベルなアセンブリコードが含まれます。
- システムコール番号、引数、戻り値をレジスタ間で適切にやり取りし、OSのシステムコールエントリポイント(FreeBSD/ARMの場合は
SWI $0
命令)を呼び出します。 - Goランタイムの
entersyscall
とexitsyscall
関数を呼び出し、Goスケジューラがシステムコール中のGoroutineの状態を適切に管理できるようにします。
- Go言語のラッパーファイル (
syscall_GOOS_GOARCH.go
):- OS固有の定数、型、およびGo言語で記述されたヘルパー関数が含まれます。
- 例えば、
Getpagesize()
のようなOS固有の情報を提供する関数や、Timespec
、Timeval
などの時間構造体とGoのint64
間の変換関数などが定義されます。 Sendfile
のような、複数の引数を取る複雑なシステムコールのGo言語ラッパーもここに定義されることがあります。
- 自動生成されるGoファイル (
z*.go
):- これらのファイルは、OSのヘッダーファイルやシステムコール定義から自動的に生成されます。
zerrors_GOOS_GOARCH.go
: OSのエラーコード(例:EACCES
,ENOENT
など)に対応するGoの定数と、それらのエラーメッセージのマップが含まれます。zsyscall_GOOS_GOARCH.go
: 各システムコールに対応するGo言語の関数ラッパーが含まれます。これらの関数は、Goの引数をアセンブリコードが期待する形式に変換し、アセンブリのシステムコールエントリポイントを呼び出します。zsysnum_GOOS_GOARCH.go
: 各システムコール名に対応するシステムコール番号のGo定数が含まれます。ztypes_GOOS_GOARCH.go
: OS固有のC言語の構造体や型(例:Stat_t
,Timespec
,Timeval
など)に対応するGoの型定義が含まれます。
このコミットでは、これらのファイルがFreeBSD/ARM向けに新規作成されています。
ファイル生成プロセス
src/pkg/syscall/mkall.sh
スクリプトは、Goのsyscall
パッケージの自動生成ファイルを管理するための重要な役割を担っています。このスクリプトは、各OS/Archの組み合わせに対して、以下のツールを使用してz*.go
ファイルを生成します。
mksyscall.pl
: Perlスクリプトで、C言語のシステムコール定義からGo言語のシステムコールラッパー(zsyscall_*.go
)を生成します。引数の数や型、戻り値の処理などを自動化します。mkerrors.sh
: OSのエラーコード定義からGo言語のエラー定数(zerrors_*.go
)を生成します。mksysnum_freebsd.pl
: FreeBSDのシステムコールマスターファイル(sys/kern/syscalls.master
)からシステムコール番号のGo定数(zsysnum_*.go
)を生成します。go tool cgo -godefs
: C言語の構造体定義からGo言語の型定義(ztypes_*.go
)を生成します。これは、CGOツールの一部であり、Cの型をGoの型にマッピングするために使用されます。
このコミットでは、mkall.sh
にfreebsd_arm
のセクションが追加され、上記のツールがFreeBSD/ARM固有のオプション(例: -l32 -arm
)で実行されるように設定されています。これにより、FreeBSD/ARM環境に特化したシステムコール関連のGoコードが自動的に生成されるようになります。
コアとなるコードの変更箇所
このコミットで追加された主要なファイルは以下の通りです。
-
src/pkg/syscall/asm_freebsd_arm.s
:- FreeBSD/ARMにおけるGoのシステムコール呼び出しの低レベルなアセンブリ実装。
Syscall
,Syscall6
,Syscall9
(最大9つの引数を持つシステムコール用) およびRawSyscall
,RawSyscall6
(Goランタイムのentersyscall
/exitsyscall
をスキップするバージョン) のエントリポイントを定義。- ARMのレジスタ(R0-R3)を使用して引数を渡し、
SWI $0
命令でシステムコールをトリガー。 - 戻り値とエラーコードの処理(
BCS
命令でキャリーフラグをチェックし、エラーを検出)。
-
src/pkg/syscall/syscall_freebsd_arm.go
:- FreeBSD/ARM固有のGo言語の定数、型、およびヘルパー関数。
Getpagesize()
: ページサイズを返す。TimespecToNsec()
,NsecToTimespec()
:Timespec
構造体とナノ秒間の変換。TimevalToNsec()
,NsecToTimeval()
:Timeval
構造体とナノ秒間の変換。SetKevent()
,SetLen()
,SetControllen()
:Kevent_t
,Iovec
,Msghdr
,Cmsghdr
などのOS固有の構造体を操作するヘルパー関数。Sendfile()
:SYS_SENDFILE
システムコールを呼び出すGoラッパー。Syscall9()
: 9つの引数を持つシステムコールを呼び出すためのGo関数宣言。
-
src/pkg/syscall/mkall.sh
:freebsd_arm
セクションが追加され、FreeBSD/ARM向けのz*.go
ファイルを生成するためのコマンドが定義されています。mksyscall.pl -l32 -arm
、mksysnum_freebsd.pl
、go tool cgo -godefs
などのツールが使用されます。
-
src/pkg/syscall/zerrors_freebsd_arm.go
:- FreeBSDのエラーコード(例:
EACCES
,ENOENT
など)に対応するGoの定数と、それらのエラーメッセージのマップが自動生成されています。
- FreeBSDのエラーコード(例:
-
src/pkg/syscall/zsyscall_freebsd_arm.go
:- FreeBSD/ARMの各システムコールに対応するGo言語のラッパー関数が自動生成されています。これらの関数は、Goの引数をアセンブリコードが期待する形式に変換し、
RawSyscall
やSyscall
を呼び出します。
- FreeBSD/ARMの各システムコールに対応するGo言語のラッパー関数が自動生成されています。これらの関数は、Goの引数をアセンブリコードが期待する形式に変換し、
-
src/pkg/syscall/zsysnum_freebsd_arm.go
:- FreeBSDのシステムコール番号に対応するGoの定数が自動生成されています。
-
src/pkg/syscall/ztypes_freebsd_arm.go
:- FreeBSDのC言語の構造体や型(例:
Timespec
,Timeval
,Kevent_t
など)に対応するGoの型定義が自動生成されています。
- FreeBSDのC言語の構造体や型(例:
コアとなるコードの解説
src/pkg/syscall/asm_freebsd_arm.s
の解説
このアセンブリファイルは、GoプログラムがFreeBSD/ARM上でシステムコールを実行するための低レベルなインターフェースを提供します。
TEXT ·Syscall(SB),7,$0
:- これはGoのアセンブリ構文で、
syscall
パッケージのSyscall
関数を定義しています。SB
はスタティックベースレジスタ、7
はスタックフレームのサイズ、$0
は引数のサイズを示します。 BL runtime·entersyscall(SB)
: システムコールに入る前にGoランタイムのentersyscall
関数を呼び出します。これにより、Goスケジューラは現在のGoroutineがシステムコールを実行中であることを認識し、必要に応じて他のGoroutineをスケジュールできます。MOVW 0(FP), R0
: フレームポインタ(FP
)からのオフセットで、最初の引数(システムコール番号trap
)をレジスタR0
に移動します。ARMのシステムコール規約では、システムコール番号は通常R0
に置かれます。MOVW 4(FP), R1
/MOVW 8(FP), R2
/MOVW 12(FP), R3
: 続く引数a1
,a2
,a3
をそれぞれレジスタR1
,R2
,R3
に移動します。ARMでは、最初の4つの引数はレジスタで渡されます。SWI $0
: これはSoftware Interrupt命令で、FreeBSD/ARMにおけるシステムコールをトリガーします。$0
は、FreeBSDのシステムコールハンドラが期待する値です。この命令により、CPUはユーザーモードからカーネルモードに切り替わり、カーネル内のシステムコールハンドラが実行されます。MOVW $0, R2
: 戻り値のエラーコードを初期化します。BCS error
:SWI
命令の実行後、キャリーフラグ(Cフラグ)がセットされている場合(通常、システムコールがエラーを返した場合)、error
ラベルにジャンプします。MOVW R0, 16(FP)
/MOVW R1, 20(FP)
/MOVW R2, 24(FP)
: システムコールが成功した場合、カーネルからの戻り値(r1
,r2
,err
)をGoの戻り値としてスタックフレームに格納します。R0
には通常、最初の戻り値、R1
には2番目の戻り値、R2
にはエラーコードが格納されます。BL runtime·exitsyscall(SB)
: システムコールから戻る前にGoランタイムのexitsyscall
関数を呼び出します。これにより、GoスケジューラはGoroutineがシステムコールを完了したことを認識します。RET
: 関数から戻ります。error:
ラベル:MOVW $-1, R3
: 最初の戻り値r1
に-1
を設定します。これは、Goのsyscall
パッケージの慣習で、エラー時にr1
を-1
に設定することがあります。MOVW R0, 24(FP)
: カーネルから返された実際のエラーコード(R0
に格納されている)をGoのエラー戻り値err
としてスタックフレームに格納します。- 残りの処理は成功時と同様に
exitsyscall
を呼び出し、RET
します。
- これはGoのアセンブリ構文で、
Syscall6
とSyscall9
も同様のロジックですが、より多くの引数を処理するために、レジスタに収まらない引数をスタック経由で渡す処理が含まれます。RawSyscall
とRawSyscall6
は、entersyscall
/exitsyscall
の呼び出しを省略しており、Goランタイムのスケジューラに影響を与えない、より低レベルなシステムコール呼び出しに使用されます。
src/pkg/syscall/syscall_freebsd_arm.go
の解説
このGoファイルは、FreeBSD/ARM環境に特化したGo言語のインターフェースを提供します。
-
func Getpagesize() int { return 4096 }
:- FreeBSD/ARMのページサイズ(メモリ管理の最小単位)を返します。これはOS固有の値であり、Goプログラムがメモリを効率的に扱うために必要です。
-
func TimespecToNsec(ts Timespec) int64 { return ts.Sec*1e9 + int64(ts.Nsec) }
:- FreeBSDの
Timespec
構造体(秒とナノ秒で時間を表現)をGoのナノ秒単位のint64
に変換します。これは、Goの標準時間APIとの互換性を保つために必要です。
- FreeBSDの
-
func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
:- FreeBSDの
sendfile
システムコールをGoから呼び出すためのラッパー関数です。sendfile
は、カーネル空間内で直接ファイルからソケットへデータを転送する効率的な方法を提供します。 Syscall9
を使用してSYS_SENDFILE
システムコールを呼び出しています。Syscall9
は9つの引数を取ることができるGoのシステムコールラッパーです。uintptr(unsafe.Pointer(&writtenOut))
のように、Goのポインタをuintptr
にキャストしてシステムコールに渡すことで、カーネルが直接Goのメモリに書き込めるようにしています。
- FreeBSDの
-
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
:- これはGoの関数宣言であり、実際の実装は前述の
asm_freebsd_arm.s
にあります。 - 9つの
uintptr
型の引数を取り、2つのuintptr
型の戻り値とErrno
型のエラーを返します。uintptr
は、ポインタを整数として扱うための型で、システムコールに引数を渡す際によく使用されます。
- これはGoの関数宣言であり、実際の実装は前述の
自動生成ファイル (z*.go
) の解説
これらのファイルは、FreeBSDのシステムヘッダーファイルから情報を抽出し、Go言語のコードとして自動生成されます。
-
zerrors_freebsd_arm.go
:- FreeBSDの
errno.h
などのヘッダーファイルから、EACCES
(Permission denied),ENOENT
(No such file or directory) といったエラーコードの数値と、それに対応するGoのErrno
定数を定義します。また、これらのエラーコードに対応する文字列メッセージのマップも提供し、Errno.Error()
メソッドが人間が読めるエラーメッセージを返せるようにします。
- FreeBSDの
-
zsyscall_freebsd_arm.go
:- FreeBSDの各システムコール(例:
read
,write
,open
,socket
など)に対して、Go言語のラッパー関数を生成します。 - 例えば、
func read(fd int, p []byte) (n int, err error)
のような関数が生成され、内部でRawSyscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(&p[0])), uintptr(len(p)))
のように、対応するシステムコール番号と引数をasm_freebsd_arm.s
で定義されたRawSyscall
関数に渡します。 - これにより、Go開発者は低レベルなアセンブリコードを直接書くことなく、Goの関数としてシステムコールを呼び出すことができます。
- FreeBSDの各システムコール(例:
-
zsysnum_freebsd_arm.go
:- FreeBSDの
sys/kern/syscalls.master
ファイルから、SYS_READ
,SYS_WRITE
,SYS_OPEN
といったシステムコール名に対応する一意の数値(システムコール番号)をGoの定数として定義します。これらの番号は、アセンブリコードがどのシステムコールを呼び出すべきかをカーネルに伝えるために使用されます。
- FreeBSDの
-
ztypes_freebsd_arm.go
:- FreeBSDのC言語のヘッダーファイル(例:
sys/types.h
,sys/stat.h
など)で定義されている構造体(例:Timespec
,Stat_t
,Kevent_t
)や型エイリアスに対応するGoの型定義を生成します。これにより、GoプログラムがOS固有のデータ構造を安全に操作できるようになります。
- FreeBSDのC言語のヘッダーファイル(例:
これらの自動生成ファイルは、Go言語がFreeBSD/ARMのシステムコールインターフェースとシームレスに連携するための重要な橋渡し役を果たします。
関連リンク
- Go言語の
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall - FreeBSDハンドブック: https://docs.freebsd.org/en/books/handbook/
- ARMアーキテクチャの概要 (Wikipedia): https://ja.wikipedia.org/wiki/ARM%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3
参考にした情報源リンク
- Go言語のソースコード(
src/pkg/syscall
ディレクトリ) - FreeBSDのシステムコールに関するドキュメント
- ARMアーキテクチャのシステムコール規約に関する情報
- Go言語のクロスコンパイルに関する公式ドキュメントやブログ記事
- Go言語の
syscall
パッケージの設計に関する議論(GoのメーリングリストやIssueトラッカー) mksyscall.pl
などのGoツールチェインの内部動作に関する情報- Go言語の
unsafe
パッケージの利用に関する情報 - Go言語のアセンブリ言語に関するドキュメント
[インデックス 14139] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにFreeBSDオペレーティングシステムとARMアーキテクチャの組み合わせに対するサポートを追加するものです。具体的には、FreeBSD/ARM環境でGoプログラムがシステムコールを直接呼び出せるようにするためのアセンブリコード、Go言語のラッパー、および関連する定数や型定義が追加されています。これにより、Go言語がFreeBSD/ARMプラットフォーム上でより深く、効率的にシステムリソースと対話できるようになります。
コミット
commit 378d7ef59fd25d3003915ee2437de2274a61d505
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Fri Oct 12 16:26:42 2012 +0800
syscall: FreeBSD/ARM support
R=rsc, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/6657048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/378d7ef59fd25d3003915ee2437de2274a61d505
元コミット内容
syscall: FreeBSD/ARM support
R=rsc, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/6657048
変更の背景
Go言語は、その設計思想の一つとして、様々なプラットフォームへの高い移植性を持っています。これは、Goランタイムが各オペレーティングシステム(OS)とアーキテクチャの組み合わせ(OS/Arch)に特化した低レベルのインターフェース、特にシステムコール(syscall)を抽象化して提供することで実現されています。
このコミットが行われた2012年当時、Go言語は既に主要なOS/Archの組み合わせ(例: Linux/amd64, Darwin/amd64など)をサポートしていましたが、FreeBSDとARMアーキテクチャの組み合わせに対する公式なサポートはまだ限定的でした。FreeBSDは堅牢で高性能なUnix系OSであり、ARMアーキテクチャは組み込みシステム、モバイルデバイス、そして近年ではサーバー分野でも広く採用されています。
GoプログラムがFreeBSD/ARM環境で動作し、ファイルI/O、ネットワーク通信、プロセス管理などのOS機能にアクセスするためには、その環境固有のシステムコールを正しく呼び出すメカニズムが必要です。このコミットは、Go言語がFreeBSD/ARMプラットフォーム上で完全に機能し、そのエコシステムを拡大するための基盤を構築することを目的としています。これにより、開発者はFreeBSD/ARMデバイス上でGoアプリケーションを開発・デプロイできるようになり、Go言語の適用範囲が広がります。
前提知識の解説
システムコール (System Calls)
システムコールは、ユーザー空間で実行されるプログラムが、カーネル空間で提供されるオペレーティングシステム(OS)のサービスにアクセスするための唯一の手段です。OSは、ファイルシステム操作、プロセス管理、メモリ管理、ネットワーク通信など、様々な低レベルの機能を提供しますが、これらの機能は通常、特権モード(カーネルモード)で実行されます。
ユーザープログラムがこれらのサービスを利用したい場合、直接カーネルのコードを実行することはできません。代わりに、特定のシステムコールインターフェースを介してカーネルに要求を送信します。このプロセスは以下のようになります。
- 引数の準備: ユーザープログラムは、システムコールに必要な引数をレジスタまたはスタックに配置します。
- システムコール番号の指定: 呼び出したいシステムコールを一意に識別する番号(システムコール番号)を特定のレジスタに設定します。
- トラップ命令の実行:
SWI
(Software Interrupt) やSYSCALL
のような特殊なCPU命令を実行します。この命令は、CPUをユーザーモードからカーネルモードに切り替え、カーネル内の特定のシステムコールハンドラに制御を移します。 - カーネルでの処理: カーネルはシステムコール番号を読み取り、対応するシステムコールルーチンを実行します。
- 結果の返却: カーネルは処理結果(成功/失敗、戻り値など)をレジスタに格納し、CPUをユーザーモードに戻して、ユーザープログラムに制御を返します。
Go言語では、syscall
パッケージがこの低レベルなシステムコールインターフェースを抽象化し、GoプログラムからOSの機能にアクセスできるようにしています。
FreeBSD
FreeBSDは、カリフォルニア大学バークレー校で開発されたBSD (Berkeley Software Distribution) をルーツに持つ、オープンソースのUnix系オペレーティングシステムです。堅牢性、高性能、セキュリティ、そして優れたドキュメントで知られており、サーバー、組み込みシステム、デスクトップなど幅広い用途で利用されています。Linuxと同様に、カーネルとユーザーランドが一体となって開発・配布される特徴があります。
ARMアーキテクチャ (ARM Architecture)
ARM(Advanced RISC Machine)は、主にモバイルデバイス、組み込みシステム、IoTデバイス、そして近年ではサーバーや高性能コンピューティング(HPC)分野でも広く採用されているRISC(Reduced Instruction Set Computer)ベースのCPUアーキテクチャです。低消費電力と高い性能効率が特徴であり、スマートフォン、タブレット、Raspberry Piなどのシングルボードコンピュータ、ネットワーク機器など、多岐にわたるデバイスに搭載されています。
Go言語のsyscall
パッケージ
Go言語の標準ライブラリには、syscall
というパッケージが存在します。このパッケージは、Goプログラムがオペレーティングシステムが提供する低レベルな機能(システムコール)に直接アクセスするためのインターフェースを提供します。os
パッケージなどの高レベルなAPIは、内部的にこのsyscall
パッケージを利用してOSと対話しています。
syscall
パッケージは、OSとアーキテクチャの組み合わせごとに異なる実装を持ちます。これは、システムコールの呼び出し規約(引数の渡し方、戻り値の受け取り方)、システムコール番号、エラーコード、およびOS固有のデータ構造がプラットフォームによって異なるためです。
クロスコンパイル (Cross-compilation)
Go言語の大きな特徴の一つに、クロスコンパイルの容易さがあります。これは、あるプラットフォーム(例: Linux/amd64)でコンパイルされたGoプログラムが、別のプラットフォーム(例: FreeBSD/ARM)で実行可能なバイナリを生成できる能力を指します。Goのツールチェインは、GOOS
(ターゲットOS)とGOARCH
(ターゲットアーキテクチャ)の環境変数を設定するだけで、異なる環境向けのバイナリを生成できます。この機能は、組み込みシステムや異なるOS環境へのデプロイにおいて非常に有用です。
アセンブリ言語 (Assembly Language)
アセンブリ言語は、CPUが直接理解できる機械語に非常に近い低レベルのプログラミング言語です。システムコールの呼び出しは、OSのカーネルに制御を移すという特権的な操作を伴うため、多くの場合、アセンブリ言語で記述されたコードを介して行われます。これは、レジスタの直接操作や特定のCPU命令(トラップ命令など)の実行が必要となるためです。Go言語のsyscall
パッケージでも、各OS/Archのシステムコールエントリポイントはアセンブリ言語で実装されています。
技術的詳細
このコミットは、Go言語がFreeBSD/ARM上でシステムコールを適切に実行できるようにするための、多岐にわたる技術的詳細を含んでいます。
Go言語のsyscall
パッケージは、各OS/Archの組み合わせに対して、以下のようなファイル群で構成されます。
- アセンブリファイル (
asm_GOOS_GOARCH.s
):- Goのランタイムからシステムコールを呼び出すための低レベルなアセンブリコードが含まれます。
- システムコール番号、引数、戻り値をレジスタ間で適切にやり取りし、OSのシステムコールエントリポイント(FreeBSD/ARMの場合は
SWI $0
命令)を呼び出します。 - Goランタイムの
entersyscall
とexitsyscall
関数を呼び出し、Goスケジューラがシステムコール中のGoroutineの状態を適切に管理できるようにします。
- Go言語のラッパーファイル (
syscall_GOOS_GOARCH.go
):- OS固有の定数、型、およびGo言語で記述されたヘルパー関数が含まれます。
- 例えば、
Getpagesize()
のようなOS固有の情報を提供する関数や、Timespec
、Timeval
などの時間構造体とGoのint64
間の変換関数などが定義されます。 Sendfile
のような、複数の引数を取る複雑なシステムコールのGo言語ラッパーもここに定義されることがあります。
- 自動生成されるGoファイル (
z*.go
):- これらのファイルは、OSのヘッダーファイルやシステムコール定義から自動的に生成されます。
zerrors_GOOS_GOARCH.go
: OSのエラーコード(例:EACCES
,ENOENT
など)に対応するGoの定数と、それらのエラーメッセージのマップが含まれます。zsyscall_GOOS_GOARCH.go
: 各システムコールに対応するGo言語の関数ラッパーが含まれます。これらの関数は、Goの引数をアセンブリコードが期待する形式に変換し、アセンブリのシステムコールエントリポイントを呼び出します。zsysnum_GOOS_GOARCH.go
: 各システムコール名に対応するシステムコール番号のGo定数が含まれます。ztypes_GOOS_GOARCH.go
: OS固有のC言語の構造体や型(例:Stat_t
,Timespec
,Timeval
など)に対応するGoの型定義が含まれます。
このコミットでは、これらのファイルがFreeBSD/ARM向けに新規作成されています。
ファイル生成プロセス
src/pkg/syscall/mkall.sh
スクリプトは、Goのsyscall
パッケージの自動生成ファイルを管理するための重要な役割を担っています。このスクリプトは、各OS/Archの組み合わせに対して、以下のツールを使用してz*.go
ファイルを生成します。
mksyscall.pl
: Perlスクリプトで、C言語のシステムコール定義からGo言語のシステムコールラッパー(zsyscall_*.go
)を生成します。引数の数や型、戻り値の処理などを自動化します。mkerrors.sh
: OSのエラーコード定義からGo言語のエラー定数(zerrors_*.go
)を生成します。mksysnum_freebsd.pl
: FreeBSDのシステムコールマスターファイル(sys/kern/syscalls.master
)からシステムコール番号のGo定数(zsysnum_*.go
)を生成します。go tool cgo -godefs
: C言語の構造体定義からGo言語の型定義(ztypes_*.go
)を生成します。これは、CGOツールの一部であり、Cの型をGoの型にマッピングするために使用されます。
このコミットでは、mkall.sh
にfreebsd_arm
のセクションが追加され、上記のツールがFreeBSD/ARM固有のオプション(例: -l32 -arm
)で実行されるように設定されています。これにより、FreeBSD/ARM環境に特化したシステムコール関連のGoコードが自動的に生成されるようになります。
コアとなるコードの変更箇所
このコミットで追加された主要なファイルは以下の通りです。
-
src/pkg/syscall/asm_freebsd_arm.s
:- FreeBSD/ARMにおけるGoのシステムコール呼び出しの低レベルなアセンブリ実装。
Syscall
,Syscall6
,Syscall9
(最大9つの引数を持つシステムコール用) およびRawSyscall
,RawSyscall6
(Goランタイムのentersyscall
/exitsyscall
をスキップするバージョン) のエントリポイントを定義。- ARMのレジスタ(R0-R3)を使用して引数を渡し、
SWI $0
命令でシステムコールをトリガー。 - 戻り値とエラーコードの処理(
BCS
命令でキャリーフラグをチェックし、エラーを検出)。
-
src/pkg/syscall/syscall_freebsd_arm.go
:- FreeBSD/ARM固有のGo言語の定数、型、およびヘルパー関数。
Getpagesize()
: ページサイズを返す。TimespecToNsec()
,NsecToTimespec()
:Timespec
構造体とナノ秒間の変換。TimevalToNsec()
,NsecToTimeval()
:Timeval
構造体とナノ秒間の変換。SetKevent()
,SetLen()
,SetControllen()
:Kevent_t
,Iovec
,Msghdr
,Cmsghdr
などのOS固有の構造体を操作するヘルパー関数。Sendfile()
:SYS_SENDFILE
システムコールを呼び出すGoラッパー。Syscall9()
: 9つの引数を持つシステムコールを呼び出すためのGo関数宣言。
-
src/pkg/syscall/mkall.sh
:freebsd_arm
セクションが追加され、FreeBSD/ARM向けのz*.go
ファイルを生成するためのコマンドが定義されています。mksyscall.pl -l32 -arm
、mksysnum_freebsd.pl
、go tool cgo -godefs
などのツールが使用されます。
-
src/pkg/syscall/zerrors_freebsd_arm.go
:- FreeBSDのエラーコード(例:
EACCES
,ENOENT
など)に対応するGoの定数と、それらのエラーメッセージのマップが自動生成されています。
- FreeBSDのエラーコード(例:
-
src/pkg/syscall/zsyscall_freebsd_arm.go
:- FreeBSD/ARMの各システムコールに対応するGo言語のラッパー関数が自動生成されています。これらの関数は、Goの引数をアセンブリコードが期待する形式に変換し、
RawSyscall
やSyscall
を呼び出します。
- FreeBSD/ARMの各システムコールに対応するGo言語のラッパー関数が自動生成されています。これらの関数は、Goの引数をアセンブリコードが期待する形式に変換し、
-
src/pkg/syscall/zsysnum_freebsd_arm.go
:- FreeBSDのシステムコール番号に対応するGoの定数が自動生成されています。
-
src/pkg/syscall/ztypes_freebsd_arm.go
:- FreeBSDのC言語の構造体や型(例:
Timespec
,Timeval
,Kevent_t
など)に対応するGoの型定義が自動生成されています。
- FreeBSDのC言語の構造体や型(例:
コアとなるコードの解説
src/pkg/syscall/asm_freebsd_arm.s
の解説
このアセンブリファイルは、GoプログラムがFreeBSD/ARM上でシステムコールを実行するための低レベルなインターフェースを提供します。
TEXT ·Syscall(SB),7,$0
:- これはGoのアセンブリ構文で、
syscall
パッケージのSyscall
関数を定義しています。SB
はスタティックベースレジスタ、7
はスタックフレームのサイズ、$0
は引数のサイズを示します。 BL runtime·entersyscall(SB)
: システムコールに入る前にGoランタイムのentersyscall
関数を呼び出します。これにより、Goスケジューラは現在のGoroutineがシステムコールを実行中であることを認識し、必要に応じて他のGoroutineをスケジュールできます。MOVW 0(FP), R0
: フレームポインタ(FP
)からのオフセットで、最初の引数(システムコール番号trap
)をレジスタR0
に移動します。ARMのシステムコール規約では、システムコール番号は通常R0
に置かれます。MOVW 4(FP), R1
/MOVW 8(FP), R2
/MOVW 12(FP), R3
: 続く引数a1
,a2
,a3
をそれぞれレジスタR1
,R2
,R3
に移動します。ARMでは、最初の4つの引数はレジスタで渡されます。SWI $0
: これはSoftware Interrupt命令で、FreeBSD/ARMにおけるシステムコールをトリガーします。$0
は、FreeBSDのシステムコールハンドラが期待する値です。この命令により、CPUはユーザーモードからカーネルモードに切り替わり、カーネル内のシステムコールハンドラが実行されます。MOVW $0, R2
: 戻り値のエラーコードを初期化します。BCS error
:SWI
命令の実行後、キャリーフラグ(Cフラグ)がセットされている場合(通常、システムコールがエラーを返した場合)、error
ラベルにジャンプします。MOVW R0, 16(FP)
/MOVW R1, 20(FP)
/MOVW R2, 24(FP)
: システムコールが成功した場合、カーネルからの戻り値(r1
,r2
,err
)をGoの戻り値としてスタックフレームに格納します。R0
には通常、最初の戻り値、R1
には2番目の戻り値、R2
にはエラーコードが格納されます。BL runtime·exitsyscall(SB)
: システムコールから戻る前にGoランタイムのexitsyscall
関数を呼び出します。これにより、GoスケジューラはGoroutineがシステムコールを完了したことを認識します。RET
: 関数から戻ります。error:
ラベル:MOVW $-1, R3
: 最初の戻り値r1
に-1
を設定します。これは、Goのsyscall
パッケージの慣習で、エラー時にr1
を-1
に設定することがあります。MOVW R0, 24(FP)
: カーネルから返された実際のエラーコード(R0
に格納されている)をGoのエラー戻り値err
としてスタックフレームに格納します。- 残りの処理は成功時と同様に
exitsyscall
を呼び出し、RET
します。
- これはGoのアセンブリ構文で、
Syscall6
とSyscall9
も同様のロジックですが、より多くの引数を処理するために、レジスタに収まらない引数をスタック経由で渡す処理が含まれます。RawSyscall
とRawSyscall6
は、entersyscall
/exitsyscall
の呼び出しを省略しており、Goランタイムのスケジューラに影響を与えない、より低レベルなシステムコール呼び出しに使用されます。
src/pkg/syscall/syscall_freebsd_arm.go
の解説
このGoファイルは、FreeBSD/ARM環境に特化したGo言語のインターフェースを提供します。
-
func Getpagesize() int { return 4096 }
:- FreeBSD/ARMのページサイズ(メモリ管理の最小単位)を返します。これはOS固有の値であり、Goプログラムがメモリを効率的に扱うために必要です。
-
func TimespecToNsec(ts Timespec) int64 { return ts.Sec*1e9 + int64(ts.Nsec) }
:- FreeBSDの
Timespec
構造体(秒とナノ秒で時間を表現)をGoのナノ秒単位のint64
に変換します。これは、Goの標準時間APIとの互換性を保つために必要です。
- FreeBSDの
-
func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
:- FreeBSDの
sendfile
システムコールをGoから呼び出すためのラッパー関数です。sendfile
は、カーネル空間内で直接ファイルからソケットへデータを転送する効率的な方法を提供します。 Syscall9
を使用してSYS_SENDFILE
システムコールを呼び出しています。Syscall9
は9つの引数を取ることができるGoのシステムコールラッパーです。uintptr(unsafe.Pointer(&writtenOut))
のように、Goのポインタをuintptr
にキャストしてシステムコールに渡すことで、カーネルが直接Goのメモリに書き込めるようにしています。
- FreeBSDの
-
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
:- これはGoの関数宣言であり、実際の実装は前述の
asm_freebsd_arm.s
にあります。 - 9つの
uintptr
型の引数を取り、2つのuintptr
型の戻り値とErrno
型のエラーを返します。uintptr
は、ポインタを整数として扱うための型で、システムコールに引数を渡す際によく使用されます。
- これはGoの関数宣言であり、実際の実装は前述の
自動生成ファイル (z*.go
) の解説
これらのファイルは、FreeBSDのシステムヘッダーファイルから情報を抽出し、Go言語のコードとして自動生成されます。
-
zerrors_freebsd_arm.go
:- FreeBSDの
errno.h
などのヘッダーファイルから、EACCES
(Permission denied),ENOENT
(No such file or directory) といったエラーコードの数値と、それに対応するGoのErrno
定数を定義します。また、これらのエラーコードに対応する文字列メッセージのマップも提供し、Errno.Error()
メソッドが人間が読めるエラーメッセージを返せるようにします。
- FreeBSDの
-
zsyscall_freebsd_arm.go
:- FreeBSDの各システムコール(例:
read
,write
,open
,socket
など)に対して、Go言語のラッパー関数を生成します。 - 例えば、
func read(fd int, p []byte) (n int, err error)
のような関数が生成され、内部でRawSyscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(&p[0])), uintptr(len(p)))
のように、対応するシステムコール番号と引数をasm_freebsd_arm.s
で定義されたRawSyscall
関数に渡します。 - これにより、Go開発者は低レベルなアセンブリコードを直接書くことなく、Goの関数としてシステムコールを呼び出すことができます。
- FreeBSDの各システムコール(例:
-
zsysnum_freebsd_arm.go
:- FreeBSDの
sys/kern/syscalls.master
ファイルから、SYS_READ
,SYS_WRITE
,SYS_OPEN
といったシステムコール名に対応する一意の数値(システムコール番号)をGoの定数として定義します。これらの番号は、アセンブリコードがどのシステムコールを呼び出すべきかをカーネルに伝えるために使用されます。
- FreeBSDの
-
ztypes_freebsd_arm.go
:- FreeBSDのC言語のヘッダーファイル(例:
sys/types.h
,sys/stat.h
など)で定義されている構造体(例:Timespec
,Stat_t
,Kevent_t
)や型エイリアスに対応するGoの型定義を生成します。これにより、GoプログラムがOS固有のデータ構造を安全に操作できるようになります。
- FreeBSDのC言語のヘッダーファイル(例:
これらの自動生成ファイルは、Go言語がFreeBSD/ARMのシステムコールインターフェースとシームレスに連携するための重要な橋渡し役を果たします。
関連リンク
- Go言語の
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall - FreeBSDハンドブック: https://docs.freebsd.org/en/books/handbook/
- ARMアーキテクチャの概要 (Wikipedia): https://ja.wikipedia.org/wiki/ARM%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3
参考にした情報源リンク
- Go言語のソースコード(
src/pkg/syscall
ディレクトリ) - FreeBSDのシステムコールに関するドキュメント
- ARMアーキテクチャのシステムコール規約に関する情報
- Go言語のクロスコンパイルに関する公式ドキュメントやブログ記事
- Go言語の
syscall
パッケージの設計に関する議論(GoのメーリングリストやIssueトラッカー) mksyscall.pl
などのGoツールチェインの内部動作に関する情報- Go言語の
unsafe
パッケージの利用に関する情報 - Go言語のアセンブリ言語に関するドキュメント
[インデックス 14139] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにFreeBSDオペレーティングシステムとARMアーキテクチャの組み合わせに対するサポートを追加するものです。具体的には、FreeBSD/ARM環境でGoプログラムがシステムコールを直接呼び出せるようにするためのアセンブリコード、Go言語のラッパー、および関連する定数や型定義が追加されています。これにより、Go言語がFreeBSD/ARMプラットフォーム上でより深く、効率的にシステムリソースと対話できるようになります。
コミット
commit 378d7ef59fd25d3003915ee2437de2274a61d505
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Fri Oct 12 16:26:42 2012 +0800
syscall: FreeBSD/ARM support
R=rsc, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/6657048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/378d7ef59fd25d3003915ee2437de2274a61d505
元コミット内容
syscall: FreeBSD/ARM support
R=rsc, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/6657048
変更の背景
Go言語は、その設計思想の一つとして、様々なプラットフォームへの高い移植性を持っています。これは、Goランタイムが各オペレーティングシステム(OS)とアーキテクチャの組み合わせ(OS/Arch)に特化した低レベルのインターフェース、特にシステムコール(syscall)を抽象化して提供することで実現されています。
このコミットが行われた2012年当時、Go言語は既に主要なOS/Archの組み合わせ(例: Linux/amd64, Darwin/amd64など)をサポートしていましたが、FreeBSDとARMアーキテクチャの組み合わせに対する公式なサポートはまだ限定的でした。FreeBSDは堅牢で高性能なUnix系OSであり、ARMアーキテクチャは組み込みシステム、モバイルデバイス、そして近年ではサーバー分野でも広く採用されています。
GoプログラムがFreeBSD/ARM環境で動作し、ファイルI/O、ネットワーク通信、プロセス管理などのOS機能にアクセスするためには、その環境固有のシステムコールを正しく呼び出すメカニズムが必要です。このコミットは、Go言語がFreeBSD/ARMプラットフォーム上で完全に機能し、そのエコシステムを拡大するための基盤を構築することを目的としています。これにより、開発者はFreeBSD/ARMデバイス上でGoアプリケーションを開発・デプロイできるようになり、Go言語の適用範囲が広がります。
前提知識の解説
システムコール (System Calls)
システムコールは、ユーザー空間で実行されるプログラムが、カーネル空間で提供されるオペレーティングシステム(OS)のサービスにアクセスするための唯一の手段です。OSは、ファイルシステム操作、プロセス管理、メモリ管理、ネットワーク通信など、様々な低レベルの機能を提供しますが、これらの機能は通常、特権モード(カーネルモード)で実行されます。
ユーザープログラムがこれらのサービスを利用したい場合、直接カーネルのコードを実行することはできません。代わりに、特定のシステムコールインターフェースを介してカーネルに要求を送信します。このプロセスは以下のようになります。
- 引数の準備: ユーザープログラムは、システムコールに必要な引数をレジスタまたはスタックに配置します。
- システムコール番号の指定: 呼び出したいシステムコールを一意に識別する番号(システムコール番号)を特定のレジスタに設定します。
- トラップ命令の実行:
SWI
(Software Interrupt) やSYSCALL
のような特殊なCPU命令を実行します。この命令は、CPUをユーザーモードからカーネルモードに切り替え、カーネル内の特定のシステムコールハンドラに制御を移します。 - カーネルでの処理: カーネルはシステムコール番号を読み取り、対応するシステムコールルーチンを実行します。
- 結果の返却: カーネルは処理結果(成功/失敗、戻り値など)をレジスタに格納し、CPUをユーザーモードに戻して、ユーザープログラムに制御を返します。
Go言語では、syscall
パッケージがこの低レベルなシステムコールインターフェースを抽象化し、GoプログラムからOSの機能にアクセスできるようにしています。
FreeBSD
FreeBSDは、カリフォルニア大学バークレー校で開発されたBSD (Berkeley Software Distribution) をルーツに持つ、オープンソースのUnix系オペレーティングシステムです。堅牢性、高性能、セキュリティ、そして優れたドキュメントで知られており、サーバー、組み込みシステム、デスクトップなど幅広い用途で利用されています。Linuxと同様に、カーネルとユーザーランドが一体となって開発・配布される特徴があります。
ARMアーキテクチャ (ARM Architecture)
ARM(Advanced RISC Machine)は、主にモバイルデバイス、組み込みシステム、IoTデバイス、そして近年ではサーバーや高性能コンピューティング(HPC)分野でも広く採用されているRISC(Reduced Instruction Set Computer)ベースのCPUアーキテクチャです。低消費電力と高い性能効率が特徴であり、スマートフォン、タブレット、Raspberry Piなどのシングルボードコンピュータ、ネットワーク機器など、多岐にわたるデバイスに搭載されています。
Go言語のsyscall
パッケージ
Go言語の標準ライブラリには、syscall
というパッケージが存在します。このパッケージは、Goプログラムがオペレーティングシステムが提供する低レベルな機能(システムコール)に直接アクセスするためのインターフェースを提供します。os
パッケージなどの高レベルなAPIは、内部的にこのsyscall
パッケージを利用してOSと対話しています。
syscall
パッケージは、OSとアーキテクチャの組み合わせごとに異なる実装を持ちます。これは、システムコールの呼び出し規約(引数の渡し方、戻り値の受け取り方)、システムコール番号、エラーコード、およびOS固有のデータ構造がプラットフォームによって異なるためです。
クロスコンパイル (Cross-compilation)
Go言語の大きな特徴の一つに、クロスコンパイルの容易さがあります。これは、あるプラットフォーム(例: Linux/amd64)でコンパイルされたGoプログラムが、別のプラットフォーム(例: FreeBSD/ARM)で実行可能なバイナリを生成できる能力を指します。Goのツールチェインは、GOOS
(ターゲットOS)とGOARCH
(ターゲットアーキテクチャ)の環境変数を設定するだけで、異なる環境向けのバイナリを生成できます。この機能は、組み込みシステムや異なるOS環境へのデプロイにおいて非常に有用です。
アセンブリ言語 (Assembly Language)
アセンブリ言語は、CPUが直接理解できる機械語に非常に近い低レベルのプログラミング言語です。システムコールの呼び出しは、OSのカーネルに制御を移すという特権的な操作を伴うため、多くの場合、アセンブリ言語で記述されたコードを介して行われます。これは、レジスタの直接操作や特定のCPU命令(トラップ命令など)の実行が必要となるためです。Go言語のsyscall
パッケージでも、各OS/Archのシステムコールエントリポイントはアセンブリ言語で実装されています。
技術的詳細
このコミットは、Go言語がFreeBSD/ARM上でシステムコールを適切に実行できるようにするための、多岐にわたる技術的詳細を含んでいます。
Go言語のsyscall
パッケージは、各OS/Archの組み合わせに対して、以下のようなファイル群で構成されます。
- アセンブリファイル (
asm_GOOS_GOARCH.s
):- Goのランタイムからシステムコールを呼び出すための低レベルなアセンブリコードが含まれます。
- システムコール番号、引数、戻り値をレジスタ間で適切にやり取りし、OSのシステムコールエントリポイント(FreeBSD/ARMの場合は
SWI $0
命令)を呼び出します。 - Goランタイムの
entersyscall
とexitsyscall
関数を呼び出し、Goスケジューラがシステムコール中のGoroutineの状態を適切に管理できるようにします。
- Go言語のラッパーファイル (
syscall_GOOS_GOARCH.go
):- OS固有の定数、型、およびGo言語で記述されたヘルパー関数が含まれます。
- 例えば、
Getpagesize()
のようなOS固有の情報を提供する関数や、Timespec
、Timeval
などの時間構造体とGoのint64
間の変換関数などが定義されます。 Sendfile
のような、複数の引数を取る複雑なシステムコールのGo言語ラッパーもここに定義されることがあります。
- 自動生成されるGoファイル (
z*.go
):- これらのファイルは、OSのヘッダーファイルやシステムコール定義から自動的に生成されます。
zerrors_GOOS_GOARCH.go
: OSのエラーコード(例:EACCES
,ENOENT
など)に対応するGoの定数と、それらのエラーメッセージのマップが含まれます。zsyscall_GOOS_GOARCH.go
: 各システムコールに対応するGo言語の関数ラッパーが含まれます。これらの関数は、Goの引数をアセンブリコードが期待する形式に変換し、アセンブリのシステムコールエントリポイントを呼び出します。zsysnum_GOOS_GOARCH.go
: 各システムコール名に対応するシステムコール番号のGo定数が含まれます。ztypes_GOOS_GOARCH.go
: OS固有のC言語の構造体や型(例:Stat_t
,Timespec
,Timeval
など)に対応するGoの型定義が含まれます。
このコミットでは、これらのファイルがFreeBSD/ARM向けに新規作成されています。
ファイル生成プロセス
src/pkg/syscall/mkall.sh
スクリプトは、Goのsyscall
パッケージの自動生成ファイルを管理するための重要な役割を担っています。このスクリプトは、各OS/Archの組み合わせに対して、以下のツールを使用してz*.go
ファイルを生成します。
mksyscall.pl
: Perlスクリプトで、C言語のシステムコール定義からGo言語のシステムコールラッパー(zsyscall_*.go
)を生成します。引数の数や型、戻り値の処理などを自動化します。mkerrors.sh
: OSのエラーコード定義からGo言語のエラー定数(zerrors_*.go
)を生成します。mksysnum_freebsd.pl
: FreeBSDのシステムコールマスターファイル(sys/kern/syscalls.master
)からシステムコール番号のGo定数(zsysnum_*.go
)を生成します。go tool cgo -godefs
: C言語の構造体定義からGo言語の型定義(ztypes_*.go
)を生成します。これは、CGOツールの一部であり、Cの型をGoの型にマッピングするために使用されます。
このコミットでは、mkall.sh
にfreebsd_arm
のセクションが追加され、上記のツールがFreeBSD/ARM固有のオプション(例: -l32 -arm
)で実行されるように設定されています。これにより、FreeBSD/ARM環境に特化したシステムコール関連のGoコードが自動的に生成されるようになります。
コアとなるコードの変更箇所
このコミットで追加された主要なファイルは以下の通りです。
-
src/pkg/syscall/asm_freebsd_arm.s
:- FreeBSD/ARMにおけるGoのシステムコール呼び出しの低レベルなアセンブリ実装。
Syscall
,Syscall6
,Syscall9
(最大9つの引数を持つシステムコール用) およびRawSyscall
,RawSyscall6
(Goランタイムのentersyscall
/exitsyscall
をスキップするバージョン) のエントリポイントを定義。- ARMのレジスタ(R0-R3)を使用して引数を渡し、
SWI $0
命令でシステムコールをトリガー。 - 戻り値とエラーコードの処理(
BCS
命令でキャリーフラグをチェックし、エラーを検出)。
-
src/pkg/syscall/syscall_freebsd_arm.go
:- FreeBSD/ARM固有のGo言語の定数、型、およびヘルパー関数。
Getpagesize()
: ページサイズを返す。TimespecToNsec()
,NsecToTimespec()
:Timespec
構造体とナノ秒間の変換。TimevalToNsec()
,NsecToTimeval()
:Timeval
構造体とナノ秒間の変換。SetKevent()
,SetLen()
,SetControllen()
:Kevent_t
,Iovec
,Msghdr
,Cmsghdr
などのOS固有の構造体を操作するヘルパー関数。Sendfile()
:SYS_SENDFILE
システムコールを呼び出すGoラッパー。Syscall9()
: 9つの引数を持つシステムコールを呼び出すためのGo関数宣言。
-
src/pkg/syscall/mkall.sh
:freebsd_arm
セクションが追加され、FreeBSD/ARM向けのz*.go
ファイルを生成するためのコマンドが定義されています。mksyscall.pl -l32 -arm
、mksysnum_freebsd.pl
、go tool cgo -godefs
などのツールが使用されます。
-
src/pkg/syscall/zerrors_freebsd_arm.go
:- FreeBSDのエラーコード(例:
EACCES
,ENOENT
など)に対応するGoの定数と、それらのエラーメッセージのマップが自動生成されています。
- FreeBSDのエラーコード(例:
-
src/pkg/syscall/zsyscall_freebsd_arm.go
:- FreeBSD/ARMの各システムコールに対応するGo言語のラッパー関数が自動生成されています。これらの関数は、Goの引数をアセンブリコードが期待する形式に変換し、
RawSyscall
やSyscall
を呼び出します。
- FreeBSD/ARMの各システムコールに対応するGo言語のラッパー関数が自動生成されています。これらの関数は、Goの引数をアセンブリコードが期待する形式に変換し、
-
src/pkg/syscall/zsysnum_freebsd_arm.go
:- FreeBSDのシステムコール番号に対応するGoの定数が自動生成されています。
-
src/pkg/syscall/ztypes_freebsd_arm.go
:- FreeBSDのC言語の構造体や型(例:
Timespec
,Timeval
,Kevent_t
など)に対応するGoの型定義が自動生成されています。
- FreeBSDのC言語の構造体や型(例:
コアとなるコードの解説
src/pkg/syscall/asm_freebsd_arm.s
の解説
このアセンブリファイルは、GoプログラムがFreeBSD/ARM上でシステムコールを実行するための低レベルなインターフェースを提供します。
TEXT ·Syscall(SB),7,$0
:- これはGoのアセンブリ構文で、
syscall
パッケージのSyscall
関数を定義しています。SB
はスタティックベースレジスタ、7
はスタックフレームのサイズ、$0
は引数のサイズを示します。 BL runtime·entersyscall(SB)
: システムコールに入る前にGoランタイムのentersyscall
関数を呼び出します。これにより、Goスケジューラは現在のGoroutineがシステムコールを実行中であることを認識し、必要に応じて他のGoroutineをスケジュールできます。MOVW 0(FP), R0
: フレームポインタ(FP
)からのオフセットで、最初の引数(システムコール番号trap
)をレジスタR0
に移動します。ARMのシステムコール規約では、システムコール番号は通常R0
に置かれます。MOVW 4(FP), R1
/MOVW 8(FP), R2
/MOVW 12(FP), R3
: 続く引数a1
,a2
,a3
をそれぞれレジスタR1
,R2
,R3
に移動します。ARMでは、最初の4つの引数はレジスタで渡されます。SWI $0
: これはSoftware Interrupt命令で、FreeBSD/ARMにおけるシステムコールをトリガーします。$0
は、FreeBSDのシステムコールハンドラが期待する値です。この命令により、CPUはユーザーモードからカーネルモードに切り替わり、カーネル内のシステムコールハンドラが実行されます。MOVW $0, R2
: 戻り値のエラーコードを初期化します。BCS error
:SWI
命令の実行後、キャリーフラグ(Cフラグ)がセットされている場合(通常、システムコールがエラーを返した場合)、error
ラベルにジャンプします。MOVW R0, 16(FP)
/MOVW R1, 20(FP)
/MOVW R2, 24(FP)
: システムコールが成功した場合、カーネルからの戻り値(r1
,r2
,err
)をGoの戻り値としてスタックフレームに格納します。R0
には通常、最初の戻り値、R1
には2番目の戻り値、R2
にはエラーコードが格納されます。BL runtime·exitsyscall(SB)
: システムコールから戻る前にGoランタイムのexitsyscall
関数を呼び出します。これにより、GoスケジューラはGoroutineがシステムコールを完了したことを認識します。RET
: 関数から戻ります。error:
ラベル:MOVW $-1, R3
: 最初の戻り値r1
に-1
を設定します。これは、Goのsyscall
パッケージの慣習で、エラー時にr1
を-1
に設定することがあります。MOVW R0, 24(FP)
: カーネルから返された実際のエラーコード(R0
に格納されている)をGoのエラー戻り値err
としてスタックフレームに格納します。- 残りの処理は成功時と同様に
exitsyscall
を呼び出し、RET
します。
- これはGoのアセンブリ構文で、
Syscall6
とSyscall9
も同様のロジックですが、より多くの引数を処理するために、レジスタに収まらない引数をスタック経由で渡す処理が含まれます。RawSyscall
とRawSyscall6
は、entersyscall
/exitsyscall
の呼び出しを省略しており、Goランタイムのスケジューラに影響を与えない、より低レベルなシステムコール呼び出しに使用されます。
src/pkg/syscall/syscall_freebsd_arm.go
の解説
このGoファイルは、FreeBSD/ARM環境に特化したGo言語のインターフェースを提供します。
-
func Getpagesize() int { return 4096 }
:- FreeBSD/ARMのページサイズ(メモリ管理の最小単位)を返します。これはOS固有の値であり、Goプログラムがメモリを効率的に扱うために必要です。
-
func TimespecToNsec(ts Timespec) int64 { return ts.Sec*1e9 + int64(ts.Nsec) }
:- FreeBSDの
Timespec
構造体(秒とナノ秒で時間を表現)をGoのナノ秒単位のint64
に変換します。これは、Goの標準時間APIとの互換性を保つために必要です。
- FreeBSDの
-
func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
:- FreeBSDの
sendfile
システムコールをGoから呼び出すためのラッパー関数です。sendfile
は、カーネル空間内で直接ファイルからソケットへデータを転送する効率的な方法を提供します。 Syscall9
を使用してSYS_SENDFILE
システムコールを呼び出しています。Syscall9
は9つの引数を取ることができるGoのシステムコールラッパーです。uintptr(unsafe.Pointer(&writtenOut))
のように、Goのポインタをuintptr
にキャストしてシステムコールに渡すことで、カーネルが直接Goのメモリに書き込めるようにしています。
- FreeBSDの
-
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
:- これはGoの関数宣言であり、実際の実装は前述の
asm_freebsd_arm.s
にあります。 - 9つの
uintptr
型の引数を取り、2つのuintptr
型の戻り値とErrno
型のエラーを返します。uintptr
は、ポインタを整数として扱うための型で、システムコールに引数を渡す際によく使用されます。
- これはGoの関数宣言であり、実際の実装は前述の
自動生成ファイル (z*.go
) の解説
これらのファイルは、FreeBSDのシステムヘッダーファイルから情報を抽出し、Go言語のコードとして自動生成されます。
-
zerrors_freebsd_arm.go
:- FreeBSDの
errno.h
などのヘッダーファイルから、EACCES
(Permission denied),ENOENT
(No such file or directory) といったエラーコードの数値と、それに対応するGoのErrno
定数を定義します。また、これらのエラーコードに対応する文字列メッセージのマップも提供し、Errno.Error()
メソッドが人間が読めるエラーメッセージを返せるようにします。
- FreeBSDの
-
zsyscall_freebsd_arm.go
:- FreeBSDの各システムコール(例:
read
,write
,open
,socket
など)に対して、Go言語のラッパー関数を生成します。 - 例えば、
func read(fd int, p []byte) (n int, err error)
のような関数が生成され、内部でRawSyscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(&p[0])), uintptr(len(p)))
のように、対応するシステムコール番号と引数をasm_freebsd_arm.s
で定義されたRawSyscall
関数に渡します。 - これにより、Go開発者は低レベルなアセンブリコードを直接書くことなく、Goの関数としてシステムコールを呼び出すことができます。
- FreeBSDの各システムコール(例:
-
zsysnum_freebsd_arm.go
:- FreeBSDの
sys/kern/syscalls.master
ファイルから、SYS_READ
,SYS_WRITE
,SYS_OPEN
といったシステムコール名に対応する一意の数値(システムコール番号)をGoの定数として定義します。これらの番号は、アセンブリコードがどのシステムコールを呼び出すべきかをカーネルに伝えるために使用されます。
- FreeBSDの
-
ztypes_freebsd_arm.go
:- FreeBSDのC言語のヘッダーファイル(例:
sys/types.h
,sys/stat.h
など)で定義されている構造体(例:Timespec
,Stat_t
,Kevent_t
)や型エイリアスに対応するGoの型定義を生成します。これにより、GoプログラムがOS固有のデータ構造を安全に操作できるようになります。
- FreeBSDのC言語のヘッダーファイル(例:
これらの自動生成ファイルは、Go言語がFreeBSD/ARMのシステムコールインターフェースとシームレスに連携するための重要な橋渡し役を果たします。
関連リンク
- Go言語の
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall - FreeBSDハンドブック: https://docs.freebsd.org/en/books/handbook/
- ARMアーキテクチャの概要 (Wikipedia): https://ja.wikipedia.org/wiki/ARM%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3
参考にした情報源リンク
- Go言語のソースコード(
src/pkg/syscall
ディレクトリ) - FreeBSDのシステムコールに関するドキュメント
- ARMアーキテクチャのシステムコール規約に関する情報
- Go言語のクロスコンパイルに関する公式ドキュメントやブログ記事
- Go言語の
syscall
パッケージの設計に関する議論(GoのメーリングリストやIssueトラッカー) mksyscall.pl
などのGoツールチェインの内部動作に関する情報- Go言語の
unsafe
パッケージの利用に関する情報 - Go言語のアセンブリ言語に関するドキュメント