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

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

このコミットは、Go言語のsyscallパッケージにおけるFlock_t構造体を、Linux ARMアーキテクチャのEABI (Embedded Application Binary Interface) に準拠させるために再生成するものです。具体的には、構造体内のフィールドのアライメントを調整するためにパディングを追加しています。

コミット

commit 2ff431189e4be35d3121cce7cc7d3df21dcfd71c
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Mon Feb 24 08:36:56 2014 -0800

    syscall: regenerate Flock_t to make it compliant with EABI
    
    Note that current z-files for linux/amd64,386,arm are based on 3.2 kernel.
    
    LGTM=iant
    R=golang-codereviews, dave, bradfitz, gobot, iant
    CC=golang-codereviews
    https://golang.org/cl/59160044

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

https://github.com/golang/go/commit/2ff431189e4be35d3121cce7cc7d3df21dcfd71c

元コミット内容

syscall: regenerate Flock_t to make it compliant with EABI

このコミットは、syscallパッケージ内のFlock_t構造体を再生成し、EABI (Embedded Application Binary Interface) に準拠させることを目的としています。また、現在のLinux/amd64, 386, arm向けのz-filesが3.2カーネルに基づいていることに言及しています。

変更の背景

この変更の背景には、Go言語が異なるアーキテクチャやオペレーティングシステム上で正しく動作するためのABI (Application Binary Interface) 互換性の問題があります。特にARMアーキテクチャでは、EABIという特定のABIが広く採用されており、データ構造のメモリ配置やアライメントに関する厳格なルールが存在します。

Flock_t構造体は、Unix系のシステムコールであるflockfcntlを用いたファイルロック操作に関連する情報を保持する構造体です。この構造体がEABIの要件を満たしていない場合、Goプログラムがファイルロックシステムコールを呼び出す際に、カーネルとの間でデータのやり取りが正しく行われず、予期せぬ動作やクラッシュを引き起こす可能性があります。

コミットメッセージにある「current z-files for linux/amd64,386,arm are based on 3.2 kernel」という記述は、Goのsyscallパッケージがシステムコールに関連する定数や構造体を生成する際に、特定のカーネルバージョン(この場合はLinux 3.2)のヘッダーファイルを参照していることを示唆しています。カーネルのバージョンやアーキテクチャによって構造体のアライメント要件が異なるため、EABIに準拠させるためには、そのアーキテクチャとカーネルバージョンに合わせた構造体の定義が必要となります。

このコミットは、GoプログラムがARM Linux環境でファイルロックを安全かつ正確に実行できるようにするために、Flock_t構造体のメモリレイアウトをEABIの仕様に合わせる必要があったために行われました。

前提知識の解説

ABI (Application Binary Interface)

ABIは、オペレーティングシステムとアプリケーション、またはアプリケーションの異なるモジュール間で、バイナリレベルでの互換性を定義する一連のルールです。これには、関数呼び出し規約(引数の渡し方、戻り値の受け取り方)、データ型のサイズとアライメント、レジスタの使用方法などが含まれます。ABIが異なると、ある環境でコンパイルされたバイナリが別の環境で正しく動作しない可能性があります。

EABI (Embedded Application Binary Interface)

EABIは、特に組み込みシステム向けのARMプロセッサで使用されるABIの標準です。通常のABIと比較して、リソースが限られた環境での効率性を重視しており、データのアライメント、スタックフレームのレイアウト、レジスタの使用方法などについて特定の規則を定めています。Go言語のようなクロスプラットフォーム言語がARM環境で動作するためには、このEABIに準拠したコードを生成する必要があります。

データ構造のアライメントとパディング

コンピュータのメモリはバイト単位でアドレス指定されますが、CPUは通常、特定のバイト数のブロック(ワード)単位でデータを読み書きします。効率的なメモリアクセスのためには、データ構造内の各フィールドがそのデータ型に適したメモリアドレスに配置されている必要があります。これを「アライメント」と呼びます。

例えば、4バイトの整数型は4の倍数のアドレスに配置されるのが一般的です。もし、前のフィールドが原因で次のフィールドが適切なアライメント境界に配置されない場合、コンパイラは自動的に「パディング」と呼ばれる未使用のバイトを挿入して、次のフィールドが正しくアライメントされるようにします。

Flock_tのようなシステムコールで使用される構造体の場合、このアライメントとパディングは特に重要です。カーネルは特定のメモリレイアウトを期待しており、Goプログラムが異なるレイアウトで構造体を渡すと、カーネルがデータを誤って解釈し、システムコールが失敗したり、データが破損したりする可能性があります。

Flock_t構造体とファイルロック

Flock_t構造体は、Unix系のシステムコールであるfcntlflock関数で使用され、ファイルロックに関する情報を定義します。主なフィールドは以下の通りです。

  • Type: ロックの種類(共有ロック、排他ロック、アンロックなど)
  • Whence: ロック範囲の開始位置の基準(ファイルの先頭、現在の位置、ファイルの末尾)
  • Start: ロック範囲の開始オフセット
  • Len: ロック範囲の長さ
  • Pid: ロックを保持しているプロセスのID(読み取り専用)

これらのフィールドは、ファイルの一部または全体をロックして、複数のプロセスが同時にファイルにアクセスすることによる競合状態を防ぐために使用されます。

Goのsyscallパッケージとz-files

Go言語のsyscallパッケージは、オペレーティングシステムの低レベルな機能(システムコール)にアクセスするためのインターフェースを提供します。このパッケージは、各OSとアーキテクチャの組み合わせ(例: linux/arm, linux/amd64)ごとに、システムコールに関連する定数、構造体、関数などを定義しています。

「z-files」とは、Goのビルドプロセス中に自動生成されるファイル群を指します。これらは通常、C言語のヘッダーファイルからGoの構造体や定数を自動的に生成するために使用されます。例えば、Linuxカーネルのヘッダーファイルから、Goのsyscallパッケージが使用するFlock_tのような構造体の定義が生成されます。コミットメッセージにある「current z-files for linux/amd64,386,arm are based on 3.2 kernel」は、これらの自動生成ファイルがLinuxカーネルバージョン3.2の定義に基づいていることを意味します。

技術的詳細

このコミットの技術的な核心は、ARMアーキテクチャにおけるEABIのデータアライメント要件と、それがFlock_t構造体に与える影響です。

元のFlock_t構造体は以下のようになっていました。

type Flock_t struct {
	Type   int16
	Whence int16
	Start  int64
	Len    int64
	Pid    int32
}

ここで注目すべきは、Type (int16) と Whence (int16) の後に Start (int64) が続く点です。int16は2バイト、int64は8バイトです。EABIでは、8バイトのデータ型(int64など)は8バイト境界にアライメントされる必要があります。

元の構造体では、TypeWhenceが合計4バイトを占めます。その直後にStartが続くと、Startは4バイト境界に配置されますが、8バイト境界には配置されません。これにより、EABIの要件を満たさなくなります。

この問題を解決するために、コンパイラは通常、自動的にパディングを挿入しますが、システムコールインターフェースでは、Goの構造体レイアウトがカーネルの期待するレイアウトと完全に一致している必要があります。Goのsyscallパッケージは、C言語の構造体をGoの構造体にマッピングするため、明示的なパディングが必要になることがあります。

変更後のFlock_t構造体は以下のようになります。

type Flock_t struct {
	Type      int16
	Whence    int16
	Pad_cgo_0 [4]byte // 4バイトのパディング
	Start     int64
	Len       int64
	Pid       int32
	Pad_cgo_1 [4]byte // 4バイトのパディング
}
  • Type (int16) と Whence (int16) の後に、Pad_cgo_0 [4]byte が追加されています。これにより、TypeWhenceで消費される4バイトと合わせて、次のStartフィールドが8バイト境界に配置されるようになります。
    • Type (2バイト) + Whence (2バイト) = 4バイト。
    • Pad_cgo_0 (4バイト) を追加することで、合計8バイトとなり、Startが8バイト境界に配置されます。
  • Pid (int32) の後に Pad_cgo_1 [4]byte が追加されています。これは、構造体全体のサイズを8の倍数にするためのパディングであると考えられます。構造体の合計サイズがアライメント要件を満たすように調整されます。

この変更により、GoのFlock_t構造体のメモリレイアウトがARM LinuxのEABIに準拠し、カーネルが期待するflock構造体と一致するようになります。これにより、Goプログラムがファイルロックシステムコールを正しく呼び出し、安定して動作することが保証されます。

コミットメッセージにある「Note that current z-files for linux/amd64,386,arm are based on 3.2 kernel」という記述は、この変更がLinux 3.2カーネルのABI定義に基づいて行われたことを示しています。異なるカーネルバージョンでは、構造体の定義やアライメント要件がわずかに異なる場合があるため、Goのsyscallパッケージは特定のカーネルバージョンに「固定」されていることが一般的です。

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

--- a/src/pkg/syscall/ztypes_linux_arm.go
+++ b/src/pkg/syscall/ztypes_linux_arm.go
@@ -145,11 +145,13 @@ type Fsid struct {
 }
 
 type Flock_t struct {
-	Type   int16
-	Whence int16
-	Start  int64
-	Len    int64
-	Pid    int32
+	Type      int16
+	Whence    int16
+	Pad_cgo_0 [4]byte
+	Start     int64
+	Len       int64
+	Pid       int32
+	Pad_cgo_1 [4]byte
 }
 
 type RawSockaddrInet4 struct {

コアとなるコードの解説

変更はsrc/pkg/syscall/ztypes_linux_arm.goファイル内のFlock_t構造体に集中しています。

  • 削除された行:

    -	Type   int16
    -	Whence int16
    -	Start  int64
    -	Len    int64
    -	Pid    int32
    

    これは、元のFlock_t構造体の定義です。

  • 追加された行:

    +	Type      int16
    +	Whence    int16
    +	Pad_cgo_0 [4]byte
    +	Start     int64
    +	Len       int64
    +	Pid       int32
    +	Pad_cgo_1 [4]byte
    

    新しい定義では、以下の2つのパディングフィールドが追加されています。

    1. Pad_cgo_0 [4]byte: Whence (int16) の直後、Start (int64) の直前に挿入されています。これにより、TypeWhenceの合計4バイトにこの4バイトのパディングが加わり、Startフィールドが8バイト境界に正しくアライメントされるようになります。
    2. Pad_cgo_1 [4]byte: Pid (int32) の直後に挿入されています。これは、構造体全体のサイズをEABIの要件(通常は最大アライメントの倍数)に合わせるための末尾のパディングです。

これらのパディングフィールドは、GoのコンパイラがCgo(GoとC言語の相互運用機能)を使用する際に、C言語の構造体とGoの構造体のメモリレイアウトを一致させるために自動的に挿入されることがあるパターンです。ztypes_linux_arm.goのような自動生成されるファイルでは、このような明示的なパディングがABI互換性を保証するために必要となります。

関連リンク

  • Go言語のsyscallパッケージのドキュメント: https://pkg.go.dev/syscall
  • Linux flock manページ: man 2 flock
  • Linux fcntl manページ: man 2 fcntl

参考にした情報源リンク