[インデックス 18665] ファイルの概要
このコミットは、GoコンパイラおよびアセンブラがGoogle Native Client (NaCl) をサポートするための変更を導入しています。具体的には、GOARCH
環境変数を参照するようにビルドツールを更新し、NaCl環境における特定のアーキテクチャ(amd64)でのレジスタ使用制限やアドレッシングモードの制約に対応しています。
コミット
commit 7954b2b90be1b424e1dc8d9d50b3edd1d7a60d4c
Author: Dave Cheney <dave@cheney.net>
Date: Thu Feb 27 06:57:06 2014 +1100
cmd/5a, cmd/5c, cmd/6a, cmd/6c, cmd/8a, cmd/8c, cmd/cc: support for Native Client
From the original description in CL 15770043
The main change here is to consult $GOARCH.
In 6c, when GOOS=nacl, some of the more complex addressing modes must be disabled, and the BP and R15 registers must not be used.
See golang.org/s/go13nacl for design overview.
LGTM=rsc
R=golang-codereviews, gobot, rsc
CC=golang-codereviews
https://golang.org/cl/69020044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7954b2b90be1b424e1dc8d9d50b3edd1d7a60d4c
元コミット内容
このコミットは、Goコンパイラ (cmd/5c
, cmd/6c
, cmd/8c
) とアセンブラ (cmd/5a
, cmd/6a
, cmd/8a
)、および共通のCコンパイラフロントエンド (cmd/cc
) に、Google Native Client (NaCl) のサポートを追加することを目的としています。主な変更点は、ビルドプロセスにおいて$GOARCH
環境変数を参照するようにしたことです。特に、GOOS=nacl
の場合、6c
(amd64アーキテクチャのGoコンパイラ) において、一部の複雑なアドレッシングモードが無効化され、BP
およびR15
レジスタの使用が禁止されるようになります。
変更の背景
Go言語は、様々なプラットフォームやアーキテクチャで動作するように設計されています。Google Native Client (NaCl) は、ウェブブラウザ内で安全にネイティブコードを実行するための技術であり、GoプログラムをNaCl環境で実行できるようにすることは、Goの適用範囲を広げる上で重要でした。
この変更の背景には、Go 1.3におけるNaClサポートの取り組みがあります。NaCl環境は、セキュリティ上の理由から、通常のネイティブ環境とは異なる制約を課します。例えば、特定のシステムコールが利用できなかったり、メモリ保護の仕組みが異なったりします。特に、レジスタの使用やアドレッシングモードに関して、NaClのサンドボックス環境に適合させるための調整が必要でした。
このコミットは、GoコンパイラとアセンブラがNaClの制約を理解し、それに準拠したコードを生成できるようにするための基盤を構築するものです。これにより、GoプログラムがNaCl環境で安全かつ効率的に動作することが可能になります。
前提知識の解説
GoのビルドプロセスとGOOS
/GOARCH
Go言語のビルドプロセスは、クロスコンパイルを強力にサポートしています。これは、GOOS
とGOARCH
という2つの環境変数によって制御されます。
GOOS
(Go Operating System): ターゲットとなるオペレーティングシステムを指定します。例えば、linux
、windows
、darwin
(macOS)、freebsd
などがあります。このコミットでは、nacl
という新しいGOOS
が導入され、Google Native Client環境をターゲットとすることを示します。GOARCH
(Go Architecture): ターゲットとなるCPUアーキテクチャを指定します。例えば、amd64
、386
、arm
などがあります。
Goのビルドツールチェーンは、これらの環境変数の値に基づいて、適切なコンパイラ、アセンブラ、リンカを選択し、ターゲット環境に特化したバイナリを生成します。
Google Native Client (NaCl)
Google Native Client (NaCl) は、ウェブブラウザ内でC/C++などのネイティブコードを安全かつポータブルに実行するための技術です。NaClは、サンドボックス環境を提供することで、悪意のあるコードがシステムにアクセスするのを防ぎます。
NaClの主な特徴は以下の通りです。
- サンドボックス化: 実行されるコードは厳重に隔離された環境で動作し、ファイルシステムやネットワークなどのシステムリソースへの直接アクセスが制限されます。
- ポータビリティ: NaClバイナリは、異なるCPUアーキテクチャ(x86-32、x86-64、ARM)間で互換性を持つように設計されています。これは、Portable Native Client (PNaCl) と呼ばれる中間表現を介して実現されます。
- セキュリティ: コードの検証、メモリ保護、システムコールフィルタリングなどのメカニズムにより、セキュリティが強化されています。
NaCl環境では、通常のOS環境とは異なる制約が課せられるため、コンパイラやアセンブラはこれらの制約を考慮してコードを生成する必要があります。特に、特定のレジスタの使用が禁止されたり、アドレッシングモードに制限があったりすることがあります。
Goのコンパイラとアセンブラ
Goのツールチェーンには、各アーキテクチャに対応するコンパイラとアセンブラが含まれています。
cmd/5a
,cmd/6a
,cmd/8a
: それぞれARM (5a
), AMD64 (6a
), x86 (8a
) アーキテクチャ用のアセンブラです。アセンブラは、アセンブリ言語のソースコードを機械語に変換します。cmd/5c
,cmd/6c
,cmd/8c
: それぞれARM (5c
), AMD64 (6c
), x86 (8c
) アーキテクチャ用のGoコンパイラです。これらのコンパイラは、Goのソースコードをアセンブリ言語に変換し、その後アセンブラによって機械語に変換されます。cmd/cc
: Goのコンパイラが内部的に使用するCコンパイラのフロントエンドです。Goのコンパイラは、一部の低レベルな処理やランタイムのコードをC言語で記述しており、それをコンパイルするためにcmd/cc
を使用します。
これらのツールは、GOOS
とGOARCH
の値に基づいて、ターゲット環境に合わせたコードを生成する役割を担っています。
技術的詳細
このコミットにおける技術的な変更は、主に以下の点に集約されます。
-
GOARCH
の参照:src/cmd/*/lex.c
(アセンブラの字句解析部分) およびsrc/cmd/*/swt.c
(コンパイラのスイッチ文処理部分) において、生成されるオブジェクトファイルのヘッダにgetgoarch()
の値を埋め込むように変更されました。これにより、オブジェクトファイルがどのアーキテクチャ向けにビルドされたかを明確に識別できるようになります。src/cmd/5a/lex.c
,src/cmd/6a/lex.c
,src/cmd/8a/lex.c
およびsrc/cmd/cc/lex.c
では、getgoarch()
で取得した値が、現在のコンパイラ/アセンブラがサポートするアーキテクチャ (thestring
) と一致するかどうかを厳密にチェックするようになりました。これにより、誤ったGOARCH
設定でのビルドを防ぎます。
-
NaCl固有の制約対応:
nacl
フラグの導入:src/cmd/cc/cc.h
にnacl
というグローバル変数が追加され、src/cmd/cc/lex.c
でgetgoos()
が"nacl"
を返す場合にnacl
がtrue
に設定されるようになりました。このフラグは、NaCl環境特有の挙動を制御するために使用されます。flag_largemodel
の設定:src/cmd/cc/lex.c
でnacl
がtrue
の場合、flag_largemodel
が1
に設定されます。これは、NaCl環境ではアドレス空間が広いため、ポインタのサイズが通常よりも大きくなることを示唆している可能性があります。- レジスタ使用の制限 (amd64/6c):
src/cmd/6c/reg.c
のBtoR
関数において、nacl
がtrue
の場合、BP
レジスタ (D_BP
) とR15
レジスタ (D_R15
) の使用が禁止されるようになりました。これは、NaClのサンドボックス環境がこれらのレジスタに特別な意味を持たせているため、Goコンパイラがこれらを通常の汎用レジスタとして使用することを避ける必要があるためです。src/cmd/6c/txt.c
のginit
関数とgclean
関数において、nacl
がtrue
の場合、BP
とR15
レジスタが予約済みとして扱われ、コンパイラがこれらを割り当てないように制御されます。
- アドレッシングモードの制限 (amd64/6c):
src/cmd/6c/sgen.c
のxcom
関数において、nacl
がtrue
の場合、一部の複雑なアドレッシングモード(n->addable == 8
の条件)が無効化されるようになりました。これは、NaClがサポートするアドレッシングモードが通常のx86-64アーキテクチャよりも制限されているためです。
- ポインタサイズの調整 (amd64/6c):
src/cmd/6c/txt.c
において、ewidth[TIND]
(ポインタの幅) が4
バイトの場合の特殊な処理が追加されました。これは、NaClの32ビット環境 (amd64p32
) をサポートするためです。amd64p32
は、64ビットアーキテクチャ上で32ビットポインタを使用するモードで、メモリ使用量を削減する目的で利用されます。gmove
関数やgopcode
関数など、ポインタの型 (TIND
) を扱う箇所で、ewidth[TIND] == 4
の場合にTUINT
(符号なし整数) として扱うなどの調整が行われています。これにより、ポインタのサイズが4バイトであることをコンパイラが正しく認識し、適切な命令を生成できるようになります。exreg
関数では、外部レジスタのオフセット計算にewidth[TIND]
を使用するようになり、ポインタサイズに応じたオフセットが確保されるようになりました。
- アラインメントの調整 (amd64/6c):
src/cmd/6c/swt.c
のalign
関数において、ewidth[TIND] == 4
の場合の引数アラインメントのロジックが追加されました。これにより、NaClの32ビットポインタ環境での関数呼び出し規約に合わせたアラインメントが保証されます。
-
thechar
とthestring
の初期化位置の変更:src/cmd/5c/txt.c
,src/cmd/6c/txt.c
,src/cmd/8c/txt.c
において、thechar
とthestring
の初期化がginit()
関数内からグローバル変数宣言時に変更されました。これにより、これらの変数がより早期に利用可能になり、main()
関数でのGOARCH
チェックに利用できるようになります。
これらの変更により、Goのコンパイラとアセンブラは、NaCl環境の特殊な要件に対応し、GoプログラムがNaClサンドボックス内で正しく動作するためのコードを生成できるようになりました。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルと、その変更の概要は以下の通りです。
src/cmd/5a/lex.c
:main
関数でGOARCH
をチェックし、現在のアーキテクチャ (thestring
) と一致しない場合はエラーを出すように変更。- オブジェクトファイルのヘッダに
getgoarch()
の値を埋め込むように変更。
src/cmd/5c/swt.c
:- オブジェクトファイルのヘッダに
getgoarch()
の値を埋め込むように変更。
- オブジェクトファイルのヘッダに
src/cmd/5c/txt.c
:thechar
とthestring
の初期化をグローバル変数宣言時に移動。
src/cmd/6a/lex.c
:main
関数でGOARCH
をチェックし、現在のアーキテクチャ (thestring
) と一致しない場合はエラーを出すように変更。- オブジェクトファイルのヘッダに
getgoarch()
の値を埋め込むように変更。
src/cmd/6c/reg.c
:BtoR
関数でnacl
がtrue
の場合、BP
とR15
レジスタの使用を禁止するロジックを追加。
src/cmd/6c/sgen.c
:xcom
関数でnacl
がtrue
の場合、一部の複雑なアドレッシングモードを無効化する条件を追加。
src/cmd/6c/swt.c
:- オブジェクトファイルのヘッダに
getgoarch()
の値を埋め込むように変更。 align
関数でewidth[TIND] == 4
の場合の引数アラインメントのロジックを追加。
- オブジェクトファイルのヘッダに
src/cmd/6c/txt.c
:thechar
とthestring
の初期化をグローバル変数宣言時に移動。_64BITREG
を定義し、ewidth[TIND] == 8
の場合に_64BIT
を定義するように変更。ginit
関数とgclean
関数でnacl
がtrue
の場合、BP
とR15
レジスタを予約済みとして扱うロジックを追加。naddr
,gmove
,gopcode
,exreg
関数でewidth[TIND]
の値に基づいてポインタの型やオフセット計算を調整。
src/cmd/8a/lex.c
:main
関数でGOARCH
をチェックし、現在のアーキテクチャ (thestring
) と一致しない場合はエラーを出すように変更。- オブジェクトファイルのヘッダに
getgoarch()
の値を埋め込むように変更。
src/cmd/8c/swt.c
:- オブジェクトファイルのヘッダに
getgoarch()
の値を埋め込むように変更。
- オブジェクトファイルのヘッダに
src/cmd/8c/txt.c
:thechar
とthestring
の初期化をグローバル変数宣言時に移動。
src/cmd/cc/cc.h
:nacl
という外部変数を追加。thechar
とthestring
の宣言をextern
に変更。
src/cmd/cc/lex.c
:main
関数でGOARCH
をチェックし、現在のアーキテクチャ (thestring
) と一致しない場合はエラーを出すように変更。GOARCH
が"amd64p32"
の場合にewidth[TIND]
を4
に設定。getgoos()
が"nacl"
の場合にnacl
フラグをtrue
に設定し、flag_largemodel
を1
に設定。
コアとなるコードの解説
GOARCH
の厳密なチェック
src/cmd/*/lex.c
(アセンブラ) および src/cmd/cc/lex.c
(Cコンパイラフロントエンド) のmain
関数に以下のコードが追加されました。
p = getgoarch();
if(strncmp(p, thestring, strlen(thestring)) != 0)
sysfatal("cannot use %cc with GOARCH=%s", thechar, p);
これは、現在のビルドツール(アセンブラまたはCコンパイラ)が意図されたGOARCH
と一致しているかを厳密にチェックするためのものです。thestring
は、そのツールがサポートするアーキテクチャ(例: amd64
, arm
, 386
)を表します。もしGOARCH
環境変数の値がthestring
と異なる場合、ビルドは中断され、エラーメッセージが表示されます。これにより、誤ったクロスコンパイル設定による問題を未然に防ぎます。
NaCl固有のレジスタ制限 (amd64/6c)
src/cmd/6c/reg.c
のBtoR
関数に以下の変更が加えられました。
b &= 0xffffL;
if(nacl)
b &= ~((1<<(D_BP-D_AX)) | (1<<(D_R15-D_AX)));
if(b == 0)
return 0;
return bitno(b) + D_AX;
BtoR
関数は、レジスタのビットマスクからレジスタ番号を抽出する役割を担っています。nacl
フラグがtrue
の場合、BP
レジスタ (D_BP
) とR15
レジスタ (D_R15
) に対応するビットがマスクからクリアされます。これは、NaCl環境ではこれらのレジスタが特別な用途に予約されており、Goコンパイラが通常の汎用レジスタとして使用することを避ける必要があるためです。
また、src/cmd/6c/txt.c
のginit
関数とgclean
関数でも、nacl
がtrue
の場合にこれらのレジスタが予約済みとして扱われるようになり、コンパイラがレジスタ割り当てを行う際にこれらを避けるようになります。
NaCl固有のアドレッシングモード制限 (amd64/6c)
src/cmd/6c/sgen.c
のxcom
関数に以下の変更が加えられました。
if(n->addable == 8 && !side(n) && !nacl) {
indx(n);
l = new1(OINDEX, idx.basetree, idx.regtree);
l->scale = idx.scale;
このコードは、特定の複雑なアドレッシングモード (n->addable == 8
) を使用できるかどうかを判断する部分です。!nacl
という条件が追加されたことで、nacl
フラグがtrue
の場合、このアドレッシングモードは無効化されます。これは、NaClのサンドボックス環境がサポートするアドレッシングモードが通常のx86-64アーキテクチャよりも制限されているため、GoコンパイラがNaCl互換のコードを生成するために必要な調整です。
ポインタサイズの動的な調整 (amd64/6c)
src/cmd/cc/lex.c
に以下のコードが追加されました。
if(strcmp(getgoarch(), "amd64p32") == 0) // must be before cinit
ewidth[TIND] = 4;
このコードは、GOARCH
が"amd64p32"
(64ビットアーキテクチャ上で32ビットポインタを使用するモード)の場合に、ポインタのサイズ (ewidth[TIND]
) を4バイトに設定します。これにより、コンパイラはポインタを32ビットとして扱い、それに応じた命令を生成します。
また、src/cmd/6c/txt.c
のgmove
関数やgopcode
関数など、ポインタの型 (TIND
) を扱う多くの箇所で、ewidth[TIND]
の値に基づいてポインタの型をTUINT
(符号なし整数) として扱うなどの調整が行われています。これにより、ポインタのサイズが4バイトであることをコンパイラが正しく認識し、適切なロード/ストア命令(例: AMOVL
ではなくAMOVQ
)を生成できるようになります。
これらの変更は、GoコンパイラがNaCl環境の特定の制約(レジスタ使用、アドレッシングモード、ポインタサイズ)を考慮し、それに準拠した効率的で安全なコードを生成できるようにするために不可欠です。
関連リンク
- Go CL 69020044: https://golang.org/cl/69020044
- Go 1.3 Native Client Design Overview: golang.org/s/go13nacl
参考にした情報源リンク
- Reddit discussion about Go 1.3 Native Client Support: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG5qVrFnCkdtsI1ct2sj86aeKYCDCffiIXZZkE54QjsJ8M0tB1uO8XT1OmIh2yf5-mxRSBdpi1_WTIa-BYEqhMCn9LuV6fctQpAdl3J5jAjOy_dQWut1POsq1v6-SZZ_yy2EqW78M9Z0PAvr4DMMQ11KgEUFr-v2XZnNUJ1EC2Nprz4ryns1To=
- Google Native Client (Wikipedia): https://en.wikipedia.org/wiki/Google_Native_Client (一般的なNaClの概念理解のため)
- Go Environment Variables (
GOOS
,GOARCH
): https://go.dev/doc/install/source#environment (一般的なGoの環境変数理解のため)