[インデックス 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環境で適切に動作するために必要な、以下の主要な技術的側面に対応しています。
-
システムコールインターフェースの確立:
src/pkg/runtime/sys_netbsd_arm.s
は、NetBSD/ARMにおけるGoランタイムのシステムコール呼び出しのハブとなります。このファイルには、exit
、write
、lwp_create
(軽量プロセス作成)、mmap
(メモリマップ)、munmap
(メモリマップ解除)、sigaction
(シグナルアクション設定) など、GoランタイムがOSと対話するために必要な多数のシステムコールに対するアセンブリスタブが含まれています。- ARMアーキテクチャでは、システムコールは通常
SWI
(Software Interrupt) 命令を使用して呼び出されます。各システムコールには一意の番号が割り当てられており、このファイルでは0xa00001
(sys_exit) や0xa00004
(sys_write) のように、NetBSDのシステムコール番号が直接埋め込まれています。 - 引数の渡し方や戻り値の受け取り方も、ARMのレジスタ規約(R0-R3が引数、R0が戻り値など)に従って実装されています。
-
シグナルハンドリングの実装:
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のシグナルハンドラへの橋渡しをするアセンブリコードです。
-
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カーネルと正しくインタラクションするために不可欠です。特に
McontextT
とUcontextT
は、シグナルハンドラ内でCPUレジスタの状態を保存・復元するために使用されます。
-
ARMアセンブリの調整:
src/pkg/runtime/asm_arm.s
では、runtime·asminit
関数にVFP (Vector Floating Point) のrunfast
(flush-to-zero) モードを無効にするロジックが追加されています。これは、runtime.goarm
の値が5より大きい場合(つまり、ARMv6以上のアーキテクチャでVFPが利用可能な場合)に適用されます。runfast
モードはパフォーマンスを向上させる一方で、非正規化数をゼロとして扱うため、一部の浮動小数点演算で予期せぬ結果を招く可能性があります。Goランタイムは、より予測可能な浮動小数点動作を保証するためにこのモードを無効にしています。
-
エントリポイントとスレッド管理:
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
モードを無効にする処理を追加しています。
MOVW runtime·goarm(SB), R11
:runtime.goarm
の値をR11レジスタにロードします。runtime.goarm
はGoがターゲットとするARMアーキテクチャのバージョンを示します。CMP $5, R11
: R11レジスタの値(runtime.goarm
)と5を比較します。BLE 4(PC)
: もしruntime.goarm
が5以下であれば、次の4バイト(vmrs
命令をスキップ)にジャンプします。これは、ARMv6より古いアーキテクチャではVFPが利用できないか、runfast
モードの制御が異なるためです。WORD $0xeef1ba10 // vmrs r11, fpscr
:vmrs
命令は、VFPシステムレジスタ(ここではfpscr
、浮動小数点ステータスおよび制御レジスタ)の値を汎用レジスタ(R11)に転送します。BIC $(1<<24), R11
:BIC
(Bit Clear) 命令は、R11レジスタのビット24をクリアします。fpscr
レジスタのビット24は、FZ
(Flush-to-Zero) ビットであり、これをクリアすることでrunfast
モードを無効にします。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
は多くの引数を取るため、一部の引数(fid
、offset
)はスタックを介して渡されます。ARMの呼び出し規約とNetBSDのシステムコール規約に合わせて、レジスタとスタックを適切に操作しています。
これらのコード変更は、GoランタイムがNetBSD/ARM環境の低レベルな詳細(システムコール番号、レジスタ規約、シグナル処理のメカニズム)を理解し、それらに適応できるようにするために不可欠です。
関連リンク
- Go言語公式サイト: https://golang.org/
- NetBSD公式サイト: https://www.netbsd.org/
- ARMアーキテクチャ: https://developer.arm.com/
参考にした情報源リンク
- 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.h
とos_netbsd.h
のコメントに記載)