[インデックス 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_nacl
とGOARCH_arm
の組み合わせのサポート:- Goのビルドシステムにおいて、
GOOS=nacl
とGOARCH=arm
の組み合わせが認識され、それに応じたランタイムコードがコンパイルされるようになります。
- Goのビルドシステムにおいて、
-
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環境でシグナルや例外が発生した際に、レジスタの状態などを取得するために使用されます。
- 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
を呼び出すためのラッパーです。
- NaCl環境における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
など) が定義されました。これらは、シグナルハンドラ内でレジスタの状態を検査・変更するために使用されます。
- シグナルコンテキストからARMレジスタの値を取得するためのマクロ (
-
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
) の呼び出しも含まれます。
- NaCl固有のシステムコールを呼び出すためのアセンブリラッパーが多数追加されました。これには、
-
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プロセッサの特性を考慮しつつ、必要なシステムサービスや並行処理機能を利用できるようにするための基盤を構築しています。
コアとなるコードの変更箇所
このコミットにおけるコアとなる変更は、主に以下のファイルに集約されています。
src/pkg/runtime/defs_nacl_arm.h
: NaCl固有のデータ構造(シグナル情報、例外コンテキスト)の定義。src/pkg/runtime/sys_nacl_arm.s
: NaClシステムコールへのアセンブリラッパーと、シグナルハンドリングのエントリポイントの実装。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_SYSCALL
とNACL_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アーキテクチャの組み合わせという、特定の制約を持つプラットフォーム上で、そのランタイムの主要な機能(メモリ管理、スケジューリング、システムコール、シグナルハンドリングなど)を適切に動作させることが可能になりました。
関連リンク
- Go言語公式ドキュメント: https://golang.org/doc/
- Go言語のランタイムに関する情報 (Go Wiki): https://go.dev/wiki/GoRuntime
- Native Client (NaCl) 公式サイト (現在は非推奨): https://developer.chrome.com/native-client/
- ARMアーキテクチャについて: https://www.arm.com/
参考にした情報源リンク
- Go CL 103680046 (このコミットのChangeList): https://golang.org/cl/103680046
- Native Client Architecture (Google Chrome Developers): https://developer.chrome.com/native-client/nacl-and-pnacl
- Goのクロスコンパイルに関するドキュメント: https://go.dev/doc/install/source#environment
- Goのランタイムソースコード (GitHub): https://github.com/golang/go/tree/master/src/runtime
- ARM Assembly Language Programming: https://www.arm.com/resources/education/books
- Goのシステムコールに関する議論 (Go Issues): https://github.com/golang/go/issues?q=syscall
- Goのシグナルハンドリングに関する議論 (Go Issues): https://github.com/golang/go/issues?q=signal+handling
- GoのTLSに関する議論 (Go Issues): https://github.com/golang/go/issues?q=thread+local+storage
- Goの
rt0_go
に関する情報: https://go.dev/src/runtime/asm_amd64.s (他のアーキテクチャのrt0
も参照) - Goの
gogo
に関する情報: https://go.dev/src/runtime/asm_amd64.s (他のアーキテクチャのgogo
も参照) - Goの
timediv
に関する情報: https://go.dev/src/runtime/runtime.c - Goの
vlop_arm.s
に関する情報: https://go.dev/src/runtime/vlop_arm.s - Goの
vlrt_arm.c
に関する情報: https://go.dev/src/runtime/vlrt_arm.c - Goの
tls_arm.s
に関する情報: https://go.dev/src/runtime/tls_arm.s - Goの
signal_arm.c
に関する情報: https://go.dev/src/runtime/signal_arm.c - Goの
os_nacl_arm.c
に関する情報: https://go.dev/src/runtime/os_nacl_arm.c - Goの
arch_arm.h
に関する情報: https://go.dev/src/runtime/arch_arm.h