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

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

このコミットは、GoランタイムがFreeBSD/ARMアーキテクチャ上で正しくビルドされるようにするための修正を含んでいます。特に、Cgo(GoとC言語の相互運用機能)がFreeBSD/ARMで動作するための準備として、構造体のアライメントに関する問題に対処しています。

コミット

commit 8e56eb8b57220579e95e87c1370b7bb770c2fc11
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Fri Feb 7 10:22:13 2014 +0900

    runtime: fix build on freebsd/arm
    
    This CL is in preparation to make cgo work on freebsd/arm.
    
    How to generate defs-files on freebsd/arm in the bootstrapping phase:
    1. run freebsd on appropriate arm-eabi platforms
    2. both syscall z-files and runtime def-files in the current tree are
       broken about EABI padding, fix them by hand
    3. run make.bash again to build $GOTOOLDIR/cgo
    4. use $GOTOOLDIR/cgo directly
    
    LGTM=minux.ma, iant
    R=iant, minux.ma, dave
    CC=golang-codereviews
    https://golang.org/cl/59580045

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

https://github.com/golang/go/commit/8e56eb8b57220579e95e87c1370b7bb770c2fc11

元コミット内容

このコミットの目的は、FreeBSD/ARM環境でのGoランタイムのビルド問題を修正することです。特に、CgoがFreeBSD/ARMで動作するための準備として行われました。コミットメッセージには、ブートストラップフェーズでdefs-filesを生成する手順が記載されており、既存のsyscall z-filesruntime def-filesがEABIパディングに関して壊れているため、手動で修正する必要があることが示唆されています。

変更の背景

Go言語はクロスプラットフォーム対応を重視しており、様々なオペレーティングシステムとアーキテクチャで動作するように設計されています。このコミットが作成された2014年当時、GoはFreeBSD/ARM環境でのCgoのサポートを強化しようとしていました。CgoはGoプログラムからC言語のコードを呼び出すための重要な機能であり、システムコールや外部ライブラリとの連携に不可欠です。

しかし、異なるアーキテクチャやABI(Application Binary Interface)間でのデータ構造の整合性は、しばしば問題となります。特に、構造体のアライメントやパディングは、コンパイラやOSのABIによって異なることがあり、これが原因でデータが正しく解釈されず、プログラムがクラッシュしたり、予期せぬ動作をしたりすることがあります。

このコミットは、FreeBSD/ARM環境におけるEABI(Embedded Application Binary Interface)のパディング規則と、Goランタイムが期待する構造体レイアウトとの間に不一致があったために必要となりました。この不一致が、syscall z-files(システムコール定義ファイル)やruntime def-files(ランタイム定義ファイル)の生成に影響を与え、結果としてGoランタイムのビルドを妨げていたと考えられます。

前提知識の解説

1. ARMアーキテクチャとEABI

  • ARMアーキテクチャ: スマートフォン、タブレット、組み込みシステムなど、低消費電力デバイスで広く使用されているCPUアーキテクチャです。
  • ABI (Application Binary Interface): オペレーティングシステムとアプリケーション、または異なるモジュール間でバイナリレベルでの互換性を定義する規約です。これには、関数呼び出し規約、データ型表現、メモリレイアウト、レジスタの使用方法などが含まれます。
  • EABI (Embedded Application Binary Interface): ARMプロセッサ向けのABIの標準です。組み込みシステムでの効率的なコード生成と互換性を目的としています。EABIは、構造体のパディングやアライメント、関数呼び出し時の引数渡しなど、バイナリレベルでの詳細な規約を定めています。異なるコンパイラやツールチェーンがEABIに準拠することで、生成されたバイナリコードが互換性を持つようになります。

2. 構造体のアライメントとパディング

  • アライメント (Alignment): メモリ上でデータが配置されるアドレスの制約です。CPUは特定のバイト境界(例: 4バイト境界、8バイト境界)に配置されたデータを効率的にアクセスできます。データ型にはそれぞれ推奨されるアライメントがあり、例えばint32は4バイト境界、int64は8バイト境界に配置されることが多いです。
  • パディング (Padding): 構造体内で、アライメント要件を満たすためにコンパイラが自動的に挿入する未使用のバイトのことです。例えば、4バイトの変数の後に8バイトの変数が続く場合、8バイトの変数が8バイト境界に配置されるように、4バイトの変数の後に4バイトのパディングが挿入されることがあります。これにより、構造体の合計サイズが大きくなることがあります。
  • EABIパディング: EABIは、ARMアーキテクチャにおける構造体のアライメントとパディングに関する具体的な規則を定めています。GoランタイムがC言語の構造体定義(特にシステムコールに関連するもの)を扱う際、Goのコンパイラが生成する構造体のメモリレイアウトがEABIの規則と一致しないと、データの読み書きが正しく行われず、問題が発生します。

3. GoランタイムとCgo

  • Goランタイム: Goプログラムの実行を管理する部分で、ガベージコレクション、スケジューラ、システムコールインターフェースなどが含まれます。
  • Cgo: GoプログラムからC言語の関数を呼び出したり、C言語のデータ構造を扱ったりするためのGoの機能です。Cgoを使用すると、GoのコードとCのコードを混在させることができます。システムコールやOS固有の機能にアクセスする際にCgoが利用されることがあります。
  • defs-files: GoランタイムがOS固有の構造体や定数を理解するために使用する定義ファイルです。これらは通常、C言語のヘッダファイルから自動生成されるか、手動で定義されます。このコミットでは、FreeBSD/ARM向けのdefs_freebsd_arm.hが対象となっています。
  • syscall z-files: システムコールに関連する定義ファイルで、GoがOSのシステムコールを呼び出すためのインターフェースを提供します。

4. ブートストラップフェーズ

Goのビルドプロセスでは、Goコンパイラ自体がGoで書かれているため、Goコンパイラをビルドするために既存のGoコンパイラ(またはCコンパイラ)が必要となる「ブートストラップ」という段階があります。新しいアーキテクチャやOSをサポートする場合、このブートストラッププロセスが特に重要になります。コミットメッセージにある「bootstrapping phase」は、この初期ビルド段階を指しています。

技術的詳細

このコミットの核心は、src/pkg/runtime/defs_freebsd_arm.hファイル内の構造体定義に明示的なパディングを追加することです。

FreeBSD/ARM環境では、EABIの規則に従って構造体のアライメントとパディングが行われます。GoのコンパイラがC言語の構造体をGoの構造体として解釈する際、Goのコンパイラが自動的に行うパディングと、EABIが期待するパディングが一致しないと、構造体のサイズやメンバのオフセットがずれてしまいます。これにより、Cgoを介してC言語の関数に構造体を渡したり、C言語の関数から構造体を受け取ったりする際に、データが破損したり、不正なメモリにアクセスしたりする問題が発生します。

具体的には、Timespec構造体とTimeval構造体において、int32型のメンバの後にbyte Pad_cgo_0[4];という4バイトのパディングが追加されています。

struct Timespec {
    int64   tv_sec;
    int32   tv_nsec;
+   byte    Pad_cgo_0[4]; // 追加されたパディング
};
struct Timeval {
    int64   tv_sec;
    int32   tv_usec;
+   byte    Pad_cgo_0[4]; // 追加されたパディング
};

int64は8バイト、int32は4バイトです。ARM EABIでは、構造体のメンバは自身のサイズまたは構造体全体の最大アライメントのいずれか小さい方にアライメントされることが一般的です。int64が8バイト境界に配置される場合、その後に続くint32は4バイト境界に配置されます。しかし、構造体全体のサイズが最大アライメントの倍数になるように、最後にパディングが追加されることがあります。

この場合、TimespecTimevalint64を含むため、8バイトアライメントが適用される可能性があります。int64 (8バイト) + int32 (4バイト) = 12バイトとなりますが、8バイトアライメントを維持するためには、合計サイズが8の倍数である必要があります。したがって、12バイトの後に4バイトのパディングを追加することで、合計16バイトとなり、8バイトの倍数になります。この明示的なパディングは、Goのコンパイラがこれらの構造体をC言語のABIと互換性のある形で解釈し、メモリに配置することを保証します。

また、EFAULTEV_ADDなどの定数の定義において、等号=の後のスペースがタブ文字に修正されています。これは、コードの整形(フォーマット)に関する変更であり、機能的な影響はありませんが、Goプロジェクトのコーディング規約に合わせたものと考えられます。

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

src/pkg/runtime/defs_freebsd_arm.hファイルが変更されています。

--- a/src/pkg/runtime/defs_freebsd_arm.h
+++ b/src/pkg/runtime/defs_freebsd_arm.h
@@ -4,7 +4,7 @@
 
 enum {
 	EINTR	= 0x4,
-	EFAULT  = 0xe,
+	EFAULT	= 0xe,
 
 	PROT_NONE	= 0x0,
 	PROT_READ	= 0x1,
@@ -76,13 +76,13 @@ enum {
 	ITIMER_VIRTUAL	= 0x1,
 	ITIMER_PROF	= 0x2,
 
-	EV_ADD          = 0x1,\n-	EV_DELETE       = 0x2,\n-	EV_CLEAR        = 0x20,\n-	EV_RECEIPT      = 0x40,\n-	EV_ERROR        = 0x4000,\n-	EVFILT_READ     = -0x1,\n-	EVFILT_WRITE    = -0x2,\n+	EV_ADD\t\t= 0x1,\n+	EV_DELETE\t= 0x2,\n+	EV_CLEAR\t= 0x20,\n+	EV_RECEIPT\t= 0x40,\n+	EV_ERROR\t= 0x4000,\n+	EVFILT_READ\t= -0x1,\n+	EVFILT_WRITE\t= -0x2,\n };
 
 typedef struct Rtprio Rtprio;\n@@ -159,10 +159,12 @@ struct Ucontext {\n struct Timespec {\n \tint64\ttv_sec;\n \tint32\ttv_nsec;\n+\tbyte\tPad_cgo_0[4];\n };\n struct Timeval {\n \tint64\ttv_sec;\n \tint32\ttv_usec;\n+\tbyte\tPad_cgo_0[4];\n };\n struct Itimerval {\n \tTimeval\tit_interval;\n@@ -170,12 +172,12 @@ struct Itimerval {\n };\n \n struct Kevent {\n-\tuint32  ident;\n-\tint16   filter;\n-\tuint16  flags;\n-\tuint32  fflags;\n-\tint32   data;\n-\tbyte    *udata;\n+\tuint32\tident;\n+\tint16\tfilter;\n+\tuint16\tflags;\n+\tuint32\tfflags;\n+\tint32\tdata;\n+\tbyte\t*udata;\n };

コアとなるコードの解説

このコミットの主要な変更点は、Timespec構造体とTimeval構造体にbyte Pad_cgo_0[4];という4バイトのパディングを追加したことです。

  • Timespec構造体:

    • tv_sec (64ビット整数): 秒を表します。
    • tv_nsec (32ビット整数): ナノ秒を表します。
    • byte Pad_cgo_0[4];: この4バイトのパディングが追加されたことで、構造体全体のサイズが8バイトの倍数(8 + 4 + 4 = 16バイト)となり、FreeBSD/ARMのEABIにおけるアライメント要件を満たすようになります。これにより、Cgoを介してこの構造体がC言語のコードとやり取りされる際に、メモリレイアウトの不一致による問題が解消されます。
  • Timeval構造体:

    • tv_sec (64ビット整数): 秒を表します。
    • tv_usec (32ビット整数): マイクロ秒を表します。
    • byte Pad_cgo_0[4];: Timespecと同様に、このパディングによって構造体全体のサイズが8バイトの倍数となり、EABIとの互換性が確保されます。

その他の変更点として、EFAULTEV_ADDEV_DELETEなどの定数定義における等号=の後の空白文字が、スペースからタブ文字に変更されています。これは、Goプロジェクトのコーディングスタイルガイドラインに合わせた整形であり、機能的な変更ではありません。

これらの変更により、GoランタイムがFreeBSD/ARM上でCgoを介してシステムコールやC言語のライブラリと連携する際に、データ構造の整合性が保たれるようになり、ビルドエラーや実行時エラーが解消されます。

関連リンク

参考にした情報源リンク