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

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

このコミットは、Go言語のsyscallパッケージにおけるLinux/ARMアーキテクチャ向けのStatfsおよびFstatfsシステムコールの実装に関する修正です。具体的には、これらの関数が正しく動作するように、引数の渡し方とシステムコール呼び出しのロジックが変更されています。

コミット

commit f8485954bf06546675ac779f18fa7e8fb561113a
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Mon Oct 8 00:13:28 2012 +0800

    syscall: fix Statfs and Fstatfs on Linux/ARM
    
    R=golang-dev, dave, rsc
    CC=golang-dev
    https://golang.org/cl/6615055
---
 src/pkg/syscall/syscall_linux_arm.go  | 24 ++++++++++++++++++++++--
 src/pkg/syscall/zsyscall_linux_arm.go | 25 -------------------------
 2 files changed, 22 insertions(+), 27 deletions(-)

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

https://github.com/golang/go/commit/f8485954bf06546675ac779f18fa7e8fb561113a

元コミット内容

syscall: fix Statfs and Fstatfs on Linux/ARM

このコミットメッセージは、Linux/ARM環境におけるStatfsおよびFstatfsシステムコールの問題を修正することを示しています。

変更の背景

Go言語のsyscallパッケージは、オペレーティングシステムの低レベルな機能(システムコール)にアクセスするためのインターフェースを提供します。システムコールはOSのカーネルが提供するサービスを利用するための唯一の手段であり、ファイルシステム情報の取得(StatfsFstatfsなど)もその一つです。

Linux/ARMアーキテクチャでは、システムコールの呼び出し規約(レジスタへの引数の渡し方など)が他のアーキテクチャ(x86-64など)と異なる場合があります。特に、構造体を引数として渡す場合や、ポインタとサイズを同時に渡す場合に、特定のレジスタに正しい値を配置する必要があります。

このコミット以前のGoのsyscallパッケージにおけるStatfsFstatfsの実装は、Linux/ARM環境で正しく機能していませんでした。これはおそらく、システムコールに渡される引数(特にStatfs_t構造体へのポインタとそのサイズ)が、ARMのシステムコール規約に合致していなかったためと考えられます。結果として、これらの関数を呼び出すと、不正なデータが渡されたり、システムコールが期待通りに実行されなかったりする問題が発生していました。

この修正は、Linux/ARM環境でファイルシステム情報を正確に取得できるようにするために不可欠でした。

前提知識の解説

  • システムコール (System Call): オペレーティングシステムが提供するサービスをプログラムが利用するためのインターフェースです。ファイル操作、メモリ管理、プロセス制御など、OSの機能にアクセスするために使用されます。
  • Statfs / Fstatfs: これらはファイルシステムに関する統計情報を取得するためのシステムコールです。
    • Statfs: 指定されたパス(ファイルまたはディレクトリ)が属するファイルシステムの情報を取得します。
    • Fstatfs: 指定されたファイルディスクリプタに関連付けられたファイルシステムの情報を取得します。
    • 取得される情報には、ブロックサイズ、総ブロック数、空きブロック数、利用可能なブロック数、inode数などが含まれます。
  • Statfs_t 構造体: StatfsおよびFstatfsシステムコールによって返されるファイルシステム情報を格納するための構造体です。この構造体の定義は、OSやアーキテクチャによって異なる場合があります。
  • ARMアーキテクチャ: Advanced RISC Machineの略で、モバイルデバイスや組み込みシステムで広く使用されているCPUアーキテクチャです。RISC(Reduced Instruction Set Computer)の原則に基づいて設計されており、電力効率と性能のバランスが特徴です。
  • システムコール規約: 特定のアーキテクチャ(例: ARM)とOS(例: Linux)の組み合わせにおいて、システムコールを呼び出す際に、引数をどのようにレジスタに配置するか、戻り値をどのように取得するかといった取り決めです。これはABI (Application Binary Interface) の一部です。
  • Syscall 関数 (Go言語): Go言語のsyscallパッケージで提供される汎用的なシステムコール呼び出し関数です。引数としてシステムコール番号と、最大3つのuintptr型の引数を取ります。
  • unsafe.Pointer (Go言語): Go言語のunsafeパッケージで提供される型で、任意の型のポインタを保持できます。Goの型システムをバイパスして、低レベルなメモリ操作を行う際に使用されます。システムコールに構造体へのポインタを渡す際などに利用されます。
  • unsafe.Sizeof (Go言語): Go言語のunsafeパッケージで提供される関数で、指定された型のサイズ(バイト単位)を返します。システムコールに構造体のサイズを渡す際などに利用されます。
  • zsyscall_linux_arm.go: Goのsyscallパッケージにおいて、特定のアーキテクチャ(この場合はLinux/ARM)向けのシステムコールラッパー関数が自動生成されるファイルです。通常、mksyscall.plのようなスクリプトによって生成されます。

技術的詳細

Linux/ARMにおけるStatfsおよびFstatfsシステムコールは、通常、statfsまたはfstatfsという名前で提供されます。これらのシステムコールは、ファイルシステム情報を格納するための構造体へのポインタと、その構造体のサイズを引数として受け取ることが一般的です。

元の実装では、zsyscall_linux_arm.goファイルに自動生成されたFstatfsおよびStatfs関数が使用されていました。これらの関数は、Syscall関数を呼び出す際に、Statfs_t構造体のポインタを渡していましたが、構造体のサイズを適切に渡していなかった可能性があります。

ARMアーキテクチャのシステムコール規約では、システムコールに構造体を渡す場合、その構造体へのポインタだけでなく、構造体のサイズも別の引数として渡す必要がある場合があります。これは、カーネルが渡された構造体のサイズを知ることで、正しいバージョンの構造体(例えば、32ビットシステムと64ビットシステムで構造体のレイアウトが異なる場合など)を処理できるようにするためです。

このコミットでは、src/pkg/syscall/syscall_linux_arm.goFstatfsStatfsのカスタム実装を追加することで、この問題を解決しています。新しい実装では、Syscall関数を呼び出す際に、unsafe.Sizeof(*buf)を使用してStatfs_t構造体のサイズを明示的に第2引数として渡しています。これにより、カーネルが期待する引数の形式に合致し、システムコールが正しく実行されるようになります。

また、zsyscall_linux_arm.goからこれらの自動生成された関数を削除することで、カスタム実装が優先されるようにしています。これは、自動生成されたコードが特定のアーキテクチャの特殊なシステムコール規約に対応できない場合に、手動でオーバーライドする一般的なパターンです。

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

src/pkg/syscall/syscall_linux_arm.go

  • import "unsafe" が追加されました。これは、unsafe.Sizeofunsafe.Pointerを使用するために必要です。
  • //sys Fstatfs(fd int, buf *Statfs_t) (err error) = SYS_FSTATFS64 の行がコメントアウトされました。これは、自動生成されるFstatfs関数ではなく、手動で実装するFstatfs関数を使用することを示すためです。
  • //sys Statfs(path string, buf *Statfs_t) (err error) = SYS_STATFS64 の行がコメントアウトされました。これも同様に、手動で実装するStatfs関数を使用することを示すためです。
  • 以下のFstatfs関数のカスタム実装が追加されました。
    func Fstatfs(fd int, buf *Statfs_t) (err error) {
        _, _, e := Syscall(SYS_FSTATFS64, uintptr(fd), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf)))
        if e != 0 {
            err = e
        }
        return
    }
    
    この実装では、SYS_FSTATFS64システムコールを呼び出す際に、ファイルディスクリプタfdStatfs_t構造体のサイズunsafe.Sizeof(*buf)、そしてStatfs_t構造体へのポインタuintptr(unsafe.Pointer(buf))を引数として渡しています。
  • 以下のStatfs関数のカスタム実装が追加されました。
    func Statfs(path string, buf *Statfs_t) (err error) {
        pathp, err := BytePtrFromString(path)
        if err != nil {
            return err
        }
        _, _, e := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(pathp)), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf)))
        if e != 0 {
            err = e
        }
        return
    }
    
    この実装では、SYS_STATFS64システムコールを呼び出す際に、パス文字列へのポインタuintptr(unsafe.Pointer(pathp))Statfs_t構造体のサイズunsafe.Sizeof(*buf)、そしてStatfs_t構造体へのポインタuintptr(unsafe.Pointer(buf))を引数として渡しています。

src/pkg/syscall/zsyscall_linux_arm.go

  • 自動生成されていたFstatfs関数が削除されました。
  • 自動生成されていたStatfs関数が削除されました。

コアとなるコードの解説

この修正の核心は、Linux/ARMアーキテクチャにおけるStatfsおよびFstatfsシステムコールの呼び出し規約に正確に合わせるために、Syscall関数に渡す引数を調整した点にあります。

以前の自動生成されたコードでは、Statfs_t構造体へのポインタのみを渡していた可能性があります。しかし、Linux/ARMの特定のバージョンやカーネルでは、statfsfstatfsシステムコールが、構造体へのポインタに加えて、その構造体のサイズも引数として期待する場合があります。これは、カーネルが異なるバージョンのstatfs構造体(例えば、32ビットと64ビットのシステムでフィールドの配置やサイズが異なる場合)を区別するために必要となることがあります。

新しい実装では、Syscall関数の第2引数としてunsafe.Sizeof(*buf)を明示的に追加しています。これにより、システムコールはファイルディスクリプタ(またはパス)、構造体のサイズ、そして構造体へのポインタという3つの引数を受け取ることになります。この引数の並びと内容が、Linux/ARMカーネルが期待するstatfs/fstatfsシステムコールの呼び出し規約に合致することで、システムコールが正しく解釈され、ファイルシステム情報が正確に取得できるようになります。

unsafeパッケージの使用は、Goの型安全性を一時的にバイパスして、低レベルなメモリ操作を行うことを意味します。これはシステムコールのようなOSとの直接的なインターフェースを扱う際には一般的ですが、誤用するとプログラムの不安定性やセキュリティ上の問題を引き起こす可能性があるため、慎重に扱う必要があります。このケースでは、システムコールの正確な引数渡しのために必要不可欠な使用法です。

zsyscall_linux_arm.goから該当する関数を削除したことは、Goのビルドシステムが自動生成するコードよりも、手動で記述されたsyscall_linux_arm.go内のコードを優先させるための標準的な方法です。これにより、特定のアーキテクチャやOSの特殊な要件に対応するための柔軟性が提供されます。

関連リンク

  • Go言語のsyscallパッケージのドキュメント: https://pkg.go.dev/syscall
  • Linux statfs manページ: man 2 statfs (Linuxシステムでコマンドラインから参照可能)
  • Linux fstatfs manページ: man 2 fstatfs (Linuxシステムでコマンドラインから参照可能)
  • Go言語のunsafeパッケージのドキュメント: https://pkg.go.dev/unsafe

参考にした情報源リンク

  • Go言語のソースコード(特にsrc/pkg/syscallディレクトリ)
  • Linuxカーネルのシステムコール実装に関するドキュメントやソースコード
  • ARMアーキテクチャのABI(Application Binary Interface)に関するドキュメント
  • Go言語のIssueトラッカーやコードレビューシステム(CL: Change List)