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

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

このコミットは、Go言語のランタイムがNetBSDオペレーティングシステム上でARMアーキテクチャをサポートするための変更を導入します。具体的には、NetBSD/ARM環境でのシステムコール、シグナルハンドリング、スレッド管理、およびその他の低レベルなOSとのインタラクションを可能にするための新しいファイルと既存ファイルの修正が含まれています。これにより、GoプログラムがNetBSD/ARMデバイス上でネイティブに実行できるようになります。

コミット

commit 37aba1aa776c613d5a56fc4d001adbb4b4558a11
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Wed Feb 13 01:00:04 2013 +0800

    runtime: NetBSD/ARM support
    
    R=rsc, dave
    CC=golang-dev
    https://golang.org/cl/7289044

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

https://github.com/golang/go/commit/37aba1aa776c613d5a56fc4d001adbb4b4558a11

元コミット内容

このコミットは、GoランタイムにNetBSD/ARMのサポートを追加するものです。これには、ARMアセンブリコードの調整、NetBSD/ARM固有の定義ヘッダ、NetBSDのOSヘッダの更新、NetBSD/ARMのランタイムエントリポイント、NetBSD/ARMのシグナルハンドリング、NetBSD/ARMのシステムコールスタブ、およびNetBSDスレッド関連ファイルの修正が含まれています。

変更の背景

Go言語は、その設計思想として高いポータビリティとクロスプラットフォーム対応を重視しています。様々なオペレーティングシステムとアーキテクチャの組み合わせでGoプログラムが動作するように、Goランタイムは各プラットフォームに特化した実装を持つ必要があります。このコミットは、NetBSDというUNIX系オペレーティングシステムと、ARMという広く普及しているプロセッサアーキテクチャの組み合わせに対するサポートを追加することを目的としています。これにより、NetBSD/ARMデバイス上でGoアプリケーションを開発・実行できるようになり、Go言語のエコシステムがさらに拡大します。

前提知識の解説

  • Goランタイム (Go Runtime): Goプログラムの実行を管理する低レベルなコンポーネント群です。これには、ガベージコレクション、ゴルーチン(軽量スレッド)のスケジューリング、メモリ管理、システムコールインターフェース、シグナルハンドリングなどが含まれます。Goプログラムは、OSの機能に直接アクセスするのではなく、ランタイムを介して間接的にアクセスします。
  • NetBSD: オープンソースのUNIX系オペレーティングシステムの一つで、高いポータビリティを特徴としています。多くの異なるハードウェアアーキテクチャで動作するように設計されており、組み込みシステムからサーバーまで幅広い用途で利用されています。
  • ARMアーキテクチャ (Advanced RISC Machine): モバイルデバイス、組み込みシステム、IoTデバイスなどで広く使用されているRISC(Reduced Instruction Set Computer)ベースのプロセッサアーキテクチャです。低消費電力と高性能を両立させています。
  • システムコール (System Call): アプリケーションがオペレーティングシステムのカーネルサービス(ファイルI/O、メモリ管理、プロセス制御など)にアクセスするためのインターフェースです。各OSとアーキテクチャの組み合わせによって、システムコールの呼び出し規約や番号が異なります。
  • シグナルハンドリング (Signal Handling): オペレーティングシステムがプロセスに非同期イベント(エラー、外部からの割り込みなど)を通知するメカニズムです。プロセスはシグナルを受け取った際に、特定のハンドラ関数を実行して対応することができます。
  • アセンブリ言語 (.sファイル): プロセッサが直接実行できる機械語に非常に近い低レベルなプログラミング言語です。OSとのインタラクションやパフォーマンスが重要な部分(例: システムコール呼び出し、コンテキストスイッチ)でGoランタイムによって使用されます。
  • C言語 (.cファイル): Goランタイムの一部はC言語で記述されており、OS固有のAPIとの連携や、アセンブリ言語とGo言語の間のブリッジとして機能します。
  • ヘッダファイル (.hファイル): C言語やアセンブリ言語のソースコードで共有される定数、構造体、関数プロトタイプなどの定義が含まれます。OS固有の定数(例: シグナル番号、メモリ保護フラグ)などが定義されます。
  • VFP (Vector Floating Point): ARMプロセッサにおける浮動小数点演算ユニットです。runfast (flush-to-zero) モードは、非正規化数をゼロとして扱うことで浮動小数点演算のパフォーマンスを向上させる設定ですが、一部のアプリケーションでは精度問題を引き起こす可能性があります。

技術的詳細

このコミットは、GoランタイムがNetBSD/ARM環境で適切に動作するために必要な、以下の主要な技術的側面に対応しています。

  1. システムコールインターフェースの確立:

    • src/pkg/runtime/sys_netbsd_arm.s は、NetBSD/ARMにおけるGoランタイムのシステムコール呼び出しのハブとなります。このファイルには、exitwritelwp_create (軽量プロセス作成)、mmap (メモリマップ)、munmap (メモリマップ解除)、sigaction (シグナルアクション設定) など、GoランタイムがOSと対話するために必要な多数のシステムコールに対するアセンブリスタブが含まれています。
    • ARMアーキテクチャでは、システムコールは通常 SWI (Software Interrupt) 命令を使用して呼び出されます。各システムコールには一意の番号が割り当てられており、このファイルでは 0xa00001 (sys_exit) や 0xa00004 (sys_write) のように、NetBSDのシステムコール番号が直接埋め込まれています。
    • 引数の渡し方や戻り値の受け取り方も、ARMのレジスタ規約(R0-R3が引数、R0が戻り値など)に従って実装されています。
  2. シグナルハンドリングの実装:

    • src/pkg/runtime/signal_netbsd_arm.c は、NetBSD/ARMにおけるGoランタイムのシグナルハンドリングロジックを実装しています。Goは独自のシグナルハンドラを持ち、OSからのシグナルを捕捉し、Goのランタイムレベルで処理します。
    • runtime·sighandler 関数は、OSからシグナルが配送された際に呼び出される主要なハンドラです。この関数は、シグナル番号、シグナル情報 (Siginfo)、およびコンテキスト (UcontextT) を受け取ります。
    • 特に重要なのは、SIGPROF (プロファイリングシグナル) や、SIGSEGV (セグメンテーション違反) のようなパニックを引き起こすシグナルの処理です。パニックシグナルの場合、Goランタイムはスタックトレースを生成し、プログラムを終了させるための適切な処理を行います。
    • Sigaction 構造体は、シグナルハンドラの設定に使用され、SA_SIGINFO (シグナル情報を含むハンドラを使用)、SA_ONSTACK (代替シグナルスタックを使用) などのフラグが設定されます。
    • runtime·sigtramp は、OSのシグナルディスパッチャからGoのシグナルハンドラへの橋渡しをするアセンブリコードです。
  3. OS固有の定義と構造体:

    • src/pkg/runtime/defs_netbsd_arm.h は、NetBSD/ARM環境に特有の定数と構造体を定義しています。これには、メモリ保護フラグ (PROT_READ, PROT_WRITE, PROT_EXEC)、メモリマップフラグ (MAP_ANON, MAP_PRIVATE)、シグナル番号 (SIGHUP, SIGINT, SIGSEGVなど)、およびシグナルハンドリングやコンテキストスイッチに関連する構造体 (Sigaltstack, Sigset, Siginfo, McontextT, UcontextT) が含まれます。
    • これらの定義は、GoランタイムがNetBSDカーネルと正しくインタラクションするために不可欠です。特に McontextTUcontextT は、シグナルハンドラ内でCPUレジスタの状態を保存・復元するために使用されます。
  4. ARMアセンブリの調整:

    • src/pkg/runtime/asm_arm.s では、runtime·asminit 関数にVFP (Vector Floating Point) の runfast (flush-to-zero) モードを無効にするロジックが追加されています。これは、runtime.goarm の値が5より大きい場合(つまり、ARMv6以上のアーキテクチャでVFPが利用可能な場合)に適用されます。runfast モードはパフォーマンスを向上させる一方で、非正規化数をゼロとして扱うため、一部の浮動小数点演算で予期せぬ結果を招く可能性があります。Goランタイムは、より予測可能な浮動小数点動作を保証するためにこのモードを無効にしています。
  5. エントリポイントとスレッド管理:

    • src/pkg/runtime/rt0_netbsd_arm.s は、NetBSD/ARM上でのGoプログラムの初期エントリポイントを定義しています。これは、Goランタイムの初期化ルーチン (_rt0_arm) にジャンプするシンプルなアセンブリコードです。
    • src/pkg/runtime/thread_netbsd.c からは、_UC_SIGMASK_UC_CPU の定義が削除されています。これらの定義は、src/pkg/runtime/os_netbsd.h に移動され、より適切な場所で一元管理されるようになりました。

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

このコミットでは、以下のファイルが変更または新規追加されています。

  • src/pkg/runtime/asm_arm.s: ARMアセンブリコード。VFPのrunfastモードを無効にするロジックが追加されました。
  • src/pkg/runtime/defs_netbsd_arm.h: 新規追加。NetBSD/ARM固有の定数、シグナル番号、および構造体(Sigaltstack, Sigset, Siginfo, McontextT, UcontextTなど)の定義。
  • src/pkg/runtime/os_netbsd.h: NetBSDのOSヘッダ。_UC_SIGMASK_UC_CPUの定義が追加されました。
  • src/pkg/runtime/rt0_netbsd_arm.s: 新規追加。NetBSD/ARM上でのGoプログラムの初期エントリポイント。
  • src/pkg/runtime/signal_netbsd_arm.c: 新規追加。NetBSD/ARMにおけるGoランタイムのシグナルハンドリングロジック。
  • src/pkg/runtime/sys_netbsd_arm.s: 新規追加。NetBSD/ARMにおけるGoランタイムのシステムコール呼び出し用アセンブリスタブ。
  • src/pkg/runtime/thread_netbsd.c: NetBSDスレッド関連のCコード。_UC_SIGMASK_UC_CPUの定義が削除されました。

コアとなるコードの解説

src/pkg/runtime/asm_arm.s の変更点

+GLOBL runtime·goarm(SB), $4
 TEXT runtime·asminit(SB),7,$0
-\t// No per-thread init.
+\t// disable runfast (flush-to-zero) mode of vfp if runtime.goarm > 5
+\tMOVW runtime·goarm(SB), R11
+\tCMP $5, R11
+\tBLE 4(PC)
+\tWORD $0xeef1ba10\t// vmrs r11, fpscr
+\tBIC $(1<<24), R11
+\tWORD $0xeee1ba10\t// vmsr fpscr, r11
 \tRET

このコードは、runtime·asminit 関数内でVFP (Vector Floating Point) のrunfastモードを無効にする処理を追加しています。

  1. MOVW runtime·goarm(SB), R11: runtime.goarm の値をR11レジスタにロードします。runtime.goarm はGoがターゲットとするARMアーキテクチャのバージョンを示します。
  2. CMP $5, R11: R11レジスタの値(runtime.goarm)と5を比較します。
  3. BLE 4(PC): もしruntime.goarmが5以下であれば、次の4バイト(vmrs命令をスキップ)にジャンプします。これは、ARMv6より古いアーキテクチャではVFPが利用できないか、runfastモードの制御が異なるためです。
  4. WORD $0xeef1ba10 // vmrs r11, fpscr: vmrs命令は、VFPシステムレジスタ(ここではfpscr、浮動小数点ステータスおよび制御レジスタ)の値を汎用レジスタ(R11)に転送します。
  5. BIC $(1<<24), R11: BIC (Bit Clear) 命令は、R11レジスタのビット24をクリアします。fpscrレジスタのビット24は、FZ (Flush-to-Zero) ビットであり、これをクリアすることでrunfastモードを無効にします。
  6. WORD $0xeee1ba10 // vmsr fpscr, r11: vmsr命令は、汎用レジスタ(R11)の値をVFPシステムレジスタ(fpscr)に転送し、変更を適用します。

src/pkg/runtime/defs_netbsd_arm.h の一部

enum {
	PROT_NONE	= 0x0,
	PROT_READ	= 0x1,
	PROT_WRITE	= 0x2,
	PROT_EXEC	= 0x4,

	MAP_ANON	= 0x1000,
	MAP_PRIVATE	= 0x2,
	MAP_FIXED	= 0x10,

	// ... (シグナル番号など) ...
};

typedef struct Sigaltstack Sigaltstack;
typedef struct Sigset Sigset;
typedef struct Siginfo Siginfo;
typedef struct StackT StackT;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
typedef struct McontextT McontextT;
typedef struct UcontextT UcontextT;

#pragma pack on

struct Sigaltstack {
	byte	*ss_sp;
	uint32	ss_size;
	int32	ss_flags;
};
// ... (他の構造体定義) ...
struct McontextT {
	uint32	__gregs[17];
#ifdef __ARM_EABI__
	byte	__fpu[4+8*32+4];
#else
	byte	__fpu[4+4*33+4];
#endif
	uint32	_mc_tlsbase;
};
struct UcontextT {
	uint32	uc_flags;
	UcontextT	*uc_link;
	Sigset	uc_sigmask;
	StackT	uc_stack;
	McontextT	uc_mcontext;
	int32	__uc_pad[2];
};

#pragma pack off

このヘッダファイルは、NetBSD/ARM環境でGoランタイムがOSとインタラクションするために必要な、C言語スタイルの定数と構造体を定義しています。

  • PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC: mmapシステムコールで使用されるメモリ保護フラグ。
  • MAP_ANON, MAP_PRIVATE, MAP_FIXED: mmapシステムコールで使用されるメモリマップフラグ。
  • SIGHUPからSIGUSR2までのシグナル番号: NetBSDにおける標準的なシグナル番号。
  • Sigaltstack: 代替シグナルスタックの情報を保持する構造体。
  • McontextT: CPUのレジスタ状態(汎用レジスタ__gregs、FPUレジスタ__fpuなど)を保持する構造体。シグナルハンドラでコンテキストを保存・復元するために重要です。
  • UcontextT: ユーザーコンテキストを保持する構造体。これには、シグナルマスク、スタック情報、およびMcontextTが含まれます。

src/pkg/runtime/signal_netbsd_arm.c の一部

#define r0	__gregs[0]
// ... (他のレジスタ定義) ...
#define cpsr	__gregs[16]

void
runtime·dumpregs(McontextT *r)
{
	// ... (レジスタのダンプ処理) ...
}

void
runtime·sighandler(int32 sig, Siginfo *info, void *context, G *gp)
{
	UcontextT *uc;
	McontextT *r;
	SigTab *t;

	uc = context;
	r = &uc->uc_mcontext;

	if(sig == SIGPROF) {
		runtime·sigprof((uint8*)r->r15, (uint8*)r->r13, (uint8*)r->r14, gp);
		return;
	}

	t = &runtime·sigtab[sig];
	if(info->_code != SI_USER && (t->flags & SigPanic)) {
		// ... (パニックシグナル処理) ...
	}
	// ... (その他のシグナル処理) ...
}

void
runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
{
	Sigaction sa;

	runtime·memclr((byte*)&sa, sizeof sa);
	sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
	if(restart)
		sa.sa_flags |= SA_RESTART;
	sa.sa_mask[0] = ~0U; // 全てのシグナルをブロック
	// ... (sa_maskの他の要素) ...
	if (fn == runtime·sighandler)
		fn = (void*)runtime·sigtramp; // GoのハンドラをOSに登録する際はsigtrampを介する
	sa._sa_u._sa_sigaction = (void*)fn;
	runtime·sigaction(i, &sa, nil);
}

このファイルは、NetBSD/ARMにおけるGoランタイムのシグナルハンドリングの中核をなします。

  • r0からcpsrまでのマクロ定義は、McontextT構造体の__gregs配列内の特定のレジスタにアクセスするためのものです。
  • runtime·dumpregsは、デバッグ目的でCPUレジスタの状態をダンプする関数です。
  • runtime·sighandlerは、OSからシグナルを受け取った際にGoランタイムが実行するメインのシグナルハンドラです。SIGPROFのようなプロファイリングシグナルや、SIGSEGVのようなパニックを引き起こすシグナルを区別して処理します。パニックシグナルの場合、Goのゴルーチンコンテキストを調整し、runtime·sigpanic関数を呼び出すようにPC(プログラムカウンタ)とLR(リンクレジスタ)を設定します。
  • runtime·setsigは、特定のシグナルに対するハンドラを設定するための関数です。Sigaction構造体を使用して、シグナルハンドラの動作(例: SA_SIGINFOで詳細なシグナル情報を取得、SA_ONSTACKで代替スタックを使用)を定義し、runtime·sigactionシステムコールを呼び出してOSに登録します。Goの内部ハンドラであるruntime·sighandlerを登録する際には、直接登録するのではなく、アセンブリのruntime·sigtrampを介して呼び出されるように設定されます。

src/pkg/runtime/sys_netbsd_arm.s の一部

TEXT runtime·exit(SB),7,$-4
	MOVW 0(FP), R0	// arg 1 exit status
	SWI $0xa00001	// sys_exit
	MOVW.CS $0, R9	// crash on syscall failure
	MOVW.CS R9, (R9)
	RET

TEXT runtime·write(SB),7,$-4
	MOVW	0(FP), R0	// arg 1 - fd
	MOVW	4(FP), R1	// arg 2 - buf
	MOVW	8(FP), R2	// arg 3 - nbyte
	SWI $0xa00004	// sys_write
	RET

TEXT runtime·mmap(SB),7,$12
	MOVW 0(FP), R0	// arg 1 - addr
	MOVW 4(FP), R1	// arg 2 - len
	MOVW 8(FP), R2	// arg 3 - prot
	MOVW 12(FP), R3	// arg 4 - flags
	// arg 5 (fid) and arg6 (offset_lo, offset_hi) are passed on stack
	// note the C runtime only passes the 32-bit offset_lo to us
	MOVW 16(FP), R4		// arg 5
	MOVW R4, 4(R13)
	MOVW 20(FP), R5		// arg 6 lower 32-bit
	MOVW R5, 8(R13)
	MOVW $0, R6 // higher 32-bit for arg 6
	MOVW R6, 12(R13)
	ADD $4, R13 // pass arg 5 and arg 6 on stack
	SWI $0xa000c5	// sys_mmap
	SUB $4, R13
	RET

このファイルは、GoランタイムがNetBSD/ARM上でOSの機能にアクセスするためのアセンブリラッパーを提供します。

  • runtime·exit: プログラムを終了させるためのexitシステムコール(0xa00001)を呼び出します。引数(終了ステータス)はR0レジスタに渡されます。
  • runtime·write: ファイルディスクリプタへの書き込みを行うwriteシステムコール(0xa00004)を呼び出します。ファイルディスクリプタ、バッファ、バイト数がそれぞれR0、R1、R2レジスタに渡されます。
  • runtime·mmap: メモリをプロセスのアドレス空間にマップするためのmmapシステムコール(0xa000c5)を呼び出します。mmapは多くの引数を取るため、一部の引数(fidoffset)はスタックを介して渡されます。ARMの呼び出し規約とNetBSDのシステムコール規約に合わせて、レジスタとスタックを適切に操作しています。

これらのコード変更は、GoランタイムがNetBSD/ARM環境の低レベルな詳細(システムコール番号、レジスタ規約、シグナル処理のメカニズム)を理解し、それらに適応できるようにするために不可欠です。

関連リンク

参考にした情報源リンク

  • NetBSDのシステムコールに関するドキュメント (一般的な情報源として)
  • Go言語のランタイムに関する公式ドキュメントやソースコード (一般的な情報源として)
  • ARMアーキテクチャのプログラミングリファレンス (一般的な情報源として)
  • Goのコードレビューシステム (Gerrit): https://golang.org/cl/7289044 (コミットメッセージに記載)
  • /usr/src/sys/kern/syscalls.master (NetBSDのシステムコール番号の定義元、sys_netbsd_arm.sのコメントに記載)
  • NetBSDの<sys/ucontext.h> (defs_netbsd_arm.hos_netbsd.hのコメントに記載)