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

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

コミット

このコミットは、GoランタイムにNative Client (NaCl) のARMアーキテクチャサポートを追加するものです。これにより、GoプログラムをGoogle Native ClientプラットフォームのARM環境で実行できるようになります。

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

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

元コミット内容

runtime: nacl/arm support.

LGTM=rsc
R=rsc, iant, dave
CC=golang-codereviews
https://golang.org/cl/103680046

変更の背景

この変更の背景には、Go言語の実行環境を多様なプラットフォームに拡大するというGoプロジェクトの目標があります。特に、Google Native Client (NaCl) は、Webブラウザ内でネイティブコードを安全に実行するためのサンドボックス技術であり、Webアプリケーションのパフォーマンス向上に貢献します。当初、NaClはx86アーキテクチャを主なターゲットとしていましたが、ARMアーキテクチャはモバイルデバイスや組み込みシステムで広く普及しており、その重要性が増していました。

Go言語がNaClのARM環境をサポートすることで、開発者はGoで記述された高性能なアプリケーションを、WebブラウザやNaClがサポートする他の環境でARMデバイス向けに展開できるようになります。これは、Goの利用範囲を広げ、WebAssemblyが登場する以前の時代において、Web上でのネイティブコード実行の可能性を追求する上で重要な一歩でした。

前提知識の解説

Go言語のランタイム

Go言語は、ガベージコレクション、軽量なゴルーチン、チャネルによる並行処理など、独自のランタイムシステムを持っています。Goプログラムが実行される際には、このランタイムがメモリ管理、スケジューリング、システムコールインターフェースなどを担当します。異なるオペレーティングシステム (OS) やCPUアーキテクチャ (GOOS/GOARCH) に対応するためには、ランタイムの各部分がそれぞれのプラットフォームの特性に合わせて実装される必要があります。

Native Client (NaCl)

Native Client (NaCl) は、Googleが開発したオープンソースのサンドボックス技術で、Webブラウザ内でネイティブコード(C/C++など)を安全かつ高性能に実行することを目的としていました。NaClは、厳格なサンドボックス環境を提供し、悪意のあるコードがシステムにアクセスするのを防ぎます。これは、Webアプリケーションがより複雑な計算やグラフィックス処理をクライアント側で実行できるようにするための技術でした。

NaClには、特定のCPUアーキテクチャに依存する「Native Client (NaCl)」と、中間表現(バイトコード)を使用することで異なるアーキテクチャ間でポータビリティを確保する「Portable Native Client (PNaCl)」の2種類があります。このコミットは、特定のアーキテクチャであるARM向けのNaClサポートに関するものです。

ARMアーキテクチャ

ARM (Advanced RISC Machine) は、モバイルデバイス、組み込みシステム、IoTデバイス、さらにはサーバーやデスクトップPCなど、幅広い分野で利用されているRISC (Reduced Instruction Set Computer) ベースのCPUアーキテクチャです。低消費電力と高い性能効率が特徴であり、スマートフォンやタブレットの普及とともにその重要性が飛躍的に増しました。Go言語がARMをサポートすることは、これらのデバイスでGoプログラムを実行可能にする上で不可欠です。

クロスコンパイル

Go言語は、異なるOSやCPUアーキテクチャ向けにプログラムをコンパイルする「クロスコンパイル」を強力にサポートしています。例えば、Linux上でWindows向けの実行ファイルを生成したり、x86マシン上でARM向けのバイナリを生成したりすることが可能です。この機能は、GOOS (ターゲットOS) と GOARCH (ターゲットアーキテクチャ) という環境変数を設定することで利用できます。このコミットは、GOOS=nacl かつ GOARCH=arm の組み合わせを可能にするためのランタイム変更です。

システムコール

システムコールは、ユーザー空間のプログラムがオペレーティングシステムカーネルのサービス(ファイルI/O、メモリ管理、プロセス制御など)を要求するためのインターフェースです。Goランタイムは、プログラムがOSと対話するために、各プラットフォームのシステムコールを適切に呼び出す必要があります。NaClのようなサンドボックス環境では、通常のOSシステムコールとは異なる、NaCl独自のシステムコールインターフェースが提供されます。

シグナルハンドリング

シグナルは、オペレーティングシステムがプロセスに非同期的に通知を送るメカニズムです。例えば、プログラムのエラー(セグメンテーション違反など)、外部からの終了要求、タイマーイベントなどがシグナルとして通知されます。Goランタイムは、これらのシグナルを適切に捕捉し、処理するためのシグナルハンドリング機構を持っています。NaCl環境では、シグナル処理もNaClのサンドボックスモデルに合わせて調整する必要があります。

TLS (Thread Local Storage)

TLS (Thread Local Storage) は、各スレッドがそれぞれ独立したデータを持つためのメカニズムです。Go言語のランタイムでは、ゴルーチンやM (Machine) の状態など、スレッドごとに異なる情報を管理するためにTLSが利用されることがあります。しかし、NaClのような特定のサンドボックス環境では、TLSの利用方法が通常のOSとは異なるか、あるいは全くサポートされない場合があります。

技術的詳細

このコミットは、GoランタイムがNaCl環境のARMアーキテクチャ上で動作するために必要な多岐にわたる変更を導入しています。主な変更点は以下の通りです。

  • GOOS_naclGOARCH_arm の組み合わせのサポート:

    • Goのビルドシステムにおいて、GOOS=naclGOARCH=arm の組み合わせが認識され、それに応じたランタイムコードがコンパイルされるようになります。
  • src/pkg/runtime/arch_arm.h の変更:

    • PhysPageSize (物理ページサイズ) の定義が、NaCl環境では 65536 バイト(64KB)に設定されるようになりました。これは、NaClがメモリをページ単位で管理する際の最小単位が、一般的なOS(4KB)とは異なるためです。
  • src/pkg/runtime/asm_arm.s の変更:

    • _cgo_init (Cgo初期化関数) の呼び出しが GOOS_nacl の場合は無効化されました。NaCl環境ではCgoの利用が制限されるためです。
    • runtime·breakpoint 命令が、NaCl環境では BKPT 0x5bef (NACL_INSTR_ARM_BREAKPOINT) というNaCl固有のブレークポイント命令を使用するように変更されました。これにより、デバッガがNaClサンドボックス内で適切に動作するようになります。
    • runtime·gogo 関数(ゴルーチンのコンテキストスイッチを行う関数)において、PC (プログラムカウンタ) の設定方法が MOVW gobuf_pc(R1), PC から MOVW gobuf_pc(R1), R11; B (R11) に変更されました。これは、ARMアーキテクチャにおけるPCレジスタへの直接書き込みの制約や、NaClサンドボックスの制約に対応するための変更と考えられます。
  • src/pkg/runtime/defs_nacl_arm.h の新規追加:

    • NaCl固有のシグナル定義 (SIGSEGV, SIGPROF) や、NaClの例外コンテキスト (ExcContext, ExcRegsARM, ExcPortableContext) の構造体が定義されました。これらは、NaCl環境でシグナルや例外が発生した際に、レジスタの状態などを取得するために使用されます。
  • src/pkg/runtime/os_nacl_arm.c の新規追加:

    • runtime·checkgoarm 関数が追加され、NaCl/ARMはARMv7のみをサポートすることが明示されました。
    • runtime·cputicks 関数が runtime·nanotime() を呼び出すように実装されました。これは、CPUティックの正確な情報がNaCl環境で取得できないため、ナノ秒単位の時間を近似値として利用するためです。
  • src/pkg/runtime/rt0_nacl_arm.s の新規追加:

    • NaCl環境におけるGoプログラムのエントリポイントである _rt0_arm_nacl が定義されました。これは、NaClがプログラムを起動する際の引数(argc, argv, envv など)の受け渡し方法に合わせて、Goランタイムの初期化関数 _rt0_go を呼び出すためのラッパーです。
  • src/pkg/runtime/runtime.c の変更:

    • runtime·timediv 関数(時間除算を行う関数)のロジックが修正されました。これはNaCl固有の変更というよりは、汎用的なバグ修正または最適化の可能性がありますが、このコミットに含まれていることから、NaCl環境での特定の振る舞いに影響を与える可能性も考えられます。
  • src/pkg/runtime/signal_arm.c の変更:

    • ビルドタグに nacl が追加され、このファイルがNaCl環境でもコンパイルされるようになりました。これにより、ARMアーキテクチャにおけるシグナルハンドリングの共通部分がNaClでも利用されます。
  • src/pkg/runtime/signal_nacl_arm.h の新規追加:

    • シグナルコンテキストからARMレジスタの値を取得するためのマクロ (SIG_R0, SIG_PC など) が定義されました。これらは、シグナルハンドラ内でレジスタの状態を検査・変更するために使用されます。
  • src/pkg/runtime/sys_nacl_arm.s の新規追加:

    • NaCl固有のシステムコールを呼び出すためのアセンブリラッパーが多数追加されました。これには、exit, read, write, open, close, mmap などの基本的なI/O操作やメモリ管理、さらにはスレッド作成 (thread_create)、ミューテックス (mutex_create, mutex_lock など)、セマフォ (sem_create, sem_wait など)、条件変数 (cond_create, cond_wait など) といった並行処理プリミティブ、時間取得 (clock_gettime, nanosleep) などが含まれます。
    • NACL_SYSCALL および NACL_SYSJMP マクロが定義され、NaClのシステムコール呼び出し規約 (0x10000 + (code<<5)) に従って、レジスタR8にシステムコール番号を設定し、間接ジャンプを行うことでシステムコールを実行します。
    • runtime·sigtramp 関数(シグナルハンドラのエントリポイント)が実装されました。これは、シグナル発生時にGoランタイムのシグナルハンドラ (runtime·sighandler) を呼び出し、レジスタの状態を復元する役割を担います。NaClの例外クリアフラグ (SYS_exception_clear_flag) の呼び出しも含まれます。
  • src/pkg/runtime/tls_arm.s の変更:

    • GOOS_nacl の場合、TLS (Thread Local Storage) を使用しないことが明示されました。runtime·save_g および runtime·load_g 関数が、NaCl環境では何もしない (RET) ように変更されています。これは、NaClのサンドボックスモデルではTLSの概念が通常のOSとは異なるか、GoランタイムがNaCl環境でTLSを必要としないように設計されたためと考えられます。
  • src/pkg/runtime/vlop_arm.s の変更:

    • fast_udiv_tab のデータ定義方法が WORD から DATA ディレクティブを使用するように変更されました。これは、アセンブリコードにおけるデータセクションの定義方法の改善またはNaCl環境でのリンカの要件に合わせた調整と考えられます。
  • src/pkg/runtime/vlrt_arm.c の変更:

    • _vasop 関数に #pragma textflag NOSPLIT が追加されました。これは、この関数がスタック分割の対象とならないようにするための指示であり、ランタイムの低レベルな部分でスタックのオーバーヘッドを避けるために使用されます。

これらの変更は、GoランタイムがNaClのサンドボックス内で、ARMプロセッサの特性を考慮しつつ、必要なシステムサービスや並行処理機能を利用できるようにするための基盤を構築しています。

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

このコミットにおけるコアとなる変更は、主に以下のファイルに集約されています。

  1. src/pkg/runtime/defs_nacl_arm.h: NaCl固有のデータ構造(シグナル情報、例外コンテキスト)の定義。
  2. src/pkg/runtime/sys_nacl_arm.s: NaClシステムコールへのアセンブリラッパーと、シグナルハンドリングのエントリポイントの実装。
  3. src/pkg/runtime/rt0_nacl_arm.s: NaCl環境でのGoプログラムの初期エントリポイント。

これらのファイルは、GoランタイムがNaCl/ARM環境と直接対話するためのインターフェースを定義・実装しています。

src/pkg/runtime/defs_nacl_arm.h (新規追加)

// Copyright 2014 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Created by hand, not machine generated.

enum
{
	// These values are referred to in the source code
	// but really don't matter. Even so, use the standard numbers.
	SIGSEGV = 11,
	SIGPROF = 27,
};

typedef struct Siginfo Siginfo;

// native_client/src/trusted/service_runtime/include/machine/_types.h
typedef struct Timespec Timespec;

struct Timespec
{
	int64 tv_sec;
	int32 tv_nsec;
};

// native_client/src/trusted/service_runtime/nacl_exception.h
// native_client/src/include/nacl/nacl_exception.h

typedef struct ExcContext ExcContext;
typedef struct ExcPortable ExcPortable;
typedef struct ExcRegsARM ExcRegsARM;

struct ExcRegsARM
{
	uint32	r0;
	uint32	r1;
	uint32	r2;
	uint32	r3;
	uint32	r4;
	uint32	r5;
	uint32	r6;
	uint32	r7;
	uint32	r8;
	uint32	r9;	// the value reported here is undefined.
	uint32	r10;
	uint32	r11;
	uint32	r12;
	uint32	sp;	/* r13 */
	uint32	lr;	/* r14 */
	uint32	pc;	/* r15 */
	uint32	cpsr;
};

struct ExcContext
{
	uint32	size;
	uint32	portable_context_offset;
	uint32	portable_context_size;
	uint32	arch;
	uint32	regs_size;
	uint32	reserved[11];
	ExcRegsARM	regs;
};

struct ExcPortableContext
{
	uint32	pc;
	uint32	sp;
	uint32	fp;
};

src/pkg/runtime/rt0_nacl_arm.s (新規追加)

// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "../../cmd/ld/textflag.h"

// NaCl entry has:
//	0(FP) - 0
//	4(FP) - cleanup function pointer, always 0
//	8(FP) - envc
//	12(FP) - argc
//	16(FP) - argv, then 0, then envv, then 0, then auxv
TEXT _rt0_arm_nacl(SB),NOSPLIT,$-4
	MOVW	8(R13), R0
	MOVW	$12(R13), R1
	MOVM.DB.W [R0-R1], (R13)
	B	main(SB)

TEXT main(SB),NOSPLIT,$0
	B	_rt0_go(SB)

src/pkg/runtime/sys_nacl_arm.s (新規追加、一部抜粋)

// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "zasm_GOOS_GOARCH.h"
#include "../../cmd/ld/textflag.h"
#include "syscall_nacl.h"

#define NACL_SYSCALL(code) \\\
	MOVW	$(0x10000 + ((code)<<5)), R8; BL (R8)

#define NACL_SYSJMP(code) \\\
	MOVW	$(0x10000 + ((code)<<5)), R8; B (R8)

TEXT runtime·exit(SB),NOSPLIT,$0
	MOVW	arg1+0(FP), R0
	NACL_SYSJMP(SYS_exit)

// ... (多数のシステムコールラッパーが続く) ...

TEXT runtime·sigtramp(SB),NOSPLIT,$80
	// load g from thread context
	MOVW	$ctxt+-4(FP), R0
	MOVW	(16*4+10*4)(R0), g

	// check that g exists
	CMP	$0, g
	BNE 	4(PC)
	MOVW  	$runtime·badsignal2(SB), R11
	BL	(R11)
	RET

	// save g
	MOVW	g, R3
	MOVW	g, 20(R13)

	// g = m->gsignal
	MOVW	g_m(g), R8
	MOVW	m_gsignal(R8), g

	// copy arguments for call to sighandler
	MOVW	$11, R0
	MOVW	R0, 4(R13) // signal
	MOVW	$0, R0
	MOVW	R0, 8(R13) // siginfo
	MOVW	$ctxt+-4(FP), R0
	MOVW	R0, 12(R13) // context
	MOVW	R3, 16(R13) // g

	BL	runtime·sighandler(SB)

	// restore g
	MOVW	20(R13), g

sigtramp_ret:
	// Enable exceptions again.
	NACL_SYSCALL(SYS_exception_clear_flag)

	// Restore registers as best we can. Impossible to do perfectly.
	// See comment in sys_nacl_386.s for extended rationale.
	MOVW	$ctxt+-4(FP), R1
	ADD	$64, R1
	MOVW	(0*4)(R1), R0
	MOVW	(2*4)(R1), R2
	MOVW	(3*4)(R1), R3
	MOVW	(4*4)(R1), R4
	MOVW	(5*4)(R1), R5
	MOVW	(6*4)(R1), R6
	MOVW	(7*4)(R1), R7
	MOVW	(8*4)(R1), R8
	// cannot write to R9
	MOVW	(10*4)(R1), g
	MOVW	(11*4)(R1), R11
	MOVW	(12*4)(R1), R12
	MOVW	(13*4)(R1), R13
	MOVW	(14*4)(R1), R14
	MOVW	(15*4)(R1), R1
	B	(R1)

コアとなるコードの解説

src/pkg/runtime/defs_nacl_arm.h

このヘッダーファイルは、GoランタイムがNaCl環境と連携するために必要なC言語の構造体と定数を定義しています。特に重要なのは、NaClが例外やシグナルを処理する際に使用するコンテキスト情報 (ExcContext, ExcRegsARM) の定義です。ExcRegsARM はARMプロセッサのレジスタセットをミラーリングしており、シグナルハンドラが例外発生時のCPUの状態を正確に把握し、必要に応じて変更できるようにします。これにより、GoランタイムはNaClのサンドボックス内で発生したシグナル(例えば、メモリ不正アクセスによるSIGSEGV)を捕捉し、Goのパニック機構に変換するなどの適切な処理を行うことが可能になります。

src/pkg/runtime/rt0_nacl_arm.s

このアセンブリファイルは、NaCl環境でGoプログラムが起動する際のエントリポイント (_rt0_arm_nacl) を定義しています。通常のOSでは、プログラムはOSが提供する標準的なエントリポイントから開始しますが、NaClは独自の起動プロセスを持ちます。_rt0_arm_nacl は、NaClがプログラムに渡す引数(環境変数やコマンドライン引数など)をGoランタイムが理解できる形式に変換し、最終的にGoランタイムの初期化関数である _rt0_go を呼び出します。これにより、GoプログラムはNaClサンドボックス内で正しく初期化され、実行を開始できます。

src/pkg/runtime/sys_nacl_arm.s

このファイルは、GoランタイムがNaClのシステムサービスを利用するための「橋渡し」となるアセンブリコードを含んでいます。

  • システムコールラッパー: NACL_SYSCALLNACL_SYSJMP マクロは、GoランタイムがNaClのシステムコールを呼び出すための汎用的なメカニズムを提供します。NaClのシステムコールは、特定のレジスタ(ARMではR8)にシステムコール番号を設定し、特別な命令シーケンスを実行することで呼び出されます。このファイルには、exit, read, write, mmap など、Goプログラムが動作するために不可欠な多数のシステムコールに対するアセンブリラッパーが実装されています。これにより、GoランタイムはNaClのサンドボックス内でファイルI/O、メモリ割り当て、プロセス制御などの基本的な操作を実行できます。
  • シグナルハンドラ (runtime·sigtramp): runtime·sigtramp は、NaCl環境でシグナルが発生した際に最初に実行されるアセンブリコードです。この関数は、シグナル発生時のCPUの状態(レジスタ値など)を保存し、GoランタイムのC言語で記述されたシグナルハンドラ (runtime·sighandler) を呼び出します。runtime·sighandler は、Goのゴルーチンモデルに沿ってシグナルを処理し、必要に応じてパニックを発生させたり、プログラムを終了させたりします。シグナル処理後、sigtramp はレジスタの状態を可能な限り復元し、プログラムの実行を再開させます。SYS_exception_clear_flag の呼び出しは、NaClの例外フラグをクリアし、次の例外が正しく処理されるようにするために重要です。

これらのコアとなる変更により、Go言語はNaClのサンドボックス環境とARMアーキテクチャの組み合わせという、特定の制約を持つプラットフォーム上で、そのランタイムの主要な機能(メモリ管理、スケジューリング、システムコール、シグナルハンドリングなど)を適切に動作させることが可能になりました。

関連リンク

参考にした情報源リンク