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

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

このコミットは、Go言語のsyscallパッケージにおいて、FreeBSD/ARMアーキテクチャ向けにEABI (Embedded Application Binary Interface) の呼び出し規約に準拠したシステムコールを再生成する変更です。これにより、FreeBSD/ARM環境でのCgo(GoとC言語の相互運用機能)の動作を可能にするための準備が行われました。

コミット

commit 5d871c7ef0771e77884c7ff0345fe3458b00bbb0
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Fri Feb 7 10:23:26 2014 +0900

    syscall: regenerate EABI call convention compliant syscalls for freebsd/arm
    
    This CL is in preparation to make cgo work on freebsd/arm.
    
    LGTM=iant
    R=iant
    CC=golang-codereviews
    https://golang.org/cl/59490051

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

https://github.com/golang/go/commit/5d871c7ef0771e77884c7ff0345fe3458b00bbb0

元コミット内容

このコミットの目的は、FreeBSD/ARM環境におけるシステムコールがEABIの呼び出し規約に準拠するように再生成することです。これは、FreeBSD/ARMでCgoが正しく機能するための前提条件となります。具体的には、src/pkg/syscall/zsyscall_freebsd_arm.goファイルが変更され、mksyscall.plスクリプトの実行オプションが更新され、それに伴い生成されるシステムコールラッパーの引数渡しが修正されています。

変更の背景

Go言語は、様々なアーキテクチャやオペレーティングシステムをサポートしています。システムコールは、OSのカーネル機能にアクセスするための重要なインターフェースであり、その呼び出し規約はアーキテクチャやOSによって異なります。ARMアーキテクチャには、複数のABI(Application Binary Interface)が存在し、特にEABI (Embedded Application Binary Interface) は組み込みシステムで広く採用されています。

このコミットが行われた2014年当時、GoのFreeBSD/ARMポートにおいてCgoのサポートが課題となっていました。CgoはGoコードからCライブラリを呼び出すための機能であり、GoとCの間でデータや関数呼び出しの規約が一致している必要があります。システムコールもまた、OSカーネルというC言語で書かれたインターフェースを呼び出すため、ABIの整合性が不可欠です。

従来のシステムコール生成では、FreeBSD/ARMのEABIに完全に準拠していなかったため、特に64ビット整数(int64)のような複数のレジスタを必要とする引数の渡し方に問題がありました。この不整合がCgoの動作を妨げていたと考えられます。したがって、このコミットは、システムコールがEABIの規約に従って引数を渡すように修正し、CgoがFreeBSD/ARMで安定して動作するための基盤を構築することを目的としています。

前提知識の解説

ABI (Application Binary Interface)

ABIは、オペレーティングシステムとアプリケーション、またはアプリケーションの異なるモジュール間でのバイナリレベルのインターフェースを定義するものです。これには、以下の要素が含まれます。

  • データ型のサイズとアラインメント: 各データ型がメモリ上でどれくらいのサイズを占め、どのように配置されるか。
  • 関数呼び出し規約: 関数に引数を渡す方法(レジスタ、スタック)、戻り値を受け取る方法、スタックの管理方法など。
  • システムコール規約: アプリケーションがOSカーネルのサービスを呼び出す方法。
  • レジスタの使用規約: 関数呼び出し時にどのレジスタが呼び出し側によって保存され、どのレジスタが呼び出された側によって保存されるか。

ABIが異なると、バイナリ互換性が失われ、異なるABIでコンパイルされたコードは互いに連携できません。

EABI (Embedded Application Binary Interface)

EABIは、ARMアーキテクチャ向けの特定のABIであり、特に組み込みシステムやモバイルデバイスで広く採用されています。EABIは、効率的なコード生成と実行を目的としており、浮動小数点演算や64ビット整数などの扱いに関して特定の規約を定めています。

ARMのEABIでは、関数呼び出しにおいて、引数は通常レジスタ(R0-R3)を介して渡されます。64ビットの引数(long longint64など)は、2つの32ビットレジスタのペア(例: R0とR1)を使用して渡されます。この「レジスタペア」による引数渡しは、従来のARM ABIとは異なる重要な点であり、Goのシステムコールラッパーがこれに準拠する必要がありました。

Goのsyscallパッケージとmksyscall.pl

Go言語のsyscallパッケージは、OSのシステムコールをGoから呼び出すためのインターフェースを提供します。このパッケージ内の多くのシステムコールラッパーは、手書きではなく、mksyscall.plというPerlスクリプトによって自動生成されます。

mksyscall.plは、Goのソースファイル(例: syscall_bsd.go, syscall_freebsd.goなど)に記述されたシステムコールの定義を読み込み、各アーキテクチャとOSに特化したzsyscall_*.goファイルを生成します。この生成プロセスにおいて、引数の型や数に応じて、Syscall, Syscall6, Syscall9などのGoの内部関数が使用されます。これらの関数は、実際のシステムコールをトリガーし、引数をOSカーネルに渡す役割を担います。

mksyscall.plに渡されるオプションは、生成されるコードのABI準拠性に影響を与えます。例えば、-armオプションはARMアーキテクチャ向けの特定のABI規約(この場合はEABI)に従ってコードを生成するよう指示します。

Syscall, Syscall6, Syscall9

これらはGoのsyscallパッケージ内部で使用される低レベルの関数で、実際のシステムコール呼び出しを行います。

  • Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno): 最大3つの引数を持つシステムコール用。
  • Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno): 最大6つの引数を持つシステムコール用。
  • Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno): 最大9つの引数を持つシステムコール用。

これらの関数は、uintptr型の引数を受け取ります。int64のような64ビット値は、通常、2つのuintptr(32ビットシステムでは2つのレジスタ)に分割して渡されます。EABIの規約では、これらの分割された値が特定のレジスタにどのように配置されるかが重要になります。

技術的詳細

このコミットの核心は、mksyscall.plスクリプトの呼び出し方法を変更し、FreeBSD/ARM向けのシステムコール生成時にEABIの規約を適用することです。

変更前:

// mksyscall.pl -l32 syscall_bsd.go syscall_freebsd.go syscall_freebsd_386.go

変更後:

// mksyscall.pl -l32 -arm syscall_bsd.go syscall_freebsd.go syscall_freebsd_arm.go

追加された-armオプションは、mksyscall.plに対して、ARMアーキテクチャのEABIに準拠したシステムコールラッパーを生成するよう指示します。これにより、特に64ビット整数(int64)の引数を持つシステムコールにおいて、引数のレジスタ配置がEABIの規約に合うように調整されます。

具体的には、EABIでは64ビット値はレジスタペアで渡されますが、その際に「偶数レジスタから始まる」という制約があります。例えば、int64R0R1で渡される場合、もしR0が既に他の引数で使われていると、int64R2R3に渡されることになります。しかし、従来のGoのシステムコールラッパーの生成では、このレジスタアラインメントが考慮されていなかった可能性があります。

このコミットでは、Ftruncate, Pread, Pwrite, Seek, Truncate, mmapといったシステムコールで、int64型の引数(length, offset, pos)の渡し方が変更されています。

例: Ftruncateの変更 変更前:

func Ftruncate(fd int, length int64) (err error) {
	_, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), uintptr(length>>32))
	// ...
}

変更後:

func Ftruncate(fd int, length int64) (err error) {
	_, _, e1 := Syscall6(SYS_FTRUNCATE, uintptr(fd), 0, uintptr(length), uintptr(length>>32), 0, 0)
	// ...
}

変更前はSyscallが使われていましたが、変更後はSyscall6が使われています。そして、lengthuintptr(length)uintptr(length>>32)の間に0が挿入されています。これは、EABIの規約に従って、64ビット値の引数が適切なレジスタペアに配置されるように、ダミーの引数(0)を挿入してレジスタアラインメントを調整していることを示唆しています。

同様に、Pread, Pwrite, Seek, Truncate, mmapでも、64ビット引数の前に0が挿入されたり、引数の順序が変更されたりしています。これは、mksyscall.pl-armオプションによって生成するコードが、EABIのレジスタ割り当て規約に厳密に従うようになった結果です。

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

変更はsrc/pkg/syscall/zsyscall_freebsd_arm.goファイルに集中しています。

--- a/src/pkg/syscall/zsyscall_freebsd_arm.go
+++ b/src/pkg/syscall/zsyscall_freebsd_arm.go
@@ -1,4 +1,4 @@
-// mksyscall.pl -l32 syscall_bsd.go syscall_freebsd.go syscall_freebsd_386.go
+// mksyscall.pl -l32 -arm syscall_bsd.go syscall_freebsd.go syscall_freebsd_arm.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
 package syscall
@@ -500,7 +500,7 @@ func Fsync(fd int) (err error) {\n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n func Ftruncate(fd int, length int64) (err error) {\n-\t_, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), uintptr(length>>32))\n+\t_, _, e1 := Syscall6(SYS_FTRUNCATE, uintptr(fd), 0, uintptr(length), uintptr(length>>32), 0, 0)\n \tif e1 != 0 {\n \t\terr = e1\n \t}\n@@ -853,7 +853,7 @@ func Pread(fd int, p []byte, offset int64) (n int, err error) {\n \t} else {\n \t\t_p0 = unsafe.Pointer(&_zero)\n \t}\n-\tr0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)\n+\tr0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32))\n \tn = int(r0)\n \tif e1 != 0 {\n \t\terr = e1\n@@ -870,7 +870,7 @@ func Pwrite(fd int, p []byte, offset int64) (n int, err error) {\n \t} else {\n \t\t_p0 = unsafe.Pointer(&_zero)\n \t}\n-\tr0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)\n+\tr0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32))\n \tn = int(r0)\n \tif e1 != 0 {\n \t\terr = e1\n@@ -970,7 +970,7 @@ func Rmdir(path string) (err error) {\n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {\n-\tr0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(offset>>32), uintptr(whence), 0, 0)\n+\tr0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), 0, uintptr(offset), uintptr(offset>>32), uintptr(whence), 0)\n \tnewoffset = int64(int64(r1)<<32 | int64(r0))\n \tif e1 != 0 {\n \t\terr = e1\n@@ -1182,7 +1182,7 @@ func Truncate(path string, length int64) (err error) {\n \tif err != nil {\n \t\treturn\n \t}\n-\t_, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), uintptr(length>>32))\n+\t_, _, e1 := Syscall6(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), 0, uintptr(length), uintptr(length>>32), 0, 0)\n \tif e1 != 0 {\n \t\terr = e1\n \t}\n@@ -1262,7 +1262,7 @@ func write(fd int, p []byte) (n int, err error) {\n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) {\n-\tr0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), uintptr(pos), uintptr(pos>>32), 0, 0)\n+\tr0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), 0, uintptr(pos), uintptr(pos>>32), 0)\n \tret = uintptr(r0)\n \tif e1 != 0 {\n \t\terr = e1\n```

## コアとなるコードの解説

このコミットの主要な変更点は、`src/pkg/syscall/zsyscall_freebsd_arm.go`ファイルの冒頭にある`mksyscall.pl`の呼び出しコマンドの変更と、それに伴って自動生成されたシステムコールラッパーの引数渡し部分の修正です。

1.  **`mksyscall.pl`の呼び出しコマンドの変更**:
    ```diff
    --- a/src/pkg/syscall/zsyscall_freebsd_arm.go
    +++ b/src/pkg/syscall/zsyscall_freebsd_arm.go
    @@ -1,4 +1,4 @@
    -// mksyscall.pl -l32 syscall_bsd.go syscall_freebsd.go syscall_freebsd_386.go
    +// mksyscall.pl -l32 -arm syscall_bsd.go syscall_freebsd.go syscall_freebsd_arm.go
    ```
    この変更は、`zsyscall_freebsd_arm.go`ファイルがどのように生成されるかを示しています。以前は`syscall_freebsd_386.go`が参照されていましたが、ARMアーキテクチャに特化した`syscall_freebsd_arm.go`が参照されるようになり、さらに`-arm`オプションが追加されました。この`-arm`オプションが、EABI準拠のコード生成をトリガーする重要な役割を果たします。

2.  **システムコールラッパーの引数渡し修正**:
    `Ftruncate`, `Pread`, `Pwrite`, `Seek`, `Truncate`, `mmap`といった関数で、`int64`型の引数(`length`, `offset`, `pos`)の渡し方が変更されています。

    *   **`Ftruncate`と`Truncate`**:
        `Syscall`から`Syscall6`への変更と、`uintptr(length)`と`uintptr(length>>32)`の間に`0`が挿入されています。
        変更前: `Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), uintptr(length>>32))`
        変更後: `Syscall6(SYS_FTRUNCATE, uintptr(fd), 0, uintptr(length), uintptr(length>>32), 0, 0)`
        これは、`length`という64ビット値が、EABIの規約に従って適切なレジスタペアに配置されるように、ダミーの引数(`0`)を挿入してレジスタアラインメントを調整していることを示しています。`Syscall`は3つの引数しか取れませんが、`Syscall6`は6つの引数を取れるため、より柔軟な引数配置が可能になります。

    *   **`Pread`と`Pwrite`**:
        `Syscall6`の引数リスト内で、`uintptr(len(p))`と`uintptr(offset)`の間に`0`が挿入され、`offset`の低位と高位の順序が変更されています。
        変更前: `Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)`
        変更後: `Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), 0, uintptr(offset), uintptr(offset>>32))`
        ここでも、`offset`という64ビット値がEABIの規約に沿って渡されるように、ダミーの`0`が挿入され、引数の位置が調整されています。

    *   **`Seek`**:
        `Syscall6`の引数リスト内で、`uintptr(fd)`と`uintptr(offset)`の間に`0`が挿入されています。
        変更前: `Syscall6(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(offset>>32), uintptr(whence), 0, 0)`
        変更後: `Syscall6(SYS_LSEEK, uintptr(fd), 0, uintptr(offset), uintptr(offset>>32), uintptr(whence), 0)`
        これも同様に、`offset`の64ビット値がEABIのレジスタアラインメント要件を満たすように調整されています。

    *   **`mmap`**:
        `Syscall9`の引数リスト内で、`uintptr(fd)`と`uintptr(pos)`の間に`0`が挿入されています。
        変更前: `Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), uintptr(pos), uintptr(pos>>32), 0, 0)`
        変更後: `Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), 0, uintptr(pos), uintptr(pos>>32), 0)`
        ここでも、`pos`という64ビット値がEABIの規約に沿って渡されるように、ダミーの`0`が挿入され、引数の位置が調整されています。

これらの変更は、`mksyscall.pl`が`-arm`オプションを受け取った際に、ARM EABIの呼び出し規約、特に64ビット引数のレジスタ配置に関するルールを正確に反映したコードを生成するようになった結果です。これにより、Goの`syscall`パッケージがFreeBSD/ARM上でCgoと連携する際のABIの不整合が解消され、Cgoが正しく動作するための基盤が整えられました。

## 関連リンク

*   Go言語の`syscall`パッケージのドキュメント: [https://pkg.go.dev/syscall](https://pkg.go.dev/syscall)
*   Go言語のCgoに関するドキュメント: [https://go.dev/blog/cgo](https://go.dev/blog/cgo)
*   ARM EABIに関する情報 (例: ARM社の公式ドキュメントや関連する技術記事)

## 参考にした情報源リンク

*   Go言語のソースコード (特に`src/pkg/syscall`ディレクトリ)
*   ARM Architecture Reference Manual (ARM ARM) - EABIに関する詳細な情報
*   FreeBSDのシステムコールに関するドキュメント
*   Go言語のコミット履歴と関連するコードレビュー (CL 59490051)
*   [https://golang.org/cl/59490051](https://golang.org/cl/59490051) (コミットメッセージに記載されているGoのコードレビューリンク)
*   [https://go.dev/doc/install/source#freebsd](https://go.dev/doc/install/source#freebsd) (GoのFreeBSDサポートに関する情報)
*   [https://go.dev/doc/install/source#arm](https://go.dev/doc/install/source#arm) (GoのARMサポートに関する情報)
*   [https://en.wikipedia.org/wiki/Application_binary_interface](https://en.wikipedia.org/wiki/Application_binary_interface) (ABIに関するWikipedia記事)
*   [https://en.wikipedia.org/wiki/ARM_architecture#ARM_EABI](https://en.wikipedia.org/wiki/ARM_architecture#ARM_EABI) (ARM EABIに関するWikipedia記事)
*   [https://github.com/golang/go/blob/master/src/syscall/mksyscall.pl](https://github.com/golang/go/blob/master/src/syscall/mksyscall.pl) (mksyscall.plスクリプトのソースコード)